@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 +25 -30
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -0
- package/dist/index.d.ts +55 -15
- package/dist/index.js +70 -61
- package/dist/integrations/llm/anthropic.d.ts +8 -7
- package/dist/integrations/llm/anthropic.js +19 -20
- package/dist/integrations/llm/openai.d.ts +15 -8
- package/dist/integrations/llm/openai.js +30 -28
- package/dist/registry-types.d.ts +153 -0
- package/dist/registry-types.js +67 -0
- package/dist/registry.d.ts +6 -0
- package/dist/registry.js +50 -0
- package/dist/types.d.ts +0 -163
- package/dist/types.js +0 -34
- package/package.json +11 -5
- package/dist/examples/shell.d.ts +0 -1
- package/dist/examples/shell.js +0 -78
- package/dist/examples/simple.d.ts +0 -1
- package/dist/examples/simple.js +0 -60
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
|
-
-
|
|
11
|
-
- Supports
|
|
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 {
|
|
33
|
-
import {
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
51
|
-
const
|
|
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
|
|
62
|
+
tools: await adapter.listTools(),
|
|
57
63
|
})
|
|
58
64
|
// Obtain the tool outputs as new messages
|
|
59
|
-
const toolMessages = await
|
|
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
|
|
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
|
|
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
|
|
90
|
+
tools: await adapter.listTools(),
|
|
85
91
|
})
|
|
86
92
|
// Handle tool calls
|
|
87
|
-
const toolMessages = await
|
|
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
|
package/dist/config.d.ts
ADDED
|
@@ -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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
this.
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
42
|
-
await
|
|
42
|
+
await transport.connect(serverTransport);
|
|
43
|
+
await client.connect(clientTransport);
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
+
else {
|
|
46
|
+
await client.connect(transport);
|
|
47
|
+
}
|
|
48
|
+
this.clients[namespace] = client;
|
|
45
49
|
}));
|
|
46
|
-
return connection;
|
|
47
50
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
call(response: Message, options?: RequestOptions): Promise<MessageParam[]>;
|
|
11
|
+
callTool(response: Message, options?: RequestOptions): Promise<MessageParam[]>;
|
|
11
12
|
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
})
|
|
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 {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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(
|
|
6
|
-
|
|
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:
|
|
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
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
27
|
-
if (!
|
|
31
|
+
const choice = response.choices[0];
|
|
32
|
+
if (!choice?.message?.tool_calls) {
|
|
28
33
|
return [];
|
|
29
34
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
})
|
|
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>;
|
package/dist/registry.js
ADDED
|
@@ -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.
|
|
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": [
|
|
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": [
|
|
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",
|
package/dist/examples/shell.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/examples/shell.js
DELETED
|
@@ -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 {};
|
package/dist/examples/simple.js
DELETED
|
@@ -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
|
-
});
|