@tigerdata/mcp-boilerplate 0.9.0 → 0.9.1

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,6 @@
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 ZodRawShape } from 'zod';
5
+ import type { ApiFactory, RouterFactoryResult } from '../types.js';
6
+ export declare const apiRouterFactory: <Context extends Record<string, unknown>>(context: Context, apiFactories: readonly ApiFactory<Context, ZodRawShape, ZodRawShape>[]) => 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,5 +1,5 @@
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
5
  }, { name, stateful, }?: {
package/dist/http/mcp.js CHANGED
@@ -1,9 +1,9 @@
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;
@@ -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) => {
@@ -214,7 +215,7 @@ export const mcpRouterFactory = (context, createServer, { name, stateful = true,
214
215
  for (const sessionId in transports) {
215
216
  try {
216
217
  log.info(`Closing transport for session ${sessionId}`);
217
- await transports.get(sessionId).close();
218
+ await transports.get(sessionId)?.close();
218
219
  transports.delete(sessionId);
219
220
  }
220
221
  catch (error) {
@@ -1,14 +1,15 @@
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 { ZodRawShape } from 'zod';
5
+ import { type AdditionalSetupArgs } from './mcpServer.js';
6
+ import type { ApiFactory, PromptFactory, ResourceFactory } from './types.js';
6
7
  export declare const httpServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, stateful, instructions, }: {
7
8
  name: string;
8
9
  version?: string;
9
10
  context: Context;
10
- apiFactories?: readonly ApiFactory<Context, any, any>[];
11
- promptFactories?: readonly PromptFactory<Context, any>[];
11
+ apiFactories?: readonly ApiFactory<Context, ZodRawShape, ZodRawShape>[];
12
+ promptFactories?: readonly PromptFactory<Context, ZodRawShape>[];
12
13
  resourceFactories?: readonly ResourceFactory<Context>[];
13
14
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
14
15
  cleanupFn?: () => void | Promise<void>;
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import express from 'express';
3
- import { mcpRouterFactory } from './http/mcp.js';
4
3
  import { apiRouterFactory } from './http/api.js';
5
- import { registerExitHandlers } from './registerExitHandlers.js';
6
- import { mcpServerFactory } from './mcpServer.js';
4
+ import { mcpRouterFactory } from './http/mcp.js';
7
5
  import { log } from './logger.js';
6
+ import { mcpServerFactory } from './mcpServer.js';
7
+ import { registerExitHandlers } from './registerExitHandlers.js';
8
8
  import { StatusError } from './StatusError.js';
9
9
  export const httpServerFactory = ({ name, version, context, apiFactories = [], promptFactories, resourceFactories, additionalSetup, cleanupFn, stateful = true, instructions, }) => {
10
10
  const cleanupFns = cleanupFn
@@ -31,7 +31,7 @@ export const httpServerFactory = ({ name, version, context, apiFactories = [], p
31
31
  cleanupFns.push(apiCleanup);
32
32
  app.use('/api', apiRouter);
33
33
  // Error handler
34
- app.use(function (err, req, res, _next) {
34
+ app.use((err, _req, res, _next) => {
35
35
  if (err instanceof StatusError && err.status < 500) {
36
36
  log.info('HTTP error response', {
37
37
  message: err.message,
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,7 @@
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 { ZodRawShape } from 'zod';
4
+ import type { ApiFactory, McpFeatureFlags, PromptFactory, ResourceFactory } from './types.js';
4
5
  export interface AdditionalSetupArgs<Context extends Record<string, unknown>> {
5
6
  context: Context;
6
7
  server: McpServer;
@@ -10,8 +11,8 @@ export declare const mcpServerFactory: <Context extends Record<string, unknown>>
10
11
  name: string;
11
12
  version?: string;
12
13
  context: Context;
13
- apiFactories?: readonly ApiFactory<Context, any, any>[];
14
- promptFactories?: readonly PromptFactory<Context, any>[];
14
+ apiFactories?: readonly ApiFactory<Context, ZodRawShape, ZodRawShape>[];
15
+ promptFactories?: readonly PromptFactory<Context, ZodRawShape>[];
15
16
  resourceFactories?: readonly ResourceFactory<Context>[];
16
17
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
17
18
  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
  }
@@ -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,13 @@
1
1
  #!/usr/bin/env node
2
- import { ApiFactory, PromptFactory, ResourceFactory } from './types.js';
3
- import { AdditionalSetupArgs } from './mcpServer.js';
2
+ import type { ZodRawShape } from 'zod';
3
+ import { type AdditionalSetupArgs } from './mcpServer.js';
4
+ import type { ApiFactory, PromptFactory, ResourceFactory } from './types.js';
4
5
  export declare const stdioServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, instructions, }: {
5
6
  name: string;
6
7
  version?: string;
7
8
  context: Context;
8
- apiFactories?: readonly ApiFactory<Context, any, any>[];
9
- promptFactories?: readonly PromptFactory<Context, any>[];
9
+ apiFactories?: readonly ApiFactory<Context, ZodRawShape, ZodRawShape>[];
10
+ promptFactories?: readonly PromptFactory<Context, ZodRawShape>[];
10
11
  resourceFactories?: readonly ResourceFactory<Context>[];
11
12
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
12
13
  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,8 +1,7 @@
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';
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';
6
5
  export type ToolConfig<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape> = {
7
6
  title?: string;
8
7
  description?: string;
@@ -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.1",
4
4
  "description": "MCP boilerplate code for Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "author": "TigerData",
@@ -31,17 +31,14 @@
31
31
  ],
32
32
  "scripts": {
33
33
  "build": "tsc",
34
- "prepare": "npm run build",
34
+ "prepare": "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
+ "@modelcontextprotocol/sdk": "^1.25.1",
43
40
  "@opentelemetry/api": "^1.9.0",
44
- "@opentelemetry/auto-instrumentations-node": "^0.67.2",
41
+ "@opentelemetry/auto-instrumentations-node": "^0.67.3",
45
42
  "@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
46
43
  "@opentelemetry/instrumentation-http": "^0.208.0",
47
44
  "@opentelemetry/sdk-metrics": "^2.2.0",
@@ -53,15 +50,14 @@
53
50
  "zod": "^3.23.8"
54
51
  },
55
52
  "devDependencies": {
56
- "@eslint/js": "^9.39.1",
57
- "@types/bun": "^1.3.3",
53
+ "@biomejs/biome": "^2.3.10",
54
+ "@types/bun": "^1.3.5",
58
55
  "@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"
56
+ "@types/node": "^22.19.3",
57
+ "@typescript-eslint/typescript-estree": "^8.50.0",
58
+ "ai": "^5.0.115",
59
+ "eslint": "^9.39.2",
60
+ "typescript": "^5.9.3"
65
61
  },
66
62
  "publishConfig": {
67
63
  "access": "public"