Skip to content

Commit 35822b2

Browse files
authored
feat: Added toolbox protocol (#7)
* feat: Add basic client and tool * cleanup * clean * add unit tests * increase test cov * test file cleanup * clean * small fix * Added unit tests for tool * cleanup * test cleanup * new tests * del e2e file * chore: lint (#11) * lint * lint * lint * move files to different PR * move files to different pr * lint * Update protocol.ts * remove unused deps * rename methods * lint
1 parent c8cc15f commit 35822b2

File tree

6 files changed

+552
-1
lines changed

6 files changed

+552
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*node_modules*
2+
*build*
23
*coverage*

packages/toolbox-core/jest.config.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,13 @@
33
"<rootDir>/test/*.ts"
44
],
55
"preset": "ts-jest",
6-
"testEnvironment": "node"
6+
"testEnvironment": "node",
7+
"collectCoverage": true,
8+
"coverageDirectory": "coverage",
9+
"coverageReporters": ["lcov", "text"],
10+
"collectCoverageFrom": [
11+
"src/**/*.ts",
12+
"!src/**/*.d.ts",
13+
"!src/**/index.ts"
14+
]
715
}

packages/toolbox-core/package-lock.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/toolbox-core/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,8 @@
4343
"jest": "^29.7.0",
4444
"ts-jest": "^29.3.2",
4545
"typescript": "^5.8.3"
46+
},
47+
"dependencies": {
48+
"zod": "^3.24.4"
4649
}
4750
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {z, ZodRawShape, ZodTypeAny, ZodObject} from 'zod';
16+
17+
// Define All Interfaces
18+
19+
interface BaseParameter {
20+
name: string;
21+
description: string;
22+
authSources?: string[];
23+
}
24+
25+
interface StringParameter extends BaseParameter {
26+
type: 'string';
27+
}
28+
29+
interface IntegerParameter extends BaseParameter {
30+
type: 'integer';
31+
}
32+
33+
interface FloatParameter extends BaseParameter {
34+
type: 'float';
35+
}
36+
37+
interface BooleanParameter extends BaseParameter {
38+
type: 'boolean';
39+
}
40+
41+
interface ArrayParameter extends BaseParameter {
42+
type: 'array';
43+
items: ParameterSchema; // Recursive reference to the ParameterSchema type
44+
}
45+
46+
export type ParameterSchema =
47+
| StringParameter
48+
| IntegerParameter
49+
| FloatParameter
50+
| BooleanParameter
51+
| ArrayParameter;
52+
53+
// Get all Zod schema types
54+
55+
const ZodBaseParameter = z.object({
56+
name: z.string().min(1, 'Parameter name cannot be empty'),
57+
description: z.string(),
58+
authSources: z.array(z.string()).optional(),
59+
});
60+
61+
export const ZodParameterSchema = z.lazy(() =>
62+
z.discriminatedUnion('type', [
63+
ZodBaseParameter.extend({
64+
type: z.literal('string'),
65+
}),
66+
ZodBaseParameter.extend({
67+
type: z.literal('integer'),
68+
}),
69+
ZodBaseParameter.extend({
70+
type: z.literal('float'),
71+
}),
72+
ZodBaseParameter.extend({
73+
type: z.literal('boolean'),
74+
}),
75+
ZodBaseParameter.extend({
76+
type: z.literal('array'),
77+
items: ZodParameterSchema, // Recursive reference for the item's definition
78+
}),
79+
])
80+
) as z.ZodType<ParameterSchema>;
81+
82+
export const ZodToolSchema = z.object({
83+
description: z.string().min(1, 'Tool description cannot be empty'),
84+
parameters: z.array(ZodParameterSchema),
85+
authRequired: z.array(z.string()).optional(),
86+
});
87+
88+
export const ZodManifestSchema = z.object({
89+
serverVersion: z.string().min(1, 'Server version cannot be empty'),
90+
tools: z.record(
91+
z.string().min(1, 'Tool name cannot be empty'),
92+
ZodToolSchema
93+
),
94+
});
95+
96+
/**
97+
* Recursively builds a Zod schema for a single parameter based on its TypeScript definition.
98+
* @param param The ParameterSchema (TypeScript type) to convert.
99+
* @returns A ZodTypeAny representing the schema for this parameter.
100+
*/
101+
function buildZodShapeFromParam(param: ParameterSchema): ZodTypeAny {
102+
switch (param.type) {
103+
case 'string':
104+
return z.string();
105+
case 'integer':
106+
return z.number().int();
107+
case 'float':
108+
return z.number();
109+
case 'boolean':
110+
return z.boolean();
111+
case 'array':
112+
// Recursively build the schema for array items
113+
return z.array(buildZodShapeFromParam(param.items));
114+
default: {
115+
// This ensures exhaustiveness at compile time if ParameterSchema is a discriminated union
116+
const _exhaustiveCheck: never = param;
117+
throw new Error(`Unknown parameter type: ${_exhaustiveCheck['type']}`);
118+
}
119+
}
120+
}
121+
122+
/**
123+
* Creates a ZodObject schema from an array of ParameterSchema (TypeScript types).
124+
* This combined schema is used by ToolboxTool to validate its call arguments.
125+
* @param params Array of ParameterSchema objects.
126+
* @returns A ZodObject schema.
127+
*/
128+
export function createZodSchemaFromParams(
129+
params: ParameterSchema[]
130+
): ZodObject<ZodRawShape> {
131+
const shape: ZodRawShape = {};
132+
for (const param of params) {
133+
shape[param.name] = buildZodShapeFromParam(param);
134+
}
135+
return z.object(shape).strict();
136+
}

0 commit comments

Comments
 (0)