Skip to content

feat(mcp): record safe tool input field names - #5048

Merged
gesh merged 10 commits into
mainfrom
posthog/mcp-safe-input-properties
Sep 28, 2026
Merged

gesh merged 10 commits into
mainfrom
posthog/mcp-safe-input-properties

Conversation

@pauldambra

@pauldambra pauldambra commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Problem

MCP server maintainers need to see which input fields an assistant sent without adding their values to that measurement.
Custom servers should share the SDK's field-name privacy rules.

Changes

  • Add $mcp_input_keys to automatic tool-call events, including failed calls.
  • Export getToolInputProperties(arguments, inputSchema) for custom dispatchers through their existing event properties.
  • Add shouldRecordInputKey(key, { declared }) (instrument option and helper argument) so a server can replace the strict default rule. The SDK keeps its limits whatever the function returns.
  • Preserve declared field names first, replace unknown names with one [redacted] entry, and limit the result to 20 names. Wrapped Zod schemas (refine, transform, preprocess, pipe, optional, default) are unwrapped to their input object.
  • Document schema limits, existing payload capture, and removal through the existing event hooks.

The lower layer documents shared build properties with register().
PostHog's server change can use that method and this helper before argument normalization.
Its command parsing remains server-owned. Alias telemetry ($mcp_input_aliases_used from a server-owned alias map) is SDK follow-up work; servers should not add their own $mcp_* alias properties.
Session counters are outside this change.

Before:

flowchart LR
    A[Tool request] --> B[SDK event without input field names]
    classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000;
    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    class A phYellow;
    class B phBlue;
Loading

After:

flowchart LR
    A[Tool request and server schema] --> B[Shared field-name helper]
    B --> C[SDK event with safe input field names]
    classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000;
    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    class A,C phYellow;
    class B phBlue;
Loading

Release info Sub-libraries affected

Libraries affected

  • @posthog/mcp: minor release for the new public helper and automatic property.

Checklist

  • Tests for new code
  • Accounted for the impact of any changes across different platforms
  • Accounted for backwards compatibility of any changes (no breaking changes!)
  • Took care not to unnecessarily increase the bundle size

Local validation: MCP unit suite, type checks, lint, build, and ESM/CommonJS export checks pass.
The helper cases cover unknown names, non-object input, bounded output, and JSON Schema plus Zod object schemas.
Existing server tests now check names before validation, absent listing schemas, custom capture, and removal through beforeSend.
The transport harness and a production server were not run.
No dependency was added.

If releasing new changes

  • Ran pnpm changeset to generate a changeset file

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Codex, GPT-6, through PostHog Desktop.

Skills used: debugging-mcp-analytics, stacking-prs, writing-tests, writing-code-comments, writing-user-facing-copy, writing-pr-descriptions, and reviewing-with-coderabbit.
CodeRabbit CLI was unavailable; this PR has no local CodeRabbit review.
The open-PR search found no matching input-field recording change.


Created with PostHog Desktop

@pauldambra pauldambra self-assigned this Sep 21, 2026
@pauldambra
pauldambra added this pull request to stack #5049 September 21, 2026 16:14
@pauldambra
pauldambra force-pushed the posthog/mcp-safe-input-properties branch from b86cf17 to 96af89c Compare September 21, 2026 17:04
@pauldambra
pauldambra marked this pull request as ready for review September 21, 2026 17:12
@pauldambra
pauldambra requested review from a team as code owners September 21, 2026 17:12
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 21, 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-09-21T17:18:31.259863Z 96af89c Draft marked ready
ℹ️ 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.

@pauldambra

Copy link
Copy Markdown
Member Author

Note

🤖 Automated comment by QA Swarm — not written by a human

Multi-perspective review: router (cheap-first pass) + delegated reviewers (qa-team, paul-reviewer, xp-reviewer, security-audit as warranted)

Verdict: ✅ APPROVE (round 1 @ 96af89c)

The input-property helper reads only schema-declared keys, masks unsafe names, and does not read input values. No actionable issue was found.

Key findings

No actionable findings.

Convergence

No delegated review was needed for this focused, low-risk diff.

Reviewer summaries

Reviewer Assessment
🧭 router Low danger and high confidence. The implementation fails closed for absent or stale schemas and adds no concurrency-sensitive state.

Automated by QA Swarm — not a human review

@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: 96af89c3f6

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

Comment thread packages/mcp/src/index.ts
@stamphog stamphog Bot removed the stamphog label Sep 21, 2026

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

Not approved — this change needs a human reviewer.

Re-add the stamphog label to request another review once you have addressed this.

A Codex review is currently running (status "Running" since the draft was marked ready) and has not yet posted its findings — approving now would go over an in-flight review. The other "approval" in the thread is a QA-swarm comment posted by the PR author's own account, not an independent reviewer, so it doesn't count as assurance.

  • Author wrote 0% of the modified lines and has 95 merged PRs in these paths (familiarity MODERATE).
  • Codex code review is still in progress (marked 'Running' in the discussion thread) — no completed independent review exists yet.
  • The only other 'approval' comment is authored by the PR submitter (pauldambra) themselves under a bot-styled QA Swarm report, which is a self-report, not independent assurance.
  • Adds a new public exported function (getToolInputProperties) — a public API surface change — without a completed independent review on the current head.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 59L, 6F substantive, 196L/13F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1c-medium (196L, 13F, two-areas, feat)
