@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.
Files changed (102) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +253 -0
  3. package/dist/addTool.typetest.d.ts +11 -0
  4. package/dist/addTool.typetest.d.ts.map +1 -0
  5. package/dist/addTool.typetest.js +248 -0
  6. package/dist/create-state-tools.d.ts +77 -0
  7. package/dist/create-state-tools.d.ts.map +1 -0
  8. package/dist/create-state-tools.js +181 -0
  9. package/dist/create-tool.d.ts +90 -0
  10. package/dist/create-tool.d.ts.map +1 -0
  11. package/dist/create-tool.js +82 -0
  12. package/dist/expanded-schema-tools/generate-fixed-shape-tools.d.ts +8 -0
  13. package/dist/expanded-schema-tools/generate-fixed-shape-tools.d.ts.map +1 -0
  14. package/dist/expanded-schema-tools/generate-fixed-shape-tools.js +53 -0
  15. package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.d.ts +2 -0
  16. package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.d.ts.map +1 -0
  17. package/dist/expanded-schema-tools/generate-fixed-shape-tools.test.js +331 -0
  18. package/dist/expanded-schema-tools/index.d.ts +4 -0
  19. package/dist/expanded-schema-tools/index.d.ts.map +1 -0
  20. package/dist/expanded-schema-tools/index.js +2 -0
  21. package/dist/expanded-schema-tools/integration.test.d.ts +2 -0
  22. package/dist/expanded-schema-tools/integration.test.d.ts.map +1 -0
  23. package/dist/expanded-schema-tools/integration.test.js +599 -0
  24. package/dist/expanded-schema-tools/schema-analysis.d.ts +18 -0
  25. package/dist/expanded-schema-tools/schema-analysis.d.ts.map +1 -0
  26. package/dist/expanded-schema-tools/schema-analysis.js +142 -0
  27. package/dist/expanded-schema-tools/schema-analysis.test.d.ts +2 -0
  28. package/dist/expanded-schema-tools/schema-analysis.test.d.ts.map +1 -0
  29. package/dist/expanded-schema-tools/schema-analysis.test.js +314 -0
  30. package/dist/expanded-schema-tools/schema-helpers.d.ts +69 -0
  31. package/dist/expanded-schema-tools/schema-helpers.d.ts.map +1 -0
  32. package/dist/expanded-schema-tools/schema-helpers.js +139 -0
  33. package/dist/expanded-schema-tools/schema-helpers.test.d.ts +2 -0
  34. package/dist/expanded-schema-tools/schema-helpers.test.d.ts.map +1 -0
  35. package/dist/expanded-schema-tools/schema-helpers.test.js +223 -0
  36. package/dist/expanded-schema-tools/tool-generator.d.ts +10 -0
  37. package/dist/expanded-schema-tools/tool-generator.d.ts.map +1 -0
  38. package/dist/expanded-schema-tools/tool-generator.js +430 -0
  39. package/dist/expanded-schema-tools/tool-generator.test.d.ts +2 -0
  40. package/dist/expanded-schema-tools/tool-generator.test.d.ts.map +1 -0
  41. package/dist/expanded-schema-tools/tool-generator.test.js +689 -0
  42. package/dist/expanded-schema-tools/types.d.ts +26 -0
  43. package/dist/expanded-schema-tools/types.d.ts.map +1 -0
  44. package/dist/expanded-schema-tools/types.js +1 -0
  45. package/dist/expanded-schema-tools/utils.d.ts +16 -0
  46. package/dist/expanded-schema-tools/utils.d.ts.map +1 -0
  47. package/dist/expanded-schema-tools/utils.js +35 -0
  48. package/dist/expanded-schema-tools/utils.test.d.ts +2 -0
  49. package/dist/expanded-schema-tools/utils.test.d.ts.map +1 -0
  50. package/dist/expanded-schema-tools/utils.test.js +169 -0
  51. package/dist/group-state.d.ts +60 -0
  52. package/dist/group-state.d.ts.map +1 -0
  53. package/dist/group-state.js +54 -0
  54. package/dist/index.d.ts +14 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +13 -0
  57. package/dist/query.d.ts +104 -0
  58. package/dist/query.d.ts.map +1 -0
  59. package/dist/query.js +128 -0
  60. package/dist/schema-helpers.d.ts +69 -0
  61. package/dist/schema-helpers.d.ts.map +1 -0
  62. package/dist/schema-helpers.js +139 -0
  63. package/dist/schemas.d.ts +140 -0
  64. package/dist/schemas.d.ts.map +1 -0
  65. package/dist/schemas.js +70 -0
  66. package/dist/tool-generators/generate-basic-state-tools.d.ts +23 -0
  67. package/dist/tool-generators/generate-basic-state-tools.d.ts.map +1 -0
  68. package/dist/tool-generators/generate-basic-state-tools.js +95 -0
  69. package/dist/tool-generators/generate-fixed-shape-tools.d.ts +8 -0
  70. package/dist/tool-generators/generate-fixed-shape-tools.d.ts.map +1 -0
  71. package/dist/tool-generators/generate-fixed-shape-tools.js +53 -0
  72. package/dist/tool-generators/index.d.ts +6 -0
  73. package/dist/tool-generators/index.d.ts.map +1 -0
  74. package/dist/tool-generators/index.js +3 -0
  75. package/dist/tool-generators/schema-analysis.d.ts +18 -0
  76. package/dist/tool-generators/schema-analysis.d.ts.map +1 -0
  77. package/dist/tool-generators/schema-analysis.js +142 -0
  78. package/dist/tool-generators/schema-helpers.d.ts +87 -0
  79. package/dist/tool-generators/schema-helpers.d.ts.map +1 -0
  80. package/dist/tool-generators/schema-helpers.js +157 -0
  81. package/dist/tool-generators/tool-generator.d.ts +11 -0
  82. package/dist/tool-generators/tool-generator.d.ts.map +1 -0
  83. package/dist/tool-generators/tool-generator.js +437 -0
  84. package/dist/tool-generators/types.d.ts +26 -0
  85. package/dist/tool-generators/types.d.ts.map +1 -0
  86. package/dist/tool-generators/types.js +1 -0
  87. package/dist/tool-generators/utils.d.ts +16 -0
  88. package/dist/tool-generators/utils.d.ts.map +1 -0
  89. package/dist/tool-generators/utils.js +35 -0
  90. package/dist/types.d.ts +17 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +1 -0
  93. package/dist/utils.d.ts +31 -0
  94. package/dist/utils.d.ts.map +1 -0
  95. package/dist/utils.js +108 -0
  96. package/dist/web.d.ts +680 -0
  97. package/dist/web.d.ts.map +1 -0
  98. package/dist/web.js +1312 -0
  99. package/dist/zod-to-tools.d.ts +49 -0
  100. package/dist/zod-to-tools.d.ts.map +1 -0
  101. package/dist/zod-to-tools.js +623 -0
  102. 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
+ }