Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions crates/goose/tests/mcp_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ enum TestMode {
vec![]
)]
#[test_case(
vec!["cargo", "run", "-p", "goose-server", "--bin", "goosed", "--", "mcp", "developer"],
vec!["cargo", "run", "--quiet", "-p", "goose-server", "--bin", "goosed", "--", "mcp", "developer"],
vec![
ToolCall::new("text_editor", json!({
"command": "view",
Expand All @@ -74,12 +74,6 @@ enum TestMode {
"new_str": "# codename goose"
})),
ToolCall::new("list_windows", json!({})),
ToolCall::new("screen_capture", json!({
"display": 0
})),
ToolCall::new("image_processor", json!({
"path": "~/goose/crates/goose/tests/tmp/goose-test.png"
})),
],
vec![]
)]
Expand Down Expand Up @@ -162,10 +156,9 @@ async fn test_replayed_session(

let extension_manager = ExtensionManager::new();

let result = extension_manager.add_extension(extension_config).await;
assert!(result.is_ok(), "Failed to add extension: {:?}", result);

let result = (async || -> Result<(), Box<dyn std::error::Error>> {
extension_manager.add_extension(extension_config).await?;

let mut results = Vec::new();
for tool_call in tool_calls {
let tool_call = ToolCall::new(format!("test__{}", tool_call.name), tool_call.arguments);
Expand Down Expand Up @@ -196,12 +189,14 @@ async fn test_replayed_session(
.await;

if let Err(err) = result {
let errors =
fs::read_to_string(format!("{}.errors.txt", replay_file_path.to_string_lossy()))
.expect("could not read errors");
eprintln!("errors from {}", replay_file_path.to_string_lossy());
eprintln!("{}", errors);
eprintln!();
if matches!(mode, TestMode::Playback) {
let errors =
fs::read_to_string(format!("{}.errors.txt", replay_file_path.to_string_lossy()))
.expect("could not read errors");
eprintln!("errors from {}", replay_file_path.to_string_lossy());
eprintln!("{}", errors);
eprintln!();
}
panic!("Test failed: {:?}", err);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"goose","version":"0.1.0"}}}
STDERR: 2025-09-25T16:45:17.536419Z  INFO goose_mcp::mcp_server_runner: Starting MCP server
STDERR: at crates/goose-mcp/src/mcp_server_runner.rs:18
STDERR:
STDERR: 2025-09-25T16:45:17.539210Z  INFO goose_mcp::developer::analyze::cache: Initializing analysis cache with size 100
STDERR: at crates/goose-mcp/src/developer/analyze/cache.rs:25
STDERR:
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"prompts":{},"tools":{}},"serverInfo":{"name":"goose-developer","version":"1.9.0"},"instructions":" The developer extension gives you the capabilities to edit code files and run shell commands,\n and can be used to solve a wide range of problems.\n\nYou can use the shell tool to run any command that would work on the relevant operating system.\nUse the shell tool as needed to locate files or interact with the project.\n\nLeverage `analyze` through `return_last_only=true` subagents for deep codebase understanding with lean context\n- delegate analysis, retain summaries\n\nYour windows/screen tools can be used for visual debugging. You should not use these tools unless\nprompted to, but you can mention they are available if they are relevant.\n\nAlways prefer ripgrep (rg -C 3) to grep.\n\noperating system: macos\ncurrent directory: /Users/jackamadeo/development/goose/crates/goose\n\n \n\nAdditional Text Editor Tool Instructions:\n\nPerform text editing operations on files.\n\nThe `command` parameter specifies the operation to perform. Allowed options are:\n- `view`: View the content of a file.\n- `write`: Create or overwrite a file with the given content\n- `str_replace`: Replace text in one or more files.\n- `insert`: Insert text at a specific line location in the file.\n- `undo_edit`: Undo the last edit made to a file.\n\nTo use the write command, you must specify `file_text` which will become the new content of the file. Be careful with\nexisting files! This is a full overwrite, so you must include everything - not just sections you are modifying.\n\nTo use the str_replace command to edit multiple files, use the `diff` parameter with a unified diff.\nTo use the str_replace command to edit one file, you must specify both `old_str` and `new_str` - the `old_str` needs to exactly match one\nunique section of the original file, including any whitespace. Make sure to include enough context that the match is not\nambiguous. The entire original string will be replaced with `new_str`\n\nWhen possible, batch file edits together by using a multi-file unified `diff` within a single str_replace tool call.\n\nTo use the insert command, you must specify both `insert_line` (the line number after which to insert, 0 for beginning, -1 for end)\nand `new_str` (the text to insert).\n\n\n\nAdditional Shell Tool Instructions:\nExecute a command in the shell.\n\nThis will return the output and error concatenated into a single string, as\nyou would see from running on the command line. There will also be an indication\nof if the command succeeded or failed.\n\nAvoid commands that produce a large amount of output, and consider piping those outputs to files.\n\n**Important**: Each shell command runs in its own process. Things like directory changes or\nsourcing files do not persist between tool calls. So you may need to repeat them each time by\nstringing together commands.\nIf you need to run a long lived command, background it - e.g. `uvicorn main:app &` so that\nthis tool does not run indefinitely.\n\n**Important**: Use ripgrep - `rg` - exclusively when you need to locate a file or a code reference,\nother solutions may produce too large output because of hidden files! For example *do not* use `find` or `ls -r`\n - List files by name: `rg --files | rg <filename>`\n - List files that contain a regex: `rg '<regex>' -l`\n\n - Multiple commands: Use && to chain commands, avoid newlines\n - Example: `cd example && ls` or `source env/bin/activate && pip install numpy`\n\n\n### Global Hints\nThe developer extension includes some global hints that apply to all projects & directories.\nThese are my global goose hints.\n\n### Project Hints\nThe developer extension includes some hints for working on the project in this directory.\n# AGENTS Instructions\n\nGoose is an AI agent framework in Rust with CLI and Electron desktop interfaces.\n\n## Setup\n```bash\nsource bin/activate-hermit\ncargo build\n```\n\n## Commands\n\n### Build\n```bash\ncargo build # debug\ncargo build --release # release \njust release-binary # release + openapi\n```\n\n### Test\n```bash\ncargo test # all tests\ncargo test -p goose # specific crate\ncargo test --package goose --test mcp_integration_test\njust record-mcp-tests # record MCP\n```\n\n### Lint/Format\n```bash\ncargo fmt\n./scripts/clippy-lint.sh\ncargo clippy --fix\n```\n\n### UI\n```bash\njust generate-openapi # after server changes\njust run-ui # start desktop\ncd ui/desktop && npm test # test UI\n```\n\n## Structure\n```\ncrates/\n├── goose # core logic\n├── goose-bench # benchmarking\n├── goose-cli # CLI entry\n├── goose-server # backend (binary: goosed)\n├── goose-mcp # MCP extensions\n├── goose-test # test utilities\n├── mcp-client # MCP client\n├── mcp-core # MCP shared\n└── mcp-server # MCP server\n\ntemporal-service/ # Go scheduler\nui/desktop/ # Electron app\n```\n\n## Development Loop\n```bash\n# 1. source bin/activate-hermit\n# 2. Make changes\n# 3. cargo fmt\n# 4. cargo build\n# 5. cargo test -p <crate>\n# 6. ./scripts/clippy-lint.sh\n# 7. [if server] just generate-openapi\n```\n\n## Rules\n\nTest: Prefer tests/ folder, e.g. crates/goose/tests/\nError: Use anyhow::Result\nProvider: Implement Provider trait see providers/base.rs\nMCP: Extensions in crates/goose-mcp/\nServer: Changes need just generate-openapi\n\n## Never\n\nNever: Edit ui/desktop/openapi.json manually\nNever: Edit Cargo.toml use cargo add\nNever: Skip cargo fmt\nNever: Merge without ./scripts/clippy-lint.sh\n\n## Entry Points\n- CLI: crates/goose-cli/src/main.rs\n- Server: crates/goose-server/src/main.rs\n- UI: ui/desktop/src/main.ts\n- Agent: crates/goose/src/agents/agent.rs\n\nThis is a rust project with crates in the crates dir:\ngoose: the main code for goose, contains all the core logic\ngoose-bench: bench marking\ngoose-cli: the command line interface, use goose crate\ngoose-mcp: the mcp servers that ship with goose. the developer sub system is of special interest\ngoose-server: the server that suports the desktop (electron) app. also known as goosed\n\n\nui/desktop has an electron app in typescript. \n\nnon trivial features should be implemented in the goose crate and then be called from the goose-cli crate for the cli. for the desktop, you want to add routes to \ngoose-server/src/routes. you can then run `just generate-openapi` to generate the openapi spec which will modify the ui/desktop/src/api files. once you have\nthat you can call the functionality from the server from the typescript.\n\ntips: \n- can look at unstaged changes for what is being worked on if starting\n- always check rust compiles, cargo fmt etc and `./scripts/clippy-lint.sh` (as well as run tests in files you are working on)\n- in ui/desktop, look at how you can run lint checks and if other tests can run\n"}}
STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"}
STDERR: 2025-09-25T16:45:17.543464Z  INFO rmcp::handler::server: client initialized
STDERR: at /Users/jackamadeo/development/goose/.hermit/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rmcp-0.6.2/src/handler/server.rs:218
STDERR:
STDIN: {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"_meta":{"progressToken":0},"name":"text_editor","arguments":{"command":"view","path":"~/goose/crates/goose/tests/tmp/goose.txt"}}}
STDERR: 2025-09-25T16:45:17.543588Z  INFO rmcp::service: Service initialized as server, peer_info: Some(InitializeRequestParam { protocol_version: ProtocolVersion("2025-03-26"), capabilities: ClientCapabilities { experimental: None, roots: None, sampling: None, elicitation: None }, client_info: Implementation { name: "goose", version: "0.1.0" } })
STDERR: at /Users/jackamadeo/development/goose/.hermit/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rmcp-0.6.2/src/service.rs:561
STDERR: in rmcp::service::serve_inner
STDERR:
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"resource","resource":{"uri":"file:///Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt","mimeType":"text","text":"# codename goose\n"},"annotations":{"audience":["assistant"]}},{"type":"text","text":"### /Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt\n```\n1: # codename goose\n```\n","annotations":{"audience":["user"],"priority":0.0}}],"isError":false}}
STDIN: {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"_meta":{"progressToken":1},"name":"text_editor","arguments":{"command":"str_replace","new_str":"# codename goose (modified by test)","old_str":"# codename goose","path":"~/goose/crates/goose/tests/tmp/goose.txt"}}}
STDOUT: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"The file /Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt has been edited, and the section now reads:\n```\n# codename goose (modified by test)\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n","annotations":{"audience":["assistant"]}},{"type":"text","text":"```\n# codename goose (modified by test)\n```\n","annotations":{"audience":["user"],"priority":0.2}}],"isError":false}}
STDIN: {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"_meta":{"progressToken":2},"name":"shell","arguments":{"command":"cat ~/goose/crates/goose/tests/tmp/goose.txt"}}}
STDOUT: {"jsonrpc":"2.0","method":"notifications/message","params":{"level":"info","logger":"shell_tool","data":{"type":"shell_output","stream":"stdout","output":"# codename goose (modified by test)"}}}
STDOUT: {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"# codename goose (modified by test)\n","annotations":{"audience":["assistant"]}},{"type":"text","text":"# codename goose (modified by test)\n","annotations":{"audience":["user"],"priority":0.0}}],"isError":false}}
STDIN: {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"_meta":{"progressToken":3},"name":"text_editor","arguments":{"command":"str_replace","new_str":"# codename goose","old_str":"# codename goose (modified by test)","path":"~/goose/crates/goose/tests/tmp/goose.txt"}}}
STDOUT: {"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"text","text":"The file /Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt has been edited, and the section now reads:\n```\n# codename goose\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n","annotations":{"audience":["assistant"]}},{"type":"text","text":"```\n# codename goose\n```\n","annotations":{"audience":["user"],"priority":0.2}}],"isError":false}}
STDIN: {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"_meta":{"progressToken":4},"name":"list_windows","arguments":{}}}
STDOUT: {"jsonrpc":"2.0","id":5,"result":{"content":[{"type":"text","text":"Available windows:\nMenubar","annotations":{"audience":["assistant"]}},{"type":"text","text":"Available windows:\nMenubar","annotations":{"audience":["user"],"priority":0.0}}],"isError":false}}
STDERR: 2025-09-25T16:45:17.625285Z  INFO rmcp::service: input stream terminated
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
[
[
{
"type": "resource",
"resource": {
"uri": "file:///Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt",
"mimeType": "text",
"text": "# codename goose\n"
},
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "### /Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt\n```\n1: # codename goose\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
],
[
{
"type": "text",
"text": "The file /Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt has been edited, and the section now reads:\n```\n# codename goose (modified by test)\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "```\n# codename goose (modified by test)\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.2
}
}
],
[
{
"type": "text",
"text": "# codename goose (modified by test)\n",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "# codename goose (modified by test)\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
],
[
{
"type": "text",
"text": "The file /Users/jackamadeo/goose/crates/goose/tests/tmp/goose.txt has been edited, and the section now reads:\n```\n# codename goose\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "```\n# codename goose\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.2
}
}
],
[
{
"type": "text",
"text": "Available windows:\nMenubar",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "Available windows:\nMenubar",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
]
]
Loading
Loading