@webda/mcp 0.0.0-stage → 4.0.0-beta.3
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/LICENSE +166 -0
- package/README.md +168 -2
- package/lib/auth.d.ts +27 -0
- package/lib/auth.d.ts.map +1 -0
- package/lib/auth.js +14 -0
- package/lib/auth.js.map +1 -0
- package/lib/bridge.d.ts +26 -0
- package/lib/bridge.d.ts.map +1 -0
- package/lib/bridge.js +72 -0
- package/lib/bridge.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +10 -0
- package/lib/index.js.map +1 -0
- package/lib/invoke.d.ts +108 -0
- package/lib/invoke.d.ts.map +1 -0
- package/lib/invoke.js +177 -0
- package/lib/invoke.js.map +1 -0
- package/lib/mcpservice.service.d.ts +194 -0
- package/lib/mcpservice.service.d.ts.map +1 -0
- package/lib/mcpservice.service.js +463 -0
- package/lib/mcpservice.service.js.map +1 -0
- package/lib/resources.d.ts +86 -0
- package/lib/resources.d.ts.map +1 -0
- package/lib/resources.js +125 -0
- package/lib/resources.js.map +1 -0
- package/lib/schema.d.ts +23 -0
- package/lib/schema.d.ts.map +1 -0
- package/lib/schema.js +69 -0
- package/lib/schema.js.map +1 -0
- package/lib/server.d.ts +54 -0
- package/lib/server.d.ts.map +1 -0
- package/lib/server.js +167 -0
- package/lib/server.js.map +1 -0
- package/lib/sessions.d.ts +78 -0
- package/lib/sessions.d.ts.map +1 -0
- package/lib/sessions.js +99 -0
- package/lib/sessions.js.map +1 -0
- package/lib/tools.d.ts +62 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +126 -0
- package/lib/tools.js.map +1 -0
- package/package.json +47 -3
- package/webda.module.json +211 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { Command, OperationsTransport, OperationsTransportParameters, Session, useApplication, useService } from "@webda/core";
|
|
36
|
+
import { AsyncResource } from "node:async_hooks";
|
|
37
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
38
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
39
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
40
|
+
import { useLog } from "@webda/workout";
|
|
41
|
+
import { randomUUID } from "node:crypto";
|
|
42
|
+
import { Writable } from "node:stream";
|
|
43
|
+
import { SessionAuthenticator } from "./auth.js";
|
|
44
|
+
import { jsonRpcError, toRequest, writeResponse } from "./bridge.js";
|
|
45
|
+
import { ResourceRegistry } from "./resources.js";
|
|
46
|
+
import { createMcpServer } from "./server.js";
|
|
47
|
+
import { McpSessionManager } from "./sessions.js";
|
|
48
|
+
import { operationsFingerprint, ToolRegistry } from "./tools.js";
|
|
49
|
+
/**
|
|
50
|
+
* Parameters for the MCP transport
|
|
51
|
+
*/
|
|
52
|
+
export class McpServiceParameters extends OperationsTransportParameters {
|
|
53
|
+
/**
|
|
54
|
+
* HTTP endpoint; false disables HTTP (stdio only)
|
|
55
|
+
* @default "/mcp"
|
|
56
|
+
*/
|
|
57
|
+
url;
|
|
58
|
+
/**
|
|
59
|
+
* Models exposed as resources (short ids or "*"); false disables resources
|
|
60
|
+
* @default {"models":["*"]}
|
|
61
|
+
*/
|
|
62
|
+
resources;
|
|
63
|
+
/**
|
|
64
|
+
* Seconds of inactivity before an MCP session is evicted
|
|
65
|
+
* @default 1800
|
|
66
|
+
*/
|
|
67
|
+
sessionTimeout;
|
|
68
|
+
/**
|
|
69
|
+
* Extra Origin values accepted besides the request's own origin
|
|
70
|
+
* @default []
|
|
71
|
+
*/
|
|
72
|
+
allowedOrigins;
|
|
73
|
+
/**
|
|
74
|
+
* Hostnames (any port) accepted in the request Host, against DNS rebinding;
|
|
75
|
+
* "*" disables the check
|
|
76
|
+
* @default ["localhost","127.0.0.1","::1"]
|
|
77
|
+
*/
|
|
78
|
+
allowedHosts;
|
|
79
|
+
/**
|
|
80
|
+
* Maximum number of concurrent MCP HTTP sessions; at the limit the least
|
|
81
|
+
* recently used session is closed to make room for a new one
|
|
82
|
+
* @default 1000
|
|
83
|
+
*/
|
|
84
|
+
maxSessions;
|
|
85
|
+
/**
|
|
86
|
+
* Larger tool/resource outputs are truncated
|
|
87
|
+
* @default 1048576
|
|
88
|
+
*/
|
|
89
|
+
maxOutputBytes;
|
|
90
|
+
/**
|
|
91
|
+
* Server name announced to clients (defaults to the application package name)
|
|
92
|
+
*/
|
|
93
|
+
serverName;
|
|
94
|
+
/**
|
|
95
|
+
* Server version announced to clients (defaults to the application package version)
|
|
96
|
+
*/
|
|
97
|
+
serverVersion;
|
|
98
|
+
/**
|
|
99
|
+
* stdio defaults
|
|
100
|
+
*/
|
|
101
|
+
stdio;
|
|
102
|
+
/**
|
|
103
|
+
* Name of a service implementing McpAuthenticator; default uses the Webda session
|
|
104
|
+
*/
|
|
105
|
+
authenticator;
|
|
106
|
+
/**
|
|
107
|
+
* @param params - raw parameters
|
|
108
|
+
* @returns this with defaults applied
|
|
109
|
+
*/
|
|
110
|
+
load(params = {}) {
|
|
111
|
+
super.load(params);
|
|
112
|
+
this.url ??= "/mcp";
|
|
113
|
+
this.resources ??= { models: ["*"] };
|
|
114
|
+
if (this.resources) {
|
|
115
|
+
this.resources.models ??= ["*"];
|
|
116
|
+
}
|
|
117
|
+
this.sessionTimeout ??= 1800;
|
|
118
|
+
this.allowedOrigins ??= [];
|
|
119
|
+
this.allowedHosts ??= ["localhost", "127.0.0.1", "::1"];
|
|
120
|
+
this.maxSessions ??= 1000;
|
|
121
|
+
this.maxOutputBytes ??= 1048576;
|
|
122
|
+
this.stdio ??= {};
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Reserve stdout for the MCP protocol: every other write to `stdout` (log lines,
|
|
128
|
+
* whatever `--log-stream` says, unpatched console calls, third-party output) is
|
|
129
|
+
* sent to `stderr`, and the returned stream writes to the real stdout.
|
|
130
|
+
* @param stdout - stream carrying protocol frames
|
|
131
|
+
* @param stderr - stream receiving everything else
|
|
132
|
+
* @returns the protocol stream and a function restoring stdout
|
|
133
|
+
*/
|
|
134
|
+
export function reserveStdout(stdout = process.stdout, stderr = process.stderr) {
|
|
135
|
+
const original = stdout.write;
|
|
136
|
+
const write = original.bind(stdout);
|
|
137
|
+
const protocol = new Writable({
|
|
138
|
+
write(chunk, encoding, callback) {
|
|
139
|
+
if (write(chunk, encoding)) {
|
|
140
|
+
callback();
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
stdout.once("drain", () => callback());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
stdout.write = stderr.write.bind(stderr);
|
|
148
|
+
return {
|
|
149
|
+
protocol,
|
|
150
|
+
restore: () => {
|
|
151
|
+
stdout.write = original;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* MCP transport: exposes operations as Model Context Protocol tools and
|
|
157
|
+
* models as resources, over Streamable HTTP (a Webda route) and stdio (`webda mcp`).
|
|
158
|
+
*
|
|
159
|
+
* @WebdaModda
|
|
160
|
+
*/
|
|
161
|
+
let McpService = (() => {
|
|
162
|
+
let _classSuper = OperationsTransport;
|
|
163
|
+
let _instanceExtraInitializers = [];
|
|
164
|
+
let _mcp_decorators;
|
|
165
|
+
return class McpService extends _classSuper {
|
|
166
|
+
static {
|
|
167
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
168
|
+
_mcp_decorators = [Command("mcp", { description: "Serve operations as MCP tools over stdio", requires: ["mcp"] })];
|
|
169
|
+
__esDecorate(this, null, _mcp_decorators, { kind: "method", name: "mcp", static: false, private: false, access: { has: obj => "mcp" in obj, get: obj => obj.mcp }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
170
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
171
|
+
}
|
|
172
|
+
tools = __runInitializers(this, _instanceExtraInitializers);
|
|
173
|
+
resourceRegistry;
|
|
174
|
+
sessions;
|
|
175
|
+
fingerprint = "";
|
|
176
|
+
authenticator;
|
|
177
|
+
evictTimer;
|
|
178
|
+
stdioServers = [];
|
|
179
|
+
/**
|
|
180
|
+
* Build typed parameters with defaults. Application replaces this with the
|
|
181
|
+
* webda.module.json `Configuration` class (the same McpServiceParameters);
|
|
182
|
+
* it is defined here so the service also works when registered by hand.
|
|
183
|
+
* @param params - raw parameters
|
|
184
|
+
* @returns typed parameters
|
|
185
|
+
*/
|
|
186
|
+
static createConfiguration(params = {}) {
|
|
187
|
+
return new McpServiceParameters().load(params);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Tools are built in bulk by refreshRegistries
|
|
191
|
+
* @param _operationId - unused
|
|
192
|
+
* @param _definition - unused
|
|
193
|
+
*/
|
|
194
|
+
exposeOperation(_operationId, _definition) { }
|
|
195
|
+
/**
|
|
196
|
+
* @returns this once the authenticator is resolved
|
|
197
|
+
*/
|
|
198
|
+
resolve() {
|
|
199
|
+
super.resolve();
|
|
200
|
+
if (this.parameters.authenticator) {
|
|
201
|
+
const service = useService(this.parameters.authenticator);
|
|
202
|
+
if (typeof service?.authenticate !== "function") {
|
|
203
|
+
throw new Error(`MCP authenticator '${this.parameters.authenticator}' must implement authenticate(ctx)`);
|
|
204
|
+
}
|
|
205
|
+
this.authenticator = service;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
this.authenticator = new SessionAuthenticator();
|
|
209
|
+
}
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Build registries, mount the HTTP route and start session eviction
|
|
214
|
+
* @returns this
|
|
215
|
+
*/
|
|
216
|
+
async init() {
|
|
217
|
+
await super.init();
|
|
218
|
+
const resolve = (name) => {
|
|
219
|
+
try {
|
|
220
|
+
return useApplication().getSchema(name);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
this.tools = new ToolRegistry(resolve);
|
|
227
|
+
this.resourceRegistry = this.parameters.resources
|
|
228
|
+
? new ResourceRegistry(this.parameters.resources.models)
|
|
229
|
+
: undefined;
|
|
230
|
+
this.sessions = new McpSessionManager(this.parameters.sessionTimeout * 1000);
|
|
231
|
+
this.refreshRegistries();
|
|
232
|
+
if (this.parameters.url !== false) {
|
|
233
|
+
this.addRoute(this.parameters.url, ["GET", "POST", "DELETE"], this.handleHttp, { hidden: true });
|
|
234
|
+
}
|
|
235
|
+
this.evictTimer = setInterval(() => void this.sessions.evictIdle(), 60_000);
|
|
236
|
+
this.evictTimer.unref();
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Rebuild tool and resource registries when operations changed
|
|
241
|
+
* @returns true when they were rebuilt
|
|
242
|
+
*/
|
|
243
|
+
refreshRegistries() {
|
|
244
|
+
const ops = this.getOperations();
|
|
245
|
+
const fingerprint = operationsFingerprint(ops);
|
|
246
|
+
if (fingerprint === this.fingerprint) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
this.fingerprint = fingerprint;
|
|
250
|
+
this.tools.build(ops);
|
|
251
|
+
this.resourceRegistry?.build(ops);
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Detect runtime operation registration and notify open sessions
|
|
256
|
+
*/
|
|
257
|
+
checkForChanges() {
|
|
258
|
+
if (!this.refreshRegistries()) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const servers = [...this.sessions.all().map(e => e.server), ...this.stdioServers];
|
|
262
|
+
for (const server of servers) {
|
|
263
|
+
server.sendToolListChanged().catch(err => useLog("DEBUG", "tools/list_changed not delivered", err));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* @param getSession - caller session resolution
|
|
268
|
+
* @returns a new SDK server over this service's registries
|
|
269
|
+
*/
|
|
270
|
+
createServer(getSession) {
|
|
271
|
+
const pkg = useApplication().getPackageDescription?.() || {};
|
|
272
|
+
return createMcpServer({
|
|
273
|
+
info: {
|
|
274
|
+
name: this.parameters.serverName ?? pkg.name ?? "webda",
|
|
275
|
+
version: this.parameters.serverVersion ?? pkg.version ?? "0.0.0"
|
|
276
|
+
},
|
|
277
|
+
tools: this.tools,
|
|
278
|
+
resources: this.resourceRegistry,
|
|
279
|
+
maxOutputBytes: this.parameters.maxOutputBytes,
|
|
280
|
+
getSession,
|
|
281
|
+
beforeRequest: () => this.checkForChanges()
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Origin (protocol + host[:port]) of a request, from the `x-forwarded-host` header
|
|
286
|
+
* when present (HttpServer rejects forwarded headers from untrusted sources) or
|
|
287
|
+
* the `host` header. Parsed here rather than via HttpContext.getAbsoluteUrl,
|
|
288
|
+
* which cannot represent IPv6 literals like `[::1]:18080`.
|
|
289
|
+
* @param ctx - the request context
|
|
290
|
+
* @returns the parsed origin, or undefined when the host is missing or invalid
|
|
291
|
+
*/
|
|
292
|
+
requestOrigin(ctx) {
|
|
293
|
+
const http = ctx.getHttpContext();
|
|
294
|
+
const host = (http.getUniqueHeader("x-forwarded-host") || http.getUniqueHeader("host") || "").split(",")[0].trim();
|
|
295
|
+
// a Host header is only host[:port]: reject anything that smuggles a path, credentials, etc.
|
|
296
|
+
if (!host || /[/\\@?#\s]/.test(host)) {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
return new URL(`${http.getProtocol() || "http:"}//${host}`);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* DNS-rebinding guard: the request hostname must be in allowedHosts
|
|
308
|
+
* @param ctx - the request context
|
|
309
|
+
* @returns true when the host is accepted
|
|
310
|
+
*/
|
|
311
|
+
isHostAllowed(ctx) {
|
|
312
|
+
const allowed = this.parameters.allowedHosts;
|
|
313
|
+
if (allowed.includes("*")) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
const origin = this.requestOrigin(ctx);
|
|
317
|
+
if (!origin) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
const hostname = origin.hostname.replace(/^\[(.*)\]$/, "$1").toLowerCase();
|
|
321
|
+
return allowed.some(h => h.replace(/^\[(.*)\]$/, "$1").toLowerCase() === hostname);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Handle GET/POST/DELETE on the MCP endpoint
|
|
325
|
+
* @param ctx - the request context
|
|
326
|
+
*/
|
|
327
|
+
async handleHttp(ctx) {
|
|
328
|
+
const http = ctx.getHttpContext();
|
|
329
|
+
const origin = this.requestOrigin(ctx);
|
|
330
|
+
if (!this.isHostAllowed(ctx)) {
|
|
331
|
+
jsonRpcError(ctx, 403, -32000, "Host not allowed");
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const session = await this.authenticator.authenticate(ctx);
|
|
335
|
+
const userId = session?.userId;
|
|
336
|
+
const body = http.getMethod() === "POST" ? await ctx.getRawInputAsString() : undefined;
|
|
337
|
+
let parsedBody;
|
|
338
|
+
if (body) {
|
|
339
|
+
try {
|
|
340
|
+
parsedBody = JSON.parse(body);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// left undefined: the SDK transport parses the raw body and answers with a parse error
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const sessionId = http.getUniqueHeader("mcp-session-id");
|
|
347
|
+
let entry;
|
|
348
|
+
if (sessionId) {
|
|
349
|
+
entry = this.sessions.get(sessionId);
|
|
350
|
+
if (!entry) {
|
|
351
|
+
jsonRpcError(ctx, 404, -32001, "Session not found");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (entry.userId !== userId) {
|
|
355
|
+
jsonRpcError(ctx, 403, -32000, "Session belongs to another user");
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
const initializing = Array.isArray(parsedBody)
|
|
361
|
+
? parsedBody.some(m => isInitializeRequest(m))
|
|
362
|
+
: isInitializeRequest(parsedBody);
|
|
363
|
+
if (!initializing && parsedBody !== undefined) {
|
|
364
|
+
jsonRpcError(ctx, 400, -32000, "Bad Request: Mcp-Session-Id header is required");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
// At the cap, make room by evicting the least recently used session:
|
|
368
|
+
// refusing new sessions would let anonymous callers lock out the endpoint
|
|
369
|
+
while (this.sessions.all().length >= Math.max(1, this.parameters.maxSessions)) {
|
|
370
|
+
const evicted = await this.sessions.evictOldest();
|
|
371
|
+
useLog("INFO", "MCP session limit reached, evicted least recently used session", evicted);
|
|
372
|
+
}
|
|
373
|
+
entry = await this.createHttpSession(ctx, userId, origin);
|
|
374
|
+
}
|
|
375
|
+
this.checkForChanges();
|
|
376
|
+
const authInfo = { token: "", clientId: userId ?? "anonymous", scopes: [], extra: { session } };
|
|
377
|
+
const response = await entry.transport.handleRequest(toRequest(ctx, body, origin), {
|
|
378
|
+
parsedBody,
|
|
379
|
+
authInfo
|
|
380
|
+
});
|
|
381
|
+
await writeResponse(ctx, response);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Create an SDK server + transport pair for a new MCP session
|
|
385
|
+
* @param ctx - the initializing request
|
|
386
|
+
* @param userId - caller user id
|
|
387
|
+
* @param requestOrigin - origin of the request (see requestOrigin)
|
|
388
|
+
* @returns the (not yet registered) session entry; it registers on initialize
|
|
389
|
+
*/
|
|
390
|
+
async createHttpSession(ctx, userId, requestOrigin = this.requestOrigin(ctx)) {
|
|
391
|
+
const origin = requestOrigin?.origin;
|
|
392
|
+
const entry = { id: "", userId, lastSeen: Date.now(), server: undefined, transport: undefined };
|
|
393
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
394
|
+
sessionIdGenerator: () => randomUUID(),
|
|
395
|
+
enableDnsRebindingProtection: true,
|
|
396
|
+
allowedOrigins: [...(origin ? [origin] : []), ...this.parameters.allowedOrigins],
|
|
397
|
+
onsessioninitialized: id => {
|
|
398
|
+
entry.id = id;
|
|
399
|
+
this.sessions.add(entry);
|
|
400
|
+
},
|
|
401
|
+
onsessionclosed: id => this.sessions.remove(id)
|
|
402
|
+
});
|
|
403
|
+
entry.transport = transport;
|
|
404
|
+
entry.server = this.createServer(extra => extra.authInfo?.extra?.session);
|
|
405
|
+
await entry.server.connect(transport);
|
|
406
|
+
return entry;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Serve the MCP tools over stdio until stdin closes
|
|
410
|
+
*
|
|
411
|
+
* Anyone able to start this command acts with `user`'s permissions
|
|
412
|
+
* (or anonymous permissions when no user is given).
|
|
413
|
+
*
|
|
414
|
+
* @param user - user id whose permissions apply
|
|
415
|
+
*/
|
|
416
|
+
async mcp(user = "") {
|
|
417
|
+
// stdout carries only protocol frames: logging goes to stderr regardless of --log-stream
|
|
418
|
+
const { protocol, restore } = reserveStdout();
|
|
419
|
+
try {
|
|
420
|
+
const server = await this.serveStdio(new StdioServerTransport(process.stdin, protocol), user || this.parameters.stdio.user);
|
|
421
|
+
await new Promise(resolve => {
|
|
422
|
+
server.onclose = () => resolve();
|
|
423
|
+
process.stdin.once("end", () => void server.close());
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
finally {
|
|
427
|
+
restore();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Connect a new MCP server to a transport, acting as `user`
|
|
432
|
+
* @param transport - stdio (or any) transport
|
|
433
|
+
* @param user - user id whose permissions apply; anonymous when empty
|
|
434
|
+
* @returns the connected server
|
|
435
|
+
*/
|
|
436
|
+
async serveStdio(transport, user) {
|
|
437
|
+
const session = new Session();
|
|
438
|
+
if (user) {
|
|
439
|
+
session.login(user, user);
|
|
440
|
+
}
|
|
441
|
+
const server = this.createServer(() => session);
|
|
442
|
+
this.stdioServers.push(server);
|
|
443
|
+
await server.connect(transport);
|
|
444
|
+
// Stream events (stdin) fire outside the instance storage: rebind the handlers to the current async context
|
|
445
|
+
if (transport.onmessage) {
|
|
446
|
+
transport.onmessage = AsyncResource.bind(transport.onmessage);
|
|
447
|
+
}
|
|
448
|
+
return server;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Stop eviction and close every MCP session
|
|
452
|
+
*/
|
|
453
|
+
async stop() {
|
|
454
|
+
clearInterval(this.evictTimer);
|
|
455
|
+
await this.sessions?.closeAll();
|
|
456
|
+
await Promise.all(this.stdioServers.map(s => s.close().catch(() => { })));
|
|
457
|
+
this.stdioServers = [];
|
|
458
|
+
await super.stop();
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
})();
|
|
462
|
+
export { McpService };
|
|
463
|
+
//# sourceMappingURL=mcpservice.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpservice.service.js","sourceRoot":"","sources":["../src/mcpservice.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { OperationDefinition } from "@webda/core";
|
|
2
|
+
import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* A model exposed as MCP resources
|
|
5
|
+
*/
|
|
6
|
+
export interface ResourceModel {
|
|
7
|
+
/**
|
|
8
|
+
* Short model id as used in operation ids, e.g. "Post"
|
|
9
|
+
*/
|
|
10
|
+
name: string;
|
|
11
|
+
/**
|
|
12
|
+
* Operation returning one instance, e.g. "Post.Get"
|
|
13
|
+
*/
|
|
14
|
+
getOperationId: string;
|
|
15
|
+
/**
|
|
16
|
+
* Operation listing instances, e.g. "Posts.Query"
|
|
17
|
+
*/
|
|
18
|
+
queryOperationId?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Primary key fields, in URI order
|
|
21
|
+
*/
|
|
22
|
+
pkFields: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Registry of models exposed as `webda://{Model}/{pk...}` resources
|
|
26
|
+
*/
|
|
27
|
+
export declare class ResourceRegistry {
|
|
28
|
+
protected filter: string[];
|
|
29
|
+
protected entries: ResourceModel[];
|
|
30
|
+
/**
|
|
31
|
+
* @param filter - model short ids to expose, or ["*"]
|
|
32
|
+
*/
|
|
33
|
+
constructor(filter: string[]);
|
|
34
|
+
/**
|
|
35
|
+
* Rebuild from operations: every visible `{Model}.Get` with `context.pkFields`
|
|
36
|
+
* becomes a model; its `.Query` operation shares the same `context.model`.
|
|
37
|
+
* @param ops - visible operations
|
|
38
|
+
*/
|
|
39
|
+
build(ops: Record<string, OperationDefinition>): void;
|
|
40
|
+
/**
|
|
41
|
+
* @returns exposed models, sorted by name
|
|
42
|
+
*/
|
|
43
|
+
models(): ResourceModel[];
|
|
44
|
+
/**
|
|
45
|
+
* @returns MCP resource templates
|
|
46
|
+
*/
|
|
47
|
+
templates(): ResourceTemplate[];
|
|
48
|
+
/**
|
|
49
|
+
* @param model - the model
|
|
50
|
+
* @param record - an instance (or key object)
|
|
51
|
+
* @returns its resource URI
|
|
52
|
+
*/
|
|
53
|
+
uriFor(model: ResourceModel, record: Record<string, unknown>): string;
|
|
54
|
+
/**
|
|
55
|
+
* @param uri - a resource URI
|
|
56
|
+
* @returns the model and decoded key, or undefined when it does not match
|
|
57
|
+
*/
|
|
58
|
+
parse(uri: string): {
|
|
59
|
+
model: ResourceModel;
|
|
60
|
+
key: Record<string, string>;
|
|
61
|
+
} | undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @param cursor - model and continuation token
|
|
65
|
+
* @param cursor.model - model short id
|
|
66
|
+
* @param cursor.token - continuation token of that model's query
|
|
67
|
+
* @returns opaque cursor
|
|
68
|
+
*/
|
|
69
|
+
export declare function encodeCursor(cursor: {
|
|
70
|
+
model: string;
|
|
71
|
+
token?: string;
|
|
72
|
+
}): string;
|
|
73
|
+
/**
|
|
74
|
+
* @param cursor - opaque cursor
|
|
75
|
+
* @returns the decoded cursor, or undefined when absent or invalid
|
|
76
|
+
*/
|
|
77
|
+
export declare function decodeCursor(cursor: string | undefined): {
|
|
78
|
+
model: string;
|
|
79
|
+
token?: string;
|
|
80
|
+
} | undefined;
|
|
81
|
+
/**
|
|
82
|
+
* @param token - continuation token
|
|
83
|
+
* @returns the WebdaQL query for one page
|
|
84
|
+
*/
|
|
85
|
+
export declare function queryFor(token?: string): string;
|
|
86
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;GAEG;AACH,qBAAa,gBAAgB;IAMf,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE;IALtC,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,CAAM;IAExC;;OAEG;IACH,YAAsB,MAAM,EAAE,MAAM,EAAE,EAAI;IAE1C;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,IAAI,CAkBpD;IAED;;OAEG;IACH,MAAM,IAAI,aAAa,EAAE,CAExB;IAED;;OAEG;IACH,SAAS,IAAI,gBAAgB,EAAE,CAM9B;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEpE;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,aAAa,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,SAAS,CAgBpF;CACF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE9E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAgBtG;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAM/C"}
|