vite-plugin-swagger-mcp 0.0.21 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,6 +33,13 @@ npm run dev
33
33
  ```
34
34
  Then the MCP server will be available at http://ip:port/_mcp/sse/swagger.
35
35
 
36
+ ### New MCP endpoint (recommended)
37
+ Newer MCP clients/editors expect the Streamable HTTP "single endpoint" style. Prefer:
38
+
39
+ - `http://ip:port/_mcp/swagger`
40
+
41
+ `/_mcp/sse/swagger` is kept as a legacy alias for backward compatibility.
42
+
36
43
  When using VSCode, Cursor, Windsurf, Claude Code, the module will automatically update the config files for you.
37
44
 
38
45
  ## LICENSE
package/dist/cjs/index.js CHANGED
@@ -31,7 +31,15 @@ async function readJsonBody(req) {
31
31
  let body = "";
32
32
  for await (const chunk of req)
33
33
  body += chunk;
34
- return JSON.parse(body || "{}");
34
+ if (!body)
35
+ return {};
36
+ try {
37
+ return JSON.parse(body);
38
+ } catch {
39
+ const err = new Error("Invalid JSON body");
40
+ err.statusCode = 400;
41
+ throw err;
42
+ }
35
43
  }
36
44
  var SwaggerMcpServer = class {
37
45
  constructor(swaggerUrl, token) {
@@ -140,7 +148,6 @@ var SwaggerMcpServer = class {
140
148
  };
141
149
  }
142
150
  };
