Skip to content

Commit ecea142

Browse files
authored
Fix #374 add connector support (#401)
1 parent 42702c0 commit ecea142

File tree

8 files changed

+238
-59
lines changed

8 files changed

+238
-59
lines changed

.changeset/tiny-maps-begin.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@openai/agents-core": patch
3+
"@openai/agents-openai": patch
4+
---
5+
6+
Fix #374 add connector support

examples/connectors/index.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Agent, run, hostedMcpTool } from '@openai/agents';
2+
3+
async function main(verbose: boolean, stream: boolean): Promise<void> {
4+
// 1. Visit https://developers.google.com/oauthplayground/
5+
// 2. Input https://www.googleapis.com/auth/calendar.events as the required scope
6+
// 3. Grab the acccess token starting with "ya29."
7+
const authorization = process.env.GOOGLE_CALENDAR_AUTHORIZATION!;
8+
9+
const agent = new Agent({
10+
name: 'My Calendar Assistant',
11+
instructions:
12+
'You are a helpful assistant that can help a user with their calendar.',
13+
tools: [
14+
hostedMcpTool({
15+
serverLabel: 'google_calendar',
16+
connectorId: 'connector_googlecalendar',
17+
authorization,
18+
requireApproval: 'never',
19+
}),
20+
],
21+
});
22+
23+
const today = new Date().toISOString().split('T')[0];
24+
const input = `What is my schedule for ${today}?`;
25+
if (stream) {
26+
const result = await run(agent, input, { stream: true });
27+
for await (const event of result) {
28+
if (
29+
event.type === 'raw_model_stream_event' &&
30+
event.data.type === 'model' &&
31+
event.data.event.type !== 'response.mcp_call_arguments.delta' &&
32+
event.data.event.type !== 'response.output_text.delta'
33+
) {
34+
console.log(`Got event of type ${JSON.stringify(event.data)}`);
35+
}
36+
}
37+
for (const item of result.newItems) {
38+
console.log(JSON.stringify(item, null, 2));
39+
}
40+
console.log(`Done streaming; final result: ${result.finalOutput}`);
41+
} else {
42+
const res = await run(agent, input);
43+
if (verbose) {
44+
for (const item of res.output) {
45+
console.log(JSON.stringify(item, null, 2));
46+
}
47+
}
48+
console.log(res.finalOutput);
49+
}
50+
}
51+
52+
const args = process.argv.slice(2);
53+
const verbose = args.includes('--verbose');
54+
const stream = args.includes('--stream');
55+
56+
main(verbose, stream).catch((err) => {
57+
console.error(err);
58+
process.exit(1);
59+
});

examples/connectors/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"private": true,
3+
"name": "connectors",
4+
"dependencies": {
5+
"@openai/agents": "workspace:*",
6+
"zod": "^3.25.40"
7+
},
8+
"scripts": {
9+
"build-check": "tsc --noEmit",
10+
"start": "tsx index.ts"
11+
}
12+
}

examples/connectors/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../../tsconfig.examples.json"
3+
}

packages/agents-core/src/tool.ts

