fix(tools): include response in structuredContent for MCP clients#133
fix(tools): include response in structuredContent for MCP clients#133USEONGEE wants to merge 1 commit into
Conversation
WalkthroughAdds a new top-level Changes
Estimated Code Review Effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip You can make CodeRabbit's review stricter and more nitpicky using the `assertive` profile, if that's what you prefer.Change the |
When STRUCTURED_CONTENT_ENABLED is true, structuredContent only contains metadata (threadId, model, sessionId) but not the actual response text. MCP clients that rely on structuredContent to extract tool results cannot access the response. Add the response field to both the codex tool's outputSchema and the structuredContent payload so MCP clients can reliably retrieve it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: useongee <118250509+USEONGEE@users.noreply.github.com>
30126a0 to
80a805d
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/__tests__/mcp-stdio.test.ts (1)
149-157:⚠️ Potential issue | 🟡 MinorAdd an assertion for
structuredContent.responseto validate the PR objective.Line 149 updates typing, but the test still doesn’t assert that
structuredContent.responseis actually populated at runtime.✅ Suggested assertion addition
expect(callParse.success).toBe(true); expect(callResponse.content[0]._meta?.threadId).toBe('th_stub_123'); expect(callResponse.structuredContent?.threadId).toBe('th_stub_123'); + expect(callResponse.structuredContent?.response).toBeDefined(); + expect(callResponse.structuredContent?.response).toBe( + callResponse.content[0].text + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/mcp-stdio.test.ts` around lines 149 - 157, Add an assertion to verify that structuredContent.response is populated at runtime: after parsing with CallToolResultSchema and the existing threadId assertions, assert that callResponse.structuredContent?.response (or the equivalent property on the callResponse object) equals the expected response string; locate where CallToolResultSchema, callResponse, and structuredContent are used in this test and add the assertion there.
🧹 Nitpick comments (2)
src/tools/definitions.ts (1)
58-64: Consider makingresponserequired in the codex output schema.You now always populate
responsein handler output, but the schema still treats it as optional. Making it required gives MCP clients a stronger contract.💡 Suggested schema tightening
outputSchema: { type: 'object', properties: { response: { type: 'string' }, threadId: { type: 'string' }, }, + required: ['response'], },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tools/definitions.ts` around lines 58 - 64, The outputSchema currently defines properties response and threadId but leaves response optional; update the schema in outputSchema so that response is a required property (e.g., add a required: ['response'] entry alongside properties) so callers have a stronger contract; ensure this change is made near the outputSchema object (the properties.response and properties.threadId definitions) in src/tools/definitions.ts and run/type-check any schema consumers to confirm no breaking uses.src/tools/handlers.ts (1)
205-224: Avoid placing fullresponseincontent[0]._meta; keep it instructuredContentonly.Current code duplicates the full response text into both channels. Keeping
_metafocused on compatibility metadata (e.g.,threadId) avoids payload bloat while still exposingresponseinstructuredContent.♻️ Suggested split of meta payloads
- const metadata: Record<string, unknown> = { - response, - ...(threadId && { threadId }), - ...(selectedModel && { model: selectedModel }), - ...(activeSessionId && { sessionId: activeSessionId }), - ...(effectiveCallbackUri && { callbackUri: effectiveCallbackUri }), - }; + const contentMeta: Record<string, unknown> = { + ...(threadId && { threadId }), + ...(selectedModel && { model: selectedModel }), + ...(activeSessionId && { sessionId: activeSessionId }), + ...(effectiveCallbackUri && { callbackUri: effectiveCallbackUri }), + }; + const structuredPayload: Record<string, unknown> = { + response, + ...contentMeta, + }; return { content: [ { type: 'text', text: response, - _meta: metadata, + _meta: contentMeta, }, ], - structuredContent: - isStructuredContentEnabled() && Object.keys(metadata).length > 0 - ? metadata - : undefined, + structuredContent: isStructuredContentEnabled() + ? structuredPayload + : undefined, };Based on learnings: "When Codex emits threadId, it must be returned in content[0]._meta for Claude Code compatibility and structuredContent for other MCP clients when STRUCTURED_CONTENT_ENABLED env var is set".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/tools/handlers.ts` around lines 205 - 224, The content payload currently injects the full response into content[0]._meta via the metadata object, duplicating the response; instead, build _meta to include only compatibility fields (e.g., threadId and any minimal required identifiers) and omit the full response string, while keeping the full response only in structuredContent when isStructuredContentEnabled() is true; update the code that constructs metadata/metadataForStructuredContent so that content[0]._meta contains threadId (and other minimal compatibility keys if necessary) and structuredContent contains the response plus selectedModel, sessionId, callbackUri, etc., referencing the existing variables metadata, response, content[0]._meta, structuredContent, isStructuredContentEnabled(), selectedModel, activeSessionId, effectiveCallbackUri, and threadId.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/__tests__/mcp-stdio.test.ts`:
- Around line 149-157: Add an assertion to verify that
structuredContent.response is populated at runtime: after parsing with
CallToolResultSchema and the existing threadId assertions, assert that
callResponse.structuredContent?.response (or the equivalent property on the
callResponse object) equals the expected response string; locate where
CallToolResultSchema, callResponse, and structuredContent are used in this test
and add the assertion there.
---
Nitpick comments:
In `@src/tools/definitions.ts`:
- Around line 58-64: The outputSchema currently defines properties response and
threadId but leaves response optional; update the schema in outputSchema so that
response is a required property (e.g., add a required: ['response'] entry
alongside properties) so callers have a stronger contract; ensure this change is
made near the outputSchema object (the properties.response and
properties.threadId definitions) in src/tools/definitions.ts and run/type-check
any schema consumers to confirm no breaking uses.
In `@src/tools/handlers.ts`:
- Around line 205-224: The content payload currently injects the full response
into content[0]._meta via the metadata object, duplicating the response;
instead, build _meta to include only compatibility fields (e.g., threadId and
any minimal required identifiers) and omit the full response string, while
keeping the full response only in structuredContent when
isStructuredContentEnabled() is true; update the code that constructs
metadata/metadataForStructuredContent so that content[0]._meta contains threadId
(and other minimal compatibility keys if necessary) and structuredContent
contains the response plus selectedModel, sessionId, callbackUri, etc.,
referencing the existing variables metadata, response, content[0]._meta,
structuredContent, isStructuredContentEnabled(), selectedModel, activeSessionId,
effectiveCallbackUri, and threadId.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e3bd963d-f56e-4b23-9f07-f10814cac4f6
📒 Files selected for processing (3)
src/__tests__/mcp-stdio.test.tssrc/tools/definitions.tssrc/tools/handlers.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/__tests__/mcp-stdio.test.ts (1)
149-157:⚠️ Potential issue | 🟡 MinorMissing assertion for
structuredContent.response.The type annotation at line 149 now includes
response?: string, but there's no assertion to verify thatstructuredContent.responseis actually populated with the expected value. Since the stub outputs"ok", consider adding an assertion to validate the core feature this PR introduces.Proposed fix to add missing assertion
expect(callResponse.content[0]._meta?.threadId).toBe('th_stub_123'); + expect(callResponse.structuredContent?.response).toBe('ok'); expect(callResponse.structuredContent?.threadId).toBe('th_stub_123');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/mcp-stdio.test.ts` around lines 149 - 157, Add an assertion that verifies the stubbed response populates structuredContent.response: after parsing with CallToolResultSchema and the existing threadId checks, assert that callResponse.structuredContent?.response equals the expected stub value (e.g., "ok") so the new optional response field is validated; reference CallToolResultSchema and the callResponse object to locate where to insert the expect check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/__tests__/mcp-stdio.test.ts`:
- Around line 149-157: Add an assertion that verifies the stubbed response
populates structuredContent.response: after parsing with CallToolResultSchema
and the existing threadId checks, assert that
callResponse.structuredContent?.response equals the expected stub value (e.g.,
"ok") so the new optional response field is validated; reference
CallToolResultSchema and the callResponse object to locate where to insert the
expect check.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fe43a562-6cbf-4134-9bad-f1909e0fad1e
📒 Files selected for processing (3)
src/__tests__/mcp-stdio.test.tssrc/tools/definitions.tssrc/tools/handlers.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/tools/definitions.ts
- src/tools/handlers.ts
Summary
STRUCTURED_CONTENT_ENABLED=true,structuredContentonly contains metadata (threadId,model,sessionId,callbackUri) but not the actual response textstructuredContentto extract tool results cannot access the codex responseresponsefield to both the codex tool'soutputSchemaand thestructuredContentpayloadChanges
src/tools/definitions.ts: Addresponseproperty to codex tool'soutputSchemasrc/tools/handlers.ts: Includeresponsein metadata object used forstructuredContentsrc/__tests__/mcp-stdio.test.ts: Update test expectations and types forresponseinstructuredContentTest plan
npm run buildpassesnpm run lintpassesnpm run format:checkpassesnpm test— all 71 tests passstructuredContent.responseis populated whenSTRUCTURED_CONTENT_ENABLED=1🤖 Generated with Claude Code