Skip to content

fix(mcp): default inputSchema to {type:"object"} for registered tools in tools/list - #529

Merged
jpr5 merged 3 commits into
CopilotKit:mainfrom
vishalhabib99:fix/mcp-tools-list-default-input-schema
Oct 12, 2026
Merged

jpr5 merged 3 commits into
CopilotKit:mainfrom
vishalhabib99:fix/mcp-tools-list-default-input-schema

Conversation

@vishalhabib99

Copy link
Copy Markdown
Contributor

MCPMock lists a registered tool exactly as it was registered. A tool added without inputSchema is therefore listed without one, but the MCP schema requires inputSchema on every Tool. The official clients reject the whole tools/list result, not just that tool.

Two documented paths produce such a tool:

  • The docs Quick Start: mcp.addTool({ name: "search", description: "Search the web" })
  • mcp.onToolCall("name", handler) for a name that was never added. That stores { name }.

Repro (@copilotkit/aimock 1.45.0; current main has the same handler):

const mcp = new MCPMock({ port: 38733 });
mcp.addTool({ name: "search", description: "Search", inputSchema: { type: "object", properties: { query: { type: "string" } } } });
mcp.addTool({ name: "rich", description: "rich" });
await mcp.start();
  • Python SDK 2.3.0 list_tools(): ValidationError: ... ListToolsResult tools.1.inputSchema Field required
  • TypeScript SDK 1.30.0 client.listTools(): Invalid input: expected object, received undefined at tools[1].inputSchema

So a test that points a real MCP client at the mock fails at listTools, before it reaches the tool it's testing.

Fix: in tools/list, a registered tool with no inputSchema is listed with { type: "object" }. This is the default McpFakes.listTools() already uses ("A tool with no inputSchema lists { type: "object" }"), so registered tools and fake tools now behave the same. A tool that has a schema is listed unchanged. The recorded-list path also gets the default, because it reuses the same tools array.

Tests: mcp-mock.test.ts.

  • The existing "tools/list returns registered tools" case asserted that calc is listed without inputSchema. I updated it to expect { type: "object" }.
  • I added a case covering both the addTool and onToolCall paths. Both fail on main and pass here.
  • prettier --check, eslint and pnpm typecheck pass.
  • The full pnpm test run on macOS shows the same 36 failures with and without this change, all in validate-cli*, mcp-fakes-report-route, mcp-fakes-doc-samples and mcp-fakes-redgreen. They look like they need a built CLI or other local setup.

I left out one related gap: resources/read for a resource registered without content returns no text or blob. That would be a separate PR if you want it.

… in tools/list

inputSchema is required on every Tool in a tools/list result. A tool
registered with addTool() and no schema (the docs Quick Start) or only
via onToolCall() was listed without one, and the official Python and
TypeScript SDK clients rejected the whole list. Fake tools already list
{ type: "object" } in this case.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-11T23:08:54.880074Z 5e8074c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@jpr5 jpr5 self-assigned this Oct 12, 2026
@pkg-pr-new

pkg-pr-new Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@copilotkit/aimock@529

commit: 3b13230

jpr5 added 2 commits October 11, 2026 19:17
tools/list only defaulted a missing inputSchema. A schema present without
a root type ({} or properties only) was listed as is, and the official MCP
SDK client rejected the whole list (inputSchema.type: expected "object").
Such a schema now lists with type "object" added and its other keys kept;
an explicit type is left unchanged.
Add the changelog entry for the tools/list inputSchema default and update
the MCP mock docs: registered tools without an inputSchema (addTool without
one, or onToolCall only) list { "type": "object" }, and a schema without a
type gets type "object" added.
@jpr5
jpr5 merged commit a23779e into CopilotKit:main Oct 12, 2026
29 checks passed
@jpr5 jpr5 mentioned this pull request Oct 12, 2026
jpr5 added a commit that referenced this pull request Oct 12, 2026
Patch release npm/plugin **1.45.1** from `main` at a23779e (#529
merged). aimock-pytest stays at 0.7.0 (its `pyproject.toml` version is
unchanged); only its default aimock pin moves to 1.45.1.

