@nebutra/mcp 0.1.0 → 0.1.2

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.
@@ -0,0 +1,704 @@
1
+ // src/registry/serverRegistry.ts
2
+ var MCPServerRegistry = class {
3
+ servers = /* @__PURE__ */ new Map();
4
+ toolIndex = /* @__PURE__ */ new Map();
5
+ // tool name -> server id
6
+ /**
7
+ * Register an MCP server
8
+ */
9
+ register(config) {
10
+ this.servers.set(config.id, config);
11
+ for (const tool of config.tools) {
12
+ const qualifiedName = `${config.id}:${tool.name}`;
13
+ this.toolIndex.set(qualifiedName, config.id);
14
+ if (!this.toolIndex.has(tool.name)) {
15
+ this.toolIndex.set(tool.name, config.id);
16
+ }
17
+ }
18
+ }
19
+ /**
20
+ * Unregister an MCP server
21
+ */
22
+ unregister(serverId) {
23
+ const server = this.servers.get(serverId);
24
+ if (!server) return false;
25
+ for (const tool of server.tools) {
26
+ const qualifiedName = `${serverId}:${tool.name}`;
27
+ this.toolIndex.delete(qualifiedName);
28
+ if (this.toolIndex.get(tool.name) === serverId) {
29
+ this.toolIndex.delete(tool.name);
30
+ }
31
+ }
32
+ this.servers.delete(serverId);
33
+ return true;
34
+ }
35
+ /**
36
+ * Get server by ID
37
+ */
38
+ getServer(serverId) {
39
+ return this.servers.get(serverId);
40
+ }
41
+ /**
42
+ * Find server by tool name
43
+ */
44
+ findServerByTool(toolName) {
45
+ const serverId = this.toolIndex.get(toolName);
46
+ if (!serverId) return void 0;
47
+ return this.servers.get(serverId);
48
+ }
49
+ /**
50
+ * Get all registered servers
51
+ */
52
+ getAllServers() {
53
+ return Array.from(this.servers.values());
54
+ }
55
+ /**
56
+ * Get all available tools
57
+ */
58
+ getAllTools() {
59
+ const tools = [];
60
+ for (const server of this.servers.values()) {
61
+ for (const tool of server.tools) {
62
+ tools.push({ ...tool, serverId: server.id });
63
+ }
64
+ }
65
+ return tools;
66
+ }
67
+ /**
68
+ * Check if a context has access to a server
69
+ */
70
+ canAccess(serverId, context) {
71
+ const server = this.servers.get(serverId);
72
+ if (!server) return false;
73
+ if (server.allowedPlans && server.allowedPlans.length > 0) {
74
+ if (!context.plan || !server.allowedPlans.includes(context.plan)) {
75
+ return false;
76
+ }
77
+ }
78
+ if (server.allowedTenants && server.allowedTenants.length > 0) {
79
+ if (!context.tenantId || !server.allowedTenants.includes(context.tenantId)) {
80
+ return false;
81
+ }
82
+ }
83
+ return true;
84
+ }
85
+ /**
86
+ * Check whether a context can execute a specific tool. Tool-level policy
87
+ * narrows the server policy and supports tenant, plan, and permission gates.
88
+ */
89
+ canAccessTool(toolName, context) {
90
+ const server = this.findServerByTool(toolName);
91
+ if (!server || !this.canAccess(server.id, context)) return false;
92
+ const tool = this.findTool(server, toolName);
93
+ if (!tool) return false;
94
+ if (tool.allowedPlans && tool.allowedPlans.length > 0) {
95
+ if (!context.plan || !tool.allowedPlans.includes(context.plan)) return false;
96
+ }
97
+ if (tool.allowedTenants && tool.allowedTenants.length > 0) {
98
+ if (!context.tenantId || !tool.allowedTenants.includes(context.tenantId)) return false;
99
+ }
100
+ if (tool.requiredPermissions && tool.requiredPermissions.length > 0) {
101
+ const permissions = new Set(context.permissions ?? []);
102
+ if (!tool.requiredPermissions.every((permission) => permissions.has(permission))) {
103
+ return false;
104
+ }
105
+ }
106
+ return true;
107
+ }
108
+ /**
109
+ * Get tools accessible by a context
110
+ */
111
+ getAccessibleTools(context) {
112
+ const tools = [];
113
+ for (const server of this.servers.values()) {
114
+ if (this.canAccess(server.id, context)) {
115
+ for (const tool of server.tools) {
116
+ if (this.canAccessTool(`${server.id}:${tool.name}`, context)) {
117
+ tools.push({ ...tool, serverId: server.id });
118
+ }
119
+ }
120
+ }
121
+ }
122
+ return tools;
123
+ }
124
+ findTool(server, toolName) {
125
+ return server.tools.find(
126
+ (tool) => tool.name === toolName || `${server.id}:${tool.name}` === toolName
127
+ );
128
+ }
129
+ };
130
+ var serverRegistry = new MCPServerRegistry();
131
+
132
+ // src/client/mcpClient.ts
133
+ var MCPClient = class {
134
+ constructor(registry = serverRegistry) {
135
+ this.registry = registry;
136
+ }
137
+ requestCounter = 0;
138
+ /**
139
+ * Execute a tool by name
140
+ */
141
+ async executeTool(toolName, args, context) {
142
+ const startTime = Date.now();
143
+ const server = this.registry.findServerByTool(toolName);
144
+ if (!server) {
145
+ return {
146
+ success: false,
147
+ error: `Tool not found: ${toolName}`,
148
+ duration: Date.now() - startTime
149
+ };
150
+ }
151
+ if (!this.registry.canAccessTool(toolName, context)) {
152
+ return {
153
+ success: false,
154
+ error: `Access denied to tool: ${toolName}`,
155
+ duration: Date.now() - startTime
156
+ };
157
+ }
158
+ try {
159
+ const result = await this.executeOnServer(server, toolName, args, context);
160
+ return {
161
+ success: true,
162
+ result,
163
+ duration: Date.now() - startTime
164
+ };
165
+ } catch (error) {
166
+ return {
167
+ success: false,
168
+ error: error instanceof Error ? error.message : String(error),
169
+ duration: Date.now() - startTime
170
+ };
171
+ }
172
+ }
173
+ /**
174
+ * Execute tool on a specific server
175
+ */
176
+ async executeOnServer(server, toolName, args, context) {
177
+ const requestId = `${context.requestId}-${++this.requestCounter}`;
178
+ switch (server.transport) {
179
+ case "http":
180
+ return this.executeHttp(server, toolName, args, requestId);
181
+ case "websocket":
182
+ return this.executeWebSocket(server, toolName, args, requestId);
183
+ case "local":
184
+ return this.executeLocal(server, toolName, args, context);
185
+ case "stdio":
186
+ throw new Error("stdio transport not supported in browser/serverless");
187
+ default:
188
+ throw new Error(`Unknown transport: ${server.transport}`);
189
+ }
190
+ }
191
+ /**
192
+ * Execute via HTTP transport
193
+ */
194
+ async executeHttp(server, toolName, args, requestId) {
195
+ const headers = {
196
+ "Content-Type": "application/json",
197
+ "X-Request-ID": requestId
198
+ };
199
+ if (server.authentication?.type === "bearer") {
200
+ const token = process.env[`MCP_${server.id.toUpperCase()}_TOKEN`];
201
+ if (token) {
202
+ headers.Authorization = `Bearer ${token}`;
203
+ }
204
+ } else if (server.authentication?.type === "api-key") {
205
+ const apiKey = process.env[`MCP_${server.id.toUpperCase()}_KEY`];
206
+ const headerName = server.authentication.headerName || "X-API-Key";
207
+ if (apiKey) {
208
+ headers[headerName] = apiKey;
209
+ }
210
+ }
211
+ const response = await fetch(server.endpoint, {
212
+ method: "POST",
213
+ headers,
214
+ body: JSON.stringify({
215
+ jsonrpc: "2.0",
216
+ id: requestId,
217
+ method: "tools/call",
218
+ params: {
219
+ name: toolName,
220
+ arguments: args
221
+ }
222
+ })
223
+ });
224
+ if (!response.ok) {
225
+ throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
226
+ }
227
+ const data = await response.json();
228
+ if (data.error) {
229
+ throw new Error(data.error.message || "Unknown MCP error");
230
+ }
231
+ return data.result;
232
+ }
233
+ /**
234
+ * Execute via WebSocket transport
235
+ */
236
+ async executeWebSocket(_server, _toolName, _args, _requestId) {
237
+ throw new Error("WebSocket transport not yet implemented");
238
+ }
239
+ async executeLocal(server, toolName, args, context) {
240
+ const shortName = toolName.startsWith(`${server.id}:`) ? toolName.slice(server.id.length + 1) : toolName;
241
+ const handler = server.handlers?.[shortName] ?? server.handlers?.[toolName];
242
+ if (!handler) {
243
+ throw new Error(`No local handler registered for tool: ${toolName}`);
244
+ }
245
+ return handler(args, context);
246
+ }
247
+ /**
248
+ * List available tools for a context
249
+ */
250
+ listTools(context) {
251
+ return this.registry.getAccessibleTools(context);
252
+ }
253
+ /**
254
+ * Get tool definition
255
+ */
256
+ getTool(toolName, context) {
257
+ const server = this.registry.findServerByTool(toolName);
258
+ if (!server) return void 0;
259
+ if (!this.registry.canAccessTool(toolName, context)) {
260
+ return void 0;
261
+ }
262
+ return server.tools.find((t) => t.name === toolName || `${server.id}:${t.name}` === toolName);
263
+ }
264
+ };
265
+ var mcpClient = new MCPClient();
266
+
267
+ // src/consent.ts
268
+ function consentKey(grant) {
269
+ return [grant.tenantId, grant.userId ?? "*", grant.serverId, grant.toolName].join(":");
270
+ }
271
+ var InMemoryToolConsentStore = class {
272
+ #grants = /* @__PURE__ */ new Map();
273
+ async grant(grant) {
274
+ this.#grants.set(consentKey(grant), { ...grant, scopes: [...grant.scopes] });
275
+ }
276
+ async hasConsent(grant) {
277
+ const exact = this.#grants.get(consentKey(grant));
278
+ const wildcard = this.#grants.get(
279
+ consentKey({
280
+ tenantId: grant.tenantId,
281
+ serverId: grant.serverId,
282
+ toolName: grant.toolName
283
+ })
284
+ );
285
+ const existing = exact ?? wildcard;
286
+ if (!existing) return false;
287
+ const scopes = new Set(existing.scopes);
288
+ return grant.scopes.every((scope) => scopes.has(scope));
289
+ }
290
+ async list(tenantId) {
291
+ return Array.from(this.#grants.values()).filter(
292
+ (grant) => tenantId === void 0 || grant.tenantId === tenantId
293
+ );
294
+ }
295
+ };
296
+
297
+ // src/debug.ts
298
+ import {
299
+ appendCapabilityDebug,
300
+ capabilityDebugPath,
301
+ readCapabilityDebug
302
+ } from "@nebutra/capability-kit/debug";
303
+ function toolDebugPath() {
304
+ return capabilityDebugPath("tool-protocol");
305
+ }
306
+ async function appendToolDebug(entry) {
307
+ await appendCapabilityDebug("tool-protocol", entry);
308
+ }
309
+ async function readToolDebug(limit = 10) {
310
+ return readCapabilityDebug("tool-protocol", { limit });
311
+ }
312
+
313
+ // src/host.ts
314
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
315
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
316
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
317
+ import { CapabilityError } from "@nebutra/errors";
318
+ function shortToolName(serverId, toolName) {
319
+ return toolName.startsWith(`${serverId}:`) ? toolName.slice(serverId.length + 1) : toolName;
320
+ }
321
+ function requireManifest(serverId, manifest) {
322
+ if (!manifest || manifest.scopes.length === 0 || manifest.version.length === 0) {
323
+ throw new CapabilityError("tool-protocol", "MCP server manifest is required", {
324
+ suggestion: "Register every connected server with name, version, and declared scopes.",
325
+ metadata: { serverId },
326
+ statusCode: 400
327
+ });
328
+ }
329
+ return manifest;
330
+ }
331
+ function toJsonObject(value) {
332
+ return value && typeof value === "object" && !Array.isArray(value) ? value : { value };
333
+ }
334
+ var McpHost = class {
335
+ #registry;
336
+ #client;
337
+ #consent;
338
+ #traceStore;
339
+ #eventLog;
340
+ #sdkClients = /* @__PURE__ */ new Map();
341
+ constructor(options = {}) {
342
+ this.#registry = options.registry ?? new MCPServerRegistry();
343
+ this.#client = new MCPClient(this.#registry);
344
+ this.#consent = options.consent ?? new InMemoryToolConsentStore();
345
+ this.#traceStore = options.traceStore;
346
+ this.#eventLog = options.eventLog;
347
+ }
348
+ get registry() {
349
+ return this.#registry;
350
+ }
351
+ connectLocal(config) {
352
+ const manifest = requireManifest(config.id, config.manifest);
353
+ this.#registry.register({
354
+ id: config.id,
355
+ name: config.name,
356
+ description: config.description,
357
+ endpoint: `local://${config.id}`,
358
+ transport: "local",
359
+ manifest,
360
+ tools: [...config.tools],
361
+ handlers: config.handlers,
362
+ ...config.allowedPlans !== void 0 && { allowedPlans: [...config.allowedPlans] },
363
+ ...config.allowedTenants !== void 0 && { allowedTenants: [...config.allowedTenants] }
364
+ });
365
+ }
366
+ async connectStdio(command, args, options) {
367
+ const transport = new StdioClientTransport({ command, args: [...args] });
368
+ await this.#connectSdkClient(options, transport, `stdio://${command}`);
369
+ }
370
+ async connectStreamableHttp(endpoint, options) {
371
+ const transport = new StreamableHTTPClientTransport(
372
+ new URL(endpoint),
373
+ options.headers !== void 0 ? { requestInit: { headers: options.headers } } : {}
374
+ );
375
+ await this.#connectSdkClient(options, transport, endpoint);
376
+ }
377
+ async callTool(toolName, args, context) {
378
+ const start = Date.now();
379
+ const server = this.#registry.findServerByTool(toolName);
380
+ const serverId = server?.id;
381
+ const shortName = serverId ? shortToolName(serverId, toolName) : toolName;
382
+ const span = this.#traceStore?.start("tool", toolName, {
383
+ traceId: context.requestId,
384
+ tenantId: context.tenantId,
385
+ serverId
386
+ });
387
+ if (!context.tenantId) {
388
+ const result2 = {
389
+ success: false,
390
+ error: "Missing tenant context; suggestion: call tools with a tenantId.",
391
+ duration: Date.now() - start
392
+ };
393
+ await this.#recordCall(toolName, shortName, serverId, context, result2);
394
+ span?.fail(result2.error);
395
+ return result2;
396
+ }
397
+ if (!server || !server.manifest) {
398
+ const result2 = {
399
+ success: false,
400
+ error: "Tool server is not connected with a manifest; suggestion: run `pnpm tool:doctor`.",
401
+ duration: Date.now() - start
402
+ };
403
+ await this.#recordCall(toolName, shortName, serverId, context, result2);
404
+ span?.fail(result2.error);
405
+ return result2;
406
+ }
407
+ const consentRequest = {
408
+ tenantId: context.tenantId,
409
+ serverId: server.id,
410
+ toolName: shortName,
411
+ scopes: server.manifest.scopes,
412
+ ...context.userId !== void 0 && { userId: context.userId }
413
+ };
414
+ const hasConsent = await this.#consent.hasConsent(consentRequest);
415
+ if (!hasConsent) {
416
+ const result2 = {
417
+ success: false,
418
+ error: `Missing per-tool consent for ${server.id}:${shortName}; suggestion: grant scopes ${server.manifest.scopes.join(", ")}.`,
419
+ duration: Date.now() - start
420
+ };
421
+ await this.#recordCall(toolName, shortName, server.id, context, result2);
422
+ span?.fail(result2.error);
423
+ return result2;
424
+ }
425
+ const result = await this.#client.executeTool(toolName, args, context);
426
+ await this.#recordCall(toolName, shortName, server.id, context, result);
427
+ if (result.success) span?.end({ toolName });
428
+ else span?.fail(result.error);
429
+ return result;
430
+ }
431
+ listTools(context) {
432
+ return this.#client.listTools(context);
433
+ }
434
+ inspect(serverId) {
435
+ const server = this.#registry.getServer(serverId);
436
+ if (!server) {
437
+ throw new CapabilityError("tool-protocol", "MCP server not found", {
438
+ suggestion: "Run `pnpm tool:doctor` to list connected servers.",
439
+ metadata: { serverId },
440
+ statusCode: 404
441
+ });
442
+ }
443
+ void appendToolDebug({ type: "inspect", serverId, ok: true });
444
+ return server;
445
+ }
446
+ async doctor() {
447
+ const servers = this.#registry.getAllServers().map((server) => ({
448
+ id: server.id,
449
+ name: server.name,
450
+ transport: server.transport,
451
+ tools: server.tools.length,
452
+ ...server.manifest !== void 0 && { manifest: server.manifest }
453
+ }));
454
+ return {
455
+ ok: servers.length > 0,
456
+ servers,
457
+ ...servers.length === 0 && {
458
+ suggestion: "Connect an MCP server or register a local tool surface before running agents."
459
+ }
460
+ };
461
+ }
462
+ async debug(limit = 10) {
463
+ return readToolDebug(limit);
464
+ }
465
+ async close() {
466
+ await Promise.all(Array.from(this.#sdkClients.values()).map((client) => client.close()));
467
+ this.#sdkClients.clear();
468
+ }
469
+ async #connectSdkClient(options, transport, endpoint) {
470
+ const manifest = requireManifest(options.id, options.manifest);
471
+ const client = new Client({ name: "nebutra-tool-protocol", version: "0.1.0" });
472
+ await client.connect(transport);
473
+ const listed = await client.listTools();
474
+ const tools = listed.tools.map(
475
+ (tool) => ({
476
+ name: tool.name,
477
+ description: tool.description ?? tool.name,
478
+ parameters: {}
479
+ })
480
+ );
481
+ const handlers = Object.fromEntries(
482
+ tools.map((tool) => [
483
+ tool.name,
484
+ async (args) => {
485
+ return client.callTool({ name: tool.name, arguments: toJsonObject(args) });
486
+ }
487
+ ])
488
+ );
489
+ this.#sdkClients.set(options.id, client);
490
+ this.#registry.register({
491
+ id: options.id,
492
+ name: options.name ?? manifest.name,
493
+ description: options.description ?? `${manifest.name} MCP server`,
494
+ endpoint,
495
+ transport: endpoint.startsWith("stdio://") ? "stdio" : "http",
496
+ manifest,
497
+ tools,
498
+ handlers
499
+ });
500
+ await appendToolDebug({ type: "connect", serverId: options.id, ok: true });
501
+ }
502
+ async #recordCall(originalToolName, toolName, serverId, context, result) {
503
+ await appendToolDebug({
504
+ type: "tool_call",
505
+ requestId: context.requestId,
506
+ toolName,
507
+ ok: result.success,
508
+ durationMs: result.duration,
509
+ ...context.tenantId !== void 0 && { tenantId: context.tenantId },
510
+ ...serverId !== void 0 && { serverId },
511
+ ...result.error !== void 0 && { error: result.error }
512
+ });
513
+ if (result.success && context.tenantId && this.#eventLog) {
514
+ await this.#eventLog.commit({
515
+ traceId: context.requestId,
516
+ kind: "tool_call",
517
+ affected: [],
518
+ parent: null,
519
+ snapshot: {
520
+ [`tool/${serverId ?? "unknown"}/${toolName}.json`]: JSON.stringify({
521
+ toolName: originalToolName,
522
+ result: result.result
523
+ })
524
+ }
525
+ });
526
+ }
527
+ }
528
+ };
529
+ var toolConsentStore = new InMemoryToolConsentStore();
530
+ var mcpHost = new McpHost({ consent: toolConsentStore });
531
+
532
+ // src/middleware/mcpMiddleware.ts
533
+ function createRateLimitMiddleware(options) {
534
+ const requests = /* @__PURE__ */ new Map();
535
+ return async (ctx, next) => {
536
+ const key = `${ctx.tenantId || "anon"}:${ctx.userId || "anon"}`;
537
+ const now = Date.now();
538
+ let record = requests.get(key);
539
+ if (!record || now > record.resetAt) {
540
+ record = { count: 0, resetAt: now + options.windowMs };
541
+ requests.set(key, record);
542
+ }
543
+ record.count++;
544
+ if (record.count > options.maxRequests) {
545
+ return {
546
+ success: false,
547
+ error: `MCP rate limit exceeded. Max ${options.maxRequests} requests per ${options.windowMs / 1e3}s`,
548
+ duration: 0
549
+ };
550
+ }
551
+ return next();
552
+ };
553
+ }
554
+ function createAuditMiddleware(options) {
555
+ return async (ctx, next) => {
556
+ const result = await next();
557
+ const entry = {
558
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
559
+ requestId: ctx.requestId,
560
+ toolName: ctx.toolName,
561
+ arguments: ctx.arguments,
562
+ success: result.success,
563
+ duration: result.duration
564
+ };
565
+ if (ctx.tenantId) entry.tenantId = ctx.tenantId;
566
+ if (ctx.userId) entry.userId = ctx.userId;
567
+ if (result.error) entry.error = result.error;
568
+ Promise.resolve(options.onLog(entry)).catch(console.error);
569
+ return result;
570
+ };
571
+ }
572
+ function createAccessControlMiddleware(options) {
573
+ return async (ctx, next) => {
574
+ if (options.blockedTools?.includes(ctx.toolName)) {
575
+ return {
576
+ success: false,
577
+ error: `Tool ${ctx.toolName} is blocked`,
578
+ duration: 0
579
+ };
580
+ }
581
+ if (options.allowedTools && options.allowedTools.length > 0 && !options.allowedTools.includes(ctx.toolName)) {
582
+ return {
583
+ success: false,
584
+ error: `Tool ${ctx.toolName} is not in allowed list`,
585
+ duration: 0
586
+ };
587
+ }
588
+ return next();
589
+ };
590
+ }
591
+ function composeMCPMiddleware(middlewares) {
592
+ return async (ctx, finalNext) => {
593
+ let index = -1;
594
+ const dispatch = async (i) => {
595
+ if (i <= index) {
596
+ throw new Error("next() called multiple times");
597
+ }
598
+ index = i;
599
+ const middleware = middlewares[i];
600
+ if (!middleware) {
601
+ return finalNext();
602
+ }
603
+ return middleware(ctx, () => dispatch(i + 1));
604
+ };
605
+ return dispatch(0);
606
+ };
607
+ }
608
+
609
+ // src/server/internalServers.ts
610
+ var INTERNAL_SERVERS = [
611
+ // AI Service
612
+ {
613
+ id: "nebutra-ai",
614
+ name: "Nebutra AI Service",
615
+ description: "AI-powered text generation, embedding, and translation",
616
+ endpoint: process.env.AI_SERVICE_URL || "http://localhost:8001",
617
+ transport: "http",
618
+ tools: [
619
+ {
620
+ name: "generate_text",
621
+ description: "Generate text using LLM",
622
+ parameters: {
623
+ prompt: { type: "string", description: "Input prompt", required: true },
624
+ max_tokens: { type: "number", description: "Max tokens to generate", default: 1e3 },
625
+ temperature: { type: "number", description: "Sampling temperature", default: 0.7 }
626
+ },
627
+ returns: "Generated text"
628
+ },
629
+ {
630
+ name: "create_embedding",
631
+ description: "Create vector embedding for text",
632
+ parameters: {
633
+ text: { type: "string", description: "Text to embed", required: true },
634
+ model: {
635
+ type: "string",
636
+ description: "Embedding model",
637
+ default: "text-embedding-3-small"
638
+ }
639
+ },
640
+ returns: "Vector embedding array"
641
+ },
642
+ {
643
+ name: "translate",
644
+ description: "Translate text between languages",
645
+ parameters: {
646
+ text: { type: "string", description: "Text to translate", required: true },
647
+ source: { type: "string", description: "Source language code", default: "en" },
648
+ target: { type: "string", description: "Target language code", required: true }
649
+ },
650
+ returns: "Translated text"
651
+ }
652
+ ],
653
+ rateLimit: { maxRequests: 100, windowMs: 6e4 }
654
+ }
655
+ ];
656
+ function registerInternalServers() {
657
+ for (const server of INTERNAL_SERVERS) {
658
+ serverRegistry.register(server);
659
+ }
660
+ }
661
+ function getInternalServerIds() {
662
+ return INTERNAL_SERVERS.map((s) => s.id);
663
+ }
664
+
665
+ // src/types.ts
666
+ import { z } from "zod";
667
+ var ToolParameterSchema = z.object({
668
+ type: z.enum(["string", "number", "boolean", "object", "array"]),
669
+ description: z.string().optional(),
670
+ required: z.boolean().optional(),
671
+ default: z.any().optional()
672
+ });
673
+ var ToolDefinitionSchema = z.object({
674
+ name: z.string(),
675
+ description: z.string(),
676
+ parameters: z.record(z.string(), ToolParameterSchema).optional(),
677
+ returns: z.string().optional(),
678
+ allowedPlans: z.array(z.string()).optional(),
679
+ allowedTenants: z.array(z.string()).optional(),
680
+ requiredPermissions: z.array(z.string()).optional()
681
+ });
682
+
683
+ export {
684
+ MCPServerRegistry,
685
+ serverRegistry,
686
+ MCPClient,
687
+ mcpClient,
688
+ InMemoryToolConsentStore,
689
+ toolDebugPath,
690
+ appendToolDebug,
691
+ readToolDebug,
692
+ McpHost,
693
+ toolConsentStore,
694
+ mcpHost,
695
+ createRateLimitMiddleware,
696
+ createAuditMiddleware,
697
+ createAccessControlMiddleware,
698
+ composeMCPMiddleware,
699
+ INTERNAL_SERVERS,
700
+ registerInternalServers,
701
+ getInternalServerIds,
702
+ ToolParameterSchema,
703
+ ToolDefinitionSchema
704
+ };