Lines changed: 118 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -134,52 +134,126 @@ export type HostedMCPTool<Context = UnknownContext> = HostedTool & {
134134
*/
135135
export function hostedMcpTool<Context = UnknownContext>(
136136
options: {
137-
serverLabel: string;
138-
serverUrl: string;
139137
allowedTools?: string[] | { toolNames?: string[] };
140-
headers?: Record<string, string>;
141-
} & (
142-
| { requireApproval?: never }
143-
| { requireApproval: 'never' }
144-
| {
145-
requireApproval:
146-
| 'always'
147-
| {
148-
never?: { toolNames: string[] };
149-
always?: { toolNames: string[] };
150-
};
151-
onApproval?: HostedMCPApprovalFunction<Context>;
152-
}
153-
),
154-
): HostedMCPTool<Context> {
155-
const providerData: ProviderData.HostedMCPTool<Context> =
156-
typeof options.requireApproval === 'undefined' ||
157-
options.requireApproval === 'never'
158-
? {
159-
type: 'mcp',
160-
server_label: options.serverLabel,
161-
server_url: options.serverUrl,
162-
require_approval: 'never',
163-
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
164-
headers: options.headers,
138+
} &
139+
// MCP server
140+
(| {
141+
serverLabel: string;
142+
serverUrl?: string;
143+
authorization?: string;
144+
headers?: Record<string, string>;
165145
}
166-
: {
167-
type: 'mcp',
168-
server_label: options.serverLabel,
169-
server_url: options.serverUrl,
170-
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
171-
headers: options.headers,
172-
require_approval:
173-
typeof options.requireApproval === 'string'
174-
? 'always'
175-
: buildRequireApproval(options.requireApproval),
176-
on_approval: options.onApproval,
177-
};
178-
return {
179-
type: 'hosted_tool',
180-
name: 'hosted_mcp',
181-
providerData,
182-
};
146+
// OpenAI Connector
147+
| {
148+
serverLabel: string;
149+
connectorId: string;
150+
authorization?: string;
151+
headers?: Record<string, string>;
152+
}
153+
) &
154+
(
155+
| { requireApproval?: never }
156+
| { requireApproval: 'never' }
157+
| {
158+
requireApproval:
159+
| 'always'
160+
| {
161+
never?: { toolNames: string[] };
162+
always?: { toolNames: string[] };
163+
};
164+
onApproval?: HostedMCPApprovalFunction<Context>;
165+
}
166+
),
167+
): HostedMCPTool<Context> {
168+
if ('serverUrl' in options) {
169+
// the MCP servers comaptible with the specification
170+
const providerData: ProviderData.HostedMCPTool<Context> =
171+
typeof options.requireApproval === 'undefined' ||
172+
options.requireApproval === 'never'
173+
? {
174+
type: 'mcp',
175+
server_label: options.serverLabel,
176+
server_url: options.serverUrl,
177+
require_approval: 'never',
178+
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
179+
headers: options.headers,
180+
}
181+
: {
182+
type: 'mcp',
183+
server_label: options.serverLabel,
184+
server_url: options.serverUrl,
185+
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
186+
headers: options.headers,
187+
require_approval:
188+
typeof options.requireApproval === 'string'
189+
? 'always'
190+
: buildRequireApproval(options.requireApproval),
191+
on_approval: options.onApproval,
192+
};
193+
return {
194+
type: 'hosted_tool',
195+
name: 'hosted_mcp',
196+
providerData,
197+
};
198+
} else if ('connectorId' in options) {
199+
// OpenAI's connectors
200+
const providerData: ProviderData.HostedMCPTool<Context> =
201+
typeof options.requireApproval === 'undefined' ||
202+
options.requireApproval === 'never'
203+
? {
204+
type: 'mcp',
205+
server_label: options.serverLabel,
206+
connector_id: options.connectorId,
207+
authorization: options.authorization,
208+
require_approval: 'never',
209+
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
210+
headers: options.headers,
211+
}
212+
: {
213+
type: 'mcp',
214+
server_label: options.serverLabel,
215+
connector_id: options.connectorId,
216+
authorization: options.authorization,
217+
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
218+
headers: options.headers,
219+
require_approval:
220+
typeof options.requireApproval === 'string'
221+
? 'always'
222+
: buildRequireApproval(options.requireApproval),
223+
on_approval: options.onApproval,
224+
};
225+
return {
226+
type: 'hosted_tool',
227+
name: 'hosted_mcp',
228+
providerData,
229+
};
230+
} else {
231+
// the MCP servers comaptible with the specification
232+
const providerData: ProviderData.HostedMCPTool<Context> =
233+
typeof options.requireApproval === 'undefined' ||
234+
options.requireApproval === 'never'
235+
? {
236+
type: 'mcp',
237+
server_label: options.serverLabel,
238+
require_approval: 'never',
239+
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
240+
}
241+
: {
242+
type: 'mcp',
243+
server_label: options.serverLabel,
244+
allowed_tools: toMcpAllowedToolsFilter(options.allowedTools),
245+
require_approval:
246+
typeof options.requireApproval === 'string'
247+
? 'always'
248+
: buildRequireApproval(options.requireApproval),
249+
on_approval: options.onApproval,
250+
};
251+
return {
252+
type: 'hosted_tool',
253+
name: 'hosted_mcp',
254+
providerData,
255+
};
256+
}
183257
}
184258

185259
/**

packages/agents-core/src/types/providerData.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,35 @@ import { UnknownContext } from './aliases';
66
*/
77
export type HostedMCPTool<Context = UnknownContext> = {
88
type: 'mcp';
9-
server_label: string;
10-
server_url: string;
119
allowed_tools?: string[] | { tool_names: string[] };
12-
headers?: Record<string, string>;
13-
} & (
14-
| { require_approval?: 'never'; on_approval?: never }
15-
| {
16-
require_approval:
17-
| 'always'
18-
| {
19-
never?: { tool_names: string[] };
20-
always?: { tool_names: string[] };
21-
};
22-
on_approval?: HostedMCPApprovalFunction<Context>;
23-
}
24-
);
10+
} &
11+
// MCP server
12+
(| {
13+
server_label: string;
14+
server_url?: string;
15+
authorization?: string;
16+
headers?: Record<string, string>;
17+
}
18+
// OpenAI Connector
19+
| {
20+
server_label: string;
21+
connector_id: string;
22+
authorization?: string;
23+
headers?: Record<string, string>;
24+
}
25+
) &
26+
(
27+
| { require_approval?: 'never'; on_approval?: never }
28+
| {
29+
require_approval:
30+
| 'always'
31+
| {
32+
never?: { tool_names: string[] };
33+
always?: { tool_names: string[] };
34+
};
35+
on_approval?: HostedMCPApprovalFunction<Context>;
36+
}
37+
);
2538

2639
export type HostedMCPListTools = {
2740
id: string;
@@ -39,6 +52,7 @@ export type HostedMCPCall = {
3952
arguments: string;
4053
name: string;
4154
server_label: string;
55+
connector_id?: string;
4256
error?: string | null;
4357
// excluding this large data field
4458
// output?: string | null;

packages/agents-openai/src/openaiResponsesModel.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ function converTool<_TContext = unknown>(
210210
type: 'mcp',
211211
server_label: tool.providerData.server_label,
212212
server_url: tool.providerData.server_url,
213+
connector_id: tool.providerData.connector_id,
214+
authorization: tool.providerData.authorization,
213215
allowed_tools: tool.providerData.allowed_tools,
214216
headers: tool.providerData.headers,
215217
require_approval: convertMCPRequireApproval(

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)