Skip to content

Commit 22587de

Browse files
committed
test(tools): check parameter schema surfaces
Generate JSON Schema from registered tool parameters so read-only and mutating root-pick surfaces stay intentional.
1 parent cfed44f commit 22587de

2 files changed

Lines changed: 85 additions & 2 deletions

File tree

src/server/test-harness.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type ExecuteFn = (args: AnyRecord, context: AnyRecord) => Promise<string | AnyRe
3232

3333
interface CapturedTool {
3434
name: string;
35+
parameters?: unknown;
3536
execute: ExecuteFn;
3637
}
3738

@@ -83,8 +84,11 @@ function makeFakeServer(): { server: FastMCP; tools: CapturedTool[] } {
8384
const tools: CapturedTool[] = [];
8485
const server = {
8586
sessions: [],
86-
addTool(tool: { name: string; execute: ExecuteFn }) {
87-
tools.push({ name: tool.name, execute: tool.execute });
87+
addTool(tool: { name: string; parameters?: unknown; execute: ExecuteFn }) {
88+
tools.push({ name: tool.name, parameters: tool.parameters, execute: tool.execute });
89+
},
90+
addResource() {
91+
// Resource tests do not need transport behavior; tool-surface tests only need registration to complete.
8892
},
8993
} as unknown as FastMCP;
9094
return { server, tools };
@@ -120,3 +124,9 @@ export function captureTool(
120124
return JSON.stringify(result);
121125
};
122126
}
127+
128+
export function captureToolDefinitions(register: (server: FastMCP) => void): CapturedTool[] {
129+
const { server, tools } = makeFakeServer();
130+
register(server);
131+
return tools;
132+
}

src/server/tool-parameters.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Tool parameter surface checks.
3+
*/
4+
5+
import { describe, expect, test } from "bun:test";
6+
import { z } from "zod";
7+
import { captureToolDefinitions } from "./test-harness.js";
8+
import { registerRethunkGitTools } from "./tools.js";
9+
10+
const READ_ONLY_ABSOLUTE_ROOT_TOOLS = [
11+
"git_status",
12+
"git_inventory",
13+
"git_parity",
14+
"list_presets",
15+
"git_log",
16+
"git_diff_summary",
17+
];
18+
19+
const MUTATING_TOOLS = [
20+
"batch_commit",
21+
"git_push",
22+
"git_merge",
23+
"git_cherry_pick",
24+
"git_reset_soft",
25+
"git_worktree_add",
26+
"git_worktree_remove",
27+
];
28+
29+
const ALL_TOOLS = [...READ_ONLY_ABSOLUTE_ROOT_TOOLS, "git_worktree_list", ...MUTATING_TOOLS];
30+
31+
type JsonObjectSchema = { properties?: Record<string, unknown>; required?: string[] };
32+
33+
function toolSchemas(): Map<string, JsonObjectSchema> {
34+
return new Map(
35+
captureToolDefinitions(registerRethunkGitTools).map((tool) => [
36+
tool.name,
37+
z.toJSONSchema(tool.parameters as z.ZodType) as JsonObjectSchema,
38+
]),
39+
);
40+
}
41+
42+
describe("tool parameter schemas", () => {
43+
test("generates JSON Schema for every registered tool", () => {
44+
const schemas = toolSchemas();
45+
expect([...schemas.keys()].sort()).toEqual([...ALL_TOOLS].sort());
46+
for (const [name, schema] of schemas) {
47+
expect(name.length).toBeGreaterThan(0);
48+
expect(schema.properties).toBeDefined();
49+
}
50+
});
51+
52+
test("read-only batch tools expose absoluteGitRoots", () => {
53+
const schemas = toolSchemas();
54+
for (const name of READ_ONLY_ABSOLUTE_ROOT_TOOLS) {
55+
expect(schemas.get(name)?.properties).toHaveProperty("absoluteGitRoots");
56+
}
57+
});
58+
59+
test("mutating tools do not expose absoluteGitRoots", () => {
60+
const schemas = toolSchemas();
61+
for (const name of MUTATING_TOOLS) {
62+
expect(schemas.get(name)?.properties).not.toHaveProperty("absoluteGitRoots");
63+
}
64+
});
65+
66+
test("standalone git_push exposes push-only parameters", () => {
67+
const schema = toolSchemas().get("git_push");
68+
expect(schema?.properties).toHaveProperty("remote");
69+
expect(schema?.properties).toHaveProperty("branch");
70+
expect(schema?.properties).toHaveProperty("setUpstream");
71+
expect(schema?.properties).not.toHaveProperty("commits");
72+
});
73+
});

0 commit comments

Comments
 (0)