@tigerdata/mcp-boilerplate 0.9.0 → 0.9.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/README.md CHANGED
@@ -14,7 +14,7 @@ This provides some common code for creating a [Model Context Protocol](https://m
14
14
  2. Install dependencies:
15
15
 
16
16
  ```bash
17
- npm install
17
+ ./bun install
18
18
  ```
19
19
 
20
20
  ## Eslint Plugin
@@ -45,7 +45,7 @@ export default [
45
45
  To build the TypeScript project:
46
46
 
47
47
  ```bash
48
- npm run build
48
+ ./bun run build
49
49
  ```
50
50
 
51
51
  This compiles the TypeScript files from `src/` to JavaScript in `dist/`.
@@ -55,7 +55,7 @@ This compiles the TypeScript files from `src/` to JavaScript in `dist/`.
55
55
  To run TypeScript compilation in watch mode (rebuilds on file changes):
56
56
 
57
57
  ```bash
58
- npm run watch
58
+ ./bun run watch
59
59
  ```
60
60
 
61
61
  ### Linting
@@ -65,13 +65,13 @@ This project uses ESLint for code linting with TypeScript support.
65
65
  To run the linter:
66
66
 
67
67
  ```bash
68
- npm run lint
68
+ ./bun run lint
69
69
  ```
70
70
 
71
71
  To automatically fix linting issues where possible:
72
72
 
73
73
  ```bash
74
- npm run lint:fix
74
+ ./bun run lint --write
75
75
  ```
76
76
 
77
77
  ### Continuous Integration
@@ -1,4 +1,4 @@
1
- import { join } from 'path';
1
+ import { join } from 'node:path';
2
2
  export async function cliEntrypoint(stdioEntrypoint, httpEntrypoint, instrumentation = join(import.meta.dir, './instrumentation.js')) {
3
3
  // Parse command line arguments first
4
4
  const args = process.argv.slice(2);
@@ -34,11 +34,15 @@ const noOptionalInputSchema = {
34
34
  VariableDeclarator(node) {
35
35
  const varNode = node;
36
36
  // Check if this variable has a TypeScript type annotation
37
- if (varNode.id?.typeAnnotation?.typeAnnotation) {
38
- const typeAnn = varNode.id.typeAnnotation.typeAnnotation;
37
+ if (varNode.id &&
38
+ varNode.id.typeAnnotation?.typeAnnotation) {
39
+ const typeAnn = varNode.id.typeAnnotation
40
+ ?.typeAnnotation;
39
41
  // Look for ApiFactory type reference
40
- if (typeAnn.type === 'TSTypeReference' &&
41
- typeAnn.typeName?.name === 'ApiFactory') {
42
+ if (typeAnn?.type === 'TSTypeReference' &&
43
+ typeAnn.typeName &&
44
+ 'name' in typeAnn.typeName &&
45
+ typeAnn.typeName.name === 'ApiFactory') {
42
46
  // Get the type parameters
43
47
  const typeParams = typeAnn.typeArguments?.params;
44
48
  if (typeParams && typeParams.length >= 2) {
@@ -100,7 +104,6 @@ function isInsideApiFactoryInputSchema(node, context, apiFactoryInputSchemas) {
100
104
  }
101
105
  }
102
106
  }
103
- // Fallback: walk up parent chain if node.parent is available
104
107
  let current = node.parent;
105
108
  while (current) {
106
109
  // Variable that's an ApiFactory input schema
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * REST API alternative to MCP for direct use of the same tools.
3
3
  */
4
- import { ApiFactory, RouterFactoryResult } from '../types.js';
5
- export declare const apiRouterFactory: <Context extends Record<string, unknown>>(context: Context, apiFactories: readonly ApiFactory<Context, any, any>[]) => RouterFactoryResult;
4
+ import type { BaseApiFactory, RouterFactoryResult } from '../types.js';
5
+ export declare const apiRouterFactory: <Context extends Record<string, unknown>>(context: Context, apiFactories: readonly BaseApiFactory<Context>[]) => RouterFactoryResult;
package/dist/http/api.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * REST API alternative to MCP for direct use of the same tools.
3
3
  */
4
- import { Router } from 'express';
5
4
  import bodyParser from 'body-parser';
5
+ import { Router } from 'express';
6
6
  import { z } from 'zod';
7
7
  export const apiRouterFactory = (context, apiFactories) => {
8
8
  const router = Router();
@@ -1,8 +1,9 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { RouterFactoryResult, McpFeatureFlags } from '../types.js';
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { McpFeatureFlags, RouterFactoryResult } from '../types.js';
3
3
  export declare const mcpRouterFactory: <Context extends Record<string, unknown>>(context: Context, createServer: (context: Context, featureFlags: McpFeatureFlags) => {
4
4
  server: McpServer;
5
- }, { name, stateful, }?: {
5
+ }, { name, stateful, inspector, }?: {
6
6
  name?: string;
7
7
  stateful?: boolean;
8
+ inspector?: boolean;
8
9
  }) => RouterFactoryResult;
package/dist/http/mcp.js CHANGED
@@ -1,14 +1,14 @@
1
- import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
1
+ import { randomUUID } from 'node:crypto';
2
2
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
- import { trace, context as otelContext, propagation, SpanKind, SpanStatusCode, } from '@opentelemetry/api';
3
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
4
+ import { context as otelContext, propagation, SpanKind, SpanStatusCode, trace, } from '@opentelemetry/api';
4
5
  import { ATTR_HTTP_RESPONSE_STATUS_CODE } from '@opentelemetry/semantic-conventions';
5
6
  import { Router } from 'express';
6
- import { randomUUID } from 'node:crypto';
7
7
  import getRawBody from 'raw-body';
8
8
  import { log } from '../logger.js';
9
9
  const name = process.env.OTEL_SERVICE_NAME;
10
10
  const tracer = trace.getTracer(name ? `${name}.router.mcp` : 'router.mcp');
11
- export const mcpRouterFactory = (context, createServer, { name, stateful = true, } = {}) => {
11
+ export const mcpRouterFactory = (context, createServer, { name, stateful = true, inspector = false, } = {}) => {
12
12
  const router = Router();
13
13
  const transports = new Map();
14
14
  const sessionFeatureFlags = new Map();
@@ -49,7 +49,8 @@ export const mcpRouterFactory = (context, createServer, { name, stateful = true,
49
49
  let transport;
50
50
  let body = req.body;
51
51
  if (sessionId) {
52
- if (!transports.has(sessionId)) {
52
+ const t = transports.get(sessionId);
53
+ if (!t) {
53
54
  res.status(404).json({
54
55
  jsonrpc: '2.0',
55
56
  error: {
@@ -60,7 +61,7 @@ export const mcpRouterFactory = (context, createServer, { name, stateful = true,
60
61
  });
61
62
  return;
62
63
  }
63
- transport = transports.get(sessionId);
64
+ transport = t;
64
65
  }
65
66
  else {
66
67
  if (!body) {
@@ -177,7 +178,7 @@ export const mcpRouterFactory = (context, createServer, { name, stateful = true,
177
178
  });
178
179
  return;
179
180
  }
180
- await transports.get(sessionId).handleRequest(req, res);
181
+ await transports.get(sessionId)?.handleRequest(req, res);
181
182
  };
182
183
  // Handle GET requests for server-to-client notifications via SSE
183
184
  router.get('/', (req, res) => {
@@ -197,7 +198,11 @@ export const mcpRouterFactory = (context, createServer, { name, stateful = true,
197
198
  <h1>${name}</h1>
198
199
  <h2>Model Context Protocol (MCP) Server</h2>
199
200
  <p>This endpoint is used for MCP communication. Please use an MCP-compatible client to interact with this server.</p>
200
-
201
+ ${inspector
202
+ ? `
203
+ <h3>Inspector</h3>
204
+ <p>You can use the <a href="/inspector">MCP Inspector</a> for testing purposes.</p>`
205
+ : ''}
201
206
  <h3>Claude Code</h3>
202
207
  <p>To connect to this MCP server using Claude Code, run the following command in your terminal:</p>
203
208
  <pre><code>claude mcp add --transport http ${name || req.get('host')} ${fullUrl}</code></pre>
@@ -214,7 +219,7 @@ export const mcpRouterFactory = (context, createServer, { name, stateful = true,
214
219
  for (const sessionId in transports) {
215
220
  try {
216
221
  log.info(`Closing transport for session ${sessionId}`);
217
- await transports.get(sessionId).close();
222
+ await transports.get(sessionId)?.close();
218
223
  transports.delete(sessionId);
219
224
  }
220
225
  catch (error) {
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import type { Server } from 'node:http';
2
3
  import express from 'express';
3
- import { ApiFactory, PromptFactory, ResourceFactory } from './types.js';
4
- import { AdditionalSetupArgs } from './mcpServer.js';
5
- import { Server } from 'node:http';
4
+ import { type AdditionalSetupArgs } from './mcpServer.js';
5
+ import type { BaseApiFactory, BasePromptFactory, ResourceFactory } from './types.js';
6
6
  export declare const httpServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, stateful, instructions, }: {
7
7
  name: string;
8
8
  version?: string;
9
9
  context: Context;
10
- apiFactories?: readonly ApiFactory<Context, any, any>[];
11
- promptFactories?: readonly PromptFactory<Context, any>[];
10
+ apiFactories?: readonly BaseApiFactory<Context>[];
11
+ promptFactories?: readonly BasePromptFactory<Context>[];
12
12
  resourceFactories?: readonly ResourceFactory<Context>[];
13
13
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
14
14
  cleanupFn?: () => void | Promise<void>;
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import bodyParser from 'body-parser';
2
3
  import express from 'express';
3
- import { mcpRouterFactory } from './http/mcp.js';
4
4
  import { apiRouterFactory } from './http/api.js';
5
- import { registerExitHandlers } from './registerExitHandlers.js';
6
- import { mcpServerFactory } from './mcpServer.js';
5
+ import { mcpRouterFactory } from './http/mcp.js';
7
6
  import { log } from './logger.js';
7
+ import { mcpServerFactory } from './mcpServer.js';
8
+ import { registerExitHandlers } from './registerExitHandlers.js';
8
9
  import { StatusError } from './StatusError.js';
9
10
  export const httpServerFactory = ({ name, version, context, apiFactories = [], promptFactories, resourceFactories, additionalSetup, cleanupFn, stateful = true, instructions, }) => {
10
11
  const cleanupFns = cleanupFn
@@ -14,6 +15,9 @@ export const httpServerFactory = ({ name, version, context, apiFactories = [], p
14
15
  log.info('Starting HTTP server...');
15
16
  const app = express();
16
17
  app.enable('trust proxy');
18
+ const PORT = process.env.PORT || 3001;
19
+ const inspector = process.env.NODE_ENV !== 'production' ||
20
+ ['1', 'true'].includes(process.env.ENABLE_INSPECTOR ?? '0');
17
21
  const [mcpRouter, mcpCleanup] = mcpRouterFactory(context, (context, featureFlags) => mcpServerFactory({
18
22
  name,
19
23
  version,
@@ -24,14 +28,14 @@ export const httpServerFactory = ({ name, version, context, apiFactories = [], p
24
28
  additionalSetup,
25
29
  featureFlags,
26
30
  instructions,
27
- }), { name, stateful });
31
+ }), { name, stateful, inspector });
28
32
  cleanupFns.push(mcpCleanup);
29
33
  app.use('/mcp', mcpRouter);
30
34
  const [apiRouter, apiCleanup] = apiRouterFactory(context, apiFactories);
31
35
  cleanupFns.push(apiCleanup);
32
36
  app.use('/api', apiRouter);
33
37
  // Error handler
34
- app.use(function (err, req, res, _next) {
38
+ app.use((err, _req, res, _next) => {
35
39
  if (err instanceof StatusError && err.status < 500) {
36
40
  log.info('HTTP error response', {
37
41
  message: err.message,
@@ -45,15 +49,26 @@ export const httpServerFactory = ({ name, version, context, apiFactories = [], p
45
49
  .status(err instanceof StatusError ? err.status : 500)
46
50
  .json({ error: err.message });
47
51
  });
52
+ if (inspector) {
53
+ process.env.MCP_USE_ANONYMIZED_TELEMETRY = 'false';
54
+ import('@mcp-use/inspector')
55
+ .then(({ mountInspector }) => {
56
+ app.use(bodyParser.json());
57
+ mountInspector(app, { autoConnectUrl: `http://localhost:${PORT}/mcp` });
58
+ })
59
+ .catch(log.error);
60
+ }
48
61
  // Start the server
49
- const PORT = process.env.PORT || 3001;
50
- const server = app.listen(PORT, (error) => {
62
+ const server = app.listen(PORT, async (error) => {
51
63
  if (error) {
52
64
  log.error('Error starting HTTP server:', error);
53
65
  exitHandler(1);
54
66
  }
55
67
  else {
56
68
  log.info(`HTTP Server listening on port ${PORT}`);
69
+ if (inspector) {
70
+ log.info(`🌐 MCP inspector running at http://localhost:${PORT}/inspector`);
71
+ }
57
72
  }
58
73
  });
59
74
  cleanupFns.push(async () => {
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  export { cliEntrypoint } from './cliEntrypoint.js';
2
2
  export { httpServerFactory } from './httpServer.js';
3
3
  export { log } from './logger.js';
4
- export { stdioServerFactory } from './stdio.js';
5
- export type { ApiFactory, McpFeatureFlags, PromptFactory, ResourceFactory, ParsedQs, } from './types.js';
6
- export { StatusError } from './StatusError.js';
7
4
  export type { AdditionalSetupArgs } from './mcpServer.js';
8
- export { withSpan, addAiResultToSpan } from './tracing.js';
9
5
  export { registerExitHandlers } from './registerExitHandlers.js';
10
- export type { InferSchema } from './types.js';
6
+ export { StatusError } from './StatusError.js';
7
+ export { stdioServerFactory } from './stdio.js';
8
+ export { addAiResultToSpan, withSpan } from './tracing.js';
9
+ export type { ApiFactory, InferSchema, McpFeatureFlags, ParsedQs, PromptFactory, ResourceFactory, } from './types.js';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { cliEntrypoint } from './cliEntrypoint.js';
2
2
  export { httpServerFactory } from './httpServer.js';
3
3
  export { log } from './logger.js';
4
- export { stdioServerFactory } from './stdio.js';
5
- export { StatusError } from './StatusError.js';
6
- export { withSpan, addAiResultToSpan } from './tracing.js';
7
4
  export { registerExitHandlers } from './registerExitHandlers.js';
5
+ export { StatusError } from './StatusError.js';
6
+ export { stdioServerFactory } from './stdio.js';
7
+ export { addAiResultToSpan, withSpan } from './tracing.js';
@@ -1,6 +1,6 @@
1
- import { NodeSDK } from '@opentelemetry/sdk-node';
2
- import { InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
1
+ import { type InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
3
2
  import type { Instrumentation } from '@opentelemetry/instrumentation';
3
+ import { NodeSDK } from '@opentelemetry/sdk-node';
4
4
  interface Options {
5
5
  environment?: string;
6
6
  instrumentations?: Instrumentation[];
@@ -1,11 +1,11 @@
1
- import { NodeSDK } from '@opentelemetry/sdk-node';
2
- import { resourceFromAttributes } from '@opentelemetry/resources';
1
+ import { getNodeAutoInstrumentations, } from '@opentelemetry/auto-instrumentations-node';
2
+ import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
3
3
  import { OTLPTraceExporter as GrpcTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
4
4
  import { OTLPTraceExporter as HttpTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
5
- import { getNodeAutoInstrumentations, } from '@opentelemetry/auto-instrumentations-node';
6
- import { BatchSpanProcessor, } from '@opentelemetry/sdk-trace-base';
5
+ import { resourceFromAttributes } from '@opentelemetry/resources';
7
6
  import { BatchLogRecordProcessor, } from '@opentelemetry/sdk-logs';
8
- import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
7
+ import { NodeSDK } from '@opentelemetry/sdk-node';
8
+ import { BatchSpanProcessor, } from '@opentelemetry/sdk-trace-base';
9
9
  import { log } from './logger.js';
10
10
  export const instrument = (options = {}) => {
11
11
  const spanProcessors = [];
package/dist/logger.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { LogAttributes } from '@opentelemetry/api-logs';
1
+ import { type LogAttributes } from '@opentelemetry/api-logs';
2
2
  interface LogInterface {
3
3
  debug: (body: string, attributes?: LogAttributes) => void;
4
4
  info: (body: string, attributes?: LogAttributes) => void;
package/dist/logger.js CHANGED
@@ -1,4 +1,4 @@
1
- import { logs, SeverityNumber } from '@opentelemetry/api-logs';
1
+ import { logs, SeverityNumber, } from '@opentelemetry/api-logs';
2
2
  const name = process.env.OTEL_SERVICE_NAME || 'mcp-app';
3
3
  const logger = logs.getLogger(name);
4
4
  // Helper functions to replace console.log
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { ApiFactory, PromptFactory, McpFeatureFlags, ResourceFactory } from './types.js';
3
- import { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
2
+ import type { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';
3
+ import type { BaseApiFactory, BasePromptFactory, McpFeatureFlags, ResourceFactory } from './types.js';
4
4
  export interface AdditionalSetupArgs<Context extends Record<string, unknown>> {
5
5
  context: Context;
6
6
  server: McpServer;
@@ -10,8 +10,8 @@ export declare const mcpServerFactory: <Context extends Record<string, unknown>>
10
10
  name: string;
11
11
  version?: string;
12
12
  context: Context;
13
- apiFactories?: readonly ApiFactory<Context, any, any>[];
14
- promptFactories?: readonly PromptFactory<Context, any>[];
13
+ apiFactories?: readonly BaseApiFactory<Context>[];
14
+ promptFactories?: readonly BasePromptFactory<Context>[];
15
15
  resourceFactories?: readonly ResourceFactory<Context>[];
16
16
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
17
17
  additionalCapabilities?: ServerCapabilities;
package/dist/mcpServer.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { McpServer, ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { SpanStatusCode, trace, context as otelContext, propagation, SpanKind, } from '@opentelemetry/api';
2
+ import { context as otelContext, propagation, SpanKind, SpanStatusCode, trace, } from '@opentelemetry/api';
3
3
  import { log } from './logger.js';
4
4
  const name = process.env.OTEL_SERVICE_NAME;
5
5
  const tracer = trace.getTracer(name ? `${name}.mcpServer` : 'mcpServer');
@@ -19,7 +19,7 @@ const shouldSkip = (item, enabledSets, disabledSets) => {
19
19
  }
20
20
  }
21
21
  for (const disabledSet of disabledSets) {
22
- if (disabledSet && disabledSet.has(item.name)) {
22
+ if (disabledSet?.has(item.name)) {
23
23
  return true;
24
24
  }
25
25
  }
@@ -59,7 +59,7 @@ export const mcpServerFactory = ({ name, version = '1.0.0', context, apiFactorie
59
59
  // make sense.
60
60
  title: tool.config.title,
61
61
  },
62
- }, async (args, extra) => {
62
+ }, (async (args, extra) => {
63
63
  let traceContext = otelContext.active();
64
64
  if (extra?._meta?.traceparent) {
65
65
  // Some MCP clients (e.g. pydantic) pass the parent trace context
@@ -106,7 +106,7 @@ export const mcpServerFactory = ({ name, version = '1.0.0', context, apiFactorie
106
106
  span.end();
107
107
  }
108
108
  });
109
- });
109
+ }));
110
110
  }
111
111
  }
112
112
  if (enablePrompts) {
@@ -173,6 +173,9 @@ export const mcpServerFactory = ({ name, version = '1.0.0', context, apiFactorie
173
173
  list: resource.list &&
174
174
  ((extra) => tracer.startActiveSpan(`mcp.resource.templated.${resource.name}.list`, async (span) => {
175
175
  try {
176
+ if (!resource.list) {
177
+ throw new Error('resource.list is not defined');
178
+ }
176
179
  const result = await resource.list(extra);
177
180
  span.setAttribute('mcp.resource.list.uris', result.resources.map((r) => r.uri).join(', '));
178
181
  span.setStatus({ code: SpanStatusCode.OK });
package/dist/stdio.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { ApiFactory, PromptFactory, ResourceFactory } from './types.js';
3
- import { AdditionalSetupArgs } from './mcpServer.js';
2
+ import { type AdditionalSetupArgs } from './mcpServer.js';
3
+ import type { BaseApiFactory, BasePromptFactory, ResourceFactory } from './types.js';
4
4
  export declare const stdioServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, instructions, }: {
5
5
  name: string;
6
6
  version?: string;
7
7
  context: Context;
8
- apiFactories?: readonly ApiFactory<Context, any, any>[];
9
- promptFactories?: readonly PromptFactory<Context, any>[];
8
+ apiFactories?: readonly BaseApiFactory<Context>[];
9
+ promptFactories?: readonly BasePromptFactory<Context>[];
10
10
  resourceFactories?: readonly ResourceFactory<Context>[];
11
11
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
12
12
  cleanupFn?: () => Promise<void>;
package/dist/tracing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { type Span, type Tracer } from '@opentelemetry/api';
2
- import type { GenerateTextResult, ModelMessage } from 'ai';
2
+ import type { GenerateTextResult, ModelMessage, ToolSet } from 'ai';
3
3
  export declare const withSpan: <T>(tracer: Tracer, name: string, fn: (span: Span) => Promise<T>) => Promise<T>;
4
- export declare const addAiResultToSpan: (span: Span, aiResult: GenerateTextResult<any, unknown>, inputMessages: ModelMessage[]) => void;
4
+ export declare const addAiResultToSpan: (span: Span, aiResult: GenerateTextResult<ToolSet, unknown>, inputMessages: ModelMessage[]) => void;
package/dist/types.d.ts CHANGED
@@ -1,23 +1,47 @@
1
- import { z } from 'zod';
2
- import type { ZodRawShape, ZodTypeAny } from 'zod';
3
- import type { ToolAnnotations, GetPromptResult } from '@modelcontextprotocol/sdk/types.js';
4
- import { Router } from 'express';
5
- import { CompleteResourceTemplateCallback, ListResourcesCallback, ReadResourceCallback, ReadResourceTemplateCallback, ResourceMetadata } from '@modelcontextprotocol/sdk/server/mcp.js';
6
- export type ToolConfig<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape> = {
1
+ import type { CompleteResourceTemplateCallback, ListResourcesCallback, ReadResourceCallback, ReadResourceTemplateCallback, ResourceMetadata } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { GetPromptResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
3
+ import type { Router } from 'express';
4
+ import type { ZodRawShape, ZodTypeAny, z } from 'zod';
5
+ export type BaseToolConfig = {
7
6
  title?: string;
8
7
  description?: string;
9
- inputSchema: InputArgs;
10
- outputSchema: OutputArgs;
8
+ inputSchema: ZodRawShape;
9
+ outputSchema: ZodRawShape;
11
10
  annotations?: ToolAnnotations;
12
11
  };
13
- export interface ApiDefinition<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape, SimplifiedOutputArgs = OutputArgs> {
12
+ export interface BaseApiDefinition {
14
13
  name: string;
15
14
  method?: 'get' | 'post' | 'put' | 'delete';
16
15
  route?: string | string[];
17
- config: ToolConfig<InputArgs, OutputArgs>;
16
+ config: BaseToolConfig;
18
17
  disabled?: boolean;
19
- fn: (args: z.objectOutputType<InputArgs, ZodTypeAny>) => Promise<z.objectOutputType<OutputArgs, ZodTypeAny>>;
20
- pickResult?: (result: z.objectOutputType<OutputArgs, ZodTypeAny>) => SimplifiedOutputArgs;
18
+ fn(args: Record<string, unknown>): Promise<Record<string, unknown>>;
19
+ pickResult?(result: Record<string, unknown>): unknown;
20
+ }
21
+ export type BaseApiFactory<Context extends Record<string, unknown>> = (ctx: Context, featureFlags: McpFeatureFlags) => BaseApiDefinition;
22
+ export type BasePromptConfig = {
23
+ title?: string;
24
+ description?: string;
25
+ inputSchema: ZodRawShape;
26
+ };
27
+ export interface BasePromptDefinition {
28
+ name: string;
29
+ config: BasePromptConfig;
30
+ disabled?: boolean;
31
+ fn(args: Record<string, unknown>): Promise<GetPromptResult>;
32
+ }
33
+ export type BasePromptFactory<Context extends Record<string, unknown>> = (ctx: Context, featureFlags: McpFeatureFlags) => BasePromptDefinition;
34
+ export type ToolConfig<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape> = {
35
+ title?: string;
36
+ description?: string;
37
+ inputSchema: InputArgs;
38
+ outputSchema: OutputArgs;
39
+ annotations?: ToolAnnotations;
40
+ };
41
+ export interface ApiDefinition<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape, SimplifiedOutputArgs = OutputArgs> extends BaseApiDefinition {
42
+ config: ToolConfig<InputArgs, OutputArgs>;
43
+ fn(args: z.objectOutputType<InputArgs, ZodTypeAny>): Promise<z.objectOutputType<OutputArgs, ZodTypeAny>>;
44
+ pickResult?(result: z.objectOutputType<OutputArgs, ZodTypeAny>): SimplifiedOutputArgs;
21
45
  }
22
46
  export type ApiFactory<Context extends Record<string, unknown>, Input extends ZodRawShape, Output extends ZodRawShape, RestOutput = Output> = (ctx: Context, featureFlags: McpFeatureFlags) => ApiDefinition<Input, Output, RestOutput>;
23
47
  export type RouterFactoryResult = [Router, () => void | Promise<void>];
@@ -26,11 +50,9 @@ export type PromptConfig<InputArgs extends ZodRawShape> = {
26
50
  description?: string;
27
51
  inputSchema: InputArgs;
28
52
  };
29
- export interface PromptDefinition<InputArgs extends ZodRawShape> {
30
- name: string;
53
+ export interface PromptDefinition<InputArgs extends ZodRawShape> extends BasePromptDefinition {
31
54
  config: PromptConfig<InputArgs>;
32
- disabled?: boolean;
33
- fn: (args: z.objectOutputType<InputArgs, ZodTypeAny>) => Promise<GetPromptResult>;
55
+ fn(args: z.objectOutputType<InputArgs, ZodTypeAny>): Promise<GetPromptResult>;
34
56
  }
35
57
  export type PromptFactory<Context extends Record<string, unknown>, Input extends ZodRawShape> = (ctx: Context, featureFlags: McpFeatureFlags) => PromptDefinition<Input>;
36
58
  export interface TemplatedResourceDefinition {
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, expectTypeOf } from 'bun:test';
1
+ import { describe, expect, expectTypeOf, it } from 'bun:test';
2
2
  import { z } from 'zod';
3
3
  describe('InferSchema', () => {
4
4
  it('should infer required properties correctly', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tigerdata/mcp-boilerplate",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "MCP boilerplate code for Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "author": "TigerData",
@@ -31,17 +31,15 @@
31
31
  ],
32
32
  "scripts": {
33
33
  "build": "tsc",
34
- "prepare": "npm run build",
34
+ "prepublishOnly": "tsc",
35
35
  "watch": "tsc --watch",
36
- "lint": "eslint",
37
- "lint:fix": "eslint --fix",
38
- "prettier:write": "prettier --write .",
39
- "prettier:check": "prettier --check ."
36
+ "lint": "./bun x @biomejs/biome check"
40
37
  },
41
38
  "dependencies": {
42
- "@modelcontextprotocol/sdk": "^1.24.2",
39
+ "@mcp-use/inspector": "^0.13.2",
40
+ "@modelcontextprotocol/sdk": "^1.25.1",
43
41
  "@opentelemetry/api": "^1.9.0",
44
- "@opentelemetry/auto-instrumentations-node": "^0.67.2",
42
+ "@opentelemetry/auto-instrumentations-node": "^0.67.3",
45
43
  "@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
46
44
  "@opentelemetry/instrumentation-http": "^0.208.0",
47
45
  "@opentelemetry/sdk-metrics": "^2.2.0",
@@ -53,15 +51,14 @@
53
51
  "zod": "^3.23.8"
54
52
  },
55
53
  "devDependencies": {
56
- "@eslint/js": "^9.39.1",
57
- "@types/bun": "^1.3.3",
54
+ "@biomejs/biome": "^2.3.10",
55
+ "@types/bun": "^1.3.5",
58
56
  "@types/express": "^5.0.6",
59
- "@types/node": "^22.19.1",
60
- "ai": "^5.0.106",
61
- "eslint": "^9.39.1",
62
- "prettier": "^3.7.4",
63
- "typescript": "^5.9.3",
64
- "typescript-eslint": "^8.48.1"
57
+ "@types/node": "^22.19.3",
58
+ "@typescript-eslint/typescript-estree": "^8.50.0",
59
+ "ai": "^5.0.115",
60
+ "eslint": "^9.39.2",
61
+ "typescript": "^5.9.3"
65
62
  },
66
63
  "publishConfig": {
67
64
  "access": "public"