@tigerdata/mcp-boilerplate 0.7.0 → 0.9.0

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.
@@ -1,7 +1,5 @@
1
- import { dirname, join } from 'path';
2
- import { fileURLToPath } from 'url';
3
- const __dirname = dirname(fileURLToPath(import.meta.url));
4
- export async function cliEntrypoint(stdioEntrypoint, httpEntrypoint, instrumentation = join(__dirname, './instrumentation.js')) {
1
+ import { join } from 'path';
2
+ export async function cliEntrypoint(stdioEntrypoint, httpEntrypoint, instrumentation = join(import.meta.dir, './instrumentation.js')) {
5
3
  // Parse command line arguments first
6
4
  const args = process.argv.slice(2);
7
5
  const scriptName = args[0] || 'stdio';
@@ -3,7 +3,7 @@ import express from 'express';
3
3
  import { ApiFactory, PromptFactory, ResourceFactory } from './types.js';
4
4
  import { AdditionalSetupArgs } from './mcpServer.js';
5
5
  import { Server } from 'node:http';
6
- export declare const httpServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, stateful, }: {
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;
@@ -13,6 +13,7 @@ export declare const httpServerFactory: <Context extends Record<string, unknown>
13
13
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
14
14
  cleanupFn?: () => void | Promise<void>;
15
15
  stateful?: boolean;
16
+ instructions?: string;
16
17
  }) => {
17
18
  app: express.Express;
18
19
  server: Server;
@@ -6,7 +6,7 @@ import { registerExitHandlers } from './registerExitHandlers.js';
6
6
  import { mcpServerFactory } from './mcpServer.js';
7
7
  import { log } from './logger.js';
8
8
  import { StatusError } from './StatusError.js';
9
- export const httpServerFactory = ({ name, version, context, apiFactories = [], promptFactories, resourceFactories, additionalSetup, cleanupFn, stateful = true, }) => {
9
+ export const httpServerFactory = ({ name, version, context, apiFactories = [], promptFactories, resourceFactories, additionalSetup, cleanupFn, stateful = true, instructions, }) => {
10
10
  const cleanupFns = cleanupFn
11
11
  ? [cleanupFn]
12
12
  : [];
@@ -23,6 +23,7 @@ export const httpServerFactory = ({ name, version, context, apiFactories = [], p
23
23
  resourceFactories,
24
24
  additionalSetup,
25
25
  featureFlags,
26
+ instructions,
26
27
  }), { name, stateful });
27
28
  cleanupFns.push(mcpCleanup);
28
29
  app.use('/mcp', mcpRouter);
@@ -6,7 +6,7 @@ export interface AdditionalSetupArgs<Context extends Record<string, unknown>> {
6
6
  server: McpServer;
7
7
  featureFlags: McpFeatureFlags;
8
8
  }
9
- export declare const mcpServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, additionalCapabilities, featureFlags, }: {
9
+ export declare const mcpServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, additionalCapabilities, featureFlags, instructions, }: {
10
10
  name: string;
11
11
  version?: string;
12
12
  context: Context;
@@ -16,6 +16,7 @@ export declare const mcpServerFactory: <Context extends Record<string, unknown>>
16
16
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
17
17
  additionalCapabilities?: ServerCapabilities;
18
18
  featureFlags?: McpFeatureFlags;
19
+ instructions?: string;
19
20
  }) => {
20
21
  server: McpServer;
21
22
  };
package/dist/mcpServer.js CHANGED
@@ -25,7 +25,7 @@ const shouldSkip = (item, enabledSets, disabledSets) => {
25
25
  }
26
26
  return false;
27
27
  };
