@mcp-web/core 0.1.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/LICENSE +201 -0
- package/README.md +253 -0
- package/dist/addTool.typetest.d.ts +11 -0
- package/dist/addTool.typetest.d.ts.map +1 -0
- package/dist/addTool.typetest.js +248 -0
- package/dist/create-state-tools.d.ts +77 -0
- package/dist/create-state-tools.d.ts.map +1 -0
- package/dist/create-state-tools.js +181 -0
- package/dist/create-tool.d.ts +90 -0
- package/dist/create-tool.d.ts.map +1 -0
- package/dist/create-tool.js +82 -0
- package/dist/expanded-schema-tools/generate-fixed-shape-tools.d.ts +8 -0
- package/dist/expanded-schema-tools/generate-fixed-shape-tools.d.ts.map +1 -0
- package/dist/expanded-schema-tools/generate-fixed-shape-tools.js +53 -0
- package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.d.ts +2 -0
- package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.d.ts.map +1 -0
- package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.js +331 -0
- package/dist/expanded-schema-tools/index.d.ts +4 -0
- package/dist/expanded-schema-tools/index.d.ts.map +1 -0
- package/dist/expanded-schema-tools/index.js +2 -0
- package/dist/expanded-schema-tools/integration.test.d.ts +2 -0
- package/dist/expanded-schema-tools/integration.test.d.ts.map +1 -0
- package/dist/expanded-schema-tools/integration.test.js +599 -0
- package/dist/expanded-schema-tools/schema-analysis.d.ts +18 -0
- package/dist/expanded-schema-tools/schema-analysis.d.ts.map +1 -0
- package/dist/expanded-schema-tools/schema-analysis.js +142 -0
- package/dist/expanded-schema-tools/schema-analysis.test.d.ts +2 -0
- package/dist/expanded-schema-tools/schema-analysis.test.d.ts.map +1 -0
- package/dist/expanded-schema-tools/schema-analysis.test.js +314 -0
- package/dist/expanded-schema-tools/schema-helpers.d.ts +69 -0
- package/dist/expanded-schema-tools/schema-helpers.d.ts.map +1 -0
- package/dist/expanded-schema-tools/schema-helpers.js +139 -0
- package/dist/expanded-schema-tools/schema-helpers.test.d.ts +2 -0
- package/dist/expanded-schema-tools/schema-helpers.test.d.ts.map +1 -0
- package/dist/expanded-schema-tools/schema-helpers.test.js +223 -0
- package/dist/expanded-schema-tools/tool-generator.d.ts +10 -0
- package/dist/expanded-schema-tools/tool-generator.d.ts.map +1 -0
- package/dist/expanded-schema-tools/tool-generator.js +430 -0
- package/dist/expanded-schema-tools/tool-generator.test.d.ts +2 -0
- package/dist/expanded-schema-tools/tool-generator.test.d.ts.map +1 -0
- package/dist/expanded-schema-tools/tool-generator.test.js +689 -0
- package/dist/expanded-schema-tools/types.d.ts +26 -0
- package/dist/expanded-schema-tools/types.d.ts.map +1 -0
- package/dist/expanded-schema-tools/types.js +1 -0
- package/dist/expanded-schema-tools/utils.d.ts +16 -0
- package/dist/expanded-schema-tools/utils.d.ts.map +1 -0
- package/dist/expanded-schema-tools/utils.js +35 -0
- package/dist/expanded-schema-tools/utils.test.d.ts +2 -0
- package/dist/expanded-schema-tools/utils.test.d.ts.map +1 -0
- package/dist/expanded-schema-tools/utils.test.js +169 -0
- package/dist/group-state.d.ts +60 -0
- package/dist/group-state.d.ts.map +1 -0
- package/dist/group-state.js +54 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/query.d.ts +104 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +128 -0
- package/dist/schema-helpers.d.ts +69 -0
- package/dist/schema-helpers.d.ts.map +1 -0
- package/dist/schema-helpers.js +139 -0
- package/dist/schemas.d.ts +140 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +70 -0
- package/dist/tool-generators/generate-basic-state-tools.d.ts +23 -0
- package/dist/tool-generators/generate-basic-state-tools.d.ts.map +1 -0
- package/dist/tool-generators/generate-basic-state-tools.js +95 -0
- package/dist/tool-generators/generate-fixed-shape-tools.d.ts +8 -0
- package/dist/tool-generators/generate-fixed-shape-tools.d.ts.map +1 -0
- package/dist/tool-generators/generate-fixed-shape-tools.js +53 -0
- package/dist/tool-generators/index.d.ts +6 -0
- package/dist/tool-generators/index.d.ts.map +1 -0
- package/dist/tool-generators/index.js +3 -0
- package/dist/tool-generators/schema-analysis.d.ts +18 -0
- package/dist/tool-generators/schema-analysis.d.ts.map +1 -0
- package/dist/tool-generators/schema-analysis.js +142 -0
- package/dist/tool-generators/schema-helpers.d.ts +87 -0
- package/dist/tool-generators/schema-helpers.d.ts.map +1 -0
- package/dist/tool-generators/schema-helpers.js +157 -0
- package/dist/tool-generators/tool-generator.d.ts +11 -0
- package/dist/tool-generators/tool-generator.d.ts.map +1 -0
- package/dist/tool-generators/tool-generator.js +437 -0
- package/dist/tool-generators/types.d.ts +26 -0
- package/dist/tool-generators/types.d.ts.map +1 -0
- package/dist/tool-generators/types.js +1 -0
- package/dist/tool-generators/utils.d.ts +16 -0
- package/dist/tool-generators/utils.d.ts.map +1 -0
- package/dist/tool-generators/utils.js +35 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +31 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +108 -0
- package/dist/web.d.ts +680 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +1312 -0
- package/dist/zod-to-tools.d.ts +49 -0
- package/dist/zod-to-tools.d.ts.map +1 -0
- package/dist/zod-to-tools.js +623 -0
- package/package.json +58 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type tests for MCPWeb.addTool() overloads.
|
|
3
|
+
*
|
|
4
|
+
* These tests verify compile-time type safety. They don't execute any code,
|
|
5
|
+
* but TypeScript will report errors if the type constraints are violated.
|
|
6
|
+
*
|
|
7
|
+
* Positive tests should compile without errors.
|
|
8
|
+
* Negative tests are commented out with expected errors explained.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// CASE 1: Zod input + Zod output (full type safety)
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Positive test 1.1: Correct handler signature matching schemas
|
|
15
|
+
mcp.addTool({
|
|
16
|
+
name: 'double',
|
|
17
|
+
description: 'Double a number',
|
|
18
|
+
handler: ({ value }) => ({ result: value * 2 }),
|
|
19
|
+
inputSchema: z.object({ value: z.number() }),
|
|
20
|
+
outputSchema: z.object({ result: z.number() }),
|
|
21
|
+
});
|
|
22
|
+
// Positive test 1.2: Async handler with correct types
|
|
23
|
+
mcp.addTool({
|
|
24
|
+
name: 'asyncDouble',
|
|
25
|
+
description: 'Double a number asynchronously',
|
|
26
|
+
handler: async ({ value }) => ({ result: value * 2 }),
|
|
27
|
+
inputSchema: z.object({ value: z.number() }),
|
|
28
|
+
outputSchema: z.object({ result: z.number() }),
|
|
29
|
+
});
|
|
30
|
+
// Positive test 1.3: Complex nested schema
|
|
31
|
+
mcp.addTool({
|
|
32
|
+
name: 'complexTool',
|
|
33
|
+
description: 'Complex tool with nested objects',
|
|
34
|
+
handler: ({ user }) => ({
|
|
35
|
+
greeting: `Hello, ${user.name}!`,
|
|
36
|
+
metadata: { processed: true },
|
|
37
|
+
}),
|
|
38
|
+
inputSchema: z.object({
|
|
39
|
+
user: z.object({
|
|
40
|
+
name: z.string(),
|
|
41
|
+
age: z.number().optional(),
|
|
42
|
+
}),
|
|
43
|
+
}),
|
|
44
|
+
outputSchema: z.object({
|
|
45
|
+
greeting: z.string(),
|
|
46
|
+
metadata: z.object({ processed: z.boolean() }),
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// CASE 1: Negative tests (these should cause TypeScript errors)
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Negative test 1.1: Wrong input property name
|
|
53
|
+
mcp.addTool({
|
|
54
|
+
name: 'wrongInput',
|
|
55
|
+
description: 'Wrong input',
|
|
56
|
+
// @ts-expect-error
|
|
57
|
+
// Incorrect input: Handler expects 'value' but schema has 'number'
|
|
58
|
+
handler: ({ value }) => ({ result: value * 2 }),
|
|
59
|
+
inputSchema: z.object({ number: z.number() }),
|
|
60
|
+
outputSchema: z.object({ result: z.number() }),
|
|
61
|
+
});
|
|
62
|
+
// Negative test 1.2: Wrong output property type
|
|
63
|
+
mcp.addTool({
|
|
64
|
+
name: 'wrongOutput',
|
|
65
|
+
description: 'Wrong output',
|
|
66
|
+
// @ts-expect-error
|
|
67
|
+
// Incorrect output: Handler returns 'string' but schema has 'number'
|
|
68
|
+
handler: ({ value }) => ({ result: String(value) }),
|
|
69
|
+
inputSchema: z.object({ value: z.number() }),
|
|
70
|
+
outputSchema: z.object({ result: z.number() }),
|
|
71
|
+
});
|
|
72
|
+
// Negative test 1.3: Missing required output property
|
|
73
|
+
mcp.addTool({
|
|
74
|
+
name: 'missingOutput',
|
|
75
|
+
description: 'Missing output property',
|
|
76
|
+
// @ts-expect-error
|
|
77
|
+
// Incorrect output: Handler returns 'result' but schema has 'result' and 'extra'
|
|
78
|
+
handler: ({ value }) => ({ result: value * 2 }),
|
|
79
|
+
inputSchema: z.object({ value: z.number() }),
|
|
80
|
+
outputSchema: z.object({ result: z.number(), extra: z.string() }),
|
|
81
|
+
});
|
|
82
|
+
// ============================================================================
|
|
83
|
+
// CASE 2: Zod output only (no input - getter tools)
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Positive test 2.1: Simple getter
|
|
86
|
+
mcp.addTool({
|
|
87
|
+
name: 'getCount',
|
|
88
|
+
description: 'Get current count',
|
|
89
|
+
handler: () => ({ count: 42 }),
|
|
90
|
+
outputSchema: z.object({ count: z.number() }),
|
|
91
|
+
});
|
|
92
|
+
// Positive test 2.2: Async getter
|
|
93
|
+
mcp.addTool({
|
|
94
|
+
name: 'asyncGetState',
|
|
95
|
+
description: 'Get current state asynchronously',
|
|
96
|
+
handler: async () => ({ status: 'active', timestamp: Date.now() }),
|
|
97
|
+
outputSchema: z.object({ status: z.string(), timestamp: z.number() }),
|
|
98
|
+
});
|
|
99
|
+
// Positive test 2.3: Array output
|
|
100
|
+
mcp.addTool({
|
|
101
|
+
name: 'getItems',
|
|
102
|
+
description: 'Get list of items',
|
|
103
|
+
handler: () => ['item1', 'item2'],
|
|
104
|
+
outputSchema: z.array(z.string()),
|
|
105
|
+
});
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// CASE 2: Negative tests
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Negative test 2.1: Wrong return type
|
|
110
|
+
// @ts-expect-error - No overload matches (wrong property name in output)
|
|
111
|
+
mcp.addTool({
|
|
112
|
+
name: 'wrongReturn',
|
|
113
|
+
description: 'Wrong return type',
|
|
114
|
+
handler: () => ({ value: 42 }),
|
|
115
|
+
outputSchema: z.object({ count: z.number() }),
|
|
116
|
+
});
|
|
117
|
+
// Negative test 2.2: Handler has parameter but shouldn't
|
|
118
|
+
mcp.addTool({
|
|
119
|
+
name: 'unexpectedParam',
|
|
120
|
+
description: 'Unexpected parameter',
|
|
121
|
+
// @ts-expect-error
|
|
122
|
+
// Incorrect input: Handler has unexpected parameter
|
|
123
|
+
handler: (count) => ({ count }),
|
|
124
|
+
outputSchema: z.object({ count: z.number() }),
|
|
125
|
+
});
|
|
126
|
+
// Negative test 2.3: Wrong array element type
|
|
127
|
+
mcp.addTool({
|
|
128
|
+
name: 'wrongArrayType',
|
|
129
|
+
description: 'Wrong array element type',
|
|
130
|
+
handler: () => [1, 2, 3],
|
|
131
|
+
// @ts-expect-error
|
|
132
|
+
// Incorrect output: Handler returns number[] but schema has string[]
|
|
133
|
+
outputSchema: z.array(z.string()),
|
|
134
|
+
});
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// CASE 3: Zod input only (no output - void return)
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Positive test 3.1: Setter that returns void
|
|
139
|
+
mcp.addTool({
|
|
140
|
+
name: 'setCount',
|
|
141
|
+
description: 'Set count value',
|
|
142
|
+
handler: ({ count }) => {
|
|
143
|
+
// Set the count, no return value
|
|
144
|
+
console.log(count);
|
|
145
|
+
},
|
|
146
|
+
inputSchema: z.object({ count: z.number() }),
|
|
147
|
+
});
|
|
148
|
+
// Positive test 3.2: Complex input, void return
|
|
149
|
+
mcp.addTool({
|
|
150
|
+
name: 'updateUser',
|
|
151
|
+
description: 'Update user data',
|
|
152
|
+
handler: ({ name, email }) => {
|
|
153
|
+
console.log(name, email);
|
|
154
|
+
},
|
|
155
|
+
inputSchema: z.object({ name: z.string(), email: z.string() }),
|
|
156
|
+
});
|
|
157
|
+
// Positive test 3.3: Nested input, void return
|
|
158
|
+
mcp.addTool({
|
|
159
|
+
name: 'saveConfig',
|
|
160
|
+
description: 'Save configuration',
|
|
161
|
+
handler: ({ config }) => {
|
|
162
|
+
console.log(config.theme, config.fontSize);
|
|
163
|
+
},
|
|
164
|
+
inputSchema: z.object({
|
|
165
|
+
config: z.object({
|
|
166
|
+
theme: z.enum(['light', 'dark']),
|
|
167
|
+
fontSize: z.number(),
|
|
168
|
+
}),
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
// ============================================================================
|
|
172
|
+
// CASE 3: Negative tests
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// Negative test 3.1: Wrong input property
|
|
175
|
+
mcp.addTool({
|
|
176
|
+
name: 'wrongInputProp',
|
|
177
|
+
description: 'Wrong input property',
|
|
178
|
+
// @ts-expect-error
|
|
179
|
+
// Handler expects 'count' but schema has 'value'
|
|
180
|
+
handler: ({ count }) => { console.log(count); },
|
|
181
|
+
inputSchema: z.object({ value: z.number() }),
|
|
182
|
+
});
|
|
183
|
+
// USERR: I think this is a an impossible and wrong test. We cannot enforce the usage of inputs.
|
|
184
|
+
// Negative test 3.2: Missing input parameter
|
|
185
|
+
// mcp.addTool({
|
|
186
|
+
// name: 'missingParam',
|
|
187
|
+
// description: 'Missing parameter',
|
|
188
|
+
// // @ts-expect-error
|
|
189
|
+
// // Handler must accept input parameter when inputSchema is provided
|
|
190
|
+
// handler: () => { console.log('no input'); },
|
|
191
|
+
// inputSchema: z.object({ count: z.number() }),
|
|
192
|
+
// });
|
|
193
|
+
// Negative test 3.3: Wrong input type access
|
|
194
|
+
mcp.addTool({
|
|
195
|
+
name: 'wrongTypeAccess',
|
|
196
|
+
description: 'Wrong type access',
|
|
197
|
+
// @ts-expect-error
|
|
198
|
+
// Trying to access string method on number
|
|
199
|
+
handler: ({ count }) => { console.log(count.toUpperCase()); },
|
|
200
|
+
inputSchema: z.object({ count: z.number() }),
|
|
201
|
+
});
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// CASE 4, 5, 6: JSON Schema (runtime validation only - no compile-time safety)
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// These tests verify that JSON Schema overloads accept the types,
|
|
206
|
+
// but don't provide compile-time type checking. Any type mismatches
|
|
207
|
+
// would only be caught at runtime.
|
|
208
|
+
// Test 4: JSON Schema input + output
|
|
209
|
+
mcp.addTool({
|
|
210
|
+
name: 'jsonSchemaInputOutput',
|
|
211
|
+
description: 'JSON Schema input and output',
|
|
212
|
+
handler: (input) => {
|
|
213
|
+
// input is unknown, no type safety
|
|
214
|
+
return { result: input.value * 2 };
|
|
215
|
+
},
|
|
216
|
+
inputSchema: { type: 'object', properties: { value: { type: 'number' } } },
|
|
217
|
+
outputSchema: { type: 'object', properties: { result: { type: 'number' } } },
|
|
218
|
+
});
|
|
219
|
+
// Test 5: JSON Schema output only
|
|
220
|
+
mcp.addTool({
|
|
221
|
+
name: 'jsonSchemaOutputOnly',
|
|
222
|
+
description: 'JSON Schema output only',
|
|
223
|
+
handler: () => {
|
|
224
|
+
return { count: 42 };
|
|
225
|
+
},
|
|
226
|
+
outputSchema: { type: 'object', properties: { count: { type: 'number' } } },
|
|
227
|
+
});
|
|
228
|
+
// Test 6: JSON Schema input only
|
|
229
|
+
mcp.addTool({
|
|
230
|
+
name: 'jsonSchemaInputOnly',
|
|
231
|
+
description: 'JSON Schema input only',
|
|
232
|
+
handler: (input) => {
|
|
233
|
+
console.log(input);
|
|
234
|
+
},
|
|
235
|
+
inputSchema: { type: 'object', properties: { value: { type: 'number' } } },
|
|
236
|
+
});
|
|
237
|
+
// JSON Schema tests - These should pass even with type mismatches
|
|
238
|
+
// because JSON Schema doesn't provide compile-time type safety
|
|
239
|
+
// This compiles fine even though we return a string instead of number
|
|
240
|
+
mcp.addTool({
|
|
241
|
+
name: 'jsonSchemaMismatch',
|
|
242
|
+
description: 'JSON Schema with type mismatch',
|
|
243
|
+
handler: () => {
|
|
244
|
+
return { count: 'not a number' }; // Would fail at runtime, not compile time
|
|
245
|
+
},
|
|
246
|
+
outputSchema: { type: 'object', properties: { count: { type: 'number' } } },
|
|
247
|
+
});
|
|
248
|
+
console.log('Type tests passed!');
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { SplitPlan } from '@mcp-web/decompose-zod-schema';
|
|
2
|
+
import type { ToolDefinitionZod } from '@mcp-web/types';
|
|
3
|
+
import { type z } from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for creating state tools.
|
|
6
|
+
*/
|
|
7
|
+
export interface CreateStateToolsConfig<T> {
|
|
8
|
+
/** The name of the state (used as prefix for tool names). */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Description of what this state represents. */
|
|
11
|
+
description: string;
|
|
12
|
+
/** Function to get the current state value. */
|
|
13
|
+
get: () => T;
|
|
14
|
+
/** Function to set the state value. */
|
|
15
|
+
set: (value: T) => void;
|
|
16
|
+
/** Zod schema for validating state values. */
|
|
17
|
+
schema: z.ZodType<T>;
|
|
18
|
+
/** Optional split plan for decomposing the schema into multiple setter tools. */
|
|
19
|
+
schemaSplit?: SplitPlan;
|
|
20
|
+
/** When true, generates expanded tools for arrays and records. */
|
|
21
|
+
expand?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Result type for created state tools without schemaSplit or expand.
|
|
25
|
+
* Returns a single getter and single setter.
|
|
26
|
+
*/
|
|
27
|
+
export interface CreatedStateToolsBasic<T> {
|
|
28
|
+
/** Marker to identify this as created state tools. */
|
|
29
|
+
readonly __brand: 'CreatedStateTools';
|
|
30
|
+
/** The getter tool definition. */
|
|
31
|
+
readonly getter: ToolDefinitionZod;
|
|
32
|
+
/** The setter tool definition(s). Single setter for basic mode. */
|
|
33
|
+
readonly setters: ToolDefinitionZod;
|
|
34
|
+
/** All tool definitions as an array. */
|
|
35
|
+
readonly tools: ToolDefinitionZod[];
|
|
36
|
+
/** The original config. */
|
|
37
|
+
readonly config: CreateStateToolsConfig<T>;
|
|
38
|
+
/** Whether this uses expanded/decomposed tools. */
|
|
39
|
+
readonly isExpanded: false;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Result type for created state tools with schemaSplit or expand.
|
|
43
|
+
* Returns a getter and array of setters.
|
|
44
|
+
*/
|
|
45
|
+
export interface CreatedStateToolsExpanded<T> {
|
|
46
|
+
/** Marker to identify this as created state tools. */
|
|
47
|
+
readonly __brand: 'CreatedStateTools';
|
|
48
|
+
/** The getter tool definition. */
|
|
49
|
+
readonly getter: ToolDefinitionZod;
|
|
50
|
+
/** The setter tool definition(s). Array for expanded/decomposed mode. */
|
|
51
|
+
readonly setters: ToolDefinitionZod[];
|
|
52
|
+
/** All tool definitions as an array. */
|
|
53
|
+
readonly tools: ToolDefinitionZod[];
|
|
54
|
+
/** The original config. */
|
|
55
|
+
readonly config: CreateStateToolsConfig<T>;
|
|
56
|
+
/** Whether this uses expanded/decomposed tools. */
|
|
57
|
+
readonly isExpanded: true;
|
|
58
|
+
}
|
|
59
|
+
/** Union type for created state tools. */
|
|
60
|
+
export type CreatedStateTools<T> = CreatedStateToolsBasic<T> | CreatedStateToolsExpanded<T>;
|
|
61
|
+
export declare function createStateTools<T>(config: CreateStateToolsConfig<T> & {
|
|
62
|
+
schemaSplit?: undefined;
|
|
63
|
+
expand?: false;
|
|
64
|
+
}): CreatedStateToolsBasic<T>;
|
|
65
|
+
export declare function createStateTools<T>(config: CreateStateToolsConfig<T> & {
|
|
66
|
+
schemaSplit: SplitPlan;
|
|
67
|
+
expand?: boolean;
|
|
68
|
+
}): CreatedStateToolsExpanded<T>;
|
|
69
|
+
export declare function createStateTools<T>(config: CreateStateToolsConfig<T> & {
|
|
70
|
+
schemaSplit?: SplitPlan;
|
|
71
|
+
expand: true;
|
|
72
|
+
}): CreatedStateToolsExpanded<T>;
|
|
73
|
+
/**
|
|
74
|
+
* Type guard to check if a value is CreatedStateTools.
|
|
75
|
+
*/
|
|
76
|
+
export declare function isCreatedStateTools(value: unknown): value is CreatedStateTools<unknown>;
|
|
77
|
+
//# sourceMappingURL=create-state-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-state-tools.d.ts","sourceRoot":"","sources":["../src/create-state-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAa,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxC;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC,CAAC;IACb,uCAAuC;IACvC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACxB,8CAA8C;IAC9C,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACrB,iFAAiF;IACjF,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IACpC,2BAA2B;IAC3B,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAC3C,mDAAmD;IACnD,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IACtC,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IACpC,2BAA2B;IAC3B,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAC3C,mDAAmD;IACnD,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;CAC3B;AAED,0CAA0C;AAC1C,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,sBAAsB,CAAC,CAAC,CAAC,GAAG,yBAAyB,CAAC,CAAC,CAAC,CAAC;AAG5F,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG;IACtE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;AAG9B,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG;IACtE,WAAW,EAAE,SAAS,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,yBAAyB,CAAC,CAAC,CAAC,CAAC;AAGjC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG;IACtE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,MAAM,EAAE,IAAI,CAAC;CACd,GAAG,yBAAyB,CAAC,CAAC,CAAC,CAAC;AAsLjC;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAOvF"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { decomposeSchema } from '@mcp-web/decompose-zod-schema';
|
|
2
|
+
import { ZodObject } from 'zod';
|
|
3
|
+
import { generateBasicStateTools } from './tool-generators/index.js';
|
|
4
|
+
import { generateToolsForSchema } from './tool-generators/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Creates state tool definitions without registering them.
|
|
7
|
+
*
|
|
8
|
+
* This follows the Jotai pattern of creating atoms outside React components.
|
|
9
|
+
* State tools can be defined at module scope and registered when needed.
|
|
10
|
+
*
|
|
11
|
+
* @example Basic state tools (module-level definition)
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // tools.ts
|
|
14
|
+
* import { createStateTools } from '@mcp-web/core';
|
|
15
|
+
* import { z } from 'zod';
|
|
16
|
+
* import { store, todosAtom } from './states';
|
|
17
|
+
*
|
|
18
|
+
* const TodoSchema = z.object({
|
|
19
|
+
* id: z.string(),
|
|
20
|
+
* title: z.string(),
|
|
21
|
+
* completed: z.boolean(),
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* export const todoTools = createStateTools({
|
|
25
|
+
* name: 'todos',
|
|
26
|
+
* description: 'Todo list',
|
|
27
|
+
* get: () => store.get(todosAtom),
|
|
28
|
+
* set: (value) => store.set(todosAtom, value),
|
|
29
|
+
* schema: z.array(TodoSchema),
|
|
30
|
+
* expand: true, // Generates get_todos, add_todos, set_todos, delete_todos
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example Registration options
|
|
35
|
+
* ```typescript
|
|
36
|
+
* // Option 1: Direct registration
|
|
37
|
+
* mcpWeb.addStateTools(todoTools);
|
|
38
|
+
*
|
|
39
|
+
* // Option 2: React hook (auto cleanup on unmount)
|
|
40
|
+
* function App() {
|
|
41
|
+
* useTools(todoTools);
|
|
42
|
+
* return <div>...</div>;
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example With schema decomposition
|
|
47
|
+
* ```typescript
|
|
48
|
+
* export const settingsTools = createStateTools({
|
|
49
|
+
* name: 'settings',
|
|
50
|
+
* description: 'App settings',
|
|
51
|
+
* get: () => store.get(settingsAtom),
|
|
52
|
+
* set: (value) => store.set(settingsAtom, value),
|
|
53
|
+
* schema: SettingsSchema,
|
|
54
|
+
* schemaSplit: ['theme', ['sortBy', 'sortOrder']], // Creates separate setter tools
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function createStateTools(config) {
|
|
59
|
+
const { name, description, get, set, schema, schemaSplit, expand } = config;
|
|
60
|
+
const allTools = [];
|
|
61
|
+
// Determine if we're expanding (schemaSplit or expand flag)
|
|
62
|
+
const isZodObjectSchema = schema instanceof ZodObject;
|
|
63
|
+
const shouldDecompose = schemaSplit && isZodObjectSchema;
|
|
64
|
+
// Step 1: Apply schemaSplit if provided
|
|
65
|
+
let decomposedSchemas = [];
|
|
66
|
+
if (shouldDecompose) {
|
|
67
|
+
decomposedSchemas = decomposeSchema(schema, schemaSplit);
|
|
68
|
+
}
|
|
69
|
+
// Step 2: Generate tools based on mode
|
|
70
|
+
if (expand) {
|
|
71
|
+
// Expanded mode: generate targeted tools for collections
|
|
72
|
+
if (decomposedSchemas.length > 0) {
|
|
73
|
+
// Generate expanded tools for each decomposed part
|
|
74
|
+
for (const decomposed of decomposedSchemas) {
|
|
75
|
+
const result = generateToolsForSchema({
|
|
76
|
+
name: `${name}_${decomposed.name}`,
|
|
77
|
+
description: `${decomposed.name} in ${description}`,
|
|
78
|
+
get: () => {
|
|
79
|
+
const fullState = get();
|
|
80
|
+
const extracted = {};
|
|
81
|
+
for (const path of decomposed.targetPaths) {
|
|
82
|
+
extracted[path] = fullState[path];
|
|
83
|
+
}
|
|
84
|
+
return Object.keys(extracted).length === 1
|
|
85
|
+
? extracted[decomposed.targetPaths[0]]
|
|
86
|
+
: extracted;
|
|
87
|
+
},
|
|
88
|
+
set: (value) => {
|
|
89
|
+
const current = get();
|
|
90
|
+
if (decomposed.targetPaths.length === 1) {
|
|
91
|
+
set({ ...current, [decomposed.targetPaths[0]]: value });
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
set({ ...current, ...value });
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
schema: decomposed.schema,
|
|
98
|
+
});
|
|
99
|
+
allTools.push(...result.tools);
|
|
100
|
+
// Log warnings if any
|
|
101
|
+
for (const warning of result.warnings) {
|
|
102
|
+
console.warn(warning);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// Generate expanded tools for full schema
|
|
108
|
+
const result = generateToolsForSchema({
|
|
109
|
+
name,
|
|
110
|
+
description,
|
|
111
|
+
get: get,
|
|
112
|
+
set: set,
|
|
113
|
+
schema: schema,
|
|
114
|
+
});
|
|
115
|
+
allTools.push(...result.tools);
|
|
116
|
+
// Log warnings if any
|
|
117
|
+
for (const warning of result.warnings) {
|
|
118
|
+
console.warn(warning);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// For expanded mode, first tool is getter, rest are setters
|
|
122
|
+
const getter = allTools[0];
|
|
123
|
+
const setters = allTools.slice(1);
|
|
124
|
+
return {
|
|
125
|
+
__brand: 'CreatedStateTools',
|
|
126
|
+
getter,
|
|
127
|
+
setters,
|
|
128
|
+
tools: allTools,
|
|
129
|
+
config,
|
|
130
|
+
isExpanded: true,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
else if (shouldDecompose) {
|
|
134
|
+
// Decompose mode without expand: use basic state tools with decomposition
|
|
135
|
+
const basicResult = generateBasicStateTools({
|
|
136
|
+
name,
|
|
137
|
+
description,
|
|
138
|
+
get,
|
|
139
|
+
set,
|
|
140
|
+
schema: schema,
|
|
141
|
+
schemaSplit,
|
|
142
|
+
});
|
|
143
|
+
allTools.push(basicResult.getter, ...basicResult.setters);
|
|
144
|
+
return {
|
|
145
|
+
__brand: 'CreatedStateTools',
|
|
146
|
+
getter: basicResult.getter,
|
|
147
|
+
setters: basicResult.setters,
|
|
148
|
+
tools: allTools,
|
|
149
|
+
config,
|
|
150
|
+
isExpanded: true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Basic mode: simple get/set tools
|
|
155
|
+
const basicResult = generateBasicStateTools({
|
|
156
|
+
name,
|
|
157
|
+
description,
|
|
158
|
+
get,
|
|
159
|
+
set,
|
|
160
|
+
schema: schema,
|
|
161
|
+
});
|
|
162
|
+
allTools.push(basicResult.getter, ...basicResult.setters);
|
|
163
|
+
return {
|
|
164
|
+
__brand: 'CreatedStateTools',
|
|
165
|
+
getter: basicResult.getter,
|
|
166
|
+
setters: basicResult.setters[0], // Single setter for basic mode
|
|
167
|
+
tools: allTools,
|
|
168
|
+
config,
|
|
169
|
+
isExpanded: false,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Type guard to check if a value is CreatedStateTools.
|
|
175
|
+
*/
|
|
176
|
+
export function isCreatedStateTools(value) {
|
|
177
|
+
return (typeof value === 'object' &&
|
|
178
|
+
value !== null &&
|
|
179
|
+
'__brand' in value &&
|
|
180
|
+
value.__brand === 'CreatedStateTools');
|
|
181
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ToolDefinitionZod } from '@mcp-web/types';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for creating a tool with Zod schemas.
|
|
5
|
+
*/
|
|
6
|
+
export interface CreateToolConfig<TInput extends z.ZodObject | undefined = undefined, TOutput extends z.ZodType | undefined = undefined> {
|
|
7
|
+
/** The name of the tool (must be unique). */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Description of what the tool does. */
|
|
10
|
+
description: string;
|
|
11
|
+
/** The function that handles the tool execution. */
|
|
12
|
+
handler: TInput extends z.ZodObject ? (input: z.infer<TInput>) => TOutput extends z.ZodType ? z.infer<TOutput> | Promise<z.infer<TOutput>> : void | Promise<void> : TOutput extends z.ZodType ? () => z.infer<TOutput> | Promise<z.infer<TOutput>> : () => void | Promise<void>;
|
|
13
|
+
/** Optional Zod schema for validating input. */
|
|
14
|
+
inputSchema?: TInput;
|
|
15
|
+
/** Optional Zod schema for validating output. */
|
|
16
|
+
outputSchema?: TOutput;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A created tool that can be registered with MCPWeb.
|
|
20
|
+
*
|
|
21
|
+
* Created tools are validated at creation time but not yet registered.
|
|
22
|
+
* Use `mcpWeb.addTool(createdTool)` or `useTools(createdTool)` to register.
|
|
23
|
+
*/
|
|
24
|
+
export interface CreatedTool<TInput extends z.ZodObject | undefined = undefined, TOutput extends z.ZodType | undefined = undefined> {
|
|
25
|
+
/** Marker to identify this as a created tool. */
|
|
26
|
+
readonly __brand: 'CreatedTool';
|
|
27
|
+
/** The tool definition. */
|
|
28
|
+
readonly definition: ToolDefinitionZod;
|
|
29
|
+
/** The original config for type inference. */
|
|
30
|
+
readonly config: CreateToolConfig<TInput, TOutput>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates a tool definition without registering it.
|
|
34
|
+
*
|
|
35
|
+
* Useful for:
|
|
36
|
+
* - Read-only tools (derived state, computed values)
|
|
37
|
+
* - Custom action tools that don't map directly to state
|
|
38
|
+
* - Tools that need to be conditionally registered
|
|
39
|
+
*
|
|
40
|
+
* This follows the Jotai pattern of creating atoms outside React components.
|
|
41
|
+
* Tools can be defined at module scope and registered when needed.
|
|
42
|
+
*
|
|
43
|
+
* @example Read-only derived state
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // tools.ts
|
|
46
|
+
* import { createTool } from '@mcp-web/core';
|
|
47
|
+
* import { z } from 'zod';
|
|
48
|
+
* import { activeTodosAtom } from './atoms';
|
|
49
|
+
*
|
|
50
|
+
* export const activeTodosTool = createTool({
|
|
51
|
+
* name: 'get_active_todos',
|
|
52
|
+
* description: 'Get all incomplete todos',
|
|
53
|
+
* handler: () => store.get(activeTodosAtom),
|
|
54
|
+
* outputSchema: z.array(TodoSchema),
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @example Custom action
|
|
59
|
+
* ```typescript
|
|
60
|
+
* export const createTodoTool = createTool({
|
|
61
|
+
* name: 'create_todo',
|
|
62
|
+
* description: 'Create a new todo',
|
|
63
|
+
* handler: ({ title }) => {
|
|
64
|
+
* const todo = { id: crypto.randomUUID(), title, completed: false };
|
|
65
|
+
* todos.push(todo);
|
|
66
|
+
* return todo;
|
|
67
|
+
* },
|
|
68
|
+
* inputSchema: z.object({ title: z.string() }),
|
|
69
|
+
* outputSchema: TodoSchema,
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example Registration options
|
|
74
|
+
* ```typescript
|
|
75
|
+
* // Option 1: Direct registration
|
|
76
|
+
* mcpWeb.addTool(activeTodosTool);
|
|
77
|
+
*
|
|
78
|
+
* // Option 2: React hook (auto cleanup on unmount)
|
|
79
|
+
* function App() {
|
|
80
|
+
* useTools(activeTodosTool);
|
|
81
|
+
* return <div>...</div>;
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare function createTool<TInput extends z.ZodObject | undefined = undefined, TOutput extends z.ZodType | undefined = undefined>(config: CreateToolConfig<TInput, TOutput>): CreatedTool<TInput, TOutput>;
|
|
86
|
+
/**
|
|
87
|
+
* Type guard to check if a value is a CreatedTool.
|
|
88
|
+
*/
|
|
89
|
+
export declare function isCreatedTool(value: unknown): value is CreatedTool;
|
|
90
|
+
//# sourceMappingURL=create-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-tool.d.ts","sourceRoot":"","sources":["../src/create-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAC/B,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,SAAS,GAAG,SAAS,EAClD,OAAO,SAAS,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,SAAS;IAEjD,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,OAAO,EAAE,MAAM,SAAS,CAAC,CAAC,SAAS,GAC/B,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,OAAO,SAAS,CAAC,CAAC,OAAO,GACnD,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAC5C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACtB,OAAO,SAAS,CAAC,CAAC,OAAO,GACvB,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAClD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW,CAC1B,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,SAAS,GAAG,SAAS,EAClD,OAAO,SAAS,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,SAAS;IAEjD,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,2BAA2B;IAC3B,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAgB,UAAU,CACxB,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,SAAS,GAAG,SAAS,EAClD,OAAO,SAAS,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,SAAS,EACjD,MAAM,EAAE,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAOlE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ToolDefinitionSchema } from '@mcp-web/types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a tool definition without registering it.
|
|
4
|
+
*
|
|
5
|
+
* Useful for:
|
|
6
|
+
* - Read-only tools (derived state, computed values)
|
|
7
|
+
* - Custom action tools that don't map directly to state
|
|
8
|
+
* - Tools that need to be conditionally registered
|
|
9
|
+
*
|
|
10
|
+
* This follows the Jotai pattern of creating atoms outside React components.
|
|
11
|
+
* Tools can be defined at module scope and registered when needed.
|
|
12
|
+
*
|
|
13
|
+
* @example Read-only derived state
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // tools.ts
|
|
16
|
+
* import { createTool } from '@mcp-web/core';
|
|
17
|
+
* import { z } from 'zod';
|
|
18
|
+
* import { activeTodosAtom } from './atoms';
|
|
19
|
+
*
|
|
20
|
+
* export const activeTodosTool = createTool({
|
|
21
|
+
* name: 'get_active_todos',
|
|
22
|
+
* description: 'Get all incomplete todos',
|
|
23
|
+
* handler: () => store.get(activeTodosAtom),
|
|
24
|
+
* outputSchema: z.array(TodoSchema),
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example Custom action
|
|
29
|
+
* ```typescript
|
|
30
|
+
* export const createTodoTool = createTool({
|
|
31
|
+
* name: 'create_todo',
|
|
32
|
+
* description: 'Create a new todo',
|
|
33
|
+
* handler: ({ title }) => {
|
|
34
|
+
* const todo = { id: crypto.randomUUID(), title, completed: false };
|
|
35
|
+
* todos.push(todo);
|
|
36
|
+
* return todo;
|
|
37
|
+
* },
|
|
38
|
+
* inputSchema: z.object({ title: z.string() }),
|
|
39
|
+
* outputSchema: TodoSchema,
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example Registration options
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // Option 1: Direct registration
|
|
46
|
+
* mcpWeb.addTool(activeTodosTool);
|
|
47
|
+
*
|
|
48
|
+
* // Option 2: React hook (auto cleanup on unmount)
|
|
49
|
+
* function App() {
|
|
50
|
+
* useTools(activeTodosTool);
|
|
51
|
+
* return <div>...</div>;
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function createTool(config) {
|
|
56
|
+
// Validate at creation time
|
|
57
|
+
const validationResult = ToolDefinitionSchema.safeParse(config);
|
|
58
|
+
if (!validationResult.success) {
|
|
59
|
+
throw new Error(`Invalid tool definition: ${validationResult.error.message}`);
|
|
60
|
+
}
|
|
61
|
+
const definition = {
|
|
62
|
+
name: config.name,
|
|
63
|
+
description: config.description,
|
|
64
|
+
handler: config.handler,
|
|
65
|
+
inputSchema: config.inputSchema,
|
|
66
|
+
outputSchema: config.outputSchema,
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
__brand: 'CreatedTool',
|
|
70
|
+
definition,
|
|
71
|
+
config,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Type guard to check if a value is a CreatedTool.
|
|
76
|
+
*/
|
|
77
|
+
export function isCreatedTool(value) {
|
|
78
|
+
return (typeof value === 'object' &&
|
|
79
|
+
value !== null &&
|
|
80
|
+
'__brand' in value &&
|
|
81
|
+
value.__brand === 'CreatedTool');
|
|
82
|
+
}
|