Skip to content

Commit 5ac885c

Browse files
committed
feat(agents-core/mcp): return full CallToolResult; add useStructuredContent; include structuredContent in tool outputs
BREAKING: MCPServer#callTool now returns CallToolResult (was content array).\nfeat: Add useStructuredContent option (default false).\nchore: Update node/browser shims to return full results.\nrefactor: mcpToFunctionTool returns object/array and appends structuredContent when enabled.
1 parent 3984db8 commit 5ac885c

File tree

6 files changed

+263
-28
lines changed

6 files changed

+263
-28
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,5 @@ openai-agents-python
148148
bundled/
149149

150150
.wrangler/
151-
.dev.vars
151+
.dev.vars
152+
package-lock.json

packages/agents-core/src/mcp.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,28 +35,31 @@ export const DEFAULT_SSE_MCP_CLIENT_LOGGER_NAME =
3535
export interface MCPServer {
3636
cacheToolsList: boolean;
3737
toolFilter?: MCPToolFilterCallable | MCPToolFilterStatic;
38+
useStructuredContent?: boolean;
3839
connect(): Promise<void>;
3940
readonly name: string;
4041
close(): Promise<void>;
4142
listTools(): Promise<MCPTool[]>;
4243
callTool(
4344
toolName: string,
4445
args: Record<string, unknown> | null,
45-
): Promise<CallToolResultContent>;
46+
): Promise<CallToolResult>;
4647
invalidateToolsCache(): Promise<void>;
4748
}
4849

