agents 0.0.59 → 0.0.61
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/dist/ai-chat-agent.d.ts +4 -0
- package/dist/ai-chat-agent.js +17 -17
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/mcp/client.d.ts +29 -28
- package/dist/mcp/client.js +63 -126
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/do-oauth-client-provider.d.ts +41 -0
- package/dist/mcp/do-oauth-client-provider.js +107 -0
- package/dist/mcp/do-oauth-client-provider.js.map +1 -0
- package/package.json +6 -1
package/dist/ai-chat-agent.d.ts
CHANGED
|
@@ -30,6 +30,10 @@ declare class AIChatAgent<Env = unknown, State = unknown> extends Agent<
|
|
|
30
30
|
* @param messages Chat messages to save
|
|
31
31
|
*/
|
|
32
32
|
saveMessages(messages: Message[]): Promise<void>;
|
|
33
|
+
persistMessages(
|
|
34
|
+
messages: Message[],
|
|
35
|
+
excludeBroadcastIds?: string[]
|
|
36
|
+
): Promise<void>;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export { AIChatAgent };
|
package/dist/ai-chat-agent.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
// src/ai-chat-agent.ts
|
|
10
10
|
import { appendResponseMessages } from "ai";
|
|
11
11
|
var decoder = new TextDecoder();
|
|
12
|
-
var _AIChatAgent_instances, broadcastChatMessage_fn, tryCatch_fn,
|
|
12
|
+
var _AIChatAgent_instances, broadcastChatMessage_fn, tryCatch_fn, reply_fn;
|
|
13
13
|
var AIChatAgent = class extends Agent {
|
|
14
14
|
constructor(ctx, env) {
|
|
15
15
|
super(ctx, env);
|
|
@@ -53,14 +53,14 @@ var AIChatAgent = class extends Agent {
|
|
|
53
53
|
type: "cf_agent_chat_messages",
|
|
54
54
|
messages
|
|
55
55
|
}, [connection.id]);
|
|
56
|
-
await
|
|
56
|
+
await this.persistMessages(messages, [connection.id]);
|
|
57
57
|
return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, async () => {
|
|
58
58
|
const response = await this.onChatMessage(async ({ response: response2 }) => {
|
|
59
59
|
const finalMessages = appendResponseMessages({
|
|
60
60
|
messages,
|
|
61
61
|
responseMessages: response2.messages
|
|
62
62
|
});
|
|
63
|
-
await
|
|
63
|
+
await this.persistMessages(finalMessages, [connection.id]);
|
|
64
64
|
});
|
|
65
65
|
if (response) {
|
|
66
66
|
await __privateMethod(this, _AIChatAgent_instances, reply_fn).call(this, data.id, response);
|
|
@@ -74,7 +74,7 @@ var AIChatAgent = class extends Agent {
|
|
|
74
74
|
type: "cf_agent_chat_clear"
|
|
75
75
|
}, [connection.id]);
|
|
76
76
|
} else if (data.type === "cf_agent_chat_messages") {
|
|
77
|
-
await
|
|
77
|
+
await this.persistMessages(data.messages, [connection.id]);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -105,13 +105,13 @@ var AIChatAgent = class extends Agent {
|
|
|
105
105
|
* @param messages Chat messages to save
|
|
106
106
|
*/
|
|
107
107
|
async saveMessages(messages) {
|
|
108
|
-
await
|
|
108
|
+
await this.persistMessages(messages);
|
|
109
109
|
const response = await this.onChatMessage(async ({ response: response2 }) => {
|
|
110
110
|
const finalMessages = appendResponseMessages({
|
|
111
111
|
messages,
|
|
112
112
|
responseMessages: response2.messages
|
|
113
113
|
});
|
|
114
|
-
await
|
|
114
|
+
await this.persistMessages(finalMessages, []);
|
|
115
115
|
});
|
|
116
116
|
if (response) {
|
|
117
117
|
for await (const chunk of response.body) {
|
|
@@ -120,6 +120,17 @@ var AIChatAgent = class extends Agent {
|
|
|
120
120
|
response.body?.cancel();
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
async persistMessages(messages, excludeBroadcastIds = []) {
|
|
124
|
+
this.sql`delete from cf_ai_chat_agent_messages`;
|
|
125
|
+
for (const message of messages) {
|
|
126
|
+
this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${message.id},${JSON.stringify(message)})`;
|
|
127
|
+
}
|
|
128
|
+
this.messages = messages;
|
|
129
|
+
__privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
|
|
130
|
+
type: "cf_agent_chat_messages",
|
|
131
|
+
messages
|
|
132
|
+
}, excludeBroadcastIds);
|
|
133
|
+
}
|
|
123
134
|
};
|
|
124
135
|
_AIChatAgent_instances = new WeakSet();
|
|
125
136
|
broadcastChatMessage_fn = function(message, exclude) {
|
|
@@ -132,17 +143,6 @@ tryCatch_fn = async function(fn) {
|
|
|
132
143
|
throw this.onError(e);
|
|
133
144
|
}
|
|
134
145
|
};
|
|
135
|
-
persistMessages_fn = async function(messages, excludeBroadcastIds = []) {
|
|
136
|
-
this.sql`delete from cf_ai_chat_agent_messages`;
|
|
137
|
-
for (const message of messages) {
|
|
138
|
-
this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${message.id},${JSON.stringify(message)})`;
|
|
139
|
-
}
|
|
140
|
-
this.messages = messages;
|
|
141
|
-
__privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
|
|
142
|
-
type: "cf_agent_chat_messages",
|
|
143
|
-
messages
|
|
144
|
-
}, excludeBroadcastIds);
|
|
145
|
-
};
|
|
146
146
|
reply_fn = async function(id, response) {
|
|
147
147
|
return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, async () => {
|
|
148
148
|
for await (const chunk of response.body) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ai-chat-agent.ts"],"sourcesContent":["import { Agent, type AgentContext, type Connection, type WSMessage } from \"./\";\nimport type {\n Message as ChatMessage,\n StreamTextOnFinishCallback,\n ToolSet,\n} from \"ai\";\nimport { appendResponseMessages } from \"ai\";\nimport type { OutgoingMessage, IncomingMessage } from \"./ai-types\";\nconst decoder = new TextDecoder();\n\n/**\n * Extension of Agent with built-in chat capabilities\n * @template Env Environment type containing bindings\n */\nexport class AIChatAgent<Env = unknown, State = unknown> extends Agent<\n Env,\n State\n> {\n /** Array of chat messages for the current conversation */\n messages: ChatMessage[];\n constructor(ctx: AgentContext, env: Env) {\n super(ctx, env);\n this.sql`create table if not exists cf_ai_chat_agent_messages (\n id text primary key,\n message text not null,\n created_at datetime default current_timestamp\n )`;\n this.messages = (\n this.sql`select * from cf_ai_chat_agent_messages` || []\n ).map((row) => {\n return JSON.parse(row.message as string);\n });\n }\n\n #broadcastChatMessage(message: OutgoingMessage, exclude?: string[]) {\n this.broadcast(JSON.stringify(message), exclude);\n }\n\n override async onMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n let data: IncomingMessage;\n try {\n data = JSON.parse(message) as IncomingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (\n data.type === \"cf_agent_use_chat_request\" &&\n data.init.method === \"POST\"\n ) {\n const {\n method,\n keepalive,\n headers,\n body, // we're reading this\n redirect,\n integrity,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher,\n // duplex\n } = data.init;\n const { messages } = JSON.parse(body as string);\n this.#broadcastChatMessage(\n {\n type: \"cf_agent_chat_messages\",\n messages,\n },\n [connection.id]\n );\n await this
|
|
1
|
+
{"version":3,"sources":["../src/ai-chat-agent.ts"],"sourcesContent":["import { Agent, type AgentContext, type Connection, type WSMessage } from \"./\";\nimport type {\n Message as ChatMessage,\n StreamTextOnFinishCallback,\n ToolSet,\n} from \"ai\";\nimport { appendResponseMessages } from \"ai\";\nimport type { OutgoingMessage, IncomingMessage } from \"./ai-types\";\nconst decoder = new TextDecoder();\n\n/**\n * Extension of Agent with built-in chat capabilities\n * @template Env Environment type containing bindings\n */\nexport class AIChatAgent<Env = unknown, State = unknown> extends Agent<\n Env,\n State\n> {\n /** Array of chat messages for the current conversation */\n messages: ChatMessage[];\n constructor(ctx: AgentContext, env: Env) {\n super(ctx, env);\n this.sql`create table if not exists cf_ai_chat_agent_messages (\n id text primary key,\n message text not null,\n created_at datetime default current_timestamp\n )`;\n this.messages = (\n this.sql`select * from cf_ai_chat_agent_messages` || []\n ).map((row) => {\n return JSON.parse(row.message as string);\n });\n }\n\n #broadcastChatMessage(message: OutgoingMessage, exclude?: string[]) {\n this.broadcast(JSON.stringify(message), exclude);\n }\n\n override async onMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n let data: IncomingMessage;\n try {\n data = JSON.parse(message) as IncomingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (\n data.type === \"cf_agent_use_chat_request\" &&\n data.init.method === \"POST\"\n ) {\n const {\n method,\n keepalive,\n headers,\n body, // we're reading this\n redirect,\n integrity,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher,\n // duplex\n } = data.init;\n const { messages } = JSON.parse(body as string);\n this.#broadcastChatMessage(\n {\n type: \"cf_agent_chat_messages\",\n messages,\n },\n [connection.id]\n );\n await this.persistMessages(messages, [connection.id]);\n return this.#tryCatch(async () => {\n const response = await this.onChatMessage(async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n await this.persistMessages(finalMessages, [connection.id]);\n });\n if (response) {\n await this.#reply(data.id, response);\n }\n });\n }\n if (data.type === \"cf_agent_chat_clear\") {\n this.sql`delete from cf_ai_chat_agent_messages`;\n this.messages = [];\n this.#broadcastChatMessage(\n {\n type: \"cf_agent_chat_clear\",\n },\n [connection.id]\n );\n } else if (data.type === \"cf_agent_chat_messages\") {\n // replace the messages with the new ones\n await this.persistMessages(data.messages, [connection.id]);\n }\n }\n }\n\n override async onRequest(request: Request): Promise<Response> {\n return this.#tryCatch(() => {\n const url = new URL(request.url);\n if (url.pathname.endsWith(\"/get-messages\")) {\n const messages = (\n this.sql`select * from cf_ai_chat_agent_messages` || []\n ).map((row) => {\n return JSON.parse(row.message as string);\n });\n return Response.json(messages);\n }\n return super.onRequest(request);\n });\n }\n\n async #tryCatch<T>(fn: () => T | Promise<T>) {\n try {\n return await fn();\n } catch (e) {\n throw this.onError(e);\n }\n }\n\n /**\n * Handle incoming chat messages and generate a response\n * @param onFinish Callback to be called when the response is finished\n * @returns Response to send to the client or undefined\n */\n async onChatMessage(\n onFinish: StreamTextOnFinishCallback<ToolSet>\n ): Promise<Response | undefined> {\n throw new Error(\n \"recieved a chat message, override onChatMessage and return a Response to send to the client\"\n );\n }\n\n /**\n * Save messages on the server side and trigger AI response\n * @param messages Chat messages to save\n */\n async saveMessages(messages: ChatMessage[]) {\n await this.persistMessages(messages);\n const response = await this.onChatMessage(async ({ response }) => {\n const finalMessages = appendResponseMessages({\n messages,\n responseMessages: response.messages,\n });\n\n await this.persistMessages(finalMessages, []);\n });\n if (response) {\n // we're just going to drain the body\n // @ts-ignore TODO: fix this type error\n for await (const chunk of response.body!) {\n decoder.decode(chunk);\n }\n response.body?.cancel();\n }\n }\n\n async persistMessages(\n messages: ChatMessage[],\n excludeBroadcastIds: string[] = []\n ) {\n this.sql`delete from cf_ai_chat_agent_messages`;\n for (const message of messages) {\n this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${\n message.id\n },${JSON.stringify(message)})`;\n }\n this.messages = messages;\n this.#broadcastChatMessage(\n {\n type: \"cf_agent_chat_messages\",\n messages: messages,\n },\n excludeBroadcastIds\n );\n }\n\n async #reply(id: string, response: Response) {\n // now take chunks out from dataStreamResponse and send them to the client\n return this.#tryCatch(async () => {\n // @ts-expect-error TODO: fix this type error\n for await (const chunk of response.body!) {\n const body = decoder.decode(chunk);\n\n this.#broadcastChatMessage({\n id,\n type: \"cf_agent_use_chat_response\",\n body,\n done: false,\n });\n }\n\n this.#broadcastChatMessage({\n id,\n type: \"cf_agent_use_chat_response\",\n body: \"\",\n done: true,\n });\n });\n }\n}\n"],"mappings":"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","names":["response"]}
|
package/dist/mcp/client.d.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import * as zod from 'zod';
|
|
2
|
-
import { Tool, Prompt, Resource, ResourceTemplate, ServerCapabilities,
|
|
2
|
+
import { ClientCapabilities, Tool, Prompt, Resource, ResourceTemplate, ServerCapabilities, CallToolRequest, CallToolResultSchema, CompatibilityCallToolResultSchema, ReadResourceRequest, GetPromptRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
4
4
|
import { SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
5
|
+
import { AgentsOAuthProvider } from './do-oauth-client-provider.js';
|
|
5
6
|
import { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
6
|
-
import
|
|
7
|
+
import '@modelcontextprotocol/sdk/client/auth.js';
|
|
8
|
+
import '@modelcontextprotocol/sdk/shared/auth.js';
|
|
7
9
|
|
|
8
10
|
declare class MCPClientConnection {
|
|
9
11
|
url: URL;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
options: {
|
|
13
|
+
transport: SSEClientTransportOptions & {
|
|
14
|
+
authProvider?: AgentsOAuthProvider;
|
|
15
|
+
};
|
|
16
|
+
client: ConstructorParameters<typeof Client>[1];
|
|
17
|
+
capabilities: ClientCapabilities;
|
|
18
|
+
};
|
|
12
19
|
client: Client;
|
|
13
20
|
connectionState: "authenticating" | "connecting" | "ready" | "discovering" | "failed";
|
|
14
21
|
instructions?: string;
|
|
@@ -18,7 +25,9 @@ declare class MCPClientConnection {
|
|
|
18
25
|
resourceTemplates: ResourceTemplate[];
|
|
19
26
|
serverCapabilities: ServerCapabilities | undefined;
|
|
20
27
|
constructor(url: URL, info: ConstructorParameters<typeof Client>[0], options?: {
|
|
21
|
-
transport: SSEClientTransportOptions
|
|
28
|
+
transport: SSEClientTransportOptions & {
|
|
29
|
+
authProvider?: AgentsOAuthProvider;
|
|
30
|
+
};
|
|
22
31
|
client: ConstructorParameters<typeof Client>[1];
|
|
23
32
|
capabilities: ClientCapabilities;
|
|
24
33
|
});
|
|
@@ -75,28 +84,20 @@ declare class MCPClientConnection {
|
|
|
75
84
|
}[]>;
|
|
76
85
|
}
|
|
77
86
|
|
|
78
|
-
interface AgentsOAuthProvider extends OAuthClientProvider {
|
|
79
|
-
authUrl: string | undefined;
|
|
80
|
-
clientId: string | undefined;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
87
|
/**
|
|
84
88
|
* Utility class that aggregates multiple MCP clients into one
|
|
85
89
|
*/
|
|
86
90
|
declare class MCPClientManager {
|
|
87
91
|
private name;
|
|
88
92
|
private version;
|
|
89
|
-
private auth?;
|
|
90
93
|
mcpConnections: Record<string, MCPClientConnection>;
|
|
94
|
+
private callbackUrls;
|
|
91
95
|
/**
|
|
92
96
|
* @param name Name of the MCP client
|
|
93
97
|
* @param version Version of the MCP Client
|
|
94
98
|
* @param auth Auth paramters if being used to create a DurableObjectOAuthClientProvider
|
|
95
99
|
*/
|
|
96
|
-
constructor(name: string, version: string
|
|
97
|
-
baseCallbackUri: string;
|
|
98
|
-
storage: DurableObjectStorage;
|
|
99
|
-
} | undefined);
|
|
100
|
+
constructor(name: string, version: string);
|
|
100
101
|
/**
|
|
101
102
|
* Connect to and register an MCP server
|
|
102
103
|
*
|
|
@@ -104,14 +105,14 @@ declare class MCPClientManager {
|
|
|
104
105
|
* @param clientConfig Client config
|
|
105
106
|
* @param capabilities Client capabilities (i.e. if the client supports roots/sampling)
|
|
106
107
|
*/
|
|
107
|
-
connect(url: string,
|
|
108
|
+
connect(url: string, options?: {
|
|
108
109
|
reconnect?: {
|
|
109
110
|
id: string;
|
|
110
111
|
oauthClientId?: string;
|
|
111
112
|
oauthCode?: string;
|
|
112
113
|
};
|
|
113
114
|
transport?: SSEClientTransportOptions & {
|
|
114
|
-
authProvider
|
|
115
|
+
authProvider?: AgentsOAuthProvider;
|
|
115
116
|
};
|
|
116
117
|
client?: ConstructorParameters<typeof Client>[1];
|
|
117
118
|
capabilities?: ClientCapabilities;
|
|
@@ -144,9 +145,9 @@ declare class MCPClientManager {
|
|
|
144
145
|
*/
|
|
145
146
|
callTool(params: CallToolRequest["params"] & {
|
|
146
147
|
serverId: string;
|
|
147
|
-
}, resultSchema: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema, options: RequestOptions): Promise<zod.objectOutputType<
|
|
148
|
+
}, resultSchema: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema, options: RequestOptions): Promise<zod.objectOutputType<{
|
|
148
149
|
_meta: zod.ZodOptional<zod.ZodObject<{}, "passthrough", zod.ZodTypeAny, zod.objectOutputType<{}, zod.ZodTypeAny, "passthrough">, zod.objectInputType<{}, zod.ZodTypeAny, "passthrough">>>;
|
|
149
|
-
}
|
|
150
|
+
} & {
|
|
150
151
|
content: zod.ZodArray<zod.ZodUnion<[zod.ZodObject<{
|
|
151
152
|
type: zod.ZodLiteral<"text">;
|
|
152
153
|
text: zod.ZodString;
|
|
@@ -281,19 +282,19 @@ declare class MCPClientManager {
|
|
|
281
282
|
}>, zod.ZodTypeAny, "passthrough">>]>;
|
|
282
283
|
}, zod.ZodTypeAny, "passthrough">>]>, "many">;
|
|
283
284
|
isError: zod.ZodOptional<zod.ZodDefault<zod.ZodBoolean>>;
|
|
284
|
-
}
|
|
285
|
+
}, zod.ZodTypeAny, "passthrough"> | zod.objectOutputType<{
|
|
285
286
|
_meta: zod.ZodOptional<zod.ZodObject<{}, "passthrough", zod.ZodTypeAny, zod.objectOutputType<{}, zod.ZodTypeAny, "passthrough">, zod.objectInputType<{}, zod.ZodTypeAny, "passthrough">>>;
|
|
286
|
-
}
|
|
287
|
+
} & {
|
|
287
288
|
toolResult: zod.ZodUnknown;
|
|
288
|
-
}
|
|
289
|
+
}, zod.ZodTypeAny, "passthrough">>;
|
|
289
290
|
/**
|
|
290
291
|
* Namespaced version of readResource
|
|
291
292
|
*/
|
|
292
293
|
readResource(params: ReadResourceRequest["params"] & {
|
|
293
294
|
serverId: string;
|
|
294
|
-
}, options: RequestOptions): Promise<zod.objectOutputType<
|
|
295
|
+
}, options: RequestOptions): Promise<zod.objectOutputType<{
|
|
295
296
|
_meta: zod.ZodOptional<zod.ZodObject<{}, "passthrough", zod.ZodTypeAny, zod.objectOutputType<{}, zod.ZodTypeAny, "passthrough">, zod.objectInputType<{}, zod.ZodTypeAny, "passthrough">>>;
|
|
296
|
-
}
|
|
297
|
+
} & {
|
|
297
298
|
contents: zod.ZodArray<zod.ZodUnion<[zod.ZodObject<zod.objectUtil.extendShape<{
|
|
298
299
|
uri: zod.ZodString;
|
|
299
300
|
mimeType: zod.ZodOptional<zod.ZodString>;
|
|
@@ -325,15 +326,15 @@ declare class MCPClientManager {
|
|
|
325
326
|
}, {
|
|
326
327
|
blob: zod.ZodString;
|
|
327
328
|
}>, zod.ZodTypeAny, "passthrough">>]>, "many">;
|
|
328
|
-
}
|
|
329
|
+
}, zod.ZodTypeAny, "passthrough">>;
|
|
329
330
|
/**
|
|
330
331
|
* Namespaced version of getPrompt
|
|
331
332
|
*/
|
|
332
333
|
getPrompt(params: GetPromptRequest["params"] & {
|
|
333
334
|
serverId: string;
|
|
334
|
-
}, options: RequestOptions): Promise<zod.objectOutputType<
|
|
335
|
+
}, options: RequestOptions): Promise<zod.objectOutputType<{
|
|
335
336
|
_meta: zod.ZodOptional<zod.ZodObject<{}, "passthrough", zod.ZodTypeAny, zod.objectOutputType<{}, zod.ZodTypeAny, "passthrough">, zod.objectInputType<{}, zod.ZodTypeAny, "passthrough">>>;
|
|
336
|
-
}
|
|
337
|
+
} & {
|
|
337
338
|
description: zod.ZodOptional<zod.ZodString>;
|
|
338
339
|
messages: zod.ZodArray<zod.ZodObject<{
|
|
339
340
|
role: zod.ZodEnum<["user", "assistant"]>;
|
|
@@ -741,7 +742,7 @@ declare class MCPClientManager {
|
|
|
741
742
|
}>, zod.ZodTypeAny, "passthrough">>]>;
|
|
742
743
|
}, zod.ZodTypeAny, "passthrough">>]>;
|
|
743
744
|
}, zod.ZodTypeAny, "passthrough">>, "many">;
|
|
744
|
-
}
|
|
745
|
+
}, zod.ZodTypeAny, "passthrough">>;
|
|
745
746
|
}
|
|
746
747
|
type NamespacedData = {
|
|
747
748
|
tools: (Tool & {
|
package/dist/mcp/client.js
CHANGED
|
@@ -51,7 +51,6 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
51
51
|
var MCPClientConnection = class {
|
|
52
52
|
constructor(url, info, options = { transport: {}, client: {}, capabilities: {} }) {
|
|
53
53
|
this.url = url;
|
|
54
|
-
this.info = info;
|
|
55
54
|
this.options = options;
|
|
56
55
|
this.connectionState = "connecting";
|
|
57
56
|
this.tools = [];
|
|
@@ -161,7 +160,7 @@ var MCPClientConnection = class {
|
|
|
161
160
|
do {
|
|
162
161
|
toolsResult = await this.client.listTools({
|
|
163
162
|
cursor: toolsResult.nextCursor
|
|
164
|
-
});
|
|
163
|
+
}).catch(capabilityErrorHandler({ tools: [] }, "tools/list"));
|
|
165
164
|
toolsAgg = toolsAgg.concat(toolsResult.tools);
|
|
166
165
|
} while (toolsResult.nextCursor);
|
|
167
166
|
return toolsAgg;
|
|
@@ -172,7 +171,7 @@ var MCPClientConnection = class {
|
|
|
172
171
|
do {
|
|
173
172
|
resourcesResult = await this.client.listResources({
|
|
174
173
|
cursor: resourcesResult.nextCursor
|
|
175
|
-
});
|
|
174
|
+
}).catch(capabilityErrorHandler({ resources: [] }, "resources/list"));
|
|
176
175
|
resourcesAgg = resourcesAgg.concat(resourcesResult.resources);
|
|
177
176
|
} while (resourcesResult.nextCursor);
|
|
178
177
|
return resourcesAgg;
|
|
@@ -183,7 +182,7 @@ var MCPClientConnection = class {
|
|
|
183
182
|
do {
|
|
184
183
|
promptsResult = await this.client.listPrompts({
|
|
185
184
|
cursor: promptsResult.nextCursor
|
|
186
|
-
});
|
|
185
|
+
}).catch(capabilityErrorHandler({ prompts: [] }, "prompts/list"));
|
|
187
186
|
promptsAgg = promptsAgg.concat(promptsResult.prompts);
|
|
188
187
|
} while (promptsResult.nextCursor);
|
|
189
188
|
return promptsAgg;
|
|
@@ -196,104 +195,28 @@ var MCPClientConnection = class {
|
|
|
196
195
|
do {
|
|
197
196
|
templatesResult = await this.client.listResourceTemplates({
|
|
198
197
|
cursor: templatesResult.nextCursor
|
|
199
|
-
})
|
|
198
|
+
}).catch(
|
|
199
|
+
capabilityErrorHandler(
|
|
200
|
+
{ resourceTemplates: [] },
|
|
201
|
+
"resources/templates/list"
|
|
202
|
+
)
|
|
203
|
+
);
|
|
200
204
|
templatesAgg = templatesAgg.concat(templatesResult.resourceTemplates);
|
|
201
205
|
} while (templatesResult.nextCursor);
|
|
202
206
|
return templatesAgg;
|
|
203
207
|
}
|
|
204
208
|
};
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
this.redirectUrl = redirectUrl;
|
|
213
|
-
this.clientId_ = clientId_;
|
|
214
|
-
}
|
|
215
|
-
get clientMetadata() {
|
|
216
|
-
return {
|
|
217
|
-
redirect_uris: [this.redirectUrl],
|
|
218
|
-
token_endpoint_auth_method: "none",
|
|
219
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
220
|
-
response_types: ["code"],
|
|
221
|
-
client_name: this.clientName,
|
|
222
|
-
client_uri: "example.com"
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
get clientId() {
|
|
226
|
-
if (!this.clientId_) {
|
|
227
|
-
throw new Error("no clientId");
|
|
228
|
-
}
|
|
229
|
-
return this.clientId_;
|
|
230
|
-
}
|
|
231
|
-
set clientId(clientId_) {
|
|
232
|
-
this.clientId_ = clientId_;
|
|
233
|
-
}
|
|
234
|
-
keyPrefix(clientId) {
|
|
235
|
-
return `/${this.clientName}/${this.sessionId}/${clientId}`;
|
|
236
|
-
}
|
|
237
|
-
clientInfoKey(clientId) {
|
|
238
|
-
return `${this.keyPrefix(clientId)}/client_info/`;
|
|
239
|
-
}
|
|
240
|
-
async clientInformation() {
|
|
241
|
-
if (!this.clientId_) {
|
|
242
|
-
return void 0;
|
|
243
|
-
}
|
|
244
|
-
return await this.storage.get(
|
|
245
|
-
this.clientInfoKey(this.clientId)
|
|
246
|
-
) ?? void 0;
|
|
247
|
-
}
|
|
248
|
-
async saveClientInformation(clientInformation) {
|
|
249
|
-
await this.storage.put(
|
|
250
|
-
this.clientInfoKey(clientInformation.client_id),
|
|
251
|
-
clientInformation
|
|
252
|
-
);
|
|
253
|
-
this.clientId = clientInformation.client_id;
|
|
254
|
-
}
|
|
255
|
-
tokenKey(clientId) {
|
|
256
|
-
return `${this.keyPrefix(clientId)}/token`;
|
|
257
|
-
}
|
|
258
|
-
async tokens() {
|
|
259
|
-
if (!this.clientId_) {
|
|
260
|
-
return void 0;
|
|
261
|
-
}
|
|
262
|
-
return await this.storage.get(this.tokenKey(this.clientId)) ?? void 0;
|
|
263
|
-
}
|
|
264
|
-
async saveTokens(tokens) {
|
|
265
|
-
await this.storage.put(this.tokenKey(this.clientId), tokens);
|
|
266
|
-
}
|
|
267
|
-
get authUrl() {
|
|
268
|
-
return this.authUrl_;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Because this operates on the server side (but we need browser auth), we send this url back to the user
|
|
272
|
-
* and require user interact to initiate the redirect flow
|
|
273
|
-
*/
|
|
274
|
-
async redirectToAuthorization(authUrl) {
|
|
275
|
-
const client_id = authUrl.searchParams.get("client_id");
|
|
276
|
-
if (client_id) {
|
|
277
|
-
authUrl.searchParams.append("state", client_id);
|
|
278
|
-
}
|
|
279
|
-
this.authUrl_ = authUrl.toString();
|
|
280
|
-
}
|
|
281
|
-
codeVerifierKey(clientId) {
|
|
282
|
-
return `${this.keyPrefix(clientId)}/code_verifier`;
|
|
283
|
-
}
|
|
284
|
-
async saveCodeVerifier(verifier) {
|
|
285
|
-
await this.storage.put(this.codeVerifierKey(this.clientId), verifier);
|
|
286
|
-
}
|
|
287
|
-
async codeVerifier() {
|
|
288
|
-
const codeVerifier = await this.storage.get(
|
|
289
|
-
this.codeVerifierKey(this.clientId)
|
|
290
|
-
);
|
|
291
|
-
if (!codeVerifier) {
|
|
292
|
-
throw new Error("No code verifier found");
|
|
209
|
+
function capabilityErrorHandler(empty, method) {
|
|
210
|
+
return (e) => {
|
|
211
|
+
if (e.code === -32601) {
|
|
212
|
+
console.error(
|
|
213
|
+
`The server advertised support for the capability ${method.split("/")[0]}, but returned "Method not found" for '${method}'.`
|
|
214
|
+
);
|
|
215
|
+
return empty;
|
|
293
216
|
}
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
}
|
|
217
|
+
throw e;
|
|
218
|
+
};
|
|
219
|
+
}
|
|
297
220
|
|
|
298
221
|
// src/mcp/client.ts
|
|
299
222
|
var MCPClientManager = class {
|
|
@@ -302,11 +225,11 @@ var MCPClientManager = class {
|
|
|
302
225
|
* @param version Version of the MCP Client
|
|
303
226
|
* @param auth Auth paramters if being used to create a DurableObjectOAuthClientProvider
|
|
304
227
|
*/
|
|
305
|
-
constructor(name, version
|
|
228
|
+
constructor(name, version) {
|
|
306
229
|
this.name = name;
|
|
307
230
|
this.version = version;
|
|
308
|
-
this.auth = auth;
|
|
309
231
|
this.mcpConnections = {};
|
|
232
|
+
this.callbackUrls = [];
|
|
310
233
|
}
|
|
311
234
|
/**
|
|
312
235
|
* Connect to and register an MCP server
|
|
@@ -315,20 +238,15 @@ var MCPClientManager = class {
|
|
|
315
238
|
* @param clientConfig Client config
|
|
316
239
|
* @param capabilities Client capabilities (i.e. if the client supports roots/sampling)
|
|
317
240
|
*/
|
|
318
|
-
async connect(url,
|
|
319
|
-
const id =
|
|
320
|
-
if (
|
|
241
|
+
async connect(url, options = {}) {
|
|
242
|
+
const id = options.reconnect?.id ?? crypto.randomUUID();
|
|
243
|
+
if (!options.transport?.authProvider) {
|
|
321
244
|
console.warn(
|
|
322
|
-
"
|
|
245
|
+
"No authProvider provided in the transport options. This client will only support unauthenticated remote MCP Servers"
|
|
323
246
|
);
|
|
247
|
+
} else {
|
|
248
|
+
options.transport.authProvider.serverId = id;
|
|
324
249
|
}
|
|
325
|
-
const authProvider = this.auth ? new DurableObjectOAuthClientProvider(
|
|
326
|
-
this.auth.storage,
|
|
327
|
-
this.name,
|
|
328
|
-
id,
|
|
329
|
-
`${this.auth.baseCallbackUri}/${id}`,
|
|
330
|
-
opts.reconnect?.oauthClientId
|
|
331
|
-
) : opts.transport?.authProvider;
|
|
332
250
|
this.mcpConnections[id] = new MCPClientConnection(
|
|
333
251
|
new URL(url),
|
|
334
252
|
{
|
|
@@ -336,35 +254,45 @@ var MCPClientManager = class {
|
|
|
336
254
|
version: this.version
|
|
337
255
|
},
|
|
338
256
|
{
|
|
339
|
-
transport: {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
},
|
|
343
|
-
client: opts.client ?? {},
|
|
344
|
-
capabilities: opts.client ?? {}
|
|
257
|
+
transport: options.transport ?? {},
|
|
258
|
+
client: options.client ?? {},
|
|
259
|
+
capabilities: options.client ?? {}
|
|
345
260
|
}
|
|
346
261
|
);
|
|
347
262
|
await this.mcpConnections[id].init(
|
|
348
|
-
|
|
349
|
-
|
|
263
|
+
options.reconnect?.oauthCode,
|
|
264
|
+
options.reconnect?.oauthClientId
|
|
350
265
|
);
|
|
266
|
+
const authUrl = options.transport?.authProvider?.authUrl;
|
|
267
|
+
if (authUrl && options.transport?.authProvider?.redirectUrl) {
|
|
268
|
+
this.callbackUrls.push(
|
|
269
|
+
options.transport.authProvider.redirectUrl.toString()
|
|
270
|
+
);
|
|
271
|
+
}
|
|
351
272
|
return {
|
|
352
273
|
id,
|
|
353
|
-
authUrl
|
|
274
|
+
authUrl
|
|
354
275
|
};
|
|
355
276
|
}
|
|
356
277
|
isCallbackRequest(req) {
|
|
357
|
-
|
|
358
|
-
return req.url.startsWith(
|
|
359
|
-
}
|
|
360
|
-
return false;
|
|
278
|
+
return req.method === "GET" && !!this.callbackUrls.find((url) => {
|
|
279
|
+
return req.url.startsWith(url);
|
|
280
|
+
});
|
|
361
281
|
}
|
|
362
282
|
async handleCallbackRequest(req) {
|
|
363
283
|
const url = new URL(req.url);
|
|
284
|
+
const urlMatch = this.callbackUrls.find((url2) => {
|
|
285
|
+
return req.url.startsWith(url2);
|
|
286
|
+
});
|
|
287
|
+
if (!urlMatch) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`No callback URI match found for the request url: ${req.url}. Was the request matched with \`isCallbackRequest()\`?`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
364
292
|
const code = url.searchParams.get("code");
|
|
365
293
|
const clientId = url.searchParams.get("state");
|
|
366
|
-
|
|
367
|
-
serverId =
|
|
294
|
+
const urlParams = urlMatch.split("/");
|
|
295
|
+
const serverId = urlParams[urlParams.length - 1];
|
|
368
296
|
if (!code) {
|
|
369
297
|
throw new Error("Unauthorized: no code provided");
|
|
370
298
|
}
|
|
@@ -379,13 +307,22 @@ var MCPClientManager = class {
|
|
|
379
307
|
"Failed to authenticate: the client isn't in the `authenticating` state"
|
|
380
308
|
);
|
|
381
309
|
}
|
|
382
|
-
const
|
|
310
|
+
const conn = this.mcpConnections[serverId];
|
|
311
|
+
if (!conn.options.transport.authProvider) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
"Trying to finalize authentication for a server connection without an authProvider"
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
conn.options.transport.authProvider.clientId = clientId;
|
|
317
|
+
conn.options.transport.authProvider.serverId = serverId;
|
|
318
|
+
const serverUrl = conn.url.toString();
|
|
383
319
|
await this.connect(serverUrl, {
|
|
384
320
|
reconnect: {
|
|
385
321
|
id: serverId,
|
|
386
322
|
oauthClientId: clientId,
|
|
387
323
|
oauthCode: code
|
|
388
|
-
}
|
|
324
|
+
},
|
|
325
|
+
...conn.options
|
|
389
326
|
});
|
|
390
327
|
if (this.mcpConnections[serverId].connectionState === "authenticating") {
|
|
391
328
|
throw new Error("Failed to authenticate: client failed to initialize");
|
package/dist/mcp/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/sse-edge.ts","../../src/mcp/client-connection.ts","../../src/mcp/do-oauth-client-provider.ts","../../src/mcp/client.ts"],"sourcesContent":["import {\n SSEClientTransport,\n type SSEClientTransportOptions,\n} from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport type { OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\n\nexport class SSEEdgeClientTransport extends SSEClientTransport {\n private authProvider: OAuthClientProvider | undefined;\n /**\n * Creates a new EdgeSSEClientTransport, which overrides fetch to be compatible with the CF workers environment\n */\n constructor(url: URL, options: SSEClientTransportOptions) {\n const fetchOverride: typeof fetch = async (\n fetchUrl: RequestInfo | URL,\n fetchInit: RequestInit = {}\n ) => {\n // add auth headers\n const headers = await this.authHeaders();\n const workerOptions = {\n ...fetchInit,\n headers: {\n ...fetchInit?.headers,\n ...headers,\n },\n };\n\n // Remove unsupported properties\n // biome-ignore lint/performance/noDelete: workaround for workers environment\n delete workerOptions.mode;\n\n // Call the original fetch with fixed options\n return fetch(fetchUrl, workerOptions);\n };\n\n super(url, {\n ...options,\n eventSourceInit: {\n fetch: fetchOverride,\n },\n });\n this.authProvider = options.authProvider;\n }\n\n async authHeaders() {\n if (this.authProvider) {\n const tokens = await this.authProvider.tokens();\n if (tokens) {\n return {\n Authorization: `Bearer ${tokens.access_token}`,\n };\n }\n }\n }\n}\n","import { SSEEdgeClientTransport } from \"./sse-edge\";\n\nimport {\n ToolListChangedNotificationSchema,\n type ClientCapabilities,\n type Resource,\n type Tool,\n type Prompt,\n ResourceListChangedNotificationSchema,\n PromptListChangedNotificationSchema,\n type ListToolsResult,\n type ListResourcesResult,\n type ListPromptsResult,\n type ServerCapabilities,\n type ResourceTemplate,\n type ListResourceTemplatesResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { SSEClientTransportOptions } from \"@modelcontextprotocol/sdk/client/sse.js\";\n\nexport class MCPClientConnection {\n client: Client;\n connectionState:\n | \"authenticating\"\n | \"connecting\"\n | \"ready\"\n | \"discovering\"\n | \"failed\" = \"connecting\";\n instructions?: string;\n tools: Tool[] = [];\n prompts: Prompt[] = [];\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n serverCapabilities: ServerCapabilities | undefined;\n\n constructor(\n public url: URL,\n private info: ConstructorParameters<typeof Client>[0],\n private options: {\n transport: SSEClientTransportOptions;\n client: ConstructorParameters<typeof Client>[1];\n capabilities: ClientCapabilities;\n } = { transport: {}, client: {}, capabilities: {} }\n ) {\n this.client = new Client(info, options.client);\n this.client.registerCapabilities(options.capabilities);\n }\n\n /**\n * Initialize a client connection\n *\n * @param code Optional OAuth code to initialize the connection with if auth hasn't been initialized\n * @returns\n */\n async init(code?: string, clientId?: string) {\n try {\n const transport = new SSEEdgeClientTransport(\n this.url,\n this.options.transport\n );\n if (code) {\n await transport.finishAuth(code);\n }\n await this.client.connect(transport);\n // biome-ignore lint/suspicious/noExplicitAny: allow for the error check here\n } catch (e: any) {\n if (e.toString().includes(\"Unauthorized\")) {\n // unauthorized, we should wait for the user to authenticate\n this.connectionState = \"authenticating\";\n return;\n }\n this.connectionState = \"failed\";\n throw e;\n }\n\n this.connectionState = \"discovering\";\n\n this.serverCapabilities = await this.client.getServerCapabilities();\n if (!this.serverCapabilities) {\n throw new Error(\"The MCP Server failed to return server capabilities\");\n }\n\n const [instructions, tools, resources, prompts, resourceTemplates] =\n await Promise.all([\n this.client.getInstructions(),\n this.registerTools(),\n this.registerResources(),\n this.registerPrompts(),\n this.registerResourceTemplates(),\n ]);\n\n this.instructions = instructions;\n this.tools = tools;\n this.resources = resources;\n this.prompts = prompts;\n this.resourceTemplates = resourceTemplates;\n\n this.connectionState = \"ready\";\n }\n\n /**\n * Notification handler registration\n */\n async registerTools(): Promise<Tool[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.tools) {\n return [];\n }\n\n if (this.serverCapabilities.tools.listChanged) {\n this.client.setNotificationHandler(\n ToolListChangedNotificationSchema,\n async (_notification) => {\n this.tools = await this.fetchTools();\n }\n );\n }\n\n return this.fetchTools();\n }\n\n async registerResources(): Promise<Resource[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.resources) {\n return [];\n }\n\n if (this.serverCapabilities.resources.listChanged) {\n this.client.setNotificationHandler(\n ResourceListChangedNotificationSchema,\n async (_notification) => {\n this.resources = await this.fetchResources();\n }\n );\n }\n\n return this.fetchResources();\n }\n\n async registerPrompts(): Promise<Prompt[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.prompts) {\n return [];\n }\n\n if (this.serverCapabilities.prompts.listChanged) {\n this.client.setNotificationHandler(\n PromptListChangedNotificationSchema,\n async (_notification) => {\n this.prompts = await this.fetchPrompts();\n }\n );\n }\n\n return this.fetchPrompts();\n }\n\n async registerResourceTemplates(): Promise<ResourceTemplate[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.resources) {\n return [];\n }\n\n return this.fetchResourceTemplates();\n }\n\n async fetchTools() {\n let toolsAgg: Tool[] = [];\n let toolsResult: ListToolsResult = { tools: [] };\n do {\n toolsResult = await this.client.listTools({\n cursor: toolsResult.nextCursor,\n });\n toolsAgg = toolsAgg.concat(toolsResult.tools);\n } while (toolsResult.nextCursor);\n return toolsAgg;\n }\n\n async fetchResources() {\n let resourcesAgg: Resource[] = [];\n let resourcesResult: ListResourcesResult = { resources: [] };\n do {\n resourcesResult = await this.client.listResources({\n cursor: resourcesResult.nextCursor,\n });\n resourcesAgg = resourcesAgg.concat(resourcesResult.resources);\n } while (resourcesResult.nextCursor);\n return resourcesAgg;\n }\n\n async fetchPrompts() {\n let promptsAgg: Prompt[] = [];\n let promptsResult: ListPromptsResult = { prompts: [] };\n do {\n promptsResult = await this.client.listPrompts({\n cursor: promptsResult.nextCursor,\n });\n promptsAgg = promptsAgg.concat(promptsResult.prompts);\n } while (promptsResult.nextCursor);\n return promptsAgg;\n }\n\n async fetchResourceTemplates() {\n let templatesAgg: ResourceTemplate[] = [];\n let templatesResult: ListResourceTemplatesResult = {\n resourceTemplates: [],\n };\n do {\n templatesResult = await this.client.listResourceTemplates({\n cursor: templatesResult.nextCursor,\n });\n templatesAgg = templatesAgg.concat(templatesResult.resourceTemplates);\n } while (templatesResult.nextCursor);\n return templatesAgg;\n }\n}\n","import type { OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport type {\n OAuthTokens,\n OAuthClientMetadata,\n OAuthClientInformation,\n OAuthClientInformationFull,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\n// A slight extension to the standard OAuthClientProvider interface because `redirectToAuthorization` doesn't give us the interface we need\nexport interface AgentsOAuthProvider extends OAuthClientProvider {\n authUrl: string | undefined;\n clientId: string | undefined;\n}\n\nexport class DurableObjectOAuthClientProvider implements AgentsOAuthProvider {\n private authUrl_: string | undefined;\n\n constructor(\n public storage: DurableObjectStorage,\n public clientName: string,\n public sessionId: string,\n public redirectUrl: string,\n private clientId_?: string\n ) {}\n\n get clientMetadata(): OAuthClientMetadata {\n return {\n redirect_uris: [this.redirectUrl],\n token_endpoint_auth_method: \"none\",\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n client_name: this.clientName,\n client_uri: \"example.com\",\n };\n }\n\n get clientId() {\n if (!this.clientId_) {\n throw new Error(\"no clientId\");\n }\n return this.clientId_;\n }\n\n set clientId(clientId_: string) {\n this.clientId_ = clientId_;\n }\n\n keyPrefix(clientId: string) {\n return `/${this.clientName}/${this.sessionId}/${clientId}`;\n }\n\n clientInfoKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/client_info/`;\n }\n\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n if (!this.clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthClientInformation>(\n this.clientInfoKey(this.clientId)\n )) ?? undefined\n );\n }\n\n async saveClientInformation(\n clientInformation: OAuthClientInformationFull\n ): Promise<void> {\n await this.storage.put(\n this.clientInfoKey(clientInformation.client_id),\n clientInformation\n );\n this.clientId = clientInformation.client_id;\n }\n\n tokenKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/token`;\n }\n\n async tokens(): Promise<OAuthTokens | undefined> {\n if (!this.clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthTokens>(this.tokenKey(this.clientId))) ??\n undefined\n );\n }\n\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n await this.storage.put(this.tokenKey(this.clientId), tokens);\n }\n\n get authUrl() {\n return this.authUrl_;\n }\n\n /**\n * Because this operates on the server side (but we need browser auth), we send this url back to the user\n * and require user interact to initiate the redirect flow\n */\n async redirectToAuthorization(authUrl: URL): Promise<void> {\n // We want to track the client ID in state here because the typescript SSE client sometimes does\n // a dynamic client registration AFTER generating this redirect URL.\n const client_id = authUrl.searchParams.get(\"client_id\");\n if (client_id) {\n authUrl.searchParams.append(\"state\", client_id);\n }\n this.authUrl_ = authUrl.toString();\n }\n\n codeVerifierKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/code_verifier`;\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n await this.storage.put(this.codeVerifierKey(this.clientId), verifier);\n }\n\n async codeVerifier(): Promise<string> {\n const codeVerifier = await this.storage.get<string>(\n this.codeVerifierKey(this.clientId)\n );\n if (!codeVerifier) {\n throw new Error(\"No code verifier found\");\n }\n return codeVerifier;\n }\n}\n","import { MCPClientConnection } from \"./client-connection\";\n\nimport type {\n ClientCapabilities,\n CallToolRequest,\n CallToolResultSchema,\n CompatibilityCallToolResultSchema,\n ReadResourceRequest,\n GetPromptRequest,\n Tool,\n Resource,\n Prompt,\n ResourceTemplate,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { SSEClientTransportOptions } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport type { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { RequestOptions } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport {\n DurableObjectOAuthClientProvider,\n type AgentsOAuthProvider,\n} from \"./do-oauth-client-provider\";\n\n/**\n * Utility class that aggregates multiple MCP clients into one\n */\nexport class MCPClientManager {\n public mcpConnections: Record<string, MCPClientConnection> = {};\n\n /**\n * @param name Name of the MCP client\n * @param version Version of the MCP Client\n * @param auth Auth paramters if being used to create a DurableObjectOAuthClientProvider\n */\n constructor(\n private name: string,\n private version: string,\n private auth?: { baseCallbackUri: string; storage: DurableObjectStorage }\n ) {}\n\n /**\n * Connect to and register an MCP server\n *\n * @param transportConfig Transport config\n * @param clientConfig Client config\n * @param capabilities Client capabilities (i.e. if the client supports roots/sampling)\n */\n async connect(\n url: string,\n opts: {\n // Allows you to reconnect to a server (in the case of a auth reconnect)\n // Doesn't handle session reconnection\n reconnect?: {\n id: string;\n oauthClientId?: string;\n oauthCode?: string;\n };\n // we're overriding authProvider here because we want to be able to access the auth URL\n transport?: SSEClientTransportOptions & {\n authProvider: AgentsOAuthProvider;\n };\n client?: ConstructorParameters<typeof Client>[1];\n capabilities?: ClientCapabilities;\n } = {}\n ): Promise<{ id: string; authUrl: string | undefined }> {\n const id = opts.reconnect?.id ?? crypto.randomUUID();\n\n // if we have global auth for the manager AND there's no authProvider override\n // then let's setup an auth provider\n\n if (this.auth) {\n console.warn(\n \"Using .auth configuration to generate an oauth provider, this is temporary and will be removed in the next version. Instead use transport.authProvider to provide an auth provider\"\n );\n }\n\n const authProvider: AgentsOAuthProvider | undefined = this.auth\n ? new DurableObjectOAuthClientProvider(\n this.auth.storage,\n this.name,\n id,\n `${this.auth.baseCallbackUri}/${id}`,\n opts.reconnect?.oauthClientId\n )\n : opts.transport?.authProvider;\n\n this.mcpConnections[id] = new MCPClientConnection(\n new URL(url),\n {\n name: this.name,\n version: this.version,\n },\n {\n transport: {\n ...opts.transport,\n authProvider,\n },\n client: opts.client ?? {},\n capabilities: opts.client ?? {},\n }\n );\n\n await this.mcpConnections[id].init(\n opts.reconnect?.oauthCode,\n opts.reconnect?.oauthClientId\n );\n\n return {\n id,\n authUrl: authProvider?.authUrl,\n };\n }\n\n isCallbackRequest(req: Request): boolean {\n if (this.auth?.baseCallbackUri) {\n return (\n req.url.startsWith(this.auth.baseCallbackUri) && req.method === \"GET\"\n );\n }\n return false;\n }\n\n async handleCallbackRequest(req: Request) {\n const url = new URL(req.url);\n const code = url.searchParams.get(\"code\");\n const clientId = url.searchParams.get(\"state\");\n let serverId = req.url\n .replace(this.auth!.baseCallbackUri, \"\")\n .split(\"?\")[0];\n serverId = serverId.replaceAll(\"/\", \"\");\n if (!code) {\n throw new Error(\"Unauthorized: no code provided\");\n }\n if (!clientId) {\n throw new Error(\"Unauthorized: no state provided\");\n }\n\n if (this.mcpConnections[serverId] === undefined) {\n throw new Error(`Could not find serverId: ${serverId}`);\n }\n\n if (this.mcpConnections[serverId].connectionState !== \"authenticating\") {\n throw new Error(\n \"Failed to authenticate: the client isn't in the `authenticating` state\"\n );\n }\n\n // reconnect to server with authorization\n const serverUrl = this.mcpConnections[serverId].url.toString();\n await this.connect(serverUrl, {\n reconnect: {\n id: serverId,\n oauthClientId: clientId,\n oauthCode: code,\n },\n });\n\n if (this.mcpConnections[serverId].connectionState === \"authenticating\") {\n throw new Error(\"Failed to authenticate: client failed to initialize\");\n }\n\n return { serverId };\n }\n\n /**\n * @returns namespaced list of tools\n */\n listTools(): NamespacedData[\"tools\"] {\n return getNamespacedData(this.mcpConnections, \"tools\");\n }\n\n /**\n * @returns namespaced list of prompts\n */\n listPrompts(): NamespacedData[\"prompts\"] {\n return getNamespacedData(this.mcpConnections, \"prompts\");\n }\n\n /**\n * @returns namespaced list of tools\n */\n listResources(): NamespacedData[\"resources\"] {\n return getNamespacedData(this.mcpConnections, \"resources\");\n }\n\n /**\n * @returns namespaced list of resource templates\n */\n listResourceTemplates(): NamespacedData[\"resourceTemplates\"] {\n return getNamespacedData(this.mcpConnections, \"resourceTemplates\");\n }\n\n /**\n * Namespaced version of callTool\n */\n callTool(\n params: CallToolRequest[\"params\"] & { serverId: string },\n resultSchema:\n | typeof CallToolResultSchema\n | typeof CompatibilityCallToolResultSchema,\n options: RequestOptions\n ) {\n const unqualifiedName = params.name.replace(`${params.serverId}.`, \"\");\n return this.mcpConnections[params.serverId].client.callTool(\n {\n ...params,\n name: unqualifiedName,\n },\n resultSchema,\n options\n );\n }\n\n /**\n * Namespaced version of readResource\n */\n readResource(\n params: ReadResourceRequest[\"params\"] & { serverId: string },\n options: RequestOptions\n ) {\n return this.mcpConnections[params.serverId].client.readResource(\n params,\n options\n );\n }\n\n /**\n * Namespaced version of getPrompt\n */\n getPrompt(\n params: GetPromptRequest[\"params\"] & { serverId: string },\n options: RequestOptions\n ) {\n return this.mcpConnections[params.serverId].client.getPrompt(\n params,\n options\n );\n }\n}\n\ntype NamespacedData = {\n tools: (Tool & { serverId: string })[];\n prompts: (Prompt & { serverId: string })[];\n resources: (Resource & { serverId: string })[];\n resourceTemplates: (ResourceTemplate & { serverId: string })[];\n};\n\nexport function getNamespacedData<T extends keyof NamespacedData>(\n mcpClients: Record<string, MCPClientConnection>,\n type: T\n): NamespacedData[T] {\n const sets = Object.entries(mcpClients).map(([name, conn]) => {\n return { name, data: conn[type] };\n });\n\n const namespacedData = sets.flatMap(({ name: serverId, data }) => {\n return data.map((item) => {\n return {\n ...item,\n // we add a serverId so we can easily pull it out and send the tool call to the right server\n serverId,\n };\n });\n });\n\n return namespacedData as NamespacedData[T]; // Type assertion needed due to TS limitations with conditional return types\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/sse-edge.ts","../../src/mcp/client-connection.ts","../../src/mcp/client.ts"],"sourcesContent":["import {\n SSEClientTransport,\n type SSEClientTransportOptions,\n} from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport type { OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\n\nexport class SSEEdgeClientTransport extends SSEClientTransport {\n private authProvider: OAuthClientProvider | undefined;\n /**\n * Creates a new EdgeSSEClientTransport, which overrides fetch to be compatible with the CF workers environment\n */\n constructor(url: URL, options: SSEClientTransportOptions) {\n const fetchOverride: typeof fetch = async (\n fetchUrl: RequestInfo | URL,\n fetchInit: RequestInit = {}\n ) => {\n // add auth headers\n const headers = await this.authHeaders();\n const workerOptions = {\n ...fetchInit,\n headers: {\n ...fetchInit?.headers,\n ...headers,\n },\n };\n\n // Remove unsupported properties\n // biome-ignore lint/performance/noDelete: workaround for workers environment\n delete workerOptions.mode;\n\n // Call the original fetch with fixed options\n return fetch(fetchUrl, workerOptions);\n };\n\n super(url, {\n ...options,\n eventSourceInit: {\n fetch: fetchOverride,\n },\n });\n this.authProvider = options.authProvider;\n }\n\n async authHeaders() {\n if (this.authProvider) {\n const tokens = await this.authProvider.tokens();\n if (tokens) {\n return {\n Authorization: `Bearer ${tokens.access_token}`,\n };\n }\n }\n }\n}\n","import { SSEEdgeClientTransport } from \"./sse-edge\";\n\nimport {\n ToolListChangedNotificationSchema,\n type ClientCapabilities,\n type Resource,\n type Tool,\n type Prompt,\n ResourceListChangedNotificationSchema,\n PromptListChangedNotificationSchema,\n type ListToolsResult,\n type ListResourcesResult,\n type ListPromptsResult,\n type ServerCapabilities,\n type ResourceTemplate,\n type ListResourceTemplatesResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { SSEClientTransportOptions } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport type { AgentsOAuthProvider } from \"./do-oauth-client-provider\";\n\nexport class MCPClientConnection {\n client: Client;\n connectionState:\n | \"authenticating\"\n | \"connecting\"\n | \"ready\"\n | \"discovering\"\n | \"failed\" = \"connecting\";\n instructions?: string;\n tools: Tool[] = [];\n prompts: Prompt[] = [];\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n serverCapabilities: ServerCapabilities | undefined;\n\n constructor(\n public url: URL,\n info: ConstructorParameters<typeof Client>[0],\n public options: {\n transport: SSEClientTransportOptions & {\n authProvider?: AgentsOAuthProvider;\n };\n client: ConstructorParameters<typeof Client>[1];\n capabilities: ClientCapabilities;\n } = { transport: {}, client: {}, capabilities: {} }\n ) {\n this.client = new Client(info, options.client);\n this.client.registerCapabilities(options.capabilities);\n }\n\n /**\n * Initialize a client connection\n *\n * @param code Optional OAuth code to initialize the connection with if auth hasn't been initialized\n * @returns\n */\n async init(code?: string, clientId?: string) {\n try {\n const transport = new SSEEdgeClientTransport(\n this.url,\n this.options.transport\n );\n if (code) {\n await transport.finishAuth(code);\n }\n\n await this.client.connect(transport);\n // biome-ignore lint/suspicious/noExplicitAny: allow for the error check here\n } catch (e: any) {\n if (e.toString().includes(\"Unauthorized\")) {\n // unauthorized, we should wait for the user to authenticate\n this.connectionState = \"authenticating\";\n return;\n }\n this.connectionState = \"failed\";\n throw e;\n }\n\n this.connectionState = \"discovering\";\n\n this.serverCapabilities = await this.client.getServerCapabilities();\n if (!this.serverCapabilities) {\n throw new Error(\"The MCP Server failed to return server capabilities\");\n }\n\n const [instructions, tools, resources, prompts, resourceTemplates] =\n await Promise.all([\n this.client.getInstructions(),\n this.registerTools(),\n this.registerResources(),\n this.registerPrompts(),\n this.registerResourceTemplates(),\n ]);\n\n this.instructions = instructions;\n this.tools = tools;\n this.resources = resources;\n this.prompts = prompts;\n this.resourceTemplates = resourceTemplates;\n\n this.connectionState = \"ready\";\n }\n\n /**\n * Notification handler registration\n */\n async registerTools(): Promise<Tool[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.tools) {\n return [];\n }\n\n if (this.serverCapabilities.tools.listChanged) {\n this.client.setNotificationHandler(\n ToolListChangedNotificationSchema,\n async (_notification) => {\n this.tools = await this.fetchTools();\n }\n );\n }\n\n return this.fetchTools();\n }\n\n async registerResources(): Promise<Resource[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.resources) {\n return [];\n }\n\n if (this.serverCapabilities.resources.listChanged) {\n this.client.setNotificationHandler(\n ResourceListChangedNotificationSchema,\n async (_notification) => {\n this.resources = await this.fetchResources();\n }\n );\n }\n\n return this.fetchResources();\n }\n\n async registerPrompts(): Promise<Prompt[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.prompts) {\n return [];\n }\n\n if (this.serverCapabilities.prompts.listChanged) {\n this.client.setNotificationHandler(\n PromptListChangedNotificationSchema,\n async (_notification) => {\n this.prompts = await this.fetchPrompts();\n }\n );\n }\n\n return this.fetchPrompts();\n }\n\n async registerResourceTemplates(): Promise<ResourceTemplate[]> {\n if (!this.serverCapabilities || !this.serverCapabilities.resources) {\n return [];\n }\n\n return this.fetchResourceTemplates();\n }\n\n async fetchTools() {\n let toolsAgg: Tool[] = [];\n let toolsResult: ListToolsResult = { tools: [] };\n do {\n toolsResult = await this.client\n .listTools({\n cursor: toolsResult.nextCursor,\n })\n .catch(capabilityErrorHandler({ tools: [] }, \"tools/list\"));\n toolsAgg = toolsAgg.concat(toolsResult.tools);\n } while (toolsResult.nextCursor);\n return toolsAgg;\n }\n\n async fetchResources() {\n let resourcesAgg: Resource[] = [];\n let resourcesResult: ListResourcesResult = { resources: [] };\n do {\n resourcesResult = await this.client\n .listResources({\n cursor: resourcesResult.nextCursor,\n })\n .catch(capabilityErrorHandler({ resources: [] }, \"resources/list\"));\n resourcesAgg = resourcesAgg.concat(resourcesResult.resources);\n } while (resourcesResult.nextCursor);\n return resourcesAgg;\n }\n\n async fetchPrompts() {\n let promptsAgg: Prompt[] = [];\n let promptsResult: ListPromptsResult = { prompts: [] };\n do {\n promptsResult = await this.client\n .listPrompts({\n cursor: promptsResult.nextCursor,\n })\n .catch(capabilityErrorHandler({ prompts: [] }, \"prompts/list\"));\n promptsAgg = promptsAgg.concat(promptsResult.prompts);\n } while (promptsResult.nextCursor);\n return promptsAgg;\n }\n\n async fetchResourceTemplates() {\n let templatesAgg: ResourceTemplate[] = [];\n let templatesResult: ListResourceTemplatesResult = {\n resourceTemplates: [],\n };\n do {\n templatesResult = await this.client\n .listResourceTemplates({\n cursor: templatesResult.nextCursor,\n })\n .catch(\n capabilityErrorHandler(\n { resourceTemplates: [] },\n \"resources/templates/list\"\n )\n );\n templatesAgg = templatesAgg.concat(templatesResult.resourceTemplates);\n } while (templatesResult.nextCursor);\n return templatesAgg;\n }\n}\n\nfunction capabilityErrorHandler<T>(empty: T, method: string) {\n return (e: { code: number }) => {\n // server is badly behaved and returning invalid capabilities. This commonly occurs for resource templates\n if (e.code === -32601) {\n console.error(\n `The server advertised support for the capability ${method.split(\"/\")[0]}, but returned \"Method not found\" for '${method}'.`\n );\n return empty;\n }\n throw e;\n };\n}\n","import { MCPClientConnection } from \"./client-connection\";\n\nimport type {\n ClientCapabilities,\n CallToolRequest,\n CallToolResultSchema,\n CompatibilityCallToolResultSchema,\n ReadResourceRequest,\n GetPromptRequest,\n Tool,\n Resource,\n Prompt,\n ResourceTemplate,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { SSEClientTransportOptions } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport type { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { RequestOptions } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type { AgentsOAuthProvider } from \"./do-oauth-client-provider\";\n\n/**\n * Utility class that aggregates multiple MCP clients into one\n */\nexport class MCPClientManager {\n public mcpConnections: Record<string, MCPClientConnection> = {};\n private callbackUrls: string[] = [];\n\n /**\n * @param name Name of the MCP client\n * @param version Version of the MCP Client\n * @param auth Auth paramters if being used to create a DurableObjectOAuthClientProvider\n */\n constructor(\n private name: string,\n private version: string\n ) {}\n\n /**\n * Connect to and register an MCP server\n *\n * @param transportConfig Transport config\n * @param clientConfig Client config\n * @param capabilities Client capabilities (i.e. if the client supports roots/sampling)\n */\n async connect(\n url: string,\n options: {\n // Allows you to reconnect to a server (in the case of a auth reconnect)\n // Doesn't handle session reconnection\n reconnect?: {\n id: string;\n oauthClientId?: string;\n oauthCode?: string;\n };\n // we're overriding authProvider here because we want to be able to access the auth URL\n transport?: SSEClientTransportOptions & {\n authProvider?: AgentsOAuthProvider;\n };\n client?: ConstructorParameters<typeof Client>[1];\n capabilities?: ClientCapabilities;\n } = {}\n ): Promise<{ id: string; authUrl: string | undefined }> {\n const id = options.reconnect?.id ?? crypto.randomUUID();\n\n if (!options.transport?.authProvider) {\n console.warn(\n \"No authProvider provided in the transport options. This client will only support unauthenticated remote MCP Servers\"\n );\n } else {\n options.transport.authProvider.serverId = id;\n }\n\n this.mcpConnections[id] = new MCPClientConnection(\n new URL(url),\n {\n name: this.name,\n version: this.version,\n },\n {\n transport: options.transport ?? {},\n client: options.client ?? {},\n capabilities: options.client ?? {},\n }\n );\n\n await this.mcpConnections[id].init(\n options.reconnect?.oauthCode,\n options.reconnect?.oauthClientId\n );\n\n const authUrl = options.transport?.authProvider?.authUrl;\n if (authUrl && options.transport?.authProvider?.redirectUrl) {\n this.callbackUrls.push(\n options.transport.authProvider.redirectUrl.toString()\n );\n }\n\n return {\n id,\n authUrl,\n };\n }\n\n isCallbackRequest(req: Request): boolean {\n return (\n req.method === \"GET\" &&\n !!this.callbackUrls.find((url) => {\n return req.url.startsWith(url);\n })\n );\n }\n\n async handleCallbackRequest(req: Request) {\n const url = new URL(req.url);\n const urlMatch = this.callbackUrls.find((url) => {\n return req.url.startsWith(url);\n });\n if (!urlMatch) {\n throw new Error(\n `No callback URI match found for the request url: ${req.url}. Was the request matched with \\`isCallbackRequest()\\`?`\n );\n }\n const code = url.searchParams.get(\"code\");\n const clientId = url.searchParams.get(\"state\");\n const urlParams = urlMatch.split(\"/\");\n const serverId = urlParams[urlParams.length - 1];\n if (!code) {\n throw new Error(\"Unauthorized: no code provided\");\n }\n if (!clientId) {\n throw new Error(\"Unauthorized: no state provided\");\n }\n\n if (this.mcpConnections[serverId] === undefined) {\n throw new Error(`Could not find serverId: ${serverId}`);\n }\n\n if (this.mcpConnections[serverId].connectionState !== \"authenticating\") {\n throw new Error(\n \"Failed to authenticate: the client isn't in the `authenticating` state\"\n );\n }\n\n const conn = this.mcpConnections[serverId];\n if (!conn.options.transport.authProvider) {\n throw new Error(\n \"Trying to finalize authentication for a server connection without an authProvider\"\n );\n }\n\n conn.options.transport.authProvider.clientId = clientId;\n conn.options.transport.authProvider.serverId = serverId;\n\n // reconnect to server with authorization\n const serverUrl = conn.url.toString();\n await this.connect(serverUrl, {\n reconnect: {\n id: serverId,\n oauthClientId: clientId,\n oauthCode: code,\n },\n ...conn.options,\n });\n\n if (this.mcpConnections[serverId].connectionState === \"authenticating\") {\n throw new Error(\"Failed to authenticate: client failed to initialize\");\n }\n\n return { serverId };\n }\n\n /**\n * @returns namespaced list of tools\n */\n listTools(): NamespacedData[\"tools\"] {\n return getNamespacedData(this.mcpConnections, \"tools\");\n }\n\n /**\n * @returns namespaced list of prompts\n */\n listPrompts(): NamespacedData[\"prompts\"] {\n return getNamespacedData(this.mcpConnections, \"prompts\");\n }\n\n /**\n * @returns namespaced list of tools\n */\n listResources(): NamespacedData[\"resources\"] {\n return getNamespacedData(this.mcpConnections, \"resources\");\n }\n\n /**\n * @returns namespaced list of resource templates\n */\n listResourceTemplates(): NamespacedData[\"resourceTemplates\"] {\n return getNamespacedData(this.mcpConnections, \"resourceTemplates\");\n }\n\n /**\n * Namespaced version of callTool\n */\n callTool(\n params: CallToolRequest[\"params\"] & { serverId: string },\n resultSchema:\n | typeof CallToolResultSchema\n | typeof CompatibilityCallToolResultSchema,\n options: RequestOptions\n ) {\n const unqualifiedName = params.name.replace(`${params.serverId}.`, \"\");\n return this.mcpConnections[params.serverId].client.callTool(\n {\n ...params,\n name: unqualifiedName,\n },\n resultSchema,\n options\n );\n }\n\n /**\n * Namespaced version of readResource\n */\n readResource(\n params: ReadResourceRequest[\"params\"] & { serverId: string },\n options: RequestOptions\n ) {\n return this.mcpConnections[params.serverId].client.readResource(\n params,\n options\n );\n }\n\n /**\n * Namespaced version of getPrompt\n */\n getPrompt(\n params: GetPromptRequest[\"params\"] & { serverId: string },\n options: RequestOptions\n ) {\n return this.mcpConnections[params.serverId].client.getPrompt(\n params,\n options\n );\n }\n}\n\ntype NamespacedData = {\n tools: (Tool & { serverId: string })[];\n prompts: (Prompt & { serverId: string })[];\n resources: (Resource & { serverId: string })[];\n resourceTemplates: (ResourceTemplate & { serverId: string })[];\n};\n\nexport function getNamespacedData<T extends keyof NamespacedData>(\n mcpClients: Record<string, MCPClientConnection>,\n type: T\n): NamespacedData[T] {\n const sets = Object.entries(mcpClients).map(([name, conn]) => {\n return { name, data: conn[type] };\n });\n\n const namespacedData = sets.flatMap(({ name: serverId, data }) => {\n return data.map((item) => {\n return {\n ...item,\n // we add a serverId so we can easily pull it out and send the tool call to the right server\n serverId,\n };\n });\n });\n\n return namespacedData as NamespacedData[T]; // Type assertion needed due to TS limitations with conditional return types\n}\n"],"mappings":"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","names":["url"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
2
|
+
import { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
3
|
+
|
|
4
|
+
interface AgentsOAuthProvider extends OAuthClientProvider {
|
|
5
|
+
authUrl: string | undefined;
|
|
6
|
+
clientId: string | undefined;
|
|
7
|
+
serverId: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
declare class DurableObjectOAuthClientProvider implements AgentsOAuthProvider {
|
|
10
|
+
storage: DurableObjectStorage;
|
|
11
|
+
clientName: string;
|
|
12
|
+
baseRedirectUrl: string;
|
|
13
|
+
private authUrl_;
|
|
14
|
+
private serverId_;
|
|
15
|
+
private clientId_;
|
|
16
|
+
constructor(storage: DurableObjectStorage, clientName: string, baseRedirectUrl: string);
|
|
17
|
+
get clientMetadata(): OAuthClientMetadata;
|
|
18
|
+
get redirectUrl(): string;
|
|
19
|
+
get clientId(): string;
|
|
20
|
+
set clientId(clientId_: string);
|
|
21
|
+
get serverId(): string;
|
|
22
|
+
set serverId(serverId_: string);
|
|
23
|
+
keyPrefix(clientId: string): string;
|
|
24
|
+
clientInfoKey(clientId: string): string;
|
|
25
|
+
clientInformation(): Promise<OAuthClientInformation | undefined>;
|
|
26
|
+
saveClientInformation(clientInformation: OAuthClientInformationFull): Promise<void>;
|
|
27
|
+
tokenKey(clientId: string): string;
|
|
28
|
+
tokens(): Promise<OAuthTokens | undefined>;
|
|
29
|
+
saveTokens(tokens: OAuthTokens): Promise<void>;
|
|
30
|
+
get authUrl(): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Because this operates on the server side (but we need browser auth), we send this url back to the user
|
|
33
|
+
* and require user interact to initiate the redirect flow
|
|
34
|
+
*/
|
|
35
|
+
redirectToAuthorization(authUrl: URL): Promise<void>;
|
|
36
|
+
codeVerifierKey(clientId: string): string;
|
|
37
|
+
saveCodeVerifier(verifier: string): Promise<void>;
|
|
38
|
+
codeVerifier(): Promise<string>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { type AgentsOAuthProvider, DurableObjectOAuthClientProvider };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import "../chunk-HMLY7DHA.js";
|
|
2
|
+
|
|
3
|
+
// src/mcp/do-oauth-client-provider.ts
|
|
4
|
+
var DurableObjectOAuthClientProvider = class {
|
|
5
|
+
constructor(storage, clientName, baseRedirectUrl) {
|
|
6
|
+
this.storage = storage;
|
|
7
|
+
this.clientName = clientName;
|
|
8
|
+
this.baseRedirectUrl = baseRedirectUrl;
|
|
9
|
+
}
|
|
10
|
+
get clientMetadata() {
|
|
11
|
+
return {
|
|
12
|
+
redirect_uris: [this.redirectUrl],
|
|
13
|
+
token_endpoint_auth_method: "none",
|
|
14
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
15
|
+
response_types: ["code"],
|
|
16
|
+
client_name: this.clientName,
|
|
17
|
+
client_uri: "example.com"
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
get redirectUrl() {
|
|
21
|
+
return `${this.baseRedirectUrl}/${this.serverId}`;
|
|
22
|
+
}
|
|
23
|
+
get clientId() {
|
|
24
|
+
if (!this.clientId_) {
|
|
25
|
+
throw new Error("Trying to access clientId before it was set");
|
|
26
|
+
}
|
|
27
|
+
return this.clientId_;
|
|
28
|
+
}
|
|
29
|
+
set clientId(clientId_) {
|
|
30
|
+
this.clientId_ = clientId_;
|
|
31
|
+
}
|
|
32
|
+
get serverId() {
|
|
33
|
+
if (!this.serverId_) {
|
|
34
|
+
throw new Error("Trying to access serverId before it was set");
|
|
35
|
+
}
|
|
36
|
+
return this.serverId_;
|
|
37
|
+
}
|
|
38
|
+
set serverId(serverId_) {
|
|
39
|
+
this.serverId_ = serverId_;
|
|
40
|
+
}
|
|
41
|
+
keyPrefix(clientId) {
|
|
42
|
+
return `/${this.clientName}/${this.serverId}/${clientId}`;
|
|
43
|
+
}
|
|
44
|
+
clientInfoKey(clientId) {
|
|
45
|
+
return `${this.keyPrefix(clientId)}/client_info/`;
|
|
46
|
+
}
|
|
47
|
+
async clientInformation() {
|
|
48
|
+
if (!this.clientId_) {
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
return await this.storage.get(
|
|
52
|
+
this.clientInfoKey(this.clientId)
|
|
53
|
+
) ?? void 0;
|
|
54
|
+
}
|
|
55
|
+
async saveClientInformation(clientInformation) {
|
|
56
|
+
await this.storage.put(
|
|
57
|
+
this.clientInfoKey(clientInformation.client_id),
|
|
58
|
+
clientInformation
|
|
59
|
+
);
|
|
60
|
+
this.clientId = clientInformation.client_id;
|
|
61
|
+
}
|
|
62
|
+
tokenKey(clientId) {
|
|
63
|
+
return `${this.keyPrefix(clientId)}/token`;
|
|
64
|
+
}
|
|
65
|
+
async tokens() {
|
|
66
|
+
if (!this.clientId_) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
return await this.storage.get(this.tokenKey(this.clientId)) ?? void 0;
|
|
70
|
+
}
|
|
71
|
+
async saveTokens(tokens) {
|
|
72
|
+
await this.storage.put(this.tokenKey(this.clientId), tokens);
|
|
73
|
+
}
|
|
74
|
+
get authUrl() {
|
|
75
|
+
return this.authUrl_;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Because this operates on the server side (but we need browser auth), we send this url back to the user
|
|
79
|
+
* and require user interact to initiate the redirect flow
|
|
80
|
+
*/
|
|
81
|
+
async redirectToAuthorization(authUrl) {
|
|
82
|
+
const client_id = authUrl.searchParams.get("client_id");
|
|
83
|
+
if (client_id) {
|
|
84
|
+
authUrl.searchParams.append("state", client_id);
|
|
85
|
+
}
|
|
86
|
+
this.authUrl_ = authUrl.toString();
|
|
87
|
+
}
|
|
88
|
+
codeVerifierKey(clientId) {
|
|
89
|
+
return `${this.keyPrefix(clientId)}/code_verifier`;
|
|
90
|
+
}
|
|
91
|
+
async saveCodeVerifier(verifier) {
|
|
92
|
+
await this.storage.put(this.codeVerifierKey(this.clientId), verifier);
|
|
93
|
+
}
|
|
94
|
+
async codeVerifier() {
|
|
95
|
+
const codeVerifier = await this.storage.get(
|
|
96
|
+
this.codeVerifierKey(this.clientId)
|
|
97
|
+
);
|
|
98
|
+
if (!codeVerifier) {
|
|
99
|
+
throw new Error("No code verifier found");
|
|
100
|
+
}
|
|
101
|
+
return codeVerifier;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
export {
|
|
105
|
+
DurableObjectOAuthClientProvider
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=do-oauth-client-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/do-oauth-client-provider.ts"],"sourcesContent":["import type { OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport type {\n OAuthTokens,\n OAuthClientMetadata,\n OAuthClientInformation,\n OAuthClientInformationFull,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\";\n\n// A slight extension to the standard OAuthClientProvider interface because `redirectToAuthorization` doesn't give us the interface we need\n// This allows us to track authentication for a specific server and associated dynamic client registration\nexport interface AgentsOAuthProvider extends OAuthClientProvider {\n authUrl: string | undefined;\n clientId: string | undefined;\n serverId: string | undefined;\n}\n\nexport class DurableObjectOAuthClientProvider implements AgentsOAuthProvider {\n private authUrl_: string | undefined;\n private serverId_: string | undefined;\n private clientId_: string | undefined;\n\n constructor(\n public storage: DurableObjectStorage,\n public clientName: string,\n public baseRedirectUrl: string\n ) {}\n\n get clientMetadata(): OAuthClientMetadata {\n return {\n redirect_uris: [this.redirectUrl],\n token_endpoint_auth_method: \"none\",\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n client_name: this.clientName,\n client_uri: \"example.com\",\n };\n }\n\n get redirectUrl() {\n return `${this.baseRedirectUrl}/${this.serverId}`;\n }\n\n get clientId() {\n if (!this.clientId_) {\n throw new Error(\"Trying to access clientId before it was set\");\n }\n return this.clientId_;\n }\n\n set clientId(clientId_: string) {\n this.clientId_ = clientId_;\n }\n\n get serverId() {\n if (!this.serverId_) {\n throw new Error(\"Trying to access serverId before it was set\");\n }\n return this.serverId_;\n }\n\n set serverId(serverId_: string) {\n this.serverId_ = serverId_;\n }\n\n keyPrefix(clientId: string) {\n return `/${this.clientName}/${this.serverId}/${clientId}`;\n }\n\n clientInfoKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/client_info/`;\n }\n\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n if (!this.clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthClientInformation>(\n this.clientInfoKey(this.clientId)\n )) ?? undefined\n );\n }\n\n async saveClientInformation(\n clientInformation: OAuthClientInformationFull\n ): Promise<void> {\n await this.storage.put(\n this.clientInfoKey(clientInformation.client_id),\n clientInformation\n );\n this.clientId = clientInformation.client_id;\n }\n\n tokenKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/token`;\n }\n\n async tokens(): Promise<OAuthTokens | undefined> {\n if (!this.clientId_) {\n return undefined;\n }\n return (\n (await this.storage.get<OAuthTokens>(this.tokenKey(this.clientId))) ??\n undefined\n );\n }\n\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n await this.storage.put(this.tokenKey(this.clientId), tokens);\n }\n\n get authUrl() {\n return this.authUrl_;\n }\n\n /**\n * Because this operates on the server side (but we need browser auth), we send this url back to the user\n * and require user interact to initiate the redirect flow\n */\n async redirectToAuthorization(authUrl: URL): Promise<void> {\n // We want to track the client ID in state here because the typescript SSE client sometimes does\n // a dynamic client registration AFTER generating this redirect URL.\n const client_id = authUrl.searchParams.get(\"client_id\");\n if (client_id) {\n authUrl.searchParams.append(\"state\", client_id);\n }\n this.authUrl_ = authUrl.toString();\n }\n\n codeVerifierKey(clientId: string) {\n return `${this.keyPrefix(clientId)}/code_verifier`;\n }\n\n async saveCodeVerifier(verifier: string): Promise<void> {\n await this.storage.put(this.codeVerifierKey(this.clientId), verifier);\n }\n\n async codeVerifier(): Promise<string> {\n const codeVerifier = await this.storage.get<string>(\n this.codeVerifierKey(this.clientId)\n );\n if (!codeVerifier) {\n throw new Error(\"No code verifier found\");\n }\n return codeVerifier;\n }\n}\n"],"mappings":";;;AAgBO,IAAM,mCAAN,MAAsE;AAAA,EAK3E,YACS,SACA,YACA,iBACP;AAHO;AACA;AACA;AAAA,EACN;AAAA,EAEH,IAAI,iBAAsC;AACxC,WAAO;AAAA,MACL,eAAe,CAAC,KAAK,WAAW;AAAA,MAChC,4BAA4B;AAAA,MAC5B,aAAa,CAAC,sBAAsB,eAAe;AAAA,MACnD,gBAAgB,CAAC,MAAM;AAAA,MACvB,aAAa,KAAK;AAAA,MAClB,YAAY;AAAA,IACd;AAAA,EACF;AAAA,EAEA,IAAI,cAAc;AAChB,WAAO,GAAG,KAAK,eAAe,IAAI,KAAK,QAAQ;AAAA,EACjD;AAAA,EAEA,IAAI,WAAW;AACb,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAS,WAAmB;AAC9B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,IAAI,WAAW;AACb,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,SAAS,WAAmB;AAC9B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,UAAU,UAAkB;AAC1B,WAAO,IAAI,KAAK,UAAU,IAAI,KAAK,QAAQ,IAAI,QAAQ;AAAA,EACzD;AAAA,EAEA,cAAc,UAAkB;AAC9B,WAAO,GAAG,KAAK,UAAU,QAAQ,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,oBAAiE;AACrE,QAAI,CAAC,KAAK,WAAW;AACnB,aAAO;AAAA,IACT;AACA,WACG,MAAM,KAAK,QAAQ;AAAA,MAClB,KAAK,cAAc,KAAK,QAAQ;AAAA,IAClC,KAAM;AAAA,EAEV;AAAA,EAEA,MAAM,sBACJ,mBACe;AACf,UAAM,KAAK,QAAQ;AAAA,MACjB,KAAK,cAAc,kBAAkB,SAAS;AAAA,MAC9C;AAAA,IACF;AACA,SAAK,WAAW,kBAAkB;AAAA,EACpC;AAAA,EAEA,SAAS,UAAkB;AACzB,WAAO,GAAG,KAAK,UAAU,QAAQ,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,SAA2C;AAC/C,QAAI,CAAC,KAAK,WAAW;AACnB,aAAO;AAAA,IACT;AACA,WACG,MAAM,KAAK,QAAQ,IAAiB,KAAK,SAAS,KAAK,QAAQ,CAAC,KACjE;AAAA,EAEJ;AAAA,EAEA,MAAM,WAAW,QAAoC;AACnD,UAAM,KAAK,QAAQ,IAAI,KAAK,SAAS,KAAK,QAAQ,GAAG,MAAM;AAAA,EAC7D;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,wBAAwB,SAA6B;AAGzD,UAAM,YAAY,QAAQ,aAAa,IAAI,WAAW;AACtD,QAAI,WAAW;AACb,cAAQ,aAAa,OAAO,SAAS,SAAS;AAAA,IAChD;AACA,SAAK,WAAW,QAAQ,SAAS;AAAA,EACnC;AAAA,EAEA,gBAAgB,UAAkB;AAChC,WAAO,GAAG,KAAK,UAAU,QAAQ,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,iBAAiB,UAAiC;AACtD,UAAM,KAAK,QAAQ,IAAI,KAAK,gBAAgB,KAAK,QAAQ,GAAG,QAAQ;AAAA,EACtE;AAAA,EAEA,MAAM,eAAgC;AACpC,UAAM,eAAe,MAAM,KAAK,QAAQ;AAAA,MACtC,KAAK,gBAAgB,KAAK,QAAQ;AAAA,IACpC;AACA,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.61",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
"require": "./dist/ai-chat-agent.js",
|
|
39
39
|
"import": "./dist/ai-chat-agent.js"
|
|
40
40
|
},
|
|
41
|
+
"./ai-types": {
|
|
42
|
+
"types": "./dist/ai-types.d.ts",
|
|
43
|
+
"require": "./dist/ai-types.js",
|
|
44
|
+
"import": "./dist/ai-types.js"
|
|
45
|
+
},
|
|
41
46
|
"./schedule": {
|
|
42
47
|
"types": "./dist/schedule.d.ts",
|
|
43
48
|
"require": "./dist/schedule.js",
|