diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index e1198028..44b18eae 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -341,6 +341,16 @@ enum Command { )] remote_extensions: Vec, + /// Add streamable HTTP extensions with a URL + #[arg( + long = "with-streamable-http-extension", + value_name = "URL", + help = "Add streamable HTTP extensions (can be specified multiple times)", + long_help = "Add streamable HTTP extensions from a URL. Can be specified multiple times. Format: 'url...'", + action = clap::ArgAction::Append + )] + streamable_http_extensions: Vec, + /// Add builtin extensions by name #[arg( long = "with-builtin", @@ -509,6 +519,16 @@ enum Command { )] remote_extensions: Vec, + /// Add streamable HTTP extensions + #[arg( + long = "with-streamable-http-extension", + value_name = "URL", + help = "Add streamable HTTP extensions (can be specified multiple times)", + long_help = "Add streamable HTTP extensions. Can be specified multiple times. Format: 'url...'", + action = clap::ArgAction::Append + )] + streamable_http_extensions: Vec, + /// Add builtin extensions by name #[arg( long = "with-builtin", @@ -674,6 +694,7 @@ pub async fn cli() -> Result<()> { max_turns, extensions, remote_extensions, + streamable_http_extensions, builtins, }) => { return match command { @@ -714,6 +735,7 @@ pub async fn cli() -> Result<()> { no_session: false, extensions, remote_extensions, + streamable_http_extensions, builtins, extensions_override: None, additional_system_prompt: None, @@ -774,6 +796,7 @@ pub async fn cli() -> Result<()> { max_turns, extensions, remote_extensions, + streamable_http_extensions, builtins, params, explain, @@ -863,6 +886,7 @@ pub async fn cli() -> Result<()> { no_session, extensions, remote_extensions, + streamable_http_extensions, builtins, extensions_override: input_config.extensions_override, additional_system_prompt: input_config.additional_system_prompt, @@ -990,6 +1014,7 @@ pub async fn cli() -> Result<()> { no_session: false, extensions: Vec::new(), remote_extensions: Vec::new(), + streamable_http_extensions: Vec::new(), builtins: Vec::new(), extensions_override: None, additional_system_prompt: None, diff --git a/crates/goose-cli/src/commands/bench.rs b/crates/goose-cli/src/commands/bench.rs index 76f9b7cb..a870cc53 100644 --- a/crates/goose-cli/src/commands/bench.rs +++ b/crates/goose-cli/src/commands/bench.rs @@ -37,6 +37,7 @@ pub async fn agent_generator( no_session: false, extensions: requirements.external, remote_extensions: requirements.remote, + streamable_http_extensions: Vec::new(), builtins: requirements.builtin, extensions_override: None, additional_system_prompt: None, diff --git a/crates/goose-cli/src/session/builder.rs b/crates/goose-cli/src/session/builder.rs index 86db3d2c..ba2f37f3 100644 --- a/crates/goose-cli/src/session/builder.rs +++ b/crates/goose-cli/src/session/builder.rs @@ -29,6 +29,8 @@ pub struct SessionBuilderConfig { pub extensions: Vec, /// List of remote extension commands to add pub remote_extensions: Vec, + /// List of streamable HTTP extension commands to add + pub streamable_http_extensions: Vec, /// List of builtin extension commands to add pub builtins: Vec, /// List of extensions to enable, enable only this set and ignore configured ones @@ -454,6 +456,43 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session { } } + // Add streamable HTTP extensions if provided + for extension_str in session_config.streamable_http_extensions { + if let Err(e) = session + .add_streamable_http_extension(extension_str.clone()) + .await + { + eprintln!( + "{}", + style(format!( + "Warning: Failed to start streamable HTTP extension '{}': {}", + extension_str, e + )) + .yellow() + ); + eprintln!( + "{}", + style(format!( + "Continuing without streamable HTTP extension '{}'", + extension_str + )) + .yellow() + ); + + // Offer debugging help + if let Err(debug_err) = offer_extension_debugging_help( + &extension_str, + &e.to_string(), + Arc::clone(&provider_for_display), + session_config.interactive, + ) + .await + { + eprintln!("Note: Could not start debugging session: {}", debug_err); + } + } + } + // Add builtin extensions for builtin in session_config.builtins { if let Err(e) = session.add_builtin(builtin.clone()).await { @@ -531,6 +570,7 @@ mod tests { no_session: false, extensions: vec!["echo test".to_string()], remote_extensions: vec!["http://example.com".to_string()], + streamable_http_extensions: vec!["http://example.com/streamable".to_string()], builtins: vec!["developer".to_string()], extensions_override: None, additional_system_prompt: Some("Test prompt".to_string()), @@ -549,6 +589,7 @@ mod tests { assert_eq!(config.extensions.len(), 1); assert_eq!(config.remote_extensions.len(), 1); + assert_eq!(config.streamable_http_extensions.len(), 1); assert_eq!(config.builtins.len(), 1); assert!(config.debug); assert_eq!(config.max_tool_repetitions, Some(5)); @@ -567,6 +608,7 @@ mod tests { assert!(!config.no_session); assert!(config.extensions.is_empty()); assert!(config.remote_extensions.is_empty()); + assert!(config.streamable_http_extensions.is_empty()); assert!(config.builtins.is_empty()); assert!(config.extensions_override.is_none()); assert!(config.additional_system_prompt.is_none()); diff --git a/crates/goose-cli/src/session/mod.rs b/crates/goose-cli/src/session/mod.rs index 825dd17c..27ec8507 100644 --- a/crates/goose-cli/src/session/mod.rs +++ b/crates/goose-cli/src/session/mod.rs @@ -244,6 +244,40 @@ impl Session { Ok(()) } + /// Add a streamable HTTP extension to the session + /// + /// # Arguments + /// * `extension_url` - URL of the server + pub async fn add_streamable_http_extension(&mut self, extension_url: String) -> Result<()> { + let name: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(8) + .map(char::from) + .collect(); + + let config = ExtensionConfig::StreamableHttp { + name, + uri: extension_url, + envs: Envs::new(HashMap::new()), + env_keys: Vec::new(), + headers: HashMap::new(), + description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()), + // TODO: should set timeout + timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT), + bundled: None, + }; + + self.agent + .add_extension(config) + .await + .map_err(|e| anyhow::anyhow!("Failed to start extension: {}", e))?; + + // Invalidate the completion cache when a new extension is added + self.invalidate_completion_cache().await; + + Ok(()) + } + /// Add a builtin extension to the session /// /// # Arguments diff --git a/documentation/docs/getting-started/using-extensions.md b/documentation/docs/getting-started/using-extensions.md index fcfa8b98..39a7b19e 100644 --- a/documentation/docs/getting-started/using-extensions.md +++ b/documentation/docs/getting-started/using-extensions.md @@ -291,6 +291,26 @@ For example, a deeplink for a URL like `http://localhost:8080/sse` would look li ``` goose://extension?url=http%3A%2F%2Flocalhost%3A8080%2Fsse&timeout=&id=&name=&description=> +``` + + + +``` +goose://extension?url=&type=streamable_http&id=&name=&description= +``` + +Parameters: +- `url`: The URL of the remote Streaming HTTP server +- `type`: Must be set to `streamable_http` to specify the protocol type +- `timeout`: Maximum time (in seconds) to wait for extension responses +- `id`: Unique identifier for the extension +- `name`: Display name for the extension +- `description`: Brief description of the extension's functionality + +For example, a deeplink for a URL like `https://example.com/streamable` would look like this when URL-encoded: + +``` +goose://extension?url=https%3A%2F%2Fexample.com%2Fstreamable&type=streamable_http&timeout=&id=&name=&description= ``` @@ -598,8 +618,22 @@ For example, to start a session with a remote extension over SSE running on loca goose session --with-remote-extension "http://localhost:8080/sse" ``` +### Remote Extensions over Streaming HTTP + +To enable a remote extension over Streaming HTTP while starting a session, run the following command: + +```bash +goose session --with-streamable-http-extension "{extension URL}" --with-streamable-http-extension "{another extension URL}" +``` + +For example, to start a session with a Streaming HTTP extension, you'd run: + +```bash +goose session --with-streamable-http-extension "https://example.com/streamable" +``` + ## Developing Extensions Goose extensions are implemented with MCP, a standard protocol that allows AI models and agents to securely connect with local or remote resources. Learn how to build your own [extension as an MCP server](https://modelcontextprotocol.io/quickstart/server). -[extensions-directory]: https://block.github.io/goose/v1/extensions \ No newline at end of file +[extensions-directory]: https://block.github.io/goose/v1/extensions diff --git a/documentation/docs/guides/goose-cli-commands.md b/documentation/docs/guides/goose-cli-commands.md index 0a36031e..95213846 100644 --- a/documentation/docs/guides/goose-cli-commands.md +++ b/documentation/docs/guides/goose-cli-commands.md @@ -32,7 +32,7 @@ goose configure ### session [options] -- Start a session and give it a name +#### Start a session and give it a name **Options:** @@ -43,8 +43,9 @@ goose configure ```bash goose session --name ``` +--- -- Resume a previous session +#### Resume a previous session **Options:** @@ -59,8 +60,9 @@ goose configure ```bash goose session --resume --id ``` +--- -- Start a session with the specified extension +#### Start a session with the specified extension **Options:** @@ -83,8 +85,9 @@ goose configure ```bash goose session --with-extension "GITHUB_PERSONAL_ACCESS_TOKEN= npx -y @modelcontextprotocol/server-github" ``` +--- -- Start a session with the specified remote extension over SSE +#### Start a session with the specified remote extension over SSE **Options:** @@ -102,7 +105,48 @@ goose configure goose session --with-remote-extension "http://localhost:8080/sse" ``` -- Start a session with the specified [built-in extension](/docs/getting-started/using-extensions#built-in-extensions) enabled (e.g. 'developer') +--- + +#### Start a session with the specified remote extension over Streaming HTTP + + **Options:** + + **`--with-streamable-http-extension `** + + **Usage:** + + ```bash + goose session --with-streamable-http-extension + ``` + + **Examples:** + + ```bash + goose session --with-streamable-http-extension "https://example.com/streamable" + ``` + + **Advanced Examples:** + + ```bash + # Start a session with a streamable HTTP extension + goose session --with-streamable-http-extension "http://api.example.com" + + # Use multiple streamable HTTP extensions + goose session \ + --with-streamable-http-extension "http://api1.example.com" \ + --with-streamable-http-extension "http://api2.example.com" + + # Mix different extension types + goose session \ + --with-extension "echo hello" \ + --with-remote-extension "http://sse.example.com/sse" \ + --with-streamable-http-extension "http://http.example.com" \ + --with-builtin "developer" + ``` + +--- + +#### Start a session with the specified [built-in extension](/docs/getting-started/using-extensions#built-in-extensions) enabled (e.g. 'developer') **Options:** @@ -119,8 +163,9 @@ goose configure ```bash goose session --with-builtin computercontroller ``` +--- -- Enable debug mode to output complete tool responses, detailed parameter values, and full file paths +#### Enable debug mode to output complete tool responses, detailed parameter values, and full file paths **Options:** @@ -131,8 +176,9 @@ goose configure ```bash goose session --name my-session --debug ``` +--- -- Limit the maximum number of turns the agent can take before asking for user input to continue +#### Limit the maximum number of turns the agent can take before asking for user input to continue **Options:** @@ -144,7 +190,9 @@ goose configure goose session --max-turns 50 ``` -- Set the [maximum number of turns](/docs/guides/smart-context-management#maximum-turns) allowed without user input (default: 1000) +--- + +#### Set the [maximum number of turns](/docs/guides/smart-context-management#maximum-turns) allowed without user input (default: 1000) **Options:** @@ -170,6 +218,7 @@ goose configure ``` --- + ### session list [options] List all saved sessions. @@ -326,6 +375,8 @@ Execute commands from an instruction file or stdin. Check out the [full guide](/ - **`--recipe `**: Load a custom recipe in current session - **`-p, --path `**: Path for this run session (e.g. `./playground.jsonl`) - **`--with-extension `**: Add stdio extensions (can be used multiple times in the same command) +- **`--with-remote-extension `**: Add remote extensions over SSE (can be used multiple times in the same command) +- **`--with-streamable-http-extension `**: Add remote extensions over Streaming HTTP (can be used multiple times in the same command) - **`--with-builtin `**: Add builtin extensions by name (e.g., 'developer' or multiple: 'developer,github') - **`--debug`**: Output complete tool responses, detailed parameter values, and full file paths - **`--max-turns `**: [Maximum number of turns](/docs/guides/smart-context-management#maximum-turns) allowed without user input (default: 1000) diff --git a/documentation/docs/guides/running-tasks.md b/documentation/docs/guides/running-tasks.md index a1ce9ce1..910bc780 100644 --- a/documentation/docs/guides/running-tasks.md +++ b/documentation/docs/guides/running-tasks.md @@ -100,7 +100,7 @@ goose run --no-session -t "your command here" ### Working with Extensions -If you want to ensure specific extensions are available when running your task, you can indicate this with arguments. This can be done using the `--with-extension`, `--with-remote-extension`, or `--with-builtin` flags: +If you want to ensure specific extensions are available when running your task, you can indicate this with arguments. This can be done using the `--with-extension`, `--with-remote-extension`, `--with-streamable-http-extension`, or `--with-builtin` flags: - Using built-in extensions e.g developer and computercontroller extensions @@ -120,6 +120,12 @@ goose run --with-extension "ENV1=value1 custom-extension-args" -t "your instruct goose run --with-remote-extension "url" -t "your instructions" ``` +- Using streamable HTTP extensions + +```bash +goose run --with-streamable-http-extension "https://example.com/streamable" -t "your instructions" +``` + ### Debug Mode When troubleshooting or developing complex workflows, you can enable debug mode to get more detailed information about tool execution. The `--debug` flag provides: