@smithery/sdk 0.0.4 → 0.0.7

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
@@ -7,8 +7,8 @@ Smithery is a Typescript framework that easily connects language models (LLMs) t
7
7
  **Key Features**
8
8
 
9
9
  - Connect to multiple MCPs with a single client
10
- - Tool schema is handled automatically for the LLM
11
- - Supports LLM reasoning through multiple tool calls
10
+ - Adapters to transform MCP resposnes for OpenAI and Anthropic clients
11
+ - Supports chaining tool calls until LLM completes
12
12
 
13
13
  # Quickstart
14
14
 
@@ -29,34 +29,40 @@ npm install @smithery/mcp-exa
29
29
  The following code sets up OpenAI and connects to an Exa MCP server. In this case, we're running the server locally within the same process, so it's just a simple passthrough.
30
30
 
31
31
  ```typescript
32
- import { Connection } from "@smithery/sdk"
33
- import { OpenAIHandler } from "@smithery/sdk/openai"
32
+ import { MultiClient } from "@smithery/sdk"
33
+ import { OpenAIChatAdapter } from "@smithery/sdk/integrations/llm/openai"
34
34
  import * as exa from "@smithery/mcp-exa"
35
35
  import { OpenAI } from "openai"
36
+ import { createTransport } from "@smithery/sdk/registry"
36
37
 
37
38
  const openai = new OpenAI()
38
- const connection = await Connection.connect({
39
- exa: {
40
- server: exa.createServer({
41
- apiKey: process.env.EXA_API_KEY,
42
- }),
43
- },
39
+ const exaServer = exa.createServer({
40
+ apiKey: process.env.EXA_API_KEY,
41
+ })
42
+
43
+ const sequentialThinking = await createTransport(
44
+ "@modelcontextprotocol/server-sequential-thinking",
45
+ )
46
+ const client = new MultiClient()
47
+ await client.connectAll({
48
+ exa: exaServer,
49
+ sequentialThinking: sequentialThinking,
44
50
  })
45
51
  ```
46
52
 
47
53
  Now you can make your LLM aware of the available tools from Exa.
48
54
 
49
55
  ```typescript
50
- // Create a handler
51
- const handler = new OpenAIHandler(connection)
56
+ // Create an adapter
57
+ const adapter = new OpenAIChatAdapter(client)
52
58
  const response = await openai.chat.completions.create({
53
59
  model: "gpt-4o-mini",
54
60
  messages: [{ role: "user", content: "In 2024, did OpenAI release GPT-5?" }],
55
61
  // Pass the tools to OpenAI call
56
- tools: await handler.listTools(),
62
+ tools: await adapter.listTools(),
57
63
  })
58
64
  // Obtain the tool outputs as new messages
59
- const toolMessages = await handler.call(response)
65
+ const toolMessages = await adapter.callTool(response)
60
66
  ```
61
67
 
62
68
  Using this, you can easily enable your LLM to call tools and obtain the results.
@@ -68,23 +74,23 @@ In this case, you have to loop your LLM call and update your messages until ther
68
74
  Example:
69
75
 
70
76
  ```typescript
71
- let messages: ChatCompletionMessageParam[] = [
77
+ let messages = [
72
78
  {
73
79
  role: "user",
74
80
  content:
75
81
  "Deduce Obama's age in number of days. It's November 28, 2024 today. Search to ensure correctness.",
76
82
  },
77
83
  ]
78
- const handler = new OpenAIHandler(connection)
84
+ const adapter = new OpenAIChatAdapter(client)
79
85
 
80
86
  while (!isDone) {
81
87
  const response = await openai.chat.completions.create({
82
88
  model: "gpt-4o-mini",
83
89
  messages,
84
- tools: await handler.listTools(),
90
+ tools: await adapter.listTools(),
85
91
  })
86
92
  // Handle tool calls
87
- const toolMessages = await handler.call(response)
93
+ const toolMessages = await adapter.callTool(response)
88
94
 
89
95
  // Append new messages
90
96
  messages.push(response.choices[0].message)
@@ -112,15 +118,4 @@ Patch the global EventSource object:
112
118
 
113
119
  ```typescript
114
120
  import EventSource from "eventsource"
