@wener/mcps 1.0.2 → 1.0.5

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 (103) hide show
  1. package/README.md +144 -0
  2. package/dist/index.mjs +213076 -1
  3. package/dist/mcps-cli.mjs +102632 -59429
  4. package/lib/audit/AuditContract.js.map +1 -0
  5. package/lib/{chat/audit.js → audit/chat.js} +1 -1
  6. package/lib/audit/chat.js.map +1 -0
  7. package/lib/audit/entities/ChatRequestEntity.js.map +1 -0
  8. package/lib/audit/entities/McpRequestEntity.js.map +1 -0
  9. package/lib/audit/entities/RequestLogEntity.js.map +1 -0
  10. package/lib/audit/entities/ResponseEntity.js.map +1 -0
  11. package/lib/audit/entities/index.js +6 -0
  12. package/lib/audit/entities/index.js.map +1 -0
  13. package/lib/audit/server/db.js +64 -0
  14. package/lib/audit/server/db.js.map +1 -0
  15. package/lib/audit/server/index.js +2 -0
  16. package/lib/audit/server/index.js.map +1 -0
  17. package/lib/{server/audit.js → audit/server/plugin.js} +73 -127
  18. package/lib/audit/server/plugin.js.map +1 -0
  19. package/lib/audit/types.js.map +1 -0
  20. package/lib/chat/handler.js +5 -5
  21. package/lib/chat/handler.js.map +1 -1
  22. package/lib/chat/index.js +1 -1
  23. package/lib/chat/index.js.map +1 -1
  24. package/lib/cli-start.js +36 -0
  25. package/lib/cli-start.js.map +1 -0
  26. package/lib/cli.js +19 -0
  27. package/lib/cli.js.map +1 -0
  28. package/lib/contracts/index.js +1 -1
  29. package/lib/contracts/index.js.map +1 -1
  30. package/lib/dev.server.js +7 -1
  31. package/lib/dev.server.js.map +1 -1
  32. package/lib/entities/index.js +2 -10
  33. package/lib/entities/index.js.map +1 -1
  34. package/lib/index.js +21 -3
  35. package/lib/index.js.map +1 -1
  36. package/lib/mcps-cli.js +6 -35
  37. package/lib/mcps-cli.js.map +1 -1
  38. package/lib/providers/feishu/def.js +35 -0
  39. package/lib/providers/feishu/def.js.map +1 -0
  40. package/lib/providers/findMcpServerDef.js +1 -0
  41. package/lib/providers/findMcpServerDef.js.map +1 -1
  42. package/lib/scripts/bundle.js +7 -1
  43. package/lib/scripts/bundle.js.map +1 -1
  44. package/lib/server/api-routes.js +7 -8
  45. package/lib/server/api-routes.js.map +1 -1
  46. package/lib/server/events.js +13 -0
  47. package/lib/server/events.js.map +1 -0
  48. package/lib/server/mcp-routes.js +31 -60
  49. package/lib/server/mcp-routes.js.map +1 -1
  50. package/lib/server/mcps-router.js +19 -24
  51. package/lib/server/mcps-router.js.map +1 -1
  52. package/lib/server/schema.js +22 -2
  53. package/lib/server/schema.js.map +1 -1
  54. package/lib/server/server.js +142 -87
  55. package/lib/server/server.js.map +1 -1
  56. package/package.json +145 -85
  57. package/src/{chat/audit.ts → audit/chat.ts} +3 -3
  58. package/src/audit/entities/index.ts +6 -0
  59. package/src/audit/server/db.ts +65 -0
  60. package/src/audit/server/index.ts +8 -0
  61. package/src/{server/audit.ts → audit/server/plugin.ts} +71 -144
  62. package/src/chat/handler.ts +5 -5
  63. package/src/chat/index.ts +1 -1
  64. package/src/cli-start.ts +43 -0
  65. package/src/cli.ts +45 -0
  66. package/src/contracts/index.ts +1 -1
  67. package/src/dev.server.ts +8 -1
  68. package/src/entities/index.ts +2 -12
  69. package/src/index.ts +47 -1
  70. package/src/mcps-cli.ts +6 -48
  71. package/src/providers/feishu/def.ts +37 -0
  72. package/src/providers/findMcpServerDef.ts +1 -0
  73. package/src/scripts/bundle.ts +12 -1
  74. package/src/server/api-routes.ts +11 -8
  75. package/src/server/events.ts +29 -0
  76. package/src/server/mcp-routes.ts +30 -58
  77. package/src/server/mcps-router.ts +21 -29
  78. package/src/server/schema.ts +23 -2
  79. package/src/server/server.ts +149 -81
  80. package/LICENSE +0 -21
  81. package/lib/chat/audit.js.map +0 -1
  82. package/lib/contracts/AuditContract.js.map +0 -1
  83. package/lib/entities/ChatRequestEntity.js.map +0 -1
  84. package/lib/entities/McpRequestEntity.js.map +0 -1
  85. package/lib/entities/RequestLogEntity.js.map +0 -1
  86. package/lib/entities/ResponseEntity.js.map +0 -1
  87. package/lib/entities/types.js.map +0 -1
  88. package/lib/server/audit.js.map +0 -1
  89. package/lib/server/db.js +0 -97
  90. package/lib/server/db.js.map +0 -1
  91. package/src/server/db.ts +0 -115
  92. /package/lib/{contracts → audit}/AuditContract.js +0 -0
  93. /package/lib/{entities → audit/entities}/ChatRequestEntity.js +0 -0
  94. /package/lib/{entities → audit/entities}/McpRequestEntity.js +0 -0
  95. /package/lib/{entities → audit/entities}/RequestLogEntity.js +0 -0
  96. /package/lib/{entities → audit/entities}/ResponseEntity.js +0 -0
  97. /package/lib/{entities → audit}/types.js +0 -0
  98. /package/src/{contracts → audit}/AuditContract.ts +0 -0
  99. /package/src/{entities → audit/entities}/ChatRequestEntity.ts +0 -0
  100. /package/src/{entities → audit/entities}/McpRequestEntity.ts +0 -0
  101. /package/src/{entities → audit/entities}/RequestLogEntity.ts +0 -0
  102. /package/src/{entities → audit/entities}/ResponseEntity.ts +0 -0
  103. /package/src/{entities → audit}/types.ts +0 -0
package/lib/mcps-cli.js CHANGED
@@ -1,41 +1,12 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * MCPS - MCP Proxy Server CLI
4
- *
5
- * A unified MCP server that supports:
6
- * - Tencent CLS (Cloud Log Service)
7
- * - SQL (MySQL, PostgreSQL, SQLite)
8
- * - Prometheus
9
- * - Relay (proxy to other MCP servers)
10
- */ import { serve } from "@hono/node-server";
11
- import { Command } from "commander";
12
2
  import consola from "consola";
13
- import { createServer } from "./server/server.js";
3
+ import { createProgram } from "./cli.js";
14
4
  const log = consola.withTag("mcps");
