Skip to content

Commit f869e0a

Browse files
ochafikclaude
andcommitted
Add structuredContent support to OpenAI transport
When toolOutput contains structuredContent, include it in the tool-result notification. Also auto-extract structuredContent from plain objects that aren't content arrays. This allows apps to access structured data directly without parsing JSON from text content. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent acdeee3 commit f869e0a

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

src/openai/transport.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,11 +498,38 @@ export class OpenAITransport implements Transport {
498498
// Deliver tool output if available (check for both null and undefined)
499499
if (this.openai.toolOutput != null) {
500500
queueMicrotask(() => {
501-
// Normalize toolOutput to MCP content array format
502-
let content: Array<{ type: string; text?: string; [key: string]: unknown }>;
501+
// Normalize toolOutput to MCP CallToolResult format
502+
let content: Array<{
503+
type: string;
504+
text?: string;
505+
[key: string]: unknown;
506+
}>;
507+
let structuredContent: Record<string, unknown> | undefined;
503508
const output = this.openai.toolOutput;
504509

505-
if (Array.isArray(output)) {
510+
// Check if output is already a CallToolResult-like object with content/structuredContent
511+
if (
512+
typeof output === "object" &&
513+
output !== null &&
514+
("content" in output || "structuredContent" in output)
515+
) {
516+
const result = output as {
517+
content?: unknown;
518+
structuredContent?: Record<string, unknown>;
519+
};
520+
// Prefer structuredContent if available
521+
if (result.structuredContent !== undefined) {
522+
structuredContent = result.structuredContent;
523+
// Generate content from structuredContent if not provided
524+
content = Array.isArray(result.content)
525+
? result.content
526+
: [{ type: "text", text: JSON.stringify(result.structuredContent) }];
527+
} else if (Array.isArray(result.content)) {
528+
content = result.content;
529+
} else {
530+
content = [{ type: "text", text: JSON.stringify(output) }];
531+
}
532+
} else if (Array.isArray(output)) {
506533
// Already an array of content blocks
507534
content = output;
508535
} else if (
@@ -521,6 +548,10 @@ export class OpenAITransport implements Transport {
521548
) {
522549
// Object with just text field - treat as text content
523550
content = [{ type: "text", text: (output as { text: string }).text }];
551+
} else if (typeof output === "object" && output !== null) {
552+
// Plain object - use as structuredContent and generate text content
553+
structuredContent = output as Record<string, unknown>;
554+
content = [{ type: "text", text: JSON.stringify(output) }];
524555
} else {
525556
// Unknown shape - stringify it
526557
content = [{ type: "text", text: JSON.stringify(output) }];
@@ -531,6 +562,7 @@ export class OpenAITransport implements Transport {
531562
method: "ui/notifications/tool-result",
532563
params: {
533564
content,
565+
structuredContent,
534566
// Include _meta from toolResponseMetadata if available (use undefined not null)
535567
_meta: this.openai.toolResponseMetadata ?? undefined,
536568
},

0 commit comments

Comments
 (0)