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
13 changes: 11 additions & 2 deletions docs/src/content/docs/agents/mcp-tool-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,18 @@ The async factory pattern is required because the agent needs tool definitions s
<TabItem label="TypeScript" icon="seti:typescript" color="blue">
```bash
npm install agent-squad
npm install @modelcontextprotocol/client # MCP SDK v2 — recommended
```
Or the v1 SDK, if you only talk to servers on protocol 2025-11-25 or older:
```bash
npm install @modelcontextprotocol/sdk
```
`@modelcontextprotocol/sdk` is a peer dependency — it is never installed automatically, only when you explicitly add it.
Both are optional peer dependencies — never installed automatically. When both are present, `MCPToolProvider` uses v2.

| Installed package | MCP protocol versions | Notes |
|---|---|---|
| `@modelcontextprotocol/client` >= 2.0.0 | 2026-07-28 **and** all legacy versions | auto-negotiated per server (`server/discover` probe with `initialize` fallback); Node >= 20 |
| `@modelcontextprotocol/sdk` >= 1.0.0 | 2025-11-25 and older | the v1 package will never support 2026-07-28 |
</TabItem>
<TabItem label="Python" icon="seti:python">
```bash
Expand Down Expand Up @@ -139,7 +148,7 @@ const provider = await MCPToolProvider.create([
},
]);
```
Requires `@modelcontextprotocol/sdk` >= 1.10.
Requires `@modelcontextprotocol/client` (any version) or `@modelcontextprotocol/sdk` >= 1.10.
</TabItem>
<TabItem label="Python" icon="seti:python">
```python
Expand Down
4 changes: 4 additions & 0 deletions typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@
},
"peerDependencies": {
"@dakera-ai/dakera": "^0.11.100",
"@modelcontextprotocol/client": ">=2.0.0",
"@modelcontextprotocol/sdk": ">=1.0.0"
},
"peerDependenciesMeta": {
"@dakera-ai/dakera": {
"optional": true
},
"@modelcontextprotocol/client": {
"optional": true
},
"@modelcontextprotocol/sdk": {
"optional": true
}
Expand Down
124 changes: 87 additions & 37 deletions typescript/src/tools/mcpToolProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ export interface MCPServerConfig {
* await provider.disconnect();
* ```
*
* The `@modelcontextprotocol/sdk` package must be installed separately:
* An MCP SDK must be installed separately — either the v2 client package
* (speaks protocol 2026-07-28 and every older server via auto negotiation;
* preferred when both are installed):
* ```
* npm install @modelcontextprotocol/client
* ```
* or the v1 SDK (older servers only):
* ```
* npm install @modelcontextprotocol/sdk
* ```
Expand Down Expand Up @@ -123,40 +129,64 @@ export class MCPToolProvider extends AgentTools {
let ClientClass: any;
let StdioClientTransport: any;
let SSEClientTransport: any;
let StreamableHTTPClientTransport: any;
// v2 (@modelcontextprotocol/client) speaks both the 2026-07-28 stateless
// protocol and the legacy handshake; preferred over v1 when installed.
let usingV2 = false;

try {
// @ts-ignore — optional peerDependency; not available in type-checking until installed
const clientMod = await import("@modelcontextprotocol/sdk/client/index.js");
ClientClass = clientMod.Client;
const v2Mod = await import("@modelcontextprotocol/client");
ClientClass = v2Mod.Client;
SSEClientTransport = v2Mod.SSEClientTransport;
StreamableHTTPClientTransport = v2Mod.StreamableHTTPClientTransport;
usingV2 = true;
try {
// @ts-ignore — optional peerDependency; Node-only subpath
const v2StdioMod = await import("@modelcontextprotocol/client/stdio");
StdioClientTransport = v2StdioMod.StdioClientTransport;
} catch {
StdioClientTransport = null;
}
} catch {
throw new Error(
"Install @modelcontextprotocol/sdk to use MCPToolProvider: npm install @modelcontextprotocol/sdk"
);
usingV2 = false;
}

try {
// @ts-ignore — optional peerDependency
const stdioMod = await import("@modelcontextprotocol/sdk/client/stdio.js");
StdioClientTransport = stdioMod.StdioClientTransport;
} catch {
StdioClientTransport = null;
}
if (!usingV2) {
try {
// @ts-ignore — optional peerDependency
const clientMod = await import("@modelcontextprotocol/sdk/client/index.js");
ClientClass = clientMod.Client;
} catch {
throw new Error(
"Install an MCP SDK to use MCPToolProvider: npm install @modelcontextprotocol/client " +
"(supports protocol 2026-07-28 and older servers) or npm install @modelcontextprotocol/sdk (older servers only)"
);
}

try {
// @ts-ignore — optional peerDependency
const sseMod = await import("@modelcontextprotocol/sdk/client/sse.js");
SSEClientTransport = sseMod.SSEClientTransport;
} catch {
SSEClientTransport = null;
}
try {
// @ts-ignore — optional peerDependency
const stdioMod = await import("@modelcontextprotocol/sdk/client/stdio.js");
StdioClientTransport = stdioMod.StdioClientTransport;
} catch {
StdioClientTransport = null;
}

let StreamableHTTPClientTransport: any;
try {
// @ts-ignore — optional peerDependency; module exists since SDK ~1.10
const streamableMod = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
StreamableHTTPClientTransport = streamableMod.StreamableHTTPClientTransport;
} catch {
StreamableHTTPClientTransport = null;
try {
// @ts-ignore — optional peerDependency
const sseMod = await import("@modelcontextprotocol/sdk/client/sse.js");
SSEClientTransport = sseMod.SSEClientTransport;
} catch {
SSEClientTransport = null;
}

try {
// @ts-ignore — optional peerDependency; module exists since SDK ~1.10
const streamableMod = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
StreamableHTTPClientTransport = streamableMod.StreamableHTTPClientTransport;
} catch {
StreamableHTTPClientTransport = null;
}
}

const allTools: AgentTool[] = [];
Expand All @@ -167,7 +197,8 @@ export class MCPToolProvider extends AgentTools {
if (serverConfig.type === "stdio") {
if (!StdioClientTransport) {
throw new Error(
"StdioClientTransport not available — check your @modelcontextprotocol/sdk installation"
"StdioClientTransport not available — check your MCP SDK installation " +
`(${usingV2 ? "@modelcontextprotocol/client" : "@modelcontextprotocol/sdk"})`
);
}
if (!serverConfig.command) {
Expand All @@ -183,21 +214,33 @@ export class MCPToolProvider extends AgentTools {
} else if (serverConfig.type === "sse") {
if (!SSEClientTransport) {
throw new Error(
"SSEClientTransport not available — check your @modelcontextprotocol/sdk installation"
"SSEClientTransport not available — check your MCP SDK installation " +
`(${usingV2 ? "@modelcontextprotocol/client" : "@modelcontextprotocol/sdk"})`
);
}
if (!serverConfig.url) {
throw new Error(
"MCPServerConfig with type 'sse' requires a 'url' field"
);
}
transport = new SSEClientTransport(new URL(serverConfig.url), {
headers: serverConfig.headers ?? {},
});
// v2 has no top-level `headers` option; POST-channel headers go via
// requestInit (GET-stream headers tracked in #640).
transport = usingV2
? new SSEClientTransport(
new URL(serverConfig.url),
serverConfig.headers
? { requestInit: { headers: serverConfig.headers } }
: undefined
Comment on lines +231 to +233

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Forward SSE headers to the initial event-stream request

When the v2 client is selected and an SSE server requires authentication on its initial event-stream GET (including the documented Authorization-header configuration), requestInit applies these headers only to the POST channel, leaving the GET unauthenticated. Such servers reject the connection before tools can be listed, whereas the v1 path forwarded the configured headers; the v2 transport must also attach them to the event-source fetch.

Useful? React with 👍 / 👎.

)
: new SSEClientTransport(new URL(serverConfig.url), {
headers: serverConfig.headers ?? {},
});
} else if (serverConfig.type === "streamable-http") {
if (!StreamableHTTPClientTransport) {
throw new Error(
"StreamableHTTPClientTransport not available — upgrade @modelcontextprotocol/sdk (requires >=1.10)"
usingV2
? "StreamableHTTPClientTransport not available — check your @modelcontextprotocol/client installation"
: "StreamableHTTPClientTransport not available — upgrade @modelcontextprotocol/sdk (requires >=1.10)"
);
}
if (!serverConfig.url) {
Expand All @@ -217,10 +260,17 @@ export class MCPToolProvider extends AgentTools {
);
}

const client = new ClientClass(
{ name: "agent-squad-mcp-client", version: "1.0.0" },
{ capabilities: {} }
);
// mode "auto" probes server/discover (protocol 2026-07-28) and falls back
// to the legacy initialize handshake, so both server eras work.
const client = usingV2
? new ClientClass(
{ name: "agent-squad-mcp-client", version: "1.0.0" },
{ capabilities: {}, versionNegotiation: { mode: "auto" } }
)
: new ClientClass(
{ name: "agent-squad-mcp-client", version: "1.0.0" },
{ capabilities: {} }
);

await client.connect(transport);
this.clients.push(client);
Expand Down
Loading
Loading