@revojs/ai 0.2.26 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -17
- package/dist/index.d.ts +137 -22
- package/dist/index.js +367 -84
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ for (const call of result.content) {
|
|
|
29
29
|
`listTools()` returns AI function-tool definitions. `executeTool()` accepts a
|
|
30
30
|
normalized model tool call and returns a `LanguageModelToolResultPart`, preserving
|
|
31
31
|
the entire tool result as JSON, like the Model Context Protocol client. Use
|
|
32
|
-
`callTool(name, input, { signal })` for the
|
|
32
|
+
`callTool(name, input, { signal })` for the raw `CallToolResult` without
|
|
33
33
|
a model call. Both execution methods validate arguments before running a handler.
|
|
34
34
|
Unknown tools, invalid arguments and unexpected handler failures throw locally;
|
|
35
35
|
HTTP error responses and thrown responses become error results. The HTTP endpoint
|
|
@@ -130,7 +130,7 @@ target when middleware or route parameters are required.
|
|
|
130
130
|
Choose exactly one of `route` and `execute`. Input schemas must describe an object;
|
|
131
131
|
plain JSON Schema and Standard JSON Schema providers are supported. Arguments are
|
|
132
132
|
validated before execution, and tool names must be unique across sources.
|
|
133
|
-
Handlers can return JSON, text, a `Response`, or
|
|
133
|
+
Handlers can return JSON, text, a `Response`, or a `CallToolResult` for custom
|
|
134
134
|
multimodal content. JSON objects from direct handlers also become structured
|
|
135
135
|
content. HTTP error responses and thrown responses become tool errors; unexpected
|
|
136
136
|
exceptions return a generic tool error without disclosing internal details.
|
|
@@ -149,10 +149,9 @@ hosts in your deployment. Model Context Protocol clients without an Origin heade
|
|
|
149
149
|
|
|
150
150
|
### Connect to a server
|
|
151
151
|
|
|
152
|
-
Connect to a Model Context Protocol server using Streamable HTTP. Headers
|
|
153
|
-
|
|
154
|
-
and
|
|
155
|
-
connect to trusted endpoints.
|
|
152
|
+
Connect to a Model Context Protocol server using Streamable HTTP. Headers and a custom
|
|
153
|
+
fetch implementation are supported; OAuth and stdio transports are not. Keep credentials on
|
|
154
|
+
the server and only connect to trusted endpoints.
|
|
156
155
|
|
|
157
156
|
```ts
|
|
158
157
|
import { useAi, useModelContextProtocol } from "@revojs/ai";
|
|
@@ -187,17 +186,14 @@ transport/protocol errors throw. Tool execution is explicit: apply authorization
|
|
|
187
186
|
or approval checks before calling it. The application controls the conversation
|
|
188
187
|
loop, stream consumption, and tool namespacing/routing when combining multiple servers.
|
|
189
188
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Node/Bun application code and pass it as `transport`. The runtime entry itself
|
|
199
|
-
does not import Node process APIs. Always close the connection when finished;
|
|
200
|
-
HTTP sessions are terminated before disconnecting.
|
|
189
|
+
`modelContextProtocol.client` is the underlying `ModelContextProtocolClient`:
|
|
190
|
+
`client.listTools({ cursor })` returns one raw page (including tool annotations),
|
|
191
|
+
`client.callTool({ name, arguments })` the raw result, and `client.request(method, params)`
|
|
192
|
+
sends any other JSON-RPC request such as `resources/list` or `prompts/get`. Request options
|
|
193
|
+
(`{ signal, timeout }`, default timeout 60 seconds) are accepted by `useModelContextProtocol`
|
|
194
|
+
as `requestOptions` and by every method as its last argument; aborted requests send
|
|
195
|
+
`notifications/cancelled`. Always close the connection when finished; HTTP sessions are
|
|
196
|
+
terminated with `DELETE` before disconnecting.
|
|
201
197
|
|
|
202
198
|
## OpenCode Go
|
|
203
199
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import { type StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
3
|
-
import type { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
|
-
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
5
|
-
import { type CallToolResult, type ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
6
1
|
import { type HttpMethod, type Route } from "@revojs/server";
|
|
7
2
|
import { type Scope, type Source, type SourceScope, type SourceValue } from "revojs";
|
|
8
3
|
import { type Awaitable, type JSONObject, type JsonSchema, type JSONValue } from "revojs/shared";
|
|
@@ -11,7 +6,10 @@ export type Tool<T extends JSONObject = JSONObject> = FunctionTool<T> | RouteToo
|
|
|
11
6
|
export type ToolHandler<T extends JSONObject = JSONObject> = (scope: Scope, input: T, options: ToolExecutionOptions) => Awaitable<ToolResult>;
|
|
12
7
|
export type ToolRouteResolver<T extends JSONObject = JSONObject> = (scope: Scope, input: T, options: ToolExecutionOptions) => Awaitable<ToolRequest>;
|
|
13
8
|
export type ToolRoute<T extends JSONObject = JSONObject> = string | ToolRequest | Route | ToolRouteResolver<T>;
|
|
14
|
-
export type
|
|
9
|
+
export type ModelContextProtocolContent = ModelContextProtocolTextContent | ModelContextProtocolImageContent | ModelContextProtocolAudioContent | ModelContextProtocolResourceLink | ModelContextProtocolEmbeddedResource;
|
|
10
|
+
export type ModelContextProtocolFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
11
|
+
export type JsonRpcId = string | number;
|
|
12
|
+
export type JsonRpcMessage = JsonRpcRequest | JsonRpcResponse;
|
|
15
13
|
export type SharedWarning = {
|
|
16
14
|
type: "unsupported" | "compatibility";
|
|
17
15
|
feature: string;
|
|
@@ -72,24 +70,105 @@ export interface ToolRequest {
|
|
|
72
70
|
body?: JSONValue;
|
|
73
71
|
headers?: HeadersInit;
|
|
74
72
|
}
|
|
75
|
-
export interface
|
|
73
|
+
export interface ModelContextProtocolImplementation {
|
|
76
74
|
name: string;
|
|
77
75
|
version: string;
|
|
78
76
|
}
|
|
79
|
-
export interface
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
export interface ModelContextProtocolRequestOptions {
|
|
78
|
+
signal?: AbortSignal;
|
|
79
|
+
timeout?: number;
|
|
82
80
|
}
|
|
83
|
-
export interface
|
|
81
|
+
export interface ModelContextProtocolConnectOptions {
|
|
84
82
|
endpoint: string | URL;
|
|
85
|
-
transport?: "http";
|
|
86
83
|
headers?: Record<string, string>;
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
fetch?: ModelContextProtocolFetch;
|
|
85
|
+
clientInfo?: ModelContextProtocolImplementation;
|
|
86
|
+
requestOptions?: ModelContextProtocolRequestOptions;
|
|
87
|
+
}
|
|
88
|
+
export interface ToolAnnotations {
|
|
89
|
+
title?: string;
|
|
90
|
+
readOnlyHint?: boolean;
|
|
91
|
+
destructiveHint?: boolean;
|
|
92
|
+
idempotentHint?: boolean;
|
|
93
|
+
openWorldHint?: boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface ModelContextProtocolTextContent {
|
|
96
|
+
type: "text";
|
|
97
|
+
text: string;
|
|
98
|
+
}
|
|
99
|
+
export interface ModelContextProtocolImageContent {
|
|
100
|
+
type: "image";
|
|
101
|
+
data: string;
|
|
102
|
+
mimeType: string;
|
|
103
|
+
}
|
|
104
|
+
export interface ModelContextProtocolAudioContent {
|
|
105
|
+
type: "audio";
|
|
106
|
+
data: string;
|
|
107
|
+
mimeType: string;
|
|
108
|
+
}
|
|
109
|
+
export interface ModelContextProtocolResourceLink {
|
|
110
|
+
type: "resource_link";
|
|
111
|
+
uri: string;
|
|
112
|
+
name: string;
|
|
113
|
+
description?: string;
|
|
114
|
+
mimeType?: string;
|
|
115
|
+
}
|
|
116
|
+
export interface ModelContextProtocolResourceContents {
|
|
117
|
+
uri: string;
|
|
118
|
+
mimeType?: string;
|
|
119
|
+
text?: string;
|
|
120
|
+
blob?: string;
|
|
121
|
+
}
|
|
122
|
+
export interface ModelContextProtocolEmbeddedResource {
|
|
123
|
+
type: "resource";
|
|
124
|
+
resource: ModelContextProtocolResourceContents;
|
|
125
|
+
}
|
|
126
|
+
export interface CallToolResult {
|
|
127
|
+
content: Array<ModelContextProtocolContent>;
|
|
128
|
+
structuredContent?: JSONObject;
|
|
129
|
+
isError?: boolean;
|
|
130
|
+
}
|
|
131
|
+
export interface CallToolParams {
|
|
132
|
+
name: string;
|
|
133
|
+
arguments?: JSONObject;
|
|
89
134
|
}
|
|
90
|
-
export interface
|
|
91
|
-
|
|
92
|
-
|
|
135
|
+
export interface ListToolsParams {
|
|
136
|
+
cursor?: string;
|
|
137
|
+
}
|
|
138
|
+
export interface ModelContextProtocolTool {
|
|
139
|
+
name: string;
|
|
140
|
+
title?: string;
|
|
141
|
+
description?: string;
|
|
142
|
+
inputSchema: JSONObject;
|
|
143
|
+
outputSchema?: JSONObject;
|
|
144
|
+
annotations?: ToolAnnotations;
|
|
145
|
+
}
|
|
146
|
+
export interface ListToolsResult {
|
|
147
|
+
tools: Array<ModelContextProtocolTool>;
|
|
148
|
+
nextCursor?: string;
|
|
149
|
+
}
|
|
150
|
+
export interface InitializeResult {
|
|
151
|
+
protocolVersion: string;
|
|
152
|
+
capabilities: JSONObject;
|
|
153
|
+
serverInfo: ModelContextProtocolImplementation;
|
|
154
|
+
instructions?: string;
|
|
155
|
+
}
|
|
156
|
+
export interface JsonRpcRequest {
|
|
157
|
+
jsonrpc: "2.0";
|
|
158
|
+
id?: JsonRpcId;
|
|
159
|
+
method: string;
|
|
160
|
+
params?: JSONObject;
|
|
161
|
+
}
|
|
162
|
+
export interface JsonRpcError {
|
|
163
|
+
code: number;
|
|
164
|
+
message: string;
|
|
165
|
+
data?: JSONValue;
|
|
166
|
+
}
|
|
167
|
+
export interface JsonRpcResponse {
|
|
168
|
+
jsonrpc: "2.0";
|
|
169
|
+
id: JsonRpcId | null;
|
|
170
|
+
result?: unknown;
|
|
171
|
+
error?: JsonRpcError;
|
|
93
172
|
}
|
|
94
173
|
export interface LanguageModelText {
|
|
95
174
|
type: "text";
|
|
@@ -737,12 +816,39 @@ export interface SkillsUploadSkillResult {
|
|
|
737
816
|
export interface Provider {
|
|
738
817
|
setup: (scope: Scope) => Awaitable<ProviderConnection>;
|
|
739
818
|
}
|
|
819
|
+
export declare class ModelContextProtocolError extends Error {
|
|
820
|
+
readonly code: number;
|
|
821
|
+
readonly data?: JSONValue;
|
|
822
|
+
constructor(code: number, message: string, data?: JSONValue);
|
|
823
|
+
}
|
|
824
|
+
export declare class ModelContextProtocolClient {
|
|
825
|
+
readonly endpoint: URL;
|
|
826
|
+
sessionId?: string;
|
|
827
|
+
protocolVersion?: string;
|
|
828
|
+
serverInfo?: ModelContextProtocolImplementation;
|
|
829
|
+
capabilities?: JSONObject;
|
|
830
|
+
instructions?: string;
|
|
831
|
+
private readonly headers;
|
|
832
|
+
private readonly fetch;
|
|
833
|
+
private readonly clientInfo;
|
|
834
|
+
private readonly requestOptions;
|
|
835
|
+
private requests;
|
|
836
|
+
constructor(options: ModelContextProtocolConnectOptions);
|
|
837
|
+
connect(options?: ModelContextProtocolRequestOptions): Promise<void>;
|
|
838
|
+
listTools(params?: ListToolsParams, options?: ModelContextProtocolRequestOptions): Promise<ListToolsResult>;
|
|
839
|
+
callTool(params: CallToolParams, options?: ModelContextProtocolRequestOptions): Promise<CallToolResult>;
|
|
840
|
+
request<T>(method: string, params?: JSONObject, options?: ModelContextProtocolRequestOptions): Promise<T>;
|
|
841
|
+
notify(method: string, params?: JSONObject): Promise<void>;
|
|
842
|
+
close(): Promise<void>;
|
|
843
|
+
private send;
|
|
844
|
+
private receive;
|
|
845
|
+
private answer;
|
|
846
|
+
}
|
|
740
847
|
export declare class ModelContextProtocolConnection {
|
|
741
|
-
readonly client:
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
executeTool(call: LanguageModelToolCall, options?: RequestOptions): Promise<LanguageModelToolResultPart>;
|
|
848
|
+
readonly client: ModelContextProtocolClient;
|
|
849
|
+
constructor(client: ModelContextProtocolClient);
|
|
850
|
+
listTools(options?: ModelContextProtocolRequestOptions): Promise<Array<LanguageModelFunctionTool>>;
|
|
851
|
+
executeTool(call: LanguageModelToolCall, options?: ModelContextProtocolRequestOptions): Promise<LanguageModelToolResultPart>;
|
|
746
852
|
close(): Promise<void>;
|
|
747
853
|
}
|
|
748
854
|
export declare function defineTool<T extends JSONObject = JSONObject>(tool: Tool<T>): Tool<T>;
|
|
@@ -769,3 +875,12 @@ export declare function postJson(url: string, body: unknown, options?: {
|
|
|
769
875
|
}): Promise<Response>;
|
|
770
876
|
export declare function readDataFrames(response: Response): AsyncGenerator<string>;
|
|
771
877
|
export declare function toStream<T>(read: () => AsyncGenerator<T>): ReadableStream<T>;
|
|
878
|
+
export declare const MODEL_CONTEXT_PROTOCOL_VERSIONS: string[];
|
|
879
|
+
export declare const MODEL_CONTEXT_PROTOCOL_TIMEOUT = 60000;
|
|
880
|
+
export declare const ModelContextProtocolErrorCode: {
|
|
881
|
+
readonly ParseError: -32700;
|
|
882
|
+
readonly InvalidRequest: -32600;
|
|
883
|
+
readonly MethodNotFound: -32601;
|
|
884
|
+
readonly InvalidParams: -32602;
|
|
885
|
+
readonly InternalError: -32603;
|
|
886
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,181 @@
|
|
|
1
1
|
import config from "#config/ai";
|
|
2
2
|
import providers from "#providers";
|
|
3
3
|
import tools from "#tools";
|
|
4
|
-
import {
|
|
5
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
-
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
8
|
-
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
-
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
|
|
4
|
+
import { Validator } from "@cfworker/json-schema";
|
|
10
5
|
import { SERVER_CONTEXT, createServerScope, isResponse, requireSameOrigin, useServer } from "@revojs/server";
|
|
11
6
|
import { useVariables } from "revojs";
|
|
12
7
|
import { toJsonSchema } from "revojs/shared";
|
|
13
8
|
//#region src/index.ts
|
|
9
|
+
var ModelContextProtocolError = class extends Error {
|
|
10
|
+
code;
|
|
11
|
+
data;
|
|
12
|
+
constructor(code, message, data) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "ModelContextProtocolError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.data = data;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var ModelContextProtocolClient = class {
|
|
20
|
+
endpoint;
|
|
21
|
+
sessionId;
|
|
22
|
+
protocolVersion;
|
|
23
|
+
serverInfo;
|
|
24
|
+
capabilities;
|
|
25
|
+
instructions;
|
|
26
|
+
headers;
|
|
27
|
+
fetch;
|
|
28
|
+
clientInfo;
|
|
29
|
+
requestOptions;
|
|
30
|
+
requests = 0;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.endpoint = new URL(options.endpoint);
|
|
33
|
+
this.headers = options.headers ?? {};
|
|
34
|
+
this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
35
|
+
this.clientInfo = options.clientInfo ?? {
|
|
36
|
+
name: "revojs",
|
|
37
|
+
version: "1.0.0"
|
|
38
|
+
};
|
|
39
|
+
this.requestOptions = options.requestOptions ?? {};
|
|
40
|
+
}
|
|
41
|
+
async connect(options) {
|
|
42
|
+
const result = await this.request("initialize", {
|
|
43
|
+
protocolVersion: MODEL_CONTEXT_PROTOCOL_VERSIONS[0],
|
|
44
|
+
capabilities: {},
|
|
45
|
+
clientInfo: {
|
|
46
|
+
name: this.clientInfo.name,
|
|
47
|
+
version: this.clientInfo.version
|
|
48
|
+
}
|
|
49
|
+
}, options);
|
|
50
|
+
if (!MODEL_CONTEXT_PROTOCOL_VERSIONS.includes(result.protocolVersion)) throw new Error(`Unsupported Model Context Protocol version "${result.protocolVersion}".`);
|
|
51
|
+
this.protocolVersion = result.protocolVersion;
|
|
52
|
+
this.serverInfo = result.serverInfo;
|
|
53
|
+
this.capabilities = result.capabilities;
|
|
54
|
+
this.instructions = result.instructions;
|
|
55
|
+
await this.notify("notifications/initialized");
|
|
56
|
+
}
|
|
57
|
+
async listTools(params = {}, options) {
|
|
58
|
+
const request = {};
|
|
59
|
+
if (params.cursor !== void 0) request.cursor = params.cursor;
|
|
60
|
+
return await this.request("tools/list", request, options);
|
|
61
|
+
}
|
|
62
|
+
async callTool(params, options) {
|
|
63
|
+
return await this.request("tools/call", {
|
|
64
|
+
name: params.name,
|
|
65
|
+
arguments: params.arguments ?? {}
|
|
66
|
+
}, options);
|
|
67
|
+
}
|
|
68
|
+
async request(method, params = {}, options = this.requestOptions) {
|
|
69
|
+
this.requests += 1;
|
|
70
|
+
const id = this.requests;
|
|
71
|
+
const signals = [AbortSignal.timeout(options.timeout ?? 6e4)];
|
|
72
|
+
if (options.signal) signals.push(options.signal);
|
|
73
|
+
const signal = AbortSignal.any(signals);
|
|
74
|
+
try {
|
|
75
|
+
const response = await this.send({
|
|
76
|
+
jsonrpc: "2.0",
|
|
77
|
+
id,
|
|
78
|
+
method,
|
|
79
|
+
params
|
|
80
|
+
}, signal);
|
|
81
|
+
const message = await this.receive(response, id);
|
|
82
|
+
if (message.error) throw new ModelContextProtocolError(message.error.code, message.error.message, message.error.data);
|
|
83
|
+
return message.result;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (signal.aborted && this.protocolVersion !== void 0) (async () => {
|
|
86
|
+
try {
|
|
87
|
+
await this.notify("notifications/cancelled", {
|
|
88
|
+
requestId: id,
|
|
89
|
+
reason: "Request aborted"
|
|
90
|
+
});
|
|
91
|
+
} catch {}
|
|
92
|
+
})();
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async notify(method, params) {
|
|
97
|
+
const notification = {
|
|
98
|
+
jsonrpc: "2.0",
|
|
99
|
+
method
|
|
100
|
+
};
|
|
101
|
+
if (params !== void 0) notification.params = params;
|
|
102
|
+
await (await this.send(notification)).body?.cancel();
|
|
103
|
+
}
|
|
104
|
+
async close() {
|
|
105
|
+
const sessionId = this.sessionId;
|
|
106
|
+
this.sessionId = void 0;
|
|
107
|
+
if (sessionId === void 0) return;
|
|
108
|
+
const response = await this.fetch(this.endpoint, {
|
|
109
|
+
method: "DELETE",
|
|
110
|
+
headers: {
|
|
111
|
+
...this.headers,
|
|
112
|
+
"mcp-session-id": sessionId
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
await response.body?.cancel();
|
|
116
|
+
if (!response.ok && response.status !== 405) throw new Error(`Model Context Protocol session termination failed with HTTP ${response.status}.`);
|
|
117
|
+
}
|
|
118
|
+
async send(message, signal) {
|
|
119
|
+
const headers = new Headers(this.headers);
|
|
120
|
+
headers.set("content-type", "application/json");
|
|
121
|
+
headers.set("accept", "application/json, text/event-stream");
|
|
122
|
+
if (this.sessionId !== void 0) headers.set("mcp-session-id", this.sessionId);
|
|
123
|
+
if (this.protocolVersion !== void 0) headers.set("mcp-protocol-version", this.protocolVersion);
|
|
124
|
+
const response = await this.fetch(this.endpoint, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers,
|
|
127
|
+
body: JSON.stringify(message),
|
|
128
|
+
signal
|
|
129
|
+
});
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
await response.body?.cancel();
|
|
132
|
+
throw new Error(`Model Context Protocol request failed with HTTP ${response.status}.`);
|
|
133
|
+
}
|
|
134
|
+
const sessionId = response.headers.get("mcp-session-id");
|
|
135
|
+
if (sessionId !== null) this.sessionId = sessionId;
|
|
136
|
+
return response;
|
|
137
|
+
}
|
|
138
|
+
async receive(response, id) {
|
|
139
|
+
if (response.headers.get("content-type")?.includes("text/event-stream")) {
|
|
140
|
+
for await (const data of readDataFrames(response)) {
|
|
141
|
+
if (data === "") continue;
|
|
142
|
+
const message = JSON.parse(data);
|
|
143
|
+
if ("method" in message) {
|
|
144
|
+
await this.answer(message);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (message.id === id) return message;
|
|
148
|
+
}
|
|
149
|
+
throw new Error("Model Context Protocol stream ended without a response.");
|
|
150
|
+
}
|
|
151
|
+
const body = await response.json();
|
|
152
|
+
const messages = new Array();
|
|
153
|
+
if (Array.isArray(body)) for (const message of body) messages.push(message);
|
|
154
|
+
else messages.push(body);
|
|
155
|
+
for (const message of messages) {
|
|
156
|
+
if ("method" in message) continue;
|
|
157
|
+
if (message.id === id) return message;
|
|
158
|
+
}
|
|
159
|
+
throw new Error("Model Context Protocol response does not contain the requested result.");
|
|
160
|
+
}
|
|
161
|
+
async answer(request) {
|
|
162
|
+
if (request.id === void 0) return;
|
|
163
|
+
const reply = {
|
|
164
|
+
jsonrpc: "2.0",
|
|
165
|
+
id: request.id
|
|
166
|
+
};
|
|
167
|
+
if (request.method === "ping") reply.result = {};
|
|
168
|
+
else reply.error = {
|
|
169
|
+
code: ModelContextProtocolErrorCode.MethodNotFound,
|
|
170
|
+
message: `Method "${request.method}" is not supported by this client.`
|
|
171
|
+
};
|
|
172
|
+
await (await this.send(reply)).body?.cancel();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
14
175
|
var ModelContextProtocolConnection = class {
|
|
15
176
|
client;
|
|
16
|
-
|
|
17
|
-
constructor(client, transport) {
|
|
177
|
+
constructor(client) {
|
|
18
178
|
this.client = client;
|
|
19
|
-
this.transport = transport;
|
|
20
179
|
}
|
|
21
180
|
async listTools(options) {
|
|
22
181
|
const tools = new Array();
|
|
@@ -42,7 +201,7 @@ var ModelContextProtocolConnection = class {
|
|
|
42
201
|
const result = await this.client.callTool({
|
|
43
202
|
name: call.toolName,
|
|
44
203
|
arguments: input
|
|
45
|
-
},
|
|
204
|
+
}, options);
|
|
46
205
|
return {
|
|
47
206
|
type: "tool-result",
|
|
48
207
|
toolCallId: call.toolCallId,
|
|
@@ -54,11 +213,7 @@ var ModelContextProtocolConnection = class {
|
|
|
54
213
|
};
|
|
55
214
|
}
|
|
56
215
|
async close() {
|
|
57
|
-
|
|
58
|
-
if (this.transport instanceof StreamableHTTPClientTransport && this.transport.sessionId !== void 0) await this.transport.terminateSession();
|
|
59
|
-
} finally {
|
|
60
|
-
await this.client.close();
|
|
61
|
-
}
|
|
216
|
+
await this.client.close();
|
|
62
217
|
}
|
|
63
218
|
};
|
|
64
219
|
function defineTool(tool) {
|
|
@@ -73,13 +228,14 @@ async function toToolResult(output) {
|
|
|
73
228
|
isError: !output.ok
|
|
74
229
|
};
|
|
75
230
|
if (output !== null && typeof output === "object" && "content" in output && Array.isArray(output.content)) return output;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
231
|
+
let text = JSON.stringify(output ?? null);
|
|
232
|
+
if (typeof output === "string") text = output;
|
|
233
|
+
const result = { content: [{
|
|
234
|
+
type: "text",
|
|
235
|
+
text
|
|
236
|
+
}] };
|
|
237
|
+
if (output !== null && typeof output === "object" && !Array.isArray(output)) result.structuredContent = output;
|
|
238
|
+
return result;
|
|
83
239
|
}
|
|
84
240
|
function useTools(scope) {
|
|
85
241
|
let loading;
|
|
@@ -92,17 +248,18 @@ function useTools(scope) {
|
|
|
92
248
|
}
|
|
93
249
|
const definitions = await loading;
|
|
94
250
|
if (registered) return registered;
|
|
95
|
-
const validator = new CfWorkerJsonSchemaValidator();
|
|
96
251
|
const entries = /* @__PURE__ */ new Map();
|
|
97
252
|
for (const tool of definitions) {
|
|
98
253
|
if (!/^[A-Za-z0-9_.-]{1,128}$/.test(tool.name)) throw new Error(`Invalid Model Context Protocol tool name "${tool.name}".`);
|
|
99
254
|
if (entries.has(tool.name)) throw new Error("Model Context Protocol tool names must be unique across all tool sources.");
|
|
100
255
|
const schema = toJsonSchema(tool.inputSchema);
|
|
101
256
|
if (schema.type !== "object") throw new Error(`Model Context Protocol tool "${tool.name}" must have an object input schema.`);
|
|
257
|
+
let draft = "2020-12";
|
|
258
|
+
if (typeof schema.$schema === "string" && JSON_SCHEMA_DRAFTS[schema.$schema]) draft = JSON_SCHEMA_DRAFTS[schema.$schema];
|
|
102
259
|
entries.set(tool.name, {
|
|
103
260
|
tool,
|
|
104
261
|
schema,
|
|
105
|
-
|
|
262
|
+
validator: new Validator(schema, draft, true)
|
|
106
263
|
});
|
|
107
264
|
}
|
|
108
265
|
registered = entries;
|
|
@@ -111,14 +268,20 @@ function useTools(scope) {
|
|
|
111
268
|
const callTool = async (name, input = {}, options = {}) => {
|
|
112
269
|
options.signal?.throwIfAborted();
|
|
113
270
|
const entry = (await resolveTools()).get(name);
|
|
114
|
-
if (!entry) throw new
|
|
115
|
-
const
|
|
116
|
-
if (!
|
|
271
|
+
if (!entry) throw new ModelContextProtocolError(ModelContextProtocolErrorCode.InvalidParams, `Unknown tool "${name}".`);
|
|
272
|
+
const validation = entry.validator.validate(input);
|
|
273
|
+
if (!validation.valid) {
|
|
274
|
+
const errors = new Array();
|
|
275
|
+
for (const error of validation.errors) errors.push(`${error.instanceLocation}: ${error.error}`);
|
|
276
|
+
let message = "Invalid tool arguments.";
|
|
277
|
+
if (errors.length > 0) message = errors.join("; ");
|
|
278
|
+
throw new ModelContextProtocolError(ModelContextProtocolErrorCode.InvalidParams, message);
|
|
279
|
+
}
|
|
117
280
|
options.signal?.throwIfAborted();
|
|
118
281
|
try {
|
|
119
282
|
const { tool } = entry;
|
|
120
283
|
if (tool.execute) {
|
|
121
|
-
const output = await tool.execute(scope,
|
|
284
|
+
const output = await tool.execute(scope, input, options);
|
|
122
285
|
options.signal?.throwIfAborted();
|
|
123
286
|
return await toToolResult(output);
|
|
124
287
|
}
|
|
@@ -127,7 +290,7 @@ function useTools(scope) {
|
|
|
127
290
|
const signal = options.signal ? AbortSignal.any([request.signal, options.signal]) : request.signal;
|
|
128
291
|
signal.throwIfAborted();
|
|
129
292
|
let target = tool.route;
|
|
130
|
-
if (typeof target === "function") target = await target(scope,
|
|
293
|
+
if (typeof target === "function") target = await target(scope, input, { signal });
|
|
131
294
|
let direct;
|
|
132
295
|
let targetOptions;
|
|
133
296
|
if (typeof target === "string") targetOptions = { path: target };
|
|
@@ -140,8 +303,15 @@ function useTools(scope) {
|
|
|
140
303
|
if (!direct && (url.pathname === "/v1/model-context-protocol" || url.pathname === "/v1/model-context-protocol/")) throw new Error("A tool cannot call the Model Context Protocol endpoint.");
|
|
141
304
|
const method = targetOptions.method ?? "POST";
|
|
142
305
|
const readOnly = method === "GET" || method === "HEAD";
|
|
143
|
-
|
|
144
|
-
|
|
306
|
+
let query = targetOptions.query ?? {};
|
|
307
|
+
if (targetOptions.query === void 0 && readOnly) query = input;
|
|
308
|
+
let parameters = Object.entries(query);
|
|
309
|
+
if (query instanceof URLSearchParams) parameters = query;
|
|
310
|
+
for (const [name, value] of parameters) {
|
|
311
|
+
if (value === void 0) continue;
|
|
312
|
+
if (typeof value === "string") url.searchParams.append(name, value);
|
|
313
|
+
else url.searchParams.append(name, JSON.stringify(value));
|
|
314
|
+
}
|
|
145
315
|
const headers = new Headers(targetOptions.headers);
|
|
146
316
|
for (const name of [
|
|
147
317
|
"authorization",
|
|
@@ -155,7 +325,9 @@ function useTools(scope) {
|
|
|
155
325
|
let body;
|
|
156
326
|
if (!readOnly) {
|
|
157
327
|
headers.set("content-type", "application/json");
|
|
158
|
-
|
|
328
|
+
let payload = input;
|
|
329
|
+
if ("body" in targetOptions) payload = targetOptions.body;
|
|
330
|
+
body = JSON.stringify(payload);
|
|
159
331
|
}
|
|
160
332
|
const nextRequest = new Request(url, {
|
|
161
333
|
method,
|
|
@@ -192,7 +364,7 @@ function useTools(scope) {
|
|
|
192
364
|
callTool,
|
|
193
365
|
async executeTool(call, options) {
|
|
194
366
|
const input = JSON.parse(call.input);
|
|
195
|
-
if (input === null || typeof input !== "object" || Array.isArray(input)) throw new
|
|
367
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) throw new ModelContextProtocolError(ModelContextProtocolErrorCode.InvalidParams, `Tool "${call.toolName}" requires a JSON object as input.`);
|
|
196
368
|
const result = await callTool(call.toolName, input, options);
|
|
197
369
|
return {
|
|
198
370
|
type: "tool-result",
|
|
@@ -213,67 +385,149 @@ async function handleModelContextProtocol(scope, options = {}) {
|
|
|
213
385
|
status: 405,
|
|
214
386
|
headers: { Allow: "POST" }
|
|
215
387
|
});
|
|
388
|
+
const version = request.headers.get("mcp-protocol-version");
|
|
389
|
+
if (version !== null && !MODEL_CONTEXT_PROTOCOL_VERSIONS.includes(version)) return Response.json({
|
|
390
|
+
jsonrpc: "2.0",
|
|
391
|
+
id: null,
|
|
392
|
+
error: {
|
|
393
|
+
code: ModelContextProtocolErrorCode.InvalidRequest,
|
|
394
|
+
message: `Unsupported Model Context Protocol version "${version}".`
|
|
395
|
+
}
|
|
396
|
+
}, { status: 400 });
|
|
397
|
+
let body;
|
|
398
|
+
try {
|
|
399
|
+
body = await request.json();
|
|
400
|
+
} catch {
|
|
401
|
+
return Response.json({
|
|
402
|
+
jsonrpc: "2.0",
|
|
403
|
+
id: null,
|
|
404
|
+
error: {
|
|
405
|
+
code: ModelContextProtocolErrorCode.ParseError,
|
|
406
|
+
message: "Parse error."
|
|
407
|
+
}
|
|
408
|
+
}, { status: 400 });
|
|
409
|
+
}
|
|
216
410
|
const collection = useTools(scope);
|
|
217
|
-
const server = new McpServer({
|
|
218
|
-
name: config.modelContextProtocol.name,
|
|
219
|
-
version: config.modelContextProtocol.version
|
|
220
|
-
}, {
|
|
221
|
-
capabilities: { tools: {} },
|
|
222
|
-
instructions: config.modelContextProtocol.instructions,
|
|
223
|
-
jsonSchemaValidator: new CfWorkerJsonSchemaValidator()
|
|
224
|
-
});
|
|
225
|
-
const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });
|
|
226
411
|
const listTools = async () => {
|
|
227
|
-
const
|
|
228
|
-
|
|
412
|
+
const visible = new Array();
|
|
413
|
+
for (const tool of await collection.listTools()) if (!options.filter || options.filter(tool)) visible.push(tool);
|
|
414
|
+
return visible;
|
|
229
415
|
};
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
416
|
+
const batch = Array.isArray(body);
|
|
417
|
+
const entries = new Array();
|
|
418
|
+
if (Array.isArray(body)) for (const entry of body) entries.push(entry);
|
|
419
|
+
else entries.push(body);
|
|
420
|
+
const responses = new Array();
|
|
421
|
+
for (const entry of entries) {
|
|
422
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
|
|
423
|
+
responses.push({
|
|
424
|
+
jsonrpc: "2.0",
|
|
425
|
+
id: null,
|
|
426
|
+
error: {
|
|
427
|
+
code: ModelContextProtocolErrorCode.InvalidRequest,
|
|
428
|
+
message: "Invalid request."
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
const message = entry;
|
|
434
|
+
if (typeof message.method !== "string" || message.id === void 0) continue;
|
|
435
|
+
const params = message.params ?? {};
|
|
238
436
|
try {
|
|
239
|
-
|
|
437
|
+
let result;
|
|
438
|
+
switch (message.method) {
|
|
439
|
+
case "initialize": {
|
|
440
|
+
const requested = params.protocolVersion;
|
|
441
|
+
let protocolVersion = MODEL_CONTEXT_PROTOCOL_VERSIONS[0];
|
|
442
|
+
if (typeof requested === "string" && MODEL_CONTEXT_PROTOCOL_VERSIONS.includes(requested)) protocolVersion = requested;
|
|
443
|
+
result = {
|
|
444
|
+
protocolVersion,
|
|
445
|
+
capabilities: { tools: {} },
|
|
446
|
+
serverInfo: {
|
|
447
|
+
name: config.modelContextProtocol.name,
|
|
448
|
+
version: config.modelContextProtocol.version
|
|
449
|
+
},
|
|
450
|
+
instructions: config.modelContextProtocol.instructions
|
|
451
|
+
};
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
case "ping":
|
|
455
|
+
result = {};
|
|
456
|
+
break;
|
|
457
|
+
case "tools/list": {
|
|
458
|
+
const tools = new Array();
|
|
459
|
+
for (const tool of await listTools()) tools.push({
|
|
460
|
+
name: tool.name,
|
|
461
|
+
description: tool.description,
|
|
462
|
+
inputSchema: tool.inputSchema,
|
|
463
|
+
annotations: tool.annotations
|
|
464
|
+
});
|
|
465
|
+
result = { tools };
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case "tools/call": {
|
|
469
|
+
const name = params.name;
|
|
470
|
+
const input = params.arguments ?? {};
|
|
471
|
+
if (typeof name !== "string" || input === null || typeof input !== "object" || Array.isArray(input)) throw new ModelContextProtocolError(ModelContextProtocolErrorCode.InvalidParams, "Tool calls require a name and object arguments.");
|
|
472
|
+
if (options.filter) {
|
|
473
|
+
let visible = false;
|
|
474
|
+
for (const tool of await listTools()) if (tool.name === name) {
|
|
475
|
+
visible = true;
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
if (!visible) throw new ModelContextProtocolError(ModelContextProtocolErrorCode.InvalidParams, `Unknown tool "${name}".`);
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
result = await collection.callTool(name, input, { signal: request.signal });
|
|
482
|
+
} catch (error) {
|
|
483
|
+
if (error instanceof ModelContextProtocolError) throw error;
|
|
484
|
+
result = {
|
|
485
|
+
content: [{
|
|
486
|
+
type: "text",
|
|
487
|
+
text: "Tool execution failed."
|
|
488
|
+
}],
|
|
489
|
+
isError: true
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
default: throw new ModelContextProtocolError(ModelContextProtocolErrorCode.MethodNotFound, `Method "${message.method}" not found.`);
|
|
495
|
+
}
|
|
496
|
+
responses.push({
|
|
497
|
+
jsonrpc: "2.0",
|
|
498
|
+
id: message.id,
|
|
499
|
+
result
|
|
500
|
+
});
|
|
240
501
|
} catch (error) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
502
|
+
let failure = {
|
|
503
|
+
code: ModelContextProtocolErrorCode.InternalError,
|
|
504
|
+
message: "Internal error."
|
|
505
|
+
};
|
|
506
|
+
if (error instanceof ModelContextProtocolError) failure = {
|
|
507
|
+
code: error.code,
|
|
508
|
+
message: error.message,
|
|
509
|
+
data: error.data
|
|
248
510
|
};
|
|
511
|
+
responses.push({
|
|
512
|
+
jsonrpc: "2.0",
|
|
513
|
+
id: message.id,
|
|
514
|
+
error: failure
|
|
515
|
+
});
|
|
249
516
|
}
|
|
250
|
-
});
|
|
251
|
-
try {
|
|
252
|
-
await server.connect(transport);
|
|
253
|
-
return await transport.handleRequest(request);
|
|
254
|
-
} finally {
|
|
255
|
-
await server.close();
|
|
256
517
|
}
|
|
518
|
+
if (responses.length === 0) return new Response(null, { status: 202 });
|
|
519
|
+
if (batch) return Response.json(responses);
|
|
520
|
+
return Response.json(responses[0]);
|
|
257
521
|
}
|
|
258
522
|
async function useModelContextProtocol(options) {
|
|
259
|
-
const client = new
|
|
260
|
-
name: "revojs",
|
|
261
|
-
version: "1.0.0"
|
|
262
|
-
});
|
|
263
|
-
let transport = options.transport;
|
|
264
|
-
if (typeof transport !== "object") {
|
|
265
|
-
if (options.endpoint === void 0) throw new Error("Model Context Protocol requires an endpoint or a custom transport.");
|
|
266
|
-
transport = new StreamableHTTPClientTransport(new URL(options.endpoint), {
|
|
267
|
-
authProvider: options.authProvider,
|
|
268
|
-
fetch: options.fetch,
|
|
269
|
-
requestInit: { headers: options.headers }
|
|
270
|
-
});
|
|
271
|
-
}
|
|
523
|
+
const client = new ModelContextProtocolClient(options);
|
|
272
524
|
try {
|
|
273
|
-
await client.connect(
|
|
274
|
-
return new ModelContextProtocolConnection(client
|
|
525
|
+
await client.connect();
|
|
526
|
+
return new ModelContextProtocolConnection(client);
|
|
275
527
|
} catch (error) {
|
|
276
|
-
|
|
528
|
+
try {
|
|
529
|
+
await client.close();
|
|
530
|
+
} catch {}
|
|
277
531
|
throw error;
|
|
278
532
|
}
|
|
279
533
|
}
|
|
@@ -398,8 +652,15 @@ async function* readDataFrames(response) {
|
|
|
398
652
|
buffer += decoder.decode(chunk, { stream: true });
|
|
399
653
|
const frames = buffer.split(/\r?\n\r?\n/);
|
|
400
654
|
buffer = frames.pop() ?? "";
|
|
401
|
-
for (const frame of frames)
|
|
655
|
+
for (const frame of frames) {
|
|
656
|
+
const lines = new Array();
|
|
657
|
+
for (const line of frame.split(/\r?\n/)) if (line.startsWith("data:")) lines.push(line.slice(5).trim());
|
|
658
|
+
if (lines.length > 0) yield lines.join("\n");
|
|
659
|
+
}
|
|
402
660
|
}
|
|
661
|
+
const lines = new Array();
|
|
662
|
+
for (const line of buffer.split(/\r?\n/)) if (line.startsWith("data:")) lines.push(line.slice(5).trim());
|
|
663
|
+
if (lines.length > 0) yield lines.join("\n");
|
|
403
664
|
}
|
|
404
665
|
function toStream(read) {
|
|
405
666
|
const iterator = read();
|
|
@@ -423,5 +684,27 @@ function toStream(read) {
|
|
|
423
684
|
});
|
|
424
685
|
}
|
|
425
686
|
var modelsDevCache = { loadedAt: 0 };
|
|
687
|
+
var MODEL_CONTEXT_PROTOCOL_VERSIONS = [
|
|
688
|
+
"2025-11-25",
|
|
689
|
+
"2025-06-18",
|
|
690
|
+
"2025-03-26",
|
|
691
|
+
"2024-11-05"
|
|
692
|
+
];
|
|
693
|
+
var MODEL_CONTEXT_PROTOCOL_TIMEOUT = 6e4;
|
|
694
|
+
var ModelContextProtocolErrorCode = {
|
|
695
|
+
ParseError: -32700,
|
|
696
|
+
InvalidRequest: -32600,
|
|
697
|
+
MethodNotFound: -32601,
|
|
698
|
+
InvalidParams: -32602,
|
|
699
|
+
InternalError: -32603
|
|
700
|
+
};
|
|
701
|
+
var JSON_SCHEMA_DRAFTS = {
|
|
702
|
+
"https://json-schema.org/draft/2020-12/schema": "2020-12",
|
|
703
|
+
"https://json-schema.org/draft/2019-09/schema": "2019-09",
|
|
704
|
+
"http://json-schema.org/draft-07/schema": "7",
|
|
705
|
+
"http://json-schema.org/draft-07/schema#": "7",
|
|
706
|
+
"http://json-schema.org/draft-04/schema": "4",
|
|
707
|
+
"http://json-schema.org/draft-04/schema#": "4"
|
|
708
|
+
};
|
|
426
709
|
//#endregion
|
|
427
|
-
export { ModelContextProtocolConnection, defineProvider, defineTool, handleModelContextProtocol, mergeHeaders, modelsDevProtocol, postJson, readDataFrames, toStream, toToolInput, useAi, useModelContextProtocol, useModelsDev, useTools };
|
|
710
|
+
export { MODEL_CONTEXT_PROTOCOL_TIMEOUT, MODEL_CONTEXT_PROTOCOL_VERSIONS, ModelContextProtocolClient, ModelContextProtocolConnection, ModelContextProtocolError, ModelContextProtocolErrorCode, defineProvider, defineTool, handleModelContextProtocol, mergeHeaders, modelsDevProtocol, postJson, readDataFrames, toStream, toToolInput, useAi, useModelContextProtocol, useModelsDev, useTools };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revojs/ai",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@cfworker/json-schema": "^4.1.1",
|
|
33
|
-
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
34
33
|
"@revojs/server": "*",
|
|
35
34
|
"revojs": "*"
|
|
36
35
|
},
|