143
- var transports = {};
144
151
  function vitePluginSwaggerMcp({
145
152
  swaggerUrl,
146
153
  token
@@ -151,76 +158,54 @@ function vitePluginSwaggerMcp({
151
158
  async configureServer(server) {
152
159
  var _a, _b;
153
160
  const swaggerServer = new SwaggerMcpServer(swaggerUrl, token);
154
- const createNewServer = () => {
155
- const mcpServer = new import_mcp.McpServer({
156
- name: "swagger-mcp-server",
157
- version: "0.1.0"
158
- });
159
- mcpServer.tool("getModules", "获取模块列表", async () => {
160
- const res = await swaggerServer.getModules();
161
+ const mcpServer = new import_mcp.McpServer({
162
+ name: "swagger-mcp-server",
163
+ version: "0.1.0"
164
+ });
165
+ mcpServer.tool("getModules", "获取模块列表", async () => {
166
+ const res = await swaggerServer.getModules();
167
+ return { content: [{ type: "text", text: JSON.stringify(res) }] };
168
+ });
169
+ mcpServer.tool(
170
+ "getModuleApis",
171
+ "获取特定模块下的所有接口",
172
+ { module: import_v4.z.string().describe("模块名称") },
173
+ async ({ module: module2 }) => {
174
+ const res = await swaggerServer.getModuleApis(module2);
161
175
  return { content: [{ type: "text", text: JSON.stringify(res) }] };
162
- });
163
- mcpServer.tool(
164
- "getModuleApis",
165
- "获取特定模块下的所有接口",
166
- { module: import_v4.z.string().describe("模块名称") },
167
- async ({ module: module2 }) => {
168
- const res = await swaggerServer.getModuleApis(module2);
169
- return { content: [{ type: "text", text: JSON.stringify(res) }] };
170
- }
171
- );
172
- mcpServer.tool(
173
- "getApiTypes",
174
- "获取特定接口的参数及返回值类型",
175
- { path: import_v4.z.string(), method: import_v4.z.string() },
176
- async (args) => ({
177
- content: [{ type: "text", text: JSON.stringify(await swaggerServer.getApiTypes(args.path, args.method)) }]
178
- })
179
- );
180
- return mcpServer;
181
- };
182
- const ENDPOINT = "/_mcp/sse/swagger";
183
- server.middlewares.use(ENDPOINT, async (req, res, next) => {
176
+ }
177
+ );
178
+ mcpServer.tool(
179
+ "getApiTypes",
180
+ "获取特定接口的参数及返回值类型",
181
+ { path: import_v4.z.string(), method: import_v4.z.string() },
182
+ async (args) => ({
183
+ content: [
184
+ {
185
+ type: "text",
186
+ text: JSON.stringify(
187
+ await swaggerServer.getApiTypes(args.path, args.method)
188
+ )
189
+ }
190
+ ]
191
+ })
192
+ );
193
+ const transport = new import_streamableHttp.StreamableHTTPServerTransport({
194
+ sessionIdGenerator: () => (0, import_node_crypto.randomUUID)(),
195
+ enableJsonResponse: true
196
+ });
197
+ await mcpServer.connect(transport);
198
+ const ENDPOINT = "/_mcp/swagger";
199
+ const LEGACY_ENDPOINT = "/_mcp/sse/swagger";
200
+ const handler = async (req, res) => {
184
201
  try {
185
- const url = new URL(req.url || "", `http://${req.headers.host}`);
186
- const sessionId = req.headers["mcp-session-id"] || url.searchParams.get("sessionId");
187
202
  if (req.method === "POST") {
188
203
  const json = await readJsonBody(req);
189
- let transport;
190
- if (sessionId && transports[sessionId]) {
191
- transport = transports[sessionId];
192
- } else if (!sessionId) {
193
- transport = new import_streamableHttp.StreamableHTTPServerTransport({
194
- sessionIdGenerator: () => (0, import_node_crypto.randomUUID)(),
195
- // 如果你的编辑器不支持 SSE 只能接收 JSON 响应,可以设为 true
196
- // 但大多数现代编辑器建议保持默认(false)以支持标准流
197
- enableJsonResponse: false,
198
- onsessioninitialized: (sid) => {
199
- transports[sid] = transport;
200
- console.log(`[MCP] Session initialized: ${sid}`);
201
- }
202
- });
203
- transport.onclose = () => {
204
- if (transport.sessionId)
205
- delete transports[transport.sessionId];
206
- console.log(`[MCP] Session closed: ${transport.sessionId}`);
207
- };
208
- const mcpServer = createNewServer();
209
- await mcpServer.connect(transport);
210
- } else {
211
- res.statusCode = 400;
212
- return res.end(JSON.stringify({ error: "Invalid session or not an initialize request" }));
213
- }
214
204
  await transport.handleRequest(req, res, json);
215
205
  return;
216
206
  }
217
- if (req.method === "GET") {
218
- if (!sessionId || !transports[sessionId]) {
219
- const initTransport = new import_streamableHttp.StreamableHTTPServerTransport();
220
- await initTransport.handleRequest(req, res);
221
- return;
222
- }
223
- await transports[sessionId].handleRequest(req, res);
207
+ if (req.method === "GET" || req.method === "DELETE") {
208
+ await transport.handleRequest(req, res);
224
209
  return;
225
210
  }
226
211
  res.statusCode = 405;
@@ -228,12 +213,18 @@ function vitePluginSwaggerMcp({
228
213
  } catch (error) {
229
214
  console.error("[MCP Error]", error);
230
215
  if (!res.headersSent) {
231
- res.statusCode = 500;
232
- res.end("Internal Server Error");
216
+ res.statusCode = (error == null ? void 0 : error.statusCode) ?? 500;
217
+ res.end(
218
+ res.statusCode === 400 ? "Bad Request" : "Internal Server Error"
219
+ );
233
220
  }
234
221
  }
235
- });
236
- console.log(`✅ MCP Swagger Server mounted at http://localhost:${(_b = (_a = server.config) == null ? void 0 : _a.server) == null ? void 0 : _b.port}${ENDPOINT}`);
222
+ };
223
+ server.middlewares.use(ENDPOINT, handler);
224
+ server.middlewares.use(LEGACY_ENDPOINT, handler);
225
+ console.log(
226
+ `✅ MCP Swagger Server mounted at http://localhost:${(_b = (_a = server.config) == null ? void 0 : _a.server) == null ? void 0 : _b.port}${ENDPOINT}`
227
+ );
237
228
  }
238
229
  };
239
230
  }
package/dist/esm/index.js CHANGED
@@ -22,7 +22,7 @@ function readJsonBody(_x) {
22
22
  }
23
23
  function _readJsonBody() {
24
24
  _readJsonBody = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10(req) {
25
- var body, _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, chunk;
25
+ var body, _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, chunk, err;
26
26
  return _regeneratorRuntime().wrap(function _callee10$(_context10) {
27
27
  while (1) switch (_context10.prev = _context10.next) {
28
28
  case 0:
@@ -74,12 +74,25 @@ function _readJsonBody() {
74
74
  case 28:
75
75
  return _context10.finish(19);
76
76
  case 29:
77
- return _context10.abrupt("return", JSON.parse(body || "{}"));
78
- case 30:
77
+ if (body) {
78
+ _context10.next = 31;
79
+ break;
80
+ }
81
+ return _context10.abrupt("return", {});
82
+ case 31:
83
+ _context10.prev = 31;
84
+ return _context10.abrupt("return", JSON.parse(body));
85
+ case 35:
86
+ _context10.prev = 35;
87
+ _context10.t1 = _context10["catch"](31);
88
+ err = new Error("Invalid JSON body");
89
+ err.statusCode = 400;
90
+ throw err;
91
+ case 40:
79
92
  case "end":
80
93
  return _context10.stop();
81
94
  }
82
- }, _callee10, null, [[3, 15, 19, 29], [20,, 24, 28]]);
95
+ }, _callee10, null, [[3, 15, 19, 29], [20,, 24, 28], [31, 35]]);
83
96
  }));
84
97
  return _readJsonBody.apply(this, arguments);
85
98
  }
@@ -294,9 +307,6 @@ export var SwaggerMcpServer = /*#__PURE__*/function () {
294
307
  }]);