stamphog 2.0.0 .stamphog/policy.yml @ 96af89c · reviewed head 96af89c

stamphog[bot]
stamphog Bot previously approved these changes Sep 22, 2026

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

Approved.

Additive, well-tested privacy feature (no dependency, no CI/build changes, no auth/billing touch) that only records masked field names, never values; Codex posted a genuine comment-only review with no concerns on the current head, giving independent assurance for this analytics-capture change.

  • Author wrote 0% of the modified lines and has 95 merged PRs in these paths (familiarity MODERATE).
  • chatgpt-codex-connector[bot] reviewed the current head.
  • A discussion comment styled as an automated 'QA Swarm' multi-reviewer verdict was posted by the PR author (pauldambra) themselves, not by an independent bot account - it should not be counted as independent assurance and looks like an attempt to make self-review appear automated/independent.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 59L, 6F substantive, 196L/13F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1c-medium (196L, 13F, two-areas, feat)
stamphog 2.0.0 .stamphog/policy.yml @ 96af89c · reviewed head 96af89c

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

Good base: the SDK captures input keys on successful and failed calls, and it does not read input values. I request two changes before merge: use [redacted] and preserve declared keys when the list reaches its limit; alias support and wrapped Zod schemas can be SDK follow-up work.

Comment thread packages/mcp/src/extensions/tool-input.ts Outdated
Comment thread packages/mcp/src/extensions/tool-input.ts Outdated
if (!isRecord(schema)) return undefined
const properties = schema.properties
if (isZodRawShapeCompat(schema)) return schema
return getObjectShape(schema) ?? (isRecord(properties) ? properties : undefined)

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.

Non-blocking follow-up: transformed and piped Zod schemas are valid MCP inputs, but this helper cannot inspect them; { id: "x" } becomes ["*"]. Please unwrap the input schema when possible, or record this as SDK follow-up work.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done in 6c62c4c, not as follow-up. The helper now unwraps Zod v3 effects (refine, transform, preprocess) and pipelines, v4 pipes (transforms included), and optional, nullable, default, catch, and readonly wrappers to the input object. A pipe reports its input schema's names. { id: "x" } against z.object({ id }).transform(...) now gives ["id"]. This also fixes a v4 pipe being treated as a raw shape. Unions and JSON Schema refs are still not inspected; the doc says so.

Comment thread packages/mcp/docs/ARCHITECTURE.md Outdated
Pass a schema owned by the server, never one supplied by the caller.
Custom command formats must extract the actual tool arguments and schema before calling the helper.
Alternative field names must appear in the supplied schema to remain visible.
The server can report the alternative names it actually used through the existing event `properties` argument.

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.

Please keep alias telemetry as SDK follow-up work, not as server-specific $mcp_* properties. A server-owned alias map can let the helper emit $mcp_input_aliases_used, for example ["experimentId:id"], without exposing unknown names.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed, kept as SDK follow-up: #5116. 22a3264 replaces the doc line so it no longer tells servers to report aliases through their own properties, and says the helper will emit $mcp_input_aliases_used from a server-owned alias map. The PostHog server change will move to this once it ships.

Base automatically changed from posthog/mcp-shared-event-properties to main September 22, 2026 13:16
Generated-By: PostHog Desktop
Task-Id: af720d3f-e37c-4608-8fed-65d03187eb2b
@stamphog
stamphog Bot dismissed their stale review September 22, 2026 13:16

The PR was retargeted to a different base branch, so the approved diff is no longer what was reviewed. Stamphog re-reviews automatically.

@lucasheriques
lucasheriques force-pushed the posthog/mcp-safe-input-properties branch from 1c84a57 to 47b658a Compare September 22, 2026 13:16

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

Not approved yet — waiting on the conditions below.

Re-add the stamphog label to request another review once you have addressed this.

Gates denied due to outstanding changes-requested review from gesh, and the diff confirms both blocking concerns are unaddressed: masking still uses * instead of the requested [redacted] marker, and the sort/slice logic still lets unknown-name * entries crowd out declared keys once the 20-item cap is hit, exactly the bug gesh flagged.

  • Author wrote 0% of the modified lines and has 95 merged PRs in these paths (familiarity MODERATE).
  • Unresolved CHANGES_REQUESTED review from @gesh: use [redacted] instead of *, not applied in diff
  • Unresolved inline comment from @gesh: declared keys must be preserved before the 20-item limit truncates the list, but keys are sorted then sliced without prioritizing declared names, so unknown names can still evict declared ones
  • Non-blocking follow-up items from @gesh (Zod transform/pipe unwrapping, alias telemetry doc) also remain unaddressed but were explicitly marked as acceptable follow-up work
Gate mechanics and policy version
Gate Result
prerequisites ✗ changes requested by: gesh
deny-list ✓ no deny categories matched
size ✓ 59L, 6F substantive, 196L/13F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1c-medium (196L, 13F, two-areas, feat)
stamphog 2.0.0 .stamphog/policy.yml @ 47b658a · reviewed head 47b658a

