Skip to content

feat(mcp): report ignored input keys and cap tool schema growth - #111786

Merged
trunk-io[bot] merged 6 commits into
masterfrom
feat/devex-mcp-ignored-input-keys
Oct 6, 2026
Merged

trunk-io[bot] merged 6 commits into
masterfrom
feat/devex-mcp-ignored-input-keys

Conversation

@webjunkie

@webjunkie webjunkie commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Problem

MCP tool inputs have two silent failure modes that agents hit and cannot see.

Dropped keys. Tool input schemas are Zod objects, and Zod strips unknown keys without an error.
An agent that sends a misnamed or misplaced field gets a success response while nothing was saved.
Example: insight-update reports success after an empty PATCH (PR #108314, issue #108309).
.strict() would reject these calls, but it also breaks clients that send harmless extra keys.

Oversized schemas. Claude web and desktop silently drop a tool whose serialized inputSchema reaches 16,384 characters.
In tools mode every tool is registered on its own, and 44 tool schemas are already at or over that size.
Only the exec tool has a budget test.
About 56% of properties below the top level have a description, and nothing stops that share from dropping.

Changes

Report ignored input keys

  • A successful tool call now names the input keys the schema dropped, so the agent can see its mistake. Applies to tools mode and exec mode.
  • Nothing changes when no key is dropped.
  • Declared aliases that the schema folds on purpose are not reported.
  • Tools whose root schema reshapes its input (a root .transform(), a root union, or a custom root preprocess such as read-data-schema) get no notice. A missing notice beats a wrong one, because the diff cannot tell a consumed key from a dropped one there.
  • Transforms that rename keys below the root are not detected. None exist today, and the helper's doc comment says so.
  • Key names are caller input, so control characters are masked, and the notice says how many keys were cut off at the 20 key cap.
  • The check lives in one helper, services/mcp/src/lib/ignored-input-keys.ts, used by both tool-executor.ts and exec.ts.

Response shape for an object result (paths are dotted, array items use the index):

{
  "ok": true,
  "_ignoredKeys": ["title", "query.source.serie"],
  "_ignoredKeysNote": "Ignored input keys: title, query.source.serie. These keys are not part of the tool's input schema, so the tool did not use them."
}
  • Array results are wrapped as { results, _ignoredKeys, _ignoredKeysNote }.
  • String results and formatted text results get the notice appended, because that text is what the agent reads.
  • _agentNote was not reused. It is static per tool, and a tool can already define one.

Cap tool schema growth

  • The schema snapshot test fails when the number of tool schemas at or over 16,384 characters goes above 44. The message names the largest offenders and their sizes.
  • It also fails when the count drops below 44 and says to lower the constant, so the number only goes down.
  • A second check fails when the share of described nested properties falls below 55%.
  • Failure messages say what to do: shrink the schema with exclude_params, include_params or param_overrides, or add help_text on the serializer and regenerate.
  • Size is measured after the analytics SDK's prepareToolList, the same path registration uses, so the injected context property counts.
  • The limit moves to CLAUDE_REGISTRY_INPUT_SCHEMA_CHAR_LIMIT in services/mcp/src/lib/constants.ts. The checks use counts and percentages only, so no tool names are copied into the test.
  • The exec budget test keeps its own literal 16,384. That test carries a do-not-modify note, so it was left alone.

Limits on the notice:

  • Key names are JSON-quoted in the notice and control characters are masked, so a caller-chosen key cannot read as instructions.
  • The walk stops at a fixed depth and key count, so a huge input stays cheap.
  • Connected tool calls (posthog-connection-call) validate inside their own handler and get no notice yet.

How did you test this code?

Automated only: the MCP unit tests, the schema snapshot test and test:hono pass locally, and typecheck is clean. No manual run against a live server.

Test rationale:

  • Ignored keys: cases in tests/unit/exec.test.ts extend the existing exec call tests. They cover a top-level key, a nested key, an array item key, a key with a newline, a folded alias, a clean call, and the three schema shapes that must report nothing. Those three fail if the reshaping guard is removed. One case in tests/hono/tool-executor.test.ts covers tools mode, which does not share the exec code path.
  • Schema budgets: the checks extend tests/unit/tool-schema-snapshots.test.ts, which already iterates every tool schema. They catch a new oversized schema or a drop in nested descriptions, which no existing test covers outside exec.

Release status

  • No feature flag controls this change

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code. Code written by a Claude Sonnet 5.5 (claude-sonnet-5-5) subagent, orchestrated by Claude Opus 5.5 (claude-opus-5-5).

  • Skills read: /writing-tests, /writing-code-comments, /reviewing-with-coderabbit, /writing-pr-descriptions.
  • The two changes started as separate PRs and were merged into one for the same reviewer.
  • A first version of the ratchet measured the schema before the SDK adds context. A ReviewHog finding caught it. The count moved from 42 to 44 after the fix.
  • CodeRabbit CLI pass (no --deep) on the ignored keys change, two findings, both rejected:
    • Add the notice to prebuilt tool-call payloads: not reachable. Only the exec wrapper brands a payload, and it does so after the inner handler result is wrapped.
    • Detect transforms inside object fields: valid in principle. No nested schema renames keys today, so the limit is documented instead.
  • CodeRabbit CLI pass on the ratchet change: no findings.
  • A CodeRabbit PR review nitpick (show the count of truncated keys) and a security note (control characters in key names) were fixed in a follow-up commit.
  • No MCP Analytics instrumentation changed. No $mcp_* fields were touched.

Tool input schemas are Zod objects, which strip unknown keys without an
error. A misnamed or misplaced field produced a success response while
nothing was saved (insight-update after an empty PATCH).

Making schemas strict would break clients that send harmless extras, so
the result now names the keys that were dropped. Declared aliases are
excluded because the schema folds them on purpose. The same helper runs
in tools mode and exec mode.
@webjunkie webjunkie self-assigned this Oct 5, 2026
@trunk-io

trunk-io Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@posthog

posthog Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review

🦔 PostHog Review reviewed this pull request

Nothing worth raising this time. Enjoy the moment:

Someone relaxing in a sunny garden

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

Walkthrough

The MCP tool execution paths compare caller-supplied arguments with validated arguments to identify keys that were not retained. Supported results include an ignored-key notice and, for object results, the ignored-key list. Schema reshaping beyond registered aliases suppresses key detection. Tests cover both execution paths and several input and output cases. Schema snapshot tests also check serialized input-schema size and nested-property description coverage against defined thresholds.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to 88c82

Ignored-key notices can still contain Unicode line separators, and the schema-description check can miss properties inside map values. Both are bounded concerns to address or explicitly accept before merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 88c82

The change retains input validation and existing access controls. No privilege expansion or successful attack was established, but incomplete security verification and uncertainty around diagnostic rendering prevent a minimal-risk assessment.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated new exposure is reflected key-name diagnostics in direct and exec tool responses, including existing direct response tracing. The inspected comparison does not expand tool identities, backend credentials, or handler inputs. Downstream rendering and tracing destinations were not fully assessed.

Security Findings and Attack Paths

  • observed — The canonical security evidence contains no retained findings. The ignored-key candidate remains deferred because its exact verification receipt is missing and verification context was incomplete. The inspected reflection path establishes caller-controlled text reaching results, but not successful instruction injection or a sensitive downstream action.

Trust Boundaries and Controls

  • observed — Direct dispatch still requires membership in the resolved tool catalog. Exec still resolves tools through its existing gated catalog and applies destructive-action confirmation when configured. Schema validation precedes the new reporting helper, and discarded fields are not restored to handler inputs.

Resilience and Maintainability Implications

  • observed — Detection runs before handler side effects. Result wrapping runs afterward inside existing error boundaries and preserves formatted-text and informational-response markers. The concrete context-switch handlers return plain objects; no attacker-reachable throwing result was identified to substantiate the speculative post-write wrapper failure.

Hardening Proposals

  • proposed — If the notice must remain single-line across clients, explicitly escape Unicode line and paragraph separators and verify the final rendered text. Treat this as additional display hardening, not proof of an exploitable injection.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is complete and follows the repository template. It explains the problem, user-visible changes, limitations, test coverage, release status, docs update, and agent context.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
services/mcp/src/lib/ignored-input-keys.ts (1)

80-80: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Truncation hides the true count of ignored keys.

slice(0, MAX_IGNORED_KEYS) drops keys beyond 20 with no indicator. The notice then reads as a complete list. Add the total count when truncation happens so the agent does not assume the list is complete.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 4624b219-dc1e-4809-b3d4-dacbcfacbe0f
📥 Commits

Reviewing files that changed from the base of the PR and between 849e056 and 4c8d16a.

📒 Files selected for processing (6)
  • services/mcp/src/hono/tool-executor.ts
  • services/mcp/src/lib/ignored-input-keys.ts
  • services/mcp/src/tools/cast-helpers.ts
  • services/mcp/src/tools/exec.ts
  • services/mcp/tests/hono/tool-executor.test.ts
  • services/mcp/tests/unit/exec.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 5 remain after this review.

@hosthog

hosthog Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

HostHog preview — posthog-desktop-web

The previews for this PR have been torn down and no longer serve.

@webjunkie
webjunkie marked this pull request as ready for review October 5, 2026 10:34
Copilot AI balanced review requested due to automatic review settings October 5, 2026 10:34
@parameterai

parameterai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Risk: No findings

Since the last review this pull request adds one test-file line that enables the ai-observability-offline-evaluations feature flag in the MCP tool-schema snapshot loader, so those tools join the schema ratchet checks. The line runs only in tests, touches no production surface, and introduces no security risk; all other files in the PR are unchanged since the prior review (verified per-file via git diff).

Sentinel reviewed 88c82aa · Review settings

@webjunkie
webjunkie requested review from a team October 5, 2026 10:34
@webjunkie

Copy link
Copy Markdown
Contributor Author

@codex review

Copilot stopped reviewing on behalf of webjunkie due to an error October 5, 2026 10:34

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 5, 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 ✅ Completed 2026-10-05T10:44:17.940869Z 4c8d16a Manual request
ℹ️ 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.

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

✅ Trunk lane — non-backend lane (svc:mcp)

This PR is assigned to the non-backend lane (svc:mcp). It does not run backend Python tests and may merge in parallel with PRs in other lanes.

✅ Duplication (Python) — clean

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

✅ Duplication (TypeScript) — clean

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

🚨 Comment density — 6% of added code lines are comments (23 of 367)

This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.

Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.

Files with the most added comment lines:

File Comment lines Added lines
services/mcp/src/lib/ignored-input-keys.ts 12 109
services/mcp/tests/unit/tool-schema-snapshots.test.ts 7 113
services/mcp/src/tools/cast-helpers.ts 2 19
services/mcp/src/hono/tool-executor.ts 1 6
services/mcp/src/lib/constants.ts 1 2

This check does not block merging. It updates on every push and clears when the share drops.

…rage

Claude web and desktop silently drop a tool whose serialized inputSchema
reaches 16,384 chars. Only the exec tool had a budget test, but tools
mode registers every tool on its own and 44 schemas are already over.

The count of oversized schemas can now only go down, and nested property
description coverage cannot fall below its current level. Both checks
live in the existing schema snapshot test and use counts and percentages
only, so no tool names are copied into the test.
@posthog

posthog Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog 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.

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review

PostHog Review

Found 5 should fix, 1 consider.

Comment thread services/mcp/src/hono/tool-executor.ts Resolved
Key names come from the caller and ended up verbatim in a message the
agent reads as server output, newlines included. Control characters are
now masked. The notice also states how many keys were cut off at the
cap, so a truncated list is not read as complete.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c8d16a37d

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@webjunkie webjunkie changed the title feat(mcp): report input keys the tool schema ignored feat(mcp): report ignored input keys and cap tool schema growth Oct 5, 2026

@coderabbitai coderabbitai 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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
services/mcp/tests/unit/tool-schema-snapshots.test.ts-162-164 (1)

162-164: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Count properties inside map-value schemas. When a nested object uses an object-valued additionalProperties schema, schemaChildren does not visit that value schema. Its properties therefore do not enter the coverage calculation. Traverse object-valued additionalProperties so missing descriptions in map values cannot inflate the reported percentage.


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 402c3851-d796-45ac-a5d3-82d093bb61d1
📥 Commits

Reviewing files that changed from the base of the PR and between 4c8d16a and ea26e5f.

📒 Files selected for processing (4)
  • services/mcp/src/lib/constants.ts
  • services/mcp/src/lib/ignored-input-keys.ts
  • services/mcp/tests/unit/exec.test.ts
  • services/mcp/tests/unit/tool-schema-snapshots.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 4 remain after this review.

Review follow-ups on the ignored keys notice:
- Key names are JSON-encoded in the notice so a caller-chosen name stays
  inside quotes.
- The walk has a depth cap and stops collecting at 100 keys, and in
  tools mode it runs before the handler so it cannot fail after a write.
- Own-property check instead of `in`, so keys like `constructor` that
  the schema strips are reported.
- Array results keep their formatted override and informational marker
  when wrapped, which were read from the wrapper and lost before.

@posthog posthog 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.

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review

PostHog Review

Found 1 should fix.

Comment thread services/mcp/src/hono/tool-executor.ts Resolved
@trunk-io

trunk-io Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

@posthog posthog 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.

FLASH MODE - Faster, but stupid, use regular ReviewHog for a heavy review

PostHog Review

Found 1 consider.

Comment thread services/mcp/src/lib/constants.ts Outdated
Comment on lines +46 to +50
/**
* claude.ai's connector registry silently drops a tool whose serialized `inputSchema`
* reaches this many characters (2^14). See the exec budget test in
* `tests/unit/instructions-formatter-snapshot.test.ts` for how the limit was measured.
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nitpick: I'm spotting some potentially drift-prone comments; did this PR follow our code comments skill? It says it read /writing-code-comments but if so why did it post these?

@rnegron rnegron left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

didn't check but worth confirming: this is backwards compatible right? won't break existing MCP usage?

Comments now explain only why. File pointers, tool examples and behavior
that the code already shows are removed because they go stale.

@coderabbitai coderabbitai 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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
services/mcp/src/lib/ignored-input-keys.ts-12-12 (1)

12-12: 🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟡 Minor | ⚡ Quick win

LLM Security

Reachability: External
Exploitability: Moderate
CWE: CWE-93

Mask Unicode line separators in ignored key names.

If a caller puts U+2028 or U+2029 in an ignored key, the key reaches the agent-facing notice through collectIgnoredKeys. The current mask leaves both characters intact, and JSON.stringify does not escape them. A crafted key can therefore create a visible line break followed by instruction-like text despite the existing newline fix. Mask both separators before building _ignoredKeys and the notice; verify their display in the agent-facing output. (tc39.es)

Based on learnings, “do not rely on JSON.stringify or similar escaping alone” for U+2028 and U+2029 in model-facing text.

Source: Learnings


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Enterprise
  • Run ID: 34c6b675-9002-4097-a19c-af225018cc68
📥 Commits

Reviewing files that changed from the base of the PR and between e8720ec and 88c82aa.

📒 Files selected for processing (4)
  • services/mcp/src/lib/constants.ts
  • services/mcp/src/lib/ignored-input-keys.ts
  • services/mcp/src/tools/cast-helpers.ts
  • services/mcp/tests/unit/tool-schema-snapshots.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.

@trunk-io
trunk-io Bot merged commit f0d58eb into master Oct 6, 2026
@trunk-io
trunk-io Bot deleted the feat/devex-mcp-ignored-input-keys branch October 6, 2026 06:58
@deployment-status-posthog

deployment-status-posthog Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-06 07:16 UTC Run
prod-us ✅ Deployed 2026-10-06 07:25 UTC Run
prod-eu ✅ Deployed 2026-10-06 07:28 UTC Run

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.

3 participants