@hasna/microservices 0.0.22 → 0.0.24
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 +2 -1
- package/README.md +12 -0
- package/bin/mcp.js +1586 -324
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -15133,6 +15133,7 @@ config(en_default2());
|
|
|
15133
15133
|
|
|
15134
15134
|
// node_modules/.bun/@modelcontextprotocol+sdk@1.27.1/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
15135
15135
|
var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
15136
|
+
var DEFAULT_NEGOTIATED_PROTOCOL_VERSION = "2025-03-26";
|
|
15136
15137
|
var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
|
|
15137
15138
|
var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
|
|
15138
15139
|
var JSONRPC_VERSION = "2.0";
|
|
@@ -15305,6 +15306,7 @@ var InitializeRequestSchema = RequestSchema.extend({
|
|
|
15305
15306
|
method: literal("initialize"),
|
|
15306
15307
|
params: InitializeRequestParamsSchema
|
|
15307
15308
|
});
|
|
15309
|
+
var isInitializeRequest = (value) => InitializeRequestSchema.safeParse(value).success;
|
|
15308
15310
|
var ServerCapabilitiesSchema = object2({
|
|
15309
15311
|
experimental: record(string2(), AssertObjectSchema).optional(),
|
|
15310
15312
|
logging: AssertObjectSchema.optional(),
|
|
@@ -20073,407 +20075,1667 @@ async function runMicroserviceCommand(name, args, timeout = 30000) {
|
|
|
20073
20075
|
});
|
|
20074
20076
|
}
|
|
20075
20077
|
|
|
20076
|
-
// src/mcp/
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
}
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20078
|
+
// src/mcp/http.ts
|
|
20079
|
+
import { createServer } from "http";
|
|
20080
|
+
|
|
20081
|
+
// node_modules/.bun/@hono+node-server@1.19.11+88d33b5845f82712/node_modules/@hono/node-server/dist/index.mjs
|
|
20082
|
+
import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
|
|
20083
|
+
import { Http2ServerRequest } from "http2";
|
|
20084
|
+
import { Readable } from "stream";
|
|
20085
|
+
import crypto2 from "crypto";
|
|
20086
|
+
var RequestError = class extends Error {
|
|
20087
|
+
constructor(message, options) {
|
|
20088
|
+
super(message, options);
|
|
20089
|
+
this.name = "RequestError";
|
|
20090
|
+
}
|
|
20091
|
+
};
|
|
20092
|
+
var toRequestError = (e) => {
|
|
20093
|
+
if (e instanceof RequestError) {
|
|
20094
|
+
return e;
|
|
20095
|
+
}
|
|
20096
|
+
return new RequestError(e.message, { cause: e });
|
|
20097
|
+
};
|
|
20098
|
+
var GlobalRequest = global.Request;
|
|
20099
|
+
var Request = class extends GlobalRequest {
|
|
20100
|
+
constructor(input, options) {
|
|
20101
|
+
if (typeof input === "object" && getRequestCache in input) {
|
|
20102
|
+
input = input[getRequestCache]();
|
|
20103
|
+
}
|
|
20104
|
+
if (typeof options?.body?.getReader !== "undefined") {
|
|
20105
|
+
options.duplex ??= "half";
|
|
20106
|
+
}
|
|
20107
|
+
super(input, options);
|
|
20108
|
+
}
|
|
20109
|
+
};
|
|
20110
|
+
var newHeadersFromIncoming = (incoming) => {
|
|
20111
|
+
const headerRecord = [];
|
|
20112
|
+
const rawHeaders = incoming.rawHeaders;
|
|
20113
|
+
for (let i = 0;i < rawHeaders.length; i += 2) {
|
|
20114
|
+
const { [i]: key, [i + 1]: value } = rawHeaders;
|
|
20115
|
+
if (key.charCodeAt(0) !== 58) {
|
|
20116
|
+
headerRecord.push([key, value]);
|
|
20117
|
+
}
|
|
20118
|
+
}
|
|
20119
|
+
return new Headers(headerRecord);
|
|
20120
|
+
};
|
|
20121
|
+
var wrapBodyStream = Symbol("wrapBodyStream");
|
|
20122
|
+
var newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
|
|
20123
|
+
const init = {
|
|
20124
|
+
method,
|
|
20125
|
+
headers,
|
|
20126
|
+
signal: abortController.signal
|
|
20127
|
+
};
|
|
20128
|
+
if (method === "TRACE") {
|
|
20129
|
+
init.method = "GET";
|
|
20130
|
+
const req = new Request(url, init);
|
|
20131
|
+
Object.defineProperty(req, "method", {
|
|
20132
|
+
get() {
|
|
20133
|
+
return "TRACE";
|
|
20134
|
+
}
|
|
20135
|
+
});
|
|
20136
|
+
return req;
|
|
20137
|
+
}
|
|
20138
|
+
if (!(method === "GET" || method === "HEAD")) {
|
|
20139
|
+
if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) {
|
|
20140
|
+
init.body = new ReadableStream({
|
|
20141
|
+
start(controller) {
|
|
20142
|
+
controller.enqueue(incoming.rawBody);
|
|
20143
|
+
controller.close();
|
|
20144
|
+
}
|
|
20145
|
+
});
|
|
20146
|
+
} else if (incoming[wrapBodyStream]) {
|
|
20147
|
+
let reader;
|
|
20148
|
+
init.body = new ReadableStream({
|
|
20149
|
+
async pull(controller) {
|
|
20150
|
+
try {
|
|
20151
|
+
reader ||= Readable.toWeb(incoming).getReader();
|
|
20152
|
+
const { done, value } = await reader.read();
|
|
20153
|
+
if (done) {
|
|
20154
|
+
controller.close();
|
|
20155
|
+
} else {
|
|
20156
|
+
controller.enqueue(value);
|
|
20157
|
+
}
|
|
20158
|
+
} catch (error2) {
|
|
20159
|
+
controller.error(error2);
|
|
20160
|
+
}
|
|
20161
|
+
}
|
|
20162
|
+
});
|
|
20163
|
+
} else {
|
|
20164
|
+
init.body = Readable.toWeb(incoming);
|
|
20165
|
+
}
|
|
20166
|
+
}
|
|
20167
|
+
return new Request(url, init);
|
|
20168
|
+
};
|
|
20169
|
+
var getRequestCache = Symbol("getRequestCache");
|
|
20170
|
+
var requestCache = Symbol("requestCache");
|
|
20171
|
+
var incomingKey = Symbol("incomingKey");
|
|
20172
|
+
var urlKey = Symbol("urlKey");
|
|
20173
|
+
var headersKey = Symbol("headersKey");
|
|
20174
|
+
var abortControllerKey = Symbol("abortControllerKey");
|
|
20175
|
+
var getAbortController = Symbol("getAbortController");
|
|
20176
|
+
var requestPrototype = {
|
|
20177
|
+
get method() {
|
|
20178
|
+
return this[incomingKey].method || "GET";
|
|
20179
|
+
},
|
|
20180
|
+
get url() {
|
|
20181
|
+
return this[urlKey];
|
|
20182
|
+
},
|
|
20183
|
+
get headers() {
|
|
20184
|
+
return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
|
|
20185
|
+
},
|
|
20186
|
+
[getAbortController]() {
|
|
20187
|
+
this[getRequestCache]();
|
|
20188
|
+
return this[abortControllerKey];
|
|
20189
|
+
},
|
|
20190
|
+
[getRequestCache]() {
|
|
20191
|
+
this[abortControllerKey] ||= new AbortController;
|
|
20192
|
+
return this[requestCache] ||= newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], this[abortControllerKey]);
|
|
20193
|
+
}
|
|
20194
|
+
};
|
|
20195
|
+
[
|
|
20196
|
+
"body",
|
|
20197
|
+
"bodyUsed",
|
|
20198
|
+
"cache",
|
|
20199
|
+
"credentials",
|
|
20200
|
+
"destination",
|
|
20201
|
+
"integrity",
|
|
20202
|
+
"mode",
|
|
20203
|
+
"redirect",
|
|
20204
|
+
"referrer",
|
|
20205
|
+
"referrerPolicy",
|
|
20206
|
+
"signal",
|
|
20207
|
+
"keepalive"
|
|
20208
|
+
].forEach((k) => {
|
|
20209
|
+
Object.defineProperty(requestPrototype, k, {
|
|
20210
|
+
get() {
|
|
20211
|
+
return this[getRequestCache]()[k];
|
|
20212
|
+
}
|
|
20213
|
+
});
|
|
20214
|
+
});
|
|
20215
|
+
["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
|
|
20216
|
+
Object.defineProperty(requestPrototype, k, {
|
|
20217
|
+
value: function() {
|
|
20218
|
+
return this[getRequestCache]()[k]();
|
|
20219
|
+
}
|
|
20220
|
+
});
|
|
20221
|
+
});
|
|
20222
|
+
Object.setPrototypeOf(requestPrototype, Request.prototype);
|
|
20223
|
+
var newRequest = (incoming, defaultHostname) => {
|
|
20224
|
+
const req = Object.create(requestPrototype);
|
|
20225
|
+
req[incomingKey] = incoming;
|
|
20226
|
+
const incomingUrl = incoming.url || "";
|
|
20227
|
+
if (incomingUrl[0] !== "/" && (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) {
|
|
20228
|
+
if (incoming instanceof Http2ServerRequest) {
|
|
20229
|
+
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
20230
|
+
}
|
|
20231
|
+
try {
|
|
20232
|
+
const url2 = new URL(incomingUrl);
|
|
20233
|
+
req[urlKey] = url2.href;
|
|
20234
|
+
} catch (e) {
|
|
20235
|
+
throw new RequestError("Invalid absolute URL", { cause: e });
|
|
20236
|
+
}
|
|
20237
|
+
return req;
|
|
20238
|
+
}
|
|
20239
|
+
const host = (incoming instanceof Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
|
|
20240
|
+
if (!host) {
|
|
20241
|
+
throw new RequestError("Missing host header");
|
|
20242
|
+
}
|
|
20243
|
+
let scheme;
|
|
20244
|
+
if (incoming instanceof Http2ServerRequest) {
|
|
20245
|
+
scheme = incoming.scheme;
|
|
20246
|
+
if (!(scheme === "http" || scheme === "https")) {
|
|
20247
|
+
throw new RequestError("Unsupported scheme");
|
|
20248
|
+
}
|
|
20249
|
+
} else {
|
|
20250
|
+
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
20251
|
+
}
|
|
20252
|
+
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
20253
|
+
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
20254
|
+
throw new RequestError("Invalid host header");
|
|
20255
|
+
}
|
|
20256
|
+
req[urlKey] = url.href;
|
|
20257
|
+
return req;
|
|
20258
|
+
};
|
|
20259
|
+
var responseCache = Symbol("responseCache");
|
|
20260
|
+
var getResponseCache = Symbol("getResponseCache");
|
|
20261
|
+
var cacheKey = Symbol("cache");
|
|
20262
|
+
var GlobalResponse = global.Response;
|
|
20263
|
+
var Response2 = class _Response {
|
|
20264
|
+
#body;
|
|
20265
|
+
#init;
|
|
20266
|
+
[getResponseCache]() {
|
|
20267
|
+
delete this[cacheKey];
|
|
20268
|
+
return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
|
|
20269
|
+
}
|
|
20270
|
+
constructor(body, init) {
|
|
20271
|
+
let headers;
|
|
20272
|
+
this.#body = body;
|
|
20273
|
+
if (init instanceof _Response) {
|
|
20274
|
+
const cachedGlobalResponse = init[responseCache];
|
|
20275
|
+
if (cachedGlobalResponse) {
|
|
20276
|
+
this.#init = cachedGlobalResponse;
|
|
20277
|
+
this[getResponseCache]();
|
|
20278
|
+
return;
|
|
20279
|
+
} else {
|
|
20280
|
+
this.#init = init.#init;
|
|
20281
|
+
headers = new Headers(init.#init.headers);
|
|
20282
|
+
}
|
|
20283
|
+
} else {
|
|
20284
|
+
this.#init = init;
|
|
20285
|
+
}
|
|
20286
|
+
if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) {
|
|
20287
|
+
this[cacheKey] = [init?.status || 200, body, headers || init?.headers];
|
|
20288
|
+
}
|
|
20289
|
+
}
|
|
20290
|
+
get headers() {
|
|
20291
|
+
const cache = this[cacheKey];
|
|
20292
|
+
if (cache) {
|
|
20293
|
+
if (!(cache[2] instanceof Headers)) {
|
|
20294
|
+
cache[2] = new Headers(cache[2] || { "content-type": "text/plain; charset=UTF-8" });
|
|
20295
|
+
}
|
|
20296
|
+
return cache[2];
|
|
20297
|
+
}
|
|
20298
|
+
return this[getResponseCache]().headers;
|
|
20299
|
+
}
|
|
20300
|
+
get status() {
|
|
20301
|
+
return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
|
|
20302
|
+
}
|
|
20303
|
+
get ok() {
|
|
20304
|
+
const status = this.status;
|
|
20305
|
+
return status >= 200 && status < 300;
|
|
20306
|
+
}
|
|
20307
|
+
};
|
|
20308
|
+
["body", "bodyUsed", "redirected", "statusText", "trailers", "type", "url"].forEach((k) => {
|
|
20309
|
+
Object.defineProperty(Response2.prototype, k, {
|
|
20310
|
+
get() {
|
|
20311
|
+
return this[getResponseCache]()[k];
|
|
20312
|
+
}
|
|
20313
|
+
});
|
|
20314
|
+
});
|
|
20315
|
+
["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
|
|
20316
|
+
Object.defineProperty(Response2.prototype, k, {
|
|
20317
|
+
value: function() {
|
|
20318
|
+
return this[getResponseCache]()[k]();
|
|
20319
|
+
}
|
|
20320
|
+
});
|
|
20321
|
+
});
|
|
20322
|
+
Object.setPrototypeOf(Response2, GlobalResponse);
|
|
20323
|
+
Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
|
|
20324
|
+
async function readWithoutBlocking(readPromise) {
|
|
20325
|
+
return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(undefined))]);
|
|
20326
|
+
}
|
|
20327
|
+
function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {
|
|
20328
|
+
const cancel = (error2) => {
|
|
20329
|
+
reader.cancel(error2).catch(() => {});
|
|
20330
|
+
};
|
|
20331
|
+
writable.on("close", cancel);
|
|
20332
|
+
writable.on("error", cancel);
|
|
20333
|
+
(currentReadPromise ?? reader.read()).then(flow, handleStreamError);
|
|
20334
|
+
return reader.closed.finally(() => {
|
|
20335
|
+
writable.off("close", cancel);
|
|
20336
|
+
writable.off("error", cancel);
|
|
20337
|
+
});
|
|
20338
|
+
function handleStreamError(error2) {
|
|
20339
|
+
if (error2) {
|
|
20340
|
+
writable.destroy(error2);
|
|
20341
|
+
}
|
|
20342
|
+
}
|
|
20343
|
+
function onDrain() {
|
|
20344
|
+
reader.read().then(flow, handleStreamError);
|
|
20345
|
+
}
|
|
20346
|
+
function flow({ done, value }) {
|
|
20347
|
+
try {
|
|
20348
|
+
if (done) {
|
|
20349
|
+
writable.end();
|
|
20350
|
+
} else if (!writable.write(value)) {
|
|
20351
|
+
writable.once("drain", onDrain);
|
|
20352
|
+
} else {
|
|
20353
|
+
return reader.read().then(flow, handleStreamError);
|
|
20354
|
+
}
|
|
20355
|
+
} catch (e) {
|
|
20356
|
+
handleStreamError(e);
|
|
20357
|
+
}
|
|
20358
|
+
}
|
|
20359
|
+
}
|
|
20360
|
+
function writeFromReadableStream(stream, writable) {
|
|
20361
|
+
if (stream.locked) {
|
|
20362
|
+
throw new TypeError("ReadableStream is locked.");
|
|
20363
|
+
} else if (writable.destroyed) {
|
|
20364
|
+
return;
|
|
20365
|
+
}
|
|
20366
|
+
return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
|
|
20367
|
+
}
|
|
20368
|
+
var buildOutgoingHttpHeaders = (headers) => {
|
|
20369
|
+
const res = {};
|
|
20370
|
+
if (!(headers instanceof Headers)) {
|
|
20371
|
+
headers = new Headers(headers ?? undefined);
|
|
20372
|
+
}
|
|
20373
|
+
const cookies = [];
|
|
20374
|
+
for (const [k, v] of headers) {
|
|
20375
|
+
if (k === "set-cookie") {
|
|
20376
|
+
cookies.push(v);
|
|
20377
|
+
} else {
|
|
20378
|
+
res[k] = v;
|
|
20379
|
+
}
|
|
20380
|
+
}
|
|
20381
|
+
if (cookies.length > 0) {
|
|
20382
|
+
res["set-cookie"] = cookies;
|
|
20383
|
+
}
|
|
20384
|
+
res["content-type"] ??= "text/plain; charset=UTF-8";
|
|
20385
|
+
return res;
|
|
20386
|
+
};
|
|
20387
|
+
var X_ALREADY_SENT = "x-hono-already-sent";
|
|
20388
|
+
if (typeof global.crypto === "undefined") {
|
|
20389
|
+
global.crypto = crypto2;
|
|
20390
|
+
}
|
|
20391
|
+
var outgoingEnded = Symbol("outgoingEnded");
|
|
20392
|
+
var handleRequestError = () => new Response(null, {
|
|
20393
|
+
status: 400
|
|
20394
|
+
});
|
|
20395
|
+
var handleFetchError = (e) => new Response(null, {
|
|
20396
|
+
status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500
|
|
20397
|
+
});
|
|
20398
|
+
var handleResponseError = (e, outgoing) => {
|
|
20399
|
+
const err = e instanceof Error ? e : new Error("unknown error", { cause: e });
|
|
20400
|
+
if (err.code === "ERR_STREAM_PREMATURE_CLOSE") {
|
|
20401
|
+
console.info("The user aborted a request.");
|
|
20402
|
+
} else {
|
|
20403
|
+
console.error(e);
|
|
20404
|
+
if (!outgoing.headersSent) {
|
|
20405
|
+
outgoing.writeHead(500, { "Content-Type": "text/plain" });
|
|
20406
|
+
}
|
|
20407
|
+
outgoing.end(`Error: ${err.message}`);
|
|
20408
|
+
outgoing.destroy(err);
|
|
20409
|
+
}
|
|
20410
|
+
};
|
|
20411
|
+
var flushHeaders = (outgoing) => {
|
|
20412
|
+
if ("flushHeaders" in outgoing && outgoing.writable) {
|
|
20413
|
+
outgoing.flushHeaders();
|
|
20414
|
+
}
|
|
20415
|
+
};
|
|
20416
|
+
var responseViaCache = async (res, outgoing) => {
|
|
20417
|
+
let [status, body, header] = res[cacheKey];
|
|
20418
|
+
let hasContentLength = false;
|
|
20419
|
+
if (!header) {
|
|
20420
|
+
header = { "content-type": "text/plain; charset=UTF-8" };
|
|
20421
|
+
} else if (header instanceof Headers) {
|
|
20422
|
+
hasContentLength = header.has("content-length");
|
|
20423
|
+
header = buildOutgoingHttpHeaders(header);
|
|
20424
|
+
} else if (Array.isArray(header)) {
|
|
20425
|
+
const headerObj = new Headers(header);
|
|
20426
|
+
hasContentLength = headerObj.has("content-length");
|
|
20427
|
+
header = buildOutgoingHttpHeaders(headerObj);
|
|
20428
|
+
} else {
|
|
20429
|
+
for (const key in header) {
|
|
20430
|
+
if (key.length === 14 && key.toLowerCase() === "content-length") {
|
|
20431
|
+
hasContentLength = true;
|
|
20432
|
+
break;
|
|
20433
|
+
}
|
|
20434
|
+
}
|
|
20435
|
+
}
|
|
20436
|
+
if (!hasContentLength) {
|
|
20437
|
+
if (typeof body === "string") {
|
|
20438
|
+
header["Content-Length"] = Buffer.byteLength(body);
|
|
20439
|
+
} else if (body instanceof Uint8Array) {
|
|
20440
|
+
header["Content-Length"] = body.byteLength;
|
|
20441
|
+
} else if (body instanceof Blob) {
|
|
20442
|
+
header["Content-Length"] = body.size;
|
|
20443
|
+
}
|
|
20444
|
+
}
|
|
20445
|
+
outgoing.writeHead(status, header);
|
|
20446
|
+
if (typeof body === "string" || body instanceof Uint8Array) {
|
|
20447
|
+
outgoing.end(body);
|
|
20448
|
+
} else if (body instanceof Blob) {
|
|
20449
|
+
outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
20450
|
+
} else {
|
|
20451
|
+
flushHeaders(outgoing);
|
|
20452
|
+
await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
|
|
20453
|
+
}
|
|
20454
|
+
outgoing[outgoingEnded]?.();
|
|
20455
|
+
};
|
|
20456
|
+
var isPromise = (res) => typeof res.then === "function";
|
|
20457
|
+
var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
20458
|
+
if (isPromise(res)) {
|
|
20459
|
+
if (options.errorHandler) {
|
|
20460
|
+
try {
|
|
20461
|
+
res = await res;
|
|
20462
|
+
} catch (err) {
|
|
20463
|
+
const errRes = await options.errorHandler(err);
|
|
20464
|
+
if (!errRes) {
|
|
20465
|
+
return;
|
|
20466
|
+
}
|
|
20467
|
+
res = errRes;
|
|
20468
|
+
}
|
|
20469
|
+
} else {
|
|
20470
|
+
res = await res.catch(handleFetchError);
|
|
20471
|
+
}
|
|
20472
|
+
}
|
|
20473
|
+
if (cacheKey in res) {
|
|
20474
|
+
return responseViaCache(res, outgoing);
|
|
20475
|
+
}
|
|
20476
|
+
const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
|
|
20477
|
+
if (res.body) {
|
|
20478
|
+
const reader = res.body.getReader();
|
|
20479
|
+
const values = [];
|
|
20480
|
+
let done = false;
|
|
20481
|
+
let currentReadPromise = undefined;
|
|
20482
|
+
if (resHeaderRecord["transfer-encoding"] !== "chunked") {
|
|
20483
|
+
let maxReadCount = 2;
|
|
20484
|
+
for (let i = 0;i < maxReadCount; i++) {
|
|
20485
|
+
currentReadPromise ||= reader.read();
|
|
20486
|
+
const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => {
|
|
20487
|
+
console.error(e);
|
|
20488
|
+
done = true;
|
|
20489
|
+
});
|
|
20490
|
+
if (!chunk) {
|
|
20491
|
+
if (i === 1) {
|
|
20492
|
+
await new Promise((resolve2) => setTimeout(resolve2));
|
|
20493
|
+
maxReadCount = 3;
|
|
20494
|
+
continue;
|
|
20495
|
+
}
|
|
20496
|
+
break;
|
|
20497
|
+
}
|
|
20498
|
+
currentReadPromise = undefined;
|
|
20499
|
+
if (chunk.value) {
|
|
20500
|
+
values.push(chunk.value);
|
|
20501
|
+
}
|
|
20502
|
+
if (chunk.done) {
|
|
20503
|
+
done = true;
|
|
20504
|
+
break;
|
|
20505
|
+
}
|
|
20506
|
+
}
|
|
20507
|
+
if (done && !("content-length" in resHeaderRecord)) {
|
|
20508
|
+
resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0);
|
|
20509
|
+
}
|
|
20510
|
+
}
|
|
20511
|
+
outgoing.writeHead(res.status, resHeaderRecord);
|
|
20512
|
+
values.forEach((value) => {
|
|
20513
|
+
outgoing.write(value);
|
|
20514
|
+
});
|
|
20515
|
+
if (done) {
|
|
20516
|
+
outgoing.end();
|
|
20517
|
+
} else {
|
|
20518
|
+
if (values.length === 0) {
|
|
20519
|
+
flushHeaders(outgoing);
|
|
20520
|
+
}
|
|
20521
|
+
await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise);
|
|
20522
|
+
}
|
|
20523
|
+
} else if (resHeaderRecord[X_ALREADY_SENT]) {} else {
|
|
20524
|
+
outgoing.writeHead(res.status, resHeaderRecord);
|
|
20525
|
+
outgoing.end();
|
|
20526
|
+
}
|
|
20527
|
+
outgoing[outgoingEnded]?.();
|
|
20528
|
+
};
|
|
20529
|
+
var getRequestListener = (fetchCallback, options = {}) => {
|
|
20530
|
+
const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
|
|
20531
|
+
if (options.overrideGlobalObjects !== false && global.Request !== Request) {
|
|
20532
|
+
Object.defineProperty(global, "Request", {
|
|
20533
|
+
value: Request
|
|
20534
|
+
});
|
|
20535
|
+
Object.defineProperty(global, "Response", {
|
|
20536
|
+
value: Response2
|
|
20537
|
+
});
|
|
20538
|
+
}
|
|
20539
|
+
return async (incoming, outgoing) => {
|
|
20540
|
+
let res, req;
|
|
20541
|
+
try {
|
|
20542
|
+
req = newRequest(incoming, options.hostname);
|
|
20543
|
+
let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
|
|
20544
|
+
if (!incomingEnded) {
|
|
20545
|
+
incoming[wrapBodyStream] = true;
|
|
20546
|
+
incoming.on("end", () => {
|
|
20547
|
+
incomingEnded = true;
|
|
20548
|
+
});
|
|
20549
|
+
if (incoming instanceof Http2ServerRequest2) {
|
|
20550
|
+
outgoing[outgoingEnded] = () => {
|
|
20551
|
+
if (!incomingEnded) {
|
|
20552
|
+
setTimeout(() => {
|
|
20553
|
+
if (!incomingEnded) {
|
|
20554
|
+
setTimeout(() => {
|
|
20555
|
+
incoming.destroy();
|
|
20556
|
+
outgoing.destroy();
|
|
20557
|
+
});
|
|
20558
|
+
}
|
|
20559
|
+
});
|
|
20560
|
+
}
|
|
20561
|
+
};
|
|
20562
|
+
}
|
|
20563
|
+
}
|
|
20564
|
+
outgoing.on("close", () => {
|
|
20565
|
+
const abortController = req[abortControllerKey];
|
|
20566
|
+
if (abortController) {
|
|
20567
|
+
if (incoming.errored) {
|
|
20568
|
+
req[abortControllerKey].abort(incoming.errored.toString());
|
|
20569
|
+
} else if (!outgoing.writableFinished) {
|
|
20570
|
+
req[abortControllerKey].abort("Client connection prematurely closed.");
|
|
20571
|
+
}
|
|
20572
|
+
}
|
|
20573
|
+
if (!incomingEnded) {
|
|
20574
|
+
setTimeout(() => {
|
|
20575
|
+
if (!incomingEnded) {
|
|
20576
|
+
setTimeout(() => {
|
|
20577
|
+
incoming.destroy();
|
|
20578
|
+
});
|
|
20579
|
+
}
|
|
20580
|
+
});
|
|
20581
|
+
}
|
|
20582
|
+
});
|
|
20583
|
+
res = fetchCallback(req, { incoming, outgoing });
|
|
20584
|
+
if (cacheKey in res) {
|
|
20585
|
+
return responseViaCache(res, outgoing);
|
|
20586
|
+
}
|
|
20587
|
+
} catch (e) {
|
|
20588
|
+
if (!res) {
|
|
20589
|
+
if (options.errorHandler) {
|
|
20590
|
+
res = await options.errorHandler(req ? e : toRequestError(e));
|
|
20591
|
+
if (!res) {
|
|
20592
|
+
return;
|
|
20593
|
+
}
|
|
20594
|
+
} else if (!req) {
|
|
20595
|
+
res = handleRequestError();
|
|
20596
|
+
} else {
|
|
20597
|
+
res = handleFetchError(e);
|
|
20598
|
+
}
|
|
20599
|
+
} else {
|
|
20600
|
+
return handleResponseError(e, outgoing);
|
|
20601
|
+
}
|
|
20602
|
+
}
|
|
20603
|
+
try {
|
|
20604
|
+
return await responseViaResponseObject(res, outgoing, options);
|
|
20605
|
+
} catch (e) {
|
|
20606
|
+
return handleResponseError(e, outgoing);
|
|
20607
|
+
}
|
|
20608
|
+
};
|
|
20609
|
+
};
|
|
20610
|
+
|
|
20611
|
+
// node_modules/.bun/@modelcontextprotocol+sdk@1.27.1/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js
|
|
20612
|
+
class WebStandardStreamableHTTPServerTransport {
|
|
20613
|
+
constructor(options = {}) {
|
|
20614
|
+
this._started = false;
|
|
20615
|
+
this._hasHandledRequest = false;
|
|
20616
|
+
this._streamMapping = new Map;
|
|
20617
|
+
this._requestToStreamMapping = new Map;
|
|
20618
|
+
this._requestResponseMap = new Map;
|
|
20619
|
+
this._initialized = false;
|
|
20620
|
+
this._enableJsonResponse = false;
|
|
20621
|
+
this._standaloneSseStreamId = "_GET_stream";
|
|
20622
|
+
this.sessionIdGenerator = options.sessionIdGenerator;
|
|
20623
|
+
this._enableJsonResponse = options.enableJsonResponse ?? false;
|
|
20624
|
+
this._eventStore = options.eventStore;
|
|
20625
|
+
this._onsessioninitialized = options.onsessioninitialized;
|
|
20626
|
+
this._onsessionclosed = options.onsessionclosed;
|
|
20627
|
+
this._allowedHosts = options.allowedHosts;
|
|
20628
|
+
this._allowedOrigins = options.allowedOrigins;
|
|
20629
|
+
this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;
|
|
20630
|
+
this._retryInterval = options.retryInterval;
|
|
20631
|
+
}
|
|
20632
|
+
async start() {
|
|
20633
|
+
if (this._started) {
|
|
20634
|
+
throw new Error("Transport already started");
|
|
20635
|
+
}
|
|
20636
|
+
this._started = true;
|
|
20637
|
+
}
|
|
20638
|
+
createJsonErrorResponse(status, code, message, options) {
|
|
20639
|
+
const error2 = { code, message };
|
|
20640
|
+
if (options?.data !== undefined) {
|
|
20641
|
+
error2.data = options.data;
|
|
20642
|
+
}
|
|
20643
|
+
return new Response(JSON.stringify({
|
|
20644
|
+
jsonrpc: "2.0",
|
|
20645
|
+
error: error2,
|
|
20646
|
+
id: null
|
|
20647
|
+
}), {
|
|
20648
|
+
status,
|
|
20649
|
+
headers: {
|
|
20650
|
+
"Content-Type": "application/json",
|
|
20651
|
+
...options?.headers
|
|
20652
|
+
}
|
|
20653
|
+
});
|
|
20654
|
+
}
|
|
20655
|
+
validateRequestHeaders(req) {
|
|
20656
|
+
if (!this._enableDnsRebindingProtection) {
|
|
20657
|
+
return;
|
|
20658
|
+
}
|
|
20659
|
+
if (this._allowedHosts && this._allowedHosts.length > 0) {
|
|
20660
|
+
const hostHeader = req.headers.get("host");
|
|
20661
|
+
if (!hostHeader || !this._allowedHosts.includes(hostHeader)) {
|
|
20662
|
+
const error2 = `Invalid Host header: ${hostHeader}`;
|
|
20663
|
+
this.onerror?.(new Error(error2));
|
|
20664
|
+
return this.createJsonErrorResponse(403, -32000, error2);
|
|
20665
|
+
}
|
|
20666
|
+
}
|
|
20667
|
+
if (this._allowedOrigins && this._allowedOrigins.length > 0) {
|
|
20668
|
+
const originHeader = req.headers.get("origin");
|
|
20669
|
+
if (originHeader && !this._allowedOrigins.includes(originHeader)) {
|
|
20670
|
+
const error2 = `Invalid Origin header: ${originHeader}`;
|
|
20671
|
+
this.onerror?.(new Error(error2));
|
|
20672
|
+
return this.createJsonErrorResponse(403, -32000, error2);
|
|
20673
|
+
}
|
|
20674
|
+
}
|
|
20675
|
+
return;
|
|
20676
|
+
}
|
|
20677
|
+
async handleRequest(req, options) {
|
|
20678
|
+
if (!this.sessionIdGenerator && this._hasHandledRequest) {
|
|
20679
|
+
throw new Error("Stateless transport cannot be reused across requests. Create a new transport per request.");
|
|
20680
|
+
}
|
|
20681
|
+
this._hasHandledRequest = true;
|
|
20682
|
+
const validationError = this.validateRequestHeaders(req);
|
|
20683
|
+
if (validationError) {
|
|
20684
|
+
return validationError;
|
|
20685
|
+
}
|
|
20686
|
+
switch (req.method) {
|
|
20687
|
+
case "POST":
|
|
20688
|
+
return this.handlePostRequest(req, options);
|
|
20689
|
+
case "GET":
|
|
20690
|
+
return this.handleGetRequest(req);
|
|
20691
|
+
case "DELETE":
|
|
20692
|
+
return this.handleDeleteRequest(req);
|
|
20693
|
+
default:
|
|
20694
|
+
return this.handleUnsupportedRequest();
|
|
20695
|
+
}
|
|
20696
|
+
}
|
|
20697
|
+
async writePrimingEvent(controller, encoder, streamId, protocolVersion) {
|
|
20698
|
+
if (!this._eventStore) {
|
|
20699
|
+
return;
|
|
20700
|
+
}
|
|
20701
|
+
if (protocolVersion < "2025-11-25") {
|
|
20702
|
+
return;
|
|
20703
|
+
}
|
|
20704
|
+
const primingEventId = await this._eventStore.storeEvent(streamId, {});
|
|
20705
|
+
let primingEvent = `id: ${primingEventId}
|
|
20706
|
+
data:
|
|
20707
|
+
|
|
20708
|
+
`;
|
|
20709
|
+
if (this._retryInterval !== undefined) {
|
|
20710
|
+
primingEvent = `id: ${primingEventId}
|
|
20711
|
+
retry: ${this._retryInterval}
|
|
20712
|
+
data:
|
|
20713
|
+
|
|
20714
|
+
`;
|
|
20715
|
+
}
|
|
20716
|
+
controller.enqueue(encoder.encode(primingEvent));
|
|
20717
|
+
}
|
|
20718
|
+
async handleGetRequest(req) {
|
|
20719
|
+
const acceptHeader = req.headers.get("accept");
|
|
20720
|
+
if (!acceptHeader?.includes("text/event-stream")) {
|
|
20721
|
+
this.onerror?.(new Error("Not Acceptable: Client must accept text/event-stream"));
|
|
20722
|
+
return this.createJsonErrorResponse(406, -32000, "Not Acceptable: Client must accept text/event-stream");
|
|
20723
|
+
}
|
|
20724
|
+
const sessionError = this.validateSession(req);
|
|
20725
|
+
if (sessionError) {
|
|
20726
|
+
return sessionError;
|
|
20727
|
+
}
|
|
20728
|
+
const protocolError = this.validateProtocolVersion(req);
|
|
20729
|
+
if (protocolError) {
|
|
20730
|
+
return protocolError;
|
|
20731
|
+
}
|
|
20732
|
+
if (this._eventStore) {
|
|
20733
|
+
const lastEventId = req.headers.get("last-event-id");
|
|
20734
|
+
if (lastEventId) {
|
|
20735
|
+
return this.replayEvents(lastEventId);
|
|
20736
|
+
}
|
|
20737
|
+
}
|
|
20738
|
+
if (this._streamMapping.get(this._standaloneSseStreamId) !== undefined) {
|
|
20739
|
+
this.onerror?.(new Error("Conflict: Only one SSE stream is allowed per session"));
|
|
20740
|
+
return this.createJsonErrorResponse(409, -32000, "Conflict: Only one SSE stream is allowed per session");
|
|
20741
|
+
}
|
|
20742
|
+
const encoder = new TextEncoder;
|
|
20743
|
+
let streamController;
|
|
20744
|
+
const readable = new ReadableStream({
|
|
20745
|
+
start: (controller) => {
|
|
20746
|
+
streamController = controller;
|
|
20747
|
+
},
|
|
20748
|
+
cancel: () => {
|
|
20749
|
+
this._streamMapping.delete(this._standaloneSseStreamId);
|
|
20750
|
+
}
|
|
20751
|
+
});
|
|
20752
|
+
const headers = {
|
|
20753
|
+
"Content-Type": "text/event-stream",
|
|
20754
|
+
"Cache-Control": "no-cache, no-transform",
|
|
20755
|
+
Connection: "keep-alive"
|
|
20756
|
+
};
|
|
20757
|
+
if (this.sessionId !== undefined) {
|
|
20758
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
20759
|
+
}
|
|
20760
|
+
this._streamMapping.set(this._standaloneSseStreamId, {
|
|
20761
|
+
controller: streamController,
|
|
20762
|
+
encoder,
|
|
20763
|
+
cleanup: () => {
|
|
20764
|
+
this._streamMapping.delete(this._standaloneSseStreamId);
|
|
20765
|
+
try {
|
|
20766
|
+
streamController.close();
|
|
20767
|
+
} catch {}
|
|
20768
|
+
}
|
|
20769
|
+
});
|
|
20770
|
+
return new Response(readable, { headers });
|
|
20771
|
+
}
|
|
20772
|
+
async replayEvents(lastEventId) {
|
|
20773
|
+
if (!this._eventStore) {
|
|
20774
|
+
this.onerror?.(new Error("Event store not configured"));
|
|
20775
|
+
return this.createJsonErrorResponse(400, -32000, "Event store not configured");
|
|
20776
|
+
}
|
|
20777
|
+
try {
|
|
20778
|
+
let streamId;
|
|
20779
|
+
if (this._eventStore.getStreamIdForEventId) {
|
|
20780
|
+
streamId = await this._eventStore.getStreamIdForEventId(lastEventId);
|
|
20781
|
+
if (!streamId) {
|
|
20782
|
+
this.onerror?.(new Error("Invalid event ID format"));
|
|
20783
|
+
return this.createJsonErrorResponse(400, -32000, "Invalid event ID format");
|
|
20784
|
+
}
|
|
20785
|
+
if (this._streamMapping.get(streamId) !== undefined) {
|
|
20786
|
+
this.onerror?.(new Error("Conflict: Stream already has an active connection"));
|
|
20787
|
+
return this.createJsonErrorResponse(409, -32000, "Conflict: Stream already has an active connection");
|
|
20788
|
+
}
|
|
20789
|
+
}
|
|
20790
|
+
const headers = {
|
|
20791
|
+
"Content-Type": "text/event-stream",
|
|
20792
|
+
"Cache-Control": "no-cache, no-transform",
|
|
20793
|
+
Connection: "keep-alive"
|
|
20794
|
+
};
|
|
20795
|
+
if (this.sessionId !== undefined) {
|
|
20796
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
20797
|
+
}
|
|
20798
|
+
const encoder = new TextEncoder;
|
|
20799
|
+
let streamController;
|
|
20800
|
+
const readable = new ReadableStream({
|
|
20801
|
+
start: (controller) => {
|
|
20802
|
+
streamController = controller;
|
|
20803
|
+
},
|
|
20804
|
+
cancel: () => {}
|
|
20805
|
+
});
|
|
20806
|
+
const replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, {
|
|
20807
|
+
send: async (eventId, message) => {
|
|
20808
|
+
const success = this.writeSSEEvent(streamController, encoder, message, eventId);
|
|
20809
|
+
if (!success) {
|
|
20810
|
+
this.onerror?.(new Error("Failed replay events"));
|
|
20811
|
+
try {
|
|
20812
|
+
streamController.close();
|
|
20813
|
+
} catch {}
|
|
20814
|
+
}
|
|
20815
|
+
}
|
|
20816
|
+
});
|
|
20817
|
+
this._streamMapping.set(replayedStreamId, {
|
|
20818
|
+
controller: streamController,
|
|
20819
|
+
encoder,
|
|
20820
|
+
cleanup: () => {
|
|
20821
|
+
this._streamMapping.delete(replayedStreamId);
|
|
20822
|
+
try {
|
|
20823
|
+
streamController.close();
|
|
20824
|
+
} catch {}
|
|
20825
|
+
}
|
|
20826
|
+
});
|
|
20827
|
+
return new Response(readable, { headers });
|
|
20828
|
+
} catch (error2) {
|
|
20829
|
+
this.onerror?.(error2);
|
|
20830
|
+
return this.createJsonErrorResponse(500, -32000, "Error replaying events");
|
|
20831
|
+
}
|
|
20832
|
+
}
|
|
20833
|
+
writeSSEEvent(controller, encoder, message, eventId) {
|
|
20834
|
+
try {
|
|
20835
|
+
let eventData = `event: message
|
|
20836
|
+
`;
|
|
20837
|
+
if (eventId) {
|
|
20838
|
+
eventData += `id: ${eventId}
|
|
20839
|
+
`;
|
|
20840
|
+
}
|
|
20841
|
+
eventData += `data: ${JSON.stringify(message)}
|
|
20842
|
+
|
|
20843
|
+
`;
|
|
20844
|
+
controller.enqueue(encoder.encode(eventData));
|
|
20845
|
+
return true;
|
|
20846
|
+
} catch (error2) {
|
|
20847
|
+
this.onerror?.(error2);
|
|
20848
|
+
return false;
|
|
20849
|
+
}
|
|
20850
|
+
}
|
|
20851
|
+
handleUnsupportedRequest() {
|
|
20852
|
+
this.onerror?.(new Error("Method not allowed."));
|
|
20853
|
+
return new Response(JSON.stringify({
|
|
20854
|
+
jsonrpc: "2.0",
|
|
20855
|
+
error: {
|
|
20856
|
+
code: -32000,
|
|
20857
|
+
message: "Method not allowed."
|
|
20858
|
+
},
|
|
20859
|
+
id: null
|
|
20860
|
+
}), {
|
|
20861
|
+
status: 405,
|
|
20862
|
+
headers: {
|
|
20863
|
+
Allow: "GET, POST, DELETE",
|
|
20864
|
+
"Content-Type": "application/json"
|
|
20865
|
+
}
|
|
20866
|
+
});
|
|
20867
|
+
}
|
|
20868
|
+
async handlePostRequest(req, options) {
|
|
20869
|
+
try {
|
|
20870
|
+
const acceptHeader = req.headers.get("accept");
|
|
20871
|
+
if (!acceptHeader?.includes("application/json") || !acceptHeader.includes("text/event-stream")) {
|
|
20872
|
+
this.onerror?.(new Error("Not Acceptable: Client must accept both application/json and text/event-stream"));
|
|
20873
|
+
return this.createJsonErrorResponse(406, -32000, "Not Acceptable: Client must accept both application/json and text/event-stream");
|
|
20874
|
+
}
|
|
20875
|
+
const ct = req.headers.get("content-type");
|
|
20876
|
+
if (!ct || !ct.includes("application/json")) {
|
|
20877
|
+
this.onerror?.(new Error("Unsupported Media Type: Content-Type must be application/json"));
|
|
20878
|
+
return this.createJsonErrorResponse(415, -32000, "Unsupported Media Type: Content-Type must be application/json");
|
|
20879
|
+
}
|
|
20880
|
+
const requestInfo = {
|
|
20881
|
+
headers: Object.fromEntries(req.headers.entries()),
|
|
20882
|
+
url: new URL(req.url)
|
|
20883
|
+
};
|
|
20884
|
+
let rawMessage;
|
|
20885
|
+
if (options?.parsedBody !== undefined) {
|
|
20886
|
+
rawMessage = options.parsedBody;
|
|
20887
|
+
} else {
|
|
20888
|
+
try {
|
|
20889
|
+
rawMessage = await req.json();
|
|
20890
|
+
} catch {
|
|
20891
|
+
this.onerror?.(new Error("Parse error: Invalid JSON"));
|
|
20892
|
+
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON");
|
|
20893
|
+
}
|
|
20894
|
+
}
|
|
20895
|
+
let messages;
|
|
20896
|
+
try {
|
|
20897
|
+
if (Array.isArray(rawMessage)) {
|
|
20898
|
+
messages = rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg));
|
|
20899
|
+
} else {
|
|
20900
|
+
messages = [JSONRPCMessageSchema.parse(rawMessage)];
|
|
20901
|
+
}
|
|
20902
|
+
} catch {
|
|
20903
|
+
this.onerror?.(new Error("Parse error: Invalid JSON-RPC message"));
|
|
20904
|
+
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
|
|
20905
|
+
}
|
|
20906
|
+
const isInitializationRequest = messages.some(isInitializeRequest);
|
|
20907
|
+
if (isInitializationRequest) {
|
|
20908
|
+
if (this._initialized && this.sessionId !== undefined) {
|
|
20909
|
+
this.onerror?.(new Error("Invalid Request: Server already initialized"));
|
|
20910
|
+
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
|
|
20911
|
+
}
|
|
20912
|
+
if (messages.length > 1) {
|
|
20913
|
+
this.onerror?.(new Error("Invalid Request: Only one initialization request is allowed"));
|
|
20914
|
+
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
|
|
20915
|
+
}
|
|
20916
|
+
this.sessionId = this.sessionIdGenerator?.();
|
|
20917
|
+
this._initialized = true;
|
|
20918
|
+
if (this.sessionId && this._onsessioninitialized) {
|
|
20919
|
+
await Promise.resolve(this._onsessioninitialized(this.sessionId));
|
|
20920
|
+
}
|
|
20921
|
+
}
|
|
20922
|
+
if (!isInitializationRequest) {
|
|
20923
|
+
const sessionError = this.validateSession(req);
|
|
20924
|
+
if (sessionError) {
|
|
20925
|
+
return sessionError;
|
|
20926
|
+
}
|
|
20927
|
+
const protocolError = this.validateProtocolVersion(req);
|
|
20928
|
+
if (protocolError) {
|
|
20929
|
+
return protocolError;
|
|
20930
|
+
}
|
|
20931
|
+
}
|
|
20932
|
+
const hasRequests = messages.some(isJSONRPCRequest);
|
|
20933
|
+
if (!hasRequests) {
|
|
20934
|
+
for (const message of messages) {
|
|
20935
|
+
this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo });
|
|
20936
|
+
}
|
|
20937
|
+
return new Response(null, { status: 202 });
|
|
20938
|
+
}
|
|
20939
|
+
const streamId = crypto.randomUUID();
|
|
20940
|
+
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
20941
|
+
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
20942
|
+
if (this._enableJsonResponse) {
|
|
20943
|
+
return new Promise((resolve2) => {
|
|
20944
|
+
this._streamMapping.set(streamId, {
|
|
20945
|
+
resolveJson: resolve2,
|
|
20946
|
+
cleanup: () => {
|
|
20947
|
+
this._streamMapping.delete(streamId);
|
|
20948
|
+
}
|
|
20949
|
+
});
|
|
20950
|
+
for (const message of messages) {
|
|
20951
|
+
if (isJSONRPCRequest(message)) {
|
|
20952
|
+
this._requestToStreamMapping.set(message.id, streamId);
|
|
20953
|
+
}
|
|
20954
|
+
}
|
|
20955
|
+
for (const message of messages) {
|
|
20956
|
+
this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo });
|
|
20957
|
+
}
|
|
20958
|
+
});
|
|
20959
|
+
}
|
|
20960
|
+
const encoder = new TextEncoder;
|
|
20961
|
+
let streamController;
|
|
20962
|
+
const readable = new ReadableStream({
|
|
20963
|
+
start: (controller) => {
|
|
20964
|
+
streamController = controller;
|
|
20965
|
+
},
|
|
20966
|
+
cancel: () => {
|
|
20967
|
+
this._streamMapping.delete(streamId);
|
|
20968
|
+
}
|
|
20969
|
+
});
|
|
20970
|
+
const headers = {
|
|
20971
|
+
"Content-Type": "text/event-stream",
|
|
20972
|
+
"Cache-Control": "no-cache",
|
|
20973
|
+
Connection: "keep-alive"
|
|
20974
|
+
};
|
|
20975
|
+
if (this.sessionId !== undefined) {
|
|
20976
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
20977
|
+
}
|
|
20978
|
+
for (const message of messages) {
|
|
20979
|
+
if (isJSONRPCRequest(message)) {
|
|
20980
|
+
this._streamMapping.set(streamId, {
|
|
20981
|
+
controller: streamController,
|
|
20982
|
+
encoder,
|
|
20983
|
+
cleanup: () => {
|
|
20984
|
+
this._streamMapping.delete(streamId);
|
|
20985
|
+
try {
|
|
20986
|
+
streamController.close();
|
|
20987
|
+
} catch {}
|
|
20988
|
+
}
|
|
20989
|
+
});
|
|
20990
|
+
this._requestToStreamMapping.set(message.id, streamId);
|
|
20991
|
+
}
|
|
20992
|
+
}
|
|
20993
|
+
await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
|
|
20994
|
+
for (const message of messages) {
|
|
20995
|
+
let closeSSEStream;
|
|
20996
|
+
let closeStandaloneSSEStream;
|
|
20997
|
+
if (isJSONRPCRequest(message) && this._eventStore && clientProtocolVersion >= "2025-11-25") {
|
|
20998
|
+
closeSSEStream = () => {
|
|
20999
|
+
this.closeSSEStream(message.id);
|
|
21000
|
+
};
|
|
21001
|
+
closeStandaloneSSEStream = () => {
|
|
21002
|
+
this.closeStandaloneSSEStream();
|
|
21003
|
+
};
|
|
21004
|
+
}
|
|
21005
|
+
this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo, closeSSEStream, closeStandaloneSSEStream });
|
|
21006
|
+
}
|
|
21007
|
+
return new Response(readable, { status: 200, headers });
|
|
21008
|
+
} catch (error2) {
|
|
21009
|
+
this.onerror?.(error2);
|
|
21010
|
+
return this.createJsonErrorResponse(400, -32700, "Parse error", { data: String(error2) });
|
|
21011
|
+
}
|
|
21012
|
+
}
|
|
21013
|
+
async handleDeleteRequest(req) {
|
|
21014
|
+
const sessionError = this.validateSession(req);
|
|
21015
|
+
if (sessionError) {
|
|
21016
|
+
return sessionError;
|
|
21017
|
+
}
|
|
21018
|
+
const protocolError = this.validateProtocolVersion(req);
|
|
21019
|
+
if (protocolError) {
|
|
21020
|
+
return protocolError;
|
|
21021
|
+
}
|
|
21022
|
+
await Promise.resolve(this._onsessionclosed?.(this.sessionId));
|
|
21023
|
+
await this.close();
|
|
21024
|
+
return new Response(null, { status: 200 });
|
|
21025
|
+
}
|
|
21026
|
+
validateSession(req) {
|
|
21027
|
+
if (this.sessionIdGenerator === undefined) {
|
|
21028
|
+
return;
|
|
21029
|
+
}
|
|
21030
|
+
if (!this._initialized) {
|
|
21031
|
+
this.onerror?.(new Error("Bad Request: Server not initialized"));
|
|
21032
|
+
return this.createJsonErrorResponse(400, -32000, "Bad Request: Server not initialized");
|
|
21033
|
+
}
|
|
21034
|
+
const sessionId = req.headers.get("mcp-session-id");
|
|
21035
|
+
if (!sessionId) {
|
|
21036
|
+
this.onerror?.(new Error("Bad Request: Mcp-Session-Id header is required"));
|
|
21037
|
+
return this.createJsonErrorResponse(400, -32000, "Bad Request: Mcp-Session-Id header is required");
|
|
21038
|
+
}
|
|
21039
|
+
if (sessionId !== this.sessionId) {
|
|
21040
|
+
this.onerror?.(new Error("Session not found"));
|
|
21041
|
+
return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
21042
|
+
}
|
|
21043
|
+
return;
|
|
21044
|
+
}
|
|
21045
|
+
validateProtocolVersion(req) {
|
|
21046
|
+
const protocolVersion = req.headers.get("mcp-protocol-version");
|
|
21047
|
+
if (protocolVersion !== null && !SUPPORTED_PROTOCOL_VERSIONS.includes(protocolVersion)) {
|
|
21048
|
+
this.onerror?.(new Error(`Bad Request: Unsupported protocol version: ${protocolVersion}` + ` (supported versions: ${SUPPORTED_PROTOCOL_VERSIONS.join(", ")})`));
|
|
21049
|
+
return this.createJsonErrorResponse(400, -32000, `Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${SUPPORTED_PROTOCOL_VERSIONS.join(", ")})`);
|
|
21050
|
+
}
|
|
21051
|
+
return;
|
|
21052
|
+
}
|
|
21053
|
+
async close() {
|
|
21054
|
+
this._streamMapping.forEach(({ cleanup }) => {
|
|
21055
|
+
cleanup();
|
|
21056
|
+
});
|
|
21057
|
+
this._streamMapping.clear();
|
|
21058
|
+
this._requestResponseMap.clear();
|
|
21059
|
+
this.onclose?.();
|
|
21060
|
+
}
|
|
21061
|
+
closeSSEStream(requestId) {
|
|
21062
|
+
const streamId = this._requestToStreamMapping.get(requestId);
|
|
21063
|
+
if (!streamId)
|
|
21064
|
+
return;
|
|
21065
|
+
const stream = this._streamMapping.get(streamId);
|
|
21066
|
+
if (stream) {
|
|
21067
|
+
stream.cleanup();
|
|
21068
|
+
}
|
|
21069
|
+
}
|
|
21070
|
+
closeStandaloneSSEStream() {
|
|
21071
|
+
const stream = this._streamMapping.get(this._standaloneSseStreamId);
|
|
21072
|
+
if (stream) {
|
|
21073
|
+
stream.cleanup();
|
|
21074
|
+
}
|
|
21075
|
+
}
|
|
21076
|
+
async send(message, options) {
|
|
21077
|
+
let requestId = options?.relatedRequestId;
|
|
21078
|
+
if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
|
|
21079
|
+
requestId = message.id;
|
|
21080
|
+
}
|
|
21081
|
+
if (requestId === undefined) {
|
|
21082
|
+
if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
|
|
21083
|
+
throw new Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");
|
|
21084
|
+
}
|
|
21085
|
+
let eventId;
|
|
21086
|
+
if (this._eventStore) {
|
|
21087
|
+
eventId = await this._eventStore.storeEvent(this._standaloneSseStreamId, message);
|
|
21088
|
+
}
|
|
21089
|
+
const standaloneSse = this._streamMapping.get(this._standaloneSseStreamId);
|
|
21090
|
+
if (standaloneSse === undefined) {
|
|
21091
|
+
return;
|
|
21092
|
+
}
|
|
21093
|
+
if (standaloneSse.controller && standaloneSse.encoder) {
|
|
21094
|
+
this.writeSSEEvent(standaloneSse.controller, standaloneSse.encoder, message, eventId);
|
|
21095
|
+
}
|
|
21096
|
+
return;
|
|
21097
|
+
}
|
|
21098
|
+
const streamId = this._requestToStreamMapping.get(requestId);
|
|
21099
|
+
if (!streamId) {
|
|
21100
|
+
throw new Error(`No connection established for request ID: ${String(requestId)}`);
|
|
21101
|
+
}
|
|
21102
|
+
const stream = this._streamMapping.get(streamId);
|
|
21103
|
+
if (!this._enableJsonResponse && stream?.controller && stream?.encoder) {
|
|
21104
|
+
let eventId;
|
|
21105
|
+
if (this._eventStore) {
|
|
21106
|
+
eventId = await this._eventStore.storeEvent(streamId, message);
|
|
21107
|
+
}
|
|
21108
|
+
this.writeSSEEvent(stream.controller, stream.encoder, message, eventId);
|
|
21109
|
+
}
|
|
21110
|
+
if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
|
|
21111
|
+
this._requestResponseMap.set(requestId, message);
|
|
21112
|
+
const relatedIds = Array.from(this._requestToStreamMapping.entries()).filter(([_, sid]) => sid === streamId).map(([id]) => id);
|
|
21113
|
+
const allResponsesReady = relatedIds.every((id) => this._requestResponseMap.has(id));
|
|
21114
|
+
if (allResponsesReady) {
|
|
21115
|
+
if (!stream) {
|
|
21116
|
+
throw new Error(`No connection established for request ID: ${String(requestId)}`);
|
|
21117
|
+
}
|
|
21118
|
+
if (this._enableJsonResponse && stream.resolveJson) {
|
|
21119
|
+
const headers = {
|
|
21120
|
+
"Content-Type": "application/json"
|
|
21121
|
+
};
|
|
21122
|
+
if (this.sessionId !== undefined) {
|
|
21123
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
21124
|
+
}
|
|
21125
|
+
const responses = relatedIds.map((id) => this._requestResponseMap.get(id));
|
|
21126
|
+
if (responses.length === 1) {
|
|
21127
|
+
stream.resolveJson(new Response(JSON.stringify(responses[0]), { status: 200, headers }));
|
|
21128
|
+
} else {
|
|
21129
|
+
stream.resolveJson(new Response(JSON.stringify(responses), { status: 200, headers }));
|
|
21130
|
+
}
|
|
21131
|
+
} else {
|
|
21132
|
+
stream.cleanup();
|
|
21133
|
+
}
|
|
21134
|
+
for (const id of relatedIds) {
|
|
21135
|
+
this._requestResponseMap.delete(id);
|
|
21136
|
+
this._requestToStreamMapping.delete(id);
|
|
21137
|
+
}
|
|
21138
|
+
}
|
|
21139
|
+
}
|
|
21140
|
+
}
|
|
21141
|
+
}
|
|
21142
|
+
|
|
21143
|
+
// node_modules/.bun/@modelcontextprotocol+sdk@1.27.1/node_modules/@modelcontextprotocol/sdk/dist/esm/server/streamableHttp.js
|
|
21144
|
+
class StreamableHTTPServerTransport {
|
|
21145
|
+
constructor(options = {}) {
|
|
21146
|
+
this._requestContext = new WeakMap;
|
|
21147
|
+
this._webStandardTransport = new WebStandardStreamableHTTPServerTransport(options);
|
|
21148
|
+
this._requestListener = getRequestListener(async (webRequest) => {
|
|
21149
|
+
const context = this._requestContext.get(webRequest);
|
|
21150
|
+
return this._webStandardTransport.handleRequest(webRequest, {
|
|
21151
|
+
authInfo: context?.authInfo,
|
|
21152
|
+
parsedBody: context?.parsedBody
|
|
21153
|
+
});
|
|
21154
|
+
}, { overrideGlobalObjects: false });
|
|
21155
|
+
}
|
|
21156
|
+
get sessionId() {
|
|
21157
|
+
return this._webStandardTransport.sessionId;
|
|
21158
|
+
}
|
|
21159
|
+
set onclose(handler) {
|
|
21160
|
+
this._webStandardTransport.onclose = handler;
|
|
21161
|
+
}
|
|
21162
|
+
get onclose() {
|
|
21163
|
+
return this._webStandardTransport.onclose;
|
|
21164
|
+
}
|
|
21165
|
+
set onerror(handler) {
|
|
21166
|
+
this._webStandardTransport.onerror = handler;
|
|
21167
|
+
}
|
|
21168
|
+
get onerror() {
|
|
21169
|
+
return this._webStandardTransport.onerror;
|
|
21170
|
+
}
|
|
21171
|
+
set onmessage(handler) {
|
|
21172
|
+
this._webStandardTransport.onmessage = handler;
|
|
21173
|
+
}
|
|
21174
|
+
get onmessage() {
|
|
21175
|
+
return this._webStandardTransport.onmessage;
|
|
21176
|
+
}
|
|
21177
|
+
async start() {
|
|
21178
|
+
return this._webStandardTransport.start();
|
|
21179
|
+
}
|
|
21180
|
+
async close() {
|
|
21181
|
+
return this._webStandardTransport.close();
|
|
21182
|
+
}
|
|
21183
|
+
async send(message, options) {
|
|
21184
|
+
return this._webStandardTransport.send(message, options);
|
|
21185
|
+
}
|
|
21186
|
+
async handleRequest(req, res, parsedBody) {
|
|
21187
|
+
const authInfo = req.auth;
|
|
21188
|
+
const handler = getRequestListener(async (webRequest) => {
|
|
21189
|
+
return this._webStandardTransport.handleRequest(webRequest, {
|
|
21190
|
+
authInfo,
|
|
21191
|
+
parsedBody
|
|
21192
|
+
});
|
|
21193
|
+
}, { overrideGlobalObjects: false });
|
|
21194
|
+
await handler(req, res);
|
|
21195
|
+
}
|
|
21196
|
+
closeSSEStream(requestId) {
|
|
21197
|
+
this._webStandardTransport.closeSSEStream(requestId);
|
|
21198
|
+
}
|
|
21199
|
+
closeStandaloneSSEStream() {
|
|
21200
|
+
this._webStandardTransport.closeStandaloneSSEStream();
|
|
21201
|
+
}
|
|
21202
|
+
}
|
|
21203
|
+
|
|
21204
|
+
// src/mcp/http.ts
|
|
21205
|
+
var MCP_HTTP_SERVICE_NAME = "microservices";
|
|
21206
|
+
var DEFAULT_MCP_HTTP_PORT = 8825;
|
|
21207
|
+
function isHttpMode(argv = process.argv, env = process.env) {
|
|
21208
|
+
return argv.includes("--http") || env.MCP_HTTP === "1";
|
|
21209
|
+
}
|
|
21210
|
+
function resolveMcpHttpPort(argv = process.argv, env = process.env) {
|
|
21211
|
+
const portIdx = argv.indexOf("--port");
|
|
21212
|
+
if (portIdx !== -1 && argv[portIdx + 1]) {
|
|
21213
|
+
return parsePort(argv[portIdx + 1], "--port");
|
|
21214
|
+
}
|
|
21215
|
+
if (env.MCP_HTTP_PORT) {
|
|
21216
|
+
return parsePort(env.MCP_HTTP_PORT, "MCP_HTTP_PORT");
|
|
21217
|
+
}
|
|
21218
|
+
return DEFAULT_MCP_HTTP_PORT;
|
|
21219
|
+
}
|
|
21220
|
+
function parsePort(raw, source) {
|
|
21221
|
+
const parsed = Number(raw);
|
|
21222
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
21223
|
+
throw new Error(`Invalid ${source} value "${raw}". Expected 0-65535.`);
|
|
21224
|
+
}
|
|
21225
|
+
return parsed;
|
|
21226
|
+
}
|
|
21227
|
+
async function readJsonBody(req) {
|
|
21228
|
+
const chunks = [];
|
|
21229
|
+
for await (const chunk of req) {
|
|
21230
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
21231
|
+
}
|
|
21232
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
21233
|
+
if (!text)
|
|
21234
|
+
return;
|
|
21235
|
+
return JSON.parse(text);
|
|
21236
|
+
}
|
|
21237
|
+
async function startMcpHttpServer(buildServer, options) {
|
|
21238
|
+
const host = options?.host ?? "127.0.0.1";
|
|
21239
|
+
const requestedPort = options?.port ?? resolveMcpHttpPort();
|
|
21240
|
+
const serviceName = options?.serviceName ?? MCP_HTTP_SERVICE_NAME;
|
|
21241
|
+
const httpServer = createServer(async (req, res) => {
|
|
21242
|
+
try {
|
|
21243
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
21244
|
+
if (req.method === "GET" && url.pathname === "/health") {
|
|
21245
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
21246
|
+
res.end(JSON.stringify({ status: "ok", name: serviceName }));
|
|
21247
|
+
return;
|
|
20101
21248
|
}
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
title: "Search Microservices",
|
|
20107
|
-
description: "Search microservices by name, description, or tags.",
|
|
20108
|
-
inputSchema: { query: exports_external.string() }
|
|
20109
|
-
}, async ({ query }) => {
|
|
20110
|
-
const results = searchMicroservices(query);
|
|
20111
|
-
return {
|
|
20112
|
-
content: [
|
|
20113
|
-
{
|
|
20114
|
-
type: "text",
|
|
20115
|
-
text: JSON.stringify(results.map((m) => ({
|
|
20116
|
-
name: m.name,
|
|
20117
|
-
package: m.package,
|
|
20118
|
-
description: m.description,
|
|
20119
|
-
category: m.category,
|
|
20120
|
-
tags: m.tags
|
|
20121
|
-
})), null, 2)
|
|
21249
|
+
if (url.pathname !== "/mcp") {
|
|
21250
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
21251
|
+
res.end("Not Found");
|
|
21252
|
+
return;
|
|
20122
21253
|
}
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
21254
|
+
const server = buildServer();
|
|
21255
|
+
const transport = new StreamableHTTPServerTransport({
|
|
21256
|
+
sessionIdGenerator: undefined
|
|
21257
|
+
});
|
|
21258
|
+
await server.connect(transport);
|
|
21259
|
+
let parsedBody;
|
|
21260
|
+
if (req.method === "POST") {
|
|
21261
|
+
parsedBody = await readJsonBody(req);
|
|
21262
|
+
}
|
|
21263
|
+
await transport.handleRequest(req, res, parsedBody);
|
|
21264
|
+
res.on("close", () => {
|
|
21265
|
+
transport.close();
|
|
21266
|
+
server.close();
|
|
21267
|
+
});
|
|
21268
|
+
} catch (error2) {
|
|
21269
|
+
console.error(`[${serviceName}-mcp] HTTP error:`, error2);
|
|
21270
|
+
if (!res.headersSent) {
|
|
21271
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
21272
|
+
res.end(JSON.stringify({
|
|
21273
|
+
jsonrpc: "2.0",
|
|
21274
|
+
error: { code: -32603, message: "Internal server error" },
|
|
21275
|
+
id: null
|
|
21276
|
+
}));
|
|
20137
21277
|
}
|
|
20138
|
-
|
|
21278
|
+
}
|
|
21279
|
+
});
|
|
21280
|
+
await new Promise((resolve2, reject) => {
|
|
21281
|
+
httpServer.once("error", reject);
|
|
21282
|
+
httpServer.listen(requestedPort, host, () => resolve2());
|
|
21283
|
+
});
|
|
21284
|
+
const addr = httpServer.address();
|
|
21285
|
+
const port = typeof addr === "object" && addr ? addr.port : requestedPort;
|
|
21286
|
+
console.error(`[${serviceName}-mcp] Streamable HTTP listening on http://${host}:${port}/mcp`);
|
|
21287
|
+
return {
|
|
21288
|
+
port,
|
|
21289
|
+
host,
|
|
21290
|
+
close: () => new Promise((resolve2, reject) => {
|
|
21291
|
+
httpServer.close((err) => err ? reject(err) : resolve2());
|
|
21292
|
+
})
|
|
20139
21293
|
};
|
|
20140
|
-
}
|
|
20141
|
-
|
|
20142
|
-
|
|
20143
|
-
|
|
20144
|
-
|
|
20145
|
-
}
|
|
20146
|
-
|
|
21294
|
+
}
|
|
21295
|
+
|
|
21296
|
+
// src/mcp/index.ts
|
|
21297
|
+
function hasFlag(...flags) {
|
|
21298
|
+
return process.argv.some((arg) => flags.includes(arg));
|
|
21299
|
+
}
|
|
21300
|
+
function printHelp() {
|
|
21301
|
+
process.stdout.write(`Usage: microservices-mcp [options]
|
|
21302
|
+
|
|
21303
|
+
Microservices MCP server (stdio transport by default)
|
|
21304
|
+
|
|
21305
|
+
Options:
|
|
21306
|
+
--http Serve MCP over Streamable HTTP (127.0.0.1)
|
|
21307
|
+
--port <number> HTTP port (default: 8825, env: MCP_HTTP_PORT)
|
|
21308
|
+
-h, --help Show help
|
|
21309
|
+
-V, --version Show version
|
|
21310
|
+
`);
|
|
21311
|
+
}
|
|
21312
|
+
function buildServer() {
|
|
21313
|
+
const server = new McpServer({
|
|
21314
|
+
name: "microservices",
|
|
21315
|
+
version: getPackageVersion()
|
|
21316
|
+
});
|
|
21317
|
+
server.registerTool("list_microservices", {
|
|
21318
|
+
title: "List Microservices",
|
|
21319
|
+
description: "List all 21 available production microservices (auth, teams, billing, llm, agents, memory, etc).",
|
|
21320
|
+
inputSchema: { installed_only: exports_external.boolean().optional() }
|
|
21321
|
+
}, async ({ installed_only }) => {
|
|
21322
|
+
const services = installed_only ? MICROSERVICES.filter((m) => microserviceExists(m.name)) : MICROSERVICES;
|
|
20147
21323
|
return {
|
|
20148
21324
|
content: [
|
|
20149
21325
|
{
|
|
20150
21326
|
type: "text",
|
|
20151
|
-
text: JSON.stringify(
|
|
21327
|
+
text: JSON.stringify(services.map((m) => ({
|
|
21328
|
+
name: m.name,
|
|
21329
|
+
package: m.package,
|
|
21330
|
+
binary: m.binary,
|
|
21331
|
+
category: m.category,
|
|
21332
|
+
description: m.description,
|
|
21333
|
+
schemaPrefix: m.schemaPrefix,
|
|
21334
|
+
installed: microserviceExists(m.name),
|
|
21335
|
+
requiredEnv: m.requiredEnv
|
|
21336
|
+
})), null, 2)
|
|
20152
21337
|
}
|
|
20153
21338
|
]
|
|
20154
21339
|
};
|
|
20155
|
-
}
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
|
|
20159
|
-
|
|
20160
|
-
})
|
|
20161
|
-
|
|
20162
|
-
title: "Run Microservice Command",
|
|
20163
|
-
description: "Run a CLI command on an installed microservice. Example: run migrate, serve, status.",
|
|
20164
|
-
inputSchema: {
|
|
20165
|
-
name: exports_external.string().describe("Microservice name (e.g. 'auth', 'billing')"),
|
|
20166
|
-
args: exports_external.array(exports_external.string()).describe("CLI arguments (e.g. ['migrate'] or ['status'])")
|
|
20167
|
-
}
|
|
20168
|
-
}, async ({ name, args }) => {
|
|
20169
|
-
const result = await runMicroserviceCommand(name, args);
|
|
20170
|
-
return {
|
|
20171
|
-
content: [
|
|
20172
|
-
{
|
|
20173
|
-
type: "text",
|
|
20174
|
-
text: JSON.stringify({
|
|
20175
|
-
success: result.success,
|
|
20176
|
-
exitCode: result.exitCode,
|
|
20177
|
-
stdout: result.stdout,
|
|
20178
|
-
stderr: result.stderr
|
|
20179
|
-
}, null, 2)
|
|
20180
|
-
}
|
|
20181
|
-
]
|
|
20182
|
-
};
|
|
20183
|
-
});
|
|
20184
|
-
server.registerTool("check_env", {
|
|
20185
|
-
title: "Check Environment Variables",
|
|
20186
|
-
description: "Verify if all required and optional environment variables are set for installed microservices.",
|
|
20187
|
-
inputSchema: {}
|
|
20188
|
-
}, async () => {
|
|
20189
|
-
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
20190
|
-
if (installed.length === 0) {
|
|
21340
|
+
});
|
|
21341
|
+
server.registerTool("search_microservices", {
|
|
21342
|
+
title: "Search Microservices",
|
|
21343
|
+
description: "Search microservices by name, description, or tags.",
|
|
21344
|
+
inputSchema: { query: exports_external.string() }
|
|
21345
|
+
}, async ({ query }) => {
|
|
21346
|
+
const results = searchMicroservices(query);
|
|
20191
21347
|
return {
|
|
20192
21348
|
content: [
|
|
20193
|
-
{
|
|
21349
|
+
{
|
|
21350
|
+
type: "text",
|
|
21351
|
+
text: JSON.stringify(results.map((m) => ({
|
|
21352
|
+
name: m.name,
|
|
21353
|
+
package: m.package,
|
|
21354
|
+
description: m.description,
|
|
21355
|
+
category: m.category,
|
|
21356
|
+
tags: m.tags
|
|
21357
|
+
})), null, 2)
|
|
21358
|
+
}
|
|
20194
21359
|
]
|
|
20195
21360
|
};
|
|
20196
|
-
}
|
|
20197
|
-
|
|
20198
|
-
|
|
20199
|
-
|
|
21361
|
+
});
|
|
21362
|
+
server.registerTool("install_microservice", {
|
|
21363
|
+
title: "Install Microservice",
|
|
21364
|
+
description: "Install a microservice globally via bun install -g.",
|
|
21365
|
+
inputSchema: { name: exports_external.string(), force: exports_external.boolean().optional() }
|
|
21366
|
+
}, async ({ name, force }) => {
|
|
21367
|
+
const result = installMicroservice(name, { force });
|
|
20200
21368
|
return {
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
|
|
20204
|
-
|
|
21369
|
+
content: [
|
|
21370
|
+
{
|
|
21371
|
+
type: "text",
|
|
21372
|
+
text: JSON.stringify(result, null, 2)
|
|
21373
|
+
}
|
|
21374
|
+
]
|
|
20205
21375
|
};
|
|
20206
21376
|
});
|
|
20207
|
-
|
|
20208
|
-
|
|
20209
|
-
|
|
20210
|
-
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
|
|
20216
|
-
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
21377
|
+
server.registerTool("microservice_status", {
|
|
21378
|
+
title: "Microservice Status",
|
|
21379
|
+
description: "Check if a microservice is installed and get its version.",
|
|
21380
|
+
inputSchema: { name: exports_external.string().optional() }
|
|
21381
|
+
}, async ({ name }) => {
|
|
21382
|
+
if (name) {
|
|
21383
|
+
return {
|
|
21384
|
+
content: [
|
|
21385
|
+
{
|
|
21386
|
+
type: "text",
|
|
21387
|
+
text: JSON.stringify(getMicroserviceStatus(name), null, 2)
|
|
21388
|
+
}
|
|
21389
|
+
]
|
|
21390
|
+
};
|
|
21391
|
+
}
|
|
21392
|
+
const statuses = MICROSERVICES.map((m) => getMicroserviceStatus(m.name));
|
|
21393
|
+
return {
|
|
21394
|
+
content: [{ type: "text", text: JSON.stringify(statuses, null, 2) }]
|
|
21395
|
+
};
|
|
21396
|
+
});
|
|
21397
|
+
server.registerTool("run_microservice_command", {
|
|
21398
|
+
title: "Run Microservice Command",
|
|
21399
|
+
description: "Run a CLI command on an installed microservice. Example: run migrate, serve, status.",
|
|
21400
|
+
inputSchema: {
|
|
21401
|
+
name: exports_external.string().describe("Microservice name (e.g. 'auth', 'billing')"),
|
|
21402
|
+
args: exports_external.array(exports_external.string()).describe("CLI arguments (e.g. ['migrate'] or ['status'])")
|
|
21403
|
+
}
|
|
21404
|
+
}, async ({ name, args }) => {
|
|
21405
|
+
const result = await runMicroserviceCommand(name, args);
|
|
20231
21406
|
return {
|
|
20232
21407
|
content: [
|
|
20233
21408
|
{
|
|
20234
21409
|
type: "text",
|
|
20235
21410
|
text: JSON.stringify({
|
|
20236
|
-
success:
|
|
20237
|
-
|
|
21411
|
+
success: result.success,
|
|
21412
|
+
exitCode: result.exitCode,
|
|
21413
|
+
stdout: result.stdout,
|
|
21414
|
+
stderr: result.stderr
|
|
20238
21415
|
}, null, 2)
|
|
20239
21416
|
}
|
|
20240
21417
|
]
|
|
20241
21418
|
};
|
|
20242
|
-
}
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
const
|
|
20249
|
-
if (
|
|
21419
|
+
});
|
|
21420
|
+
server.registerTool("check_env", {
|
|
21421
|
+
title: "Check Environment Variables",
|
|
21422
|
+
description: "Verify if all required and optional environment variables are set for installed microservices.",
|
|
21423
|
+
inputSchema: {}
|
|
21424
|
+
}, async () => {
|
|
21425
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
21426
|
+
if (installed.length === 0) {
|
|
21427
|
+
return {
|
|
21428
|
+
content: [
|
|
21429
|
+
{ type: "text", text: "No microservices installed to check." }
|
|
21430
|
+
]
|
|
21431
|
+
};
|
|
21432
|
+
}
|
|
21433
|
+
const report = installed.map((m) => {
|
|
21434
|
+
const missingRequired = m.requiredEnv.filter((env) => !process.env[env]);
|
|
21435
|
+
const missingOptional = (m.optionalEnv ?? []).filter((env) => !process.env[env]);
|
|
21436
|
+
return {
|
|
21437
|
+
name: m.name,
|
|
21438
|
+
ok: missingRequired.length === 0,
|
|
21439
|
+
missing_required: missingRequired,
|
|
21440
|
+
missing_optional: missingOptional
|
|
21441
|
+
};
|
|
21442
|
+
});
|
|
21443
|
+
return {
|
|
21444
|
+
content: [
|
|
21445
|
+
{
|
|
21446
|
+
type: "text",
|
|
21447
|
+
text: JSON.stringify({
|
|
21448
|
+
summary: {
|
|
21449
|
+
installed: installed.length,
|
|
21450
|
+
total_missing_required: report.reduce((acc, r) => acc + r.missing_required.length, 0),
|
|
21451
|
+
all_ok: report.every((r) => r.ok)
|
|
21452
|
+
},
|
|
21453
|
+
services: report
|
|
21454
|
+
}, null, 2)
|
|
21455
|
+
}
|
|
21456
|
+
]
|
|
21457
|
+
};
|
|
21458
|
+
});
|
|
21459
|
+
server.registerTool("scaffold_microservice", {
|
|
21460
|
+
title: "Scaffold Microservice",
|
|
21461
|
+
description: "Scaffold a new microservice from the _template directory.",
|
|
21462
|
+
inputSchema: {
|
|
21463
|
+
name: exports_external.string().describe("The name of the new microservice (lowercase, dashes only)")
|
|
21464
|
+
}
|
|
21465
|
+
}, async ({ name }) => {
|
|
21466
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
20250
21467
|
return {
|
|
20251
21468
|
content: [
|
|
20252
21469
|
{
|
|
20253
21470
|
type: "text",
|
|
20254
21471
|
text: JSON.stringify({
|
|
20255
21472
|
success: false,
|
|
20256
|
-
error:
|
|
21473
|
+
error: "Name can only contain lowercase letters, numbers, and dashes."
|
|
20257
21474
|
}, null, 2)
|
|
20258
21475
|
}
|
|
20259
21476
|
]
|
|
20260
21477
|
};
|
|
20261
21478
|
}
|
|
20262
|
-
|
|
21479
|
+
try {
|
|
21480
|
+
const fs = await import("fs");
|
|
21481
|
+
const path = await import("path");
|
|
21482
|
+
const cwd = process.cwd();
|
|
21483
|
+
const templateDir = path.join(cwd, "microservices", "_template");
|
|
21484
|
+
const targetDir = path.join(cwd, "microservices", `microservice-${name}`);
|
|
21485
|
+
if (!fs.existsSync(templateDir)) {
|
|
21486
|
+
return {
|
|
21487
|
+
content: [
|
|
21488
|
+
{
|
|
21489
|
+
type: "text",
|
|
21490
|
+
text: JSON.stringify({
|
|
21491
|
+
success: false,
|
|
21492
|
+
error: `Template directory not found at ${templateDir}.`
|
|
21493
|
+
}, null, 2)
|
|
21494
|
+
}
|
|
21495
|
+
]
|
|
21496
|
+
};
|
|
21497
|
+
}
|
|
21498
|
+
if (fs.existsSync(targetDir)) {
|
|
21499
|
+
return {
|
|
21500
|
+
content: [
|
|
21501
|
+
{
|
|
21502
|
+
type: "text",
|
|
21503
|
+
text: JSON.stringify({
|
|
21504
|
+
success: false,
|
|
21505
|
+
error: `Target directory already exists at ${targetDir}`
|
|
21506
|
+
}, null, 2)
|
|
21507
|
+
}
|
|
21508
|
+
]
|
|
21509
|
+
};
|
|
21510
|
+
}
|
|
21511
|
+
fs.cpSync(templateDir, targetDir, { recursive: true });
|
|
21512
|
+
const replaceInFile = (filePath) => {
|
|
21513
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
21514
|
+
let updated = content.replace(/__NAME__/g, name.toUpperCase().replace(/-/g, "_"));
|
|
21515
|
+
updated = updated.replace(/__Name__/g, name.charAt(0).toUpperCase() + name.slice(1));
|
|
21516
|
+
updated = updated.replace(/__name__/g, name);
|
|
21517
|
+
fs.writeFileSync(filePath, updated, "utf-8");
|
|
21518
|
+
};
|
|
21519
|
+
const walkAndReplace = (dir) => {
|
|
21520
|
+
const files = fs.readdirSync(dir);
|
|
21521
|
+
for (const file of files) {
|
|
21522
|
+
const filePath = path.join(dir, file);
|
|
21523
|
+
if (fs.statSync(filePath).isDirectory()) {
|
|
21524
|
+
walkAndReplace(filePath);
|
|
21525
|
+
} else {
|
|
21526
|
+
replaceInFile(filePath);
|
|
21527
|
+
}
|
|
21528
|
+
}
|
|
21529
|
+
};
|
|
21530
|
+
walkAndReplace(targetDir);
|
|
20263
21531
|
return {
|
|
20264
21532
|
content: [
|
|
20265
21533
|
{
|
|
20266
21534
|
type: "text",
|
|
20267
21535
|
text: JSON.stringify({
|
|
20268
|
-
success:
|
|
20269
|
-
|
|
21536
|
+
success: true,
|
|
21537
|
+
message: `Created microservices/microservice-${name}`
|
|
20270
21538
|
}, null, 2)
|
|
20271
21539
|
}
|
|
20272
21540
|
]
|
|
20273
21541
|
};
|
|
21542
|
+
} catch (err) {
|
|
21543
|
+
return {
|
|
21544
|
+
content: [
|
|
21545
|
+
{
|
|
21546
|
+
type: "text",
|
|
21547
|
+
text: JSON.stringify({ success: false, error: String(err) }, null, 2)
|
|
21548
|
+
}
|
|
21549
|
+
]
|
|
21550
|
+
};
|
|
21551
|
+
}
|
|
21552
|
+
});
|
|
21553
|
+
server.registerTool("init_all_microservices", {
|
|
21554
|
+
title: "Init All Microservices",
|
|
21555
|
+
description: "Run migrations and confirm setup for all installed microservices.",
|
|
21556
|
+
inputSchema: { db: exports_external.string().describe("PostgreSQL connection URL") }
|
|
21557
|
+
}, async ({ db }) => {
|
|
21558
|
+
process.env.DATABASE_URL = db;
|
|
21559
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
21560
|
+
if (installed.length === 0) {
|
|
21561
|
+
return {
|
|
21562
|
+
content: [
|
|
21563
|
+
{ type: "text", text: "No microservices installed to init." }
|
|
21564
|
+
]
|
|
21565
|
+
};
|
|
21566
|
+
}
|
|
21567
|
+
const results = [];
|
|
21568
|
+
let hasErrors = false;
|
|
21569
|
+
for (const m of installed) {
|
|
21570
|
+
const res = await runMicroserviceCommand(m.name, ["init", "--db", db]);
|
|
21571
|
+
results.push({
|
|
21572
|
+
name: m.name,
|
|
21573
|
+
success: res.success,
|
|
21574
|
+
output: res.stdout || res.stderr
|
|
21575
|
+
});
|
|
21576
|
+
if (!res.success)
|
|
21577
|
+
hasErrors = true;
|
|
20274
21578
|
}
|
|
20275
|
-
fs.cpSync(templateDir, targetDir, { recursive: true });
|
|
20276
|
-
const replaceInFile = (filePath) => {
|
|
20277
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
20278
|
-
let updated = content.replace(/__NAME__/g, name.toUpperCase().replace(/-/g, "_"));
|
|
20279
|
-
updated = updated.replace(/__Name__/g, name.charAt(0).toUpperCase() + name.slice(1));
|
|
20280
|
-
updated = updated.replace(/__name__/g, name);
|
|
20281
|
-
fs.writeFileSync(filePath, updated, "utf-8");
|
|
20282
|
-
};
|
|
20283
|
-
const walkAndReplace = (dir) => {
|
|
20284
|
-
const files = fs.readdirSync(dir);
|
|
20285
|
-
for (const file of files) {
|
|
20286
|
-
const filePath = path.join(dir, file);
|
|
20287
|
-
if (fs.statSync(filePath).isDirectory()) {
|
|
20288
|
-
walkAndReplace(filePath);
|
|
20289
|
-
} else {
|
|
20290
|
-
replaceInFile(filePath);
|
|
20291
|
-
}
|
|
20292
|
-
}
|
|
20293
|
-
};
|
|
20294
|
-
walkAndReplace(targetDir);
|
|
20295
21579
|
return {
|
|
20296
21580
|
content: [
|
|
20297
21581
|
{
|
|
20298
21582
|
type: "text",
|
|
20299
|
-
text: JSON.stringify({
|
|
20300
|
-
success: true,
|
|
20301
|
-
message: `Created microservices/microservice-${name}`
|
|
20302
|
-
}, null, 2)
|
|
21583
|
+
text: JSON.stringify({ success: !hasErrors, initialized: installed.length, results }, null, 2)
|
|
20303
21584
|
}
|
|
20304
21585
|
]
|
|
20305
21586
|
};
|
|
20306
|
-
}
|
|
21587
|
+
});
|
|
21588
|
+
server.registerTool("migrate_all_microservices", {
|
|
21589
|
+
title: "Migrate All Microservices",
|
|
21590
|
+
description: "Run database migrations for all installed microservices.",
|
|
21591
|
+
inputSchema: {
|
|
21592
|
+
db: exports_external.string().optional().describe("PostgreSQL connection URL (overrides DATABASE_URL)")
|
|
21593
|
+
}
|
|
21594
|
+
}, async ({ db }) => {
|
|
21595
|
+
if (db)
|
|
21596
|
+
process.env.DATABASE_URL = db;
|
|
21597
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
21598
|
+
if (installed.length === 0) {
|
|
21599
|
+
return {
|
|
21600
|
+
content: [
|
|
21601
|
+
{ type: "text", text: "No microservices installed to migrate." }
|
|
21602
|
+
]
|
|
21603
|
+
};
|
|
21604
|
+
}
|
|
21605
|
+
const results = [];
|
|
21606
|
+
let hasErrors = false;
|
|
21607
|
+
for (const m of installed) {
|
|
21608
|
+
const res = await runMicroserviceCommand(m.name, ["migrate"]);
|
|
21609
|
+
results.push({
|
|
21610
|
+
name: m.name,
|
|
21611
|
+
success: res.success,
|
|
21612
|
+
output: res.stdout || res.stderr
|
|
21613
|
+
});
|
|
21614
|
+
if (!res.success)
|
|
21615
|
+
hasErrors = true;
|
|
21616
|
+
}
|
|
20307
21617
|
return {
|
|
20308
21618
|
content: [
|
|
20309
21619
|
{
|
|
20310
21620
|
type: "text",
|
|
20311
|
-
text: JSON.stringify({ success:
|
|
21621
|
+
text: JSON.stringify({ success: !hasErrors, migrated: installed.length, results }, null, 2)
|
|
20312
21622
|
}
|
|
20313
21623
|
]
|
|
20314
21624
|
};
|
|
20315
|
-
}
|
|
20316
|
-
|
|
20317
|
-
|
|
20318
|
-
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
}
|
|
20322
|
-
|
|
20323
|
-
|
|
20324
|
-
|
|
20325
|
-
|
|
20326
|
-
|
|
20327
|
-
|
|
20328
|
-
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20338
|
-
|
|
20339
|
-
|
|
20340
|
-
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
|
|
20344
|
-
content: [
|
|
20345
|
-
{
|
|
20346
|
-
type: "text",
|
|
20347
|
-
text: JSON.stringify({ success: !hasErrors, initialized: installed.length, results }, null, 2)
|
|
20348
|
-
}
|
|
20349
|
-
]
|
|
20350
|
-
};
|
|
20351
|
-
});
|
|
20352
|
-
server.registerTool("migrate_all_microservices", {
|
|
20353
|
-
title: "Migrate All Microservices",
|
|
20354
|
-
description: "Run database migrations for all installed microservices.",
|
|
20355
|
-
inputSchema: {
|
|
20356
|
-
db: exports_external.string().optional().describe("PostgreSQL connection URL (overrides DATABASE_URL)")
|
|
20357
|
-
}
|
|
20358
|
-
}, async ({ db }) => {
|
|
20359
|
-
if (db)
|
|
20360
|
-
process.env.DATABASE_URL = db;
|
|
20361
|
-
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
20362
|
-
if (installed.length === 0) {
|
|
20363
|
-
return {
|
|
20364
|
-
content: [
|
|
20365
|
-
{ type: "text", text: "No microservices installed to migrate." }
|
|
20366
|
-
]
|
|
20367
|
-
};
|
|
20368
|
-
}
|
|
20369
|
-
const results = [];
|
|
20370
|
-
let hasErrors = false;
|
|
20371
|
-
for (const m of installed) {
|
|
20372
|
-
const res = await runMicroserviceCommand(m.name, ["migrate"]);
|
|
20373
|
-
results.push({
|
|
20374
|
-
name: m.name,
|
|
20375
|
-
success: res.success,
|
|
20376
|
-
output: res.stdout || res.stderr
|
|
20377
|
-
});
|
|
20378
|
-
if (!res.success)
|
|
20379
|
-
hasErrors = true;
|
|
20380
|
-
}
|
|
20381
|
-
return {
|
|
20382
|
-
content: [
|
|
20383
|
-
{
|
|
20384
|
-
type: "text",
|
|
20385
|
-
text: JSON.stringify({ success: !hasErrors, migrated: installed.length, results }, null, 2)
|
|
21625
|
+
});
|
|
21626
|
+
server.registerTool("serve_all_microservices", {
|
|
21627
|
+
title: "Serve All Microservices",
|
|
21628
|
+
description: "Start HTTP servers for all installed microservices. NOTE: This will run them in the background. It is highly recommended to run this directly in your terminal if you need to view live logs.",
|
|
21629
|
+
inputSchema: {
|
|
21630
|
+
db: exports_external.string().optional().describe("PostgreSQL connection URL (overrides DATABASE_URL)")
|
|
21631
|
+
}
|
|
21632
|
+
}, async ({ db }) => {
|
|
21633
|
+
if (db)
|
|
21634
|
+
process.env.DATABASE_URL = db;
|
|
21635
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
21636
|
+
if (installed.length === 0) {
|
|
21637
|
+
return {
|
|
21638
|
+
content: [
|
|
21639
|
+
{ type: "text", text: "No microservices installed to serve." }
|
|
21640
|
+
]
|
|
21641
|
+
};
|
|
21642
|
+
}
|
|
21643
|
+
try {
|
|
21644
|
+
const { spawn } = await import("child_process");
|
|
21645
|
+
const servicesStarted = [];
|
|
21646
|
+
for (const m of installed) {
|
|
21647
|
+
const proc = spawn(m.binary, ["serve"], {
|
|
21648
|
+
env: process.env,
|
|
21649
|
+
stdio: "ignore",
|
|
21650
|
+
detached: true
|
|
21651
|
+
});
|
|
21652
|
+
proc.unref();
|
|
21653
|
+
servicesStarted.push(m.name);
|
|
20386
21654
|
}
|
|
20387
|
-
|
|
20388
|
-
|
|
20389
|
-
|
|
20390
|
-
|
|
20391
|
-
|
|
20392
|
-
|
|
20393
|
-
|
|
20394
|
-
|
|
20395
|
-
|
|
20396
|
-
},
|
|
20397
|
-
|
|
20398
|
-
|
|
20399
|
-
|
|
20400
|
-
|
|
20401
|
-
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
20405
|
-
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
const servicesStarted = [];
|
|
20410
|
-
for (let i = 0;i < installed.length; i++) {
|
|
20411
|
-
const m = installed[i];
|
|
20412
|
-
const proc = spawn(m.binary, ["serve"], {
|
|
20413
|
-
env: process.env,
|
|
20414
|
-
stdio: "ignore",
|
|
20415
|
-
detached: true
|
|
20416
|
-
});
|
|
20417
|
-
proc.unref();
|
|
20418
|
-
servicesStarted.push(m.name);
|
|
21655
|
+
return {
|
|
21656
|
+
content: [
|
|
21657
|
+
{
|
|
21658
|
+
type: "text",
|
|
21659
|
+
text: JSON.stringify({
|
|
21660
|
+
success: true,
|
|
21661
|
+
started: servicesStarted.length,
|
|
21662
|
+
services: servicesStarted,
|
|
21663
|
+
note: "Processes have been detached and are running in the background."
|
|
21664
|
+
}, null, 2)
|
|
21665
|
+
}
|
|
21666
|
+
]
|
|
21667
|
+
};
|
|
21668
|
+
} catch (err) {
|
|
21669
|
+
return {
|
|
21670
|
+
content: [
|
|
21671
|
+
{
|
|
21672
|
+
type: "text",
|
|
21673
|
+
text: JSON.stringify({ success: false, error: String(err) }, null, 2)
|
|
21674
|
+
}
|
|
21675
|
+
]
|
|
21676
|
+
};
|
|
20419
21677
|
}
|
|
21678
|
+
});
|
|
21679
|
+
server.registerTool("remove_microservice", {
|
|
21680
|
+
title: "Remove Microservice",
|
|
21681
|
+
description: "Uninstall a microservice global package.",
|
|
21682
|
+
inputSchema: { name: exports_external.string() }
|
|
21683
|
+
}, async ({ name }) => {
|
|
21684
|
+
const ok = removeMicroservice(name);
|
|
20420
21685
|
return {
|
|
20421
21686
|
content: [
|
|
20422
|
-
{
|
|
20423
|
-
type: "text",
|
|
20424
|
-
text: JSON.stringify({
|
|
20425
|
-
success: true,
|
|
20426
|
-
started: servicesStarted.length,
|
|
20427
|
-
services: servicesStarted,
|
|
20428
|
-
note: "Processes have been detached and are running in the background."
|
|
20429
|
-
}, null, 2)
|
|
20430
|
-
}
|
|
21687
|
+
{ type: "text", text: JSON.stringify({ removed: ok, name }, null, 2) }
|
|
20431
21688
|
]
|
|
20432
21689
|
};
|
|
20433
|
-
}
|
|
21690
|
+
});
|
|
21691
|
+
server.registerTool("get_microservice_info", {
|
|
21692
|
+
title: "Get Microservice Info",
|
|
21693
|
+
description: "Get detailed info about a microservice including schema, required env vars, and tags.",
|
|
21694
|
+
inputSchema: { name: exports_external.string() }
|
|
21695
|
+
}, async ({ name }) => {
|
|
21696
|
+
const m = getMicroservice(name);
|
|
21697
|
+
if (!m)
|
|
21698
|
+
return {
|
|
21699
|
+
content: [{ type: "text", text: `Unknown microservice: ${name}` }]
|
|
21700
|
+
};
|
|
20434
21701
|
return {
|
|
20435
21702
|
content: [
|
|
20436
21703
|
{
|
|
20437
21704
|
type: "text",
|
|
20438
|
-
text: JSON.stringify({
|
|
21705
|
+
text: JSON.stringify({ ...m, installed: microserviceExists(name) }, null, 2)
|
|
20439
21706
|
}
|
|
20440
21707
|
]
|
|
20441
21708
|
};
|
|
20442
|
-
}
|
|
20443
|
-
|
|
20444
|
-
|
|
20445
|
-
title: "Remove Microservice",
|
|
20446
|
-
description: "Uninstall a microservice global package.",
|
|
20447
|
-
inputSchema: { name: exports_external.string() }
|
|
20448
|
-
}, async ({ name }) => {
|
|
20449
|
-
const ok = removeMicroservice(name);
|
|
20450
|
-
return {
|
|
20451
|
-
content: [
|
|
20452
|
-
{ type: "text", text: JSON.stringify({ removed: ok, name }, null, 2) }
|
|
20453
|
-
]
|
|
20454
|
-
};
|
|
20455
|
-
});
|
|
20456
|
-
server.registerTool("get_microservice_info", {
|
|
20457
|
-
title: "Get Microservice Info",
|
|
20458
|
-
description: "Get detailed info about a microservice including schema, required env vars, and tags.",
|
|
20459
|
-
inputSchema: { name: exports_external.string() }
|
|
20460
|
-
}, async ({ name }) => {
|
|
20461
|
-
const m = getMicroservice(name);
|
|
20462
|
-
if (!m)
|
|
20463
|
-
return {
|
|
20464
|
-
content: [{ type: "text", text: `Unknown microservice: ${name}` }]
|
|
20465
|
-
};
|
|
20466
|
-
return {
|
|
20467
|
-
content: [
|
|
20468
|
-
{
|
|
20469
|
-
type: "text",
|
|
20470
|
-
text: JSON.stringify({ ...m, installed: microserviceExists(name) }, null, 2)
|
|
20471
|
-
}
|
|
20472
|
-
]
|
|
20473
|
-
};
|
|
20474
|
-
});
|
|
21709
|
+
});
|
|
21710
|
+
return server;
|
|
21711
|
+
}
|
|
20475
21712
|
async function main() {
|
|
21713
|
+
if (hasFlag("--help", "-h")) {
|
|
21714
|
+
printHelp();
|
|
21715
|
+
return;
|
|
21716
|
+
}
|
|
21717
|
+
if (hasFlag("--version", "-V")) {
|
|
21718
|
+
process.stdout.write(`${getPackageVersion()}
|
|
21719
|
+
`);
|
|
21720
|
+
return;
|
|
21721
|
+
}
|
|
21722
|
+
if (isHttpMode()) {
|
|
21723
|
+
const handle = await startMcpHttpServer(buildServer, {
|
|
21724
|
+
port: resolveMcpHttpPort()
|
|
21725
|
+
});
|
|
21726
|
+
process.on("SIGINT", () => {
|
|
21727
|
+
handle.close().finally(() => process.exit(0));
|
|
21728
|
+
});
|
|
21729
|
+
process.on("SIGTERM", () => {
|
|
21730
|
+
handle.close().finally(() => process.exit(0));
|
|
21731
|
+
});
|
|
21732
|
+
return;
|
|
21733
|
+
}
|
|
21734
|
+
const server = buildServer();
|
|
20476
21735
|
const transport = new StdioServerTransport;
|
|
20477
21736
|
await server.connect(transport);
|
|
20478
21737
|
}
|
|
20479
21738
|
main().catch(console.error);
|
|
21739
|
+
export {
|
|
21740
|
+
buildServer
|
|
21741
|
+
};
|