Allow agents to register MCP extensions dynamically at runtime
- Lingua principale
- Rust
- Stelle
- 54.2k
- Fork
- 6.2k
- Merge medio
- 3g 2h
- PR unite (30g)
- 262
Descrizione
**Please explain the motivation behind the feature request.**
Identity/credential brokers (e.g. [Warden](https://github.com/stephnangue/warden), and similar projects) sit between agents and external services. At session start, the agent calls the broker's discovery endpoint and gets back a list of **roles** — each role is a (service, policy, credential-scope) tuple with an operator-written description. Multiple roles can sit in front of the same MCP service with different scopes; the broker exposes one gateway URL per role, with the role name embedded in the URL path. The agent reads the descriptions, picks the role whose description matches its task, and opens an MCP session at that role's gateway URL using its workload identity token as the bearer.
Concrete shape: an agent runs in a Kubernetes pod with a ServiceAccount JWT, calls the broker, and sees that the broker's `mcp_slack/` mount is fronted by two roles:
- `slack-publisher` — described as "publish hygiene canvases to channel #sec-hygiene; create/update canvas tools only"
- `slack-alert-poster` — described as "post one-line alerts to channel #oncall; chat.postMessage only"
The agent reads the descriptions, picks the role whose scope matches its task, and needs to open an MCP session at that role's gateway URL — e.g. `https://broker.example.com/v1//mcp_slack/role/slack-publisher/gateway/` with `Authorization: Bearer `. The role itself — and therefore the role segment in the URL — isn't known until after the discovery turn, so the URL can't be pre-registered in `~/.config/goose/config.yaml` at pod startup.
Today, `extensionmanager__manage_extensions` (in [crates/goose/src/agents/platform_extensions/ext_manager.rs](https://github.com/aaif-goose/goose/blob/main/crates/goose/src/agents/platform_extensions/ext_manager.rs)) only enables/disables extensions by name; the extension must already be in the config. There's no path for an agent-mediated MCP server discovery flow where URL + headers come from a runtime API call.
**Describe the solution you'd like**
Extend `manage_extensions` with a `Register` action (or add a sibling tool) that lets the agent supply full transport config:
```rust
pub enum ManageExtensionAction {
Enable,
Disable,
Register {
name: String, // identifier for subsequent Enable/Disable
transport: ExtensionTransport, // StreamableHttp | Stdio | Sse
uri: Option, // required for HTTP transports
cmd: Option, // required for Stdio
args: Option>,
headers: Option>,
env: Option>,
description: Option,
},
}
```
After `Register`, the extension lives in the session's in-memory extension set (not persisted to config) and is targetable by `Enable`/`Disable`.
Off-by-default for safety, behind an operator opt-in (e.g. `extensions.allow_dynamic_registration: true`). Suggested additional safeguards: optional URI allowlist in config, audit-log entry per agent-initiated registration, interactive-mode confirmation prompt before the new registration takes effect.
**Describe alternatives you've considered**
1. **Pre-register every candidate as disabled.** Bootstrap script enumerates all possible (provider, role) tuples at startup and writes each as a disabled extension to Goose config; agent enables by name. Works, but requires bootstrap-time enumeration of the candidate set, and the agent's description-driven selection collapses to name-matching against config-file names.
2. **Bypass Goose's MCP machinery.** Install a standalone MCP CLI (`mcpc`, `mcptools`) in the container and shell out via the `developer` builtin with the discovered URL + headers. Works fully dynamically, but the MCP tools never appear in Goose's native tool list — every call is a shell hop.
Neither is satisfying: (1) constrains operators to pre-enumerate, (2) gives up Goose's native MCP integration.
**Additional context**
Prior art:
- Docker's [MCP Gateway](https://www.docker.com/blog/dynamic-mcps-stop-hardcoding-your-agents-world/) exposes an `mcp-add` agent tool, but it's restricted to servers in Docker's curated catalog (no arbitrary URL).
- MCP's [elicitations](https://modelcontextprotocol.io/specification/draft/client/elicitation) spec covers OAuth-style negotiation but assumes the URL is already known to the client.
Acceptance: in a single session, the agent can — discover URL via a runtime API call → call `manage_extensions(Register{...})` with the discovered URL + headers → call `manage_extensions(Enable{name})` → invoke tools on the newly-registered MCP server. No pre-declaration of that specific URL in Goose config.
- [x] I have verified this does not duplicate an existing feature request
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.