295
308
  return SwaggerMcpServer;
296
309
  }();
297
-
298
- // 存储会话 ID 对应的 Transport 实例
299
- var transports = {};
300
310
  export default function vitePluginSwaggerMcp(_ref) {
301
311
  var swaggerUrl = _ref.swaggerUrl,
302
312
  token = _ref.token;
@@ -306,213 +316,165 @@ export default function vitePluginSwaggerMcp(_ref) {
306
316
  configureServer: function configureServer(server) {
307
317
  return _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9() {
308
318
  var _server$config;
309
- var swaggerServer, createNewServer, ENDPOINT;
319
+ var swaggerServer, mcpServer, transport, ENDPOINT, LEGACY_ENDPOINT, handler;
310
320
  return _regeneratorRuntime().wrap(function _callee9$(_context9) {
311
321
  while (1) switch (_context9.prev = _context9.next) {
312
322
  case 0:
313
- swaggerServer = new SwaggerMcpServer(swaggerUrl, token); // --- 辅助函数:创建并配置一个新的 MCP Server 实例 ---
314
- createNewServer = function createNewServer() {
315
- var mcpServer = new McpServer({
316
- name: "swagger-mcp-server",
317
- version: "0.1.0"
318
- });
319
-
320
- // 注册所有工具
321
- mcpServer.tool("getModules", "获取模块列表", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
322
- var res;
323
- return _regeneratorRuntime().wrap(function _callee5$(_context5) {
324
- while (1) switch (_context5.prev = _context5.next) {
323
+ swaggerServer = new SwaggerMcpServer(swaggerUrl, token); // 标准 Streamable HTTP:单 MCP endpoint,POST 发送 JSON-RPC,GET/DELETE 可选(由 transport 处理)
324
+ mcpServer = new McpServer({
325
+ name: "swagger-mcp-server",
326
+ version: "0.1.0"
327
+ }); // 注册所有工具
328
+ mcpServer.tool("getModules", "获取模块列表", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
329
+ var res;
330
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
331
+ while (1) switch (_context5.prev = _context5.next) {
332
+ case 0:
333
+ _context5.next = 2;
334
+ return swaggerServer.getModules();
335
+ case 2:
336
+ res = _context5.sent;
337
+ return _context5.abrupt("return", {
338
+ content: [{
339
+ type: "text",
340
+ text: JSON.stringify(res)
341
+ }]
342
+ });
343
+ case 4:
344
+ case "end":
345
+ return _context5.stop();
346
+ }
347
+ }, _callee5);
348
+ })));
349
+ mcpServer.tool("getModuleApis", "获取特定模块下的所有接口", {
350
+ module: z.string().describe("模块名称")
351
+ }, /*#__PURE__*/function () {
352
+ var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(_ref3) {
353
+ var module, res;
354
+ return _regeneratorRuntime().wrap(function _callee6$(_context6) {
355
+ while (1) switch (_context6.prev = _context6.next) {
325
356
  case 0:
326
- _context5.next = 2;
327
- return swaggerServer.getModules();
328
- case 2:
329
- res = _context5.sent;
330
- return _context5.abrupt("return", {
357
+ module = _ref3.module;
358
+ _context6.next = 3;
359
+ return swaggerServer.getModuleApis(module);
360
+ case 3:
361
+ res = _context6.sent;
362
+ return _context6.abrupt("return", {
331
363
  content: [{
332
364
  type: "text",
333
365
  text: JSON.stringify(res)
334
366
  }]
335
367
  });
336
- case 4:
368
+ case 5:
337
369
  case "end":
338
- return _context5.stop();
370
+ return _context6.stop();
339
371
  }
340
- }, _callee5);
341
- })));
342
- mcpServer.tool("getModuleApis", "获取特定模块下的所有接口", {
343
- module: z.string().describe("模块名称")
344
- }, /*#__PURE__*/function () {
345
- var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6(_ref3) {
346
- var module, res;
347
- return _regeneratorRuntime().wrap(function _callee6$(_context6) {
348
- while (1) switch (_context6.prev = _context6.next) {
349
- case 0:
350
- module = _ref3.module;
351
- _context6.next = 3;
352
- return swaggerServer.getModuleApis(module);
353
- case 3:
354
- res = _context6.sent;
355
- return _context6.abrupt("return", {
356
- content: [{
357
- type: "text",
358
- text: JSON.stringify(res)
359
- }]
360
- });
361
- case 5:
362
- case "end":
363
- return _context6.stop();
364
- }
365
- }, _callee6);
366
- }));
367
- return function (_x5) {
368
- return _ref4.apply(this, arguments);
369
- };
370
- }());
371
- mcpServer.tool("getApiTypes", "获取特定接口的参数及返回值类型", {
372
- path: z.string(),
373
- method: z.string()
374
- }, /*#__PURE__*/function () {
375
- var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7(args) {
376
- return _regeneratorRuntime().wrap(function _callee7$(_context7) {
377
- while (1) switch (_context7.prev = _context7.next) {
378
- case 0:
379
- _context7.t0 = JSON;
380
- _context7.next = 3;
381
- return swaggerServer.getApiTypes(args.path, args.method);
382
- case 3:
383
- _context7.t1 = _context7.sent;
384
- _context7.t2 = _context7.t0.stringify.call(_context7.t0, _context7.t1);
385
- _context7.t3 = {
386
- type: "text",
387
- text: _context7.t2
388
- };
389
- _context7.t4 = [_context7.t3];
390
- return _context7.abrupt("return", {
391
- content: _context7.t4
392
- });
393
- case 8:
394
- case "end":
395
- return _context7.stop();
396
- }
397
- }, _callee7);
398
- }));
399
- return function (_x6) {
400
- return _ref5.apply(this, arguments);
401
- };
402
- }());
403
- return mcpServer;
404
- };
405
- ENDPOINT = "/_mcp/sse/swagger";
406
- server.middlewares.use(ENDPOINT, /*#__PURE__*/function () {
407
- var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8(req, res, next) {
408
- var url, _sessionId, json, transport, mcpServer, initTransport;
372
+ }, _callee6);
373
+ }));
374
+ return function (_x5) {
375
+ return _ref4.apply(this, arguments);
376
+ };
377
+ }());
378
+ mcpServer.tool("getApiTypes", "获取特定接口的参数及返回值类型", {
379
+ path: z.string(),
380
+ method: z.string()
381
+ }, /*#__PURE__*/function () {
382
+ var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7(args) {
383
+ return _regeneratorRuntime().wrap(function _callee7$(_context7) {
384
+ while (1) switch (_context7.prev = _context7.next) {
385
+ case 0:
386
+ _context7.t0 = JSON;
387
+ _context7.next = 3;
388
+ return swaggerServer.getApiTypes(args.path, args.method);
389
+ case 3:
390
+ _context7.t1 = _context7.sent;
391
+ _context7.t2 = _context7.t0.stringify.call(_context7.t0, _context7.t1);
392
+ _context7.t3 = {
393
+ type: "text",
394
+ text: _context7.t2
395
+ };
396
+ _context7.t4 = [_context7.t3];
397
+ return _context7.abrupt("return", {
398
+ content: _context7.t4
399
+ });
400
+ case 8:
401
+ case "end":
402
+ return _context7.stop();
403
+ }
404
+ }, _callee7);
405
+ }));
406
+ return function (_x6) {
407
+ return _ref5.apply(this, arguments);
408
+ };
409
+ }());
410
+
411
+ // 这里使用 stateful session,兼容需要 MCP-Session-Id 的客户端;同时开启 JSON 响应模式以减少 SSE 兼容问题
412
+ transport = new StreamableHTTPServerTransport({
413
+ sessionIdGenerator: function sessionIdGenerator() {
414
+ return randomUUID();
415
+ },
416
+ enableJsonResponse: true
417
+ }); // 连接一次即可,后续每个 HTTP 请求都走 transport.handleRequest
418
+ _context9.next = 8;
419
+ return mcpServer.connect(transport);
420
+ case 8:
421
+ ENDPOINT = "/_mcp/swagger";
422
+ LEGACY_ENDPOINT = "/_mcp/sse/swagger";
423
+ handler = /*#__PURE__*/function () {
424
+ var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8(req, res) {
425
+ var json, _statusCode;
409
426
  return _regeneratorRuntime().wrap(function _callee8$(_context8) {
410
427
  while (1) switch (_context8.prev = _context8.next) {
411
428
  case 0:
412
429
  _context8.prev = 0;
413
- url = new URL(req.url || "", "http://".concat(req.headers.host));
414
- _sessionId = req.headers["mcp-session-id"] || url.searchParams.get("sessionId");
415
- /* ================= POST 请求处理 ================= */
416
430
  if (!(req.method === "POST")) {
417
- _context8.next = 24;
431
+ _context8.next = 8;
418
432
  break;
419
433
  }
420
- _context8.next = 6;
434
+ _context8.next = 4;
421
435
  return readJsonBody(req);
422
- case 6:
436
+ case 4:
423
437
  json = _context8.sent;
424
- if (!(_sessionId && transports[_sessionId])) {
425
- _context8.next = 11;
426
- break;
427
- }
428
- transport = transports[_sessionId];
429
- _context8.next = 21;
430
- break;
431
- case 11:
432
- if (_sessionId) {
433
- _context8.next = 19;
434
- break;
435
- }
436
- transport = new StreamableHTTPServerTransport({
437
- sessionIdGenerator: function sessionIdGenerator() {
438
- return randomUUID();
439
- },
440
- // 如果你的编辑器不支持 SSE 只能接收 JSON 响应,可以设为 true
441
- // 但大多数现代编辑器建议保持默认(false)以支持标准流
442
- enableJsonResponse: false,
443
- onsessioninitialized: function onsessioninitialized(sid) {
444
- transports[sid] = transport;
445
- console.log("[MCP] Session initialized: ".concat(sid));
446
- }
447
- });
448
- transport.onclose = function () {
449
- if (transport.sessionId) delete transports[transport.sessionId];
450
- console.log("[MCP] Session closed: ".concat(transport.sessionId));
451
- };
452
-
453
- // 为这个 Transport 绑定一个新的 Server 实例,防止 "Already initialized"
454
- mcpServer = createNewServer();
455
- _context8.next = 17;
456
- return mcpServer.connect(transport);
457
- case 17:
458
- _context8.next = 21;
459
- break;
460
- case 19:
461
- res.statusCode = 400;
462
- return _context8.abrupt("return", res.end(JSON.stringify({
463
- error: "Invalid session or not an initialize request"
464
- })));
465
- case 21:
466
- _context8.next = 23;
438
+ _context8.next = 7;
467
439
  return transport.handleRequest(req, res, json);
468
- case 23:
440
+ case 7:
469
441
  return _context8.abrupt("return");
470
- case 24:
471
- if (!(req.method === "GET")) {
472
- _context8.next = 33;
473
- break;
474
- }
475
- if (!(!_sessionId || !transports[_sessionId])) {
476
- _context8.next = 30;
442
+ case 8:
443
+ if (!(req.method === "GET" || req.method === "DELETE")) {
444
+ _context8.next = 12;
477
445
  break;
478
446
  }
479
- // 如果是初次连接请求且没有 sessionId,让 transport 处理以发送 endpoint 事件
480
- // 这里创建一个临时的 transport 来引导客户端
481
- initTransport = new StreamableHTTPServerTransport();
482
- _context8.next = 29;
483
- return initTransport.handleRequest(req, res);
484
- case 29:
485
- return _context8.abrupt("return");
486
- case 30:
487
- _context8.next = 32;
488
- return transports[_sessionId].handleRequest(req, res);
489
- case 32:
447
+ _context8.next = 11;
448
+ return transport.handleRequest(req, res);
449
+ case 11:
490
450
  return _context8.abrupt("return");
491
- case 33:
451
+ case 12:
492
452
  res.statusCode = 405;
493
453
  res.end("Method Not Allowed");
494
- _context8.next = 41;
454
+ _context8.next = 20;
495
455
  break;
496
- case 37:
497
- _context8.prev = 37;
456
+ case 16:
457
+ _context8.prev = 16;
498
458
  _context8.t0 = _context8["catch"](0);
499
459
  console.error("[MCP Error]", _context8.t0);
500
460
  if (!res.headersSent) {
501
- res.statusCode = 500;
502
- res.end("Internal Server Error");
461
+ res.statusCode = (_statusCode = _context8.t0 === null || _context8.t0 === void 0 ? void 0 : _context8.t0.statusCode) !== null && _statusCode !== void 0 ? _statusCode : 500;
462
+ res.end(res.statusCode === 400 ? "Bad Request" : "Internal Server Error");
503
463
  }
504
- case 41:
464
+ case 20:
505
465
  case "end":
506
466
  return _context8.stop();
507
467
  }
508
- }, _callee8, null, [[0, 37]]);
468
+ }, _callee8, null, [[0, 16]]);
509
469
  }));
510
- return function (_x7, _x8, _x9) {
470
+ return function handler(_x7, _x8) {
511
471
  return _ref6.apply(this, arguments);
512
472
  };
513
- }());
473
+ }();
474
+ server.middlewares.use(ENDPOINT, handler);
475
+ server.middlewares.use(LEGACY_ENDPOINT, handler);
514
476
  console.log("\u2705 MCP Swagger Server mounted at http://localhost:".concat((_server$config = server.config) === null || _server$config === void 0 || (_server$config = _server$config.server) === null || _server$config === void 0 ? void 0 : _server$config.port).concat(ENDPOINT));
515
- case 5:
477
+ case 14:
516
478
  case "end":
517
479
  return _context9.stop();
518
480
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-swagger-mcp",
3
- "version": "0.0.21",
3
+ "version": "0.1.1",
4
4
  "description": "vite plugin for swagger mcp",
5
5
  "homepage": "https://github.com/mmdctjj/vite-plugin-swagger-mcp",
6
6
  "repository": {