> **Prepared, not merged.** `publish-release.yml` runs on every push to
`main`. Merging this PR publishes npm `@copilotkit/aimock@1.45.1`, the
`v1.45.1` tag, the `v1` major-tag move, the GitHub Release, the Docker
images and the Slack announcement. Merge only when the release is
authorized.

## What's in 1.45.1

### Changed

- **OpenRouter Video: `openai/sora-2` returns the real removal
rejection** ([#520](#520)).
OpenRouter removed the model; a live submit returns `400
{"error":{"message":"Model openai/sora-2 does not exist","code":400}}`.
`POST /api/v1/videos` with `model: "openai/sora-2"` now returns that 400
even when a fixture matches, and never reaches a record-mode upstream.
The default `GET /api/v1/videos/models` listing no longer includes
`openai/sora-2`, and a fixture for it no longer adds it. Move fixtures
to a model OpenRouter still serves (for example
`bytedance/seedance-2.0`). The daily drift check now confirms the model
stays absent.

### Fixed

- **MCP `tools/list` gives every registered tool an `inputSchema`**
([#529](#529)). A tool
registered with `addTool()` without an `inputSchema`, or only via
`onToolCall()`, was listed with no `inputSchema`, and the official MCP
SDK client rejected the whole `tools/list` response. These tools are now
listed with `inputSchema: { "type": "object" }`, as fake-only tools
already were. Tools registered with an `inputSchema` are unchanged.
- **MCP `tools/list` adds `"type": "object"` to a typeless registered
schema** ([#529](#529)). A
schema such as `{}` or `{ "properties": { ... } }` was listed as is, and
the SDK client rejected it (`inputSchema.type: expected "object"`). It
is now listed with `"type": "object"` added and its other keys kept. A
schema with an explicit `type` is unchanged.

Everything else merged since v1.45.0 is test-only.

## Versions

| Surface | Before | After |
|---|---|---|
| `package.json` `version` | 1.45.0 | 1.45.1 |
| `.claude-plugin/plugin.json` `version` | 1.45.0 | 1.45.1 |
| `.claude-plugin/marketplace.json` `plugins[0].source.version` |
^1.45.0 | ^1.45.1 |
| `charts/aimock/Chart.yaml` `appVersion` (chart `version` stays 0.1.0)
| 1.45.0 | 1.45.1 |
| `docs/index.html` CLI banner | aimock v1.45.0 | aimock v1.45.1 |
| `packages/aimock-pytest/src/aimock_pytest/_version.py`
`AIMOCK_VERSION` | 1.45.0 | 1.45.1 |
| `packages/aimock-pytest/README.md` `--aimock-version` default | 1.45.0
| 1.45.1 |

Left at 1.45.0 on purpose: CHANGELOG history, and "requires 1.45.0 or
later" minimum-version notes in `README.md`, `docs/*`,
`packages/aimock-pytest/README.md` and `skills/write-fixtures/SKILL.md`,
plus the illustrative `aimockVersion` in the `docs/mcp-mock`
recorded-file example. `pnpm-lock.yaml` does not carry the root version.

`CHANGELOG.md`: `[Unreleased]` became `[1.45.1] - 2026-10-11` with an
empty `[Unreleased]` kept above it, as in prior releases.

## Local gates

- `pnpm install --frozen-lockfile`: pass
- `pnpm format:check`: pass
- `pnpm lint`: pass
- `pnpm typecheck`: pass
- `pnpm build`: pass
- `vitest run` on `npm-publish-verify-workflow`,
`unreleased-check-workflow`, `drift-scripts`, `openrouter-video-record`:
4 files, 165 tests pass
@jpr5

jpr5 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Thank you @vishalhabib99! 🙏

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants