@smithery/sdk 1.1.2 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,94 +1,53 @@
1
1
  # Smithery Typescript SDK
2
2
 
3
+ The SDK provides files for you to easily setup Smithery-compatible MCP servers and clients.
4
+
3
5
  ## Installation
4
6
 
5
7
  ```bash
6
- npm install @smithery/sdk
8
+ npm install @smithery/sdk @modelcontextprotocol/sdk
7
9
  ```
8
10
 
9
11
  ## Usage
10
12
 
11
- In this example, we'll connect to Exa search capabilities using either OpenAI or Anthropic.
12
-
13
- ```bash
14
- npm install @smithery/sdk @modelcontextprotocol/sdk
15
- ```
13
+ ### Spawning a Server
16
14
 
17
- The following code sets up the client and connects to an Exa MCP server:
15
+ Here's a minimal example of how to use the SDK to spawn an MCP server.
18
16
 
19
17
  ```typescript
20
- import { MultiClient } from "@smithery/sdk"
21
- import { OpenAIChatAdapter } from "@smithery/sdk/integrations/llm/openai"
22
- import { AnthropicChatAdapter } from "@smithery/sdk/integrations/llm/anthropic"
23
- import { createTransport } from "@smithery/sdk/transport.js"
24
- import { OpenAI } from "openai"
25
- import Anthropic from "@anthropic-ai/sdk"
26
-
27
- // Create a new connection
28
- const exaTransport = createTransport(
29
- // Replace with your deployed MCP server URL
30
- "https://your-mcp-server.example.com"
31
- )
32
-
33
- // Initialize a multi-client connection
34
- const client = new MultiClient()
35
- await client.connectAll({
36
- exa: exaTransport,
37
- // You can add more connections here...
38
- })
18
+ import { createStatelessServer } from '@smithery/sdk/server/stateless.js'
19
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
20
+
21
+ // Create your MCP server function
22
+ function createMcpServer({ sessionId, config }) {
23
+ // Create and return a server instance
24
+ // https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#core-concepts
25
+ const server = new McpServer({
26
+ name: "My App",
27
+ version: "1.0.0"
28
+ })
39
29
 
40
- // Configure and authenticate
41
- await client.clients.exa.request({
42
- method: "config",
43
- params: {
44
- config: {
45
- apiKey: process.env.EXA_API_KEY,
46
- },
47
- },
48
- })
49
- ```
30
+ // ...
31
+
32
+ return server
33
+ }
50
34
 
51
- Now you can use either OpenAI or Anthropic to interact with the tools:
35
+ // Create the stateless server using your MCP server function.
36
+ const { app } = createStatelessServer(createMcpServer)
52
37
 
53
- ```typescript
54
- // Using OpenAI
55
- const openai = new OpenAI()
56
- const openaiAdapter = new OpenAIChatAdapter(client)
57
- const openaiResponse = await openai.chat.completions.create({
58
- model: "gpt-4o-mini",
59
- messages: [{ role: "user", content: "What AI events are happening in Singapore?" }],
60
- tools: await openaiAdapter.listTools(),
38
+ // Start the server
39
+ const PORT = process.env.PORT || 8081
40
+ app.listen(PORT, () => {
41
+ console.log(`MCP server running on port ${PORT}`)
61
42
  })
62
- const openaiToolMessages = await openaiAdapter.callTool(openaiResponse)
63
43
  ```
64
44
 
65
- For more complex interactions where the LLM needs to process tool outputs and potentially make additional calls, you'll need to implement a conversation loop. Here's an example:
66
-
67
- ```typescript
68
- let messages = [
69
- {
70
- role: "user",
71
- content: "What are some AI events happening in Singapore and how many days until the next one?",
72
- },
73
- ]
74
- const adapter = new OpenAIChatAdapter(client)
75
- let isDone = false
45
+ This example:
46
+ 1. Creates a stateless server that handles MCP requests
47
+ 2. Defines a function to create MCP server instances for each session
48
+ 3. Starts the Express server on the specified port. You must listen on the PORT env var if provided for the deployment to work on Smithery.
76
49
 
77
- while (!isDone) {
78
- const response = await openai.chat.completions.create({
79
- model: "gpt-4o-mini",
80
- messages,
81
- tools: await adapter.listTools(),
82
- })
83
-
84
- // Handle tool calls
85
- const toolMessages = await adapter.callTool(response)
86
-
87
- // Append new messages
88
- messages.push(response.choices[0].message)
89
- messages.push(...toolMessages)
90
- isDone = toolMessages.length === 0
91
- }
92
- ```
50
+ #### Stateful Server
51
+ Most API integrations are stateless.
93
52
 
94
- See a full example in the [examples](./src/examples) directory.
53
+ However, if your MCP server needs to persist state between calls (i.e., remembering previous interactions in a single chat conversation), you can use the `createStatefulServer` function instead.
@@ -0,0 +1,9 @@
1
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
2
+ /**
3
+ * Creates a transport to connect to the Smithery server
4
+ * @param baseUrl The URL of the Smithery server (without trailing slash or protocol)
5
+ * @param config Config to pass to the server
6
+ * @param apiKey Optional API key for authentication
7
+ * @returns Transport
8
+ */
9
+ export declare function createTransport(baseUrl: string, config?: object, apiKey?: string): StreamableHTTPClientTransport;
@@ -0,0 +1,12 @@
1
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
2
+ import { createSmitheryUrl } from "../config.js";
3
+ /**
4
+ * Creates a transport to connect to the Smithery server
5
+ * @param baseUrl The URL of the Smithery server (without trailing slash or protocol)
6
+ * @param config Config to pass to the server
7
+ * @param apiKey Optional API key for authentication
8
+ * @returns Transport
9
+ */
10
+ export function createTransport(baseUrl, config, apiKey) {
11
+ return new StreamableHTTPClientTransport(createSmitheryUrl(`${baseUrl}`, config, apiKey));
12
+ }
package/dist/config.d.ts CHANGED
@@ -1,8 +1,15 @@
1
+ import type express from "express";
1
2
  /**
2
- * Creates a URL to connect to the Smithery MCP server
3
+ * Creates a URL to connect to the Smithery MCP server.
3
4
  * @param baseUrl The base URL of the Smithery server
4
5
  * @param config Optional configuration object
5
6
  * @param apiKey API key for authentication. Required if using Smithery.
6
- * @returns A URL object with properly encoded parameters
7
+ * @returns A URL object with properly encoded parameters. Example: https://server.smithery.ai/{namespace}/mcp?config=BASE64_ENCODED_CONFIG&api_key=API_KEY
7
8
  */
8
9
  export declare function createSmitheryUrl(baseUrl: string, config?: object, apiKey?: string): URL;
10
+ /**
11
+ * Parses the config from an express request by checking the query parameter "config".
12
+ * @param req The express request
13
+ * @returns The config
14
+ */
15
+ export declare function parseExpressRequestConfig(req: express.Request): Record<string, unknown>;
package/dist/config.js CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Creates a URL to connect to the Smithery MCP server
2
+ * Creates a URL to connect to the Smithery MCP server.
3
3
  * @param baseUrl The base URL of the Smithery server
4
4
  * @param config Optional configuration object
5
5
  * @param apiKey API key for authentication. Required if using Smithery.
6
- * @returns A URL object with properly encoded parameters
6
+ * @returns A URL object with properly encoded parameters. Example: https://server.smithery.ai/{namespace}/mcp?config=BASE64_ENCODED_CONFIG&api_key=API_KEY
7
7
  */
8
8
  export function createSmitheryUrl(baseUrl, config, apiKey) {
9
9
  const url = new URL(`${baseUrl}/mcp`);
@@ -18,3 +18,11 @@ export function createSmitheryUrl(baseUrl, config, apiKey) {
18
18
  }
19
19
  return url;
20
20
  }
