Skip to content

fix(core): normalize MCP tool schemas to ensure type:object at root - #28839

Open
Xsidz wants to merge 1 commit into
google-gemini:mainfrom
Xsidz:fix/mcp-schema-normalize
Open

fix(core): normalize MCP tool schemas to ensure type:object at root#28839
Xsidz wants to merge 1 commit into
google-gemini:mainfrom
Xsidz:fix/mcp-schema-normalize

Conversation

@Xsidz

@Xsidz Xsidz commented Aug 16, 2026

Copy link
Copy Markdown

Summary

  • MCP servers sometimes advertise tool schemas with missing type, a non-object type, or a completely malformed structure. Strict JSON Schema validators (Vertex AI in strict mode, or any provider that validates schemas before forwarding) reject these with tools.N.custom.input_schema.type: Input should be 'object'.
  • Added normalizeToolSchema() in mcp-tool.ts and applied it at both instantiation points.

Details

normalizeToolSchema(schema):

  • Valid {type: 'object', ...} schema → returned as-is (no allocation)
  • Object missing type or with wrong type → spreads existing fields, injects type: 'object'
  • null/undefined/array/primitive → returns {type: 'object', properties: {}}

Applied at:

  1. mcp-client.ts line 1374: toolDef.inputSchema ?? {…}normalizeToolSchema(toolDef.inputSchema)
  2. tool-registry.ts lines 500–505: replaced the existing object/array guard → normalizeToolSchema(func.parametersJsonSchema)

Both callers already import from mcp-tool.ts so no new dependency edge.

Related Issues

Fixes #23382

How to Validate

npm test -w @google/gemini-cli-core -- src/tools/mcp-tool.test.ts

Pre-Merge Checklist

  • Added/updated tests — 4 new cases for normalizeToolSchema
  • Noted breaking changes — none; previously-malformed schemas now always get type: 'object' injected

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 77
  • Additions: +66
  • Deletions: -11
  • Files changed: 4

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

🛑 Action Required: Evaluation Approval

Steering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged.

Maintainers:

  1. Go to the Workflow Run Summary.
  2. Click the yellow 'Review deployments' button.
  3. Select the 'eval-gate' environment and click 'Approve'.

Once approved, the evaluation results will be posted here automatically.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses compatibility issues where MCP servers provide malformed or non-object tool schemas that fail strict JSON Schema validation. By centralizing schema normalization, the core logic now guarantees that all tool definitions conform to the expected object structure, improving reliability when interacting with strict downstream services.

Highlights

  • New Normalization Utility: Introduced normalizeToolSchema in mcp-tool.ts to ensure all tool schemas have a root type: 'object', preventing validation errors with strict providers like Vertex AI.
  • Implementation: Integrated the new utility into mcp-client.ts and tool-registry.ts to standardize incoming tool definitions.
  • Testing: Added comprehensive unit tests in mcp-tool.test.ts covering various edge cases, including missing types, incorrect types, and primitive inputs.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a helper function normalizeToolSchema to standardize tool parameter schemas, ensuring they have type: 'object' at the root as required by strict JSON Schema validators like Vertex AI. This helper is integrated into mcp-client.ts and tool-registry.ts, replacing inline fallback logic, and unit tests are added to verify its behavior. The review feedback suggests enhancing normalizeToolSchema to guarantee that a valid properties object is always present when type is 'object' (even if the input schema lacks it or has a non-object type) and updating the unit tests accordingly.

