agents 0.0.0-c3e8618 → 0.0.0-c552d8b
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 +47 -23
- package/dist/ai-chat-agent.js +234 -0
- package/dist/ai-chat-agent.js.map +1 -0
- package/dist/ai-react.d.ts +72 -44
- package/dist/ai-react.js +204 -0
- package/dist/ai-react.js.map +1 -0
- package/dist/ai-types.d.ts +65 -40
- package/dist/ai-types.js +1 -0
- package/dist/ai-types.js.map +1 -0
- package/dist/chunk-HMLY7DHA.js +16 -0
- package/dist/chunk-HMLY7DHA.js.map +1 -0
- package/dist/chunk-Q5ZBHY4Z.js +456 -0
- package/dist/chunk-Q5ZBHY4Z.js.map +1 -0
- package/dist/chunk-WNIGPPNB.js +608 -0
- package/dist/chunk-WNIGPPNB.js.map +1 -0
- package/dist/client.d.ts +57 -37
- package/dist/client.js +131 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +234 -179
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +138 -36
- package/dist/mcp/client.js +10 -0
- package/dist/mcp/client.js.map +1 -0
- 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/dist/mcp/index.d.ts +39 -6
- package/dist/mcp/index.js +808 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/react.d.ts +24 -15
- package/dist/react.js +104 -0
- package/dist/react.js.map +1 -0
- package/dist/schedule.d.ts +30 -20
- package/dist/schedule.js +73 -0
- package/dist/schedule.js.map +1 -0
- package/package.json +25 -5
- package/src/index.ts +37 -14
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai-react.tsx"],"sourcesContent":["import { useChat } from \"@ai-sdk/react\";\nimport type { Message } from \"ai\";\nimport { use, useEffect } from \"react\";\nimport type { OutgoingMessage } from \"./ai-types\";\nimport type { useAgent } from \"./react\";\nimport { nanoid } from \"nanoid\";\n\ntype GetInitialMessagesOptions = {\n agent: string;\n name: string;\n url: string;\n};\n\n/**\n * Options for the useAgentChat hook\n */\ntype UseAgentChatOptions<State> = Omit<\n Parameters<typeof useChat>[0] & {\n /** Agent connection from useAgent */\n agent: ReturnType<typeof useAgent<State>>;\n getInitialMessages?:\n | undefined\n | null\n // | (() => Message[])\n | ((options: GetInitialMessagesOptions) => Promise<Message[]>);\n },\n \"fetch\"\n>;\n\nconst requestCache = new Map<string, Promise<Message[]>>();\n\n/**\n * React hook for building AI chat interfaces using an Agent\n * @param options Chat options including the agent connection\n * @returns Chat interface controls and state with added clearHistory method\n */\nexport function useAgentChat<State = unknown>(\n options: UseAgentChatOptions<State>\n) {\n const { agent, getInitialMessages, ...rest } = options;\n\n const agentUrl = new URL(\n `${// @ts-expect-error we're using a protected _url property that includes query params\n ((agent._url as string | null) || agent._pkurl)\n ?.replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")}`\n );\n\n // delete the _pk query param\n agentUrl.searchParams.delete(\"_pk\");\n const agentUrlString = agentUrl.toString();\n\n async function defaultGetInitialMessagesFetch({\n url,\n }: GetInitialMessagesOptions) {\n const getMessagesUrl = new URL(url);\n getMessagesUrl.pathname += \"/get-messages\";\n const response = await fetch(getMessagesUrl.toString(), {\n headers: options.headers,\n credentials: options.credentials,\n });\n return response.json<Message[]>();\n }\n\n const getInitialMessagesFetch =\n getInitialMessages || defaultGetInitialMessagesFetch;\n\n function doGetInitialMessages(\n getInitialMessagesOptions: GetInitialMessagesOptions\n ) {\n if (requestCache.has(agentUrlString)) {\n return requestCache.get(agentUrlString)!;\n }\n const promise = getInitialMessagesFetch(getInitialMessagesOptions);\n // immediately cache the promise so that we don't\n // create multiple requests for the same agent during multiple\n // concurrent renders\n requestCache.set(agentUrlString, promise);\n return promise;\n }\n\n const initialMessagesPromise =\n getInitialMessages === null\n ? null\n : doGetInitialMessages({\n agent: agent.agent,\n name: agent.name,\n url: agentUrlString,\n });\n const initialMessages = initialMessagesPromise\n ? use(initialMessagesPromise)\n : (rest.initialMessages ?? []);\n\n // manages adding and removing the promise from the cache\n useEffect(() => {\n if (!initialMessagesPromise) {\n return;\n }\n // this effect is responsible for removing the promise from the cache\n // when the component unmounts or the promise changes,\n // but that means it also must add the promise to the cache\n // so that multiple arbitrary effect runs produce the expected state\n // when resolved.\n requestCache.set(agentUrlString, initialMessagesPromise!);\n return () => {\n if (requestCache.get(agentUrlString) === initialMessagesPromise) {\n requestCache.delete(agentUrlString);\n }\n };\n }, [agentUrlString, initialMessagesPromise]);\n\n async function aiFetch(\n request: RequestInfo | URL,\n options: RequestInit = {}\n ) {\n // we're going to use a websocket to do the actual \"fetching\"\n // but still satisfy the type signature of the fetch function\n // so we'll return a promise that resolves to a response\n\n const {\n method,\n keepalive,\n headers,\n body,\n redirect,\n integrity,\n signal,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher, duplex\n } = options;\n const id = nanoid(8);\n const abortController = new AbortController();\n\n signal?.addEventListener(\"abort\", () => {\n // Propagate request cancellation to the Agent\n // We need to communciate cancellation as a websocket message, instead of a request signal\n agent.send(\n JSON.stringify({\n type: \"cf_agent_chat_request_cancel\",\n id,\n })\n );\n\n // NOTE - If we wanted to, we could preserve the \"interrupted\" message here, with the code below\n // However, I think it might be the responsibility of the library user to implement that behavior manually?\n // Reasoning: This code could be subject to collisions, as it \"force saves\" the messages we have locally\n //\n // agent.send(JSON.stringify({\n // type: \"cf_agent_chat_messages\",\n // messages: ... /* some way of getting current messages ref? */\n // }))\n\n abortController.abort();\n });\n\n agent.addEventListener(\n \"message\",\n (event) => {\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_use_chat_response\") {\n if (data.id === id) {\n controller.enqueue(new TextEncoder().encode(data.body));\n if (data.done) {\n controller.close();\n abortController.abort();\n }\n }\n }\n },\n { signal: abortController.signal }\n );\n\n let controller: ReadableStreamDefaultController;\n\n const stream = new ReadableStream({\n start(c) {\n controller = c;\n },\n });\n\n agent.send(\n JSON.stringify({\n type: \"cf_agent_use_chat_request\",\n id,\n url: request.toString(),\n init: {\n method,\n keepalive,\n headers,\n body,\n redirect,\n integrity,\n credentials,\n mode,\n referrer,\n referrerPolicy,\n window,\n // dispatcher,\n // duplex\n },\n })\n );\n\n return new Response(stream);\n }\n const useChatHelpers = useChat({\n initialMessages,\n sendExtraMessageFields: true,\n fetch: aiFetch,\n ...rest,\n });\n\n useEffect(() => {\n function onClearHistory(event: MessageEvent) {\n if (typeof event.data !== \"string\") {\n return;\n }\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_chat_clear\") {\n useChatHelpers.setMessages([]);\n }\n }\n\n function onMessages(event: MessageEvent) {\n if (typeof event.data !== \"string\") {\n return;\n }\n let data: OutgoingMessage;\n try {\n data = JSON.parse(event.data) as OutgoingMessage;\n } catch (error) {\n // silently ignore invalid messages for now\n // TODO: log errors with log levels\n return;\n }\n if (data.type === \"cf_agent_chat_messages\") {\n useChatHelpers.setMessages(data.messages);\n }\n }\n\n agent.addEventListener(\"message\", onClearHistory);\n agent.addEventListener(\"message\", onMessages);\n\n return () => {\n agent.removeEventListener(\"message\", onClearHistory);\n agent.removeEventListener(\"message\", onMessages);\n };\n }, [agent, useChatHelpers.setMessages]);\n\n return {\n ...useChatHelpers,\n /**\n * Set the chat messages and synchronize with the Agent\n * @param messages New messages to set\n */\n setMessages: (messages: Message[]) => {\n useChatHelpers.setMessages(messages);\n agent.send(\n JSON.stringify({\n type: \"cf_agent_chat_messages\",\n messages,\n })\n );\n },\n /**\n * Clear chat history on both client and Agent\n */\n clearHistory: () => {\n useChatHelpers.setMessages([]);\n agent.send(\n JSON.stringify({\n type: \"cf_agent_chat_clear\",\n })\n );\n },\n };\n}\n"],"mappings":"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","names":["options"]}
|
package/dist/ai-types.d.ts
CHANGED
|
@@ -1,49 +1,74 @@
|
|
|
1
|
-
import { Message } from
|
|
1
|
+
import { Message } from "ai";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Types of messages sent from the Agent to clients
|
|
5
5
|
*/
|
|
6
|
-
type OutgoingMessage =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
6
|
+
type OutgoingMessage =
|
|
7
|
+
| {
|
|
8
|
+
/** Indicates this message contains updated chat messages */
|
|
9
|
+
type: "cf_agent_chat_messages";
|
|
10
|
+
/** Array of chat messages */
|
|
11
|
+
messages: Message[];
|
|
12
|
+
}
|
|
13
|
+
| {
|
|
14
|
+
/** Indicates this message is a response to a chat request */
|
|
15
|
+
type: "cf_agent_use_chat_response";
|
|
16
|
+
/** Unique ID of the request this response corresponds to */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Content body of the response */
|
|
19
|
+
body: string;
|
|
20
|
+
/** Whether this is the final chunk of the response */
|
|
21
|
+
done: boolean;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
/** Indicates this message contains updated chat messages */
|
|
25
|
+
type: "cf_agent_chat_messages";
|
|
26
|
+
/** Array of chat messages */
|
|
27
|
+
messages: Message[];
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
/** Indicates this message is a command to clear chat history */
|
|
31
|
+
type: "cf_agent_chat_clear";
|
|
32
|
+
};
|
|
29
33
|
/**
|
|
30
34
|
* Types of messages sent from clients to the Agent
|
|
31
35
|
*/
|
|
32
|
-
type IncomingMessage =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
type IncomingMessage =
|
|
37
|
+
| {
|
|
38
|
+
/** Indicates this message is a request to the chat API */
|
|
39
|
+
type: "cf_agent_use_chat_request";
|
|
40
|
+
/** Unique ID for this request */
|
|
41
|
+
id: string;
|
|
42
|
+
/** Request initialization options */
|
|
43
|
+
init: Pick<
|
|
44
|
+
RequestInit,
|
|
45
|
+
| "method"
|
|
46
|
+
| "keepalive"
|
|
47
|
+
| "headers"
|
|
48
|
+
| "body"
|
|
49
|
+
| "redirect"
|
|
50
|
+
| "integrity"
|
|
51
|
+
| "credentials"
|
|
52
|
+
| "mode"
|
|
53
|
+
| "referrer"
|
|
54
|
+
| "referrerPolicy"
|
|
55
|
+
| "window"
|
|
56
|
+
>;
|
|
57
|
+
}
|
|
58
|
+
| {
|
|
59
|
+
/** Indicates this message is a command to clear chat history */
|
|
60
|
+
type: "cf_agent_chat_clear";
|
|
61
|
+
}
|
|
62
|
+
| {
|
|
63
|
+
/** Indicates this message contains updated chat messages */
|
|
64
|
+
type: "cf_agent_chat_messages";
|
|
65
|
+
/** Array of chat messages */
|
|
66
|
+
messages: Message[];
|
|
67
|
+
}
|
|
68
|
+
| {
|
|
69
|
+
/** Indicates the user wants to stop generation of this message */
|
|
70
|
+
type: "cf_agent_chat_request_cancel";
|
|
71
|
+
id: string;
|
|
72
|
+
};
|
|
48
73
|
|
|
49
74
|
export type { IncomingMessage, OutgoingMessage };
|
package/dist/ai-types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=ai-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
__privateGet,
|
|
12
|
+
__privateAdd,
|
|
13
|
+
__privateSet,
|
|
14
|
+
__privateMethod
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=chunk-HMLY7DHA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
// src/mcp/sse-edge.ts
|
|
2
|
+
import {
|
|
3
|
+
SSEClientTransport
|
|
4
|
+
} from "@modelcontextprotocol/sdk/client/sse.js";
|
|
5
|
+
var SSEEdgeClientTransport = class extends SSEClientTransport {
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new EdgeSSEClientTransport, which overrides fetch to be compatible with the CF workers environment
|
|
8
|
+
*/
|
|
9
|
+
constructor(url, options) {
|
|
10
|
+
const fetchOverride = async (fetchUrl, fetchInit = {}) => {
|
|
11
|
+
const headers = await this.authHeaders();
|
|
12
|
+
const workerOptions = {
|
|
13
|
+
...fetchInit,
|
|
14
|
+
headers: {
|
|
15
|
+
...fetchInit?.headers,
|
|
16
|
+
...headers
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
delete workerOptions.mode;
|
|
20
|
+
return fetch(fetchUrl, workerOptions);
|
|
21
|
+
};
|
|
22
|
+
super(url, {
|
|
23
|
+
...options,
|
|
24
|
+
eventSourceInit: {
|
|
25
|
+
fetch: fetchOverride
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
this.authProvider = options.authProvider;
|
|
29
|
+
}
|
|
30
|
+
async authHeaders() {
|
|
31
|
+
if (this.authProvider) {
|
|
32
|
+
const tokens = await this.authProvider.tokens();
|
|
33
|
+
if (tokens) {
|
|
34
|
+
return {
|
|
35
|
+
Authorization: `Bearer ${tokens.access_token}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/mcp/client-connection.ts
|
|
43
|
+
import {
|
|
44
|
+
ToolListChangedNotificationSchema,
|
|
45
|
+
ResourceListChangedNotificationSchema,
|
|
46
|
+
PromptListChangedNotificationSchema
|
|
47
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
48
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
49
|
+
var MCPClientConnection = class {
|
|
50
|
+
constructor(url, info, options = { transport: {}, client: {}, capabilities: {} }) {
|
|
51
|
+
this.url = url;
|
|
52
|
+
this.options = options;
|
|
53
|
+
this.connectionState = "connecting";
|
|
54
|
+
this.tools = [];
|
|
55
|
+
this.prompts = [];
|
|
56
|
+
this.resources = [];
|
|
57
|
+
this.resourceTemplates = [];
|
|
58
|
+
this.client = new Client(info, options.client);
|
|
59
|
+
this.client.registerCapabilities(options.capabilities);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Initialize a client connection
|
|
63
|
+
*
|
|
64
|
+
* @param code Optional OAuth code to initialize the connection with if auth hasn't been initialized
|
|
65
|
+
* @returns
|
|
66
|
+
*/
|
|
67
|
+
async init(code, clientId) {
|
|
68
|
+
try {
|
|
69
|
+
const transport = new SSEEdgeClientTransport(
|
|
70
|
+
this.url,
|
|
71
|
+
this.options.transport
|
|
72
|
+
);
|
|
73
|
+
if (code) {
|
|
74
|
+
await transport.finishAuth(code);
|
|
75
|
+
}
|
|
76
|
+
await this.client.connect(transport);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
if (e.toString().includes("Unauthorized")) {
|
|
79
|
+
this.connectionState = "authenticating";
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.connectionState = "failed";
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
85
|
+
this.connectionState = "discovering";
|
|
86
|
+
this.serverCapabilities = await this.client.getServerCapabilities();
|
|
87
|
+
if (!this.serverCapabilities) {
|
|
88
|
+
throw new Error("The MCP Server failed to return server capabilities");
|
|
89
|
+
}
|
|
90
|
+
const [instructions, tools, resources, prompts, resourceTemplates] = await Promise.all([
|
|
91
|
+
this.client.getInstructions(),
|
|
92
|
+
this.registerTools(),
|
|
93
|
+
this.registerResources(),
|
|
94
|
+
this.registerPrompts(),
|
|
95
|
+
this.registerResourceTemplates()
|
|
96
|
+
]);
|
|
97
|
+
this.instructions = instructions;
|
|
98
|
+
this.tools = tools;
|
|
99
|
+
this.resources = resources;
|
|
100
|
+
this.prompts = prompts;
|
|
101
|
+
this.resourceTemplates = resourceTemplates;
|
|
102
|
+
this.connectionState = "ready";
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Notification handler registration
|
|
106
|
+
*/
|
|
107
|
+
async registerTools() {
|
|
108
|
+
if (!this.serverCapabilities || !this.serverCapabilities.tools) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
if (this.serverCapabilities.tools.listChanged) {
|
|
112
|
+
this.client.setNotificationHandler(
|
|
113
|
+
ToolListChangedNotificationSchema,
|
|
114
|
+
async (_notification) => {
|
|
115
|
+
this.tools = await this.fetchTools();
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return this.fetchTools();
|
|
120
|
+
}
|
|
121
|
+
async registerResources() {
|
|
122
|
+
if (!this.serverCapabilities || !this.serverCapabilities.resources) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
if (this.serverCapabilities.resources.listChanged) {
|
|
126
|
+
this.client.setNotificationHandler(
|
|
127
|
+
ResourceListChangedNotificationSchema,
|
|
128
|
+
async (_notification) => {
|
|
129
|
+
this.resources = await this.fetchResources();
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return this.fetchResources();
|
|
134
|
+
}
|
|
135
|
+
async registerPrompts() {
|
|
136
|
+
if (!this.serverCapabilities || !this.serverCapabilities.prompts) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
if (this.serverCapabilities.prompts.listChanged) {
|
|
140
|
+
this.client.setNotificationHandler(
|
|
141
|
+
PromptListChangedNotificationSchema,
|
|
142
|
+
async (_notification) => {
|
|
143
|
+
this.prompts = await this.fetchPrompts();
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
return this.fetchPrompts();
|
|
148
|
+
}
|
|
149
|
+
async registerResourceTemplates() {
|
|
150
|
+
if (!this.serverCapabilities || !this.serverCapabilities.resources) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
return this.fetchResourceTemplates();
|
|
154
|
+
}
|
|
155
|
+
async fetchTools() {
|
|
156
|
+
let toolsAgg = [];
|
|
157
|
+
let toolsResult = { tools: [] };
|
|
158
|
+
do {
|
|
159
|
+
toolsResult = await this.client.listTools({
|
|
160
|
+
cursor: toolsResult.nextCursor
|
|
161
|
+
}).catch(capabilityErrorHandler({ tools: [] }, "tools/list"));
|
|
162
|
+
toolsAgg = toolsAgg.concat(toolsResult.tools);
|
|
163
|
+
} while (toolsResult.nextCursor);
|
|
164
|
+
return toolsAgg;
|
|
165
|
+
}
|
|
166
|
+
async fetchResources() {
|
|
167
|
+
let resourcesAgg = [];
|
|
168
|
+
let resourcesResult = { resources: [] };
|
|
169
|
+
do {
|
|
170
|
+
resourcesResult = await this.client.listResources({
|
|
171
|
+
cursor: resourcesResult.nextCursor
|
|
172
|
+
}).catch(capabilityErrorHandler({ resources: [] }, "resources/list"));
|
|
173
|
+
resourcesAgg = resourcesAgg.concat(resourcesResult.resources);
|
|
174
|
+
} while (resourcesResult.nextCursor);
|
|
175
|
+
return resourcesAgg;
|
|
176
|
+
}
|
|
177
|
+
async fetchPrompts() {
|
|
178
|
+
let promptsAgg = [];
|
|
179
|
+
let promptsResult = { prompts: [] };
|
|
180
|
+
do {
|
|
181
|
+
promptsResult = await this.client.listPrompts({
|
|
182
|
+
cursor: promptsResult.nextCursor
|
|
183
|
+
}).catch(capabilityErrorHandler({ prompts: [] }, "prompts/list"));
|
|
184
|
+
promptsAgg = promptsAgg.concat(promptsResult.prompts);
|
|
185
|
+
} while (promptsResult.nextCursor);
|
|
186
|
+
return promptsAgg;
|
|
187
|
+
}
|
|
188
|
+
async fetchResourceTemplates() {
|
|
189
|
+
let templatesAgg = [];
|
|
190
|
+
let templatesResult = {
|
|
191
|
+
resourceTemplates: []
|
|
192
|
+
};
|
|
193
|
+
do {
|
|
194
|
+
templatesResult = await this.client.listResourceTemplates({
|
|
195
|
+
cursor: templatesResult.nextCursor
|
|
196
|
+
}).catch(
|
|
197
|
+
capabilityErrorHandler(
|
|
198
|
+
{ resourceTemplates: [] },
|
|
199
|
+
"resources/templates/list"
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
templatesAgg = templatesAgg.concat(templatesResult.resourceTemplates);
|
|
203
|
+
} while (templatesResult.nextCursor);
|
|
204
|
+
return templatesAgg;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
function capabilityErrorHandler(empty, method) {
|
|
208
|
+
return (e) => {
|
|
209
|
+
if (e.code === -32601) {
|
|
210
|
+
console.error(
|
|
211
|
+
`The server advertised support for the capability ${method.split("/")[0]}, but returned "Method not found" for '${method}'.`
|
|
212
|
+
);
|
|
213
|
+
return empty;
|
|
214
|
+
}
|
|
215
|
+
throw e;
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/mcp/client.ts
|
|
220
|
+
import { jsonSchema } from "ai";
|
|
221
|
+
import { nanoid } from "nanoid";
|
|
222
|
+
var MCPClientManager = class {
|
|
223
|
+
/**
|
|
224
|
+
* @param name Name of the MCP client
|
|
225
|
+
* @param version Version of the MCP Client
|
|
226
|
+
* @param auth Auth paramters if being used to create a DurableObjectOAuthClientProvider
|
|
227
|
+
*/
|
|
228
|
+
constructor(name, version) {
|
|
229
|
+
this.name = name;
|
|
230
|
+
this.version = version;
|
|
231
|
+
this.mcpConnections = {};
|
|
232
|
+
this.callbackUrls = [];
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Connect to and register an MCP server
|
|
236
|
+
*
|
|
237
|
+
* @param transportConfig Transport config
|
|
238
|
+
* @param clientConfig Client config
|
|
239
|
+
* @param capabilities Client capabilities (i.e. if the client supports roots/sampling)
|
|
240
|
+
*/
|
|
241
|
+
async connect(url, options = {}) {
|
|
242
|
+
const id = options.reconnect?.id ?? nanoid(8);
|
|
243
|
+
if (!options.transport?.authProvider) {
|
|
244
|
+
console.warn(
|
|
245
|
+
"No authProvider provided in the transport options. This client will only support unauthenticated remote MCP Servers"
|
|
246
|
+
);
|
|
247
|
+
} else {
|
|
248
|
+
options.transport.authProvider.serverId = id;
|
|
249
|
+
}
|
|
250
|
+
this.mcpConnections[id] = new MCPClientConnection(
|
|
251
|
+
new URL(url),
|
|
252
|
+
{
|
|
253
|
+
name: this.name,
|
|
254
|
+
version: this.version
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
transport: options.transport ?? {},
|
|
258
|
+
client: options.client ?? {},
|
|
259
|
+
capabilities: options.client ?? {}
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
await this.mcpConnections[id].init(
|
|
263
|
+
options.reconnect?.oauthCode,
|
|
264
|
+
options.reconnect?.oauthClientId
|
|
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
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
id,
|
|
274
|
+
authUrl
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
isCallbackRequest(req) {
|
|
278
|
+
return req.method === "GET" && !!this.callbackUrls.find((url) => {
|
|
279
|
+
return req.url.startsWith(url);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
async handleCallbackRequest(req) {
|
|
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
|
+
}
|
|
292
|
+
const code = url.searchParams.get("code");
|
|
293
|
+
const clientId = url.searchParams.get("state");
|
|
294
|
+
const urlParams = urlMatch.split("/");
|
|
295
|
+
const serverId = urlParams[urlParams.length - 1];
|
|
296
|
+
if (!code) {
|
|
297
|
+
throw new Error("Unauthorized: no code provided");
|
|
298
|
+
}
|
|
299
|
+
if (!clientId) {
|
|
300
|
+
throw new Error("Unauthorized: no state provided");
|
|
301
|
+
}
|
|
302
|
+
if (this.mcpConnections[serverId] === void 0) {
|
|
303
|
+
throw new Error(`Could not find serverId: ${serverId}`);
|
|
304
|
+
}
|
|
305
|
+
if (this.mcpConnections[serverId].connectionState !== "authenticating") {
|
|
306
|
+
throw new Error(
|
|
307
|
+
"Failed to authenticate: the client isn't in the `authenticating` state"
|
|
308
|
+
);
|
|
309
|
+
}
|
|
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();
|
|
319
|
+
await this.connect(serverUrl, {
|
|
320
|
+
reconnect: {
|
|
321
|
+
id: serverId,
|
|
322
|
+
oauthClientId: clientId,
|
|
323
|
+
oauthCode: code
|
|
324
|
+
},
|
|
325
|
+
...conn.options
|
|
326
|
+
});
|
|
327
|
+
if (this.mcpConnections[serverId].connectionState === "authenticating") {
|
|
328
|
+
throw new Error("Failed to authenticate: client failed to initialize");
|
|
329
|
+
}
|
|
330
|
+
return { serverId };
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* @returns namespaced list of tools
|
|
334
|
+
*/
|
|
335
|
+
listTools() {
|
|
336
|
+
return getNamespacedData(this.mcpConnections, "tools");
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* @returns a set of tools that you can use with the AI SDK
|
|
340
|
+
*/
|
|
341
|
+
unstable_getAITools() {
|
|
342
|
+
return Object.fromEntries(
|
|
343
|
+
getNamespacedData(this.mcpConnections, "tools").map((tool) => {
|
|
344
|
+
return [
|
|
345
|
+
`${tool.serverId}_${tool.name}`,
|
|
346
|
+
{
|
|
347
|
+
parameters: jsonSchema(tool.inputSchema),
|
|
348
|
+
description: tool.description,
|
|
349
|
+
execute: async (args) => {
|
|
350
|
+
const result = await this.callTool({
|
|
351
|
+
name: tool.name,
|
|
352
|
+
arguments: args,
|
|
353
|
+
serverId: tool.serverId
|
|
354
|
+
});
|
|
355
|
+
if (result.isError) {
|
|
356
|
+
throw new Error(result.content[0].text);
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
];
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Closes all connections to MCP servers
|
|
367
|
+
*/
|
|
368
|
+
async closeAllConnections() {
|
|
369
|
+
return Promise.all(
|
|
370
|
+
Object.values(this.mcpConnections).map(async (connection) => {
|
|
371
|
+
await connection.client.close();
|
|
372
|
+
})
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Closes a connection to an MCP server
|
|
377
|
+
* @param id The id of the connection to close
|
|
378
|
+
*/
|
|
379
|
+
async closeConnection(id) {
|
|
380
|
+
if (!this.mcpConnections[id]) {
|
|
381
|
+
throw new Error(`Connection with id "${id}" does not exist.`);
|
|
382
|
+
}
|
|
383
|
+
await this.mcpConnections[id].client.close();
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* @returns namespaced list of prompts
|
|
387
|
+
*/
|
|
388
|
+
listPrompts() {
|
|
389
|
+
return getNamespacedData(this.mcpConnections, "prompts");
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* @returns namespaced list of tools
|
|
393
|
+
*/
|
|
394
|
+
listResources() {
|
|
395
|
+
return getNamespacedData(this.mcpConnections, "resources");
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* @returns namespaced list of resource templates
|
|
399
|
+
*/
|
|
400
|
+
listResourceTemplates() {
|
|
401
|
+
return getNamespacedData(this.mcpConnections, "resourceTemplates");
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Namespaced version of callTool
|
|
405
|
+
*/
|
|
406
|
+
callTool(params, resultSchema, options) {
|
|
407
|
+
const unqualifiedName = params.name.replace(`${params.serverId}.`, "");
|
|
408
|
+
return this.mcpConnections[params.serverId].client.callTool(
|
|
409
|
+
{
|
|
410
|
+
...params,
|
|
411
|
+
name: unqualifiedName
|
|
412
|
+
},
|
|
413
|
+
resultSchema,
|
|
414
|
+
options
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Namespaced version of readResource
|
|
419
|
+
*/
|
|
420
|
+
readResource(params, options) {
|
|
421
|
+
return this.mcpConnections[params.serverId].client.readResource(
|
|
422
|
+
params,
|
|
423
|
+
options
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Namespaced version of getPrompt
|
|
428
|
+
*/
|
|
429
|
+
getPrompt(params, options) {
|
|
430
|
+
return this.mcpConnections[params.serverId].client.getPrompt(
|
|
431
|
+
params,
|
|
432
|
+
options
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
function getNamespacedData(mcpClients, type) {
|
|
437
|
+
const sets = Object.entries(mcpClients).map(([name, conn]) => {
|
|
438
|
+
return { name, data: conn[type] };
|
|
439
|
+
});
|
|
440
|
+
const namespacedData = sets.flatMap(({ name: serverId, data }) => {
|
|
441
|
+
return data.map((item) => {
|
|
442
|
+
return {
|
|
443
|
+
...item,
|
|
444
|
+
// we add a serverId so we can easily pull it out and send the tool call to the right server
|
|
445
|
+
serverId
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
return namespacedData;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export {
|
|
453
|
+
MCPClientManager,
|
|
454
|
+
getNamespacedData
|
|
455
|
+
};
|
|
456
|
+
//# sourceMappingURL=chunk-Q5ZBHY4Z.js.map
|