@wener/mcps 1.0.1 → 1.0.4
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.
Potentially problematic release.
This version of @wener/mcps might be problematic. Click here for more details.
- package/README.md +144 -0
- package/dist/index.mjs +213076 -1
- package/dist/mcps-cli.mjs +102547 -59344
- package/lib/chat/handler.js +2 -2
- package/lib/chat/handler.js.map +1 -1
- package/lib/cli-start.js +36 -0
- package/lib/cli-start.js.map +1 -0
- package/lib/cli.js +19 -0
- package/lib/cli.js.map +1 -0
- package/lib/dev.server.js +7 -1
- package/lib/dev.server.js.map +1 -1
- package/lib/index.js +21 -3
- package/lib/index.js.map +1 -1
- package/lib/mcps-cli.js +6 -35
- package/lib/mcps-cli.js.map +1 -1
- package/lib/providers/feishu/def.js +35 -0
- package/lib/providers/feishu/def.js.map +1 -0
- package/lib/providers/findMcpServerDef.js +1 -0
- package/lib/providers/findMcpServerDef.js.map +1 -1
- package/lib/scripts/bundle.js +7 -1
- package/lib/scripts/bundle.js.map +1 -1
- package/lib/server/api-routes.js +7 -8
- package/lib/server/api-routes.js.map +1 -1
- package/lib/server/audit-db.js +64 -0
- package/lib/server/audit-db.js.map +1 -0
- package/lib/server/{audit.js → audit-plugin.js} +72 -126
- package/lib/server/audit-plugin.js.map +1 -0
- package/lib/server/events.js +13 -0
- package/lib/server/events.js.map +1 -0
- package/lib/server/mcp-routes.js +31 -60
- package/lib/server/mcp-routes.js.map +1 -1
- package/lib/server/mcps-router.js +19 -24
- package/lib/server/mcps-router.js.map +1 -1
- package/lib/server/schema.js +22 -2
- package/lib/server/schema.js.map +1 -1
- package/lib/server/server.js +142 -87
- package/lib/server/server.js.map +1 -1
- package/package.json +35 -5
- package/src/chat/handler.ts +2 -2
- package/src/cli-start.ts +43 -0
- package/src/cli.ts +45 -0
- package/src/dev.server.ts +8 -1
- package/src/index.ts +47 -1
- package/src/mcps-cli.ts +6 -48
- package/src/providers/feishu/def.ts +37 -0
- package/src/providers/findMcpServerDef.ts +1 -0
- package/src/scripts/bundle.ts +12 -1
- package/src/server/api-routes.ts +11 -8
- package/src/server/audit-db.ts +65 -0
- package/src/server/{audit.ts → audit-plugin.ts} +69 -142
- package/src/server/events.ts +29 -0
- package/src/server/mcp-routes.ts +30 -58
- package/src/server/mcps-router.ts +21 -29
- package/src/server/schema.ts +23 -2
- package/src/server/server.ts +149 -81
- package/lib/server/audit.js.map +0 -1
- package/lib/server/db.js +0 -97
- package/lib/server/db.js.map +0 -1
- package/src/server/db.ts +0 -115
package/lib/server/server.js
CHANGED
|
@@ -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
|
-
//
|
|
21
|
-
app.use(
|
|
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
|
-
//
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
package/lib/server/server.js.map
CHANGED
|
@@ -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":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wener/mcps",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "",
|
|
5
6
|
"author": "",
|
|
6
7
|
"bin": {
|
|
@@ -18,6 +19,30 @@
|
|
|
18
19
|
"./server": {
|
|
19
20
|
"types": "./src/server/index.ts",
|
|
20
21
|
"default": "./lib/server/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./entities": {
|
|
24
|
+
"types": "./src/entities/index.ts",
|
|
25
|
+
"default": "./lib/entities/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./contracts": {
|
|
28
|
+
"types": "./src/contracts/index.ts",
|
|
29
|
+
"default": "./lib/contracts/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./chat": {
|
|
32
|
+
"types": "./src/chat/index.ts",
|
|
33
|
+
"default": "./lib/chat/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./providers": {
|
|
36
|
+
"types": "./src/providers/McpServerHandlerDef.ts",
|
|
37
|
+
"default": "./lib/providers/McpServerHandlerDef.js"
|
|
38
|
+
},
|
|
39
|
+
"./cli": {
|
|
40
|
+
"types": "./src/cli.ts",
|
|
41
|
+
"default": "./lib/cli.js"
|
|
42
|
+
},
|
|
43
|
+
"./audit": {
|
|
44
|
+
"types": "./src/server/audit-plugin.ts",
|
|
45
|
+
"default": "./lib/server/audit-plugin.js"
|
|
21
46
|
}
|
|
22
47
|
},
|
|
23
48
|
"files": [
|
|
@@ -27,17 +52,21 @@
|
|
|
27
52
|
"README.md"
|
|
28
53
|
],
|
|
29
54
|
"keywords": [],
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"emittery": "^1.2.0"
|
|
57
|
+
},
|
|
30
58
|
"devDependencies": {
|
|
31
59
|
"@ai-sdk/mcp": "^1.0.18",
|
|
32
60
|
"@ai-sdk/openai-compatible": "^2.0.26",
|
|
33
61
|
"@ai-sdk/react": "^3.0.71",
|
|
34
62
|
"@base-ui/react": "^1.1.0",
|
|
35
63
|
"@hono/mcp": "^0.2.3",
|
|
64
|
+
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
36
65
|
"@hono/node-server": "^1.19.9",
|
|
37
66
|
"@hono/vite-dev-server": "^0.24.1",
|
|
38
|
-
"@mikro-orm/core": "^7.0.0-rc.
|
|
39
|
-
"@mikro-orm/decorators": "^7.0.0-rc.
|
|
40
|
-
"@mikro-orm/
|
|
67
|
+
"@mikro-orm/core": "^7.0.0-rc.2",
|
|
68
|
+
"@mikro-orm/decorators": "^7.0.0-rc.2",
|
|
69
|
+
"@mikro-orm/sql": "^7.0.0-rc.2",
|
|
41
70
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
42
71
|
"@orpc/client": "^1.13.4",
|
|
43
72
|
"@orpc/contract": "^1.13.4",
|
|
@@ -71,7 +100,8 @@
|
|
|
71
100
|
"tedious": "^19.2.0",
|
|
72
101
|
"yaml": "^2.8.2",
|
|
73
102
|
"zod": "^4.3.6",
|
|
74
|
-
"@wener/ai": "0.1.
|
|
103
|
+
"@wener/ai": "0.1.2",
|
|
104
|
+
"@wener/server": "2.0.5"
|
|
75
105
|
},
|
|
76
106
|
"scripts": {
|
|
77
107
|
"build": "bun run ./src/scripts/bundle.ts",
|
package/src/chat/handler.ts
CHANGED
|
@@ -747,7 +747,7 @@ export function createChatHandler(options: ChatHandlerOptions = {}) {
|
|
|
747
747
|
let previousContext: { input: unknown; output: unknown[] } | null = null;
|
|
748
748
|
if (request.previous_response_id) {
|
|
749
749
|
try {
|
|
750
|
-
const { isDbInitialized, getEntityManager } = await import('../server/db');
|
|
750
|
+
const { isDbInitialized, getEntityManager } = await import('../server/audit-db');
|
|
751
751
|
const { ResponseEntity } = await import('../entities');
|
|
752
752
|
if (isDbInitialized()) {
|
|
753
753
|
const em = getEntityManager().fork();
|
|
@@ -840,7 +840,7 @@ export function createChatHandler(options: ChatHandlerOptions = {}) {
|
|
|
840
840
|
|
|
841
841
|
// Store response for future previous_response_id lookups
|
|
842
842
|
try {
|
|
843
|
-
const { isDbInitialized, getEntityManager } = await import('../server/db');
|
|
843
|
+
const { isDbInitialized, getEntityManager } = await import('../server/audit-db');
|
|
844
844
|
const { ResponseEntity } = await import('../entities');
|
|
845
845
|
if (isDbInitialized()) {
|
|
846
846
|
const em = getEntityManager().fork();
|
package/src/cli-start.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server';
|
|
2
|
+
import consola from 'consola';
|
|
3
|
+
import { createServer, type CreateServerOptions } from './server/server';
|
|
4
|
+
|
|
5
|
+
const log = consola.withTag('mcps');
|
|
6
|
+
|
|
7
|
+
let serverHandle: ReturnType<typeof serve> | undefined;
|
|
8
|
+
|
|
9
|
+
function shutdown() {
|
|
10
|
+
log.info('Shutting down...');
|
|
11
|
+
if (serverHandle) {
|
|
12
|
+
serverHandle.close(() => process.exit(0));
|
|
13
|
+
setTimeout(() => process.exit(1), 3000);
|
|
14
|
+
} else {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
process.on('SIGINT', shutdown);
|
|
20
|
+
process.on('SIGTERM', shutdown);
|
|
21
|
+
|
|
22
|
+
export async function startServer(options: { port: string; cwd: string; discoveryConfig: boolean; setup?: CreateServerOptions['setup'] }) {
|
|
23
|
+
const port = Number.parseInt(options.port, 10);
|
|
24
|
+
const { app, printEndpoints, finalize } = createServer({
|
|
25
|
+
cwd: options.cwd,
|
|
26
|
+
port,
|
|
27
|
+
discoveryConfig: options.discoveryConfig,
|
|
28
|
+
setup: options.setup,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await finalize();
|
|
32
|
+
|
|
33
|
+
log.info(`Starting MCPS server on port ${port}`);
|
|
34
|
+
|
|
35
|
+
serverHandle = serve({
|
|
36
|
+
fetch: app.fetch,
|
|
37
|
+
port,
|
|
38
|
+
hostname: '0.0.0.0',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
log.success(`MCPS server running at http://localhost:${port}`);
|
|
42
|
+
printEndpoints();
|
|
43
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { CreateServerOptions } from './server/server';
|
|
3
|
+
|
|
4
|
+
export interface CreateProgramOptions {
|
|
5
|
+
name?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
defaultPort?: string;
|
|
9
|
+
/** Called before the server starts, allows registering additional providers via side-effect imports */
|
|
10
|
+
beforeStart?: () => void | Promise<void>;
|
|
11
|
+
/** Server setup callback, passed to createServer. Use to register plugins like audit. */
|
|
12
|
+
setup?: CreateServerOptions['setup'];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a reusable Commander program for MCPS server.
|
|
17
|
+
* Consumers can register providers before calling `program.parseAsync()`.
|
|
18
|
+
*/
|
|
19
|
+
export function createProgram(options: CreateProgramOptions = {}): Command {
|
|
20
|
+
const {
|
|
21
|
+
name = 'mcps',
|
|
22
|
+
description = 'MCP Proxy Server - Unified MCP service with relay, SQL, CLS, and Prometheus support',
|
|
23
|
+
version = '0.1.0',
|
|
24
|
+
defaultPort = '8036',
|
|
25
|
+
beforeStart,
|
|
26
|
+
setup,
|
|
27
|
+
} = options;
|
|
28
|
+
|
|
29
|
+
const program = new Command();
|
|
30
|
+
|
|
31
|
+
program
|
|
32
|
+
.name(name)
|
|
33
|
+
.description(description)
|
|
34
|
+
.version(version)
|
|
35
|
+
.option('-p, --port <port>', 'Port to listen on', defaultPort)
|
|
36
|
+
.option('-c, --cwd <path>', 'Working directory for config files', process.cwd())
|
|
37
|
+
.option('--discovery-config', 'Enable server config discovery endpoints', false)
|
|
38
|
+
.action(async (opts) => {
|
|
39
|
+
await beforeStart?.();
|
|
40
|
+
const { startServer } = await import('./cli-start.js');
|
|
41
|
+
await startServer({ ...opts, setup });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return program;
|
|
45
|
+
}
|
package/src/dev.server.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { setupAudit } from '#/server/audit-plugin';
|
|
1
2
|
import { createServer } from '#/server/server';
|
|
2
3
|
|
|
3
|
-
const { app } = createServer({
|
|
4
|
+
const { app, finalize } = createServer({
|
|
5
|
+
setup: (ctx) => {
|
|
6
|
+
setupAudit(ctx);
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
await finalize();
|
|
4
11
|
|
|
5
12
|
export default {
|
|
6
13
|
fetch: app.fetch,
|
package/src/index.ts
CHANGED
|
@@ -1 +1,47 @@
|
|
|
1
|
-
|
|
1
|
+
// Core server
|
|
2
|
+
export { createServer, type CreateServerOptions, type McpsServerContext, type StatsProvider } from './server/server';
|
|
3
|
+
|
|
4
|
+
// Events (emittery-based decoupling)
|
|
5
|
+
export { McpsEventType, createMcpsEmitter, type McpsEmitter, type McpsEventData, type McpsRequestEvent } from './server/events';
|
|
6
|
+
|
|
7
|
+
// Configuration
|
|
8
|
+
export { loadConfig, substituteEnvVars, loadEnvFiles } from './server/config';
|
|
9
|
+
export {
|
|
10
|
+
type McpsConfig,
|
|
11
|
+
type ServerConfig,
|
|
12
|
+
type ModelConfig,
|
|
13
|
+
type AuditConfig,
|
|
14
|
+
type DbConfig,
|
|
15
|
+
McpsConfigSchema,
|
|
16
|
+
ServerConfigSchema,
|
|
17
|
+
ModelConfigSchema,
|
|
18
|
+
AuditConfigSchema,
|
|
19
|
+
DbConfigSchema,
|
|
20
|
+
HeaderNames,
|
|
21
|
+
} from './server/schema';
|
|
22
|
+
|
|
23
|
+
// MCP routes and handler
|
|
24
|
+
export { registerMcpRoutes } from './server/mcp-routes';
|
|
25
|
+
export { registerChatRoutes } from './server/chat-routes';
|
|
26
|
+
export { registerApiRoutes } from './server/api-routes';
|
|
27
|
+
export { createMcpLoggingHandler } from './server/mcp-handler';
|
|
28
|
+
export { createMcpsRouter } from './server/mcps-router';
|
|
29
|
+
|
|
30
|
+
// Providers
|
|
31
|
+
export {
|
|
32
|
+
type McpServerHandlerDef,
|
|
33
|
+
type McpServerHandlerDef as McpServerDef,
|
|
34
|
+
type HeaderMapping,
|
|
35
|
+
type DefineMcpServerHandlerOptions,
|
|
36
|
+
defineMcpServerHandler,
|
|
37
|
+
registerMcpServerHandler,
|
|
38
|
+
getAllMcpServerHandlerDefs,
|
|
39
|
+
getMcpServerHandlerDef,
|
|
40
|
+
} from './providers/McpServerHandlerDef';
|
|
41
|
+
export { findMcpServerDef, resolveMcpServerDef, getMcpServerDefCount } from './providers/findMcpServerDef';
|
|
42
|
+
|
|
43
|
+
// Contracts
|
|
44
|
+
export * from './contracts';
|
|
45
|
+
|
|
46
|
+
// Chat
|
|
47
|
+
export { createChatHandler, type ChatHandlerOptions } from './chat';
|