Comment on lines +161 to +168
export function normalizeToolSchema(schema: unknown): Record<string, unknown> {
if (schema && typeof schema === 'object' && !Array.isArray(schema)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const s = schema as Record<string, unknown>;
return s['type'] === 'object' ? s : { ...s, type: 'object' };
}
return { type: 'object', properties: {} };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To ensure robust schema normalization and prevent downstream validation or runtime crashes, normalizeToolSchema should guarantee that properties is always a valid object when type is 'object'. Currently, if the input schema is {} or { type: 'string' }, the returned schema will lack a properties field, which is inconsistent with the fallback behavior for null/undefined and can cause strict JSON Schema validators (like Vertex AI) or other parts of the codebase to fail.

We should also ensure that if properties is present but malformed (e.g., null or an array), it is normalized to an empty object {}.

export function normalizeToolSchema(schema: unknown): Record<string, unknown> {
  if (schema && typeof schema === 'object' && !Array.isArray(schema)) {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
    const s = schema as Record<string, unknown>;
    const hasValidProperties = s['properties'] && typeof s['properties'] === 'object' && !Array.isArray(s['properties']);
    const properties = hasValidProperties ? s['properties'] as Record<string, unknown> : {};
    if (s['type'] === 'object' && hasValidProperties) {
      return s;
    }
    return { ...s, type: 'object', properties };
  }
  return { type: 'object', properties: {} };
}

Comment on lines +55 to +76
describe('normalizeToolSchema', () => {
it('passes through a valid {type:object} schema unchanged', () => {
const s = { type: 'object', properties: { x: { type: 'string' } } };
expect(normalizeToolSchema(s)).toBe(s);
});
it('injects type:object when type is missing', () => {
expect(normalizeToolSchema({ properties: {} })).toEqual({
properties: {},
type: 'object',
});
});
it('overrides a non-object type', () => {
expect(normalizeToolSchema({ type: 'string' })).toEqual({ type: 'object' });
});
it('returns default schema for null/undefined/array/primitive', () => {
const def = { type: 'object', properties: {} };
expect(normalizeToolSchema(null)).toEqual(def);
expect(normalizeToolSchema(undefined)).toEqual(def);
expect(normalizeToolSchema([])).toEqual(def);
expect(normalizeToolSchema('string')).toEqual(def);
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update the test cases for normalizeToolSchema to reflect the robust normalization behavior where properties is always guaranteed to be a valid object.

describe('normalizeToolSchema', () => {
  it('passes through a valid {type:object} schema unchanged', () => {
    const s = { type: 'object', properties: { x: { type: 'string' } } };
    expect(normalizeToolSchema(s)).toBe(s);
  });
  it('injects type:object when type is missing', () => {
    expect(normalizeToolSchema({ properties: {} })).toEqual({
      properties: {},
      type: 'object',
    });
  });
  it('overrides a non-object type and ensures properties is present', () => {
    expect(normalizeToolSchema({ type: 'string' })).toEqual({ type: 'object', properties: {} });
  });
  it('returns default schema for null/undefined/array/primitive', () => {
    const def = { type: 'object', properties: {} };
    expect(normalizeToolSchema(null)).toEqual(def);
    expect(normalizeToolSchema(undefined)).toEqual(def);
    expect(normalizeToolSchema([])).toEqual(def);
    expect(normalizeToolSchema('string')).toEqual(def);
  });
});

@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality labels Aug 16, 2026
Fixes google-gemini#23382

MCP servers sometimes advertise tool schemas with missing type, non-object
type, or malformed structure. Strict providers (Vertex AI strict mode) reject
these with 'Input should be object'. Added normalizeToolSchema() in mcp-tool.ts
and applied it at both call sites: DiscoveredMCPTool instantiation in
mcp-client.ts and DiscoveredTool registration in tool-registry.ts.
@Xsidz
Xsidz force-pushed the fix/mcp-schema-normalize branch from 92c19fa to c95e900 Compare August 16, 2026 13:05
@Xsidz

Xsidz commented Aug 16, 2026

Copy link
Copy Markdown
Author

Fixed in c95e900: normalizeToolSchema now also ensures properties is always a valid object — malformed or missing properties fields default to {}.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality priority/p2 Important but can be addressed in a future release. size/m A medium sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(core): MCP tool schemas with missing type:'object' cause provider rejection

1 participant