21
+ /**
22
+ * Parses the config from an express request by checking the query parameter "config".
23
+ * @param req The express request
24
+ * @returns The config
25
+ */
26
+ export function parseExpressRequestConfig(req) {
27
+ return JSON.parse(Buffer.from(req.query.config, "base64").toString());
28
+ }
@@ -0,0 +1,18 @@
1
+ import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ /**
3
+ * Arguments when we create a new instance of your server
4
+ */
5
+ export interface CreateServerArg {
6
+ sessionId: string;
7
+ config: Record<string, unknown>;
8
+ }
9
+ export type CreateServerFn = (arg: CreateServerArg) => Server;
10
+ /**
11
+ * Creates a stateful server for handling MCP requests.
12
+ * For every new session, we invoke createMcpServer to create a new instance of the server.
13
+ * @param createMcpServer Function to create an MCP server
14
+ * @returns Express app
15
+ */
16
+ export declare function createStatefulServer(createMcpServer: CreateServerFn): {
17
+ app: import("express-serve-static-core").Express;
18
+ };
@@ -0,0 +1,93 @@
1
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
3
+ import express from "express";
4
+ import { randomUUID } from "node:crypto";
5
+ import { parseExpressRequestConfig } from "../config.js";
6
+ /**
7
+ * Creates a stateful server for handling MCP requests.
8
+ * For every new session, we invoke createMcpServer to create a new instance of the server.
9
+ * @param createMcpServer Function to create an MCP server
10
+ * @returns Express app
11
+ */
12
+ export function createStatefulServer(createMcpServer) {
13
+ const app = express();
14
+ app.use(express.json());
15
+ // Map to store transports by session ID
16
+ const transports = {};
17
+ // Handle POST requests for client-to-server communication
18
+ app.post("/mcp", async (req, res) => {
19
+ // Check for existing session ID
20
+ const sessionId = req.headers["mcp-session-id"];
21
+ let transport;
22
+ if (sessionId && transports[sessionId]) {
23
+ // Reuse existing transport
24
+ transport = transports[sessionId];
25
+ }
26
+ else if (!sessionId && isInitializeRequest(req.body)) {
27
+ // New initialization request
28
+ const newSessionId = randomUUID();
29
+ transport = new StreamableHTTPServerTransport({
30
+ sessionIdGenerator: () => newSessionId,
31
+ onsessioninitialized: (sessionId) => {
32
+ // Store the transport by session ID
33
+ transports[sessionId] = transport;
34
+ },
35
+ });
36
+ // Clean up transport when closed
37
+ transport.onclose = () => {
38
+ if (transport.sessionId) {
39
+ delete transports[transport.sessionId];
40
+ }
41
+ };
42
+ try {
43
+ const config = parseExpressRequestConfig(req);
44
+ const server = createMcpServer({
45
+ sessionId: newSessionId,
46
+ config,
47
+ });
48
+ // Connect to the MCP server
49
+ await server.connect(transport);
50
+ }
51
+ catch (error) {
52
+ res.status(400).json({
53
+ jsonrpc: "2.0",
54
+ error: {
55
+ code: -32000,
56
+ message: "Bad Request: Invalid configuration",
57
+ },
58
+ id: null,
59
+ });
60
+ return;
61
+ }
62
+ }
63
+ else {
64
+ // Invalid request
65
+ res.status(400).json({
66
+ jsonrpc: "2.0",
67
+ error: {
68
+ code: -32000,
69
+ message: "Bad Request: No valid session ID provided",
70
+ },
71
+ id: null,
72
+ });
73
+ return;
74
+ }
75
+ // Handle the request
76
+ await transport.handleRequest(req, res, req.body);
77
+ });
78
+ // Reusable handler for GET and DELETE requests
79
+ const handleSessionRequest = async (req, res) => {
80
+ const sessionId = req.headers["mcp-session-id"];
81
+ if (!sessionId || !transports[sessionId]) {
82
+ res.status(400).send("Invalid or missing session ID");
83
+ return;
84
+ }
85
+ const transport = transports[sessionId];
86
+ await transport.handleRequest(req, res);
87
+ };
88
+ // Handle GET requests for server-to-client notifications via SSE
89
+ app.get("/mcp", handleSessionRequest);
90
+ // Handle DELETE requests for session termination
91
+ app.delete("/mcp", handleSessionRequest);
92
+ return { app };
93
+ }
@@ -0,0 +1,17 @@
1
+ import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ /**
3
+ * Arguments when we create a new instance of your server
4
+ */
5
+ export interface CreateServerArg {
6
+ config: Record<string, unknown>;
7
+ }
8
+ export type CreateServerFn = (arg: CreateServerArg) => Server;
9
+ /**
10
+ * Creates a stateless server for handling MCP requests
11
+ * In stateless mode, each request creates a new server and transport instance
12
+ * @param createMcpServer Function to create an MCP server
13
+ * @returns Express app
14
+ */
15
+ export declare function createStatelessServer(createMcpServer: CreateServerFn): {
16
+ app: import("express-serve-static-core").Express;
17
+ };
@@ -0,0 +1,89 @@
1
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2
+ import express from "express";
3
+ import { parseExpressRequestConfig } from "../config.js";
4
+ /**
5
+ * Creates a stateless server for handling MCP requests
6
+ * In stateless mode, each request creates a new server and transport instance
7
+ * @param createMcpServer Function to create an MCP server
8
+ * @returns Express app
9
+ */
10
+ export function createStatelessServer(createMcpServer) {
11
+ const app = express();
12
+ app.use(express.json());
13
+ app.post("/mcp", async (req, res) => {
14
+ // In stateless mode, create a new instance of transport and server for each request
15
+ // to ensure complete isolation. A single instance would cause request ID collisions
16
+ // when multiple clients connect concurrently.
17
+ try {
18
+ // Parse base64 encoded config from URL query parameter if present
19
+ let config = {};
20
+ if (req.query.config) {
21
+ try {
22
+ config = parseExpressRequestConfig(req);
23
+ }
24
+ catch (configError) {
25
+ res.status(400).json({
26
+ jsonrpc: "2.0",
27
+ error: {
28
+ code: -32000,
29
+ message: "Bad Request: Invalid configuration",
30
+ },
31
+ id: null,
32
+ });
33
+ return;
34
+ }
35
+ }
36
+ // Create a new server instance with config
37
+ const server = createMcpServer({ config });
38
+ // Create a new transport instance
39
+ const transport = new StreamableHTTPServerTransport({
40
+ sessionIdGenerator: undefined,
41
+ });
42
+ // Clean up resources when the request ends
43
+ res.on("close", () => {
44
+ transport.close();
45
+ server.close();
46
+ });
47
+ // Connect the server to the transport
48
+ await server.connect(transport);
49
+ // Handle the incoming request
50
+ await transport.handleRequest(req, res, req.body);
51
+ }
52
+ catch (error) {
53
+ console.error("Error handling MCP request:", error);
54
+ if (!res.headersSent) {
55
+ res.status(500).json({
56
+ jsonrpc: "2.0",
57
+ error: {
58
+ code: -32603,
59
+ message: "Internal server error",
60
+ },
61
+ id: null,
62
+ });
63
+ }
64
+ }
65
+ });
66
+ app.get("/mcp", async (req, res) => {
67
+ console.log("Received GET MCP request");
68
+ res.writeHead(405).end(JSON.stringify({
69
+ jsonrpc: "2.0",
70
+ error: {
71
+ code: -32000,
72
+ message: "Method not allowed in stateless mode",
73
+ },
74
+ id: null,
75
+ }));
76
+ });
77
+ app.delete("/mcp", async (req, res) => {
78
+ console.log("Received DELETE MCP request");
79
+ res.writeHead(405).end(JSON.stringify({
80
+ jsonrpc: "2.0",
81
+ error: {
82
+ code: -32000,
83
+ message: "Method not allowed in stateless mode",
84
+ },
85
+ id: null,
86
+ }));
87
+ });
88
+ return { app };
89
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@smithery/sdk",
3
- "version": "1.1.2",
4
- "description": "Connect language models to Model Context Protocols",
3
+ "version": "1.2.1",
4
+ "description": "SDK to develop with Smithery",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -19,17 +19,16 @@
19
19
  "check": "npx @biomejs/biome check --write --unsafe",
20
20
  "link": "npm link -ws --include-workspace-root"
21
21
  },
