fix(mcp): relax impact line schema minimum to 0 for adapter compatibility (#2279)

Strict MCP clients/agents validate against the advertised input schema and
reject a request before sending it. With `line` declaring `minimum: 1`, a
client that materializes the omitted optional `line` as `0` rejects a
perfectly valid callgraph impact call client-side — so the backend tolerance
added in the previous commit never gets a chance to run.

Lower the advertised `line.minimum` to 0 and document that 0 (or omission)
means "no statement anchor" while mode:'pdg' still requires a positive line.
The advertised schema is advisory (the backend self-validates and is the real
gate), so this cannot loosen any enforced contract — it only stops strict
clients from pre-rejecting `line: 0`. Negative lines are still rejected at the
client boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gergo Magyar 2026-06-23 17:25:16 +00:00
parent 4279937679
commit dc56ff94c2
2 changed files with 15 additions and 5 deletions

View file

@ -469,9 +469,14 @@ SERVICE: optional monorepo path prefix (case-sensitive path segments). When "rep
},
line: {
type: 'integer',
minimum: 1,
// `minimum: 0` (not 1) so strict client/agent adapters that materialize
// an omitted optional numeric field as `0` do not reject the request
// before sending (#2279). A positive line is still required for a real
// pdg anchor — the backend enforces that — but `0`/omitted means "no
// statement anchor" and is tolerated on the callgraph path.
minimum: 0,
description:
"1-based source line — PDG statement anchor (mode:'pdg'). Seeds affectedStatements on the statement at this line; inter-procedural symbols are still returned in interproceduralByDepth/pdgInterprocedural and the compatibility byDepth bucket.",
"1-based source line — PDG statement anchor (mode:'pdg'). Seeds affectedStatements on the statement at this line; inter-procedural symbols are still returned in interproceduralByDepth/pdgInterprocedural and the compatibility byDepth bucket. 0 (or omitted) means no statement anchor; mode:'pdg' requires a positive line.",
},
file_path: {
type: 'string',

View file

@ -134,17 +134,22 @@ describe('GITNEXUS_TOOLS', () => {
expect(impactTool.inputSchema.required).toContain('direction');
});
it('impact tool advertises the PDG-only `line` statement anchor (integer, min 1, not required)', () => {
it('impact tool advertises the PDG-only `line` statement anchor (integer, min 0, not required)', () => {
const impactTool = GITNEXUS_TOOLS.find((t) => t.name === 'impact')!;
const line = (impactTool.inputSchema.properties as Record<string, any>).line;
expect(line).toBeDefined();
expect(line.type).toBe('integer');
expect(line.minimum).toBe(1);
// minimum is 0 (not 1) so strict adapters that materialize an omitted
// optional numeric field as `0` are not rejected client-side (#2279); a
// positive line is enforced backend-side for a real pdg anchor.
expect(line.minimum).toBe(0);
// Statement-anchored slice is optional — never required.
expect(impactTool.inputSchema.required).not.toContain('line');
// The description names the mode:'pdg' statement-anchor semantics.
// The description names the mode:'pdg' statement-anchor semantics and the
// 0/omitted = no-anchor compatibility convention.
expect(line.description).toMatch(/statement anchor/i);
expect(line.description).toMatch(/pdg/i);
expect(line.description).toMatch(/0 \(or omitted\) means no statement anchor/i);
// The top-level description mentions the statement-anchored slice and result shape.
expect(impactTool.description).toMatch(/statement-anchored|STATEMENT-ANCHORED/);
expect(impactTool.description).toContain('affectedStatements');