15
- const program = new Command();
16
- program.name("mcps").description("MCP Proxy Server - Unified MCP service with relay, SQL, CLS, and Prometheus support").version("0.1.0").option("-p, --port <port>", "Port to listen on", "8036").option("-c, --cwd <path>", "Working directory for config files", process.cwd()).option("--discovery-config", "Enable server config discovery endpoints", false).action(async (options) => {
17
- const port = Number.parseInt(options.port, 10);
18
- const { app } = createServer({
19
- cwd: options.cwd,
20
- port,
21
- discoveryConfig: options.discoveryConfig
22
- });
23
- log.info(`Starting MCPS server on port ${port}`);
24
- serve({
25
- fetch: app.fetch,
26
- port,
27
- hostname: "0.0.0.0"
28
- });
29
- log.success(`MCPS server running at http://localhost:${port}`);
30
- });
31
- // Handle graceful shutdown
32
- process.on("SIGINT", () => {
33
- log.info("Shutting down...");
34
- process.exit(130);
35
- });
36
- process.on("SIGTERM", () => {
37
- log.info("Shutting down...");
38
- process.exit(143);
5
+ const program = createProgram({
6
+ setup: async (ctx) => {
7
+ const { setupAudit } = await import("./audit/server/plugin.js");
8
+ setupAudit(ctx);
9
+ }
39
10
  });
40
11
  program.parseAsync(process.argv).catch((error) => {
41
12
  log.error(error.message);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcps-cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * MCPS - MCP Proxy Server CLI\n *\n * A unified MCP server that supports:\n * - Tencent CLS (Cloud Log Service)\n * - SQL (MySQL, PostgreSQL, SQLite)\n * - Prometheus\n * - Relay (proxy to other MCP servers)\n */\nimport { serve } from '@hono/node-server';\nimport { Command } from 'commander';\nimport consola from 'consola';\nimport { createServer } from './server/server';\n\nconst log = consola.withTag('mcps');\n\nconst program = new Command();\n\nprogram\n\t.name('mcps')\n\t.description('MCP Proxy Server - Unified MCP service with relay, SQL, CLS, and Prometheus support')\n\t.version('0.1.0')\n\t.option('-p, --port <port>', 'Port to listen on', '8036')\n\t.option('-c, --cwd <path>', 'Working directory for config files', process.cwd())\n\t.option('--discovery-config', 'Enable server config discovery endpoints', false)\n\t.action(async (options) => {\n\t\tconst port = Number.parseInt(options.port, 10);\n\t\tconst { app } = createServer({\n\t\t\tcwd: options.cwd,\n\t\t\tport,\n\t\t\tdiscoveryConfig: options.discoveryConfig,\n\t\t});\n\n\t\tlog.info(`Starting MCPS server on port ${port}`);\n\n\t\tserve({\n\t\t\tfetch: app.fetch,\n\t\t\tport,\n\t\t\thostname: '0.0.0.0',\n\t\t});\n\n\t\tlog.success(`MCPS server running at http://localhost:${port}`);\n\t});\n\n// Handle graceful shutdown\nprocess.on('SIGINT', () => {\n\tlog.info('Shutting down...');\n\tprocess.exit(130);\n});\nprocess.on('SIGTERM', () => {\n\tlog.info('Shutting down...');\n\tprocess.exit(143);\n});\n\nprogram.parseAsync(process.argv).catch((error) => {\n\tlog.error(error.message);\n\tprocess.exit(1);\n});\n"],"names":["serve","Command","consola","createServer","log","withTag","program","name","description","version","option","process","cwd","action","options","port","Number","parseInt","app","discoveryConfig","info","fetch","hostname","success","on","exit","parseAsync","argv","catch","error","message"],"mappings":";AACA;;;;;;;;CAQC,GACD,SAASA,KAAK,QAAQ,oBAAoB;AAC1C,SAASC,OAAO,QAAQ,YAAY;AACpC,OAAOC,aAAa,UAAU;AAC9B,SAASC,YAAY,QAAQ,kBAAkB;AAE/C,MAAMC,MAAMF,QAAQG,OAAO,CAAC;AAE5B,MAAMC,UAAU,IAAIL;AAEpBK,QACEC,IAAI,CAAC,QACLC,WAAW,CAAC,uFACZC,OAAO,CAAC,SACRC,MAAM,CAAC,qBAAqB,qBAAqB,QACjDA,MAAM,CAAC,oBAAoB,sCAAsCC,QAAQC,GAAG,IAC5EF,MAAM,CAAC,sBAAsB,4CAA4C,OACzEG,MAAM,CAAC,OAAOC;IACd,MAAMC,OAAOC,OAAOC,QAAQ,CAACH,QAAQC,IAAI,EAAE;IAC3C,MAAM,EAAEG,GAAG,EAAE,GAAGf,aAAa;QAC5BS,KAAKE,QAAQF,GAAG;QAChBG;QACAI,iBAAiBL,QAAQK,eAAe;IACzC;IAEAf,IAAIgB,IAAI,CAAC,CAAC,6BAA6B,EAAEL,MAAM;IAE/Cf,MAAM;QACLqB,OAAOH,IAAIG,KAAK;QAChBN;QACAO,UAAU;IACX;IAEAlB,IAAImB,OAAO,CAAC,CAAC,wCAAwC,EAAER,MAAM;AAC9D;AAED,2BAA2B;AAC3BJ,QAAQa,EAAE,CAAC,UAAU;IACpBpB,IAAIgB,IAAI,CAAC;IACTT,QAAQc,IAAI,CAAC;AACd;AACAd,QAAQa,EAAE,CAAC,WAAW;IACrBpB,IAAIgB,IAAI,CAAC;IACTT,QAAQc,IAAI,CAAC;AACd;AAEAnB,QAAQoB,UAAU,CAACf,QAAQgB,IAAI,EAAEC,KAAK,CAAC,CAACC;IACvCzB,IAAIyB,KAAK,CAACA,MAAMC,OAAO;IACvBnB,QAAQc,IAAI,CAAC;AACd"}
1
+ {"version":3,"sources":["../src/mcps-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport consola from 'consola';\nimport { createProgram } from './cli';\n\nconst log = consola.withTag('mcps');\n\nconst program = createProgram({\n\tsetup: async (ctx) => {\n\t\tconst { setupAudit } = await import('./audit/server/plugin.js');\n\t\tsetupAudit(ctx);\n\t},\n});\n\nprogram.parseAsync(process.argv).catch((error) => {\n\tlog.error(error.message);\n\tprocess.exit(1);\n});\n"],"names":["consola","createProgram","log","withTag","program","setup","ctx","setupAudit","parseAsync","process","argv","catch","error","message","exit"],"mappings":";AACA,OAAOA,aAAa,UAAU;AAC9B,SAASC,aAAa,QAAQ,QAAQ;AAEtC,MAAMC,MAAMF,QAAQG,OAAO,CAAC;AAE5B,MAAMC,UAAUH,cAAc;IAC7BI,OAAO,OAAOC;QACb,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;QACpCA,WAAWD;IACZ;AACD;AAEAF,QAAQI,UAAU,CAACC,QAAQC,IAAI,EAAEC,KAAK,CAAC,CAACC;IACvCV,IAAIU,KAAK,CAACA,MAAMC,OAAO;IACvBJ,QAAQK,IAAI,CAAC;AACd"}
@@ -0,0 +1,35 @@
1
+ import { FeishuMcpServerDef } from "@wener/ai/mcp/feishu";
2
+ import { HeaderNames } from "../../server/schema.js";
3
+ import { defineMcpServerHandler, registerMcpServerHandler } from "../McpServerHandlerDef.js";
4
+ export const FeishuMcpServerHandlerDef = defineMcpServerHandler(FeishuMcpServerDef, {
5
+ headerMappings: [
6
+ {
7
+ header: HeaderNames.FEISHU_APP_ID,
8
+ property: "appId",
9
+ required: true
10
+ },
11
+ {
12
+ header: HeaderNames.FEISHU_APP_SECRET,
13
+ property: "appSecret",
14
+ required: true
15
+ },
16
+ {
17
+ header: HeaderNames.FEISHU_DOMAIN,
18
+ property: "domain"
19
+ }
20
+ ],
21
+ resolveConfig(config, headers) {
22
+ const appId = config.appId || headers?.get(HeaderNames.FEISHU_APP_ID) || config.headers?.[HeaderNames.FEISHU_APP_ID];
23
+ const appSecret = config.appSecret || headers?.get(HeaderNames.FEISHU_APP_SECRET) || config.headers?.[HeaderNames.FEISHU_APP_SECRET];
24
+ if (!appId || !appSecret)
25
+ return null;
26
+ const domain = config.domain || headers?.get(HeaderNames.FEISHU_DOMAIN) || config.headers?.[HeaderNames.FEISHU_DOMAIN] || "feishu";
27
+ return {
28
+ appId,
29
+ appSecret,
30
+ domain
31
+ };
32
+ }
33
+ });
34
+ registerMcpServerHandler(FeishuMcpServerHandlerDef);
35
+ //# sourceMappingURL=def.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/providers/feishu/def.ts"],"sourcesContent":["import { FeishuMcpServerDef, type CreateFeishuMcpServerOptions } from '@wener/ai/mcp/feishu';\nimport { HeaderNames, type FeishuConfig } from '../../server/schema';\nimport { defineMcpServerHandler, registerMcpServerHandler } from '../McpServerHandlerDef';\n\nexport const FeishuMcpServerHandlerDef = defineMcpServerHandler<CreateFeishuMcpServerOptions, FeishuConfig>(\n\tFeishuMcpServerDef,\n\t{\n\t\theaderMappings: [\n\t\t\t{ header: HeaderNames.FEISHU_APP_ID, property: 'appId', required: true },\n\t\t\t{ header: HeaderNames.FEISHU_APP_SECRET, property: 'appSecret', required: true },\n\t\t\t{ header: HeaderNames.FEISHU_DOMAIN, property: 'domain' },\n\t\t],\n\n\t\tresolveConfig(config, headers) {\n\t\t\tconst appId =\n\t\t\t\tconfig.appId ||\n\t\t\t\theaders?.get(HeaderNames.FEISHU_APP_ID) ||\n\t\t\t\tconfig.headers?.[HeaderNames.FEISHU_APP_ID];\n\t\t\tconst appSecret =\n\t\t\t\tconfig.appSecret ||\n\t\t\t\theaders?.get(HeaderNames.FEISHU_APP_SECRET) ||\n\t\t\t\tconfig.headers?.[HeaderNames.FEISHU_APP_SECRET];\n\n\t\t\tif (!appId || !appSecret) return null;\n\n\t\t\tconst domain =\n\t\t\t\tconfig.domain ||\n\t\t\t\theaders?.get(HeaderNames.FEISHU_DOMAIN) ||\n\t\t\t\tconfig.headers?.[HeaderNames.FEISHU_DOMAIN] ||\n\t\t\t\t'feishu';\n\n\t\t\treturn { appId, appSecret, domain };\n\t\t},\n\t},\n);\n\nregisterMcpServerHandler(FeishuMcpServerHandlerDef);\n"],"names":["FeishuMcpServerDef","HeaderNames","defineMcpServerHandler","registerMcpServerHandler","FeishuMcpServerHandlerDef","headerMappings","header","FEISHU_APP_ID","property","required","FEISHU_APP_SECRET","FEISHU_DOMAIN","resolveConfig","config","headers","appId","get","appSecret","domain"],"mappings":"AAAA,SAASA,kBAAkB,QAA2C,uBAAuB;AAC7F,SAASC,WAAW,QAA2B,sBAAsB;AACrE,SAASC,sBAAsB,EAAEC,wBAAwB,QAAQ,yBAAyB;AAE1F,OAAO,MAAMC,4BAA4BF,uBACxCF,oBACA;IACCK,gBAAgB;QACf;YAAEC,QAAQL,YAAYM,aAAa;YAAEC,UAAU;YAASC,UAAU;QAAK;QACvE;YAAEH,QAAQL,YAAYS,iBAAiB;YAAEF,UAAU;YAAaC,UAAU;QAAK;QAC/E;YAAEH,QAAQL,YAAYU,aAAa;YAAEH,UAAU;QAAS;KACxD;IAEDI,eAAcC,MAAM,EAAEC,OAAO;QAC5B,MAAMC,QACLF,OAAOE,KAAK,IACZD,SAASE,IAAIf,YAAYM,aAAa,KACtCM,OAAOC,OAAO,EAAE,CAACb,YAAYM,aAAa,CAAC;QAC5C,MAAMU,YACLJ,OAAOI,SAAS,IAChBH,SAASE,IAAIf,YAAYS,iBAAiB,KAC1CG,OAAOC,OAAO,EAAE,CAACb,YAAYS,iBAAiB,CAAC;QAEhD,IAAI,CAACK,SAAS,CAACE,WAAW,OAAO;QAEjC,MAAMC,SACLL,OAAOK,MAAM,IACbJ,SAASE,IAAIf,YAAYU,aAAa,KACtCE,OAAOC,OAAO,EAAE,CAACb,YAAYU,aAAa,CAAC,IAC3C;QAED,OAAO;YAAEI;YAAOE;YAAWC;QAAO;IACnC;AACD,GACC;AAEFf,yBAAyBC"}
@@ -4,6 +4,7 @@ import "./prometheus/def.js";
4
4
  import "./tencent-cls/def.js";
5
5
  import "./sql/def.js";
6
6
  import "./relay/def.js";
7
+ import "./feishu/def.js";
7
8
  /**
8
9
  * Find MCP server definitions matching a predicate
9
10
  */ export function findMcpServerDef(predicate) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/findMcpServerDef.ts"],"sourcesContent":["import { getAllMcpServerHandlerDefs, type McpServerHandlerDef } from './McpServerHandlerDef';\n// Import all server definitions to ensure they are registered\nimport './prometheus/def';\nimport './tencent-cls/def';\nimport './sql/def';\nimport './relay/def';\n\n/**\n * Find MCP server definitions matching a predicate\n */\nexport function findMcpServerDef(predicate?: (def: McpServerHandlerDef) => boolean): McpServerHandlerDef[] {\n\tconst all = getAllMcpServerHandlerDefs();\n\tif (!predicate) {\n\t\treturn all;\n\t}\n\treturn all.filter(predicate);\n}\n\n/**\n * Resolve a single MCP server definition by name\n */\nexport function resolveMcpServerDef(name: string): McpServerHandlerDef | undefined {\n\treturn findMcpServerDef((v) => v.name === name)[0];\n}\n\n/**\n * Get total count of registered MCP server types\n */\nexport function getMcpServerDefCount(): number {\n\treturn getAllMcpServerHandlerDefs().length;\n}\n"],"names":["getAllMcpServerHandlerDefs","findMcpServerDef","predicate","all","filter","resolveMcpServerDef","name","v","getMcpServerDefCount","length"],"mappings":"AAAA,SAASA,0BAA0B,QAAkC,wBAAwB;AAC7F,8DAA8D;AAC9D,OAAO,mBAAmB;AAC1B,OAAO,oBAAoB;AAC3B,OAAO,YAAY;AACnB,OAAO,cAAc;AAErB;;CAEC,GACD,OAAO,SAASC,iBAAiBC,SAAiD;IACjF,MAAMC,MAAMH;IACZ,IAAI,CAACE,WAAW;QACf,OAAOC;IACR;IACA,OAAOA,IAAIC,MAAM,CAACF;AACnB;AAEA;;CAEC,GACD,OAAO,SAASG,oBAAoBC,IAAY;IAC/C,OAAOL,iBAAiB,CAACM,IAAMA,EAAED,IAAI,KAAKA,KAAK,CAAC,EAAE;AACnD;AAEA;;CAEC,GACD,OAAO,SAASE;IACf,OAAOR,6BAA6BS,MAAM;AAC3C"}
1
+ {"version":3,"sources":["../../src/providers/findMcpServerDef.ts"],"sourcesContent":["import { getAllMcpServerHandlerDefs, type McpServerHandlerDef } from './McpServerHandlerDef';\n// Import all server definitions to ensure they are registered\nimport './prometheus/def';\nimport './tencent-cls/def';\nimport './sql/def';\nimport './relay/def';\nimport './feishu/def';\n\n/**\n * Find MCP server definitions matching a predicate\n */\nexport function findMcpServerDef(predicate?: (def: McpServerHandlerDef) => boolean): McpServerHandlerDef[] {\n\tconst all = getAllMcpServerHandlerDefs();\n\tif (!predicate) {\n\t\treturn all;\n\t}\n\treturn all.filter(predicate);\n}\n\n/**\n * Resolve a single MCP server definition by name\n */\nexport function resolveMcpServerDef(name: string): McpServerHandlerDef | undefined {\n\treturn findMcpServerDef((v) => v.name === name)[0];\n}\n\n/**\n * Get total count of registered MCP server types\n */\nexport function getMcpServerDefCount(): number {\n\treturn getAllMcpServerHandlerDefs().length;\n}\n"],"names":["getAllMcpServerHandlerDefs","findMcpServerDef","predicate","all","filter","resolveMcpServerDef","name","v","getMcpServerDefCount","length"],"mappings":"AAAA,SAASA,0BAA0B,QAAkC,wBAAwB;AAC7F,8DAA8D;AAC9D,OAAO,mBAAmB;AAC1B,OAAO,oBAAoB;AAC3B,OAAO,YAAY;AACnB,OAAO,cAAc;AACrB,OAAO,eAAe;AAEtB;;CAEC,GACD,OAAO,SAASC,iBAAiBC,SAAiD;IACjF,MAAMC,MAAMH;IACZ,IAAI,CAACE,WAAW;QACf,OAAOC;IACR;IACA,OAAOA,IAAIC,MAAM,CAACF;AACnB;AAEA;;CAEC,GACD,OAAO,SAASG,oBAAoBC,IAAY;IAC/C,OAAOL,iBAAiB,CAACM,IAAMA,EAAED,IAAI,KAAKA,KAAK,CAAC,EAAE;AACnD;AAEA;;CAEC,GACD,OAAO,SAASE;IACf,OAAOR,6BAA6BS,MAAM;AAC3C"}
@@ -40,9 +40,15 @@ const commonOptions = {
40
40
  // External native modules
41
41
  external: [
42
42
  'better-sqlite3',
43
+ 'bun:sqlite',
44
+ 'kysely-bun-sqlite',
43
45
  'oracledb',
44
46
  'mariadb/callback',
45
- 'mysql'
47
+ 'mysql',
48
+ '@nestjs/websockets',
49
+ '@nestjs/microservices',
50
+ '@nestjs/platform-express',
51
+ '@larksuiteoapi/node-sdk'
46
52
  ]
47
53
  };
48
54
  // Ensure dist directory exists
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/scripts/bundle.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'node:fs';\nimport * as esbuild from 'esbuild';\n\nconst pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));\n\nconst banner = `// Bundled with esbuild\n// ${pkg.name}@${pkg.version}\n\nvar require,__filename,__dirname;\n{\n const {createRequire} = await import('node:module');\n require ||= createRequire(import.meta.url);\n}\n{\n const {fileURLToPath} = await import('node:url');\n const {dirname} = await import('node:path');\n __filename ||= fileURLToPath(import.meta.url);\n __dirname ||= dirname(__filename)\n};\n`;\n\nconst commonOptions: esbuild.BuildOptions = {\n\tbundle: true,\n\tlogLevel: 'info',\n\tbanner: { js: banner },\n\tdefine: {\n\t\tNODE_ENV: JSON.stringify('production'),\n\t\t__DEV__: JSON.stringify(false),\n\t\t'process.env.NODE_ENV': JSON.stringify('production'),\n\t},\n\tkeepNames: true,\n\ttreeShaking: true,\n\tminifySyntax: true,\n\tformat: 'esm',\n\tplatform: 'node',\n\tcharset: 'utf8',\n\ttarget: 'node18',\n\tsourcemap: false,\n\tlegalComments: 'none',\n\t// External native modules\n\texternal: ['better-sqlite3', 'oracledb', 'mariadb/callback', 'mysql'],\n};\n\n// Ensure dist directory exists\nfs.mkdirSync('dist', { recursive: true });\n\nconsole.log('Building MCPS...');\n\n// Build library entry (index.ts)\nconst libResult = await esbuild.build({\n\t...commonOptions,\n\tentryPoints: ['src/index.ts'],\n\toutfile: 'dist/index.mjs',\n});\n\n// Build CLI entry (mcps-cli.ts)\nconst cliResult = await esbuild.build({\n\t...commonOptions,\n\tentryPoints: ['src/mcps-cli.ts'],\n\toutfile: 'dist/mcps-cli.mjs',\n});\n\nif (libResult.errors.length === 0 && cliResult.errors.length === 0) {\n\t// Process CLI output - add shebang\n\tconst cliOutfile = 'dist/mcps-cli.mjs';\n\tlet content = fs.readFileSync(cliOutfile, 'utf-8');\n\tcontent = content.replace(/^#!.*\\n/gm, '');\n\tfs.writeFileSync(cliOutfile, `#!/usr/bin/env node\\n${content}`);\n\tfs.chmodSync(cliOutfile, 0o755);\n\n\tconst libStats = fs.statSync('dist/index.mjs');\n\tconst cliStats = fs.statSync(cliOutfile);\n\n\tconsole.log(`✅ Build successful!`);\n\tconsole.log(`📦 Library: dist/index.mjs (${(libStats.size / 1024).toFixed(1)}KB)`);\n\tconsole.log(`📦 CLI: ${cliOutfile} (${(cliStats.size / 1024).toFixed(1)}KB)`);\n\tconsole.log(`🚀 Ready for npm publish and npx usage`);\n} else {\n\tconsole.error('❌ Build failed:', [...libResult.errors, ...cliResult.errors]);\n\tprocess.exit(1);\n}\n"],"names":["fs","esbuild","pkg","JSON","parse","readFileSync","banner","name","version","commonOptions","bundle","logLevel","js","define","NODE_ENV","stringify","__DEV__","keepNames","treeShaking","minifySyntax","format","platform","charset","target","sourcemap","legalComments","external","mkdirSync","recursive","console","log","libResult","build","entryPoints","outfile","cliResult","errors","length","cliOutfile","content","replace","writeFileSync","chmodSync","libStats","statSync","cliStats","size","toFixed","error","process","exit"],"mappings":";AACA,OAAOA,QAAQ,UAAU;AACzB,YAAYC,aAAa,UAAU;AAEnC,MAAMC,MAAMC,KAAKC,KAAK,CAACJ,GAAGK,YAAY,CAAC,gBAAgB;AAEvD,MAAMC,SAAS,CAAC;GACb,EAAEJ,IAAIK,IAAI,CAAC,CAAC,EAAEL,IAAIM,OAAO,CAAC;;;;;;;;;;;;;AAa7B,CAAC;AAED,MAAMC,gBAAsC;IAC3CC,QAAQ;IACRC,UAAU;IACVL,QAAQ;QAAEM,IAAIN;IAAO;IACrBO,QAAQ;QACPC,UAAUX,KAAKY,SAAS,CAAC;QACzBC,SAASb,KAAKY,SAAS,CAAC;QACxB,wBAAwBZ,KAAKY,SAAS,CAAC;IACxC;IACAE,WAAW;IACXC,aAAa;IACbC,cAAc;IACdC,QAAQ;IACRC,UAAU;IACVC,SAAS;IACTC,QAAQ;IACRC,WAAW;IACXC,eAAe;IACf,0BAA0B;IAC1BC,UAAU;QAAC;QAAkB;QAAY;QAAoB;KAAQ;AACtE;AAEA,+BAA+B;AAC/B1B,GAAG2B,SAAS,CAAC,QAAQ;IAAEC,WAAW;AAAK;AAEvCC,QAAQC,GAAG,CAAC;AAEZ,iCAAiC;AACjC,MAAMC,YAAY,MAAM9B,QAAQ+B,KAAK,CAAC;IACrC,GAAGvB,aAAa;IAChBwB,aAAa;QAAC;KAAe;IAC7BC,SAAS;AACV;AAEA,gCAAgC;AAChC,MAAMC,YAAY,MAAMlC,QAAQ+B,KAAK,CAAC;IACrC,GAAGvB,aAAa;IAChBwB,aAAa;QAAC;KAAkB;IAChCC,SAAS;AACV;AAEA,IAAIH,UAAUK,MAAM,CAACC,MAAM,KAAK,KAAKF,UAAUC,MAAM,CAACC,MAAM,KAAK,GAAG;IACnE,mCAAmC;IACnC,MAAMC,aAAa;IACnB,IAAIC,UAAUvC,GAAGK,YAAY,CAACiC,YAAY;IAC1CC,UAAUA,QAAQC,OAAO,CAAC,aAAa;IACvCxC,GAAGyC,aAAa,CAACH,YAAY,CAAC,qBAAqB,EAAEC,SAAS;IAC9DvC,GAAG0C,SAAS,CAACJ,YAAY;IAEzB,MAAMK,WAAW3C,GAAG4C,QAAQ,CAAC;IAC7B,MAAMC,WAAW7C,GAAG4C,QAAQ,CAACN;IAE7BT,QAAQC,GAAG,CAAC,CAAC,mBAAmB,CAAC;IACjCD,QAAQC,GAAG,CAAC,CAAC,4BAA4B,EAAE,AAACa,CAAAA,SAASG,IAAI,GAAG,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;IACjFlB,QAAQC,GAAG,CAAC,CAAC,QAAQ,EAAEQ,WAAW,EAAE,EAAE,AAACO,CAAAA,SAASC,IAAI,GAAG,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;IAC5ElB,QAAQC,GAAG,CAAC,CAAC,sCAAsC,CAAC;AACrD,OAAO;IACND,QAAQmB,KAAK,CAAC,mBAAmB;WAAIjB,UAAUK,MAAM;WAAKD,UAAUC,MAAM;KAAC;IAC3Ea,QAAQC,IAAI,CAAC;AACd"}
1
+ {"version":3,"sources":["../../src/scripts/bundle.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from 'node:fs';\nimport * as esbuild from 'esbuild';\n\nconst pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));\n\nconst banner = `// Bundled with esbuild\n// ${pkg.name}@${pkg.version}\n\nvar require,__filename,__dirname;\n{\n const {createRequire} = await import('node:module');\n require ||= createRequire(import.meta.url);\n}\n{\n const {fileURLToPath} = await import('node:url');\n const {dirname} = await import('node:path');\n __filename ||= fileURLToPath(import.meta.url);\n __dirname ||= dirname(__filename)\n};\n`;\n\nconst commonOptions: esbuild.BuildOptions = {\n\tbundle: true,\n\tlogLevel: 'info',\n\tbanner: { js: banner },\n\tdefine: {\n\t\tNODE_ENV: JSON.stringify('production'),\n\t\t__DEV__: JSON.stringify(false),\n\t\t'process.env.NODE_ENV': JSON.stringify('production'),\n\t},\n\tkeepNames: true,\n\ttreeShaking: true,\n\tminifySyntax: true,\n\tformat: 'esm',\n\tplatform: 'node',\n\tcharset: 'utf8',\n\ttarget: 'node18',\n\tsourcemap: false,\n\tlegalComments: 'none',\n\t// External native modules\n\texternal: [\n\t\t'better-sqlite3',\n\t\t'bun:sqlite',\n\t\t'kysely-bun-sqlite',\n\t\t'oracledb',\n\t\t'mariadb/callback',\n\t\t'mysql',\n\t\t'@nestjs/websockets',\n\t\t'@nestjs/microservices',\n\t\t'@nestjs/platform-express',\n\t\t'@larksuiteoapi/node-sdk',\n\t],\n};\n\n// Ensure dist directory exists\nfs.mkdirSync('dist', { recursive: true });\n\nconsole.log('Building MCPS...');\n\n// Build library entry (index.ts)\nconst libResult = await esbuild.build({\n\t...commonOptions,\n\tentryPoints: ['src/index.ts'],\n\toutfile: 'dist/index.mjs',\n});\n\n// Build CLI entry (mcps-cli.ts)\nconst cliResult = await esbuild.build({\n\t...commonOptions,\n\tentryPoints: ['src/mcps-cli.ts'],\n\toutfile: 'dist/mcps-cli.mjs',\n});\n\nif (libResult.errors.length === 0 && cliResult.errors.length === 0) {\n\t// Process CLI output - add shebang\n\tconst cliOutfile = 'dist/mcps-cli.mjs';\n\tlet content = fs.readFileSync(cliOutfile, 'utf-8');\n\tcontent = content.replace(/^#!.*\\n/gm, '');\n\tfs.writeFileSync(cliOutfile, `#!/usr/bin/env node\\n${content}`);\n\tfs.chmodSync(cliOutfile, 0o755);\n\n\tconst libStats = fs.statSync('dist/index.mjs');\n\tconst cliStats = fs.statSync(cliOutfile);\n\n\tconsole.log(`✅ Build successful!`);\n\tconsole.log(`📦 Library: dist/index.mjs (${(libStats.size / 1024).toFixed(1)}KB)`);\n\tconsole.log(`📦 CLI: ${cliOutfile} (${(cliStats.size / 1024).toFixed(1)}KB)`);\n\tconsole.log(`🚀 Ready for npm publish and npx usage`);\n} else {\n\tconsole.error('❌ Build failed:', [...libResult.errors, ...cliResult.errors]);\n\tprocess.exit(1);\n}\n"],"names":["fs","esbuild","pkg","JSON","parse","readFileSync","banner","name","version","commonOptions","bundle","logLevel","js","define","NODE_ENV","stringify","__DEV__","keepNames","treeShaking","minifySyntax","format","platform","charset","target","sourcemap","legalComments","external","mkdirSync","recursive","console","log","libResult","build","entryPoints","outfile","cliResult","errors","length","cliOutfile","content","replace","writeFileSync","chmodSync","libStats","statSync","cliStats","size","toFixed","error","process","exit"],"mappings":";AACA,OAAOA,QAAQ,UAAU;AACzB,YAAYC,aAAa,UAAU;AAEnC,MAAMC,MAAMC,KAAKC,KAAK,CAACJ,GAAGK,YAAY,CAAC,gBAAgB;AAEvD,MAAMC,SAAS,CAAC;GACb,EAAEJ,IAAIK,IAAI,CAAC,CAAC,EAAEL,IAAIM,OAAO,CAAC;;;;;;;;;;;;;AAa7B,CAAC;AAED,MAAMC,gBAAsC;IAC3CC,QAAQ;IACRC,UAAU;IACVL,QAAQ;QAAEM,IAAIN;IAAO;IACrBO,QAAQ;QACPC,UAAUX,KAAKY,SAAS,CAAC;QACzBC,SAASb,KAAKY,SAAS,CAAC;QACxB,wBAAwBZ,KAAKY,SAAS,CAAC;IACxC;IACAE,WAAW;IACXC,aAAa;IACbC,cAAc;IACdC,QAAQ;IACRC,UAAU;IACVC,SAAS;IACTC,QAAQ;IACRC,WAAW;IACXC,eAAe;IACf,0BAA0B;IAC1BC,UAAU;QACT;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACA;AACF;AAEA,+BAA+B;AAC/B1B,GAAG2B,SAAS,CAAC,QAAQ;IAAEC,WAAW;AAAK;AAEvCC,QAAQC,GAAG,CAAC;AAEZ,iCAAiC;AACjC,MAAMC,YAAY,MAAM9B,QAAQ+B,KAAK,CAAC;IACrC,GAAGvB,aAAa;IAChBwB,aAAa;QAAC;KAAe;IAC7BC,SAAS;AACV;AAEA,gCAAgC;AAChC,MAAMC,YAAY,MAAMlC,QAAQ+B,KAAK,CAAC;IACrC,GAAGvB,aAAa;IAChBwB,aAAa;QAAC;KAAkB;IAChCC,SAAS;AACV;AAEA,IAAIH,UAAUK,MAAM,CAACC,MAAM,KAAK,KAAKF,UAAUC,MAAM,CAACC,MAAM,KAAK,GAAG;IACnE,mCAAmC;IACnC,MAAMC,aAAa;IACnB,IAAIC,UAAUvC,GAAGK,YAAY,CAACiC,YAAY;IAC1CC,UAAUA,QAAQC,OAAO,CAAC,aAAa;IACvCxC,GAAGyC,aAAa,CAACH,YAAY,CAAC,qBAAqB,EAAEC,SAAS;IAC9DvC,GAAG0C,SAAS,CAACJ,YAAY;IAEzB,MAAMK,WAAW3C,GAAG4C,QAAQ,CAAC;IAC7B,MAAMC,WAAW7C,GAAG4C,QAAQ,CAACN;IAE7BT,QAAQC,GAAG,CAAC,CAAC,mBAAmB,CAAC;IACjCD,QAAQC,GAAG,CAAC,CAAC,4BAA4B,EAAE,AAACa,CAAAA,SAASG,IAAI,GAAG,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;IACjFlB,QAAQC,GAAG,CAAC,CAAC,QAAQ,EAAEQ,WAAW,EAAE,EAAE,AAACO,CAAAA,SAASC,IAAI,GAAG,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;IAC5ElB,QAAQC,GAAG,CAAC,CAAC,sCAAsC,CAAC;AACrD,OAAO;IACND,QAAQmB,KAAK,CAAC,mBAAmB;WAAIjB,UAAUK,MAAM;WAAKD,UAAUC,MAAM;KAAC;IAC3Ea,QAAQC,IAAI,CAAC;AACd"}
@@ -5,19 +5,18 @@ import { RPCHandler } from "@orpc/server/fetch";
5
5
  import { CORSPlugin } from "@orpc/server/plugins";
6
6
  import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
7
7
  import { html } from "hono/html";
8
- import { AuditRouter } from "./audit.js";
9
8
  import { createMcpsRouter } from "./mcps-router.js";
10
9
  /**
11
- * Register oRPC API routes for audit and MCPS
12
- */ export function registerApiRoutes({ app, config }) {
13
- // Create MCPS router with config context
10
+ * Register oRPC API routes for MCPS.
11
+ * Audit router is not included by default - use setupAudit() plugin to add it.
12
+ */ export function registerApiRoutes({ app, config, apiRouters, statsProvider }) {
14
13
  const McpsRouter = createMcpsRouter({
15
- config
14
+ config,
15
+ statsProvider
16
16
  });
17
- // Combined router for all APIs
18
17
  const combinedRouter = {
19
- audit: AuditRouter,
20
- mcps: McpsRouter
18
+ mcps: McpsRouter,
19
+ ...apiRouters
21
20
  };
22
21
  const handleByRpc = new RPCHandler(combinedRouter);
23
22
  const handleByOpenAPI = new OpenAPIHandler(combinedRouter, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/api-routes.ts"],"sourcesContent":["import { SmartCoercionPlugin } from '@orpc/json-schema';\nimport { OpenAPIGenerator } from '@orpc/openapi';\nimport { OpenAPIHandler } from '@orpc/openapi/fetch';\nimport { RPCHandler } from '@orpc/server/fetch';\nimport { CORSPlugin } from '@orpc/server/plugins';\nimport { ZodToJsonSchemaConverter } from '@orpc/zod/zod4';\nimport type { Hono } from 'hono';\nimport { html } from 'hono/html';\nimport { AuditRouter } from './audit';\nimport { createMcpsRouter } from './mcps-router';\nimport type { McpsConfig } from './schema';\n\nexport interface RegisterApiRoutesOptions {\n\tapp: Hono;\n\tconfig: McpsConfig;\n}\n\n/**\n * Register oRPC API routes for audit and MCPS\n */\nexport function registerApiRoutes({ app, config }: RegisterApiRoutesOptions) {\n\t// Create MCPS router with config context\n\tconst McpsRouter = createMcpsRouter({ config });\n\n\t// Combined router for all APIs\n\tconst combinedRouter = {\n\t\taudit: AuditRouter,\n\t\tmcps: McpsRouter,\n\t};\n\n\tconst handleByRpc = new RPCHandler(combinedRouter);\n\tconst handleByOpenAPI = new OpenAPIHandler(combinedRouter, {\n\t\tplugins: [\n\t\t\tnew SmartCoercionPlugin({\n\t\t\t\tschemaConverters: [new ZodToJsonSchemaConverter()],\n\t\t\t}),\n\t\t\tnew CORSPlugin({\n\t\t\t\texposeHeaders: ['Content-Disposition'],\n\t\t\t}),\n\t\t],\n\t});\n\n\tapp.use('/api/rpc/*', async (c, next) => {\n\t\tconst { matched, response } = await handleByRpc.handle(c.req.raw, {\n\t\t\tprefix: '/api/rpc',\n\t\t\tcontext: {},\n\t\t});\n\t\tif (matched) {\n\t\t\treturn c.newResponse(response.body, response);\n\t\t}\n\t\treturn next();\n\t});\n\n\tapp.use('/api/*', async (c, next) => {\n\t\tconst { matched, response } = await handleByOpenAPI.handle(c.req.raw, {\n\t\t\tprefix: '/api',\n\t\t\tcontext: {},\n\t\t});\n\t\tif (matched) {\n\t\t\treturn c.newResponse(response.body, response);\n\t\t}\n\t\treturn next();\n\t});\n\n\t// OpenAPI spec\n\tconst openAPIGenerator = new OpenAPIGenerator({\n\t\tschemaConverters: [new ZodToJsonSchemaConverter()],\n\t});\n\tlet specCache: unknown;\n\n\tapp.get('/api/spec.json', async (c) => {\n\t\tif (!specCache) {\n\t\t\tspecCache = await openAPIGenerator.generate(combinedRouter, {\n\t\t\t\tinfo: { title: 'MCPS API', version: '1.0.0' },\n\t\t\t\tservers: [{ url: '/api' }],\n\t\t\t});\n\t\t}\n\t\treturn c.json(specCache);\n\t});\n\n\t// Swagger UI\n\tapp.get('/api/docs', (c) => {\n\t\treturn c.html(\n\t\t\thtml`<!doctype html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t<head>\n\t\t\t\t\t\t<title>MCPS API</title>\n\t\t\t\t\t\t<meta charset=\"utf-8\" />\n\t\t\t\t\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t\t\t\t</head>\n\t\t\t\t\t<body>\n\t\t\t\t\t\t<script id=\"api-reference\" data-url=\"/api/spec.json\"></script>\n\t\t\t\t\t\t<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n\t\t\t\t\t</body>\n\t\t\t\t</html>`,\n\t\t);\n\t});\n}\n"],"names":["SmartCoercionPlugin","OpenAPIGenerator","OpenAPIHandler","RPCHandler","CORSPlugin","ZodToJsonSchemaConverter","html","AuditRouter","createMcpsRouter","registerApiRoutes","app","config","McpsRouter","combinedRouter","audit","mcps","handleByRpc","handleByOpenAPI","plugins","schemaConverters","exposeHeaders","use","c","next","matched","response","handle","req","raw","prefix","context","newResponse","body","openAPIGenerator","specCache","get","generate","info","title","version","servers","url","json"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,oBAAoB;AACxD,SAASC,gBAAgB,QAAQ,gBAAgB;AACjD,SAASC,cAAc,QAAQ,sBAAsB;AACrD,SAASC,UAAU,QAAQ,qBAAqB;AAChD,SAASC,UAAU,QAAQ,uBAAuB;AAClD,SAASC,wBAAwB,QAAQ,iBAAiB;AAE1D,SAASC,IAAI,QAAQ,YAAY;AACjC,SAASC,WAAW,QAAQ,UAAU;AACtC,SAASC,gBAAgB,QAAQ,gBAAgB;AAQjD;;CAEC,GACD,OAAO,SAASC,kBAAkB,EAAEC,GAAG,EAAEC,MAAM,EAA4B;IAC1E,yCAAyC;IACzC,MAAMC,aAAaJ,iBAAiB;QAAEG;IAAO;IAE7C,+BAA+B;IAC/B,MAAME,iBAAiB;QACtBC,OAAOP;QACPQ,MAAMH;IACP;IAEA,MAAMI,cAAc,IAAIb,WAAWU;IACnC,MAAMI,kBAAkB,IAAIf,eAAeW,gBAAgB;QAC1DK,SAAS;YACR,IAAIlB,oBAAoB;gBACvBmB,kBAAkB;oBAAC,IAAId;iBAA2B;YACnD;YACA,IAAID,WAAW;gBACdgB,eAAe;oBAAC;iBAAsB;YACvC;SACA;IACF;IAEAV,IAAIW,GAAG,CAAC,cAAc,OAAOC,GAAGC;QAC/B,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAG,MAAMT,YAAYU,MAAM,CAACJ,EAAEK,GAAG,CAACC,GAAG,EAAE;YACjEC,QAAQ;YACRC,SAAS,CAAC;QACX;QACA,IAAIN,SAAS;YACZ,OAAOF,EAAES,WAAW,CAACN,SAASO,IAAI,EAAEP;QACrC;QACA,OAAOF;IACR;IAEAb,IAAIW,GAAG,CAAC,UAAU,OAAOC,GAAGC;QAC3B,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAG,MAAMR,gBAAgBS,MAAM,CAACJ,EAAEK,GAAG,CAACC,GAAG,EAAE;YACrEC,QAAQ;YACRC,SAAS,CAAC;QACX;QACA,IAAIN,SAAS;YACZ,OAAOF,EAAES,WAAW,CAACN,SAASO,IAAI,EAAEP;QACrC;QACA,OAAOF;IACR;IAEA,eAAe;IACf,MAAMU,mBAAmB,IAAIhC,iBAAiB;QAC7CkB,kBAAkB;YAAC,IAAId;SAA2B;IACnD;IACA,IAAI6B;IAEJxB,IAAIyB,GAAG,CAAC,kBAAkB,OAAOb;QAChC,IAAI,CAACY,WAAW;YACfA,YAAY,MAAMD,iBAAiBG,QAAQ,CAACvB,gBAAgB;gBAC3DwB,MAAM;oBAAEC,OAAO;oBAAYC,SAAS;gBAAQ;gBAC5CC,SAAS;oBAAC;wBAAEC,KAAK;oBAAO;iBAAE;YAC3B;QACD;QACA,OAAOnB,EAAEoB,IAAI,CAACR;IACf;IAEA,aAAa;IACbxB,IAAIyB,GAAG,CAAC,aAAa,CAACb;QACrB,OAAOA,EAAEhB,IAAI,CACZA,IAAI,CAAC;;;;;;;;;;;WAWG,CAAC;IAEX;AACD"}
1
+ {"version":3,"sources":["../../src/server/api-routes.ts"],"sourcesContent":["import { SmartCoercionPlugin } from '@orpc/json-schema';\nimport { OpenAPIGenerator } from '@orpc/openapi';\nimport { OpenAPIHandler } from '@orpc/openapi/fetch';\nimport { RPCHandler } from '@orpc/server/fetch';\nimport { CORSPlugin } from '@orpc/server/plugins';\nimport { ZodToJsonSchemaConverter } from '@orpc/zod/zod4';\nimport type { Hono } from 'hono';\nimport { html } from 'hono/html';\nimport { createMcpsRouter } from './mcps-router';\nimport type { McpsConfig } from './schema';\nimport type { StatsProvider } from './server';\n\nexport interface RegisterApiRoutesOptions {\n\tapp: Hono;\n\tconfig: McpsConfig;\n\t/** Additional oRPC routers registered by plugins (e.g. audit) */\n\tapiRouters?: Record<string, any>;\n\t/** Optional stats provider from audit plugin */\n\tstatsProvider?: StatsProvider;\n}\n\n/**\n * Register oRPC API routes for MCPS.\n * Audit router is not included by default - use setupAudit() plugin to add it.\n */\nexport function registerApiRoutes({ app, config, apiRouters, statsProvider }: RegisterApiRoutesOptions) {\n\tconst McpsRouter = createMcpsRouter({ config, statsProvider });\n\n\tconst combinedRouter: Record<string, any> = {\n\t\tmcps: McpsRouter,\n\t\t...apiRouters,\n\t};\n\n\tconst handleByRpc = new RPCHandler(combinedRouter);\n\tconst handleByOpenAPI = new OpenAPIHandler(combinedRouter, {\n\t\tplugins: [\n\t\t\tnew SmartCoercionPlugin({\n\t\t\t\tschemaConverters: [new ZodToJsonSchemaConverter()],\n\t\t\t}),\n\t\t\tnew CORSPlugin({\n\t\t\t\texposeHeaders: ['Content-Disposition'],\n\t\t\t}),\n\t\t],\n\t});\n\n\tapp.use('/api/rpc/*', async (c, next) => {\n\t\tconst { matched, response } = await handleByRpc.handle(c.req.raw, {\n\t\t\tprefix: '/api/rpc',\n\t\t\tcontext: {},\n\t\t});\n\t\tif (matched) {\n\t\t\treturn c.newResponse(response.body, response);\n\t\t}\n\t\treturn next();\n\t});\n\n\tapp.use('/api/*', async (c, next) => {\n\t\tconst { matched, response } = await handleByOpenAPI.handle(c.req.raw, {\n\t\t\tprefix: '/api',\n\t\t\tcontext: {},\n\t\t});\n\t\tif (matched) {\n\t\t\treturn c.newResponse(response.body, response);\n\t\t}\n\t\treturn next();\n\t});\n\n\t// OpenAPI spec\n\tconst openAPIGenerator = new OpenAPIGenerator({\n\t\tschemaConverters: [new ZodToJsonSchemaConverter()],\n\t});\n\tlet specCache: unknown;\n\n\tapp.get('/api/spec.json', async (c) => {\n\t\tif (!specCache) {\n\t\t\tspecCache = await openAPIGenerator.generate(combinedRouter, {\n\t\t\t\tinfo: { title: 'MCPS API', version: '1.0.0' },\n\t\t\t\tservers: [{ url: '/api' }],\n\t\t\t});\n\t\t}\n\t\treturn c.json(specCache);\n\t});\n\n\t// Swagger UI\n\tapp.get('/api/docs', (c) => {\n\t\treturn c.html(\n\t\t\thtml`<!doctype html>\n\t\t\t\t<html lang=\"en\">\n\t\t\t\t\t<head>\n\t\t\t\t\t\t<title>MCPS API</title>\n\t\t\t\t\t\t<meta charset=\"utf-8\" />\n\t\t\t\t\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t\t\t\t</head>\n\t\t\t\t\t<body>\n\t\t\t\t\t\t<script id=\"api-reference\" data-url=\"/api/spec.json\"></script>\n\t\t\t\t\t\t<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n\t\t\t\t\t</body>\n\t\t\t\t</html>`,\n\t\t);\n\t});\n}\n"],"names":["SmartCoercionPlugin","OpenAPIGenerator","OpenAPIHandler","RPCHandler","CORSPlugin","ZodToJsonSchemaConverter","html","createMcpsRouter","registerApiRoutes","app","config","apiRouters","statsProvider","McpsRouter","combinedRouter","mcps","handleByRpc","handleByOpenAPI","plugins","schemaConverters","exposeHeaders","use","c","next","matched","response","handle","req","raw","prefix","context","newResponse","body","openAPIGenerator","specCache","get","generate","info","title","version","servers","url","json"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,oBAAoB;AACxD,SAASC,gBAAgB,QAAQ,gBAAgB;AACjD,SAASC,cAAc,QAAQ,sBAAsB;AACrD,SAASC,UAAU,QAAQ,qBAAqB;AAChD,SAASC,UAAU,QAAQ,uBAAuB;AAClD,SAASC,wBAAwB,QAAQ,iBAAiB;AAE1D,SAASC,IAAI,QAAQ,YAAY;AACjC,SAASC,gBAAgB,QAAQ,gBAAgB;AAajD;;;CAGC,GACD,OAAO,SAASC,kBAAkB,EAAEC,GAAG,EAAEC,MAAM,EAAEC,UAAU,EAAEC,aAAa,EAA4B;IACrG,MAAMC,aAAaN,iBAAiB;QAAEG;QAAQE;IAAc;IAE5D,MAAME,iBAAsC;QAC3CC,MAAMF;QACN,GAAGF,UAAU;IACd;IAEA,MAAMK,cAAc,IAAIb,WAAWW;IACnC,MAAMG,kBAAkB,IAAIf,eAAeY,gBAAgB;QAC1DI,SAAS;YACR,IAAIlB,oBAAoB;gBACvBmB,kBAAkB;oBAAC,IAAId;iBAA2B;YACnD;YACA,IAAID,WAAW;gBACdgB,eAAe;oBAAC;iBAAsB;YACvC;SACA;IACF;IAEAX,IAAIY,GAAG,CAAC,cAAc,OAAOC,GAAGC;QAC/B,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAG,MAAMT,YAAYU,MAAM,CAACJ,EAAEK,GAAG,CAACC,GAAG,EAAE;YACjEC,QAAQ;YACRC,SAAS,CAAC;QACX;QACA,IAAIN,SAAS;YACZ,OAAOF,EAAES,WAAW,CAACN,SAASO,IAAI,EAAEP;QACrC;QACA,OAAOF;IACR;IAEAd,IAAIY,GAAG,CAAC,UAAU,OAAOC,GAAGC;QAC3B,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAG,MAAMR,gBAAgBS,MAAM,CAACJ,EAAEK,GAAG,CAACC,GAAG,EAAE;YACrEC,QAAQ;YACRC,SAAS,CAAC;QACX;QACA,IAAIN,SAAS;YACZ,OAAOF,EAAES,WAAW,CAACN,SAASO,IAAI,EAAEP;QACrC;QACA,OAAOF;IACR;IAEA,eAAe;IACf,MAAMU,mBAAmB,IAAIhC,iBAAiB;QAC7CkB,kBAAkB;YAAC,IAAId;SAA2B;IACnD;IACA,IAAI6B;IAEJzB,IAAI0B,GAAG,CAAC,kBAAkB,OAAOb;QAChC,IAAI,CAACY,WAAW;YACfA,YAAY,MAAMD,iBAAiBG,QAAQ,CAACtB,gBAAgB;gBAC3DuB,MAAM;oBAAEC,OAAO;oBAAYC,SAAS;gBAAQ;gBAC5CC,SAAS;oBAAC;wBAAEC,KAAK;oBAAO;iBAAE;YAC3B;QACD;QACA,OAAOnB,EAAEoB,IAAI,CAACR;IACf;IAEA,aAAa;IACbzB,IAAI0B,GAAG,CAAC,aAAa,CAACb;QACrB,OAAOA,EAAEhB,IAAI,CACZA,IAAI,CAAC;;;;;;;;;;;WAWG,CAAC;IAEX;AACD"}
@@ -0,0 +1,13 @@
1
+ import Emittery from 'emittery';
2
+ export const McpsEventType = {
3
+ Request: 'Mcps:Request'
4
+ };
5
+ export function createMcpsEmitter() {
6
+ return new Emittery({
7
+ debug: {
8
+ name: 'McpsEmitter'
9
+ }
10
+ });
11
+ }
12
+
13
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/events.ts"],"sourcesContent":["import Emittery from 'emittery';\n\nexport const McpsEventType = {\n\tRequest: 'Mcps:Request',\n} as const;\n\nexport type McpsRequestEvent = {\n\ttimestamp: string;\n\tmethod: string;\n\tpath: string;\n\tserverName?: string;\n\tserverType?: string;\n\tstatus?: number;\n\tdurationMs?: number;\n\terror?: string;\n\trequestHeaders?: Record<string, string>;\n};\n\nexport type McpsEventData = {\n\t[McpsEventType.Request]: McpsRequestEvent;\n};\n\nexport type McpsEmitter = Emittery<McpsEventData>;\n\nexport function createMcpsEmitter(): McpsEmitter {\n\treturn new Emittery<McpsEventData>({\n\t\tdebug: { name: 'McpsEmitter' },\n\t});\n}\n"],"names":["Emittery","McpsEventType","Request","createMcpsEmitter","debug","name"],"mappings":"AAAA,OAAOA,cAAc,WAAW;AAEhC,OAAO,MAAMC,gBAAgB;IAC5BC,SAAS;AACV,EAAW;AAoBX,OAAO,SAASC;IACf,OAAO,IAAIH,SAAwB;QAClCI,OAAO;YAAEC,MAAM;QAAc;IAC9B;AACD"}
@@ -5,18 +5,19 @@ import { findMcpServerDef } from "../providers/findMcpServerDef.js";
5
5
  import { createMcpLoggingHandler } from "./mcp-handler.js";
6
6
  const log = consola.withTag("mcps");
7
7
  /**
8
- * Register MCP routes for both pre-configured and dynamic endpoints
9
- */ export function registerMcpRoutes({ app, config, serverCache }) {
8
+ * Register MCP routes for both pre-configured and dynamic endpoints.
9
+ *
10
+ * McpServer only supports one transport connection at a time, so we create
11
+ * a fresh server instance per request instead of caching stateful servers.
12
+ */ export function registerMcpRoutes({ app, config, serverCache: _serverCache }) {
10
13
  const serverDefs = findMcpServerDef();
11
14
  // Register pre-configured servers from config
12
- // These are named endpoints like /mcp/my-sql that use config from file
13
15
  for (const [name, serverConfig] of Object.entries(config.servers)) {
14
16
  const def = getMcpServerHandlerDef(serverConfig.type);
15
17
  if (!def) {
16
18
  log.warn(`Unknown server type for ${name}: ${serverConfig.type}`);
17
19
  continue;
18
20
  }
19
- // Resolve config using def (config comes from file, not headers)
20
21
  const options = def.resolveConfig(serverConfig);
21
22
  if (!options) {
22
23
  log.warn(`Failed to resolve config for ${name}`);
@@ -27,86 +28,56 @@ const log = consola.withTag("mcps");
27
28
  log.warn(`Invalid config for ${name}: ${validation.error}`);
28
29
  continue;
29
30
  }
30
- // Create and cache the server instance at startup
31
- const cacheKey = `config::${name}`;
32
- const item = def.create(options);
33
- if (!item) {
34
- log.warn(`Failed to create server: ${name}`);
35
- continue;
36
- }
37
- serverCache.set(cacheKey, item);
38
31
  const path = `/mcp/${name}`;
39
32
  log.info(`Registered MCP server: ${path} (${serverConfig.type})`);
40
33
  app.all(path, async (c) => {
41
- const serverItem = serverCache.get(cacheKey);
42
- if (!serverItem) {
43
- return c.text("Server not found", 404);
44
- }
45
- // Create a new transport for each request to avoid "Transport already started" error
46
- const transport = new StreamableHTTPTransport();
47
- try {
48
- await serverItem.server.connect(transport);
49
- const handleRequest = createMcpLoggingHandler(transport, name);
50
- return await handleRequest(c);
51
- }
52
- catch (e) {
53
- log.error(`[${name}] Request error:`, e);
54
- return c.text(`Internal server error: ${e instanceof Error ? e.message : "Unknown error"}`, 500);
55
- }
34
+ return handleMcpRequest(c, def, options, name);
56
35
  });
57
36
  }
58
- // Register dynamic endpoints for all server types
59
- // These endpoints accept config via HTTP headers
37
+ // Register dynamic endpoints for all server types (header-based config)
60
38
  for (const def of serverDefs) {
61
39
  const path = `/mcp/${def.name}`;
62
40
  log.debug(`Registering dynamic endpoint: ${path}`);
63
41
  app.all(path, async (c) => {
64
- // Use def.resolveConfig to parse config from headers
65
42
  const options = def.resolveConfig({
66
43
  type: def.name
67
44
  }, c.req.raw.headers);
68
45
  if (!options) {
69
- // Build error message from headerMappings
70
46
  const requiredHeaders = def.headerMappings?.filter((m) => m.required).map((m) => m.header).join(", ") || "required headers";
71
47
  return c.text(`Missing ${requiredHeaders}`, 400);
72
48
  }
73
- // Validate options
74
49
  const validation = def.validateOptions(options);
75
50
  if (!validation.valid) {
76
51
  return c.text(validation.error || `Invalid configuration for ${def.name}`, 400);
77
52
  }
78
- // Get or create cached server instance
79
- const key = def.getCacheKey(options);
80
- let item = serverCache.get(key);
81
- if (!item) {
82
- log.info(`Creating new ${def.title} server: ${key}`);
83
- try {
84
- const newItem = def.create(options);
85
- if (!newItem)
86
- return c.text(`Failed to create ${def.name} server`, 500);
87
- item = newItem;
88
- serverCache.set(key, item);
89
- }
90
- catch (e) {
91
- log.error(`Failed to create ${def.name} server:`, e);
92
- return c.text(`Failed to create ${def.name} server`, 500);
93
- }
94
- }
95
- // Create a new transport for each request
96
- const transport = new StreamableHTTPTransport();
97
- try {
98
- await item.server.connect(transport);
99
- const handleRequest = createMcpLoggingHandler(transport, def.name);
100
- return await handleRequest(c);
101
- }
102
- catch (e) {
103
- log.error(`[${def.name}] Request error:`, e);
104
- return c.text(`Internal server error: ${e instanceof Error ? e.message : "Unknown error"}`, 500);
105
- }
53
+ return handleMcpRequest(c, def, options, def.name);
106
54
  });
107
55
  }
108
56
  return {
109
57
  serverDefs
110
58
  };
111
59
  }
60
+ async function handleMcpRequest(c, def, options, name) {
61
+ let item;
62
+ try {
63
+ item = def.create(options);
64
+ if (!item)
65
+ return c.text(`Failed to create ${name} server`, 500);
66
+ }
67
+ catch (e) {
68
+ log.error(`Failed to create ${name} server:`, e);
69
+ return c.text(`Failed to create ${name} server`, 500);
70
+ }
71
+ const transport = new StreamableHTTPTransport();
72
+ try {
73
+ await item.server.connect(transport);
74
+ const handleRequest = createMcpLoggingHandler(transport, name);
75
+ return await handleRequest(c);
76
+ }
77
+ catch (e) {
78
+ log.error(`[${name}] Request error:`, e);
79
+ item.close?.().catch(() => { });
80
+ return c.text(`Internal server error: ${e instanceof Error ? e.message : "Unknown error"}`, 500);
81
+ }
82
+ }
112
83
  //# sourceMappingURL=mcp-routes.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/mcp-routes.ts"],"sourcesContent":["import { StreamableHTTPTransport } from '@hono/mcp';\nimport consola from 'consola';\nimport type { Hono } from 'hono';\nimport type { LRUCache } from 'lru-cache';\nimport type { McpServerInstance } from '@wener/ai/mcp';\nimport { getMcpServerHandlerDef, type McpServerHandlerDef } from '../providers/McpServerHandlerDef';\nimport { findMcpServerDef } from '../providers/findMcpServerDef';\nimport { createMcpLoggingHandler } from './mcp-handler';\nimport type { McpsConfig, ServerConfig } from './schema';\n\nconst log = consola.withTag('mcps');\n\nexport interface RegisterMcpRoutesOptions {\n\tapp: Hono;\n\tconfig: McpsConfig;\n\tserverCache: LRUCache<string, McpServerInstance>;\n}\n\n/**\n * Register MCP routes for both pre-configured and dynamic endpoints\n */\nexport function registerMcpRoutes({ app, config, serverCache }: RegisterMcpRoutesOptions) {\n\tconst serverDefs = findMcpServerDef();\n\n\t// Register pre-configured servers from config\n\t// These are named endpoints like /mcp/my-sql that use config from file\n\tfor (const [name, serverConfig] of Object.entries(config.servers)) {\n\t\tconst def = getMcpServerHandlerDef(serverConfig.type);\n\t\tif (!def) {\n\t\t\tlog.warn(`Unknown server type for ${name}: ${serverConfig.type}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve config using def (config comes from file, not headers)\n\t\tconst options = def.resolveConfig(serverConfig);\n\t\tif (!options) {\n\t\t\tlog.warn(`Failed to resolve config for ${name}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst validation = def.validateOptions(options);\n\t\tif (!validation.valid) {\n\t\t\tlog.warn(`Invalid config for ${name}: ${validation.error}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Create and cache the server instance at startup\n\t\tconst cacheKey = `config::${name}`;\n\t\tconst item = def.create(options);\n\t\tif (!item) {\n\t\t\tlog.warn(`Failed to create server: ${name}`);\n\t\t\tcontinue;\n\t\t}\n\t\tserverCache.set(cacheKey, item);\n\n\t\tconst path = `/mcp/${name}`;\n\t\tlog.info(`Registered MCP server: ${path} (${serverConfig.type})`);\n\n\t\tapp.all(path, async (c) => {\n\t\t\tconst serverItem = serverCache.get(cacheKey);\n\t\t\tif (!serverItem) {\n\t\t\t\treturn c.text('Server not found', 404);\n\t\t\t}\n\t\t\t// Create a new transport for each request to avoid \"Transport already started\" error\n\t\t\tconst transport = new StreamableHTTPTransport();\n\t\t\ttry {\n\t\t\t\tawait serverItem.server.connect(transport);\n\t\t\t\tconst handleRequest = createMcpLoggingHandler(transport, name);\n\t\t\t\treturn await handleRequest(c);\n\t\t\t} catch (e) {\n\t\t\t\tlog.error(`[${name}] Request error:`, e);\n\t\t\t\treturn c.text(`Internal server error: ${e instanceof Error ? e.message : 'Unknown error'}`, 500);\n\t\t\t}\n\t\t});\n\t}\n\n\t// Register dynamic endpoints for all server types\n\t// These endpoints accept config via HTTP headers\n\tfor (const def of serverDefs) {\n\t\tconst path = `/mcp/${def.name}`;\n\t\tlog.debug(`Registering dynamic endpoint: ${path}`);\n\n\t\tapp.all(path, async (c) => {\n\t\t\t// Use def.resolveConfig to parse config from headers\n\t\t\tconst options = def.resolveConfig({ type: def.name } as any, c.req.raw.headers);\n\n\t\t\tif (!options) {\n\t\t\t\t// Build error message from headerMappings\n\t\t\t\tconst requiredHeaders =\n\t\t\t\t\tdef.headerMappings\n\t\t\t\t\t\t?.filter((m) => m.required)\n\t\t\t\t\t\t.map((m) => m.header)\n\t\t\t\t\t\t.join(', ') || 'required headers';\n\t\t\t\treturn c.text(`Missing ${requiredHeaders}`, 400);\n\t\t\t}\n\n\t\t\t// Validate options\n\t\t\tconst validation = def.validateOptions(options);\n\t\t\tif (!validation.valid) {\n\t\t\t\treturn c.text(validation.error || `Invalid configuration for ${def.name}`, 400);\n\t\t\t}\n\n\t\t\t// Get or create cached server instance\n\t\t\tconst key = def.getCacheKey(options);\n\t\t\tlet item = serverCache.get(key);\n\t\t\tif (!item) {\n\t\t\t\tlog.info(`Creating new ${def.title} server: ${key}`);\n\t\t\t\ttry {\n\t\t\t\t\tconst newItem = def.create(options);\n\t\t\t\t\tif (!newItem) return c.text(`Failed to create ${def.name} server`, 500);\n\t\t\t\t\titem = newItem;\n\t\t\t\t\tserverCache.set(key, item);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tlog.error(`Failed to create ${def.name} server:`, e);\n\t\t\t\t\treturn c.text(`Failed to create ${def.name} server`, 500);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Create a new transport for each request\n\t\t\tconst transport = new StreamableHTTPTransport();\n\t\t\ttry {\n\t\t\t\tawait item.server.connect(transport);\n\t\t\t\tconst handleRequest = createMcpLoggingHandler(transport, def.name);\n\t\t\t\treturn await handleRequest(c);\n\t\t\t} catch (e) {\n\t\t\t\tlog.error(`[${def.name}] Request error:`, e);\n\t\t\t\treturn c.text(`Internal server error: ${e instanceof Error ? e.message : 'Unknown error'}`, 500);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn { serverDefs };\n}\n"],"names":["StreamableHTTPTransport","consola","getMcpServerHandlerDef","findMcpServerDef","createMcpLoggingHandler","log","withTag","registerMcpRoutes","app","config","serverCache","serverDefs","name","serverConfig","Object","entries","servers","def","type","warn","options","resolveConfig","validation","validateOptions","valid","error","cacheKey","item","create","set","path","info","all","c","serverItem","get","text","transport","server","connect","handleRequest","e","Error","message","debug","req","raw","headers","requiredHeaders","headerMappings","filter","m","required","map","header","join","key","getCacheKey","title","newItem"],"mappings":"AAAA,SAASA,uBAAuB,QAAQ,YAAY;AACpD,OAAOC,aAAa,UAAU;AAI9B,SAASC,sBAAsB,QAAkC,mCAAmC;AACpG,SAASC,gBAAgB,QAAQ,gCAAgC;AACjE,SAASC,uBAAuB,QAAQ,gBAAgB;AAGxD,MAAMC,MAAMJ,QAAQK,OAAO,CAAC;AAQ5B;;CAEC,GACD,OAAO,SAASC,kBAAkB,EAAEC,GAAG,EAAEC,MAAM,EAAEC,WAAW,EAA4B;IACvF,MAAMC,aAAaR;IAEnB,8CAA8C;IAC9C,uEAAuE;IACvE,KAAK,MAAM,CAACS,MAAMC,aAAa,IAAIC,OAAOC,OAAO,CAACN,OAAOO,OAAO,EAAG;QAClE,MAAMC,MAAMf,uBAAuBW,aAAaK,IAAI;QACpD,IAAI,CAACD,KAAK;YACTZ,IAAIc,IAAI,CAAC,CAAC,wBAAwB,EAAEP,KAAK,EAAE,EAAEC,aAAaK,IAAI,EAAE;YAChE;QACD;QAEA,iEAAiE;QACjE,MAAME,UAAUH,IAAII,aAAa,CAACR;QAClC,IAAI,CAACO,SAAS;YACbf,IAAIc,IAAI,CAAC,CAAC,6BAA6B,EAAEP,MAAM;YAC/C;QACD;QAEA,MAAMU,aAAaL,IAAIM,eAAe,CAACH;QACvC,IAAI,CAACE,WAAWE,KAAK,EAAE;YACtBnB,IAAIc,IAAI,CAAC,CAAC,mBAAmB,EAAEP,KAAK,EAAE,EAAEU,WAAWG,KAAK,EAAE;YAC1D;QACD;QAEA,kDAAkD;QAClD,MAAMC,WAAW,CAAC,QAAQ,EAAEd,MAAM;QAClC,MAAMe,OAAOV,IAAIW,MAAM,CAACR;QACxB,IAAI,CAACO,MAAM;YACVtB,IAAIc,IAAI,CAAC,CAAC,yBAAyB,EAAEP,MAAM;YAC3C;QACD;QACAF,YAAYmB,GAAG,CAACH,UAAUC;QAE1B,MAAMG,OAAO,CAAC,KAAK,EAAElB,MAAM;QAC3BP,IAAI0B,IAAI,CAAC,CAAC,uBAAuB,EAAED,KAAK,EAAE,EAAEjB,aAAaK,IAAI,CAAC,CAAC,CAAC;QAEhEV,IAAIwB,GAAG,CAACF,MAAM,OAAOG;YACpB,MAAMC,aAAaxB,YAAYyB,GAAG,CAACT;YACnC,IAAI,CAACQ,YAAY;gBAChB,OAAOD,EAAEG,IAAI,CAAC,oBAAoB;YACnC;YACA,qFAAqF;YACrF,MAAMC,YAAY,IAAIrC;YACtB,IAAI;gBACH,MAAMkC,WAAWI,MAAM,CAACC,OAAO,CAACF;gBAChC,MAAMG,gBAAgBpC,wBAAwBiC,WAAWzB;gBACzD,OAAO,MAAM4B,cAAcP;YAC5B,EAAE,OAAOQ,GAAG;gBACXpC,IAAIoB,KAAK,CAAC,CAAC,CAAC,EAAEb,KAAK,gBAAgB,CAAC,EAAE6B;gBACtC,OAAOR,EAAEG,IAAI,CAAC,CAAC,uBAAuB,EAAEK,aAAaC,QAAQD,EAAEE,OAAO,GAAG,iBAAiB,EAAE;YAC7F;QACD;IACD;IAEA,kDAAkD;IAClD,iDAAiD;IACjD,KAAK,MAAM1B,OAAON,WAAY;QAC7B,MAAMmB,OAAO,CAAC,KAAK,EAAEb,IAAIL,IAAI,EAAE;QAC/BP,IAAIuC,KAAK,CAAC,CAAC,8BAA8B,EAAEd,MAAM;QAEjDtB,IAAIwB,GAAG,CAACF,MAAM,OAAOG;YACpB,qDAAqD;YACrD,MAAMb,UAAUH,IAAII,aAAa,CAAC;gBAAEH,MAAMD,IAAIL,IAAI;YAAC,GAAUqB,EAAEY,GAAG,CAACC,GAAG,CAACC,OAAO;YAE9E,IAAI,CAAC3B,SAAS;gBACb,0CAA0C;gBAC1C,MAAM4B,kBACL/B,IAAIgC,cAAc,EACfC,OAAO,CAACC,IAAMA,EAAEC,QAAQ,EACzBC,IAAI,CAACF,IAAMA,EAAEG,MAAM,EACnBC,KAAK,SAAS;gBACjB,OAAOtB,EAAEG,IAAI,CAAC,CAAC,QAAQ,EAAEY,iBAAiB,EAAE;YAC7C;YAEA,mBAAmB;YACnB,MAAM1B,aAAaL,IAAIM,eAAe,CAACH;YACvC,IAAI,CAACE,WAAWE,KAAK,EAAE;gBACtB,OAAOS,EAAEG,IAAI,CAACd,WAAWG,KAAK,IAAI,CAAC,0BAA0B,EAAER,IAAIL,IAAI,EAAE,EAAE;YAC5E;YAEA,uCAAuC;YACvC,MAAM4C,MAAMvC,IAAIwC,WAAW,CAACrC;YAC5B,IAAIO,OAAOjB,YAAYyB,GAAG,CAACqB;YAC3B,IAAI,CAAC7B,MAAM;gBACVtB,IAAI0B,IAAI,CAAC,CAAC,aAAa,EAAEd,IAAIyC,KAAK,CAAC,SAAS,EAAEF,KAAK;gBACnD,IAAI;oBACH,MAAMG,UAAU1C,IAAIW,MAAM,CAACR;oBAC3B,IAAI,CAACuC,SAAS,OAAO1B,EAAEG,IAAI,CAAC,CAAC,iBAAiB,EAAEnB,IAAIL,IAAI,CAAC,OAAO,CAAC,EAAE;oBACnEe,OAAOgC;oBACPjD,YAAYmB,GAAG,CAAC2B,KAAK7B;gBACtB,EAAE,OAAOc,GAAG;oBACXpC,IAAIoB,KAAK,CAAC,CAAC,iBAAiB,EAAER,IAAIL,IAAI,CAAC,QAAQ,CAAC,EAAE6B;oBAClD,OAAOR,EAAEG,IAAI,CAAC,CAAC,iBAAiB,EAAEnB,IAAIL,IAAI,CAAC,OAAO,CAAC,EAAE;gBACtD;YACD;YAEA,0CAA0C;YAC1C,MAAMyB,YAAY,IAAIrC;YACtB,IAAI;gBACH,MAAM2B,KAAKW,MAAM,CAACC,OAAO,CAACF;gBAC1B,MAAMG,gBAAgBpC,wBAAwBiC,WAAWpB,IAAIL,IAAI;gBACjE,OAAO,MAAM4B,cAAcP;YAC5B,EAAE,OAAOQ,GAAG;gBACXpC,IAAIoB,KAAK,CAAC,CAAC,CAAC,EAAER,IAAIL,IAAI,CAAC,gBAAgB,CAAC,EAAE6B;gBAC1C,OAAOR,EAAEG,IAAI,CAAC,CAAC,uBAAuB,EAAEK,aAAaC,QAAQD,EAAEE,OAAO,GAAG,iBAAiB,EAAE;YAC7F;QACD;IACD;IAEA,OAAO;QAAEhC;IAAW;AACrB"}
1
+ {"version":3,"sources":["../../src/server/mcp-routes.ts"],"sourcesContent":["import { StreamableHTTPTransport } from '@hono/mcp';\nimport consola from 'consola';\nimport type { Hono } from 'hono';\nimport type { LRUCache } from 'lru-cache';\nimport type { McpServerInstance } from '@wener/ai/mcp';\nimport { getMcpServerHandlerDef, type McpServerHandlerDef } from '../providers/McpServerHandlerDef';\nimport { findMcpServerDef } from '../providers/findMcpServerDef';\nimport { createMcpLoggingHandler } from './mcp-handler';\nimport type { McpsConfig, ServerConfig } from './schema';\n\nconst log = consola.withTag('mcps');\n\nexport interface RegisterMcpRoutesOptions {\n\tapp: Hono;\n\tconfig: McpsConfig;\n\tserverCache: LRUCache<string, McpServerInstance>;\n}\n\n/**\n * Register MCP routes for both pre-configured and dynamic endpoints.\n *\n * McpServer only supports one transport connection at a time, so we create\n * a fresh server instance per request instead of caching stateful servers.\n */\nexport function registerMcpRoutes({ app, config, serverCache: _serverCache }: RegisterMcpRoutesOptions) {\n\tconst serverDefs = findMcpServerDef();\n\n\t// Register pre-configured servers from config\n\tfor (const [name, serverConfig] of Object.entries(config.servers)) {\n\t\tconst def = getMcpServerHandlerDef(serverConfig.type);\n\t\tif (!def) {\n\t\t\tlog.warn(`Unknown server type for ${name}: ${serverConfig.type}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst options = def.resolveConfig(serverConfig);\n\t\tif (!options) {\n\t\t\tlog.warn(`Failed to resolve config for ${name}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst validation = def.validateOptions(options);\n\t\tif (!validation.valid) {\n\t\t\tlog.warn(`Invalid config for ${name}: ${validation.error}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst path = `/mcp/${name}`;\n\t\tlog.info(`Registered MCP server: ${path} (${serverConfig.type})`);\n\n\t\tapp.all(path, async (c) => {\n\t\t\treturn handleMcpRequest(c, def, options, name);\n\t\t});\n\t}\n\n\t// Register dynamic endpoints for all server types (header-based config)\n\tfor (const def of serverDefs) {\n\t\tconst path = `/mcp/${def.name}`;\n\t\tlog.debug(`Registering dynamic endpoint: ${path}`);\n\n\t\tapp.all(path, async (c) => {\n\t\t\tconst options = def.resolveConfig({ type: def.name } as any, c.req.raw.headers);\n\n\t\t\tif (!options) {\n\t\t\t\tconst requiredHeaders =\n\t\t\t\t\tdef.headerMappings\n\t\t\t\t\t\t?.filter((m) => m.required)\n\t\t\t\t\t\t.map((m) => m.header)\n\t\t\t\t\t\t.join(', ') || 'required headers';\n\t\t\t\treturn c.text(`Missing ${requiredHeaders}`, 400);\n\t\t\t}\n\n\t\t\tconst validation = def.validateOptions(options);\n\t\t\tif (!validation.valid) {\n\t\t\t\treturn c.text(validation.error || `Invalid configuration for ${def.name}`, 400);\n\t\t\t}\n\n\t\t\treturn handleMcpRequest(c, def, options, def.name);\n\t\t});\n\t}\n\n\treturn { serverDefs };\n}\n\nasync function handleMcpRequest(c: any, def: McpServerHandlerDef, options: any, name: string) {\n\tlet item: McpServerInstance | undefined;\n\ttry {\n\t\titem = def.create(options);\n\t\tif (!item) return c.text(`Failed to create ${name} server`, 500);\n\t} catch (e) {\n\t\tlog.error(`Failed to create ${name} server:`, e);\n\t\treturn c.text(`Failed to create ${name} server`, 500);\n\t}\n\n\tconst transport = new StreamableHTTPTransport();\n\ttry {\n\t\tawait item.server.connect(transport);\n\t\tconst handleRequest = createMcpLoggingHandler(transport, name);\n\t\treturn await handleRequest(c);\n\t} catch (e) {\n\t\tlog.error(`[${name}] Request error:`, e);\n\t\titem.close?.().catch(() => {});\n\t\treturn c.text(`Internal server error: ${e instanceof Error ? e.message : 'Unknown error'}`, 500);\n\t}\n}\n"],"names":["StreamableHTTPTransport","consola","getMcpServerHandlerDef","findMcpServerDef","createMcpLoggingHandler","log","withTag","registerMcpRoutes","app","config","serverCache","_serverCache","serverDefs","name","serverConfig","Object","entries","servers","def","type","warn","options","resolveConfig","validation","validateOptions","valid","error","path","info","all","c","handleMcpRequest","debug","req","raw","headers","requiredHeaders","headerMappings","filter","m","required","map","header","join","text","item","create","e","transport","server","connect","handleRequest","close","catch","Error","message"],"mappings":"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"}
@@ -1,14 +1,10 @@
1
1
  import { implement } from "@orpc/server";
2
2
  import { McpsContract } from "../contracts/index.js";
3
3
  import { findMcpServerDef } from "../providers/findMcpServerDef.js";
4
- import { getAuditStats, queryAuditEvents } from "./audit.js";
5
- // Simple glob pattern matching
6
4
  function matchGlob(pattern, text) {
7
- const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&") // Escape regex special chars except * and ?
8
- .replace(/\*/g, ".*").replace(/\?/g, ".");
5
+ const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
9
6
  return new RegExp(`^${regexPattern}$`, "i").test(text);
10
7
  }
11
- // Build server types from registry
12
8
  function getServerTypes() {
13
9
  return findMcpServerDef().map((def) => ({
14
10
  type: def.name,
@@ -16,7 +12,6 @@ function getServerTypes() {
16
12
  dynamicEndpoint: `/mcp/${def.name}`
17
13
  }));
18
14
  }
19
- // Build endpoints from server types
20
15
  function getEndpoints() {
21
16
  const mcpEndpoints = findMcpServerDef().map((def) => `/mcp/${def.name}`);
22
17
  const chatEndpoints = [
@@ -32,17 +27,20 @@ function getEndpoints() {
32
27
  ...chatEndpoints
33
28
  ];
34
29
  }
35
- /**
36
- * Create MCPS Router with config context
37
- */ export function createMcpsRouter(ctx) {
38
- const { config } = ctx;
39
- // Build server info list
30
+ const emptyStats = {
31
+ totalRequests: 0,
32
+ totalErrors: 0,
33
+ avgDurationMs: 0,
34
+ byServer: [],
35
+ byMethod: []
36
+ };
37
+ export function createMcpsRouter(ctx) {
38
+ const { config, statsProvider } = ctx;
40
39
  const servers = Object.entries(config.servers).map(([name, serverConfig]) => ({
41
40
  name,
42
41
  type: serverConfig.type,
43
42
  disabled: serverConfig.disabled
44
43
  }));
45
- // Build model info list
46
44
  const models = (config.models ?? []).map((model) => ({
47
45
  name: model.name,
48
46
  adapter: model.adapter,
@@ -63,9 +61,14 @@ function getEndpoints() {
63
61
  };
64
62
  }),
65
63
  stats: implement(McpsContract.stats).handler(async ({ input }) => {
66
- const auditStats = getAuditStats(input);
67
- // Build endpoint stats from audit events
68
- const { events } = queryAuditEvents({
64
+ if (!statsProvider) {
65
+ return {
66
+ ...emptyStats,
67
+ byEndpoint: []
68
+ };
69
+ }
70
+ const auditStats = statsProvider.getStats(input);
71
+ const { events } = statsProvider.queryEvents({
69
72
  limit: 10000
70
73
  });
71
74
  const endpointCounts = new Map();
@@ -76,7 +79,7 @@ function getEndpoints() {
76
79
  const byEndpoint = Array.from(endpointCounts.entries()).map(([endpoint, count]) => ({
77
80
  endpoint,
78
81
  count
79
- })).sort((a, b) => b.count - a.count).slice(0, 20); // Top 20 endpoints
82
+ })).sort((a, b) => b.count - a.count).slice(0, 20);
80
83
  return {
81
84
  ...auditStats,
82
85
  byEndpoint
@@ -93,15 +96,7 @@ function getEndpoints() {
93
96
  };
94
97
  }),
95
98
  tools: implement(McpsContract.tools).handler(async ({ input }) => {
96
- // TODO: This is a placeholder that returns empty tools list.
97
- // Full implementation would require connecting to each MCP server
98
- // and calling tools/list. Consider caching tool lists and
99
- // refreshing periodically or on demand.
100
99
  const tools = [];
101
- // For now, return an empty list.
102
- // When MCP servers support persistent connections or when we
103
- // implement a tool registry, this will be populated.
104
- // Apply filters if provided
105
100
  let filteredTools = tools;
106
101
  if (input.server) {
107
102
  filteredTools = filteredTools.filter((t) => t.serverName === input.server);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/mcps-router.ts"],"sourcesContent":["import { implement } from '@orpc/server';\nimport { McpsContract, type ModelInfo, type ServerInfo, type ServerTypeInfo, type ToolInfo } from '../contracts';\nimport { findMcpServerDef } from '../providers/findMcpServerDef';\nimport { getAuditStats, queryAuditEvents } from './audit';\nimport type { McpsConfig } from './schema';\n\n// Simple glob pattern matching\nfunction matchGlob(pattern: string, text: string): boolean {\n\tconst regexPattern = pattern\n\t\t.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex special chars except * and ?\n\t\t.replace(/\\*/g, '.*')\n\t\t.replace(/\\?/g, '.');\n\treturn new RegExp(`^${regexPattern}$`, 'i').test(text);\n}\n\n// Build server types from registry\nfunction getServerTypes(): ServerTypeInfo[] {\n\treturn findMcpServerDef().map((def) => ({\n\t\ttype: def.name,\n\t\tdescription: def.description,\n\t\tdynamicEndpoint: `/mcp/${def.name}`,\n\t}));\n}\n\n// Build endpoints from server types\nfunction getEndpoints(): string[] {\n\tconst mcpEndpoints = findMcpServerDef().map((def) => `/mcp/${def.name}`);\n\tconst chatEndpoints = [\n\t\t'/v1/chat/completions',\n\t\t'/v1/messages',\n\t\t'/v1/models',\n\t\t'/v1/responses',\n\t\t'/v1/models/:model:generateContent',\n\t\t'/v1/models/:model:streamGenerateContent',\n\t];\n\treturn [...mcpEndpoints, ...chatEndpoints];\n}\n\nexport interface McpsRouterContext {\n\tconfig: McpsConfig;\n}\n\n/**\n * Create MCPS Router with config context\n */\nexport function createMcpsRouter(ctx: McpsRouterContext) {\n\tconst { config } = ctx;\n\n\t// Build server info list\n\tconst servers: ServerInfo[] = Object.entries(config.servers).map(([name, serverConfig]) => ({\n\t\tname,\n\t\ttype: serverConfig.type,\n\t\tdisabled: serverConfig.disabled,\n\t}));\n\n\t// Build model info list\n\tconst models: ModelInfo[] = (config.models ?? []).map((model) => ({\n\t\tname: model.name,\n\t\tadapter: model.adapter,\n\t\tbaseUrl: model.baseUrl,\n\t\tcontextWindow: model.contextWindow,\n\t\tmaxInputTokens: model.maxInputTokens,\n\t\tmaxOutputTokens: model.maxOutputTokens,\n\t}));\n\n\treturn implement(McpsContract).router({\n\t\toverview: implement(McpsContract.overview).handler(async () => {\n\t\t\treturn {\n\t\t\t\tname: '@wener/mcps',\n\t\t\t\tversion: '0.1.0',\n\t\t\t\tservers,\n\t\t\t\tserverTypes: getServerTypes(),\n\t\t\t\tmodels,\n\t\t\t\tendpoints: getEndpoints(),\n\t\t\t};\n\t\t}),\n\n\t\tstats: implement(McpsContract.stats).handler(async ({ input }) => {\n\t\t\tconst auditStats = getAuditStats(input);\n\n\t\t\t// Build endpoint stats from audit events\n\t\t\tconst { events } = queryAuditEvents({ limit: 10000 });\n\t\t\tconst endpointCounts = new Map<string, number>();\n\t\t\tfor (const event of events) {\n\t\t\t\tconst endpoint = event.path || 'unknown';\n\t\t\t\tendpointCounts.set(endpoint, (endpointCounts.get(endpoint) || 0) + 1);\n\t\t\t}\n\t\t\tconst byEndpoint = Array.from(endpointCounts.entries())\n\t\t\t\t.map(([endpoint, count]) => ({ endpoint, count }))\n\t\t\t\t.sort((a, b) => b.count - a.count)\n\t\t\t\t.slice(0, 20); // Top 20 endpoints\n\n\t\t\treturn {\n\t\t\t\t...auditStats,\n\t\t\t\tbyEndpoint,\n\t\t\t};\n\t\t}),\n\n\t\tservers: implement(McpsContract.servers).handler(async () => {\n\t\t\treturn { servers };\n\t\t}),\n\n\t\tmodels: implement(McpsContract.models).handler(async () => {\n\t\t\treturn { models };\n\t\t}),\n\n\t\ttools: implement(McpsContract.tools).handler(async ({ input }) => {\n\t\t\t// TODO: This is a placeholder that returns empty tools list.\n\t\t\t// Full implementation would require connecting to each MCP server\n\t\t\t// and calling tools/list. Consider caching tool lists and\n\t\t\t// refreshing periodically or on demand.\n\t\t\tconst tools: ToolInfo[] = [];\n\n\t\t\t// For now, return an empty list.\n\t\t\t// When MCP servers support persistent connections or when we\n\t\t\t// implement a tool registry, this will be populated.\n\n\t\t\t// Apply filters if provided\n\t\t\tlet filteredTools = tools;\n\n\t\t\tif (input.server) {\n\t\t\t\tfilteredTools = filteredTools.filter((t) => t.serverName === input.server);\n\t\t\t}\n\n\t\t\tif (input.filter) {\n\t\t\t\tconst patterns = input.filter.split(',').map((p) => p.trim());\n\t\t\t\tfilteredTools = filteredTools.filter((t) => patterns.some((pattern) => matchGlob(pattern, t.name)));\n\t\t\t}\n\n\t\t\treturn { tools: filteredTools };\n\t\t}),\n\t});\n}\n"],"names":["implement","McpsContract","findMcpServerDef","getAuditStats","queryAuditEvents","matchGlob","pattern","text","regexPattern","replace","RegExp","test","getServerTypes","map","def","type","name","description","dynamicEndpoint","getEndpoints","mcpEndpoints","chatEndpoints","createMcpsRouter","ctx","config","servers","Object","entries","serverConfig","disabled","models","model","adapter","baseUrl","contextWindow","maxInputTokens","maxOutputTokens","router","overview","handler","version","serverTypes","endpoints","stats","input","auditStats","events","limit","endpointCounts","Map","event","endpoint","path","set","get","byEndpoint","Array","from","count","sort","a","b","slice","tools","filteredTools","server","filter","t","serverName","patterns","split","p","trim","some"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/server/mcps-router.ts"],"sourcesContent":["import { implement } from '@orpc/server';\nimport { McpsContract, type ModelInfo, type ServerInfo, type ServerTypeInfo, type ToolInfo } from '../contracts';\nimport { findMcpServerDef } from '../providers/findMcpServerDef';\nimport type { McpsConfig } from './schema';\nimport type { StatsProvider } from './server';\n\nfunction matchGlob(pattern: string, text: string): boolean {\n\tconst regexPattern = pattern\n\t\t.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n\t\t.replace(/\\*/g, '.*')\n\t\t.replace(/\\?/g, '.');\n\treturn new RegExp(`^${regexPattern}$`, 'i').test(text);\n}\n\nfunction getServerTypes(): ServerTypeInfo[] {\n\treturn findMcpServerDef().map((def) => ({\n\t\ttype: def.name,\n\t\tdescription: def.description,\n\t\tdynamicEndpoint: `/mcp/${def.name}`,\n\t}));\n}\n\nfunction getEndpoints(): string[] {\n\tconst mcpEndpoints = findMcpServerDef().map((def) => `/mcp/${def.name}`);\n\tconst chatEndpoints = [\n\t\t'/v1/chat/completions',\n\t\t'/v1/messages',\n\t\t'/v1/models',\n\t\t'/v1/responses',\n\t\t'/v1/models/:model:generateContent',\n\t\t'/v1/models/:model:streamGenerateContent',\n\t];\n\treturn [...mcpEndpoints, ...chatEndpoints];\n}\n\nexport interface McpsRouterContext {\n\tconfig: McpsConfig;\n\t/** Optional stats provider (e.g. from audit plugin) */\n\tstatsProvider?: StatsProvider;\n}\n\nconst emptyStats = {\n\ttotalRequests: 0,\n\ttotalErrors: 0,\n\tavgDurationMs: 0,\n\tbyServer: [] as Array<{ name: string; count: number }>,\n\tbyMethod: [] as Array<{ method: string; count: number }>,\n};\n\nexport function createMcpsRouter(ctx: McpsRouterContext) {\n\tconst { config, statsProvider } = ctx;\n\n\tconst servers: ServerInfo[] = Object.entries(config.servers).map(([name, serverConfig]) => ({\n\t\tname,\n\t\ttype: serverConfig.type,\n\t\tdisabled: serverConfig.disabled,\n\t}));\n\n\tconst models: ModelInfo[] = (config.models ?? []).map((model) => ({\n\t\tname: model.name,\n\t\tadapter: model.adapter,\n\t\tbaseUrl: model.baseUrl,\n\t\tcontextWindow: model.contextWindow,\n\t\tmaxInputTokens: model.maxInputTokens,\n\t\tmaxOutputTokens: model.maxOutputTokens,\n\t}));\n\n\treturn implement(McpsContract).router({\n\t\toverview: implement(McpsContract.overview).handler(async () => {\n\t\t\treturn {\n\t\t\t\tname: '@wener/mcps',\n\t\t\t\tversion: '0.1.0',\n\t\t\t\tservers,\n\t\t\t\tserverTypes: getServerTypes(),\n\t\t\t\tmodels,\n\t\t\t\tendpoints: getEndpoints(),\n\t\t\t};\n\t\t}),\n\n\t\tstats: implement(McpsContract.stats).handler(async ({ input }) => {\n\t\t\tif (!statsProvider) {\n\t\t\t\treturn { ...emptyStats, byEndpoint: [] };\n\t\t\t}\n\n\t\t\tconst auditStats = statsProvider.getStats(input);\n\t\t\tconst { events } = statsProvider.queryEvents({ limit: 10000 });\n\n\t\t\tconst endpointCounts = new Map<string, number>();\n\t\t\tfor (const event of events) {\n\t\t\t\tconst endpoint = event.path || 'unknown';\n\t\t\t\tendpointCounts.set(endpoint, (endpointCounts.get(endpoint) || 0) + 1);\n\t\t\t}\n\t\t\tconst byEndpoint = Array.from(endpointCounts.entries())\n\t\t\t\t.map(([endpoint, count]) => ({ endpoint, count }))\n\t\t\t\t.sort((a, b) => b.count - a.count)\n\t\t\t\t.slice(0, 20);\n\n\t\t\treturn { ...auditStats, byEndpoint };\n\t\t}),\n\n\t\tservers: implement(McpsContract.servers).handler(async () => {\n\t\t\treturn { servers };\n\t\t}),\n\n\t\tmodels: implement(McpsContract.models).handler(async () => {\n\t\t\treturn { models };\n\t\t}),\n\n\t\ttools: implement(McpsContract.tools).handler(async ({ input }) => {\n\t\t\tconst tools: ToolInfo[] = [];\n\t\t\tlet filteredTools = tools;\n\n\t\t\tif (input.server) {\n\t\t\t\tfilteredTools = filteredTools.filter((t) => t.serverName === input.server);\n\t\t\t}\n\n\t\t\tif (input.filter) {\n\t\t\t\tconst patterns = input.filter.split(',').map((p) => p.trim());\n\t\t\t\tfilteredTools = filteredTools.filter((t) => patterns.some((pattern) => matchGlob(pattern, t.name)));\n\t\t\t}\n\n\t\t\treturn { tools: filteredTools };\n\t\t}),\n\t});\n}\n"],"names":["implement","McpsContract","findMcpServerDef","matchGlob","pattern","text","regexPattern","replace","RegExp","test","getServerTypes","map","def","type","name","description","dynamicEndpoint","getEndpoints","mcpEndpoints","chatEndpoints","emptyStats","totalRequests","totalErrors","avgDurationMs","byServer","byMethod","createMcpsRouter","ctx","config","statsProvider","servers","Object","entries","serverConfig","disabled","models","model","adapter","baseUrl","contextWindow","maxInputTokens","maxOutputTokens","router","overview","handler","version","serverTypes","endpoints","stats","input","byEndpoint","auditStats","getStats","events","queryEvents","limit","endpointCounts","Map","event","endpoint","path","set","get","Array","from","count","sort","a","b","slice","tools","filteredTools","server","filter","t","serverName","patterns","split","p","trim","some"],"mappings":"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"}