28
- export const mcpServerFactory = ({ name, version = '1.0.0', context, apiFactories = [], promptFactories = [], resourceFactories = [], additionalSetup, additionalCapabilities = {}, featureFlags = {}, }) => {
28
+ export const mcpServerFactory = ({ name, version = '1.0.0', context, apiFactories = [], promptFactories = [], resourceFactories = [], additionalSetup, additionalCapabilities = {}, featureFlags = {}, instructions, }) => {
29
29
  const enablePrompts = featureFlags.prompts !== false;
30
30
  const enableResources = featureFlags.resources !== false;
31
31
  const enableTools = featureFlags.tools !== false;
@@ -41,6 +41,7 @@ export const mcpServerFactory = ({ name, version = '1.0.0', context, apiFactorie
41
41
  : null),
42
42
  ...additionalCapabilities,
43
43
  },
44
+ instructions,
44
45
  });
45
46
  if (enableTools) {
46
47
  for (const factory of apiFactories) {
package/dist/stdio.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { ApiFactory, PromptFactory, ResourceFactory } from './types.js';
3
3
  import { AdditionalSetupArgs } from './mcpServer.js';
4
- export declare const stdioServerFactory: <Context extends Record<string, unknown>>({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, }: {
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;
@@ -10,4 +10,5 @@ export declare const stdioServerFactory: <Context extends Record<string, unknown
10
10
  resourceFactories?: readonly ResourceFactory<Context>[];
11
11
  additionalSetup?: (args: AdditionalSetupArgs<Context>) => void;
12
12
  cleanupFn?: () => Promise<void>;
13
+ instructions?: string;
13
14
  }) => Promise<void>;
package/dist/stdio.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { mcpServerFactory } from './mcpServer.js';
4
4
  import { registerExitHandlers } from './registerExitHandlers.js';
5
- export const stdioServerFactory = async ({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, }) => {
5
+ export const stdioServerFactory = async ({ name, version, context, apiFactories, promptFactories, resourceFactories, additionalSetup, cleanupFn, instructions, }) => {
6
6
  try {
7
7
  console.error('Starting default (STDIO) server...');
8
8
  const transport = new StdioServerTransport();
@@ -14,6 +14,7 @@ export const stdioServerFactory = async ({ name, version, context, apiFactories,
14
14
  promptFactories,
15
15
  resourceFactories,
16
16
  additionalSetup,
17
+ instructions,
17
18
  });
18
19
  await server.connect(transport);
19
20
  // Cleanup on exit
package/dist/types.d.ts CHANGED
@@ -70,6 +70,18 @@ export interface McpFeatureFlags {
70
70
  disabledTools?: Set<string> | null;
71
71
  query?: ParsedQs;
72
72
  }
73
- export type InferSchema<T extends Record<string, z.ZodType>> = {
74
- [K in keyof T]: z.infer<T[K]>;
75
- };
73
+ type Flatten<T> = {
74
+ [K in keyof T]: T[K];
75
+ } & {};
76
+ type OptionalKeys<T extends Record<string, z.ZodType>> = {
77
+ [K in keyof T]: T[K] extends z.ZodOptional<z.ZodType> ? K : never;
78
+ }[keyof T];
79
+ type RequiredKeys<T extends Record<string, z.ZodType>> = {
80
+ [K in keyof T]: T[K] extends z.ZodOptional<z.ZodType> ? never : K;
81
+ }[keyof T];
82
+ export type InferSchema<T extends Record<string, z.ZodType>> = Flatten<{
83
+ [K in RequiredKeys<T>]: z.infer<T[K]>;
84
+ } & {
85
+ [K in OptionalKeys<T>]?: z.infer<T[K]>;
86
+ }>;
87
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,153 @@
1
+ import { describe, it, expect, expectTypeOf } from 'bun:test';
2
+ import { z } from 'zod';
3
+ describe('InferSchema', () => {
4
+ it('should infer required properties correctly', () => {
5
+ const schema = {
6
+ name: z.string(),
7
+ age: z.number(),
8
+ };
9
+ const zodSchema = z.object(schema);
10
+ // Type-level assertion (checked by tsc --noEmit)
11
+ expectTypeOf().toEqualTypeOf();
12
+ // Runtime check with zod validation
13
+ const valid = { name: 'John', age: 30 };
14
+ const parsed = zodSchema.parse(valid);
15
+ expect(parsed.name).toBe('John');
16
+ expect(parsed.age).toBe(30);
17
+ });
18
+ it('should make optional zod properties optional in the inferred type', () => {
19
+ const schema = {
20
+ name: z.string(),
21
+ nickname: z.string().optional(),
22
+ };
23
+ const zodSchema = z.object(schema);
24
+ // Type-level assertion (checked by tsc --noEmit)
25
+ // nickname should be optional (string | undefined with optional key)
26
+ expectTypeOf().toEqualTypeOf();
27
+ // Runtime checks - should compile without providing nickname
28
+ const withoutNickname = { name: 'John' };
29
+ const parsedWithout = zodSchema.parse(withoutNickname);
30
+ expect(parsedWithout.name).toBe('John');
31
+ expect(parsedWithout.nickname).toBeUndefined();
32
+ // Should also compile with nickname provided
33
+ const withNickname = { name: 'John', nickname: 'Johnny' };
34
+ const parsedWith = zodSchema.parse(withNickname);
35
+ expect(parsedWith.nickname).toBe('Johnny');
36
+ });
37
+ it('should handle nullable properties', () => {
38
+ const schema = {
39
+ name: z.string(),
40
+ middleName: z.string().nullable(),
41
+ };
42
+ const zodSchema = z.object(schema);
43
+ // Type-level assertion (checked by tsc --noEmit)
44
+ // Nullable properties should still be required, but allow null
45
+ expectTypeOf().toEqualTypeOf();
46
+ // Runtime checks with zod validation
47
+ const withNull = { name: 'John', middleName: null };
48
+ const parsedNull = zodSchema.parse(withNull);
49
+ expect(parsedNull.middleName).toBeNull();
50
+ const withValue = { name: 'John', middleName: 'William' };
51
+ const parsedValue = zodSchema.parse(withValue);
52
+ expect(parsedValue.middleName).toBe('William');
53
+ });
54
+ it('should handle optional and nullable combined', () => {
55
+ const schema = {
56
+ name: z.string(),
57
+ suffix: z.string().nullable().optional(),
58
+ };
59
+ const zodSchema = z.object(schema);
60
+ // Type-level assertion (checked by tsc --noEmit)
61
+ // Should be optional and nullable
62
+ expectTypeOf().toEqualTypeOf();
63
+ // Runtime checks with zod validation
64
+ const withoutSuffix = { name: 'John' };
65
+ const parsedWithout = zodSchema.parse(withoutSuffix);
66
+ expect(parsedWithout.suffix).toBeUndefined();
67
+ const withNull = { name: 'John', suffix: null };
68
+ const parsedNull = zodSchema.parse(withNull);
69
+ expect(parsedNull.suffix).toBeNull();
70
+ const withValue = { name: 'John', suffix: 'Jr.' };
71
+ const parsedValue = zodSchema.parse(withValue);
72
+ expect(parsedValue.suffix).toBe('Jr.');
73
+ });
74
+ it('should handle complex nested schemas', () => {
75
+ const schema = {
76
+ user: z.object({
77
+ name: z.string(),
78
+ email: z.string().optional(),
79
+ }),
80
+ metadata: z
81
+ .object({
82
+ createdAt: z.date(),
83
+ })
84
+ .optional(),
85
+ };
86
+ const zodSchema = z.object(schema);
87
+ // Type-level assertion (checked by tsc --noEmit)
88
+ // metadata should be optional
89
+ expectTypeOf().toEqualTypeOf();
90
+ // Runtime checks with zod validation
91
+ const withoutMetadata = {
92
+ user: { name: 'John' },
93
+ };
94
+ const parsedWithout = zodSchema.parse(withoutMetadata);
95
+ expect(parsedWithout.user.name).toBe('John');
96
+ expect(parsedWithout.metadata).toBeUndefined();
97
+ const now = new Date();
98
+ const withMetadata = {
99
+ user: { name: 'John', email: 'john@example.com' },
100
+ metadata: { createdAt: now },
101
+ };
102
+ const parsedWith = zodSchema.parse(withMetadata);
103
+ expect(parsedWith.metadata?.createdAt).toEqual(now);
104
+ });
105
+ it('should handle arrays with optional elements', () => {
106
+ const schema = {
107
+ items: z.array(z.string()),
108
+ tags: z.array(z.string()).optional(),
109
+ };
110
+ const zodSchema = z.object(schema);
111
+ // Type-level assertion (checked by tsc --noEmit)
112
+ // tags should be optional
113
+ expectTypeOf().toEqualTypeOf();
114
+ // Runtime checks with zod validation
115
+ const withoutTags = { items: ['a', 'b'] };
116
+ const parsedWithout = zodSchema.parse(withoutTags);
117
+ expect(parsedWithout.items).toEqual(['a', 'b']);
118
+ expect(parsedWithout.tags).toBeUndefined();
119
+ const withTags = { items: ['a'], tags: ['tag1', 'tag2'] };
120
+ const parsedWith = zodSchema.parse(withTags);
121
+ expect(parsedWith.tags).toEqual(['tag1', 'tag2']);
122
+ });
123
+ it('should correctly type a mixed schema', () => {
124
+ const schema = {
125
+ id: z.string(),
126
+ description: z.string().optional(),
127
+ count: z.number(),
128
+ deletedAt: z.date().nullable().optional(),
129
+ };
130
+ const zodSchema = z.object(schema);
131
+ // Type-level assertion (checked by tsc --noEmit)
132
+ expectTypeOf().toEqualTypeOf();
133
+ // Runtime checks - minimal valid object (only required fields)
134
+ const minimal = {
135
+ id: '123',
136
+ count: 0,
137
+ };
138
+ const parsedMinimal = zodSchema.parse(minimal);
139
+ expect(parsedMinimal.id).toBe('123');
140
+ expect(parsedMinimal.count).toBe(0);
141
+ // Full object
142
+ const now = new Date();
143
+ const full = {
144
+ id: '456',
145
+ description: 'A test',
146
+ count: 5,
147
+ deletedAt: now,
148
+ };
149
+ const parsedFull = zodSchema.parse(full);
150
+ expect(parsedFull.description).toBe('A test');
151
+ expect(parsedFull.deletedAt).toEqual(now);
152
+ });
153
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tigerdata/mcp-boilerplate",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "MCP boilerplate code for Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "author": "TigerData",
@@ -39,28 +39,29 @@
39
39
  "prettier:check": "prettier --check ."
40
40
  },
41
41
  "dependencies": {
42
- "@modelcontextprotocol/sdk": "^1.22.0",
42
+ "@modelcontextprotocol/sdk": "^1.24.2",
43
43
  "@opentelemetry/api": "^1.9.0",
44
- "@opentelemetry/auto-instrumentations-node": "^0.67.0",
44
+ "@opentelemetry/auto-instrumentations-node": "^0.67.2",
45
45
  "@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
46
46
  "@opentelemetry/instrumentation-http": "^0.208.0",
47
47
  "@opentelemetry/sdk-metrics": "^2.2.0",
48
48
  "@opentelemetry/sdk-node": "^0.208.0",
49
49
  "@opentelemetry/sdk-trace-node": "^2.2.0",
50
50
  "@opentelemetry/semantic-conventions": "^1.38.0",
51
- "express": "^5.1.0",
52
- "raw-body": "^3.0.1",
51
+ "express": "^5.2.1",
52
+ "raw-body": "^3.0.2",
53
53
  "zod": "^3.23.8"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@eslint/js": "^9.39.1",
57
- "@types/express": "^5.0.5",
57
+ "@types/bun": "^1.3.3",
58
+ "@types/express": "^5.0.6",
58
59
  "@types/node": "^22.19.1",
59
- "ai": "^5.0.93",
60
+ "ai": "^5.0.106",
60
61
  "eslint": "^9.39.1",
61
- "prettier": "^3.6.2",
62
+ "prettier": "^3.7.4",
62
63
  "typescript": "^5.9.3",
63
- "typescript-eslint": "^8.46.4"
64
+ "typescript-eslint": "^8.48.1"
64
65
  },
65
66
  "publishConfig": {
66
67
  "access": "public"