4950
export abstract class BaseMCPServerStdio implements MCPServer {
5051
public cacheToolsList: boolean;
5152
protected _cachedTools: any[] | undefined = undefined;
5253
public toolFilter?: MCPToolFilterCallable | MCPToolFilterStatic;
54+
public useStructuredContent?: boolean;
5355

5456
protected logger: Logger;
5557
constructor(options: MCPServerStdioOptions) {
5658
this.logger =
5759
options.logger ?? getLogger(DEFAULT_STDIO_MCP_CLIENT_LOGGER_NAME);
5860
this.cacheToolsList = options.cacheToolsList ?? false;
5961
this.toolFilter = options.toolFilter;
62+
this.useStructuredContent = options.useStructuredContent ?? false;
6063
}
6164

6265
abstract get name(): string;
@@ -66,7 +69,7 @@ export abstract class BaseMCPServerStdio implements MCPServer {
6669
abstract callTool(
6770
_toolName: string,
6871
_args: Record<string, unknown> | null,
69-
): Promise<CallToolResultContent>;
72+
): Promise<CallToolResult>;
7073
abstract invalidateToolsCache(): Promise<void>;
7174

7275
/**
@@ -85,6 +88,7 @@ export abstract class BaseMCPServerStreamableHttp implements MCPServer {
8588
public cacheToolsList: boolean;
8689
protected _cachedTools: any[] | undefined = undefined;
8790
public toolFilter?: MCPToolFilterCallable | MCPToolFilterStatic;
91+
public useStructuredContent?: boolean;
8892

8993
protected logger: Logger;
9094
constructor(options: MCPServerStreamableHttpOptions) {
@@ -93,6 +97,7 @@ export abstract class BaseMCPServerStreamableHttp implements MCPServer {
9397
getLogger(DEFAULT_STREAMABLE_HTTP_MCP_CLIENT_LOGGER_NAME);
9498
this.cacheToolsList = options.cacheToolsList ?? false;
9599
this.toolFilter = options.toolFilter;
100+
this.useStructuredContent = options.useStructuredContent ?? false;
96101
}
97102

98103
abstract get name(): string;
@@ -102,7 +107,7 @@ export abstract class BaseMCPServerStreamableHttp implements MCPServer {
102107
abstract callTool(
103108
_toolName: string,
104109
_args: Record<string, unknown> | null,
105-
): Promise<CallToolResultContent>;
110+
): Promise<CallToolResult>;
106111
abstract invalidateToolsCache(): Promise<void>;
107112

108113
/**
@@ -121,13 +126,15 @@ export abstract class BaseMCPServerSSE implements MCPServer {
121126
public cacheToolsList: boolean;
122127
protected _cachedTools: any[] | undefined = undefined;
123128
public toolFilter?: MCPToolFilterCallable | MCPToolFilterStatic;
129+
public useStructuredContent?: boolean;
124130

125131
protected logger: Logger;
126132
constructor(options: MCPServerSSEOptions) {
127133
this.logger =
128134
options.logger ?? getLogger(DEFAULT_SSE_MCP_CLIENT_LOGGER_NAME);
129135
this.cacheToolsList = options.cacheToolsList ?? false;
130136
this.toolFilter = options.toolFilter;
137+
this.useStructuredContent = options.useStructuredContent ?? false;
131138
}
132139

133140
abstract get name(): string;
@@ -137,7 +144,7 @@ export abstract class BaseMCPServerSSE implements MCPServer {
137144
abstract callTool(
138145
_toolName: string,
139146
_args: Record<string, unknown> | null,
140-
): Promise<CallToolResultContent>;
147+
): Promise<CallToolResult>;
141148
abstract invalidateToolsCache(): Promise<void>;
142149

143150
/**
@@ -178,6 +185,7 @@ export class MCPServerStdio extends BaseMCPServerStdio {
178185
constructor(options: MCPServerStdioOptions) {
179186
super(options);
180187
this.underlying = new UnderlyingMCPServerStdio(options);
188+
this.useStructuredContent = options.useStructuredContent ?? false;
181189
}
182190
get name(): string {
183191
return this.underlying.name;
@@ -201,7 +209,7 @@ export class MCPServerStdio extends BaseMCPServerStdio {
201209
callTool(
202210
toolName: string,
203211
args: Record<string, unknown> | null,
204-
): Promise<CallToolResultContent> {
212+
): Promise<CallToolResult> {
205213
return this.underlying.callTool(toolName, args);
206214
}
207215
invalidateToolsCache(): Promise<void> {
@@ -214,6 +222,7 @@ export class MCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
214222
constructor(options: MCPServerStreamableHttpOptions) {
215223
super(options);
216224
this.underlying = new UnderlyingMCPServerStreamableHttp(options);
225+
this.useStructuredContent = options.useStructuredContent ?? false;
217226
}
218227
get name(): string {
219228
return this.underlying.name;
@@ -237,7 +246,7 @@ export class MCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
237246
callTool(
238247
toolName: string,
239248
args: Record<string, unknown> | null,
240-
): Promise<CallToolResultContent> {
249+
): Promise<CallToolResult> {
241250
return this.underlying.callTool(toolName, args);
242251
}
243252
invalidateToolsCache(): Promise<void> {
@@ -250,6 +259,7 @@ export class MCPServerSSE extends BaseMCPServerSSE {
250259
constructor(options: MCPServerSSEOptions) {
251260
super(options);
252261
this.underlying = new UnderlyingMCPServerSSE(options);
262+
this.useStructuredContent = options.useStructuredContent ?? false;
253263
}
254264
get name(): string {
255265
return this.underlying.name;
@@ -273,7 +283,7 @@ export class MCPServerSSE extends BaseMCPServerSSE {
273283
callTool(
274284
toolName: string,
275285
args: Record<string, unknown> | null,
276-
): Promise<CallToolResultContent> {
286+
): Promise<CallToolResult> {
277287
return this.underlying.callTool(toolName, args);
278288
}
279289
invalidateToolsCache(): Promise<void> {
@@ -463,8 +473,27 @@ export function mcpToFunctionTool(
463473
if (currentSpan) {
464474
currentSpan.spanData['mcp_data'] = { server: server.name };
465475
}
466-
const content = await server.callTool(mcpTool.name, args);
467-
return content.length === 1 ? content[0] : content;
476+
const result = await server.callTool(mcpTool.name, args);
477+
478+
// JS ergonomics: return objects/arrays; include structuredContent when enabled
479+
if (result.content && result.content.length === 1) {
480+
if (server.useStructuredContent && (result as any).structuredContent) {
481+
return [result.content[0], (result as any).structuredContent];
482+
}
483+
return result.content[0];
484+
} else if (result.content && result.content.length > 1) {
485+
const outputs: any[] = result.content.map((c) => c);
486+
if (server.useStructuredContent && (result as any).structuredContent) {
487+
outputs.push((result as any).structuredContent);
488+
}
489+
return outputs;
490+
} else if (
491+
server.useStructuredContent &&
492+
(result as any).structuredContent
493+
) {
494+
return (result as any).structuredContent;
495+
}
496+
return 'Error running tool.';
468497
}
469498

470499
const schema: JsonObjectSchema<any> = {
@@ -533,6 +562,7 @@ export interface BaseMCPServerStdioOptions {
533562
encodingErrorHandler?: 'strict' | 'ignore' | 'replace';
534563
logger?: Logger;
535564
toolFilter?: MCPToolFilterCallable | MCPToolFilterStatic;
565+
useStructuredContent?: boolean;
536566
timeout?: number;
537567
}
538568
export interface DefaultMCPServerStdioOptions
@@ -555,6 +585,7 @@ export interface MCPServerStreamableHttpOptions {
555585
name?: string;
556586
logger?: Logger;
557587
toolFilter?: MCPToolFilterCallable | MCPToolFilterStatic;
588+
useStructuredContent?: boolean;
558589
timeout?: number;
559590

560591
// ----------------------------------------------------
@@ -579,6 +610,7 @@ export interface MCPServerSSEOptions {
579610
name?: string;
580611
logger?: Logger;
581612
toolFilter?: MCPToolFilterCallable | MCPToolFilterStatic;
613+
useStructuredContent?: boolean;
582614
timeout?: number;
583615

584616
// ----------------------------------------------------
@@ -624,6 +656,7 @@ export interface JsonRpcResponse {
624656
export interface CallToolResponse extends JsonRpcResponse {
625657
result: {
626658
content: { type: string; text: string }[];
659+
structuredContent?: any;
627660
};
628661
}
629662
export type CallToolResult = CallToolResponse['result'];

packages/agents-core/src/shims/mcp-server/browser.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
BaseMCPServerSSE,
33
BaseMCPServerStdio,
44
BaseMCPServerStreamableHttp,
5-
CallToolResultContent,
5+
CallToolResult,
66
MCPServerSSEOptions,
77
MCPServerStdioOptions,
88
MCPServerStreamableHttpOptions,
@@ -28,7 +28,7 @@ export class MCPServerStdio extends BaseMCPServerStdio {
2828
callTool(
2929
_toolName: string,
3030
_args: Record<string, unknown> | null,
31-
): Promise<CallToolResultContent> {
31+
): Promise<CallToolResult> {
3232
throw new Error('Method not implemented.');
3333
}
3434
invalidateToolsCache(): Promise<void> {
@@ -55,7 +55,7 @@ export class MCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
5555
callTool(
5656
_toolName: string,
5757
_args: Record<string, unknown> | null,
58-
): Promise<CallToolResultContent> {
58+
): Promise<CallToolResult> {
5959
throw new Error('Method not implemented.');
6060
}
6161
invalidateToolsCache(): Promise<void> {
@@ -84,7 +84,7 @@ export class MCPServerSSE extends BaseMCPServerSSE {
8484
callTool(
8585
_toolName: string,
8686
_args: Record<string, unknown> | null,
87-
): Promise<CallToolResultContent> {
87+
): Promise<CallToolResult> {
8888
throw new Error('Method not implemented.');
8989
}
9090

packages/agents-core/src/shims/mcp-server/node.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
BaseMCPServerStdio,
66
BaseMCPServerStreamableHttp,
77
BaseMCPServerSSE,
8-
CallToolResultContent,
8+
CallToolResult,
99
DefaultMCPServerStdioOptions,
1010
InitializeResult,
1111
MCPServerStdioOptions,
@@ -124,7 +124,7 @@ export class NodeMCPServerStdio extends BaseMCPServerStdio {
124124
async callTool(
125125
toolName: string,
126126
args: Record<string, unknown> | null,
127-
): Promise<CallToolResultContent> {
127+
): Promise<CallToolResult> {
128128
const { CallToolResultSchema } = await import(
129129
'@modelcontextprotocol/sdk/types.js'
130130
).catch(failedToImport);
@@ -144,12 +144,12 @@ export class NodeMCPServerStdio extends BaseMCPServerStdio {
144144
},
145145
);
146146
const parsed = CallToolResultSchema.parse(response);
147-
const result = parsed.content;
147+
const result = parsed as CallToolResult;
148148
this.debugLog(
149149
() =>
150150
`Called tool ${toolName} (args: ${JSON.stringify(args)}, result: ${JSON.stringify(result)})`,
151151
);
152-
return result as CallToolResultContent;
152+
return result;
153153
}
154154

155155
get name() {
@@ -245,7 +245,7 @@ export class NodeMCPServerSSE extends BaseMCPServerSSE {
245245
async callTool(
246246
toolName: string,
247247
args: Record<string, unknown> | null,
248-
): Promise<CallToolResultContent> {
248+
): Promise<CallToolResult> {
249249
const { CallToolResultSchema } = await import(
250250
'@modelcontextprotocol/sdk/types.js'
251251
).catch(failedToImport);
@@ -265,12 +265,12 @@ export class NodeMCPServerSSE extends BaseMCPServerSSE {
265265
},
266266
);
267267
const parsed = CallToolResultSchema.parse(response);
268-
const result = parsed.content;
268+
const result = parsed as CallToolResult;
269269
this.debugLog(
270270
() =>
271271
`Called tool ${toolName} (args: ${JSON.stringify(args)}, result: ${JSON.stringify(result)})`,
272272
);
273-
return result as CallToolResultContent;
273+
return result;
274274
}
275275

276276
get name() {
@@ -371,7 +371,7 @@ export class NodeMCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
371371
async callTool(
372372
toolName: string,
373373
args: Record<string, unknown> | null,
374-
): Promise<CallToolResultContent> {
374+
): Promise<CallToolResult> {
375375
const { CallToolResultSchema } = await import(
376376
'@modelcontextprotocol/sdk/types.js'
377377
).catch(failedToImport);
@@ -391,12 +391,12 @@ export class NodeMCPServerStreamableHttp extends BaseMCPServerStreamableHttp {
391391
},
392392
);
393393
const parsed = CallToolResultSchema.parse(response);
394-
const result = parsed.content;
394+
const result = parsed as CallToolResult;
395395
this.debugLog(
396396
() =>
397397
`Called tool ${toolName} (args: ${JSON.stringify(args)}, result: ${JSON.stringify(result)})`,
398398
);
399-
return result as CallToolResultContent;
399+
return result;
400400
}
401401

402402
get name() {

packages/agents-core/test/mcpCache.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getAllMcpTools } from '../src/mcp';
33
import type { FunctionTool } from '../src/tool';
44
import { withTrace } from '../src/tracing';
55
import { NodeMCPServerStdio } from '../src/shims/mcp-server/node';
6-
import type { CallToolResultContent } from '../src/mcp';
6+
import type { CallToolResult } from '../src/mcp';
77
import { RunContext } from '../src/runContext';
88
import { Agent } from '../src/agent';
99

@@ -27,8 +27,8 @@ class StubServer extends NodeMCPServerStdio {
2727
async callTool(
2828
_toolName: string,
2929
_args: Record<string, unknown> | null,
30-
): Promise<CallToolResultContent> {
31-
return [];
30+
): Promise<CallToolResult> {
31+
return { content: [] } as CallToolResult;
3232
}
3333
}
3434

@@ -97,7 +97,7 @@ describe('MCP tools cache invalidation', () => {
9797
let called = false;
9898
(serverB as any).callTool = async () => {
9999
called = true;
100-
return [];
100+
return { content: [] } as CallToolResult;
101101
};
102102

103103
const cachedTools = (await getAllMcpTools({

0 commit comments

Comments
 (0)