@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.
package/src/host.ts ADDED
@@ -0,0 +1,314 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4
+ import { CapabilityError } from "@nebutra/errors";
5
+ import type { EventLog } from "@nebutra/event-log";
6
+ import type { TraceStore } from "@nebutra/trace-store";
7
+ import { MCPClient } from "./client/mcpClient";
8
+ import { InMemoryToolConsentStore, type ToolConsentStore } from "./consent";
9
+ import { appendToolDebug, readToolDebug } from "./debug";
10
+ import { MCPServerRegistry } from "./registry/serverRegistry";
11
+ import type {
12
+ MCPContext,
13
+ MCPServerConfig,
14
+ MCPServerManifest,
15
+ MCPToolHandler,
16
+ ToolDefinition,
17
+ ToolExecutionResult,
18
+ } from "./types";
19
+
20
+ export interface McpHostOptions {
21
+ readonly registry?: MCPServerRegistry;
22
+ readonly consent?: ToolConsentStore;
23
+ readonly traceStore?: TraceStore;
24
+ readonly eventLog?: EventLog;
25
+ }
26
+
27
+ export interface LocalMcpServerConfig {
28
+ readonly id: string;
29
+ readonly name: string;
30
+ readonly description: string;
31
+ readonly manifest?: MCPServerManifest;
32
+ readonly tools: readonly ToolDefinition[];
33
+ readonly handlers: Record<string, MCPToolHandler>;
34
+ readonly allowedPlans?: readonly string[];
35
+ readonly allowedTenants?: readonly string[];
36
+ }
37
+
38
+ export interface RemoteMcpConnectOptions {
39
+ readonly id: string;
40
+ readonly name?: string;
41
+ readonly description?: string;
42
+ readonly manifest: MCPServerManifest;
43
+ readonly headers?: Record<string, string>;
44
+ }
45
+
46
+ function shortToolName(serverId: string, toolName: string): string {
47
+ return toolName.startsWith(`${serverId}:`) ? toolName.slice(serverId.length + 1) : toolName;
48
+ }
49
+
50
+ function requireManifest(
51
+ serverId: string,
52
+ manifest: MCPServerManifest | undefined,
53
+ ): MCPServerManifest {
54
+ if (!manifest || manifest.scopes.length === 0 || manifest.version.length === 0) {
55
+ throw new CapabilityError("tool-protocol", "MCP server manifest is required", {
56
+ suggestion: "Register every connected server with name, version, and declared scopes.",
57
+ metadata: { serverId },
58
+ statusCode: 400,
59
+ });
60
+ }
61
+ return manifest;
62
+ }
63
+
64
+ function toJsonObject(value: unknown): Record<string, unknown> {
65
+ return value && typeof value === "object" && !Array.isArray(value)
66
+ ? (value as Record<string, unknown>)
67
+ : { value };
68
+ }
69
+
70
+ export class McpHost {
71
+ readonly #registry: MCPServerRegistry;
72
+ readonly #client: MCPClient;
73
+ readonly #consent: ToolConsentStore;
74
+ readonly #traceStore: TraceStore | undefined;
75
+ readonly #eventLog: EventLog | undefined;
76
+ readonly #sdkClients = new Map<string, Client>();
77
+
78
+ constructor(options: McpHostOptions = {}) {
79
+ this.#registry = options.registry ?? new MCPServerRegistry();
80
+ this.#client = new MCPClient(this.#registry);
81
+ this.#consent = options.consent ?? new InMemoryToolConsentStore();
82
+ this.#traceStore = options.traceStore;
83
+ this.#eventLog = options.eventLog;
84
+ }
85
+
86
+ get registry(): MCPServerRegistry {
87
+ return this.#registry;
88
+ }
89
+
90
+ connectLocal(config: LocalMcpServerConfig): void {
91
+ const manifest = requireManifest(config.id, config.manifest);
92
+ this.#registry.register({
93
+ id: config.id,
94
+ name: config.name,
95
+ description: config.description,
96
+ endpoint: `local://${config.id}`,
97
+ transport: "local",
98
+ manifest,
99
+ tools: [...config.tools],
100
+ handlers: config.handlers,
101
+ ...(config.allowedPlans !== undefined && { allowedPlans: [...config.allowedPlans] }),
102
+ ...(config.allowedTenants !== undefined && { allowedTenants: [...config.allowedTenants] }),
103
+ });
104
+ }
105
+
106
+ async connectStdio(
107
+ command: string,
108
+ args: readonly string[],
109
+ options: RemoteMcpConnectOptions,
110
+ ): Promise<void> {
111
+ const transport = new StdioClientTransport({ command, args: [...args] });
112
+ await this.#connectSdkClient(options, transport, `stdio://${command}`);
113
+ }
114
+
115
+ async connectStreamableHttp(endpoint: string, options: RemoteMcpConnectOptions): Promise<void> {
116
+ const transport = new StreamableHTTPClientTransport(
117
+ new URL(endpoint),
118
+ options.headers !== undefined ? { requestInit: { headers: options.headers } } : {},
119
+ );
120
+ await this.#connectSdkClient(options, transport, endpoint);
121
+ }
122
+
123
+ async callTool(
124
+ toolName: string,
125
+ args: Record<string, unknown>,
126
+ context: MCPContext,
127
+ ): Promise<ToolExecutionResult> {
128
+ const start = Date.now();
129
+ const server = this.#registry.findServerByTool(toolName);
130
+ const serverId = server?.id;
131
+ const shortName = serverId ? shortToolName(serverId, toolName) : toolName;
132
+ const span = this.#traceStore?.start("tool", toolName, {
133
+ traceId: context.requestId,
134
+ tenantId: context.tenantId,
135
+ serverId,
136
+ });
137
+
138
+ if (!context.tenantId) {
139
+ const result = {
140
+ success: false,
141
+ error: "Missing tenant context; suggestion: call tools with a tenantId.",
142
+ duration: Date.now() - start,
143
+ };
144
+ await this.#recordCall(toolName, shortName, serverId, context, result);
145
+ span?.fail(result.error);
146
+ return result;
147
+ }
148
+
149
+ if (!server || !server.manifest) {
150
+ const result = {
151
+ success: false,
152
+ error: "Tool server is not connected with a manifest; suggestion: run `pnpm tool:doctor`.",
153
+ duration: Date.now() - start,
154
+ };
155
+ await this.#recordCall(toolName, shortName, serverId, context, result);
156
+ span?.fail(result.error);
157
+ return result;
158
+ }
159
+
160
+ const consentRequest = {
161
+ tenantId: context.tenantId,
162
+ serverId: server.id,
163
+ toolName: shortName,
164
+ scopes: server.manifest.scopes,
165
+ ...(context.userId !== undefined && { userId: context.userId }),
166
+ };
167
+ const hasConsent = await this.#consent.hasConsent(consentRequest);
168
+
169
+ if (!hasConsent) {
170
+ const result = {
171
+ success: false,
172
+ error: `Missing per-tool consent for ${server.id}:${shortName}; suggestion: grant scopes ${server.manifest.scopes.join(", ")}.`,
173
+ duration: Date.now() - start,
174
+ };
175
+ await this.#recordCall(toolName, shortName, server.id, context, result);
176
+ span?.fail(result.error);
177
+ return result;
178
+ }
179
+
180
+ const result = await this.#client.executeTool(toolName, args, context);
181
+ await this.#recordCall(toolName, shortName, server.id, context, result);
182
+ if (result.success) span?.end({ toolName });
183
+ else span?.fail(result.error);
184
+ return result;
185
+ }
186
+
187
+ listTools(context: MCPContext): Array<ToolDefinition & { serverId: string }> {
188
+ return this.#client.listTools(context);
189
+ }
190
+
191
+ inspect(serverId: string): MCPServerConfig {
192
+ const server = this.#registry.getServer(serverId);
193
+ if (!server) {
194
+ throw new CapabilityError("tool-protocol", "MCP server not found", {
195
+ suggestion: "Run `pnpm tool:doctor` to list connected servers.",
196
+ metadata: { serverId },
197
+ statusCode: 404,
198
+ });
199
+ }
200
+ void appendToolDebug({ type: "inspect", serverId, ok: true });
201
+ return server;
202
+ }
203
+
204
+ async doctor(): Promise<{
205
+ ok: boolean;
206
+ servers: Array<{
207
+ id: string;
208
+ name: string;
209
+ transport: MCPServerConfig["transport"];
210
+ tools: number;
211
+ manifest?: MCPServerManifest;
212
+ }>;
213
+ suggestion?: string;
214
+ }> {
215
+ const servers = this.#registry.getAllServers().map((server) => ({
216
+ id: server.id,
217
+ name: server.name,
218
+ transport: server.transport,
219
+ tools: server.tools.length,
220
+ ...(server.manifest !== undefined && { manifest: server.manifest }),
221
+ }));
222
+ return {
223
+ ok: servers.length > 0,
224
+ servers,
225
+ ...(servers.length === 0 && {
226
+ suggestion: "Connect an MCP server or register a local tool surface before running agents.",
227
+ }),
228
+ };
229
+ }
230
+
231
+ async debug(limit = 10) {
232
+ return readToolDebug(limit);
233
+ }
234
+
235
+ async close(): Promise<void> {
236
+ await Promise.all(Array.from(this.#sdkClients.values()).map((client) => client.close()));
237
+ this.#sdkClients.clear();
238
+ }
239
+
240
+ async #connectSdkClient(
241
+ options: RemoteMcpConnectOptions,
242
+ transport: unknown,
243
+ endpoint: string,
244
+ ): Promise<void> {
245
+ const manifest = requireManifest(options.id, options.manifest);
246
+ const client = new Client({ name: "nebutra-tool-protocol", version: "0.1.0" });
247
+ await client.connect(transport as Parameters<Client["connect"]>[0]);
248
+ const listed = await client.listTools();
249
+ const tools = listed.tools.map(
250
+ (tool) =>
251
+ ({
252
+ name: tool.name,
253
+ description: tool.description ?? tool.name,
254
+ parameters: {},
255
+ }) satisfies ToolDefinition,
256
+ );
257
+ const handlers = Object.fromEntries(
258
+ tools.map((tool) => [
259
+ tool.name,
260
+ async (args: Record<string, unknown>) => {
261
+ return client.callTool({ name: tool.name, arguments: toJsonObject(args) });
262
+ },
263
+ ]),
264
+ );
265
+ this.#sdkClients.set(options.id, client);
266
+ this.#registry.register({
267
+ id: options.id,
268
+ name: options.name ?? manifest.name,
269
+ description: options.description ?? `${manifest.name} MCP server`,
270
+ endpoint,
271
+ transport: endpoint.startsWith("stdio://") ? "stdio" : "http",
272
+ manifest,
273
+ tools,
274
+ handlers,
275
+ });
276
+ await appendToolDebug({ type: "connect", serverId: options.id, ok: true });
277
+ }
278
+
279
+ async #recordCall(
280
+ originalToolName: string,
281
+ toolName: string,
282
+ serverId: string | undefined,
283
+ context: MCPContext,
284
+ result: ToolExecutionResult,
285
+ ): Promise<void> {
286
+ await appendToolDebug({
287
+ type: "tool_call",
288
+ requestId: context.requestId,
289
+ toolName,
290
+ ok: result.success,
291
+ durationMs: result.duration,
292
+ ...(context.tenantId !== undefined && { tenantId: context.tenantId }),
293
+ ...(serverId !== undefined && { serverId }),
294
+ ...(result.error !== undefined && { error: result.error }),
295
+ });
296
+ if (result.success && context.tenantId && this.#eventLog) {
297
+ await this.#eventLog.commit({
298
+ traceId: context.requestId,
299
+ kind: "tool_call",
300
+ affected: [],
301
+ parent: null,
302
+ snapshot: {
303
+ [`tool/${serverId ?? "unknown"}/${toolName}.json`]: JSON.stringify({
304
+ toolName: originalToolName,
305
+ result: result.result,
306
+ }),
307
+ },
308
+ });
309
+ }
310
+ }
311
+ }
312
+
313
+ export const toolConsentStore = new InMemoryToolConsentStore();
314
+ export const mcpHost = new McpHost({ consent: toolConsentStore });
package/src/index.ts CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  // Client
4
4
  export { MCPClient, mcpClient } from "./client/index";
5
+ export {
6
+ InMemoryToolConsentStore,
7
+ type ToolConsentGrant,
8
+ type ToolConsentStore,
9
+ } from "./consent";
10
+ export { appendToolDebug, readToolDebug, type ToolDebugEntry, toolDebugPath } from "./debug";
11
+ export { McpHost, mcpHost, toolConsentStore } from "./host";
5
12
  // Middleware
6
13
  export {
7
14
  type AuditLogEntry,
@@ -106,6 +106,35 @@ export class MCPServerRegistry {
106
106
  return true;
107
107
  }
108
108
 
109
+ /**
110
+ * Check whether a context can execute a specific tool. Tool-level policy
111
+ * narrows the server policy and supports tenant, plan, and permission gates.
112
+ */
113
+ canAccessTool(toolName: string, context: MCPContext): boolean {
114
+ const server = this.findServerByTool(toolName);
115
+ if (!server || !this.canAccess(server.id, context)) return false;
116
+
117
+ const tool = this.findTool(server, toolName);
118
+ if (!tool) return false;
119
+
120
+ if (tool.allowedPlans && tool.allowedPlans.length > 0) {
121
+ if (!context.plan || !tool.allowedPlans.includes(context.plan)) return false;
122
+ }
123
+
124
+ if (tool.allowedTenants && tool.allowedTenants.length > 0) {
125
+ if (!context.tenantId || !tool.allowedTenants.includes(context.tenantId)) return false;
126
+ }
127
+
128
+ if (tool.requiredPermissions && tool.requiredPermissions.length > 0) {
129
+ const permissions = new Set(context.permissions ?? []);
130
+ if (!tool.requiredPermissions.every((permission) => permissions.has(permission))) {
131
+ return false;
132
+ }
133
+ }
134
+
135
+ return true;
136
+ }
137
+
109
138
  /**
110
139
  * Get tools accessible by a context
111
140
  */
@@ -115,13 +144,21 @@ export class MCPServerRegistry {
115
144
  for (const server of this.servers.values()) {
116
145
  if (this.canAccess(server.id, context)) {
117
146
  for (const tool of server.tools) {
118
- tools.push({ ...tool, serverId: server.id });
147
+ if (this.canAccessTool(`${server.id}:${tool.name}`, context)) {
148
+ tools.push({ ...tool, serverId: server.id });
149
+ }
119
150
  }
120
151
  }
121
152
  }
122
153
 
123
154
  return tools;
124
155
  }
156
+
157
+ private findTool(server: MCPServerConfig, toolName: string): ToolDefinition | undefined {
158
+ return server.tools.find(
159
+ (tool) => tool.name === toolName || `${server.id}:${tool.name}` === toolName,
160
+ );
161
+ }
125
162
  }