22
- "license": "GPL-3.0-only",
22
+ "license": "MIT",
23
23
  "dependencies": {
24
24
  "@anthropic-ai/sdk": "^0.32.1",
25
- "@icons-pack/react-simple-icons": "^10.2.0",
26
- "@modelcontextprotocol/sdk": "^1.5.0",
25
+ "@modelcontextprotocol/sdk": "^1.10.2",
26
+ "express": "^5.1.0",
27
27
  "openai": "^4.0.0",
28
28
  "uuid": "^11.0.3"
29
29
  },
30
30
  "devDependencies": {
31
- "@smithery/mcp-e2b": "*",
32
- "@smithery/mcp-exa": "*",
31
+ "@types/express": "^5.0.1",
33
32
  "@types/node": "^20.0.0",
34
33
  "@types/uuid": "^9.0.7",
35
34
  "dotenv": "^16.4.7",
package/dist/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export { createSmitheryUrl } from "./config.js";
2
- export { wrapErrorAdapter } from "./integrations/error-adapter.js";
3
- export { AnthropicChatAdapter } from "./integrations/llm/anthropic.js";
4
- export { OpenAIChatAdapter } from "./integrations/llm/openai.js";
5
- export { MultiClient } from "./multi-client.js";
6
- export { createTransport } from "./transport.js";
package/dist/index.js DELETED
@@ -1,6 +0,0 @@
1
- export { createSmitheryUrl } from "./config.js";
2
- export { wrapErrorAdapter } from "./integrations/error-adapter.js";
3
- export { AnthropicChatAdapter } from "./integrations/llm/anthropic.js";
4
- export { OpenAIChatAdapter } from "./integrations/llm/openai.js";
5
- export { MultiClient } from "./multi-client.js";
6
- export { createTransport } from "./transport.js";
@@ -1,185 +0,0 @@
1
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
- import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
4
- import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
5
- import { type CallToolRequest, CallToolResultSchema, type CompatibilityCallToolResultSchema, type ListToolsRequest, type Tool } from "@modelcontextprotocol/sdk/types.js";
6
- interface ClientInfo {
7
- name: string;
8
- version: string;
9
- }
10
- /**
11
- * A client that connects to multiple MCPs and provides a unified interface for
12
- * accessing their tools, treating them as a single MCP.
13
- */
14
- export declare class MultiClient implements Pick<Client, "callTool" | "listTools" | "close"> {
15
- private client_capabilities;
16
- clients: Record<string, Client>;
17
- client_info: ClientInfo;
18
- constructor(client_info?: ClientInfo, client_capabilities?: {
19
- capabilities: Record<string, unknown>;
20
- });
21
- /**
22
- * Connects to a collection of transport or servers.
23
- */
24
- connectAll(transports: Record<string, Transport | Server>): Promise<void>;
25
- /**
26
- * Maps a tool name to a namespace-specific name to avoid conflicts.
27
- */
28
- toolNameMapper: (namespace: string, tool: Tool) => string;
29
- toolNameUnmapper: (fullToolName: string) => {
30
- namespace: string;
31
- toolName: string;
32
- };
33
- /**
34
- * List all tools available from all MCPs, ensuring each tool is namespaced.
35
- * @param params - Optional parameters for the request.
36
- * @param options - Optional options for the request.
37
- * @returns A promise that resolves to an array of tools.
38
- */
39
- listTools(params?: ListToolsRequest["params"], options?: RequestOptions): Promise<{
40
- tools: {
41
- [x: string]: unknown;
42
- name: string;
43
- inputSchema: {
44
- [x: string]: unknown;
45
- type: "object";
46
- properties?: {
47
- [x: string]: unknown;
48
- } | undefined;
49
- };
50
- description?: string | undefined;
51
- }[];
52
- }>;
53
- callTool(params: CallToolRequest["params"], resultSchema?: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema, options?: RequestOptions): Promise<import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
54
- _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
55
- }, {
56
- content: import("zod").ZodArray<import("zod").ZodUnion<[import("zod").ZodObject<{
57
- type: import("zod").ZodLiteral<"text">;
58
- text: import("zod").ZodString;
59
- }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
60
- type: import("zod").ZodLiteral<"text">;
61
- text: import("zod").ZodString;
62
- }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
63
- type: import("zod").ZodLiteral<"text">;
64
- text: import("zod").ZodString;
65
- }, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<{
66
- type: import("zod").ZodLiteral<"image">;
67
- data: import("zod").ZodString;
68
- mimeType: import("zod").ZodString;
69
- }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
70
- type: import("zod").ZodLiteral<"image">;
71
- data: import("zod").ZodString;
72
- mimeType: import("zod").ZodString;
73
- }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
74
- type: import("zod").ZodLiteral<"image">;
75
- data: import("zod").ZodString;
76
- mimeType: import("zod").ZodString;
77
- }, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<{
78
- type: import("zod").ZodLiteral<"resource">;
79
- resource: import("zod").ZodUnion<[import("zod").ZodObject<import("zod").objectUtil.extendShape<{
80
- uri: import("zod").ZodString;
81
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
82
- }, {
83
- text: import("zod").ZodString;
84
- }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
85
- uri: import("zod").ZodString;
86
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
87
- }, {
88
- text: import("zod").ZodString;
89
- }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
90
- uri: import("zod").ZodString;
91
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
92
- }, {
93
- text: import("zod").ZodString;
94
- }>, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<import("zod").objectUtil.extendShape<{
95
- uri: import("zod").ZodString;
96
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
97
- }, {
98
- blob: import("zod").ZodString;
99
- }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
100
- uri: import("zod").ZodString;
101
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
102
- }, {
103
- blob: import("zod").ZodString;
104
- }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
105
- uri: import("zod").ZodString;
106
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
107
- }, {
108
- blob: import("zod").ZodString;
109
- }>, import("zod").ZodTypeAny, "passthrough">>]>;
110
- }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
111
- type: import("zod").ZodLiteral<"resource">;
112
- resource: import("zod").ZodUnion<[import("zod").ZodObject<import("zod").objectUtil.extendShape<{
113
- uri: import("zod").ZodString;
114
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
115
- }, {
116
- text: import("zod").ZodString;
117
- }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
118
- uri: import("zod").ZodString;
119
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
120
- }, {
121
- text: import("zod").ZodString;
122
- }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
123
- uri: import("zod").ZodString;
124
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
125
- }, {
126
- text: import("zod").ZodString;
127
- }>, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<import("zod").objectUtil.extendShape<{
128
- uri: import("zod").ZodString;
129
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
130
- }, {
131
- blob: import("zod").ZodString;
132
- }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
133
- uri: import("zod").ZodString;
134
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
135
- }, {
136
- blob: import("zod").ZodString;
137
- }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
138
- uri: import("zod").ZodString;
139
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
140
- }, {
141
- blob: import("zod").ZodString;
142
- }>, import("zod").ZodTypeAny, "passthrough">>]>;
143
- }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
144
- type: import("zod").ZodLiteral<"resource">;
145
- resource: import("zod").ZodUnion<[import("zod").ZodObject<import("zod").objectUtil.extendShape<{
146
- uri: import("zod").ZodString;
147
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
148
- }, {
149
- text: import("zod").ZodString;
150
- }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
151
- uri: import("zod").ZodString;
152
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
153
- }, {
154
- text: import("zod").ZodString;
155
- }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
156
- uri: import("zod").ZodString;
157
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
158
- }, {
159
- text: import("zod").ZodString;
160
- }>, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<import("zod").objectUtil.extendShape<{
161
- uri: import("zod").ZodString;
162
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
163
- }, {
164
- blob: import("zod").ZodString;
165
- }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
166
- uri: import("zod").ZodString;
167
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
168
- }, {
169
- blob: import("zod").ZodString;
170
- }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
171
- uri: import("zod").ZodString;
172
- mimeType: import("zod").ZodOptional<import("zod").ZodString>;
173
- }, {
174
- blob: import("zod").ZodString;
175
- }>, import("zod").ZodTypeAny, "passthrough">>]>;
176
- }, import("zod").ZodTypeAny, "passthrough">>]>, "many">;
177
- isError: import("zod").ZodOptional<import("zod").ZodDefault<import("zod").ZodBoolean>>;
178
- }>, import("zod").ZodTypeAny, "passthrough"> | import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
179
- _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
180
- }, {
181
- toolResult: import("zod").ZodUnknown;
182
- }>, import("zod").ZodTypeAny, "passthrough">>;
183
- close(): Promise<void>;
184
- }
185
- export {};
@@ -1,84 +0,0 @@
1
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
- import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
3
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
- import { CallToolResultSchema, } from "@modelcontextprotocol/sdk/types.js";
5
- /**
6
- * A client that connects to multiple MCPs and provides a unified interface for
7
- * accessing their tools, treating them as a single MCP.
8
- */
9
- export class MultiClient {
10
- constructor(client_info, client_capabilities = {
11
- capabilities: {},
12
- }) {
13
- this.client_capabilities = client_capabilities;
14
- this.clients = {};
15
- /**
16
- * Maps a tool name to a namespace-specific name to avoid conflicts.
17
- */
18
- this.toolNameMapper = (namespace, tool) => {
19
- return `${namespace}_${tool.name}`;
20
- };
21
- this.toolNameUnmapper = (fullToolName) => {
22
- const namespace = fullToolName.split("_")[0];
23
- const toolName = fullToolName.split("_").slice(1).join("_");
24
- return { namespace, toolName };
25
- };
26
- this.client_info = client_info || {
27
- name: "MultiClient",
28
- version: "1.0.0",
29
- };
30
- }
31
- /**
32
- * Connects to a collection of transport or servers.
33
- */
34
- async connectAll(transports) {
35
- await Promise.all(Object.entries(transports).map(async ([namespace, transport]) => {
36
- const client = new Client({
37
- name: this.client_info.name,
38
- version: "1.0.0",
39
- }, this.client_capabilities);
40
- if (transport instanceof Server) {
41
- const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
42
- await transport.connect(serverTransport);
43
- await client.connect(clientTransport);
44
- }
45
- else {
46
- await client.connect(transport);
47
- }
48
- this.clients[namespace] = client;
49
- }));
50
- }
51
- /**
52
- * List all tools available from all MCPs, ensuring each tool is namespaced.
53
- * @param params - Optional parameters for the request.
54
- * @param options - Optional options for the request.
55
- * @returns A promise that resolves to an array of tools.
56
- */
57
- async listTools(params, options) {
58
- const tools = (await Promise.all(Object.entries(this.clients).map(async ([namespace, mcp]) => {
59
- const capabilities = mcp.getServerCapabilities();
60
- if (!capabilities?.tools)
61
- return [];
62
- const response = await mcp.listTools(params, options);
63
- return response.tools.map((tool) => ({
64
- ...tool,
65
- name: this.toolNameMapper(namespace, tool),
66
- }));
67
- }))).flat();
68
- return { tools };
69
- }
70
- async callTool(params, resultSchema = CallToolResultSchema, options) {
71
- const { namespace, toolName } = this.toolNameUnmapper(params.name);
72
- const mcp = this.clients[namespace];
73
- if (!mcp) {
74
- throw new Error(`MCP tool ${namespace} not found`);
75
- }
76
- return mcp.callTool({
77
- name: toolName,
78
- arguments: params.arguments,
79
- }, resultSchema, options);
80
- }
81
- async close() {
82
- await Promise.all(Object.values(this.clients).map((mcp) => mcp.close()));
83
- }
84
- }
@@ -1,9 +0,0 @@
1
- import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
2
- /**
3
- * Creates a transport to connect to the Smithery server
4
- * @param smitheryServerUrl The URL of the Smithery server (without trailing slash or protocol)
5
- * @param config Config to pass to the server
6
- * @param apiKey Optional API key for authentication
7
- * @returns Transport
8
- */
9
- export declare function createTransport(smitheryServerUrl: string, config?: object, apiKey?: string): WebSocketClientTransport;
package/dist/transport.js DELETED
@@ -1,12 +0,0 @@
1
- import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
2
- import { createSmitheryUrl } from "./config.js";
3
- /**
4
- * Creates a transport to connect to the Smithery server
5
- * @param smitheryServerUrl The URL of the Smithery server (without trailing slash or protocol)
6
- * @param config Config to pass to the server
7
- * @param apiKey Optional API key for authentication
8
- * @returns Transport
9
- */
10
- export function createTransport(smitheryServerUrl, config, apiKey) {
11
- return new WebSocketClientTransport(createSmitheryUrl(`${smitheryServerUrl}/ws`, config, apiKey));
12
- }
package/dist/types.d.ts DELETED
@@ -1,30 +0,0 @@
1
- import { z } from "zod";
2
- export declare const ToolSchema: z.ZodObject<{
3
- name: z.ZodString;
4
- description: z.ZodOptional<z.ZodString>;
5
- inputSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
6
- }, "strip", z.ZodTypeAny, {
7
- name: string;
8
- inputSchema: Record<string, unknown>;
9
- description?: string | undefined;
10
- }, {
11
- name: string;
12
- inputSchema: Record<string, unknown>;
13
- description?: string | undefined;
14
- }>;
15
- export declare const ToolsSchema: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodObject<{
16
- name: z.ZodString;
17
- description: z.ZodOptional<z.ZodString>;
18
- inputSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
19
- }, "strip", z.ZodTypeAny, {
20
- name: string;
21
- inputSchema: Record<string, unknown>;
22
- description?: string | undefined;
23
- }, {
24
- name: string;
25
- inputSchema: Record<string, unknown>;
26
- description?: string | undefined;
27
- }>, "many">>;
28
- export interface Tool extends z.infer<typeof ToolSchema> {
29
- }
30
- export type Tools = z.infer<typeof ToolsSchema>;
package/dist/types.js DELETED
@@ -1,7 +0,0 @@
1
- import { z } from "zod";
2
- export const ToolSchema = z.object({
3
- name: z.string(),
4
- description: z.string().optional(),
5
- inputSchema: z.record(z.unknown()),
6
- });
7
- export const ToolsSchema = z.record(z.array(ToolSchema));