@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.
Files changed (4) hide show
  1. package/LICENSE +2 -1
  2. package/README.md +12 -0
  3. package/bin/mcp.js +1586 -324
  4. 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/index.ts
20077
- var server = new McpServer({
20078
- name: "microservices",
20079
- version: getPackageVersion()
20080
- });
20081
- server.registerTool("list_microservices", {
20082
- title: "List Microservices",
20083
- description: "List all 21 available production microservices (auth, teams, billing, llm, agents, memory, etc).",
20084
- inputSchema: { installed_only: exports_external.boolean().optional() }
20085
- }, async ({ installed_only }) => {
20086
- const services = installed_only ? MICROSERVICES.filter((m) => microserviceExists(m.name)) : MICROSERVICES;
20087
- return {
20088
- content: [
20089
- {
20090
- type: "text",
20091
- text: JSON.stringify(services.map((m) => ({
20092
- name: m.name,
20093
- package: m.package,
20094
- binary: m.binary,
20095
- category: m.category,
20096
- description: m.description,
20097
- schemaPrefix: m.schemaPrefix,
20098
- installed: microserviceExists(m.name),
20099
- requiredEnv: m.requiredEnv
20100
- })), null, 2)
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
- server.registerTool("search_microservices", {
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
- server.registerTool("install_microservice", {
20127
- title: "Install Microservice",
20128
- description: "Install a microservice globally via bun install -g.",
20129
- inputSchema: { name: exports_external.string(), force: exports_external.boolean().optional() }
20130
- }, async ({ name, force }) => {
20131
- const result = installMicroservice(name, { force });
20132
- return {
20133
- content: [
20134
- {
20135
- type: "text",
20136
- text: JSON.stringify(result, null, 2)
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
- server.registerTool("microservice_status", {
20142
- title: "Microservice Status",
20143
- description: "Check if a microservice is installed and get its version.",
20144
- inputSchema: { name: exports_external.string().optional() }
20145
- }, async ({ name }) => {
20146
- if (name) {
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(getMicroserviceStatus(name), null, 2)
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
- const statuses = MICROSERVICES.map((m) => getMicroserviceStatus(m.name));
20157
- return {
20158
- content: [{ type: "text", text: JSON.stringify(statuses, null, 2) }]
20159
- };
20160
- });
20161
- server.registerTool("run_microservice_command", {
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
- { type: "text", text: "No microservices installed to check." }
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
- const report = installed.map((m) => {
20198
- const missingRequired = m.requiredEnv.filter((env) => !process.env[env]);
20199
- const missingOptional = (m.optionalEnv ?? []).filter((env) => !process.env[env]);
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
- name: m.name,
20202
- ok: missingRequired.length === 0,
20203
- missing_required: missingRequired,
20204
- missing_optional: missingOptional
21369
+ content: [
21370
+ {
21371
+ type: "text",
21372
+ text: JSON.stringify(result, null, 2)
21373
+ }
21374
+ ]
20205
21375
  };
20206
21376
  });
20207
- return {
20208
- content: [
20209
- {
20210
- type: "text",
20211
- text: JSON.stringify({
20212
- summary: {
20213
- installed: installed.length,
20214
- total_missing_required: report.reduce((acc, r) => acc + r.missing_required.length, 0),
20215
- all_ok: report.every((r) => r.ok)
20216
- },
20217
- services: report
20218
- }, null, 2)
20219
- }
20220
- ]
20221
- };
20222
- });
20223
- server.registerTool("scaffold_microservice", {
20224
- title: "Scaffold Microservice",
20225
- description: "Scaffold a new microservice from the _template directory.",
20226
- inputSchema: {
20227
- name: exports_external.string().describe("The name of the new microservice (lowercase, dashes only)")
20228
- }
20229
- }, async ({ name }) => {
20230
- if (!/^[a-z0-9-]+$/.test(name)) {
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: false,
20237
- error: "Name can only contain lowercase letters, numbers, and dashes."
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
- try {
20244
- const fs = await import("fs");
20245
- const path = await import("path");
20246
- const cwd = process.cwd();
20247
- const templateDir = path.join(cwd, "microservices", "_template");
20248
- const targetDir = path.join(cwd, "microservices", `microservice-${name}`);
20249
- if (!fs.existsSync(templateDir)) {
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: `Template directory not found at ${templateDir}.`
21473
+ error: "Name can only contain lowercase letters, numbers, and dashes."
20257
21474
  }, null, 2)
20258
21475
  }
20259
21476
  ]
20260
21477
  };
20261
21478
  }
20262
- if (fs.existsSync(targetDir)) {
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: false,
20269
- error: `Target directory already exists at ${targetDir}`
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
- } catch (err) {
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: false, error: String(err) }, null, 2)
21621
+ text: JSON.stringify({ success: !hasErrors, migrated: installed.length, results }, null, 2)
20312
21622
  }
20313
21623
  ]
20314
21624
  };
20315
- }
20316
- });
20317
- server.registerTool("init_all_microservices", {
20318
- title: "Init All Microservices",
20319
- description: "Run migrations and confirm setup for all installed microservices.",
20320
- inputSchema: { db: exports_external.string().describe("PostgreSQL connection URL") }
20321
- }, async ({ db }) => {
20322
- process.env.DATABASE_URL = db;
20323
- const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
20324
- if (installed.length === 0) {
20325
- return {
20326
- content: [
20327
- { type: "text", text: "No microservices installed to init." }
20328
- ]
20329
- };
20330
- }
20331
- const results = [];
20332
- let hasErrors = false;
20333
- for (const m of installed) {
20334
- const res = await runMicroserviceCommand(m.name, ["init", "--db", db]);
20335
- results.push({
20336
- name: m.name,
20337
- success: res.success,
20338
- output: res.stdout || res.stderr
20339
- });
20340
- if (!res.success)
20341
- hasErrors = true;
20342
- }
20343
- return {
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
- server.registerTool("serve_all_microservices", {
20391
- title: "Serve All Microservices",
20392
- 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.",
20393
- inputSchema: {
20394
- db: exports_external.string().optional().describe("PostgreSQL connection URL (overrides DATABASE_URL)")
20395
- }
20396
- }, async ({ db }) => {
20397
- if (db)
20398
- process.env.DATABASE_URL = db;
20399
- const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
20400
- if (installed.length === 0) {
20401
- return {
20402
- content: [
20403
- { type: "text", text: "No microservices installed to serve." }
20404
- ]
20405
- };
20406
- }
20407
- try {
20408
- const { spawn } = await import("child_process");
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
- } catch (err) {
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({ success: false, error: String(err) }, null, 2)
21705
+ text: JSON.stringify({ ...m, installed: microserviceExists(name) }, null, 2)
20439
21706
  }
20440
21707
  ]
20441
21708
  };
20442
- }
20443
- });
20444
- server.registerTool("remove_microservice", {
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
+ };