agents 0.0.0-1bfd6a7 → 0.0.0-1e060d3
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/ai-react.js +1 -5
- package/dist/ai-react.js.map +1 -1
- package/dist/mcp/index.d.ts +18 -3
- package/dist/mcp/index.js +514 -90
- package/dist/mcp/index.js.map +1 -1
- package/package.json +5 -4
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/ai-react.js
CHANGED
|
@@ -157,11 +157,7 @@ function useAgentChat(options) {
|
|
|
157
157
|
agent.removeEventListener("message", onClearHistory);
|
|
158
158
|
agent.removeEventListener("message", onMessages);
|
|
159
159
|
};
|
|
160
|
-
}, [
|
|
161
|
-
agent.addEventListener,
|
|
162
|
-
agent.removeEventListener,
|
|
163
|
-
useChatHelpers.setMessages
|
|
164
|
-
]);
|
|
160
|
+
}, [agent, useChatHelpers.setMessages]);
|
|
165
161
|
return {
|
|
166
162
|
...useChatHelpers,
|
|
167
163
|
/**
|
package/dist/ai-react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ai-react.tsx"],"sourcesContent":["import { useChat } from \"@ai-sdk/react\";\nimport type { Message } from \"ai\";\nimport type { useAgent } from \"./react\";\nimport { useEffect, use } from \"react\";\nimport type { OutgoingMessage } from \"./ai-types\";\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\n// TODO: clear cache when the agent is unmounted?\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 requestCache.set(agentUrlString, promise);\n return promise;\n }\n\n const initialMessages =\n getInitialMessages !== null\n ? use(\n doGetInitialMessages({\n agent: agent.agent,\n name: agent.name,\n url: agentUrlString,\n })\n )\n : rest.initialMessages;\n\n useEffect(() => {\n return () => {\n requestCache.delete(agentUrlString);\n };\n }, [agentUrlString]);\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 = crypto.randomUUID();\n const abortController = new AbortController();\n\n signal?.addEventListener(\"abort\", () => {\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 }, [\n agent.addEventListener,\n agent.removeEventListener,\n useChatHelpers.setMessages,\n ]);\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/ai-react.tsx"],"sourcesContent":["import { useChat } from \"@ai-sdk/react\";\nimport type { Message } from \"ai\";\nimport type { useAgent } from \"./react\";\nimport { useEffect, use } from \"react\";\nimport type { OutgoingMessage } from \"./ai-types\";\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\n// TODO: clear cache when the agent is unmounted?\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 requestCache.set(agentUrlString, promise);\n return promise;\n }\n\n const initialMessages =\n getInitialMessages !== null\n ? use(\n doGetInitialMessages({\n agent: agent.agent,\n name: agent.name,\n url: agentUrlString,\n })\n )\n : rest.initialMessages;\n\n useEffect(() => {\n return () => {\n requestCache.delete(agentUrlString);\n };\n }, [agentUrlString]);\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 = crypto.randomUUID();\n const abortController = new AbortController();\n\n signal?.addEventListener(\"abort\", () => {\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/mcp/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DurableObject } from 'cloudflare:workers';
|
|
2
|
+
import { Connection, WSMessage } from 'partyserver';
|
|
2
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
-
import { Connection } from 'partyserver';
|
|
4
4
|
|
|
5
5
|
interface CORSOptions {
|
|
6
6
|
origin?: string;
|
|
@@ -28,9 +28,12 @@ declare abstract class McpAgent<Env = unknown, State = unknown, Props extends Re
|
|
|
28
28
|
initRun: boolean;
|
|
29
29
|
abstract init(): Promise<void>;
|
|
30
30
|
_init(props: Props): Promise<void>;
|
|
31
|
+
isInitialized(): boolean;
|
|
31
32
|
fetch(request: Request): Promise<Response>;
|
|
32
33
|
getWebSocket(): WebSocket | null;
|
|
33
|
-
|
|
34
|
+
getWebSocketForResponseID(id: string): WebSocket | null;
|
|
35
|
+
onMessage(connection: Connection, event: WSMessage): Promise<void>;
|
|
36
|
+
onSSEMcpMessage(sessionId: string, request: Request): Promise<Error | null>;
|
|
34
37
|
webSocketMessage(ws: WebSocket, event: ArrayBuffer | string): Promise<void>;
|
|
35
38
|
webSocketError(ws: WebSocket, error: unknown): Promise<void>;
|
|
36
39
|
webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean): Promise<void>;
|
|
@@ -38,7 +41,19 @@ declare abstract class McpAgent<Env = unknown, State = unknown, Props extends Re
|
|
|
38
41
|
binding?: string;
|
|
39
42
|
corsOptions?: CORSOptions;
|
|
40
43
|
}): {
|
|
41
|
-
fetch: (request: Request, env: Record<string, DurableObjectNamespace<McpAgent>>, ctx: ExecutionContext) => Promise<Response
|
|
44
|
+
fetch: (request: Request, env: Record<string, DurableObjectNamespace<McpAgent>>, ctx: ExecutionContext) => Promise<Response>;
|
|
45
|
+
};
|
|
46
|
+
static serveSSE(path: string, { binding, corsOptions, }?: {
|
|
47
|
+
binding?: string;
|
|
48
|
+
corsOptions?: CORSOptions;
|
|
49
|
+
}): {
|
|
50
|
+
fetch: (request: Request, env: Record<string, DurableObjectNamespace<McpAgent>>, ctx: ExecutionContext) => Promise<Response>;
|
|
51
|
+
};
|
|
52
|
+
static serve(path: string, { binding, corsOptions, }?: {
|
|
53
|
+
binding?: string;
|
|
54
|
+
corsOptions?: CORSOptions;
|
|
55
|
+
}): {
|
|
56
|
+
fetch: (request: Request, env: Record<string, DurableObjectNamespace<McpAgent>>, ctx: ExecutionContext) => Promise<Response>;
|
|
42
57
|
};
|
|
43
58
|
}
|
|
44
59
|
|
package/dist/mcp/index.js
CHANGED
|
@@ -10,8 +10,15 @@ import {
|
|
|
10
10
|
|
|
11
11
|
// src/mcp/index.ts
|
|
12
12
|
import { DurableObject } from "cloudflare:workers";
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
InitializeRequestSchema,
|
|
15
|
+
isJSONRPCError,
|
|
16
|
+
isJSONRPCNotification,
|
|
17
|
+
isJSONRPCRequest,
|
|
18
|
+
isJSONRPCResponse,
|
|
19
|
+
JSONRPCMessageSchema
|
|
20
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
21
|
+
var MAXIMUM_MESSAGE_SIZE_BYTES = 4 * 1024 * 1024;
|
|
15
22
|
function handleCORS(request, corsOptions) {
|
|
16
23
|
const origin = request.headers.get("Origin") || "*";
|
|
17
24
|
const corsHeaders = {
|
|
@@ -26,7 +33,7 @@ function handleCORS(request, corsOptions) {
|
|
|
26
33
|
return null;
|
|
27
34
|
}
|
|
28
35
|
var _getWebSocket, _started;
|
|
29
|
-
var
|
|
36
|
+
var McpSSETransport = class {
|
|
30
37
|
constructor(getWebSocket) {
|
|
31
38
|
__privateAdd(this, _getWebSocket);
|
|
32
39
|
__privateAdd(this, _started, false);
|
|
@@ -59,18 +66,76 @@ var McpTransport = class {
|
|
|
59
66
|
};
|
|
60
67
|
_getWebSocket = new WeakMap();
|
|
61
68
|
_started = new WeakMap();
|
|
62
|
-
var
|
|
63
|
-
var
|
|
69
|
+
var _getWebSocketForGetRequest, _getWebSocketForMessageID, _notifyResponseIdSent, _started2;
|
|
70
|
+
var McpStreamableHttpTransport = class {
|
|
71
|
+
constructor(getWebSocketForMessageID, notifyResponseIdSent) {
|
|
72
|
+
// TODO: If there is an open connection to send server-initiated messages
|
|
73
|
+
// back, we should use that connection
|
|
74
|
+
__privateAdd(this, _getWebSocketForGetRequest);
|
|
75
|
+
// Get the appropriate websocket connection for a given message id
|
|
76
|
+
__privateAdd(this, _getWebSocketForMessageID);
|
|
77
|
+
// Notify the server that a response has been sent for a given message id
|
|
78
|
+
// so that it may clean up it's mapping of message ids to connections
|
|
79
|
+
// once they are no longer needed
|
|
80
|
+
__privateAdd(this, _notifyResponseIdSent);
|
|
81
|
+
__privateAdd(this, _started2, false);
|
|
82
|
+
__privateSet(this, _getWebSocketForMessageID, getWebSocketForMessageID);
|
|
83
|
+
__privateSet(this, _notifyResponseIdSent, notifyResponseIdSent);
|
|
84
|
+
__privateSet(this, _getWebSocketForGetRequest, () => null);
|
|
85
|
+
}
|
|
86
|
+
async start() {
|
|
87
|
+
if (__privateGet(this, _started2)) {
|
|
88
|
+
throw new Error("Transport already started");
|
|
89
|
+
}
|
|
90
|
+
__privateSet(this, _started2, true);
|
|
91
|
+
}
|
|
92
|
+
async send(message) {
|
|
93
|
+
if (!__privateGet(this, _started2)) {
|
|
94
|
+
throw new Error("Transport not started");
|
|
95
|
+
}
|
|
96
|
+
let websocket = null;
|
|
97
|
+
if (isJSONRPCResponse(message) || isJSONRPCError(message)) {
|
|
98
|
+
websocket = __privateGet(this, _getWebSocketForMessageID).call(this, message.id.toString());
|
|
99
|
+
if (!websocket) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Could not find WebSocket for message id: ${message.id}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
} else if (isJSONRPCRequest(message)) {
|
|
105
|
+
websocket = __privateGet(this, _getWebSocketForGetRequest).call(this);
|
|
106
|
+
} else if (isJSONRPCNotification(message)) {
|
|
107
|
+
websocket = null;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
websocket?.send(JSON.stringify(message));
|
|
111
|
+
if (isJSONRPCResponse(message)) {
|
|
112
|
+
__privateGet(this, _notifyResponseIdSent).call(this, message.id.toString());
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
this.onerror?.(error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async close() {
|
|
120
|
+
this.onclose?.();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
_getWebSocketForGetRequest = new WeakMap();
|
|
124
|
+
_getWebSocketForMessageID = new WeakMap();
|
|
125
|
+
_notifyResponseIdSent = new WeakMap();
|
|
126
|
+
_started2 = new WeakMap();
|
|
127
|
+
var _status, _transport, _transportType, _requestIdToConnectionId, _agent, _McpAgent_instances, initialize_fn;
|
|
128
|
+
var _McpAgent = class _McpAgent extends DurableObject {
|
|
64
129
|
constructor(ctx, env) {
|
|
65
130
|
var _a;
|
|
66
131
|
super(ctx, env);
|
|
67
132
|
__privateAdd(this, _McpAgent_instances);
|
|
68
133
|
__privateAdd(this, _status, "zero");
|
|
69
134
|
__privateAdd(this, _transport);
|
|
70
|
-
__privateAdd(this,
|
|
135
|
+
__privateAdd(this, _transportType, "unset");
|
|
136
|
+
__privateAdd(this, _requestIdToConnectionId, /* @__PURE__ */ new Map());
|
|
71
137
|
/**
|
|
72
|
-
* Since McpAgent's _aren't_ yet real "Agents"
|
|
73
|
-
* websockets, don't support hibernation), let's only expose a couple of the methods
|
|
138
|
+
* Since McpAgent's _aren't_ yet real "Agents", let's only expose a couple of the methods
|
|
74
139
|
* to the outer class: initialState/state/setState/onStateUpdate/sql
|
|
75
140
|
*/
|
|
76
141
|
__privateAdd(this, _agent);
|
|
@@ -80,6 +145,9 @@ var McpAgent = class extends DurableObject {
|
|
|
80
145
|
onStateUpdate(state, source) {
|
|
81
146
|
return self.onStateUpdate(state, source);
|
|
82
147
|
}
|
|
148
|
+
async onMessage(connection, message) {
|
|
149
|
+
return self.onMessage(connection, message);
|
|
150
|
+
}
|
|
83
151
|
}, _a.options = {
|
|
84
152
|
hibernate: true
|
|
85
153
|
}, _a)(ctx, env));
|
|
@@ -106,22 +174,40 @@ var McpAgent = class extends DurableObject {
|
|
|
106
174
|
onStateUpdate(state, source) {
|
|
107
175
|
return self.onStateUpdate(state, source);
|
|
108
176
|
}
|
|
177
|
+
async onMessage(connection, event) {
|
|
178
|
+
return self.onMessage(connection, event);
|
|
179
|
+
}
|
|
109
180
|
}, _a.options = {
|
|
110
181
|
hibernate: true
|
|
111
182
|
}, _a)(this.ctx, this.env));
|
|
112
183
|
this.props = await this.ctx.storage.get("props");
|
|
184
|
+
__privateSet(this, _transportType, await this.ctx.storage.get(
|
|
185
|
+
"transportType"
|
|
186
|
+
));
|
|
113
187
|
this.init?.();
|
|
114
|
-
|
|
115
|
-
|
|
188
|
+
if (__privateGet(this, _transportType) === "sse") {
|
|
189
|
+
__privateSet(this, _transport, new McpSSETransport(() => this.getWebSocket()));
|
|
190
|
+
await this.server.connect(__privateGet(this, _transport));
|
|
191
|
+
} else if (__privateGet(this, _transportType) === "streamable-http") {
|
|
192
|
+
__privateSet(this, _transport, new McpStreamableHttpTransport(
|
|
193
|
+
(id) => this.getWebSocketForResponseID(id),
|
|
194
|
+
(id) => __privateGet(this, _requestIdToConnectionId).delete(id)
|
|
195
|
+
));
|
|
196
|
+
await this.server.connect(__privateGet(this, _transport));
|
|
197
|
+
}
|
|
116
198
|
}
|
|
117
199
|
async _init(props) {
|
|
118
|
-
await this.ctx.storage.put("props", props);
|
|
200
|
+
await this.ctx.storage.put("props", props ?? {});
|
|
201
|
+
await this.ctx.storage.put("transportType", "unset");
|
|
119
202
|
this.props = props;
|
|
120
203
|
if (!this.initRun) {
|
|
121
204
|
this.initRun = true;
|
|
122
205
|
await this.init();
|
|
123
206
|
}
|
|
124
207
|
}
|
|
208
|
+
isInitialized() {
|
|
209
|
+
return this.initRun;
|
|
210
|
+
}
|
|
125
211
|
// Allow the worker to fetch a websocket connection to the agent
|
|
126
212
|
async fetch(request) {
|
|
127
213
|
if (__privateGet(this, _status) !== "started") {
|
|
@@ -133,18 +219,41 @@ var McpAgent = class extends DurableObject {
|
|
|
133
219
|
});
|
|
134
220
|
}
|
|
135
221
|
const url = new URL(request.url);
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
222
|
+
const path = url.pathname;
|
|
223
|
+
switch (path) {
|
|
224
|
+
case "/sse": {
|
|
225
|
+
const websockets = this.ctx.getWebSockets();
|
|
226
|
+
if (websockets.length > 0) {
|
|
227
|
+
return new Response("Websocket already connected", { status: 400 });
|
|
228
|
+
}
|
|
229
|
+
await this.ctx.storage.put("transportType", "sse");
|
|
230
|
+
__privateSet(this, _transportType, "sse");
|
|
231
|
+
if (!__privateGet(this, _transport)) {
|
|
232
|
+
__privateSet(this, _transport, new McpSSETransport(() => this.getWebSocket()));
|
|
233
|
+
await this.server.connect(__privateGet(this, _transport));
|
|
234
|
+
}
|
|
235
|
+
return __privateGet(this, _agent).fetch(request);
|
|
236
|
+
}
|
|
237
|
+
case "/streamable-http": {
|
|
238
|
+
if (!__privateGet(this, _transport)) {
|
|
239
|
+
__privateSet(this, _transport, new McpStreamableHttpTransport(
|
|
240
|
+
(id) => this.getWebSocketForResponseID(id),
|
|
241
|
+
(id) => __privateGet(this, _requestIdToConnectionId).delete(id)
|
|
242
|
+
));
|
|
243
|
+
await this.server.connect(__privateGet(this, _transport));
|
|
244
|
+
}
|
|
245
|
+
await this.ctx.storage.put("transportType", "streamable-http");
|
|
246
|
+
__privateSet(this, _transportType, "streamable-http");
|
|
247
|
+
return __privateGet(this, _agent).fetch(request);
|
|
248
|
+
}
|
|
249
|
+
default:
|
|
250
|
+
return new Response(
|
|
251
|
+
"Internal Server Error: Expected /sse or /streamable-http path",
|
|
252
|
+
{
|
|
253
|
+
status: 500
|
|
254
|
+
}
|
|
255
|
+
);
|
|
142
256
|
}
|
|
143
|
-
const response = await __privateGet(this, _agent).fetch(request);
|
|
144
|
-
__privateSet(this, _connected, true);
|
|
145
|
-
__privateSet(this, _transport, new McpTransport(() => this.getWebSocket()));
|
|
146
|
-
await this.server.connect(__privateGet(this, _transport));
|
|
147
|
-
return response;
|
|
148
257
|
}
|
|
149
258
|
getWebSocket() {
|
|
150
259
|
const websockets = this.ctx.getWebSockets();
|
|
@@ -153,26 +262,45 @@ var McpAgent = class extends DurableObject {
|
|
|
153
262
|
}
|
|
154
263
|
return websockets[0];
|
|
155
264
|
}
|
|
156
|
-
|
|
265
|
+
getWebSocketForResponseID(id) {
|
|
266
|
+
const connectionId = __privateGet(this, _requestIdToConnectionId).get(id);
|
|
267
|
+
if (connectionId === void 0) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
return __privateGet(this, _agent).getConnection(connectionId) ?? null;
|
|
271
|
+
}
|
|
272
|
+
// All messages received here. This is currently never called
|
|
273
|
+
async onMessage(connection, event) {
|
|
274
|
+
if (__privateGet(this, _transportType) !== "streamable-http") {
|
|
275
|
+
const err = new Error(
|
|
276
|
+
"Internal Server Error: Expected streamable-http protocol"
|
|
277
|
+
);
|
|
278
|
+
__privateGet(this, _transport)?.onerror?.(err);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
let message;
|
|
282
|
+
try {
|
|
283
|
+
const data = typeof event === "string" ? event : new TextDecoder().decode(event);
|
|
284
|
+
message = JSONRPCMessageSchema.parse(JSON.parse(data));
|
|
285
|
+
} catch (error) {
|
|
286
|
+
__privateGet(this, _transport)?.onerror?.(error);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (isJSONRPCRequest(message)) {
|
|
290
|
+
__privateGet(this, _requestIdToConnectionId).set(message.id.toString(), connection.id);
|
|
291
|
+
}
|
|
292
|
+
__privateGet(this, _transport)?.onmessage?.(message);
|
|
293
|
+
}
|
|
294
|
+
// All messages received over SSE after the initial connection has been established
|
|
295
|
+
// will be passed here
|
|
296
|
+
async onSSEMcpMessage(sessionId, request) {
|
|
157
297
|
if (__privateGet(this, _status) !== "started") {
|
|
158
298
|
await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
|
|
159
299
|
}
|
|
300
|
+
if (__privateGet(this, _transportType) !== "sse") {
|
|
301
|
+
return new Error("Internal Server Error: Expected SSE protocol");
|
|
302
|
+
}
|
|
160
303
|
try {
|
|
161
|
-
const contentType = request.headers.get("content-type") || "";
|
|
162
|
-
if (!contentType.includes("application/json")) {
|
|
163
|
-
return new Response(`Unsupported content-type: ${contentType}`, {
|
|
164
|
-
status: 400
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
const contentLength = Number.parseInt(
|
|
168
|
-
request.headers.get("content-length") || "0",
|
|
169
|
-
10
|
|
170
|
-
);
|
|
171
|
-
if (contentLength > MAXIMUM_MESSAGE_SIZE) {
|
|
172
|
-
return new Response(`Request body too large: ${contentLength} bytes`, {
|
|
173
|
-
status: 400
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
304
|
const message = await request.json();
|
|
177
305
|
let parsedMessage;
|
|
178
306
|
try {
|
|
@@ -182,44 +310,41 @@ var McpAgent = class extends DurableObject {
|
|
|
182
310
|
throw error;
|
|
183
311
|
}
|
|
184
312
|
__privateGet(this, _transport)?.onmessage?.(parsedMessage);
|
|
185
|
-
return
|
|
313
|
+
return null;
|
|
186
314
|
} catch (error) {
|
|
187
315
|
__privateGet(this, _transport)?.onerror?.(error);
|
|
188
|
-
return
|
|
316
|
+
return error;
|
|
189
317
|
}
|
|
190
318
|
}
|
|
191
|
-
//
|
|
319
|
+
// Delegate all websocket events to the underlying agent
|
|
192
320
|
async webSocketMessage(ws, event) {
|
|
193
|
-
let message;
|
|
194
|
-
try {
|
|
195
|
-
const data = typeof event === "string" ? event : new TextDecoder().decode(event);
|
|
196
|
-
message = JSONRPCMessageSchema.parse(JSON.parse(data));
|
|
197
|
-
} catch (error) {
|
|
198
|
-
__privateGet(this, _transport)?.onerror?.(error);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
321
|
if (__privateGet(this, _status) !== "started") {
|
|
202
322
|
await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
|
|
203
323
|
}
|
|
204
|
-
__privateGet(this,
|
|
324
|
+
return await __privateGet(this, _agent).webSocketMessage(ws, event);
|
|
205
325
|
}
|
|
206
326
|
// WebSocket event handlers for hibernation support
|
|
207
327
|
async webSocketError(ws, error) {
|
|
208
328
|
if (__privateGet(this, _status) !== "started") {
|
|
209
329
|
await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
|
|
210
330
|
}
|
|
211
|
-
__privateGet(this,
|
|
331
|
+
return await __privateGet(this, _agent).webSocketError(ws, error);
|
|
212
332
|
}
|
|
213
333
|
async webSocketClose(ws, code, reason, wasClean) {
|
|
214
334
|
if (__privateGet(this, _status) !== "started") {
|
|
215
335
|
await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
|
|
216
336
|
}
|
|
217
|
-
__privateGet(this,
|
|
218
|
-
__privateSet(this, _connected, false);
|
|
337
|
+
return await __privateGet(this, _agent).webSocketClose(ws, code, reason, wasClean);
|
|
219
338
|
}
|
|
220
339
|
static mount(path, {
|
|
221
340
|
binding = "MCP_OBJECT",
|
|
222
341
|
corsOptions
|
|
342
|
+
} = {}) {
|
|
343
|
+
return _McpAgent.serveSSE(path, { binding, corsOptions });
|
|
344
|
+
}
|
|
345
|
+
static serveSSE(path, {
|
|
346
|
+
binding = "MCP_OBJECT",
|
|
347
|
+
corsOptions
|
|
223
348
|
} = {}) {
|
|
224
349
|
let pathname = path;
|
|
225
350
|
if (path === "/") {
|
|
@@ -243,11 +368,11 @@ data: ${encodeURI(`${pathname}/message`)}?sessionId=${sessionId}
|
|
|
243
368
|
|
|
244
369
|
`;
|
|
245
370
|
writer.write(encoder.encode(endpointMessage));
|
|
246
|
-
const id = namespace.
|
|
371
|
+
const id = namespace.idFromName(`sse:${sessionId}`);
|
|
247
372
|
const doStub = namespace.get(id);
|
|
248
373
|
await doStub._init(ctx.props);
|
|
249
374
|
const upgradeUrl = new URL(request.url);
|
|
250
|
-
upgradeUrl.
|
|
375
|
+
upgradeUrl.pathname = "/sse";
|
|
251
376
|
const response = await doStub.fetch(
|
|
252
377
|
new Request(upgradeUrl, {
|
|
253
378
|
headers: {
|
|
@@ -261,37 +386,48 @@ data: ${encodeURI(`${pathname}/message`)}?sessionId=${sessionId}
|
|
|
261
386
|
if (!ws) {
|
|
262
387
|
console.error("Failed to establish WebSocket connection");
|
|
263
388
|
await writer.close();
|
|
264
|
-
return
|
|
389
|
+
return new Response("Failed to establish WebSocket connection", {
|
|
390
|
+
status: 500
|
|
391
|
+
});
|
|
265
392
|
}
|
|
266
393
|
ws.accept();
|
|
267
|
-
ws.addEventListener("message",
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
394
|
+
ws.addEventListener("message", (event) => {
|
|
395
|
+
async function onMessage(event2) {
|
|
396
|
+
try {
|
|
397
|
+
const message = JSON.parse(event2.data);
|
|
398
|
+
const result = JSONRPCMessageSchema.safeParse(message);
|
|
399
|
+
if (!result.success) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const messageText = `event: message
|
|
275
403
|
data: ${JSON.stringify(result.data)}
|
|
276
404
|
|
|
277
405
|
`;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
406
|
+
await writer.write(encoder.encode(messageText));
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error("Error forwarding message to SSE:", error);
|
|
409
|
+
}
|
|
281
410
|
}
|
|
411
|
+
onMessage(event).catch(console.error);
|
|
282
412
|
});
|
|
283
|
-
ws.addEventListener("error",
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
413
|
+
ws.addEventListener("error", (error) => {
|
|
414
|
+
async function onError(error2) {
|
|
415
|
+
try {
|
|
416
|
+
await writer.close();
|
|
417
|
+
} catch (e) {
|
|
418
|
+
}
|
|
287
419
|
}
|
|
420
|
+
onError(error).catch(console.error);
|
|
288
421
|
});
|
|
289
|
-
ws.addEventListener("close",
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
422
|
+
ws.addEventListener("close", () => {
|
|
423
|
+
async function onClose() {
|
|
424
|
+
try {
|
|
425
|
+
await writer.close();
|
|
426
|
+
} catch (error) {
|
|
427
|
+
console.error("Error closing SSE connection:", error);
|
|
428
|
+
}
|
|
294
429
|
}
|
|
430
|
+
onClose().catch(console.error);
|
|
295
431
|
});
|
|
296
432
|
return new Response(readable, {
|
|
297
433
|
headers: {
|
|
@@ -310,30 +446,317 @@ data: ${JSON.stringify(result.data)}
|
|
|
310
446
|
{ status: 400 }
|
|
311
447
|
);
|
|
312
448
|
}
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
"
|
|
321
|
-
|
|
449
|
+
const contentType = request.headers.get("content-type") || "";
|
|
450
|
+
if (!contentType.includes("application/json")) {
|
|
451
|
+
return new Response(`Unsupported content-type: ${contentType}`, {
|
|
452
|
+
status: 400
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
const contentLength = Number.parseInt(
|
|
456
|
+
request.headers.get("content-length") || "0",
|
|
457
|
+
10
|
|
322
458
|
);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
459
|
+
if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {
|
|
460
|
+
return new Response(
|
|
461
|
+
`Request body too large: ${contentLength} bytes`,
|
|
462
|
+
{
|
|
463
|
+
status: 400
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
const id = namespace.idFromName(`sse:${sessionId}`);
|
|
468
|
+
const doStub = namespace.get(id);
|
|
469
|
+
const error = await doStub.onSSEMcpMessage(sessionId, request);
|
|
470
|
+
if (error) {
|
|
471
|
+
return new Response(error.message, {
|
|
472
|
+
status: 400,
|
|
473
|
+
headers: {
|
|
474
|
+
"Content-Type": "text/event-stream",
|
|
475
|
+
"Cache-Control": "no-cache",
|
|
476
|
+
Connection: "keep-alive",
|
|
477
|
+
"Access-Control-Allow-Origin": corsOptions?.origin || "*"
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
return new Response("Accepted", {
|
|
482
|
+
status: 202,
|
|
483
|
+
headers: {
|
|
484
|
+
"Content-Type": "text/event-stream",
|
|
485
|
+
"Cache-Control": "no-cache",
|
|
486
|
+
Connection: "keep-alive",
|
|
487
|
+
"Access-Control-Allow-Origin": corsOptions?.origin || "*"
|
|
488
|
+
}
|
|
327
489
|
});
|
|
328
490
|
}
|
|
329
491
|
return new Response("Not Found", { status: 404 });
|
|
330
492
|
}
|
|
331
493
|
};
|
|
332
494
|
}
|
|
495
|
+
static serve(path, {
|
|
496
|
+
binding = "MCP_OBJECT",
|
|
497
|
+
corsOptions
|
|
498
|
+
} = {}) {
|
|
499
|
+
let pathname = path;
|
|
500
|
+
if (path === "/") {
|
|
501
|
+
pathname = "/*";
|
|
502
|
+
}
|
|
503
|
+
const basePattern = new URLPattern({ pathname });
|
|
504
|
+
return {
|
|
505
|
+
fetch: async (request, env, ctx) => {
|
|
506
|
+
const corsResponse = handleCORS(request, corsOptions);
|
|
507
|
+
if (corsResponse) {
|
|
508
|
+
return corsResponse;
|
|
509
|
+
}
|
|
510
|
+
const url = new URL(request.url);
|
|
511
|
+
const namespace = env[binding];
|
|
512
|
+
if (request.method === "POST" && basePattern.test(url)) {
|
|
513
|
+
const acceptHeader = request.headers.get("accept");
|
|
514
|
+
if (!acceptHeader?.includes("application/json") || !acceptHeader.includes("text/event-stream")) {
|
|
515
|
+
const body2 = JSON.stringify({
|
|
516
|
+
jsonrpc: "2.0",
|
|
517
|
+
error: {
|
|
518
|
+
code: -32e3,
|
|
519
|
+
message: "Not Acceptable: Client must accept both application/json and text/event-stream"
|
|
520
|
+
},
|
|
521
|
+
id: null
|
|
522
|
+
});
|
|
523
|
+
return new Response(body2, { status: 406 });
|
|
524
|
+
}
|
|
525
|
+
const ct = request.headers.get("content-type");
|
|
526
|
+
if (!ct || !ct.includes("application/json")) {
|
|
527
|
+
const body2 = JSON.stringify({
|
|
528
|
+
jsonrpc: "2.0",
|
|
529
|
+
error: {
|
|
530
|
+
code: -32e3,
|
|
531
|
+
message: "Unsupported Media Type: Content-Type must be application/json"
|
|
532
|
+
},
|
|
533
|
+
id: null
|
|
534
|
+
});
|
|
535
|
+
return new Response(body2, { status: 415 });
|
|
536
|
+
}
|
|
537
|
+
const contentLength = Number.parseInt(
|
|
538
|
+
request.headers.get("content-length") ?? "0",
|
|
539
|
+
10
|
|
540
|
+
);
|
|
541
|
+
if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {
|
|
542
|
+
const body2 = JSON.stringify({
|
|
543
|
+
jsonrpc: "2.0",
|
|
544
|
+
error: {
|
|
545
|
+
code: -32e3,
|
|
546
|
+
message: `Request body too large. Maximum size is ${MAXIMUM_MESSAGE_SIZE_BYTES} bytes`
|
|
547
|
+
},
|
|
548
|
+
id: null
|
|
549
|
+
});
|
|
550
|
+
return new Response(body2, { status: 413 });
|
|
551
|
+
}
|
|
552
|
+
let sessionId = request.headers.get("mcp-session-id");
|
|
553
|
+
let rawMessage;
|
|
554
|
+
try {
|
|
555
|
+
rawMessage = await request.json();
|
|
556
|
+
} catch (error) {
|
|
557
|
+
const body2 = JSON.stringify({
|
|
558
|
+
jsonrpc: "2.0",
|
|
559
|
+
error: {
|
|
560
|
+
code: -32700,
|
|
561
|
+
message: "Parse error: Invalid JSON"
|
|
562
|
+
},
|
|
563
|
+
id: null
|
|
564
|
+
});
|
|
565
|
+
return new Response(body2, { status: 400 });
|
|
566
|
+
}
|
|
567
|
+
let arrayMessage;
|
|
568
|
+
if (Array.isArray(rawMessage)) {
|
|
569
|
+
arrayMessage = rawMessage;
|
|
570
|
+
} else {
|
|
571
|
+
arrayMessage = [rawMessage];
|
|
572
|
+
}
|
|
573
|
+
let messages = [];
|
|
574
|
+
for (const msg of arrayMessage) {
|
|
575
|
+
if (!JSONRPCMessageSchema.safeParse(msg).success) {
|
|
576
|
+
const body2 = JSON.stringify({
|
|
577
|
+
jsonrpc: "2.0",
|
|
578
|
+
error: {
|
|
579
|
+
code: -32700,
|
|
580
|
+
message: "Parse error: Invalid JSON-RPC message"
|
|
581
|
+
},
|
|
582
|
+
id: null
|
|
583
|
+
});
|
|
584
|
+
return new Response(body2, { status: 400 });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
messages = arrayMessage.map((msg) => JSONRPCMessageSchema.parse(msg));
|
|
588
|
+
const isInitializationRequest = messages.some(
|
|
589
|
+
(msg) => InitializeRequestSchema.safeParse(msg).success
|
|
590
|
+
);
|
|
591
|
+
if (isInitializationRequest && sessionId) {
|
|
592
|
+
const body2 = JSON.stringify({
|
|
593
|
+
jsonrpc: "2.0",
|
|
594
|
+
error: {
|
|
595
|
+
code: -32600,
|
|
596
|
+
message: "Invalid Request: Initialization requests must not include a sessionId"
|
|
597
|
+
},
|
|
598
|
+
id: null
|
|
599
|
+
});
|
|
600
|
+
return new Response(body2, { status: 400 });
|
|
601
|
+
}
|
|
602
|
+
if (isInitializationRequest && messages.length > 1) {
|
|
603
|
+
const body2 = JSON.stringify({
|
|
604
|
+
jsonrpc: "2.0",
|
|
605
|
+
error: {
|
|
606
|
+
code: -32600,
|
|
607
|
+
message: "Invalid Request: Only one initialization request is allowed"
|
|
608
|
+
},
|
|
609
|
+
id: null
|
|
610
|
+
});
|
|
611
|
+
return new Response(body2, { status: 400 });
|
|
612
|
+
}
|
|
613
|
+
if (!isInitializationRequest && !sessionId) {
|
|
614
|
+
const body2 = JSON.stringify({
|
|
615
|
+
jsonrpc: "2.0",
|
|
616
|
+
error: {
|
|
617
|
+
code: -32e3,
|
|
618
|
+
message: "Bad Request: Mcp-Session-Id header is required"
|
|
619
|
+
},
|
|
620
|
+
id: null
|
|
621
|
+
});
|
|
622
|
+
return new Response(body2, { status: 400 });
|
|
623
|
+
}
|
|
624
|
+
sessionId = sessionId ?? namespace.newUniqueId().toString();
|
|
625
|
+
const id = namespace.idFromName(`streamable-http:${sessionId}`);
|
|
626
|
+
const doStub = namespace.get(id);
|
|
627
|
+
const isInitialized = await doStub.isInitialized();
|
|
628
|
+
if (isInitializationRequest) {
|
|
629
|
+
await doStub._init(ctx.props);
|
|
630
|
+
} else if (!isInitialized) {
|
|
631
|
+
const body2 = JSON.stringify({
|
|
632
|
+
jsonrpc: "2.0",
|
|
633
|
+
error: {
|
|
634
|
+
code: -32001,
|
|
635
|
+
message: "Session not found"
|
|
636
|
+
},
|
|
637
|
+
id: null
|
|
638
|
+
});
|
|
639
|
+
return new Response(body2, { status: 404 });
|
|
640
|
+
}
|
|
641
|
+
const { readable, writable } = new TransformStream();
|
|
642
|
+
const writer = writable.getWriter();
|
|
643
|
+
const encoder = new TextEncoder();
|
|
644
|
+
const upgradeUrl = new URL(request.url);
|
|
645
|
+
upgradeUrl.pathname = "/streamable-http";
|
|
646
|
+
const response = await doStub.fetch(
|
|
647
|
+
new Request(upgradeUrl, {
|
|
648
|
+
headers: {
|
|
649
|
+
Upgrade: "websocket",
|
|
650
|
+
// Required by PartyServer
|
|
651
|
+
"x-partykit-room": sessionId
|
|
652
|
+
}
|
|
653
|
+
})
|
|
654
|
+
);
|
|
655
|
+
const ws = response.webSocket;
|
|
656
|
+
if (!ws) {
|
|
657
|
+
console.error("Failed to establish WebSocket connection");
|
|
658
|
+
await writer.close();
|
|
659
|
+
const body2 = JSON.stringify({
|
|
660
|
+
jsonrpc: "2.0",
|
|
661
|
+
error: {
|
|
662
|
+
code: -32001,
|
|
663
|
+
message: "Failed to establish WebSocket connection"
|
|
664
|
+
},
|
|
665
|
+
id: null
|
|
666
|
+
});
|
|
667
|
+
return new Response(body2, { status: 500 });
|
|
668
|
+
}
|
|
669
|
+
const requestIds = /* @__PURE__ */ new Set();
|
|
670
|
+
ws.accept();
|
|
671
|
+
ws.addEventListener("message", (event) => {
|
|
672
|
+
async function onMessage(event2) {
|
|
673
|
+
try {
|
|
674
|
+
const data = typeof event2.data === "string" ? event2.data : new TextDecoder().decode(event2.data);
|
|
675
|
+
const message = JSON.parse(data);
|
|
676
|
+
const result = JSONRPCMessageSchema.safeParse(message);
|
|
677
|
+
if (!result.success) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (isJSONRPCResponse(result.data) || isJSONRPCError(result.data)) {
|
|
681
|
+
requestIds.delete(result.data.id);
|
|
682
|
+
}
|
|
683
|
+
const messageText = `event: message
|
|
684
|
+
data: ${JSON.stringify(result.data)}
|
|
685
|
+
|
|
686
|
+
`;
|
|
687
|
+
await writer.write(encoder.encode(messageText));
|
|
688
|
+
if (requestIds.size === 0) {
|
|
689
|
+
ws.close();
|
|
690
|
+
}
|
|
691
|
+
} catch (error) {
|
|
692
|
+
console.error("Error forwarding message to SSE:", error);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
onMessage(event).catch(console.error);
|
|
696
|
+
});
|
|
697
|
+
ws.addEventListener("error", (error) => {
|
|
698
|
+
async function onError(error2) {
|
|
699
|
+
try {
|
|
700
|
+
await writer.close();
|
|
701
|
+
} catch (e) {
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
onError(error).catch(console.error);
|
|
705
|
+
});
|
|
706
|
+
ws.addEventListener("close", () => {
|
|
707
|
+
async function onClose() {
|
|
708
|
+
try {
|
|
709
|
+
await writer.close();
|
|
710
|
+
} catch (error) {
|
|
711
|
+
console.error("Error closing SSE connection:", error);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
onClose().catch(console.error);
|
|
715
|
+
});
|
|
716
|
+
const hasOnlyNotificationsOrResponses = messages.every(
|
|
717
|
+
(msg) => isJSONRPCNotification(msg) || isJSONRPCResponse(msg)
|
|
718
|
+
);
|
|
719
|
+
if (hasOnlyNotificationsOrResponses) {
|
|
720
|
+
for (const message of messages) {
|
|
721
|
+
ws.send(JSON.stringify(message));
|
|
722
|
+
}
|
|
723
|
+
ws.close();
|
|
724
|
+
return new Response(null, { status: 202 });
|
|
725
|
+
}
|
|
726
|
+
for (const message of messages) {
|
|
727
|
+
if (isJSONRPCRequest(message)) {
|
|
728
|
+
requestIds.add(message.id);
|
|
729
|
+
}
|
|
730
|
+
ws.send(JSON.stringify(message));
|
|
731
|
+
}
|
|
732
|
+
return new Response(readable, {
|
|
733
|
+
headers: {
|
|
734
|
+
"Content-Type": "text/event-stream",
|
|
735
|
+
"Cache-Control": "no-cache",
|
|
736
|
+
Connection: "keep-alive",
|
|
737
|
+
"mcp-session-id": sessionId,
|
|
738
|
+
"Access-Control-Allow-Origin": corsOptions?.origin || "*"
|
|
739
|
+
},
|
|
740
|
+
status: 200
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
const body = JSON.stringify({
|
|
744
|
+
jsonrpc: "2.0",
|
|
745
|
+
error: {
|
|
746
|
+
code: -32e3,
|
|
747
|
+
message: "Method not allowed"
|
|
748
|
+
},
|
|
749
|
+
id: null
|
|
750
|
+
});
|
|
751
|
+
return new Response(body, { status: 405 });
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
}
|
|
333
755
|
};
|
|
334
756
|
_status = new WeakMap();
|
|
335
757
|
_transport = new WeakMap();
|
|
336
|
-
|
|
758
|
+
_transportType = new WeakMap();
|
|
759
|
+
_requestIdToConnectionId = new WeakMap();
|
|
337
760
|
_agent = new WeakMap();
|
|
338
761
|
_McpAgent_instances = new WeakSet();
|
|
339
762
|
initialize_fn = async function() {
|
|
@@ -343,6 +766,7 @@ initialize_fn = async function() {
|
|
|
343
766
|
__privateSet(this, _status, "started");
|
|
344
767
|
});
|
|
345
768
|
};
|
|
769
|
+
var McpAgent = _McpAgent;
|
|
346
770
|
export {
|
|
347
771
|
McpAgent
|
|
348
772
|
};
|
package/dist/mcp/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/index.ts"],"sourcesContent":["import { DurableObject } from \"cloudflare:workers\";\nimport { Agent } from \"../\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Connection } from \"../\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\nimport { JSONRPCMessageSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nconst MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024; // 4MB\n\n// CORS helper function\nfunction handleCORS(\n request: Request,\n corsOptions?: CORSOptions\n): Response | null {\n const origin = request.headers.get(\"Origin\") || \"*\";\n const corsHeaders = {\n \"Access-Control-Allow-Origin\": corsOptions?.origin || origin,\n \"Access-Control-Allow-Methods\":\n corsOptions?.methods || \"GET, POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": corsOptions?.headers || \"Content-Type\",\n \"Access-Control-Max-Age\": (corsOptions?.maxAge || 86400).toString(),\n };\n\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: corsHeaders });\n }\n\n return null;\n}\n\ninterface CORSOptions {\n origin?: string;\n methods?: string;\n headers?: string;\n maxAge?: number;\n}\n\nclass McpTransport implements Transport {\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n sessionId?: string;\n\n #getWebSocket: () => WebSocket | null;\n #started = false;\n constructor(getWebSocket: () => WebSocket | null) {\n this.#getWebSocket = getWebSocket;\n }\n\n async start() {\n // The transport does not manage the WebSocket connection since it's terminated\n // by the Durable Object in order to allow hibernation. There's nothing to initialize.\n if (this.#started) {\n throw new Error(\"Transport already started\");\n }\n this.#started = true;\n }\n\n async send(message: JSONRPCMessage) {\n if (!this.#started) {\n throw new Error(\"Transport not started\");\n }\n const websocket = this.#getWebSocket();\n if (!websocket) {\n throw new Error(\"WebSocket not connected\");\n }\n try {\n websocket.send(JSON.stringify(message));\n } catch (error) {\n this.onerror?.(error as Error);\n throw error;\n }\n }\n\n async close() {\n // Similar to start, the only thing to do is to pass the event on to the server\n this.onclose?.();\n }\n}\n\nexport abstract class McpAgent<\n Env = unknown,\n State = unknown,\n Props extends Record<string, unknown> = Record<string, unknown>,\n> extends DurableObject<Env> {\n #status: \"zero\" | \"starting\" | \"started\" = \"zero\";\n #transport?: McpTransport;\n #connected = false;\n\n /**\n * Since McpAgent's _aren't_ yet real \"Agents\" (they route differently, don't support\n * websockets, don't support hibernation), let's only expose a couple of the methods\n * to the outer class: initialState/state/setState/onStateUpdate/sql\n */\n #agent: Agent<Env, State>;\n\n protected constructor(ctx: DurableObjectState, env: Env) {\n super(ctx, env);\n const self = this;\n\n this.#agent = new (class extends Agent<Env, State> {\n static options = {\n hibernate: true,\n };\n\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n return self.onStateUpdate(state, source);\n }\n })(ctx, env);\n }\n\n /**\n * Agents API allowlist\n */\n initialState!: State;\n get state() {\n return this.#agent.state;\n }\n sql<T = Record<string, string | number | boolean | null>>(\n strings: TemplateStringsArray,\n ...values: (string | number | boolean | null)[]\n ) {\n return this.#agent.sql<T>(strings, ...values);\n }\n\n setState(state: State) {\n return this.#agent.setState(state);\n }\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n // override this to handle state updates\n }\n async onStart() {\n const self = this;\n\n this.#agent = new (class extends Agent<Env, State> {\n initialState: State = self.initialState;\n static options = {\n hibernate: true,\n };\n\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n return self.onStateUpdate(state, source);\n }\n })(this.ctx, this.env);\n\n this.props = (await this.ctx.storage.get(\"props\")) as Props;\n this.init?.();\n\n // Connect to the MCP server\n this.#transport = new McpTransport(() => this.getWebSocket());\n await this.server.connect(this.#transport);\n }\n\n /**\n * McpAgent API\n */\n abstract server: McpServer;\n props!: Props;\n initRun = false;\n\n abstract init(): Promise<void>;\n\n async _init(props: Props) {\n await this.ctx.storage.put(\"props\", props);\n this.props = props;\n if (!this.initRun) {\n this.initRun = true;\n await this.init();\n }\n }\n\n async #initialize(): Promise<void> {\n await this.ctx.blockConcurrencyWhile(async () => {\n this.#status = \"starting\";\n await this.onStart();\n this.#status = \"started\";\n });\n }\n\n // Allow the worker to fetch a websocket connection to the agent\n async fetch(request: Request): Promise<Response> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n\n // Only handle WebSocket upgrade requests\n if (request.headers.get(\"Upgrade\") !== \"websocket\") {\n return new Response(\"Expected WebSocket Upgrade request\", {\n status: 400,\n });\n }\n\n const url = new URL(request.url);\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\"Missing sessionId\", { status: 400 });\n }\n\n // For now, each agent can only have one connection\n // If we get an upgrade while already connected, we should error\n if (this.#connected) {\n return new Response(\"WebSocket already connected\", { status: 400 });\n }\n\n // Defer to the Agent's fetch method to handle the WebSocket connection\n // PartyServer does a lot to manage the connections under the hood\n const response = await this.#agent.fetch(request);\n\n this.#connected = true;\n\n // Connect to the MCP server\n this.#transport = new McpTransport(() => this.getWebSocket());\n await this.server.connect(this.#transport);\n\n return response;\n }\n\n getWebSocket() {\n const websockets = this.ctx.getWebSockets();\n if (websockets.length === 0) {\n return null;\n }\n return websockets[0];\n }\n\n async onMCPMessage(sessionId: string, request: Request): Promise<Response> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n try {\n const contentType = request.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"application/json\")) {\n return new Response(`Unsupported content-type: ${contentType}`, {\n status: 400,\n });\n }\n\n // check if the request body is too large\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") || \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE) {\n return new Response(`Request body too large: ${contentLength} bytes`, {\n status: 400,\n });\n }\n\n // Clone the request before reading the body to avoid stream issues\n const message = await request.json();\n let parsedMessage: JSONRPCMessage;\n try {\n parsedMessage = JSONRPCMessageSchema.parse(message);\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n throw error;\n }\n\n this.#transport?.onmessage?.(parsedMessage);\n return new Response(\"Accepted\", { status: 202 });\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n return new Response(String(error), { status: 400 });\n }\n }\n\n // This is unused since there are no incoming websocket messages\n async webSocketMessage(ws: WebSocket, event: ArrayBuffer | string) {\n let message: JSONRPCMessage;\n try {\n // Ensure event is a string\n const data =\n typeof event === \"string\" ? event : new TextDecoder().decode(event);\n message = JSONRPCMessageSchema.parse(JSON.parse(data));\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n return;\n }\n\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n\n this.#transport?.onmessage?.(message);\n }\n\n // WebSocket event handlers for hibernation support\n async webSocketError(ws: WebSocket, error: unknown): Promise<void> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n this.#transport?.onerror?.(error as Error);\n }\n\n async webSocketClose(\n ws: WebSocket,\n code: number,\n reason: string,\n wasClean: boolean\n ): Promise<void> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n this.#transport?.onclose?.();\n this.#connected = false;\n }\n\n static mount(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n }: {\n binding?: string;\n corsOptions?: CORSOptions;\n } = {}\n ) {\n let pathname = path;\n if (path === \"/\") {\n pathname = \"/*\";\n }\n const basePattern = new URLPattern({ pathname });\n const messagePattern = new URLPattern({ pathname: `${pathname}/message` });\n\n return {\n fetch: async (\n request: Request,\n env: Record<string, DurableObjectNamespace<McpAgent>>,\n ctx: ExecutionContext\n ) => {\n // Handle CORS preflight\n const corsResponse = handleCORS(request, corsOptions);\n if (corsResponse) return corsResponse;\n\n const url = new URL(request.url);\n const namespace = env[binding];\n\n // Handle SSE connections\n if (request.method === \"GET\" && basePattern.test(url)) {\n // Use a session ID if one is passed in, or create a unique\n // session ID for this connection\n const sessionId =\n url.searchParams.get(\"sessionId\") ||\n namespace.newUniqueId().toString();\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Send the endpoint event\n const endpointMessage = `event: endpoint\\ndata: ${encodeURI(`${pathname}/message`)}?sessionId=${sessionId}\\n\\n`;\n writer.write(encoder.encode(endpointMessage));\n\n // Get the Durable Object\n const id = namespace.idFromString(sessionId);\n const doStub = namespace.get(id);\n\n // Initialize the object\n await doStub._init(ctx.props);\n\n // Connect to the Durable Object via WebSocket\n const upgradeUrl = new URL(request.url);\n upgradeUrl.searchParams.set(\"sessionId\", sessionId);\n const response = await doStub.fetch(\n new Request(upgradeUrl, {\n headers: {\n Upgrade: \"websocket\",\n // Required by PartyServer\n \"x-partykit-room\": sessionId,\n },\n })\n );\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n await writer.close();\n return;\n }\n\n // Accept the WebSocket\n ws.accept();\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", async (event) => {\n try {\n const message = JSON.parse(event.data);\n\n // validate that the message is a valid JSONRPC message\n const result = JSONRPCMessageSchema.safeParse(message);\n if (!result.success) {\n // The message was not a valid JSONRPC message, so we will drop it\n // PartyKit will broadcast state change messages to all connected clients\n // and we need to filter those out so they are not passed to MCP clients\n return;\n }\n\n // Send the message as an SSE event\n const messageText = `event: message\\ndata: ${JSON.stringify(result.data)}\\n\\n`;\n await writer.write(encoder.encode(messageText));\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", async (error) => {\n try {\n await writer.close();\n } catch (e) {\n // Ignore errors when closing\n }\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", async () => {\n try {\n await writer.close();\n } catch (error) {\n console.error(\"Error closing SSE connection:\", error);\n }\n });\n\n // Return the SSE response\n return new Response(readable, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": corsOptions?.origin || \"*\",\n },\n });\n }\n\n // Handle MCP messages\n if (request.method === \"POST\" && messagePattern.test(url)) {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\n `Missing sessionId. Expected POST to ${pathname} to initiate new one`,\n { status: 400 }\n );\n }\n\n // Get the Durable Object\n const object = namespace.get(namespace.idFromString(sessionId));\n\n // Forward the request to the Durable Object\n const response = await object.onMCPMessage(sessionId, request);\n\n // Add CORS headers\n const headers = new Headers();\n response.headers.forEach?.((value, key) => {\n headers.set(key, value);\n });\n headers.set(\n \"Access-Control-Allow-Origin\",\n corsOptions?.origin || \"*\"\n );\n\n return new Response(response.body as unknown as BodyInit, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/index.ts"],"sourcesContent":["import { DurableObject } from \"cloudflare:workers\";\nimport { Agent } from \"../\";\nimport type { WSMessage } from \"../\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Connection } from \"../\";\nimport type {\n JSONRPCError,\n JSONRPCMessage,\n JSONRPCNotification,\n JSONRPCRequest,\n JSONRPCResponse,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n InitializeRequestSchema,\n isJSONRPCError,\n isJSONRPCNotification,\n isJSONRPCRequest,\n isJSONRPCResponse,\n JSONRPCErrorSchema,\n JSONRPCMessageSchema,\n JSONRPCNotificationSchema,\n JSONRPCRequestSchema,\n JSONRPCResponseSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nconst MAXIMUM_MESSAGE_SIZE_BYTES = 4 * 1024 * 1024; // 4MB\n\n// CORS helper function\nfunction handleCORS(\n request: Request,\n corsOptions?: CORSOptions\n): Response | null {\n const origin = request.headers.get(\"Origin\") || \"*\";\n const corsHeaders = {\n \"Access-Control-Allow-Origin\": corsOptions?.origin || origin,\n \"Access-Control-Allow-Methods\":\n corsOptions?.methods || \"GET, POST, OPTIONS\",\n \"Access-Control-Allow-Headers\": corsOptions?.headers || \"Content-Type\",\n \"Access-Control-Max-Age\": (corsOptions?.maxAge || 86400).toString(),\n };\n\n if (request.method === \"OPTIONS\") {\n return new Response(null, { headers: corsHeaders });\n }\n\n return null;\n}\n\ninterface CORSOptions {\n origin?: string;\n methods?: string;\n headers?: string;\n maxAge?: number;\n}\n\nclass McpSSETransport implements Transport {\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n sessionId?: string;\n\n #getWebSocket: () => WebSocket | null;\n #started = false;\n constructor(getWebSocket: () => WebSocket | null) {\n this.#getWebSocket = getWebSocket;\n }\n\n async start() {\n // The transport does not manage the WebSocket connection since it's terminated\n // by the Durable Object in order to allow hibernation. There's nothing to initialize.\n if (this.#started) {\n throw new Error(\"Transport already started\");\n }\n this.#started = true;\n }\n\n async send(message: JSONRPCMessage) {\n if (!this.#started) {\n throw new Error(\"Transport not started\");\n }\n const websocket = this.#getWebSocket();\n if (!websocket) {\n throw new Error(\"WebSocket not connected\");\n }\n try {\n websocket.send(JSON.stringify(message));\n } catch (error) {\n this.onerror?.(error as Error);\n throw error;\n }\n }\n\n async close() {\n // Similar to start, the only thing to do is to pass the event on to the server\n this.onclose?.();\n }\n}\n\ntype TransportType = \"sse\" | \"streamable-http\" | \"unset\";\n\nclass McpStreamableHttpTransport implements Transport {\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n sessionId?: string;\n\n // TODO: If there is an open connection to send server-initiated messages\n // back, we should use that connection\n #getWebSocketForGetRequest: () => WebSocket | null;\n\n // Get the appropriate websocket connection for a given message id\n #getWebSocketForMessageID: (id: string) => WebSocket | null;\n\n // Notify the server that a response has been sent for a given message id\n // so that it may clean up it's mapping of message ids to connections\n // once they are no longer needed\n #notifyResponseIdSent: (id: string) => void;\n\n #started = false;\n constructor(\n getWebSocketForMessageID: (id: string) => WebSocket | null,\n notifyResponseIdSent: (id: string | number) => void\n ) {\n this.#getWebSocketForMessageID = getWebSocketForMessageID;\n this.#notifyResponseIdSent = notifyResponseIdSent;\n // TODO\n this.#getWebSocketForGetRequest = () => null;\n }\n\n async start() {\n // The transport does not manage the WebSocket connection since it's terminated\n // by the Durable Object in order to allow hibernation. There's nothing to initialize.\n if (this.#started) {\n throw new Error(\"Transport already started\");\n }\n this.#started = true;\n }\n\n async send(message: JSONRPCMessage) {\n if (!this.#started) {\n throw new Error(\"Transport not started\");\n }\n\n let websocket: WebSocket | null = null;\n\n if (isJSONRPCResponse(message) || isJSONRPCError(message)) {\n websocket = this.#getWebSocketForMessageID(message.id.toString());\n if (!websocket) {\n throw new Error(\n `Could not find WebSocket for message id: ${message.id}`\n );\n }\n } else if (isJSONRPCRequest(message)) {\n // requests originating from the server must be sent over the\n // the connection created by a GET request\n websocket = this.#getWebSocketForGetRequest();\n } else if (isJSONRPCNotification(message)) {\n // notifications do not have an id\n // but do have a relatedRequestId field\n // so that they can be sent to the correct connection\n websocket = null;\n }\n\n try {\n websocket?.send(JSON.stringify(message));\n if (isJSONRPCResponse(message)) {\n this.#notifyResponseIdSent(message.id.toString());\n }\n } catch (error) {\n this.onerror?.(error as Error);\n throw error;\n }\n }\n\n async close() {\n // Similar to start, the only thing to do is to pass the event on to the server\n this.onclose?.();\n }\n}\n\nexport abstract class McpAgent<\n Env = unknown,\n State = unknown,\n Props extends Record<string, unknown> = Record<string, unknown>,\n> extends DurableObject<Env> {\n #status: \"zero\" | \"starting\" | \"started\" = \"zero\";\n #transport?: Transport;\n #transportType: TransportType = \"unset\";\n #requestIdToConnectionId: Map<string | number, string> = new Map();\n\n /**\n * Since McpAgent's _aren't_ yet real \"Agents\", let's only expose a couple of the methods\n * to the outer class: initialState/state/setState/onStateUpdate/sql\n */\n #agent: Agent<Env, State>;\n\n protected constructor(ctx: DurableObjectState, env: Env) {\n super(ctx, env);\n const self = this;\n\n this.#agent = new (class extends Agent<Env, State> {\n static options = {\n hibernate: true,\n };\n\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n return self.onStateUpdate(state, source);\n }\n\n async onMessage(\n connection: Connection,\n message: WSMessage\n ): Promise<void> {\n return self.onMessage(connection, message);\n }\n })(ctx, env);\n }\n\n /**\n * Agents API allowlist\n */\n initialState!: State;\n get state() {\n return this.#agent.state;\n }\n sql<T = Record<string, string | number | boolean | null>>(\n strings: TemplateStringsArray,\n ...values: (string | number | boolean | null)[]\n ) {\n return this.#agent.sql<T>(strings, ...values);\n }\n\n setState(state: State) {\n return this.#agent.setState(state);\n }\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n // override this to handle state updates\n }\n async onStart() {\n const self = this;\n\n this.#agent = new (class extends Agent<Env, State> {\n initialState: State = self.initialState;\n static options = {\n hibernate: true,\n };\n\n onStateUpdate(state: State | undefined, source: Connection | \"server\") {\n return self.onStateUpdate(state, source);\n }\n\n async onMessage(connection: Connection, event: WSMessage) {\n return self.onMessage(connection, event);\n }\n })(this.ctx, this.env);\n\n this.props = (await this.ctx.storage.get(\"props\")) as Props;\n this.#transportType = (await this.ctx.storage.get(\n \"transportType\"\n )) as TransportType;\n this.init?.();\n\n // Connect to the MCP server\n if (this.#transportType === \"sse\") {\n this.#transport = new McpSSETransport(() => this.getWebSocket());\n await this.server.connect(this.#transport);\n } else if (this.#transportType === \"streamable-http\") {\n this.#transport = new McpStreamableHttpTransport(\n (id) => this.getWebSocketForResponseID(id),\n (id) => this.#requestIdToConnectionId.delete(id)\n );\n await this.server.connect(this.#transport);\n }\n }\n\n /**\n * McpAgent API\n */\n abstract server: McpServer;\n props!: Props;\n initRun = false;\n\n abstract init(): Promise<void>;\n\n async _init(props: Props) {\n await this.ctx.storage.put(\"props\", props ?? {});\n await this.ctx.storage.put(\"transportType\", \"unset\");\n this.props = props;\n if (!this.initRun) {\n this.initRun = true;\n await this.init();\n }\n }\n\n isInitialized() {\n return this.initRun;\n }\n\n async #initialize(): Promise<void> {\n await this.ctx.blockConcurrencyWhile(async () => {\n this.#status = \"starting\";\n await this.onStart();\n this.#status = \"started\";\n });\n }\n\n // Allow the worker to fetch a websocket connection to the agent\n async fetch(request: Request): Promise<Response> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n\n // Only handle WebSocket upgrade requests\n if (request.headers.get(\"Upgrade\") !== \"websocket\") {\n return new Response(\"Expected WebSocket Upgrade request\", {\n status: 400,\n });\n }\n\n // This request does not come from the user. The worker generates this\n // request to generate a websocket connection to the agent.\n const url = new URL(request.url);\n // This is not the path that the user requested, but the path that the worker\n // generated. We'll use this path to determine which transport to use.\n const path = url.pathname;\n\n switch (path) {\n case \"/sse\": {\n // For SSE connections, we can only have one open connection per session\n // If we get an upgrade while already connected, we should error\n const websockets = this.ctx.getWebSockets();\n if (websockets.length > 0) {\n return new Response(\"Websocket already connected\", { status: 400 });\n }\n\n // This session must always use the SSE transporo\n await this.ctx.storage.put(\"transportType\", \"sse\");\n this.#transportType = \"sse\";\n\n if (!this.#transport) {\n this.#transport = new McpSSETransport(() => this.getWebSocket());\n await this.server.connect(this.#transport);\n }\n\n // Defer to the Agent's fetch method to handle the WebSocket connection\n return this.#agent.fetch(request);\n }\n case \"/streamable-http\": {\n if (!this.#transport) {\n this.#transport = new McpStreamableHttpTransport(\n (id) => this.getWebSocketForResponseID(id),\n (id) => this.#requestIdToConnectionId.delete(id)\n );\n await this.server.connect(this.#transport);\n }\n\n // This session must always use the streamable-http transport\n await this.ctx.storage.put(\"transportType\", \"streamable-http\");\n this.#transportType = \"streamable-http\";\n\n return this.#agent.fetch(request);\n }\n default:\n return new Response(\n \"Internal Server Error: Expected /sse or /streamable-http path\",\n {\n status: 500,\n }\n );\n }\n }\n\n getWebSocket() {\n const websockets = this.ctx.getWebSockets();\n if (websockets.length === 0) {\n return null;\n }\n return websockets[0];\n }\n\n getWebSocketForResponseID(id: string): WebSocket | null {\n const connectionId = this.#requestIdToConnectionId.get(id);\n if (connectionId === undefined) {\n return null;\n }\n return this.#agent.getConnection(connectionId) ?? null;\n }\n\n // All messages received here. This is currently never called\n async onMessage(connection: Connection, event: WSMessage) {\n // Since we address the DO via both the protocol and the session id,\n // this should never happen, but let's enforce it just in case\n if (this.#transportType !== \"streamable-http\") {\n const err = new Error(\n \"Internal Server Error: Expected streamable-http protocol\"\n );\n this.#transport?.onerror?.(err);\n return;\n }\n\n let message: JSONRPCMessage;\n try {\n // Ensure event is a string\n const data =\n typeof event === \"string\" ? event : new TextDecoder().decode(event);\n message = JSONRPCMessageSchema.parse(JSON.parse(data));\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n return;\n }\n\n // We need to map every incoming message to the connection that it came in on\n // so that we can send relevant responses and notifications back on the same connection\n if (isJSONRPCRequest(message)) {\n this.#requestIdToConnectionId.set(message.id.toString(), connection.id);\n }\n\n this.#transport?.onmessage?.(message);\n }\n\n // All messages received over SSE after the initial connection has been established\n // will be passed here\n async onSSEMcpMessage(\n sessionId: string,\n request: Request\n ): Promise<Error | null> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n\n // Since we address the DO via both the protocol and the session id,\n // this should never happen, but let's enforce it just in case\n if (this.#transportType !== \"sse\") {\n return new Error(\"Internal Server Error: Expected SSE protocol\");\n }\n\n try {\n const message = await request.json();\n let parsedMessage: JSONRPCMessage;\n try {\n parsedMessage = JSONRPCMessageSchema.parse(message);\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n throw error;\n }\n\n this.#transport?.onmessage?.(parsedMessage);\n return null;\n } catch (error) {\n this.#transport?.onerror?.(error as Error);\n return error as Error;\n }\n }\n\n // Delegate all websocket events to the underlying agent\n async webSocketMessage(\n ws: WebSocket,\n event: ArrayBuffer | string\n ): Promise<void> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n return await this.#agent.webSocketMessage(ws, event);\n }\n\n // WebSocket event handlers for hibernation support\n async webSocketError(ws: WebSocket, error: unknown): Promise<void> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n return await this.#agent.webSocketError(ws, error);\n }\n\n async webSocketClose(\n ws: WebSocket,\n code: number,\n reason: string,\n wasClean: boolean\n ): Promise<void> {\n if (this.#status !== \"started\") {\n // This means the server \"woke up\" after hibernation\n // so we need to hydrate it again\n await this.#initialize();\n }\n return await this.#agent.webSocketClose(ws, code, reason, wasClean);\n }\n\n static mount(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n }: {\n binding?: string;\n corsOptions?: CORSOptions;\n } = {}\n ) {\n return McpAgent.serveSSE(path, { binding, corsOptions });\n }\n\n static serveSSE(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n }: {\n binding?: string;\n corsOptions?: CORSOptions;\n } = {}\n ) {\n let pathname = path;\n if (path === \"/\") {\n pathname = \"/*\";\n }\n const basePattern = new URLPattern({ pathname });\n const messagePattern = new URLPattern({ pathname: `${pathname}/message` });\n\n return {\n fetch: async (\n request: Request,\n env: Record<string, DurableObjectNamespace<McpAgent>>,\n ctx: ExecutionContext\n ): Promise<Response> => {\n // Handle CORS preflight\n const corsResponse = handleCORS(request, corsOptions);\n if (corsResponse) return corsResponse;\n\n const url = new URL(request.url);\n const namespace = env[binding];\n\n // Handle initial SSE connection\n if (request.method === \"GET\" && basePattern.test(url)) {\n // Use a session ID if one is passed in, or create a unique\n // session ID for this connection\n const sessionId =\n url.searchParams.get(\"sessionId\") ||\n namespace.newUniqueId().toString();\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Send the endpoint event\n const endpointMessage = `event: endpoint\\ndata: ${encodeURI(`${pathname}/message`)}?sessionId=${sessionId}\\n\\n`;\n writer.write(encoder.encode(endpointMessage));\n\n // Get the Durable Object\n const id = namespace.idFromName(`sse:${sessionId}`);\n const doStub = namespace.get(id);\n\n // Initialize the object\n await doStub._init(ctx.props);\n\n // Connect to the Durable Object via WebSocket\n const upgradeUrl = new URL(request.url);\n // enforce that the path that the DO receives is always /sse\n upgradeUrl.pathname = \"/sse\";\n const response = await doStub.fetch(\n new Request(upgradeUrl, {\n headers: {\n Upgrade: \"websocket\",\n // Required by PartyServer\n \"x-partykit-room\": sessionId,\n },\n })\n );\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n await writer.close();\n return new Response(\"Failed to establish WebSocket connection\", {\n status: 500,\n });\n }\n\n // Accept the WebSocket\n ws.accept();\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", (event) => {\n async function onMessage(event: MessageEvent) {\n try {\n const message = JSON.parse(event.data);\n\n // validate that the message is a valid JSONRPC message\n const result = JSONRPCMessageSchema.safeParse(message);\n if (!result.success) {\n // The message was not a valid JSONRPC message, so we will drop it\n // PartyKit will broadcast state change messages to all connected clients\n // and we need to filter those out so they are not passed to MCP clients\n return;\n }\n\n // Send the message as an SSE event\n const messageText = `event: message\\ndata: ${JSON.stringify(result.data)}\\n\\n`;\n await writer.write(encoder.encode(messageText));\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n }\n onMessage(event).catch(console.error);\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", (error) => {\n async function onError(error: Event) {\n try {\n await writer.close();\n } catch (e) {\n // Ignore errors when closing\n }\n }\n onError(error).catch(console.error);\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", () => {\n async function onClose() {\n try {\n await writer.close();\n } catch (error) {\n console.error(\"Error closing SSE connection:\", error);\n }\n }\n onClose().catch(console.error);\n });\n\n // Return the SSE response\n return new Response(readable, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": corsOptions?.origin || \"*\",\n },\n });\n }\n\n // Handle incoming MCP messages. These will be passed to McpAgent\n // but the response will be sent back via the open SSE connection\n // so we only need to return a 202 Accepted response for success\n if (request.method === \"POST\" && messagePattern.test(url)) {\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) {\n return new Response(\n `Missing sessionId. Expected POST to ${pathname} to initiate new one`,\n { status: 400 }\n );\n }\n\n const contentType = request.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"application/json\")) {\n return new Response(`Unsupported content-type: ${contentType}`, {\n status: 400,\n });\n }\n\n // check if the request body is too large\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") || \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {\n return new Response(\n `Request body too large: ${contentLength} bytes`,\n {\n status: 400,\n }\n );\n }\n\n // Get the Durable Object\n const id = namespace.idFromName(`sse:${sessionId}`);\n const doStub = namespace.get(id);\n\n // Forward the request to the Durable Object\n const error = await doStub.onSSEMcpMessage(sessionId, request);\n\n if (error) {\n return new Response(error.message, {\n status: 400,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": corsOptions?.origin || \"*\",\n },\n });\n }\n\n return new Response(\"Accepted\", {\n status: 202,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": corsOptions?.origin || \"*\",\n },\n });\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n };\n }\n\n static serve(\n path: string,\n {\n binding = \"MCP_OBJECT\",\n corsOptions,\n }: { binding?: string; corsOptions?: CORSOptions } = {}\n ) {\n let pathname = path;\n if (path === \"/\") {\n pathname = \"/*\";\n }\n const basePattern = new URLPattern({ pathname });\n\n return {\n fetch: async (\n request: Request,\n env: Record<string, DurableObjectNamespace<McpAgent>>,\n ctx: ExecutionContext\n ) => {\n // Handle CORS preflight\n const corsResponse = handleCORS(request, corsOptions);\n if (corsResponse) {\n return corsResponse;\n }\n\n const url = new URL(request.url);\n const namespace = env[binding];\n\n if (request.method === \"POST\" && basePattern.test(url)) {\n // validate the Accept header\n const acceptHeader = request.headers.get(\"accept\");\n // The client MUST include an Accept header, listing both application/json and text/event-stream as supported content types.\n if (\n !acceptHeader?.includes(\"application/json\") ||\n !acceptHeader.includes(\"text/event-stream\")\n ) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message:\n \"Not Acceptable: Client must accept both application/json and text/event-stream\",\n },\n id: null,\n });\n return new Response(body, { status: 406 });\n }\n\n const ct = request.headers.get(\"content-type\");\n if (!ct || !ct.includes(\"application/json\")) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message:\n \"Unsupported Media Type: Content-Type must be application/json\",\n },\n id: null,\n });\n return new Response(body, { status: 415 });\n }\n\n // Check content length against maximum allowed size\n const contentLength = Number.parseInt(\n request.headers.get(\"content-length\") ?? \"0\",\n 10\n );\n if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: `Request body too large. Maximum size is ${MAXIMUM_MESSAGE_SIZE_BYTES} bytes`,\n },\n id: null,\n });\n return new Response(body, { status: 413 });\n }\n\n let sessionId = request.headers.get(\"mcp-session-id\");\n let rawMessage: unknown;\n\n try {\n rawMessage = await request.json();\n } catch (error) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32700,\n message: \"Parse error: Invalid JSON\",\n },\n id: null,\n });\n return new Response(body, { status: 400 });\n }\n\n // Make sure the message is an array to simplify logic\n let arrayMessage: unknown[];\n if (Array.isArray(rawMessage)) {\n arrayMessage = rawMessage;\n } else {\n arrayMessage = [rawMessage];\n }\n\n let messages: JSONRPCMessage[] = [];\n\n // Try to parse each message as JSON RPC. Fail if any message is invalid\n for (const msg of arrayMessage) {\n if (!JSONRPCMessageSchema.safeParse(msg).success) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32700,\n message: \"Parse error: Invalid JSON-RPC message\",\n },\n id: null,\n });\n return new Response(body, { status: 400 });\n }\n }\n\n messages = arrayMessage.map((msg) => JSONRPCMessageSchema.parse(msg));\n\n // Before we pass the messages to the agent, there's another error condition we need to enforce\n // Check if this is an initialization request\n // https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/lifecycle/\n const isInitializationRequest = messages.some(\n (msg) => InitializeRequestSchema.safeParse(msg).success\n );\n\n if (isInitializationRequest && sessionId) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32600,\n message:\n \"Invalid Request: Initialization requests must not include a sessionId\",\n },\n id: null,\n });\n return new Response(body, { status: 400 });\n }\n\n // The initialization request must be the only request in the batch\n if (isInitializationRequest && messages.length > 1) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32600,\n message:\n \"Invalid Request: Only one initialization request is allowed\",\n },\n id: null,\n });\n return new Response(body, { status: 400 });\n }\n\n // If an Mcp-Session-Id is returned by the server during initialization,\n // clients using the Streamable HTTP transport MUST include it\n // in the Mcp-Session-Id header on all of their subsequent HTTP requests.\n if (!isInitializationRequest && !sessionId) {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: \"Bad Request: Mcp-Session-Id header is required\",\n },\n id: null,\n });\n return new Response(body, { status: 400 });\n }\n\n // If we don't have a sessionId, we are serving an initialization request\n // and need to generate a new sessionId\n sessionId = sessionId ?? namespace.newUniqueId().toString();\n\n // fetch the agent DO\n const id = namespace.idFromName(`streamable-http:${sessionId}`);\n const doStub = namespace.get(id);\n const isInitialized = await doStub.isInitialized();\n\n if (isInitializationRequest) {\n await doStub._init(ctx.props);\n } else if (!isInitialized) {\n // if we have gotten here, then a session id that was never initialized\n // was provided\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32001,\n message: \"Session not found\",\n },\n id: null,\n });\n return new Response(body, { status: 404 });\n }\n\n // We've evaluated all the error conditions! Now it's time to establish\n // all the streams\n\n // Create a Transform Stream for SSE\n const { readable, writable } = new TransformStream();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n\n // Connect to the Durable Object via WebSocket\n const upgradeUrl = new URL(request.url);\n upgradeUrl.pathname = \"/streamable-http\";\n const response = await doStub.fetch(\n new Request(upgradeUrl, {\n headers: {\n Upgrade: \"websocket\",\n // Required by PartyServer\n \"x-partykit-room\": sessionId,\n },\n })\n );\n\n // Get the WebSocket\n const ws = response.webSocket;\n if (!ws) {\n console.error(\"Failed to establish WebSocket connection\");\n\n await writer.close();\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32001,\n message: \"Failed to establish WebSocket connection\",\n },\n id: null,\n });\n return new Response(body, { status: 500 });\n }\n\n // Keep track of the request ids that we have sent to the server\n // so that we can close the connection once we have received\n // all the responses\n const requestIds: Set<string | number> = new Set();\n\n // Accept the WebSocket\n ws.accept();\n\n // Handle messages from the Durable Object\n ws.addEventListener(\"message\", (event) => {\n async function onMessage(event: MessageEvent) {\n try {\n const data =\n typeof event.data === \"string\"\n ? event.data\n : new TextDecoder().decode(event.data);\n const message = JSON.parse(data);\n\n // validate that the message is a valid JSONRPC message\n const result = JSONRPCMessageSchema.safeParse(message);\n if (!result.success) {\n // The message was not a valid JSONRPC message, so we will drop it\n // PartyKit will broadcast state change messages to all connected clients\n // and we need to filter those out so they are not passed to MCP clients\n return;\n }\n\n // If the message is a response or an error, remove the id from the set of\n // request ids\n if (\n isJSONRPCResponse(result.data) ||\n isJSONRPCError(result.data)\n ) {\n requestIds.delete(result.data.id);\n }\n\n // Send the message as an SSE event\n const messageText = `event: message\\ndata: ${JSON.stringify(result.data)}\\n\\n`;\n await writer.write(encoder.encode(messageText));\n\n // If we have received all the responses, close the connection\n if (requestIds.size === 0) {\n ws!.close();\n }\n } catch (error) {\n console.error(\"Error forwarding message to SSE:\", error);\n }\n }\n onMessage(event).catch(console.error);\n });\n\n // Handle WebSocket errors\n ws.addEventListener(\"error\", (error) => {\n async function onError(error: Event) {\n try {\n await writer.close();\n } catch (e) {\n // Ignore errors when closing\n }\n }\n onError(error).catch(console.error);\n });\n\n // Handle WebSocket closure\n ws.addEventListener(\"close\", () => {\n async function onClose() {\n try {\n await writer.close();\n } catch (error) {\n console.error(\"Error closing SSE connection:\", error);\n }\n }\n onClose().catch(console.error);\n });\n\n // If there are no requests, we send the messages to the agent and acknowledge the request with a 202\n // since we don't expect any responses back through this connection\n const hasOnlyNotificationsOrResponses = messages.every(\n (msg) => isJSONRPCNotification(msg) || isJSONRPCResponse(msg)\n );\n if (hasOnlyNotificationsOrResponses) {\n for (const message of messages) {\n ws.send(JSON.stringify(message));\n }\n\n // closing the websocket will also close the SSE connection\n ws.close();\n\n return new Response(null, { status: 202 });\n }\n\n for (const message of messages) {\n if (isJSONRPCRequest(message)) {\n // add each request id that we send off to a set\n // so that we can keep track of which requests we\n // still need a response for\n requestIds.add(message.id);\n }\n ws.send(JSON.stringify(message));\n }\n\n // Return the SSE response. We handle closing the stream in the ws \"message\"\n // handler\n return new Response(readable, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"mcp-session-id\": sessionId,\n \"Access-Control-Allow-Origin\": corsOptions?.origin || \"*\",\n },\n status: 200,\n });\n }\n\n // We don't yet support GET or DELETE requests\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: \"Method not allowed\",\n },\n id: null,\n });\n return new Response(body, { status: 405 });\n },\n };\n }\n}\n"],"mappings":"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","names":["_started","event","error","body"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-1e060d3",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"check:test": "vitest -r src/tests --watch false",
|
|
8
|
+
"test": "vitest -r src/tests",
|
|
8
9
|
"evals": "(cd evals; evalite)",
|
|
9
10
|
"build": "tsx ./scripts/build.ts"
|
|
10
11
|
},
|
|
@@ -77,10 +78,10 @@
|
|
|
77
78
|
"license": "MIT",
|
|
78
79
|
"description": "A home for your AI agents",
|
|
79
80
|
"dependencies": {
|
|
80
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
81
|
+
"@modelcontextprotocol/sdk": "^1.10.2",
|
|
81
82
|
"cron-schedule": "^5.0.4",
|
|
82
83
|
"nanoid": "^5.1.5",
|
|
83
|
-
"partyserver": "^0.0.
|
|
84
|
+
"partyserver": "^0.0.67",
|
|
84
85
|
"partysocket": "1.1.3"
|
|
85
86
|
}
|
|
86
87
|
}
|