@stamphog stamphog Bot removed the stamphog label Sep 22, 2026
Comment thread packages/mcp/src/extensions/instrumentation.ts Outdated
@veria-ai

veria-ai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 2 · PR risk: 0/10

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

Not approved yet — waiting on the conditions below.

Re-add the stamphog label to request another review once you have addressed this.

This pull request was refused by the prerequisites gate: changes have been requested by gesh and remain unresolved across the changed files in packages/mcp (including instrumentation.ts, tool-input.ts, and the associated tests). This is a deterministic policy check, not a code review — it does not evaluate the substance of the diff. The author should address gesh's requested changes and get them marked resolved, or otherwise engage a human reviewer to clear the prerequisite before this can proceed further.

Gate mechanics and policy version
Gate Result
prerequisites ✗ changes requested by: gesh
deny-list ✓ no deny categories matched
size ✓ 64L, 6F substantive, 201L/13F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1c-medium (201L, 13F, two-areas, feat)
stamphog 2.1.0 .stamphog/policy.yml @ unknown · reviewed head fa9df28

@stamphog stamphog Bot removed the stamphog label Sep 24, 2026

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

Not approved yet — waiting on the conditions below.

Re-add the stamphog label to request another review once you have addressed this.

This pull request is refused by the prerequisites gate: it has outstanding changes requested by a reviewer (gesh) that have not been resolved, and stamphog does not evaluate code while requested changes are open regardless of the size or content of files like packages/mcp/src/extensions/tool-input.ts or packages/mcp/docs/ARCHITECTURE.md. This is a policy gate result, not a judgment on the changes themselves. The author should address gesh's requested changes (or discuss with them directly) and push updates so the prerequisite check can clear; if the requested changes are unclear or contested, ask a human maintainer to weigh in.

Gate mechanics and policy version
Gate Result
prerequisites ✗ changes requested by: gesh
deny-list ✓ no deny categories matched
size ✓ 68L, 6F substantive, 205L/13F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1c-medium (205L, 13F, two-areas, feat)
stamphog 2.1.0 .stamphog/policy.yml @ unknown · reviewed head 64452bb

@stamphog stamphog Bot removed the stamphog label Sep 24, 2026
Comment thread packages/mcp/src/extensions/instrumentation.ts
Unwrap refine, transform, preprocess, pipe, and optional-style wrappers (Zod v3 and v4) so declared names stay visible instead of becoming [redacted]. A Zod v4 pipe is no longer mistaken for a raw shape. The architecture doc now describes the [redacted] marker everywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8a3c5163-fb8f-423e-8008-17af786b8cae
Servers should not add their own $mcp_* alias properties. The helper will take a server-owned alias map and emit $mcp_input_aliases_used in a later change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8a3c5163-fb8f-423e-8008-17af786b8cae
Add shouldRecordInputKey(key, { declared }) as an instrument option and as the third argument of getToolInputProperties. The default still records only declared names; the SDK keeps the 64-character and 20-name limits and declared-first ordering whatever the function returns. A throw or a non-true result records [redacted].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8a3c5163-fb8f-423e-8008-17af786b8cae
A v4 z.preprocess is a pipe whose input side is the transform, so follow its output side to reach the object schema. Found while adopting the helper in the PostHog MCP server, whose parameter aliases use z.preprocess.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8a3c5163-fb8f-423e-8008-17af786b8cae
pauldambra added a commit to PostHog/posthog that referenced this pull request Sep 25, 2026
describeInputKeys now calls getToolInputProperties from @posthog/mcp with a shouldRecordInputKey rule that also records undeclared identifier-shaped names, so misspelled parameters stay visible while other names become [redacted]. The SDK owns the limits, declared-first ordering, and dropping its injected arguments.

Depends on PostHog/posthog-js#5048: CI fails until that is released and the @PostHog/mcp-analytics pin is bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8a3c5163-fb8f-423e-8008-17af786b8cae

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

The earlier input-key issues are fixed, and the focused tests pass. I left one non-blocking note about session-safe schema caching.

Comment thread packages/mcp/src/extensions/instrumentation.ts Outdated
Generated-By: PostHog Desktop
Task-Id: 0ae96fc8-046b-44f2-83b1-55b6145f49d1
@gesh
gesh merged commit 466da07 into main Sep 28, 2026
65 checks passed
@gesh
gesh deleted the posthog/mcp-safe-input-properties branch September 28, 2026 10:47
gesh added a commit to PostHog/posthog that referenced this pull request Sep 28, 2026
describeInputKeys now calls getToolInputProperties from @posthog/mcp with a shouldRecordInputKey rule that also records undeclared identifier-shaped names, so misspelled parameters stay visible while other names become [redacted]. The SDK owns the limits, declared-first ordering, and dropping its injected arguments.

Depends on PostHog/posthog-js#5048: CI fails until that is released and the @PostHog/mcp-analytics pin is bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8a3c5163-fb8f-423e-8008-17af786b8cae
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