126
163
 
127
164
  // Global registry instance
@@ -43,43 +43,74 @@ class NebutraContextServer {
43
43
  }
44
44
 
45
45
  private setupResourceHandlers() {
46
- this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
47
- resources: [
48
- {
49
- uri: "file:///nebutra.config.json",
50
- name: "Nebutra Configuration",
51
- mimeType: "application/json",
52
- description:
53
- "Global configurations for the Nebutra template including enabled features (auth, payments, i18n, db).",
54
- },
55
- {
56
- uri: "file:///prisma/schema.prisma",
57
- name: "Database Schema Overview",
58
- mimeType: "text/plain",
59
- description: "The core Prisma schema defining the database tables and relationships.",
60
- },
61
- ],
62
- }));
46
+ const handlers = createContextServerHandlers(this.projectRoot);
47
+
48
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => handlers.listResources());
63
49
 
64
50
  this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
65
- const { uri } = request.params;
66
-
67
- let filePath = "";
68
- if (uri === "file:///nebutra.config.json") {
69
- filePath = path.join(this.projectRoot, "nebutra.config.json");
70
- } else if (uri === "file:///prisma/schema.prisma") {
71
- filePath = path.join(this.projectRoot, "packages/platform/db/prisma/schema.prisma");
72
- } else {
51
+ return handlers.readResource(request.params);
52
+ });
53
+ }
54
+
55
+ private setupToolHandlers() {
56
+ const handlers = createContextServerHandlers(this.projectRoot);
57
+
58
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => handlers.listTools());
59
+
60
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
61
+ return handlers.callTool(request.params);
62
+ });
63
+ }
64
+
65
+ async run() {
66
+ const transport = new StdioServerTransport();
67
+ await this.server.connect(transport);
68
+ }
69
+ }
70
+
71
+ const RESOURCE_MAP = {
72
+ "file:///nebutra.config.json": {
73
+ relativePath: "nebutra.config.json",
74
+ name: "Nebutra Configuration",
75
+ mimeType: "application/json",
76
+ description:
77
+ "Global configurations for the Nebutra template including enabled features (auth, payments, i18n, db).",
78
+ },
79
+ "file:///prisma/schema.prisma": {
80
+ relativePath: "packages/platform/db/prisma/schema.prisma",
81
+ name: "Database Schema Overview",
82
+ mimeType: "text/plain",
83
+ description: "The core Prisma schema defining the database tables and relationships.",
84
+ },
85
+ } as const;
86
+
87
+ export function createContextServerHandlers(projectRoot: string) {
88
+ return {
89
+ async listResources() {
90
+ return {
91
+ resources: Object.entries(RESOURCE_MAP).map(([uri, resource]) => ({
92
+ uri,
93
+ name: resource.name,
94
+ mimeType: resource.mimeType,
95
+ description: resource.description,
96
+ })),
97
+ };
98
+ },
99
+
100
+ async readResource({ uri }: { uri: string }) {
101
+ const resource = RESOURCE_MAP[uri as keyof typeof RESOURCE_MAP];
102
+ if (!resource) {
73
103
  throw new Error(`Resource not found: ${uri}`);
74
104
  }
75
105
 
106
+ const filePath = path.join(projectRoot, resource.relativePath);
76
107
  try {
77
108
  const content = fs.readFileSync(filePath, "utf-8");
78
109
  return {
79
110
  contents: [
80
111
  {
81
112
  uri,
82
- mimeType: uri.endsWith(".json") ? "application/json" : "text/plain",
113
+ mimeType: resource.mimeType,
83
114
  text: content,
84
115
  },
85
116
  ],
@@ -87,43 +118,85 @@ class NebutraContextServer {
87
118
  } catch {
88
119
  throw new Error(`Failed to read the requested resource file at ${filePath}`);
89
120
  }
90
- });
91
- }
121
+ },
92
122
 
93
- private setupToolHandlers() {
94
- this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
95
- tools: [
96
- {
97
- name: "get_project_structure",
98
- description: "Retrieves the core Next.js routing tree from the apps folder.",
99
- inputSchema: {
100
- type: "object",
101
- properties: {},
102
- required: [],
123
+ async listTools() {
124
+ return {
125
+ tools: [
126
+ {
127
+ name: "get_project_structure",
128
+ description: "Returns a bounded app/package routing tree for the current project.",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {},
132
+ required: [],
133
+ },
103
134
  },
104
- },
105
- ],
106
- }));
135
+ ],
136
+ };
137
+ },
107
138
 
108
- this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
109
- if (request.params.name === "get_project_structure") {
110
- try {
111
- const appsDir = path.join(this.projectRoot, "apps");
112
- if (!fs.existsSync(appsDir))
113
- return { content: [{ type: "text", text: "Apps dir not found." }] };
114
- const dirs = fs.readdirSync(appsDir);
115
- return { content: [{ type: "text", text: `Apps: ${dirs.join(", ")}` }] };
116
- } catch (error) {
117
- return { content: [{ type: "text", text: `Error: ${error}` }], isError: true };
118
- }
139
+ async callTool({ name }: { name: string; arguments?: Record<string, unknown> | undefined }) {
140
+ if (name !== "get_project_structure") {
141
+ throw new Error(`Unknown tool: ${name}`);
119
142
  }
120
- throw new Error(`Unknown tool: ${request.params.name}`);
121
- });
143
+
144
+ try {
145
+ return {
146
+ content: [
147
+ {
148
+ type: "text",
149
+ text: getProjectStructure(projectRoot),
150
+ },
151
+ ],
152
+ };
153
+ } catch (error) {
154
+ return {
155
+ content: [
156
+ {
157
+ type: "text",
158
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
159
+ },
160
+ ],
161
+ isError: true,
162
+ };
163
+ }
164
+ },
165
+ };
166
+ }
167
+
168
+ function getProjectStructure(projectRoot: string): string {
169
+ const roots = ["apps", "packages", "backends"].filter((segment) =>
170
+ fs.existsSync(path.join(projectRoot, segment)),
171
+ );
172
+ if (roots.length === 0) return "No Nebutra workspace roots found.";
173
+
174
+ const entries: string[] = [];
175
+ for (const root of roots) {
176
+ walkProjectTree(path.join(projectRoot, root), root, entries, 4);
122
177
  }
178
+ return entries.length > 0 ? entries.join("\n") : "No project entries found.";
179
+ }
123
180
 
124
- async run() {
125
- const transport = new StdioServerTransport();
126
- await this.server.connect(transport);
181
+ function walkProjectTree(
182
+ absoluteDir: string,
183
+ relativeDir: string,
184
+ entries: string[],
185
+ depthRemaining: number,
186
+ ): void {
187
+ if (depthRemaining < 0 || entries.length >= 200) return;
188
+
189
+ entries.push(relativeDir);
190
+
191
+ for (const dirent of fs.readdirSync(absoluteDir, { withFileTypes: true })) {
192
+ if (!dirent.isDirectory()) continue;
193
+ if (dirent.name === "node_modules" || dirent.name.startsWith(".")) continue;
194
+ walkProjectTree(
195
+ path.join(absoluteDir, dirent.name),
196
+ path.join(relativeDir, dirent.name),
197
+ entries,
198
+ depthRemaining - 1,
199
+ );
127
200
  }
128
201
  }
129
202
 
package/src/types.ts CHANGED
@@ -16,6 +16,9 @@ export const ToolDefinitionSchema = z.object({
16
16
  description: z.string(),
17
17
  parameters: z.record(z.string(), ToolParameterSchema).optional(),
18
18
  returns: z.string().optional(),
19
+ allowedPlans: z.array(z.string()).optional(),
20
+ allowedTenants: z.array(z.string()).optional(),
21
+ requiredPermissions: z.array(z.string()).optional(),
19
22
  });
20
23
 
21
24
  export type ToolParameter = z.infer<typeof ToolParameterSchema>;
@@ -74,6 +77,13 @@ export interface ToolExecutionResult {
74
77
  tokensUsed?: number;
75
78
  }
76
79
 
80
+ export interface MCPServerManifest {
81
+ name: string;
82
+ version: string;
83
+ scopes: string[];
84
+ homepage?: string;
85
+ }
86
+
77
87
  // ============================================
78
88
  // MCP Server Registration
79
89
  // ============================================
@@ -83,8 +93,10 @@ export interface MCPServerConfig {
83
93
  name: string;
84
94
  description: string;
85
95
  endpoint: string;
86
- transport: "http" | "websocket" | "stdio";
96
+ transport: "http" | "websocket" | "stdio" | "local";
97
+ manifest?: MCPServerManifest;
87
98
  tools: ToolDefinition[];
99
+ handlers?: Record<string, MCPToolHandler>;
88
100
  authentication?: {
89
101
  type: "bearer" | "api-key" | "none";
90
102
  headerName?: string;
@@ -98,6 +110,11 @@ export interface MCPServerConfig {
98
110
  allowedTenants?: string[]; // specific tenant IDs
99
111
  }
100
112
 
113
+ export type MCPToolHandler = (
114
+ args: Record<string, unknown>,
115
+ context: MCPContext,
116
+ ) => unknown | Promise<unknown>;
117
+
101
118
  // ============================================
102
119
  // Smithery Integration
103
120
  // ============================================
package/tsup.config.ts CHANGED
@@ -1,11 +1,17 @@
1
1
  import { defineConfig } from "tsup";
2
2
 
3
3
  export default defineConfig({
4
- entry: ["src/index.ts", "src/server/contextServer.ts"],
4
+ entry: ["src/index.ts", "src/cli.ts", "src/server/contextServer.ts"],
5
5
  format: ["esm"],
6
6
  dts: true,
7
7
  clean: true,
8
8
  target: "node18",
9
9
  outDir: "dist",
10
10
  minify: false,
11
+ external: [
12
+ "@modelcontextprotocol/sdk",
13
+ "@nebutra/errors",
14
+ "@nebutra/event-log",
15
+ "@nebutra/trace-store",
16
+ ],
11
17
  });