@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
@@ -13,6 +13,9 @@ export const HeaderNames = {
13
13
  MCP_URL: 'X-MCP-URL',
14
14
  MCP_TYPE: 'X-MCP-TYPE',
15
15
  MCP_COMMAND: 'X-MCP-COMMAND',
16
+ FEISHU_APP_ID: 'X-FEISHU-APP-ID',
17
+ FEISHU_APP_SECRET: 'X-FEISHU-APP-SECRET',
18
+ FEISHU_DOMAIN: 'X-FEISHU-DOMAIN',
16
19
  // Tool filtering headers
17
20
  MCP_READONLY: 'X-MCP-Readonly',
18
21
  MCP_INCLUDE: 'X-MCP-Include',
@@ -44,6 +47,13 @@ export const PrometheusConfigSchema = BaseServerConfigSchema.extend({
44
47
  type: z.literal('prometheus'),
45
48
  url: z.string().optional().describe('Prometheus server URL')
46
49
  });
50
+ // Feishu/Lark config
51
+ export const FeishuConfigSchema = BaseServerConfigSchema.extend({
52
+ type: z.literal('feishu'),
53
+ appId: z.string().optional().describe('Feishu App ID'),
54
+ appSecret: z.string().optional().describe('Feishu App Secret'),
55
+ domain: z.string().optional().describe('feishu (China) or lark (International)')
56
+ });
47
57
  // Relay config for proxying to other MCP servers
48
58
  export const RelayConfigSchema = BaseServerConfigSchema.extend({
49
59
  type: z.literal('relay'),
@@ -55,13 +65,23 @@ export const RelayConfigSchema = BaseServerConfigSchema.extend({
55
65
  command: z.string().optional().describe('Command to run (stdio transport)'),
56
66
  args: z.array(z.string()).optional().describe('Command arguments')
57
67
  });
58
- // Union of all server configs
59
- export const ServerConfigSchema = z.discriminatedUnion('type', [
68
+ // Known server config schemas
69
+ const KnownServerConfigSchema = z.discriminatedUnion('type', [
60
70
  TencentClsConfigSchema,
61
71
  SqlConfigSchema,
62
72
  PrometheusConfigSchema,
73
+ FeishuConfigSchema,
63
74
  RelayConfigSchema
64
75
  ]);
76
+ // Catch-all for custom/extension server types (e.g. platform-admin, fusionops-admin)
77
+ const GenericServerConfigSchema = BaseServerConfigSchema.extend({
78
+ type: z.string()
79
+ }).passthrough();
80
+ // Union of known types with generic fallback for extensibility
81
+ export const ServerConfigSchema = z.union([
82
+ KnownServerConfigSchema,
83
+ GenericServerConfigSchema
84
+ ]);
65
85
  /**
66
86
  * Resolve config from headers - merge headers into config properties
67
87
  * @deprecated Use McpServerDef.resolveConfig instead. This function is kept for backward compatibility.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/schema.ts"],"sourcesContent":["import { z } from 'zod';\n\n// Header name constants for config parsing\nexport const HeaderNames = {\n\tCLS_SECRET_ID: 'X-CLS-SECRET-ID',\n\tCLS_SECRET_KEY: 'X-CLS-SECRET-KEY',\n\tCLS_REGION: 'X-CLS-REGION',\n\tCLS_ENDPOINT: 'X-CLS-ENDPOINT',\n\tDB_URL: 'X-DB-URL',\n\tDB_READ_URL: 'X-DB-READ-URL',\n\tDB_WRITE_URL: 'X-DB-WRITE-URL',\n\tSERVICE_URL: 'X-SERVICE-URL',\n\tTOKEN: 'X-TOKEN',\n\tMCP_URL: 'X-MCP-URL',\n\tMCP_TYPE: 'X-MCP-TYPE',\n\tMCP_COMMAND: 'X-MCP-COMMAND',\n\t// Tool filtering headers\n\tMCP_READONLY: 'X-MCP-Readonly',\n\tMCP_INCLUDE: 'X-MCP-Include',\n\tMCP_EXCLUDE: 'X-MCP-Exclude',\n} as const;\n\n// Base server config with common fields\nexport const BaseServerConfigSchema = z.object({\n\tdisabled: z.boolean().optional(),\n\t// Headers can be used as alternative to direct config properties\n\theaders: z.record(z.string(), z.string()).optional(),\n});\n\n// Tencent CLS config - supports both direct config and headers\nexport const TencentClsConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('tencent-cls'),\n\tclientId: z.string().optional().describe('Tencent Cloud Secret ID'),\n\tclientSecret: z.string().optional().describe('Tencent Cloud Secret Key'),\n\tregion: z.string().optional().describe('CLS region'),\n\tendpoint: z.string().optional().describe('CLS endpoint'),\n});\nexport type TencentClsConfig = z.infer<typeof TencentClsConfigSchema>;\n\n// SQL config with read/write separation\nexport const SqlConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('sql'),\n\tdbUrl: z.string().optional().describe('Database URL (for both read and write)'),\n\tdbReadUrl: z.string().optional().describe('Database URL for read operations'),\n\tdbWriteUrl: z.string().optional().describe('Database URL for write operations'),\n});\nexport type SqlConfig = z.infer<typeof SqlConfigSchema>;\n\n// Prometheus config\nexport const PrometheusConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('prometheus'),\n\turl: z.string().optional().describe('Prometheus server URL'),\n});\nexport type PrometheusConfig = z.infer<typeof PrometheusConfigSchema>;\n\n// Relay config for proxying to other MCP servers\nexport const RelayConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('relay'),\n\turl: z.string().optional().describe('Target MCP server URL'),\n\ttransport: z.enum(['http', 'sse']).optional().describe('MCP transport type'),\n\tcommand: z.string().optional().describe('Command to run (stdio transport)'),\n\targs: z.array(z.string()).optional().describe('Command arguments'),\n});\nexport type RelayConfig = z.infer<typeof RelayConfigSchema>;\n\n// Union of all server configs\nexport const ServerConfigSchema = z.discriminatedUnion('type', [\n\tTencentClsConfigSchema,\n\tSqlConfigSchema,\n\tPrometheusConfigSchema,\n\tRelayConfigSchema,\n]);\nexport type ServerConfig = z.infer<typeof ServerConfigSchema>;\n\n/**\n * Resolve config from headers - merge headers into config properties\n * @deprecated Use McpServerDef.resolveConfig instead. This function is kept for backward compatibility.\n */\nexport function resolveServerConfig<T extends ServerConfig>(config: T): T {\n\t// This function is now a pass-through - actual resolution is done in McpServerDef.resolveConfig\n\t// Keeping it for backward compatibility with any external code that might use it\n\treturn config;\n}\n\n// ============================================================================\n// Chat/LLM Gateway Configuration\n// ============================================================================\n\n/**\n * Adapter types for protocol conversion\n */\nexport const AdapterType = z.enum(['openai', 'anthropic', 'gemini']);\nexport type AdapterType = z.infer<typeof AdapterType>;\n\n/**\n * Adapter endpoint configuration for chat models\n */\nexport const AdapterEndpointConfigSchema = z.object({\n\tbaseUrl: z.string().optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tprocessors: z.array(z.string()).optional(),\n});\nexport type AdapterEndpointConfig = z.infer<typeof AdapterEndpointConfigSchema>;\n\n/**\n * Model configuration for chat gateway\n */\nexport const ModelConfigSchema = z.object({\n\t/** Model name or pattern (supports wildcards like \"gpt-*\") */\n\tname: z.string(),\n\t/** Base URL for the API */\n\tbaseUrl: z.string().optional(),\n\t/** API key (uses Authorization: Bearer header) */\n\tapiKey: z.string().optional(),\n\t/** Additional headers */\n\theaders: z.record(z.string(), z.string()).optional(),\n\t/** Default adapter to use for protocol conversion */\n\tadapter: AdapterType.optional(),\n\t/** Adapter-specific endpoint configurations */\n\tadapters: z\n\t\t.object({\n\t\t\topenai: AdapterEndpointConfigSchema.optional(),\n\t\t\tanthropic: AdapterEndpointConfigSchema.optional(),\n\t\t\tgemini: AdapterEndpointConfigSchema.optional(),\n\t\t})\n\t\t.optional(),\n\t/** Processor chain */\n\tprocessors: z.array(z.string()).optional(),\n\t/** Context window size (max total tokens) */\n\tcontextWindow: z.number().optional(),\n\t/** Max input tokens */\n\tmaxInputTokens: z.number().optional(),\n\t/** Max output tokens */\n\tmaxOutputTokens: z.number().optional(),\n\t/** Whether to fetch models from upstream on /v1/models */\n\tfetchUpstreamModels: z.boolean().optional(),\n});\nexport type ModelConfig = z.infer<typeof ModelConfigSchema>;\n\n/**\n * Database configuration for audit storage\n */\nexport const DbConfigSchema = z.object({\n\t/** Path to SQLite database file (default: .mcps.db) */\n\tpath: z.string().optional(),\n});\nexport type DbConfig = z.infer<typeof DbConfigSchema>;\n\n/**\n * Audit configuration\n */\nexport const AuditConfigSchema = z.object({\n\t/** Enable audit logging (default: true) */\n\tenabled: z.boolean().optional(),\n\t/** Database configuration for audit storage (overrides root db config) */\n\tdb: DbConfigSchema.optional(),\n});\nexport type AuditConfig = z.infer<typeof AuditConfigSchema>;\n\n// Main config file schema\nexport const McpsConfigSchema = z.object({\n\tservers: z.record(z.string(), ServerConfigSchema).default({}),\n\t/** Model configurations for chat gateway (array format) */\n\tmodels: z.array(ModelConfigSchema).optional(),\n\t/** Whether to expose server config discovery endpoints (default: false) */\n\tdiscoveryConfig: z.boolean().optional(),\n\t/** Database configuration (shared, used as fallback for audit.db) */\n\tdb: DbConfigSchema.optional(),\n\t/** Audit configuration */\n\taudit: AuditConfigSchema.optional(),\n});\nexport type McpsConfig = z.infer<typeof McpsConfigSchema>;\n\n// Legacy ChatConfig type alias for backwards compatibility\nexport type ChatConfig = { models?: ModelConfig[] };\n"],"names":["z","HeaderNames","CLS_SECRET_ID","CLS_SECRET_KEY","CLS_REGION","CLS_ENDPOINT","DB_URL","DB_READ_URL","DB_WRITE_URL","SERVICE_URL","TOKEN","MCP_URL","MCP_TYPE","MCP_COMMAND","MCP_READONLY","MCP_INCLUDE","MCP_EXCLUDE","BaseServerConfigSchema","object","disabled","boolean","optional","headers","record","string","TencentClsConfigSchema","extend","type","literal","clientId","describe","clientSecret","region","endpoint","SqlConfigSchema","dbUrl","dbReadUrl","dbWriteUrl","PrometheusConfigSchema","url","RelayConfigSchema","transport","enum","command","args","array","ServerConfigSchema","discriminatedUnion","resolveServerConfig","config","AdapterType","AdapterEndpointConfigSchema","baseUrl","processors","ModelConfigSchema","name","apiKey","adapter","adapters","openai","anthropic","gemini","contextWindow","number","maxInputTokens","maxOutputTokens","fetchUpstreamModels","DbConfigSchema","path","AuditConfigSchema","enabled","db","McpsConfigSchema","servers","default","models","discoveryConfig","audit"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/server/schema.ts"],"sourcesContent":["import { z } from 'zod';\n\n// Header name constants for config parsing\nexport const HeaderNames = {\n\tCLS_SECRET_ID: 'X-CLS-SECRET-ID',\n\tCLS_SECRET_KEY: 'X-CLS-SECRET-KEY',\n\tCLS_REGION: 'X-CLS-REGION',\n\tCLS_ENDPOINT: 'X-CLS-ENDPOINT',\n\tDB_URL: 'X-DB-URL',\n\tDB_READ_URL: 'X-DB-READ-URL',\n\tDB_WRITE_URL: 'X-DB-WRITE-URL',\n\tSERVICE_URL: 'X-SERVICE-URL',\n\tTOKEN: 'X-TOKEN',\n\tMCP_URL: 'X-MCP-URL',\n\tMCP_TYPE: 'X-MCP-TYPE',\n\tMCP_COMMAND: 'X-MCP-COMMAND',\n\tFEISHU_APP_ID: 'X-FEISHU-APP-ID',\n\tFEISHU_APP_SECRET: 'X-FEISHU-APP-SECRET',\n\tFEISHU_DOMAIN: 'X-FEISHU-DOMAIN',\n\t// Tool filtering headers\n\tMCP_READONLY: 'X-MCP-Readonly',\n\tMCP_INCLUDE: 'X-MCP-Include',\n\tMCP_EXCLUDE: 'X-MCP-Exclude',\n} as const;\n\n// Base server config with common fields\nexport const BaseServerConfigSchema = z.object({\n\tdisabled: z.boolean().optional(),\n\t// Headers can be used as alternative to direct config properties\n\theaders: z.record(z.string(), z.string()).optional(),\n});\n\n// Tencent CLS config - supports both direct config and headers\nexport const TencentClsConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('tencent-cls'),\n\tclientId: z.string().optional().describe('Tencent Cloud Secret ID'),\n\tclientSecret: z.string().optional().describe('Tencent Cloud Secret Key'),\n\tregion: z.string().optional().describe('CLS region'),\n\tendpoint: z.string().optional().describe('CLS endpoint'),\n});\nexport type TencentClsConfig = z.infer<typeof TencentClsConfigSchema>;\n\n// SQL config with read/write separation\nexport const SqlConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('sql'),\n\tdbUrl: z.string().optional().describe('Database URL (for both read and write)'),\n\tdbReadUrl: z.string().optional().describe('Database URL for read operations'),\n\tdbWriteUrl: z.string().optional().describe('Database URL for write operations'),\n});\nexport type SqlConfig = z.infer<typeof SqlConfigSchema>;\n\n// Prometheus config\nexport const PrometheusConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('prometheus'),\n\turl: z.string().optional().describe('Prometheus server URL'),\n});\nexport type PrometheusConfig = z.infer<typeof PrometheusConfigSchema>;\n\n// Feishu/Lark config\nexport const FeishuConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('feishu'),\n\tappId: z.string().optional().describe('Feishu App ID'),\n\tappSecret: z.string().optional().describe('Feishu App Secret'),\n\tdomain: z.string().optional().describe('feishu (China) or lark (International)'),\n});\nexport type FeishuConfig = z.infer<typeof FeishuConfigSchema>;\n\n// Relay config for proxying to other MCP servers\nexport const RelayConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.literal('relay'),\n\turl: z.string().optional().describe('Target MCP server URL'),\n\ttransport: z.enum(['http', 'sse']).optional().describe('MCP transport type'),\n\tcommand: z.string().optional().describe('Command to run (stdio transport)'),\n\targs: z.array(z.string()).optional().describe('Command arguments'),\n});\nexport type RelayConfig = z.infer<typeof RelayConfigSchema>;\n\n// Known server config schemas\nconst KnownServerConfigSchema = z.discriminatedUnion('type', [\n\tTencentClsConfigSchema,\n\tSqlConfigSchema,\n\tPrometheusConfigSchema,\n\tFeishuConfigSchema,\n\tRelayConfigSchema,\n]);\n\n// Catch-all for custom/extension server types (e.g. platform-admin, fusionops-admin)\nconst GenericServerConfigSchema = BaseServerConfigSchema.extend({\n\ttype: z.string(),\n}).passthrough();\n\n// Union of known types with generic fallback for extensibility\nexport const ServerConfigSchema = z.union([KnownServerConfigSchema, GenericServerConfigSchema]);\nexport type ServerConfig = z.infer<typeof ServerConfigSchema>;\n\n/**\n * Resolve config from headers - merge headers into config properties\n * @deprecated Use McpServerDef.resolveConfig instead. This function is kept for backward compatibility.\n */\nexport function resolveServerConfig<T extends ServerConfig>(config: T): T {\n\t// This function is now a pass-through - actual resolution is done in McpServerDef.resolveConfig\n\t// Keeping it for backward compatibility with any external code that might use it\n\treturn config;\n}\n\n// ============================================================================\n// Chat/LLM Gateway Configuration\n// ============================================================================\n\n/**\n * Adapter types for protocol conversion\n */\nexport const AdapterType = z.enum(['openai', 'anthropic', 'gemini']);\nexport type AdapterType = z.infer<typeof AdapterType>;\n\n/**\n * Adapter endpoint configuration for chat models\n */\nexport const AdapterEndpointConfigSchema = z.object({\n\tbaseUrl: z.string().optional(),\n\theaders: z.record(z.string(), z.string()).optional(),\n\tprocessors: z.array(z.string()).optional(),\n});\nexport type AdapterEndpointConfig = z.infer<typeof AdapterEndpointConfigSchema>;\n\n/**\n * Model configuration for chat gateway\n */\nexport const ModelConfigSchema = z.object({\n\t/** Model name or pattern (supports wildcards like \"gpt-*\") */\n\tname: z.string(),\n\t/** Base URL for the API */\n\tbaseUrl: z.string().optional(),\n\t/** API key (uses Authorization: Bearer header) */\n\tapiKey: z.string().optional(),\n\t/** Additional headers */\n\theaders: z.record(z.string(), z.string()).optional(),\n\t/** Default adapter to use for protocol conversion */\n\tadapter: AdapterType.optional(),\n\t/** Adapter-specific endpoint configurations */\n\tadapters: z\n\t\t.object({\n\t\t\topenai: AdapterEndpointConfigSchema.optional(),\n\t\t\tanthropic: AdapterEndpointConfigSchema.optional(),\n\t\t\tgemini: AdapterEndpointConfigSchema.optional(),\n\t\t})\n\t\t.optional(),\n\t/** Processor chain */\n\tprocessors: z.array(z.string()).optional(),\n\t/** Context window size (max total tokens) */\n\tcontextWindow: z.number().optional(),\n\t/** Max input tokens */\n\tmaxInputTokens: z.number().optional(),\n\t/** Max output tokens */\n\tmaxOutputTokens: z.number().optional(),\n\t/** Whether to fetch models from upstream on /v1/models */\n\tfetchUpstreamModels: z.boolean().optional(),\n});\nexport type ModelConfig = z.infer<typeof ModelConfigSchema>;\n\n/**\n * Database configuration for audit storage\n */\nexport const DbConfigSchema = z.object({\n\t/** Path to SQLite database file (default: .mcps.db) */\n\tpath: z.string().optional(),\n});\nexport type DbConfig = z.infer<typeof DbConfigSchema>;\n\n/**\n * Audit configuration\n */\nexport const AuditConfigSchema = z.object({\n\t/** Enable audit logging (default: true) */\n\tenabled: z.boolean().optional(),\n\t/** Database configuration for audit storage (overrides root db config) */\n\tdb: DbConfigSchema.optional(),\n});\nexport type AuditConfig = z.infer<typeof AuditConfigSchema>;\n\n// Main config file schema\nexport const McpsConfigSchema = z.object({\n\tservers: z.record(z.string(), ServerConfigSchema).default({}),\n\t/** Model configurations for chat gateway (array format) */\n\tmodels: z.array(ModelConfigSchema).optional(),\n\t/** Whether to expose server config discovery endpoints (default: false) */\n\tdiscoveryConfig: z.boolean().optional(),\n\t/** Database configuration (shared, used as fallback for audit.db) */\n\tdb: DbConfigSchema.optional(),\n\t/** Audit configuration */\n\taudit: AuditConfigSchema.optional(),\n});\nexport type McpsConfig = z.infer<typeof McpsConfigSchema>;\n\n// Legacy ChatConfig type alias for backwards compatibility\nexport type ChatConfig = { models?: ModelConfig[] };\n"],"names":["z","HeaderNames","CLS_SECRET_ID","CLS_SECRET_KEY","CLS_REGION","CLS_ENDPOINT","DB_URL","DB_READ_URL","DB_WRITE_URL","SERVICE_URL","TOKEN","MCP_URL","MCP_TYPE","MCP_COMMAND","FEISHU_APP_ID","FEISHU_APP_SECRET","FEISHU_DOMAIN","MCP_READONLY","MCP_INCLUDE","MCP_EXCLUDE","BaseServerConfigSchema","object","disabled","boolean","optional","headers","record","string","TencentClsConfigSchema","extend","type","literal","clientId","describe","clientSecret","region","endpoint","SqlConfigSchema","dbUrl","dbReadUrl","dbWriteUrl","PrometheusConfigSchema","url","FeishuConfigSchema","appId","appSecret","domain","RelayConfigSchema","transport","enum","command","args","array","KnownServerConfigSchema","discriminatedUnion","GenericServerConfigSchema","passthrough","ServerConfigSchema","union","resolveServerConfig","config","AdapterType","AdapterEndpointConfigSchema","baseUrl","processors","ModelConfigSchema","name","apiKey","adapter","adapters","openai","anthropic","gemini","contextWindow","number","maxInputTokens","maxOutputTokens","fetchUpstreamModels","DbConfigSchema","path","AuditConfigSchema","enabled","db","McpsConfigSchema","servers","default","models","discoveryConfig","audit"],"mappings":"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"}
@@ -5,38 +5,32 @@ import { LRUCache } from "lru-cache";
5
5
  import { isDevelopment } from "std-env";
6
6
  import { findMcpServerDef } from "../providers/findMcpServerDef.js";
7
7
  import { registerApiRoutes } from "./api-routes.js";
8
- import { auditMiddleware, configureAudit } from "./audit.js";
9
8
  import { registerChatRoutes } from "./chat-routes.js";
10
9
  import { loadConfig, loadEnvFiles, substituteEnvVars } from "./config.js";
10
+ import { createMcpsEmitter, McpsEventType } from "./events.js";
11
11
  import { registerMcpRoutes } from "./mcp-routes.js";
12
12
  const log = consola.withTag("mcps");
13
13
  export function createServer(options = {}) {
14
- const { cwd = process.cwd(), discoveryConfig: discoveryConfigOption } = options;
14
+ const { cwd = process.cwd(), discoveryConfig: discoveryConfigOption, setup } = options;
15
15
  const app = new Hono();
16
+ const emitter = createMcpsEmitter();
17
+ const apiRouters = {};
16
18
  // Request logging
17
19
  app.use(logger((v) => {
18
20
  log.debug(v);
19
21
  }));
20
- // Audit middleware
21
- app.use(auditMiddleware());
22
+ // Request event middleware - emits events for subscribers (audit, monitoring, etc.)
23
+ app.use(requestEventMiddleware(emitter));
22
24
  // Load .env files first
23
25
  loadEnvFiles(cwd);
24
26
  // Load config (with env var substitution)
25
27
  const config = substituteEnvVars(loadConfig(cwd));
26
- // discoveryConfig: CLI option overrides config file, defaults to false
27
28
  const discoveryConfig = discoveryConfigOption ?? config.discoveryConfig ?? false;
28
29
  // Log available server types from registry
29
30
  const serverDefs = findMcpServerDef();
30
31
  log.info(`Available server types: ${serverDefs.map((d) => d.name).join(", ")}`);
31
32
  log.info(`Loaded ${Object.keys(config.servers).length} servers from config (discoveryConfig: ${discoveryConfig})`);
32
- // Configure audit with lazy DB initialization
33
- // DB will only be initialized when the first audit event needs persistence
34
- configureAudit(config.audit, config.db);
35
- const auditEnabled = config.audit?.enabled !== false;
36
- const auditDbPath = config.audit?.db?.path ?? config.db?.path ?? ".mcps.db";
37
- log.info(`Audit configured: enabled=${auditEnabled}, db=${auditDbPath} (lazy init)`);
38
- // Unified cache for all MCP servers (both pre-configured and dynamic)
39
- // Keyed by cache key from def.getCacheKey() or `config::${name}` for pre-configured
33
+ // Unified cache for all MCP servers
40
34
  const serverCache = new LRUCache({
41
35
  max: 100,
42
36
  dispose: (value, key) => {
@@ -44,76 +38,81 @@ export function createServer(options = {}) {
44
38
  value.close?.().catch((e) => log.error("Failed to close server", e));
45
39
  }
46
40
  });
47
- // =========================================================================
48
- // Register MCP routes (pre-configured and dynamic endpoints)
49
- // =========================================================================
50
- registerMcpRoutes({
41
+ const ctx = {
51
42
  app,
52
43
  config,
53
- serverCache
54
- });
55
- // =========================================================================
56
- // Register Chat/LLM Gateway routes
57
- // =========================================================================
58
- registerChatRoutes({
59
- app,
60
- config
61
- });
62
- // =========================================================================
63
- // Health and info endpoints
64
- // =========================================================================
65
- app.get("/health", (c) => c.json({
66
- status: "ok"
67
- }));
68
- app.get("/", (c) => {
69
- return c.json({
70
- message: "hello"
44
+ emitter,
45
+ serverCache,
46
+ apiRouters
47
+ };
48
+ const finalize = async () => {
49
+ // Allow plugins to set up before routes are registered
50
+ await setup?.(ctx);
51
+ // =========================================================================
52
+ // Register MCP routes (pre-configured and dynamic endpoints)
53
+ // =========================================================================
54
+ registerMcpRoutes({
55
+ app,
56
+ config,
57
+ serverCache
71
58
  });
72
- });
73
- // Server info - only available in dev mode or when discoveryConfig is enabled
74
- if (isDevelopment || discoveryConfig) {
75
- app.get("/info", (c) => {
76
- // Dynamically get routes from Hono app
77
- const routes = app.routes.map((r) => ({
78
- method: r.method,
79
- path: r.path
80
- })).filter((r) => r.method !== "ALL" || r.path.startsWith("/mcp/")).sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
81
- // Group routes by category
82
- const mcpRoutes = routes.filter((r) => r.path.startsWith("/mcp/")).map((r) => r.path);
83
- const apiRoutes = routes.filter((r) => r.path.startsWith("/api/")).map((r) => `${r.method} ${r.path}`);
84
- const chatRoutes = routes.filter((r) => r.path.startsWith("/v1/")).map((r) => `${r.method} ${r.path}`);
85
- // Get unique MCP paths
86
- const uniqueMcpPaths = [
87
- ...new Set(mcpRoutes)
88
- ];
89
- return c.json({
90
- name: "@wener/mcps",
91
- version: "0.1.0",
92
- servers: Object.keys(config.servers),
93
- serverTypes: findMcpServerDef().map((d) => d.name),
94
- models: config.models ? config.models.map((m) => m.name) : [],
95
- endpoints: {
96
- mcp: uniqueMcpPaths,
97
- api: [
98
- ...new Set(apiRoutes)
99
- ],
100
- chat: [
101
- ...new Set(chatRoutes)
102
- ]
103
- }
59
+ // =========================================================================
60
+ // Register Chat/LLM Gateway routes
61
+ // =========================================================================
62
+ registerChatRoutes({
63
+ app,
64
+ config
65
+ });
66
+ // =========================================================================
67
+ // Health and info endpoints
68
+ // =========================================================================
69
+ app.get("/health", (c) => c.json({
70
+ status: "ok"
71
+ }));
72
+ app.get("/", (c) => c.json({
73
+ message: "hello"
74
+ }));
75
+ if (isDevelopment || discoveryConfig) {
76
+ app.get("/info", (c) => {
77
+ const routes = app.routes.map((r) => ({
78
+ method: r.method,
79
+ path: r.path
80
+ })).filter((r) => r.method !== "ALL" || r.path.startsWith("/mcp/")).sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
81
+ const mcpRoutes = routes.filter((r) => r.path.startsWith("/mcp/")).map((r) => r.path);
82
+ const apiRoutes = routes.filter((r) => r.path.startsWith("/api/")).map((r) => `${r.method} ${r.path}`);
83
+ const chatRoutes = routes.filter((r) => r.path.startsWith("/v1/")).map((r) => `${r.method} ${r.path}`);
84
+ const uniqueMcpPaths = [
85
+ ...new Set(mcpRoutes)
86
+ ];
87
+ return c.json({
88
+ name: "@wener/mcps",
89
+ version: "0.1.0",
90
+ servers: Object.keys(config.servers),
91
+ serverTypes: findMcpServerDef().map((d) => d.name),
92
+ models: config.models ? config.models.map((m) => m.name) : [],
93
+ endpoints: {
94
+ mcp: uniqueMcpPaths,
95
+ api: [
96
+ ...new Set(apiRoutes)
97
+ ],
98
+ chat: [
99
+ ...new Set(chatRoutes)
100
+ ]
101
+ }
102
+ });
104
103
  });
104
+ }
105
+ // =========================================================================
106
+ // Register oRPC API routes (with any plugin-provided routers)
107
+ // =========================================================================
108
+ registerApiRoutes({
109
+ app,
110
+ config,
111
+ apiRouters,
112
+ statsProvider: ctx.statsProvider
105
113
  });
106
- }
107
- // =========================================================================
108
- // Register oRPC API routes
109
- // =========================================================================
110
- registerApiRoutes({
111
- app,
112
- config
113
- });
114
- /**
115
- * Print available endpoints grouped by category
116
- */ const printEndpoints = () => {
114
+ };
115
+ const printEndpoints = () => {
117
116
  const routes = app.routes;
118
117
  const mcpPaths = new Set();
119
118
  const apiPaths = new Set();
@@ -135,32 +134,88 @@ export function createServer(options = {}) {
135
134
  }
136
135
  }
137
136
  log.info("Available endpoints:");
138
- if (mcpPaths.size > 0) {
137
+ if (mcpPaths.size > 0)
139
138
  log.info(` MCP: ${[
140
139
  ...mcpPaths
141
140
  ].sort().join(", ")}`);
142
- }
143
- if (chatPaths.size > 0) {
141
+ if (chatPaths.size > 0)
144
142
  log.info(` Chat: ${[
145
143
  ...chatPaths
146
144
  ].sort().join(", ")}`);
147
- }
148
- if (apiPaths.size > 0) {
145
+ if (apiPaths.size > 0)
149
146
  log.info(` API: ${[
150
147
  ...apiPaths
151
148
  ].sort().join(", ")}`);
152
- }
153
- if (otherPaths.size > 0) {
149
+ if (otherPaths.size > 0)
154
150
  log.info(` Other: ${[
155
151
  ...otherPaths
156
152
  ].sort().join(", ")}`);
157
- }
158
153
  };
159
154
  return {
160
155
  app,
161
156
  config,
157
+ emitter,
162
158
  serverCache,
163
- printEndpoints
159
+ printEndpoints,
160
+ finalize
161
+ };
162
+ }
163
+ function headersToRecord(headers) {
164
+ const record = {};
165
+ headers.forEach((value, key) => {
166
+ record[key] = value;
167
+ });
168
+ return record;
169
+ }
170
+ /**
171
+ * Middleware that emits request events via the emitter.
172
+ * Subscribers (like audit plugin) can listen and handle these events.
173
+ */ function requestEventMiddleware(emitter) {
174
+ return async (c, next) => {
175
+ const startTime = Date.now();
176
+ const path = c.req.path;
177
+ let serverName;
178
+ let serverType;
179
+ const mcpMatch = path.match(/^\/mcp\/([^/]+)/);
180
+ if (mcpMatch) {
181
+ serverName = mcpMatch[1];
182
+ if ([
183
+ "tencent-cls",
184
+ "sql",
185
+ "prometheus",
186
+ "relay"
187
+ ].includes(serverName)) {
188
+ serverType = serverName;
189
+ }
190
+ else {
191
+ serverType = "custom";
192
+ }
193
+ }
194
+ if (path.startsWith("/v1/")) {
195
+ serverType = "chat";
196
+ }
197
+ let error;
198
+ try {
199
+ await next();
200
+ }
201
+ catch (e) {
202
+ error = e instanceof Error ? e.message : String(e);
203
+ throw e;
204
+ }
205
+ finally {
206
+ const durationMs = Date.now() - startTime;
207
+ emitter.emit(McpsEventType.Request, {
208
+ timestamp: new Date().toISOString(),
209
+ method: c.req.method,
210
+ path,
211
+ serverName,
212
+ serverType,
213
+ status: c.res.status,
214
+ durationMs,
215
+ error,
216
+ requestHeaders: headersToRecord(c.req.raw.headers)
217
+ });
218
+ }
164
219
  };
165
220
  }
166
221
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/server.ts"],"sourcesContent":["import consola from 'consola';\nimport { Hono } from 'hono';\nimport { logger } from 'hono/logger';\nimport { LRUCache } from 'lru-cache';\nimport { isDevelopment } from 'std-env';\nimport type { McpServerInstance } from '@wener/ai/mcp';\nimport { findMcpServerDef } from '../providers/findMcpServerDef';\nimport { registerApiRoutes } from './api-routes';\nimport { auditMiddleware, configureAudit } from './audit';\nimport { registerChatRoutes } from './chat-routes';\nimport { loadConfig, loadEnvFiles, substituteEnvVars } from './config';\nimport { registerMcpRoutes } from './mcp-routes';\n\nconst log = consola.withTag('mcps');\n\nexport interface CreateServerOptions {\n\tcwd?: string;\n\tport?: number;\n\t/** Enable server config discovery endpoints (default: false) */\n\tdiscoveryConfig?: boolean;\n}\n\nexport function createServer(options: CreateServerOptions = {}) {\n\tconst { cwd = process.cwd(), discoveryConfig: discoveryConfigOption } = options;\n\n\tconst app = new Hono();\n\n\t// Request logging\n\tapp.use(\n\t\tlogger((v) => {\n\t\t\tlog.debug(v);\n\t\t}),\n\t);\n\n\t// Audit middleware\n\tapp.use(auditMiddleware());\n\n\t// Load .env files first\n\tloadEnvFiles(cwd);\n\n\t// Load config (with env var substitution)\n\tconst config = substituteEnvVars(loadConfig(cwd));\n\t// discoveryConfig: CLI option overrides config file, defaults to false\n\tconst discoveryConfig = discoveryConfigOption ?? config.discoveryConfig ?? false;\n\n\t// Log available server types from registry\n\tconst serverDefs = findMcpServerDef();\n\tlog.info(`Available server types: ${serverDefs.map((d) => d.name).join(', ')}`);\n\tlog.info(`Loaded ${Object.keys(config.servers).length} servers from config (discoveryConfig: ${discoveryConfig})`);\n\n\t// Configure audit with lazy DB initialization\n\t// DB will only be initialized when the first audit event needs persistence\n\tconfigureAudit(config.audit, config.db);\n\tconst auditEnabled = config.audit?.enabled !== false;\n\tconst auditDbPath = config.audit?.db?.path ?? config.db?.path ?? '.mcps.db';\n\tlog.info(`Audit configured: enabled=${auditEnabled}, db=${auditDbPath} (lazy init)`);\n\n\t// Unified cache for all MCP servers (both pre-configured and dynamic)\n\t// Keyed by cache key from def.getCacheKey() or `config::${name}` for pre-configured\n\tconst serverCache = new LRUCache<string, McpServerInstance>({\n\t\tmax: 100,\n\t\tdispose: (value, key) => {\n\t\t\tlog.info(`Closing expired MCP server: ${key}`);\n\t\t\tvalue.close?.().catch((e: unknown) => log.error('Failed to close server', e));\n\t\t},\n\t});\n\n\t// =========================================================================\n\t// Register MCP routes (pre-configured and dynamic endpoints)\n\t// =========================================================================\n\tregisterMcpRoutes({ app, config, serverCache });\n\n\t// =========================================================================\n\t// Register Chat/LLM Gateway routes\n\t// =========================================================================\n\tregisterChatRoutes({ app, config });\n\n\t// =========================================================================\n\t// Health and info endpoints\n\t// =========================================================================\n\tapp.get('/health', (c) => c.json({ status: 'ok' }));\n\n\tapp.get('/', (c) => {\n\t\treturn c.json({ message: 'hello' });\n\t});\n\n\t// Server info - only available in dev mode or when discoveryConfig is enabled\n\tif (isDevelopment || discoveryConfig) {\n\t\tapp.get('/info', (c) => {\n\t\t\t// Dynamically get routes from Hono app\n\t\t\tconst routes = app.routes\n\t\t\t\t.map((r) => ({ method: r.method, path: r.path }))\n\t\t\t\t.filter((r) => r.method !== 'ALL' || r.path.startsWith('/mcp/'))\n\t\t\t\t.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));\n\n\t\t\t// Group routes by category\n\t\t\tconst mcpRoutes = routes.filter((r) => r.path.startsWith('/mcp/')).map((r) => r.path);\n\t\t\tconst apiRoutes = routes.filter((r) => r.path.startsWith('/api/')).map((r) => `${r.method} ${r.path}`);\n\t\t\tconst chatRoutes = routes.filter((r) => r.path.startsWith('/v1/')).map((r) => `${r.method} ${r.path}`);\n\n\t\t\t// Get unique MCP paths\n\t\t\tconst uniqueMcpPaths = [...new Set(mcpRoutes)];\n\n\t\t\treturn c.json({\n\t\t\t\tname: '@wener/mcps',\n\t\t\t\tversion: '0.1.0',\n\t\t\t\tservers: Object.keys(config.servers),\n\t\t\t\tserverTypes: findMcpServerDef().map((d) => d.name),\n\t\t\t\tmodels: config.models ? config.models.map((m) => m.name) : [],\n\t\t\t\tendpoints: {\n\t\t\t\t\tmcp: uniqueMcpPaths,\n\t\t\t\t\tapi: [...new Set(apiRoutes)],\n\t\t\t\t\tchat: [...new Set(chatRoutes)],\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\t// =========================================================================\n\t// Register oRPC API routes\n\t// =========================================================================\n\tregisterApiRoutes({ app, config });\n\n\t/**\n\t * Print available endpoints grouped by category\n\t */\n\tconst printEndpoints = () => {\n\t\tconst routes = app.routes;\n\t\tconst mcpPaths = new Set<string>();\n\t\tconst apiPaths = new Set<string>();\n\t\tconst chatPaths = new Set<string>();\n\t\tconst otherPaths = new Set<string>();\n\n\t\tfor (const route of routes) {\n\t\t\tconst path = route.path;\n\t\t\tif (path.startsWith('/mcp/')) {\n\t\t\t\tmcpPaths.add(path);\n\t\t\t} else if (path.startsWith('/api/')) {\n\t\t\t\tapiPaths.add(`${route.method} ${path}`);\n\t\t\t} else if (path.startsWith('/v1/')) {\n\t\t\t\tchatPaths.add(`${route.method} ${path}`);\n\t\t\t} else if (route.method !== 'ALL') {\n\t\t\t\totherPaths.add(`${route.method} ${path}`);\n\t\t\t}\n\t\t}\n\n\t\tlog.info('Available endpoints:');\n\t\tif (mcpPaths.size > 0) {\n\t\t\tlog.info(` MCP: ${[...mcpPaths].sort().join(', ')}`);\n\t\t}\n\t\tif (chatPaths.size > 0) {\n\t\t\tlog.info(` Chat: ${[...chatPaths].sort().join(', ')}`);\n\t\t}\n\t\tif (apiPaths.size > 0) {\n\t\t\tlog.info(` API: ${[...apiPaths].sort().join(', ')}`);\n\t\t}\n\t\tif (otherPaths.size > 0) {\n\t\t\tlog.info(` Other: ${[...otherPaths].sort().join(', ')}`);\n\t\t}\n\t};\n\n\treturn { app, config, serverCache, printEndpoints };\n}\n"],"names":["consola","Hono","logger","LRUCache","isDevelopment","findMcpServerDef","registerApiRoutes","auditMiddleware","configureAudit","registerChatRoutes","loadConfig","loadEnvFiles","substituteEnvVars","registerMcpRoutes","log","withTag","createServer","options","cwd","process","discoveryConfig","discoveryConfigOption","app","use","v","debug","config","serverDefs","info","map","d","name","join","Object","keys","servers","length","audit","db","auditEnabled","enabled","auditDbPath","path","serverCache","max","dispose","value","key","close","catch","e","error","get","c","json","status","message","routes","r","method","filter","startsWith","sort","a","b","localeCompare","mcpRoutes","apiRoutes","chatRoutes","uniqueMcpPaths","Set","version","serverTypes","models","m","endpoints","mcp","api","chat","printEndpoints","mcpPaths","apiPaths","chatPaths","otherPaths","route","add","size"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/server/server.ts"],"sourcesContent":["import consola from 'consola';\nimport type { Context, Next } from 'hono';\nimport { Hono } from 'hono';\nimport { logger } from 'hono/logger';\nimport { LRUCache } from 'lru-cache';\nimport { isDevelopment } from 'std-env';\nimport type { McpServerInstance } from '@wener/ai/mcp';\nimport { findMcpServerDef } from '../providers/findMcpServerDef';\nimport { registerApiRoutes } from './api-routes';\nimport { registerChatRoutes } from './chat-routes';\nimport { loadConfig, loadEnvFiles, substituteEnvVars } from './config';\nimport { createMcpsEmitter, McpsEventType, type McpsEmitter } from './events';\nimport { registerMcpRoutes } from './mcp-routes';\n\nconst log = consola.withTag('mcps');\n\nexport interface McpsServerContext {\n\tapp: Hono;\n\tconfig: import('./schema').McpsConfig;\n\temitter: McpsEmitter;\n\tserverCache: LRUCache<string, McpServerInstance>;\n\t/** Plugins can register additional oRPC routers here */\n\tapiRouters: Record<string, any>;\n\t/** Plugins can register stats providers here */\n\tstatsProvider?: StatsProvider;\n}\n\nexport interface StatsProvider {\n\tgetStats(options: { from?: string | null; to?: string | null }): {\n\t\ttotalRequests: number;\n\t\ttotalErrors: number;\n\t\tavgDurationMs: number;\n\t\tbyServer: Array<{ name: string; count: number }>;\n\t\tbyMethod: Array<{ method: string; count: number }>;\n\t};\n\tqueryEvents(options: { limit?: number }): { events: Array<{ path: string }>; total: number };\n}\n\nexport interface CreateServerOptions {\n\tcwd?: string;\n\tport?: number;\n\t/** Enable server config discovery endpoints (default: false) */\n\tdiscoveryConfig?: boolean;\n\t/**\n\t * Called after core server is created but before routes are registered.\n\t * Use this to set up optional plugins like audit via the emitter.\n\t */\n\tsetup?: (ctx: McpsServerContext) => void | Promise<void>;\n}\n\nexport function createServer(options: CreateServerOptions = {}) {\n\tconst { cwd = process.cwd(), discoveryConfig: discoveryConfigOption, setup } = options;\n\n\tconst app = new Hono();\n\tconst emitter = createMcpsEmitter();\n\tconst apiRouters: Record<string, any> = {};\n\n\t// Request logging\n\tapp.use(\n\t\tlogger((v) => {\n\t\t\tlog.debug(v);\n\t\t}),\n\t);\n\n\t// Request event middleware - emits events for subscribers (audit, monitoring, etc.)\n\tapp.use(requestEventMiddleware(emitter));\n\n\t// Load .env files first\n\tloadEnvFiles(cwd);\n\n\t// Load config (with env var substitution)\n\tconst config = substituteEnvVars(loadConfig(cwd));\n\tconst discoveryConfig = discoveryConfigOption ?? config.discoveryConfig ?? false;\n\n\t// Log available server types from registry\n\tconst serverDefs = findMcpServerDef();\n\tlog.info(`Available server types: ${serverDefs.map((d) => d.name).join(', ')}`);\n\tlog.info(`Loaded ${Object.keys(config.servers).length} servers from config (discoveryConfig: ${discoveryConfig})`);\n\n\t// Unified cache for all MCP servers\n\tconst serverCache = new LRUCache<string, McpServerInstance>({\n\t\tmax: 100,\n\t\tdispose: (value, key) => {\n\t\t\tlog.info(`Closing expired MCP server: ${key}`);\n\t\t\tvalue.close?.().catch((e: unknown) => log.error('Failed to close server', e));\n\t\t},\n\t});\n\n\tconst ctx: McpsServerContext = { app, config, emitter, serverCache, apiRouters };\n\n\tconst finalize = async () => {\n\t\t// Allow plugins to set up before routes are registered\n\t\tawait setup?.(ctx);\n\n\t\t// =========================================================================\n\t\t// Register MCP routes (pre-configured and dynamic endpoints)\n\t\t// =========================================================================\n\t\tregisterMcpRoutes({ app, config, serverCache });\n\n\t\t// =========================================================================\n\t\t// Register Chat/LLM Gateway routes\n\t\t// =========================================================================\n\t\tregisterChatRoutes({ app, config });\n\n\t\t// =========================================================================\n\t\t// Health and info endpoints\n\t\t// =========================================================================\n\t\tapp.get('/health', (c) => c.json({ status: 'ok' }));\n\t\tapp.get('/', (c) => c.json({ message: 'hello' }));\n\n\t\tif (isDevelopment || discoveryConfig) {\n\t\t\tapp.get('/info', (c) => {\n\t\t\t\tconst routes = app.routes\n\t\t\t\t\t.map((r) => ({ method: r.method, path: r.path }))\n\t\t\t\t\t.filter((r) => r.method !== 'ALL' || r.path.startsWith('/mcp/'))\n\t\t\t\t\t.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));\n\n\t\t\t\tconst mcpRoutes = routes.filter((r) => r.path.startsWith('/mcp/')).map((r) => r.path);\n\t\t\t\tconst apiRoutes = routes.filter((r) => r.path.startsWith('/api/')).map((r) => `${r.method} ${r.path}`);\n\t\t\t\tconst chatRoutes = routes.filter((r) => r.path.startsWith('/v1/')).map((r) => `${r.method} ${r.path}`);\n\t\t\t\tconst uniqueMcpPaths = [...new Set(mcpRoutes)];\n\n\t\t\t\treturn c.json({\n\t\t\t\t\tname: '@wener/mcps',\n\t\t\t\t\tversion: '0.1.0',\n\t\t\t\t\tservers: Object.keys(config.servers),\n\t\t\t\t\tserverTypes: findMcpServerDef().map((d) => d.name),\n\t\t\t\t\tmodels: config.models ? config.models.map((m) => m.name) : [],\n\t\t\t\t\tendpoints: {\n\t\t\t\t\t\tmcp: uniqueMcpPaths,\n\t\t\t\t\t\tapi: [...new Set(apiRoutes)],\n\t\t\t\t\t\tchat: [...new Set(chatRoutes)],\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\t// =========================================================================\n\t\t// Register oRPC API routes (with any plugin-provided routers)\n\t\t// =========================================================================\n\t\tregisterApiRoutes({ app, config, apiRouters, statsProvider: ctx.statsProvider });\n\t};\n\n\tconst printEndpoints = () => {\n\t\tconst routes = app.routes;\n\t\tconst mcpPaths = new Set<string>();\n\t\tconst apiPaths = new Set<string>();\n\t\tconst chatPaths = new Set<string>();\n\t\tconst otherPaths = new Set<string>();\n\n\t\tfor (const route of routes) {\n\t\t\tconst path = route.path;\n\t\t\tif (path.startsWith('/mcp/')) {\n\t\t\t\tmcpPaths.add(path);\n\t\t\t} else if (path.startsWith('/api/')) {\n\t\t\t\tapiPaths.add(`${route.method} ${path}`);\n\t\t\t} else if (path.startsWith('/v1/')) {\n\t\t\t\tchatPaths.add(`${route.method} ${path}`);\n\t\t\t} else if (route.method !== 'ALL') {\n\t\t\t\totherPaths.add(`${route.method} ${path}`);\n\t\t\t}\n\t\t}\n\n\t\tlog.info('Available endpoints:');\n\t\tif (mcpPaths.size > 0) log.info(` MCP: ${[...mcpPaths].sort().join(', ')}`);\n\t\tif (chatPaths.size > 0) log.info(` Chat: ${[...chatPaths].sort().join(', ')}`);\n\t\tif (apiPaths.size > 0) log.info(` API: ${[...apiPaths].sort().join(', ')}`);\n\t\tif (otherPaths.size > 0) log.info(` Other: ${[...otherPaths].sort().join(', ')}`);\n\t};\n\n\treturn { app, config, emitter, serverCache, printEndpoints, finalize };\n}\n\nfunction headersToRecord(headers: Headers): Record<string, string> {\n\tconst record: Record<string, string> = {};\n\theaders.forEach((value, key) => {\n\t\trecord[key] = value;\n\t});\n\treturn record;\n}\n\n/**\n * Middleware that emits request events via the emitter.\n * Subscribers (like audit plugin) can listen and handle these events.\n */\nfunction requestEventMiddleware(emitter: McpsEmitter) {\n\treturn async (c: Context, next: Next) => {\n\t\tconst startTime = Date.now();\n\t\tconst path = c.req.path;\n\n\t\tlet serverName: string | undefined;\n\t\tlet serverType: string | undefined;\n\n\t\tconst mcpMatch = path.match(/^\\/mcp\\/([^/]+)/);\n\t\tif (mcpMatch) {\n\t\t\tserverName = mcpMatch[1];\n\t\t\tif (['tencent-cls', 'sql', 'prometheus', 'relay'].includes(serverName)) {\n\t\t\t\tserverType = serverName;\n\t\t\t} else {\n\t\t\t\tserverType = 'custom';\n\t\t\t}\n\t\t}\n\n\t\tif (path.startsWith('/v1/')) {\n\t\t\tserverType = 'chat';\n\t\t}\n\n\t\tlet error: string | undefined;\n\n\t\ttry {\n\t\t\tawait next();\n\t\t} catch (e) {\n\t\t\terror = e instanceof Error ? e.message : String(e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconst durationMs = Date.now() - startTime;\n\n\t\t\temitter.emit(McpsEventType.Request, {\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\tmethod: c.req.method,\n\t\t\t\tpath,\n\t\t\t\tserverName,\n\t\t\t\tserverType,\n\t\t\t\tstatus: c.res.status,\n\t\t\t\tdurationMs,\n\t\t\t\terror,\n\t\t\t\trequestHeaders: headersToRecord(c.req.raw.headers),\n\t\t\t});\n\t\t}\n\t};\n}\n"],"names":["consola","Hono","logger","LRUCache","isDevelopment","findMcpServerDef","registerApiRoutes","registerChatRoutes","loadConfig","loadEnvFiles","substituteEnvVars","createMcpsEmitter","McpsEventType","registerMcpRoutes","log","withTag","createServer","options","cwd","process","discoveryConfig","discoveryConfigOption","setup","app","emitter","apiRouters","use","v","debug","requestEventMiddleware","config","serverDefs","info","map","d","name","join","Object","keys","servers","length","serverCache","max","dispose","value","key","close","catch","e","error","ctx","finalize","get","c","json","status","message","routes","r","method","path","filter","startsWith","sort","a","b","localeCompare","mcpRoutes","apiRoutes","chatRoutes","uniqueMcpPaths","Set","version","serverTypes","models","m","endpoints","mcp","api","chat","statsProvider","printEndpoints","mcpPaths","apiPaths","chatPaths","otherPaths","route","add","size","headersToRecord","headers","record","forEach","next","startTime","Date","now","req","serverName","serverType","mcpMatch","match","includes","Error","String","durationMs","emit","Request","timestamp","toISOString","res","requestHeaders","raw"],"mappings":"AAAA,OAAOA,aAAa,UAAU;AAE9B,SAASC,IAAI,QAAQ,OAAO;AAC5B,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,QAAQ,QAAQ,YAAY;AACrC,SAASC,aAAa,QAAQ,UAAU;AAExC,SAASC,gBAAgB,QAAQ,gCAAgC;AACjE,SAASC,iBAAiB,QAAQ,eAAe;AACjD,SAASC,kBAAkB,QAAQ,gBAAgB;AACnD,SAASC,UAAU,EAAEC,YAAY,EAAEC,iBAAiB,QAAQ,WAAW;AACvE,SAASC,iBAAiB,EAAEC,aAAa,QAA0B,WAAW;AAC9E,SAASC,iBAAiB,QAAQ,eAAe;AAEjD,MAAMC,MAAMd,QAAQe,OAAO,CAAC;AAoC5B,OAAO,SAASC,aAAaC,UAA+B,CAAC,CAAC;IAC7D,MAAM,EAAEC,MAAMC,QAAQD,GAAG,EAAE,EAAEE,iBAAiBC,qBAAqB,EAAEC,KAAK,EAAE,GAAGL;IAE/E,MAAMM,MAAM,IAAItB;IAChB,MAAMuB,UAAUb;IAChB,MAAMc,aAAkC,CAAC;IAEzC,kBAAkB;IAClBF,IAAIG,GAAG,CACNxB,OAAO,CAACyB;QACPb,IAAIc,KAAK,CAACD;IACX;IAGD,oFAAoF;IACpFJ,IAAIG,GAAG,CAACG,uBAAuBL;IAE/B,wBAAwB;IACxBf,aAAaS;IAEb,0CAA0C;IAC1C,MAAMY,SAASpB,kBAAkBF,WAAWU;IAC5C,MAAME,kBAAkBC,yBAAyBS,OAAOV,eAAe,IAAI;IAE3E,2CAA2C;IAC3C,MAAMW,aAAa1B;IACnBS,IAAIkB,IAAI,CAAC,CAAC,wBAAwB,EAAED,WAAWE,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI,EAAEC,IAAI,CAAC,OAAO;IAC9EtB,IAAIkB,IAAI,CAAC,CAAC,OAAO,EAAEK,OAAOC,IAAI,CAACR,OAAOS,OAAO,EAAEC,MAAM,CAAC,uCAAuC,EAAEpB,gBAAgB,CAAC,CAAC;IAEjH,oCAAoC;IACpC,MAAMqB,cAAc,IAAItC,SAAoC;QAC3DuC,KAAK;QACLC,SAAS,CAACC,OAAOC;YAChB/B,IAAIkB,IAAI,CAAC,CAAC,4BAA4B,EAAEa,KAAK;YAC7CD,MAAME,KAAK,KAAKC,MAAM,CAACC,IAAelC,IAAImC,KAAK,CAAC,0BAA0BD;QAC3E;IACD;IAEA,MAAME,MAAyB;QAAE3B;QAAKO;QAAQN;QAASiB;QAAahB;IAAW;IAE/E,MAAM0B,WAAW;QAChB,uDAAuD;QACvD,MAAM7B,QAAQ4B;QAEd,4EAA4E;QAC5E,6DAA6D;QAC7D,4EAA4E;QAC5ErC,kBAAkB;YAAEU;YAAKO;YAAQW;QAAY;QAE7C,4EAA4E;QAC5E,mCAAmC;QACnC,4EAA4E;QAC5ElC,mBAAmB;YAAEgB;YAAKO;QAAO;QAEjC,4EAA4E;QAC5E,4BAA4B;QAC5B,4EAA4E;QAC5EP,IAAI6B,GAAG,CAAC,WAAW,CAACC,IAAMA,EAAEC,IAAI,CAAC;gBAAEC,QAAQ;YAAK;QAChDhC,IAAI6B,GAAG,CAAC,KAAK,CAACC,IAAMA,EAAEC,IAAI,CAAC;gBAAEE,SAAS;YAAQ;QAE9C,IAAIpD,iBAAiBgB,iBAAiB;YACrCG,IAAI6B,GAAG,CAAC,SAAS,CAACC;gBACjB,MAAMI,SAASlC,IAAIkC,MAAM,CACvBxB,GAAG,CAAC,CAACyB,IAAO,CAAA;wBAAEC,QAAQD,EAAEC,MAAM;wBAAEC,MAAMF,EAAEE,IAAI;oBAAC,CAAA,GAC7CC,MAAM,CAAC,CAACH,IAAMA,EAAEC,MAAM,KAAK,SAASD,EAAEE,IAAI,CAACE,UAAU,CAAC,UACtDC,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEJ,IAAI,CAACM,aAAa,CAACD,EAAEL,IAAI,KAAKI,EAAEL,MAAM,CAACO,aAAa,CAACD,EAAEN,MAAM;gBAEhF,MAAMQ,YAAYV,OAAOI,MAAM,CAAC,CAACH,IAAMA,EAAEE,IAAI,CAACE,UAAU,CAAC,UAAU7B,GAAG,CAAC,CAACyB,IAAMA,EAAEE,IAAI;gBACpF,MAAMQ,YAAYX,OAAOI,MAAM,CAAC,CAACH,IAAMA,EAAEE,IAAI,CAACE,UAAU,CAAC,UAAU7B,GAAG,CAAC,CAACyB,IAAM,GAAGA,EAAEC,MAAM,CAAC,CAAC,EAAED,EAAEE,IAAI,EAAE;gBACrG,MAAMS,aAAaZ,OAAOI,MAAM,CAAC,CAACH,IAAMA,EAAEE,IAAI,CAACE,UAAU,CAAC,SAAS7B,GAAG,CAAC,CAACyB,IAAM,GAAGA,EAAEC,MAAM,CAAC,CAAC,EAAED,EAAEE,IAAI,EAAE;gBACrG,MAAMU,iBAAiB;uBAAI,IAAIC,IAAIJ;iBAAW;gBAE9C,OAAOd,EAAEC,IAAI,CAAC;oBACbnB,MAAM;oBACNqC,SAAS;oBACTjC,SAASF,OAAOC,IAAI,CAACR,OAAOS,OAAO;oBACnCkC,aAAapE,mBAAmB4B,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;oBACjDuC,QAAQ5C,OAAO4C,MAAM,GAAG5C,OAAO4C,MAAM,CAACzC,GAAG,CAAC,CAAC0C,IAAMA,EAAExC,IAAI,IAAI,EAAE;oBAC7DyC,WAAW;wBACVC,KAAKP;wBACLQ,KAAK;+BAAI,IAAIP,IAAIH;yBAAW;wBAC5BW,MAAM;+BAAI,IAAIR,IAAIF;yBAAY;oBAC/B;gBACD;YACD;QACD;QAEA,4EAA4E;QAC5E,8DAA8D;QAC9D,4EAA4E;QAC5E/D,kBAAkB;YAAEiB;YAAKO;YAAQL;YAAYuD,eAAe9B,IAAI8B,aAAa;QAAC;IAC/E;IAEA,MAAMC,iBAAiB;QACtB,MAAMxB,SAASlC,IAAIkC,MAAM;QACzB,MAAMyB,WAAW,IAAIX;QACrB,MAAMY,WAAW,IAAIZ;QACrB,MAAMa,YAAY,IAAIb;QACtB,MAAMc,aAAa,IAAId;QAEvB,KAAK,MAAMe,SAAS7B,OAAQ;YAC3B,MAAMG,OAAO0B,MAAM1B,IAAI;YACvB,IAAIA,KAAKE,UAAU,CAAC,UAAU;gBAC7BoB,SAASK,GAAG,CAAC3B;YACd,OAAO,IAAIA,KAAKE,UAAU,CAAC,UAAU;gBACpCqB,SAASI,GAAG,CAAC,GAAGD,MAAM3B,MAAM,CAAC,CAAC,EAAEC,MAAM;YACvC,OAAO,IAAIA,KAAKE,UAAU,CAAC,SAAS;gBACnCsB,UAAUG,GAAG,CAAC,GAAGD,MAAM3B,MAAM,CAAC,CAAC,EAAEC,MAAM;YACxC,OAAO,IAAI0B,MAAM3B,MAAM,KAAK,OAAO;gBAClC0B,WAAWE,GAAG,CAAC,GAAGD,MAAM3B,MAAM,CAAC,CAAC,EAAEC,MAAM;YACzC;QACD;QAEA9C,IAAIkB,IAAI,CAAC;QACT,IAAIkD,SAASM,IAAI,GAAG,GAAG1E,IAAIkB,IAAI,CAAC,CAAC,OAAO,EAAE;eAAIkD;SAAS,CAACnB,IAAI,GAAG3B,IAAI,CAAC,OAAO;QAC3E,IAAIgD,UAAUI,IAAI,GAAG,GAAG1E,IAAIkB,IAAI,CAAC,CAAC,QAAQ,EAAE;eAAIoD;SAAU,CAACrB,IAAI,GAAG3B,IAAI,CAAC,OAAO;QAC9E,IAAI+C,SAASK,IAAI,GAAG,GAAG1E,IAAIkB,IAAI,CAAC,CAAC,OAAO,EAAE;eAAImD;SAAS,CAACpB,IAAI,GAAG3B,IAAI,CAAC,OAAO;QAC3E,IAAIiD,WAAWG,IAAI,GAAG,GAAG1E,IAAIkB,IAAI,CAAC,CAAC,SAAS,EAAE;eAAIqD;SAAW,CAACtB,IAAI,GAAG3B,IAAI,CAAC,OAAO;IAClF;IAEA,OAAO;QAAEb;QAAKO;QAAQN;QAASiB;QAAawC;QAAgB9B;IAAS;AACtE;AAEA,SAASsC,gBAAgBC,OAAgB;IACxC,MAAMC,SAAiC,CAAC;IACxCD,QAAQE,OAAO,CAAC,CAAChD,OAAOC;QACvB8C,MAAM,CAAC9C,IAAI,GAAGD;IACf;IACA,OAAO+C;AACR;AAEA;;;CAGC,GACD,SAAS9D,uBAAuBL,OAAoB;IACnD,OAAO,OAAO6B,GAAYwC;QACzB,MAAMC,YAAYC,KAAKC,GAAG;QAC1B,MAAMpC,OAAOP,EAAE4C,GAAG,CAACrC,IAAI;QAEvB,IAAIsC;QACJ,IAAIC;QAEJ,MAAMC,WAAWxC,KAAKyC,KAAK,CAAC;QAC5B,IAAID,UAAU;YACbF,aAAaE,QAAQ,CAAC,EAAE;YACxB,IAAI;gBAAC;gBAAe;gBAAO;gBAAc;aAAQ,CAACE,QAAQ,CAACJ,aAAa;gBACvEC,aAAaD;YACd,OAAO;gBACNC,aAAa;YACd;QACD;QAEA,IAAIvC,KAAKE,UAAU,CAAC,SAAS;YAC5BqC,aAAa;QACd;QAEA,IAAIlD;QAEJ,IAAI;YACH,MAAM4C;QACP,EAAE,OAAO7C,GAAG;YACXC,QAAQD,aAAauD,QAAQvD,EAAEQ,OAAO,GAAGgD,OAAOxD;YAChD,MAAMA;QACP,SAAU;YACT,MAAMyD,aAAaV,KAAKC,GAAG,KAAKF;YAEhCtE,QAAQkF,IAAI,CAAC9F,cAAc+F,OAAO,EAAE;gBACnCC,WAAW,IAAIb,OAAOc,WAAW;gBACjClD,QAAQN,EAAE4C,GAAG,CAACtC,MAAM;gBACpBC;gBACAsC;gBACAC;gBACA5C,QAAQF,EAAEyD,GAAG,CAACvD,MAAM;gBACpBkD;gBACAxD;gBACA8D,gBAAgBtB,gBAAgBpC,EAAE4C,GAAG,CAACe,GAAG,CAACtB,OAAO;YAClD;QACD;IACD;AACD"}