Skip to content

Commit 5a25ffe

Browse files
committed
More fixes for PR review feedback
1 parent 28a6243 commit 5a25ffe

File tree

5 files changed

+36
-22
lines changed

5 files changed

+36
-22
lines changed

server/dist/codeql-development-mcp-server.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188612,12 +188612,13 @@ function buildEnhancedToolSchema(shape) {
188612188612
if (suggestion && !(suggestion in data) && !(suggestion in normalized)) {
188613188613
normalized[suggestion] = value;
188614188614
} else {
188615-
unknownEntries.push({ key, hint: suggestion });
188615+
const isDuplicate = !!suggestion && (suggestion in data || suggestion in normalized);
188616+
unknownEntries.push({ key, hint: suggestion, isDuplicate });
188616188617
}
188617188618
}
188618188619
}
188619-
for (const { key, hint } of unknownEntries) {
188620-
const message = hint ? `unknown property '${key}' \u2014 did you mean '${hint}'?` : `unknown property '${key}'`;
188620+
for (const { key, hint, isDuplicate } of unknownEntries) {
188621+
const message = isDuplicate && hint ? `duplicate property: both '${key}' and its canonical form '${hint}' were provided; use only '${hint}'` : hint ? `unknown property '${key}' \u2014 did you mean '${hint}'?` : `unknown property '${key}'`;
188621188622
ctx.addIssue({
188622188623
code: external_exports.ZodIssueCode.custom,
188623188624
message,

server/dist/codeql-development-mcp-server.js.map

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

server/src/lib/param-normalization.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function buildEnhancedToolSchema(
7878
.passthrough()
7979
.transform((data, ctx) => {
8080
const normalized: Record<string, unknown> = {};
81-
const unknownEntries: Array<{ key: string; hint?: string }> = [];
81+
const unknownEntries: Array<{ key: string; hint?: string; isDuplicate: boolean }> = [];
8282

8383
for (const [key, value] of Object.entries(data)) {
8484
if (knownKeys.has(key)) {
@@ -93,17 +93,20 @@ export function buildEnhancedToolSchema(
9393
} else {
9494
// Either no suggestion (truly unknown) or the canonical key is
9595
// already present. Capture the suggestion so the error message
96-
// can include a "did you mean?" hint.
97-
unknownEntries.push({ key, hint: suggestion });
96+
// can include a helpful hint.
97+
const isDuplicate = !!suggestion && (suggestion in data || suggestion in normalized);
98+
unknownEntries.push({ key, hint: suggestion, isDuplicate });
9899
}
99100
}
100101
}
101102

102-
// Report unknown / duplicate properties with "did you mean?" hints
103-
for (const { key, hint } of unknownEntries) {
104-
const message = hint
105-
? `unknown property '${key}' — did you mean '${hint}'?`
106-
: `unknown property '${key}'`;
103+
// Report unknown / duplicate properties with actionable messages
104+
for (const { key, hint, isDuplicate } of unknownEntries) {
105+
const message = isDuplicate && hint
106+
? `duplicate property: both '${key}' and its canonical form '${hint}' were provided; use only '${hint}'`
107+
: hint
108+
? `unknown property '${key}' — did you mean '${hint}'?`
109+
: `unknown property '${key}'`;
107110
ctx.addIssue({
108111
code: z.ZodIssueCode.custom,
109112
message,

server/test/src/lib/cli-tool-registry.test.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,22 @@ describe('registerCLITool', () => {
204204

205205
registerCLITool(mockServer, definition);
206206

207-
// registerTool is called with (name, config, callback)
207+
// Verify the tool is registered with the right name, description, and a
208+
// schema that normalizes camelCase keys (behaviour-level assertion instead
209+
// of inspecting Zod internals like _def.typeName).
208210
expect(mockServer.registerTool).toHaveBeenCalledWith(
209211
'test_codeql_tool',
210212
expect.objectContaining({
211213
description: 'Test CodeQL tool',
212-
inputSchema: expect.objectContaining({ _def: expect.objectContaining({ typeName: 'ZodEffects' }) }),
214+
inputSchema: expect.any(Object),
213215
}),
214216
expect.any(Function)
215217
);
218+
219+
// Verify the schema normalizes a camelCase key to its canonical form
220+
const registeredSchema = (mockServer.registerTool as ReturnType<typeof vi.fn>).mock.calls[0][1].inputSchema;
221+
const parsed = registeredSchema.safeParse({ query: 'q', database: 'db' });
222+
expect(parsed.success).toBe(true);
216223
});
217224

218225
it('should register a QLT tool correctly', () => {
@@ -229,15 +236,19 @@ describe('registerCLITool', () => {
229236

230237
registerCLITool(mockServer, definition);
231238

232-
// registerTool is called with (name, config, callback)
239+
// Behaviour-level assertion: verify registration and schema parsing
233240
expect(mockServer.registerTool).toHaveBeenCalledWith(
234241
'test_qlt_tool',
235242
expect.objectContaining({
236243
description: 'Test QLT tool',
237-
inputSchema: expect.objectContaining({ _def: expect.objectContaining({ typeName: 'ZodEffects' }) }),
244+
inputSchema: expect.any(Object),
238245
}),
239246
expect.any(Function)
240247
);
248+
249+
const registeredSchema = (mockServer.registerTool as ReturnType<typeof vi.fn>).mock.calls[0][1].inputSchema;
250+
const parsed = registeredSchema.safeParse({ language: 'java' });
251+
expect(parsed.success).toBe(true);
241252
});
242253

243254
it('should use custom result processor if provided', () => {

server/test/src/lib/param-normalization.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ describe('buildEnhancedToolSchema', () => {
179179
const messages = result.error.issues.map((i) => i.message);
180180
expect(messages).toEqual(
181181
expect.arrayContaining([
182-
expect.stringContaining("unknown property 'sourceRoot' — did you mean 'source-root'?"),
182+
expect.stringContaining("duplicate property: both 'sourceRoot' and its canonical form 'source-root' were provided; use only 'source-root'"),
183183
]),
184184
);
185185
}
@@ -216,10 +216,9 @@ describe('buildEnhancedToolSchema', () => {
216216
}
217217
});
218218

219-
it('should include a "did you mean?" hint for unrecognized properties similar to known ones', () => {
220-
// Provide a key that is NOT a direct camelCase/snake_case/kebab-case
221-
// conversion of any known key — but IS a conversion in the OTHER direction.
222-
// Example: the schema has camelCase "queryName" and the user sends "query-name"
219+
it('should normalize kebab-case to camelCase when schema uses camelCase keys', () => {
220+
// The schema has camelCase "queryName" and the user sends "query-name";
221+
// normalization should silently convert to the canonical camelCase key.
223222
const camelSchema = buildEnhancedToolSchema({
224223
queryName: z.string().optional(),
225224
});

0 commit comments

Comments
 (0)