115
- global.EventSource = EventSource as any
116
- ```
117
-
118
- # Contributing
119
- Developing locally:
120
- ```sh
121
- npm link -ws --include-workspace-root
122
- ```
123
- Version bumping:
124
- ```sh
125
- npm version patch -ws --include-workspace-root
126
- ```
121
+ global.EventSource = EventSource as any
@@ -0,0 +1 @@
1
+ export declare const REGISTRY_URL = "https://registry.smithery.ai";
package/dist/config.js ADDED
@@ -0,0 +1 @@
1
+ export const REGISTRY_URL = "https://registry.smithery.ai";
package/dist/index.d.ts CHANGED
@@ -1,19 +1,58 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
3
  import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
- import { type MCPConfig, type Tools } from "./types.js";
4
- export { AnthropicHandler } from "./integrations/llm/anthropic.js";
5
- export { OpenAIHandler } from "./integrations/llm/openai.js";
6
- export type { MCPConfig, Tools } from "./types.js";
7
- export declare class Connection {
8
- mcps: Map<string, Client>;
9
- static connect(config: MCPConfig): Promise<Connection>;
10
- private toolsCache;
11
- listTools(): Promise<Tools>;
12
- callTools(calls: {
13
- mcp: string;
14
- name: string;
15
- arguments: any;
16
- }[], options?: RequestOptions): Promise<(import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
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: import("zod").objectOutputType<{
41
+ name: import("zod").ZodString;
42
+ description: import("zod").ZodOptional<import("zod").ZodString>;
43
+ inputSchema: import("zod").ZodObject<{
44
+ type: import("zod").ZodLiteral<"object">;
45
+ properties: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
46
+ }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
47
+ type: import("zod").ZodLiteral<"object">;
48
+ properties: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
49
+ }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
50
+ type: import("zod").ZodLiteral<"object">;
51
+ properties: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
52
+ }, import("zod").ZodTypeAny, "passthrough">>;
53
+ }, import("zod").ZodTypeAny, "passthrough">[];
54
+ }>;
55
+ callTool(params: CallToolRequest["params"], resultSchema?: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema, options?: RequestOptions): Promise<import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
17
56
  _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">>>;
18
57
  }, {
19
58
  content: import("zod").ZodArray<import("zod").ZodUnion<[import("zod").ZodObject<{
@@ -142,6 +181,7 @@ export declare class Connection {
142
181
  _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">>>;
143
182
  }, {
144
183
  toolResult: import("zod").ZodUnknown;
145
- }>, import("zod").ZodTypeAny, "passthrough">)[]>;
184
+ }>, import("zod").ZodTypeAny, "passthrough">>;
146
185
  close(): Promise<void>;
147
186
  }
187
+ export {};
package/dist/index.js CHANGED
@@ -1,75 +1,84 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
3
2
  import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
4
  import { CallToolResultSchema, } from "@modelcontextprotocol/sdk/types.js";
5
- import { v4 as uuidv4 } from "uuid";
6
- import { isServerConfig, isURIConfig, isWrappedServerConfig, isStdioConfig, } from "./types.js";
7
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
8
- export { AnthropicHandler } from "./integrations/llm/anthropic.js";
9
- export { OpenAIHandler } from "./integrations/llm/openai.js";
10
- export class Connection {
11
- constructor() {
12
- this.mcps = new Map();
13
- // TODO: Invalidate cache on tool change
14
- this.toolsCache = null;
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
+ };
15
30
  }
16
- static async connect(config) {
17
- const connection = new Connection();
18
- await Promise.all(Object.entries(config).map(async ([mcpName, mcpConfig]) => {
19
- const mcp = new Client({
20
- name: uuidv4(),
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,
21
38
  version: "1.0.0",
22
- }, {
23
- capabilities: {},
24
- });
25
- if (isURIConfig(mcpConfig)) {
26
- // For URI configs, connect using SSE (Server-Sent Events) transport
27
- await mcp.connect(new SSEClientTransport(new URL(mcpConfig.url)));
28
- }
29
- else if (isStdioConfig(mcpConfig)) {
30
- // For pipe configs, spawn the server locally, then connect using using Stdio transport
31
- await mcp.connect(new StdioClientTransport(mcpConfig.stdio));
32
- }
33
- else if (isServerConfig(mcpConfig) ||
34
- isWrappedServerConfig(mcpConfig)) {
35
- // Handle both direct Server instances and wrapped {server: Server} configs
36
- const server = isWrappedServerConfig(mcpConfig)
37
- ? mcpConfig.server
38
- : mcpConfig;
39
- // Create paired transports for in-memory communication between client and server
39
+ }, this.client_capabilities);
40
+ if (transport instanceof Server) {
40
41
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
41
- await server.connect(serverTransport);
42
- await mcp.connect(clientTransport);
42
+ await transport.connect(serverTransport);
43
+ await client.connect(clientTransport);
43
44
  }
44
- connection.mcps.set(mcpName, mcp);
45
+ else {
46
+ await client.connect(transport);
47
+ }
48
+ this.clients[namespace] = client;
45
49
  }));
46
- return connection;
47
50
  }
48
- async listTools() {
49
- if (this.toolsCache === null) {
50
- this.toolsCache = (await Promise.all(Array.from(this.mcps.entries()).map(async ([name, mcp]) => {
51
- const capabilities = mcp.getServerCapabilities();
52
- if (!capabilities?.tools)
53
- return [];
54
- const response = await mcp.listTools();
55
- return { [name]: response.tools };
56
- }))).reduce((acc, curr) => Object.assign(acc, curr), {});
57
- }
58
- return this.toolsCache;
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 };
59
69
  }
60
- async callTools(calls, options) {
61
- return await Promise.all(calls.map(async (call) => {
62
- const mcp = this.mcps.get(call.mcp);
63
- if (!mcp) {
64
- throw new Error(`MCP tool ${call.mcp} not found`);
65
- }
66
- return mcp.callTool({
67
- name: call.name,
68
- arguments: call.arguments,
69
- }, CallToolResultSchema, options);
70
- }));
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);
71
80
  }
72
81
  async close() {
73
- await Promise.all(Array.from(this.mcps.values()).map((mcp) => mcp.close()));
82
+ await Promise.all(Object.values(this.clients).map((mcp) => mcp.close()));
74
83
  }
75
84
  }
@@ -1,11 +1,12 @@
1
1
  import type { Message, MessageParam, Tool } from "@anthropic-ai/sdk/resources/index.js";
2
+ import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
3
  import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
- import type { Connection } from "../../index.js";
4
- import type { Tools } from "../../types.js";
5
- export declare class AnthropicHandler {
6
- private connection;
7
- constructor(connection: Connection);
4
+ /**
5
+ * Adapt an MCP client so it works seamlessly with Anthropic messages
6
+ */
7
+ export declare class AnthropicChatAdapter {
8
+ private client;
9
+ constructor(client: Pick<Client, "callTool" | "listTools">);
8
10
  listTools(): Promise<Tool[]>;
9
- format(tools: Tools): Tool[];
10
- call(response: Message, options?: RequestOptions): Promise<MessageParam[]>;
11
+ callTool(response: Message, options?: RequestOptions): Promise<MessageParam[]>;
11
12
  }
@@ -1,19 +1,21 @@
1
- export class AnthropicHandler {
2
- constructor(connection) {
3
- this.connection = connection;
1
+ import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";
2
+ /**
3
+ * Adapt an MCP client so it works seamlessly with Anthropic messages
4
+ */
5
+ export class AnthropicChatAdapter {
6
+ constructor(client) {
7
+ this.client = client;
4
8
  }
5
9
  async listTools() {
6
- return this.format(await this.connection.listTools());
7
- }
8
- format(tools) {
9
- return Object.entries(tools).flatMap(([mcpName, tools]) => tools.map((tool) => ({
10
- name: `${mcpName}_${tool.name}`,
11
- input_schema: { ...tool.inputSchema, type: "object" },
10
+ const toolResult = await this.client.listTools();
11
+ return toolResult.tools.map((tool) => ({
12
+ name: tool.name,
12
13
  description: tool.description,
13
- })));
14
+ input_schema: tool.inputSchema,
15
+ }));
14
16
  }
15
17
  // TODO: Support streaming
16
- async call(response, options) {
18
+ async callTool(response, options) {
17
19
  const content = response.content;
18
20
  if (!content || content.length === 0) {
19
21
  return [];
@@ -23,16 +25,13 @@ export class AnthropicHandler {
23
25
  if (toolCalls.length === 0) {
24
26
  return [];
25
27
  }
26
- const results = await this.connection.callTools(toolCalls.map((toolCall) => {
27
- const splitPoint = toolCall.name.indexOf("_");
28
- const mcp = toolCall.name.slice(0, splitPoint);
29
- const name = toolCall.name.slice(splitPoint + 1);
30
- return {
31
- mcp,
32
- name,
28
+ // Run parallel tool call
29
+ const results = await Promise.all(toolCalls.map(async (toolCall) => {
30
+ return await this.client.callTool({
31
+ name: toolCall.name,
33
32
  arguments: toolCall.input,
34
- };
35
- }), options);
33
+ }, CallToolResultSchema, options);
34
+ }));
36
35
  return [
37
36
  {
38
37
  role: "user",
@@ -1,12 +1,19 @@
1
1
  import type { OpenAI } from "openai";
2
2
  import type { ChatCompletionTool, ChatCompletionToolMessageParam } from "openai/resources/index.js";
3
3
  import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
4
- import type { Connection } from "../../index.js";
5
- import type { Tools } from "../../types.js";
6
- export declare class OpenAIHandler {
7
- private connection;
8
- constructor(connection: Connection);
9
- listTools(strict?: boolean): Promise<ChatCompletionTool[]>;
10
- format(tools: Tools, strict?: boolean): ChatCompletionTool[];
11
- call(response: OpenAI.Chat.Completions.ChatCompletion, options?: RequestOptions): Promise<ChatCompletionToolMessageParam[]>;
4
+ import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
+ interface OpenAIAdapterOptions {
6
+ strict?: boolean;
7
+ truncateDescriptionLength?: number;
12
8
  }
9
+ /**
10
+ * Adapt an MCP client so it works seamlessly with OpenAI chat completions
11
+ */
12
+ export declare class OpenAIChatAdapter {
13
+ private client;
14
+ private options;
15
+ constructor(client: Pick<Client, "callTool" | "listTools">, options?: OpenAIAdapterOptions);
16
+ listTools(): Promise<ChatCompletionTool[]>;
17
+ callTool(response: OpenAI.Chat.Completions.ChatCompletion, options?: RequestOptions): Promise<ChatCompletionToolMessageParam[]>;
18
+ }
19
+ export {};
@@ -1,42 +1,44 @@
1
- export class OpenAIHandler {
2
- constructor(connection) {
3
- this.connection = connection;
1
+ import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";
2
+ /**
3
+ * Adapt an MCP client so it works seamlessly with OpenAI chat completions
4
+ */
5
+ export class OpenAIChatAdapter {
6
+ constructor(client, options = {
7
+ // Restriction enforced by OpenAI
8
+ truncateDescriptionLength: 1024,
9
+ }) {
10
+ this.client = client;
11
+ this.options = options;
4
12
  }
5
- async listTools(strict = false) {
6
- return this.format(await this.connection.listTools(), strict);
7
- }
8
- format(tools, strict = false) {
9
- return Object.entries(tools).flatMap(([mcpName, tools]) => tools.map((tool) => ({
13
+ async listTools() {
14
+ const toolResult = await this.client.listTools();
15
+ return toolResult.tools.map((tool) => ({
10
16
  type: "function",
11
17
  function: {
12
- name: `${mcpName}_${tool.name}`,
13
- description: tool.description,
18
+ name: tool.name,
19
+ description: tool.description?.slice(0, this.options?.truncateDescriptionLength),
14
20
  parameters: tool.inputSchema,
15
- strict,
21
+ strict: this.options?.strict ?? false,
16
22
  },
17
- })));
23
+ }));
18
24
  }
19
25
  // TODO: Support streaming
20
- async call(response, options) {
21
- const choice = response.choices[0];
22
- // TODO: Support `n`
23
- if (!choice) {
24
- return [];
26
+ async callTool(response, options) {
27
+ if (response.choices.length !== 1) {
28
+ // TODO: Support `n`
29
+ throw new Error("Multiple choices not supported");
25
30
  }
26
- const toolCalls = choice.message?.tool_calls;
27
- if (!toolCalls) {
31
+ const choice = response.choices[0];
32
+ if (!choice?.message?.tool_calls) {
28
33
  return [];
29
34
  }
30
- const results = await this.connection.callTools(toolCalls.map((toolCall) => {
31
- const splitPoint = toolCall.function.name.indexOf("_");
32
- const mcp = toolCall.function.name.slice(0, splitPoint);
33
- const name = toolCall.function.name.slice(splitPoint + 1);
34
- return {
35
- mcp,
36
- name,
35
+ const toolCalls = choice.message.tool_calls;
36
+ const results = await Promise.all(toolCalls.map(async (toolCall) => {
37
+ return await this.client.callTool({
38
+ name: toolCall.function.name,
37
39
  arguments: JSON.parse(toolCall.function.arguments),
38
- };
39
- }), options);
40
+ }, CallToolResultSchema, options);
41
+ }));
40
42
  return results.map((result, index) => ({
41
43
  role: "tool",
42
44
  content: result.content,
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Types for entries in the Smithery registry
3
+ */
4
+ import { z } from "zod";
5
+ export declare const JSONSchemaSchema: z.ZodType;
6
+ export type JSONSchema = z.infer<typeof JSONSchemaSchema>;
7
+ export declare const StdioConnectionSchema: z.ZodObject<{
8
+ command: z.ZodString;
9
+ args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
10
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ command: string;
13
+ args?: string[] | undefined;
14
+ env?: Record<string, string> | undefined;
15
+ }, {
16
+ command: string;
17
+ args?: string[] | undefined;
18
+ env?: Record<string, string> | undefined;
19
+ }>;
20
+ export type StdioConnection = z.infer<typeof StdioConnectionSchema>;
21
+ export declare const ConnectionSchema: z.ZodIntersection<z.ZodObject<{
22
+ configSchema: z.ZodOptional<z.ZodType<any, z.ZodTypeDef, any>>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ configSchema?: any;
25
+ }, {
26
+ configSchema?: any;
27
+ }>, z.ZodUnion<[z.ZodObject<{
28
+ sse: z.ZodString;
29
+ }, "strip", z.ZodTypeAny, {
30
+ sse: string;
31
+ }, {
32
+ sse: string;
33
+ }>, z.ZodObject<{
34
+ stdio: z.ZodObject<{
35
+ command: z.ZodString;
36
+ args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
37
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
38
+ }, "strip", z.ZodTypeAny, {
39
+ command: string;
40
+ args?: string[] | undefined;
41
+ env?: Record<string, string> | undefined;
42
+ }, {
43
+ command: string;
44
+ args?: string[] | undefined;
45
+ env?: Record<string, string> | undefined;
46
+ }>;
47
+ }, "strip", z.ZodTypeAny, {
48
+ stdio: {
49
+ command: string;
50
+ args?: string[] | undefined;
51
+ env?: Record<string, string> | undefined;
52
+ };
53
+ }, {
54
+ stdio: {
55
+ command: string;
56
+ args?: string[] | undefined;
57
+ env?: Record<string, string> | undefined;
58
+ };
59
+ }>]>>;
60
+ export type Connection = z.infer<typeof ConnectionSchema>;
61
+ export declare const RegistryServerSchema: z.ZodObject<{
62
+ id: z.ZodString;
63
+ name: z.ZodString;
64
+ verified: z.ZodOptional<z.ZodBoolean>;
65
+ description: z.ZodOptional<z.ZodString>;
66
+ vendor: z.ZodString;
67
+ sourceUrl: z.ZodString;
68
+ license: z.ZodOptional<z.ZodString>;
69
+ homepage: z.ZodString;
70
+ connections: z.ZodArray<z.ZodIntersection<z.ZodObject<{
71
+ configSchema: z.ZodOptional<z.ZodType<any, z.ZodTypeDef, any>>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ configSchema?: any;
74
+ }, {
75
+ configSchema?: any;
76
+ }>, z.ZodUnion<[z.ZodObject<{
77
+ sse: z.ZodString;
78
+ }, "strip", z.ZodTypeAny, {
79
+ sse: string;
80
+ }, {
81
+ sse: string;
82
+ }>, z.ZodObject<{
83
+ stdio: z.ZodObject<{
84
+ command: z.ZodString;
85
+ args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
86
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
87
+ }, "strip", z.ZodTypeAny, {
88
+ command: string;
89
+ args?: string[] | undefined;
90
+ env?: Record<string, string> | undefined;
91
+ }, {
92
+ command: string;
93
+ args?: string[] | undefined;
94
+ env?: Record<string, string> | undefined;
95
+ }>;
96
+ }, "strip", z.ZodTypeAny, {
97
+ stdio: {
98
+ command: string;
99
+ args?: string[] | undefined;
100
+ env?: Record<string, string> | undefined;
101
+ };
102
+ }, {
103
+ stdio: {
104
+ command: string;
105
+ args?: string[] | undefined;
106
+ env?: Record<string, string> | undefined;
107
+ };
108
+ }>]>>, "many">;
109
+ }, "strip", z.ZodTypeAny, {
110
+ name: string;
111
+ id: string;
112
+ vendor: string;
113
+ sourceUrl: string;
114
+ homepage: string;
115
+ connections: ({
116
+ configSchema?: any;
117
+ } & ({
118
+ sse: string;
119
+ } | {
120
+ stdio: {
121
+ command: string;
122
+ args?: string[] | undefined;
123
+ env?: Record<string, string> | undefined;
124
+ };
125
+ }))[];
126
+ description?: string | undefined;
127
+ verified?: boolean | undefined;
128
+ license?: string | undefined;
129
+ }, {
130
+ name: string;
131
+ id: string;
132
+ vendor: string;
133
+ sourceUrl: string;
134
+ homepage: string;
135
+ connections: ({
136
+ configSchema?: any;
137
+ } & ({
138
+ sse: string;
139
+ } | {
140
+ stdio: {
141
+ command: string;
142
+ args?: string[] | undefined;
143
+ env?: Record<string, string> | undefined;
144
+ };
145
+ }))[];
146
+ description?: string | undefined;
147
+ verified?: boolean | undefined;
148
+ license?: string | undefined;
149
+ }>;
150
+ export type RegistryServer = z.infer<typeof RegistryServerSchema>;
151
+ export declare function isStdio(connection: Connection): connection is Connection & {
152
+ stdio: StdioConnection;
153
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Types for entries in the Smithery registry
3
+ */
4
+ import { z } from "zod";
5
+ export const JSONSchemaSchema = z
6
+ .lazy(() => z.object({
7
+ type: z.string().optional(),
8
+ properties: z.record(JSONSchemaSchema).optional(),
9
+ items: JSONSchemaSchema.optional(),
10
+ required: z.array(z.string()).optional(),
11
+ description: z.string().optional(),
12
+ default: z.unknown().optional(),
13
+ }))
14
+ .describe("JSON Schema defines the configuration required to initialize the server. All variables are used to template fill the commands. Leave undefined if no config required.");
15
+ export const StdioConnectionSchema = z.object({
16
+ command: z.string().describe("The executable to run to start the server."),
17
+ args: z
18
+ .array(z.string())
19
+ .optional()
20
+ .describe("Command line arguments to pass to the executable."),
21
+ env: z
22
+ .record(z.string(), z.string())
23
+ .optional()
24
+ .describe("The environment to use when spawning the process."),
25
+ });
26
+ export const ConnectionSchema = z
27
+ .object({
28
+ configSchema: JSONSchemaSchema.optional(),
29
+ })
30
+ .and(z.union([
31
+ z.object({
32
+ sse: z.string().describe("The URL to connect to the server."),
33
+ }),
34
+ z.object({
35
+ stdio: StdioConnectionSchema,
36
+ }),
37
+ ]))
38
+ .describe("A connection represents the protocol used to connect with the MCP server. A connection can be templated with shell variables in the format of ${VARNAME}. These will be replaced with the actual value of the variable defined in `configSchema` in durnig runtime.");
39
+ export const RegistryServerSchema = z.object({
40
+ id: z
41
+ .string()
42
+ .describe("The unique identifier. Usually the `npm` package name."),
43
+ name: z.string().describe("The human-readable name of the MCP server."),
44
+ verified: z
45
+ .boolean()
46
+ .optional()
47
+ .describe("Whether the server is maintained by the official API maintainers."),
48
+ description: z
49
+ .string()
50
+ .optional()
51
+ .describe("A concise description of the MCP server for end-users."),
52
+ vendor: z.string().describe("The name of the author of the MCP."),
53
+ sourceUrl: z
54
+ .string()
55
+ .describe("A URL to the official page of the MCP repository."),
56
+ license: z.string().optional().describe("The license of the MCP."),
57
+ homepage: z
58
+ .string()
59
+ .describe("The URL to the homepage of the MCP, typically the product page."),
60
+ connections: z
61
+ .array(ConnectionSchema)
62
+ .describe("A list of ways to connect with the MCP server."),
63
+ });
64
+ // Type guard
65
+ export function isStdio(connection) {
66
+ return "stdio" in connection;
67
+ }
@@ -0,0 +1,6 @@
1
+ import type { StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { type JSONSchema, type RegistryServer, type StdioConnection } from "./registry-types.js";
4
+ export declare function createStdioConfig(pkg: RegistryServer, variables: JSONSchema): StdioConnection;
5
+ export declare function fetchRegistryEntry(id: string): Promise<RegistryServer>;
6
+ export declare function createTransport(id: string, variables?: JSONSchema, options?: Partial<StdioServerParameters>): Promise<StdioClientTransport>;
@@ -0,0 +1,50 @@
1
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
2
+ import { REGISTRY_URL } from "./config.js";
3
+ import { isStdio, } from "./registry-types.js";
4
+ // Helper to create StdioClientTransport config
5
+ export function createStdioConfig(pkg, variables) {
6
+ if (pkg.connections.length === 0) {
7
+ throw new Error(`No connections defined for package: ${pkg.id}`);
8
+ }
9
+ // Use first connection for now - could add connection selection later
10
+ const connection = pkg.connections[0];
11
+ if (!isStdio(connection))
12
+ throw new Error(`Connection not supported for stdio: ${connection}`);
13
+ const env = {};
14
+ if (connection.stdio.env) {
15
+ for (const [key, template] of Object.entries(connection.stdio.env)) {
16
+ env[key] = template
17
+ .toString()
18
+ .replace(/\${([^}]+)}/g, (_, varName) => {
19
+ if (!(varName in variables)) {
20
+ throw new Error(`Missing required variable: ${varName}`);
21
+ }
22
+ return variables[varName];
23
+ });
24
+ }
25
+ }
26
+ return {
27
+ command: connection.stdio.command,
28
+ args: connection.stdio.args,
29
+ env: Object.keys(env).length > 0 ? env : undefined,
30
+ };
31
+ }
32
+ export async function fetchRegistryEntry(id) {
33
+ const response = await fetch(`${REGISTRY_URL}/servers/${id}`);
34
+ if (!response.ok) {
35
+ throw new Error(`Registry entry not found: ${id}`);
36
+ }
37
+ return await response.json();
38
+ }
39
+ export async function createTransport(id, variables = {}, options = {}) {
40
+ const pkg = await fetchRegistryEntry(id);
41
+ const config = createStdioConfig(pkg, variables);
42
+ const transport = new StdioClientTransport({ ...config, ...options });
43
+ return transport;
44
+ }
45
+ // Example usage:
46
+ /*
47
+ const transport = await createTransport("brave-search", {
48
+ braveApiKey: "user-api-key"
49
+ })
50
+ */
package/dist/types.d.ts CHANGED
@@ -1,167 +1,4 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
1
  import { z } from "zod";
3
- export declare const MCPConfigSchema: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodType<Server<{
4
- method: string;
5
- params?: z.objectOutputType<{
6
- _meta: z.ZodOptional<z.ZodObject<{
7
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
8
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
9
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
10
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
11
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
12
- }, z.ZodTypeAny, "passthrough">>>;
13
- }, z.ZodTypeAny, "passthrough"> | undefined;
14
- }, {
15
- method: string;
16
- params?: z.objectOutputType<{
17
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
18
- }, z.ZodTypeAny, "passthrough"> | undefined;
19
- }, z.objectOutputType<{
20
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
21
- }, z.ZodTypeAny, "passthrough">>, z.ZodTypeDef, Server<{
22
- method: string;
23
- params?: z.objectOutputType<{
24
- _meta: z.ZodOptional<z.ZodObject<{
25
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
26
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
27
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
28
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
29
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
30
- }, z.ZodTypeAny, "passthrough">>>;
31
- }, z.ZodTypeAny, "passthrough"> | undefined;
32
- }, {
33
- method: string;
34
- params?: z.objectOutputType<{
35
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
36
- }, z.ZodTypeAny, "passthrough"> | undefined;
37
- }, z.objectOutputType<{
38
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
39
- }, z.ZodTypeAny, "passthrough">>>, z.ZodObject<{
40
- server: z.ZodType<Server<{
41
- method: string;
42
- params?: z.objectOutputType<{
43
- _meta: z.ZodOptional<z.ZodObject<{
44
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
45
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
46
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
47
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
48
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
49
- }, z.ZodTypeAny, "passthrough">>>;
50
- }, z.ZodTypeAny, "passthrough"> | undefined;
51
- }, {
52
- method: string;
53
- params?: z.objectOutputType<{
54
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
55
- }, z.ZodTypeAny, "passthrough"> | undefined;
56
- }, z.objectOutputType<{
57
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
58
- }, z.ZodTypeAny, "passthrough">>, z.ZodTypeDef, Server<{
59
- method: string;
60
- params?: z.objectOutputType<{
61
- _meta: z.ZodOptional<z.ZodObject<{
62
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
63
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
64
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
65
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
66
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
67
- }, z.ZodTypeAny, "passthrough">>>;
68
- }, z.ZodTypeAny, "passthrough"> | undefined;
69
- }, {
70
- method: string;
71
- params?: z.objectOutputType<{
72
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
73
- }, z.ZodTypeAny, "passthrough"> | undefined;
74
- }, z.objectOutputType<{
75
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
76
- }, z.ZodTypeAny, "passthrough">>>;
77
- }, "strip", z.ZodTypeAny, {
78
- server: Server<{
79
- method: string;
80
- params?: z.objectOutputType<{
81
- _meta: z.ZodOptional<z.ZodObject<{
82
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
83
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
84
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
85
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
86
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
87
- }, z.ZodTypeAny, "passthrough">>>;
88
- }, z.ZodTypeAny, "passthrough"> | undefined;
89
- }, {
90
- method: string;
91
- params?: z.objectOutputType<{
92
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
93
- }, z.ZodTypeAny, "passthrough"> | undefined;
94
- }, z.objectOutputType<{
95
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
96
- }, z.ZodTypeAny, "passthrough">>;
97
- }, {
98
- server: Server<{
99
- method: string;
100
- params?: z.objectOutputType<{
101
- _meta: z.ZodOptional<z.ZodObject<{
102
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
103
- }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
104
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
105
- }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
106
- progressToken: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
107
- }, z.ZodTypeAny, "passthrough">>>;
108
- }, z.ZodTypeAny, "passthrough"> | undefined;
109
- }, {
110
- method: string;
111
- params?: z.objectOutputType<{
112
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
113
- }, z.ZodTypeAny, "passthrough"> | undefined;
114
- }, z.objectOutputType<{
115
- _meta: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
116
- }, z.ZodTypeAny, "passthrough">>;
117
- }>, z.ZodObject<{
118
- url: z.ZodString;
119
- }, "strip", z.ZodTypeAny, {
120
- url: string;
121
- }, {
122
- url: string;
123
- }>, z.ZodObject<{
124
- stdio: z.ZodObject<{
125
- command: z.ZodString;
126
- args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
127
- env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
128
- }, "strip", z.ZodTypeAny, {
129
- command: string;
130
- args?: string[] | undefined;
131
- env?: Record<string, string> | undefined;
132
- }, {
133
- command: string;
134
- args?: string[] | undefined;
135
- env?: Record<string, string> | undefined;
136
- }>;
137
- }, "strip", z.ZodTypeAny, {
138
- stdio: {
139
- command: string;
140
- args?: string[] | undefined;
141
- env?: Record<string, string> | undefined;
142
- };
143
- }, {
144
- stdio: {
145
- command: string;
146
- args?: string[] | undefined;
147
- env?: Record<string, string> | undefined;
148
- };
149
- }>]>>;
150
- export type MCPConfig = z.infer<typeof MCPConfigSchema>;
151
- export declare function isServerConfig(config: any): config is Server;
152
- export declare function isURIConfig(config: MCPConfig[string]): config is {
153
- url: string;
154
- };
155
- export declare function isStdioConfig(config: MCPConfig[string]): config is {
156
- stdio: {
157
- command: string;
158
- args?: string[];
159
- env?: Record<string, string>;
160
- };
161
- };
162
- export declare function isWrappedServerConfig(config: any): config is {
163
- server: Server;
164
- };
165
2
  export declare const ToolSchema: z.ZodObject<{
166
3
  name: z.ZodString;
167
4
  description: z.ZodOptional<z.ZodString>;
package/dist/types.js CHANGED
@@ -1,38 +1,4 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
1
  import { z } from "zod";
3
- export const MCPConfigSchema = z.record(z.string(), z.union([
4
- z.instanceof(Server),
5
- z.object({ server: z.instanceof(Server) }),
6
- z.object({ url: z.string() }),
7
- z.object({
8
- stdio: z.object({
9
- command: z
10
- .string()
11
- .describe("The executable to run to start the server."),
12
- args: z
13
- .array(z.string())
14
- .optional()
15
- .describe("Command line arguments to pass to the executable."),
16
- env: z
17
- .record(z.string(), z.string())
18
- .optional()
19
- .describe("The environment to use when spawning the process. If not specified, the result of getDefaultEnvironment() will be used."),
20
- }),
21
- }),
22
- ]));
23
- // Type guards
24
- export function isServerConfig(config) {
25
- return config instanceof Server;
26
- }
27
- export function isURIConfig(config) {
28
- return "url" in config && typeof config.url === "string";
29
- }
30
- export function isStdioConfig(config) {
31
- return "stdio" in config;
32
- }
33
- export function isWrappedServerConfig(config) {
34
- return "server" in config && config.server instanceof Server;
35
- }
36
2
  export const ToolSchema = z.object({
37
3
  name: z.string(),
38
4
  description: z.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smithery/sdk",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "description": "Connect language models to Model Context Protocols",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,15 +9,22 @@
9
9
  ".": "./dist/index.js",
10
10
  "./*": "./dist/*"
11
11
  },
12
- "files": ["dist"],
12
+ "files": [
13
+ "dist"
14
+ ],
13
15
  "scripts": {
14
16
  "build": "tsc",
17
+ "build:all": "npm run build -ws --include-workspace-root",
15
18
  "watch": "tsc --watch",
16
19
  "lint": "npx @biomejs/biome lint --write",
17
- "format": "npx @biomejs/biome format --write"
20
+ "format": "npx @biomejs/biome format --write",
21
+ "bump": "npm version patch -ws --include-workspace-root",
22
+ "link": "npm link -ws --include-workspace-root"
18
23
  },
19
24
  "license": "GPL-3.0-only",
20
- "workspaces": ["mcps/*"],
25
+ "workspaces": [
26
+ "mcps/*"
27
+ ],
21
28
  "dependencies": {
22
29
  "@anthropic-ai/sdk": "^0.32.1",
23
30
  "@modelcontextprotocol/sdk": "^1.0.3",
@@ -30,7 +37,6 @@
30
37
  "@types/uuid": "^9.0.7",
31
38
  "@smithery/mcp-e2b": "*",
32
39
  "@smithery/mcp-exa": "*",
33
- "@smithery/mcp-shell": "*",
34
40
  "dotenv": "^16.4.7",
35
41
  "eventsource": "^2.0.2",
36
42
  "tsx": "^4.19.2",
@@ -1 +0,0 @@
1
- export {};
@@ -1,78 +0,0 @@
1
- import * as shellMcp from "@unroute/mcp-shell";
2
- import dotenv from "dotenv";
3
- import EventSource from "eventsource";
4
- import { OpenAI } from "openai";
5
- import { Connection } from "../index.js";
6
- import { OpenAIHandler } from "../integrations/llm/openai.js";
7
- import url from "node:url";
8
- import readline from "node:readline";
9
- // Utility for human approval
10
- async function getHumanApproval(command, args) {
11
- const rl = readline.createInterface({
12
- input: process.stdin,
13
- output: process.stdout,
14
- });
15
- return new Promise((resolve) => {
16
- rl.question(`Command: ${command} ${args.join(" ")}\nApprove? [y/N]: `, (answer) => {
17
- rl.close();
18
- resolve(answer.toLowerCase() === "y");
19
- });
20
- });
21
- }
22
- // Patch event source
23
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
- global.EventSource = EventSource;
25
- async function main() {
26
- dotenv.config();
27
- // Initialize the OpenAI client
28
- const openai = new OpenAI();
29
- // Connect to MCPs
30
- const connection = await Connection.connect({
31
- shell: shellMcp.createServer({
32
- allowedCommands: ["ls", "pwd", "date", "echo"],
33
- approvalHandler: getHumanApproval,
34
- }),
35
- });
36
- // Example conversation with tool usage
37
- let isDone = false;
38
- const messages = [
39
- {
40
- role: "user",
41
- content: "What's the date?",
42
- },
43
- ];
44
- const handler = new OpenAIHandler(connection);
45
- while (!isDone) {
46
- const response = await openai.chat.completions.create({
47
- model: "gpt-4o",
48
- messages,
49
- tools: await handler.listTools(),
50
- });
51
- // Handle tool calls - will prompt for approval during execution
52
- const toolMessages = await handler.call(response);
53
- messages.push(response.choices[0].message);
54
- messages.push(...toolMessages);
55
- isDone = toolMessages.length === 0;
56
- console.log("Processing messages:", messages.map((m) => ({
57
- role: m.role,
58
- content: m.content,
59
- tools: "tool_calls" in m ? m.tool_calls?.length : 0,
60
- })));
61
- }
62
- // Print the final conversation
63
- console.log("\nFinal conversation:");
64
- messages.forEach((msg) => {
65
- console.log(`\n${msg.role.toUpperCase()}:`);
66
- console.log(msg.content);
67
- if (msg.role === "assistant" && msg.tool_calls) {
68
- console.log("Tool calls:", JSON.stringify(msg.tool_calls, null, 2));
69
- }
70
- });
71
- }
72
- // Run the example
73
- if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
74
- main().catch((err) => {
75
- console.error("Error:", err);
76
- process.exit(1);
77
- });
78
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,60 +0,0 @@
1
- import * as e2b from "@unroute/mcp-e2b";
2
- import * as exa from "@unroute/mcp-exa";
3
- import dotenv from "dotenv";
4
- import EventSource from "eventsource";
5
- import { OpenAI } from "openai";
6
- import { Connection } from "../index.js";
7
- import { OpenAIHandler } from "../integrations/llm/openai.js";
8
- // Patch event source
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- global.EventSource = EventSource;
11
- async function main() {
12
- dotenv.config();
13
- // Initialize the OpenAI client
14
- const openai = new OpenAI();
15
- // Connect to MCPs
16
- const connection = await Connection.connect({
17
- exa: {
18
- server: exa.createServer(),
19
- },
20
- e2b: {
21
- server: e2b.createServer(),
22
- },
23
- });
24
- // Example conversation with tool usage
25
- let isDone = false;
26
- const messages = [
27
- {
28
- role: "user",
29
- content: "Search about the latest news about syria and give me a summary",
30
- },
31
- ];
32
- const handler = new OpenAIHandler(connection);
33
- while (!isDone) {
34
- const response = await openai.chat.completions.create({
35
- model: "gpt-4o-mini",
36
- messages,
37
- tools: await handler.listTools(),
38
- });
39
- // Handle tool calls
40
- const toolMessages = await handler.call(response);
41
- messages.push(response.choices[0].message);
42
- messages.push(...toolMessages);
43
- isDone = toolMessages.length === 0;
44
- console.log("messages", messages);
45
- }
46
- // Print the final conversation
47
- console.log("\nFinal conversation:");
48
- messages.forEach((msg) => {
49
- console.log(`\n${msg.role.toUpperCase()}:`);
50
- console.log(msg.content);
51
- if (msg.role === "assistant" && msg.tool_calls) {
52
- console.log(msg.tool_calls);
53
- }
54
- });
55
- }
56
- // Run the example
57
- main().catch((err) => {
58
- console.error("Error:", err);
59
- process.exit(1);
60
- });