feat(mcp): capture models from client metadata - #4829
Conversation
Prefer recognized Codex request metadata over the self-reported tool argument while preserving provenance and opt-in behavior. Keep application-owned llm_model arguments untouched and validate both MCP SDK generations, including the 2026-07-28 stateless protocol. Verified with @posthog/mcp unit tests, package lint, package build, root formatting, and both MCP Apps harness lanes.
Prompt To Fix All With AI### Issue 1
packages/mcp/src/__tests__/model-parameters.test.ts:360
**Assertion checks wrong path**
The captured request keeps `llm_model` under `$mcp_parameters.params.arguments`, but this assertion checks `$mcp_parameters.llm_model`. It therefore passes even if the argument leaks into the captured parameters, leaving the intended stripping behavior without effective regression coverage.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(mcp): capture models from client me..." | Re-trigger Greptile |
| expect(toolCalls).toHaveLength(1) | ||
| expect(toolCalls[0].properties.$mcp_llm_model).toBe('gpt-5.6-sol') | ||
| expect(toolCalls[0].properties.$mcp_llm_model_source).toBe('client_metadata') | ||
| expect((toolCalls[0].properties.$mcp_parameters as any)?.llm_model).toBeUndefined() |
There was a problem hiding this comment.
The captured request keeps llm_model under $mcp_parameters.params.arguments, but this assertion checks $mcp_parameters.llm_model. It therefore passes even if the argument leaks into the captured parameters, leaving the intended stripping behavior without effective regression coverage.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/mcp/src/__tests__/model-parameters.test.ts
Line: 360
Comment:
**Assertion checks wrong path**
The captured request keeps `llm_model` under `$mcp_parameters.params.arguments`, but this assertion checks `$mcp_parameters.llm_model`. It therefore passes even if the argument leaks into the captured parameters, leaving the intended stripping behavior without effective regression coverage.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Assert against the nested request arguments that MCP capture actually stores. This closes a false-positive gap in model-parameter stripping coverage.\n\nVerification:\n- pnpm --filter @posthog/mcp test:unit\n- pnpm --filter @posthog/mcp lint
|
Fixed in 1101d6f. The assertions now verify the actual nested request.params.arguments payload, and cover every equivalent model-stripping assertion in the integration suites. |
Graphite Automations"sdk release label" took an action on this PR • (09/07/26)1 label was added to this PR based on Adam Bowker's automation. "Add graphite merge queue [copy]" took an action on this PR • (09/07/26)1 label was added to this PR based on Lucas Faria's automation. |
|
Live model-in-the-loop validation is complete:
|
|
Python SDK parity is ready in PostHog/posthog-python#927. It matches the JavaScript model-capture contract and covers MCP Python SDK 1.x, 2.x, and the 2026-07-28 wire path. |
Problem
MCP servers miss Codex model attribution because Codex sends the model in request metadata instead of the injected tool argument.
Changes
client_metadata.llm_modelarguments remain untouched.2026-07-28.Release info Sub-libraries affected
Libraries affected
Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Codex implemented this change after local Claude Code and Codex probes exposed different model carriers. The
debugging-mcp-analytics,writing-tests, andwriting-pr-descriptionsskills guided the provenance contract, regression coverage, and review summary.The design treats both sources as unverified because MCP does not standardize model identity. Client metadata wins only for a recognized vendor shape.