@tigerdata/mcp-boilerplate 0.7.0 → 0.8.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.
- package/dist/types.d.ts +15 -3
- package/dist/types.spec.d.ts +1 -0
- package/dist/types.spec.js +153 -0
- package/package.json +7 -6
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
|
-
|
|
74
|
-
[K in keyof T]:
|
|
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.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP boilerplate code for Node.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "TigerData",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"prettier:check": "prettier --check ."
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.23.0",
|
|
43
43
|
"@opentelemetry/api": "^1.9.0",
|
|
44
|
-
"@opentelemetry/auto-instrumentations-node": "^0.67.
|
|
44
|
+
"@opentelemetry/auto-instrumentations-node": "^0.67.1",
|
|
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",
|
|
@@ -49,18 +49,19 @@
|
|
|
49
49
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
|
50
50
|
"@opentelemetry/semantic-conventions": "^1.38.0",
|
|
51
51
|
"express": "^5.1.0",
|
|
52
|
-
"raw-body": "^3.0.
|
|
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/bun": "^1.3.3",
|
|
57
58
|
"@types/express": "^5.0.5",
|
|
58
59
|
"@types/node": "^22.19.1",
|
|
59
|
-
"ai": "^5.0.
|
|
60
|
+
"ai": "^5.0.102",
|
|
60
61
|
"eslint": "^9.39.1",
|
|
61
62
|
"prettier": "^3.6.2",
|
|
62
63
|
"typescript": "^5.9.3",
|
|
63
|
-
"typescript-eslint": "^8.
|
|
64
|
+
"typescript-eslint": "^8.48.0"
|
|
64
65
|
},
|
|
65
66
|
"publishConfig": {
|
|
66
67
|
"access": "public"
|