@openai/agents-core
Version:
The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.
866 lines • 35.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultMCPToolCacheKey = exports.MCPServerSSE = exports.MCPServerStreamableHttp = exports.MCPServerStdio = exports.invalidateServerToolsCache = exports.attachCallToolResultMetadata = exports.MCPTool = exports.DEFAULT_STREAMABLE_HTTP_MCP_CLIENT_LOGGER_NAME = exports.DEFAULT_STDIO_MCP_CLIENT_LOGGER_NAME = exports.DEFAULT_SSE_MCP_CLIENT_LOGGER_NAME = exports.BaseMCPServerStreamableHttp = exports.BaseMCPServerStdio = exports.BaseMCPServerSSE = void 0;
exports.getAllMcpTools = getAllMcpTools;
exports.mcpToFunctionTool = mcpToFunctionTool;
const tool_1 = require("./tool.js");
const errors_1 = require("./errors.js");
const toolIdentity_1 = require("./toolIdentity.js");
const _shims_1 = require("@openai/agents-core/_shims");
const tracing_1 = require("./tracing/index.js");
const logger_1 = require("./logger.js");
const mcpLogging_1 = require("./mcpLogging.js");
const customData_1 = require("./utils/customData.js");
const mcpShared_1 = require("./mcpShared.js");
const mcpToolCache_1 = require("./mcpToolCache.js");
const agentToolRunConfig_1 = require("./agentToolRunConfig.js");
const strictToolSchema_1 = require("./utils/strictToolSchema.js");
const tools_1 = require("./utils/tools.js");
var mcpShared_2 = require("./mcpShared.js");
Object.defineProperty(exports, "BaseMCPServerSSE", { enumerable: true, get: function () { return mcpShared_2.BaseMCPServerSSE; } });
Object.defineProperty(exports, "BaseMCPServerStdio", { enumerable: true, get: function () { return mcpShared_2.BaseMCPServerStdio; } });
Object.defineProperty(exports, "BaseMCPServerStreamableHttp", { enumerable: true, get: function () { return mcpShared_2.BaseMCPServerStreamableHttp; } });
Object.defineProperty(exports, "DEFAULT_SSE_MCP_CLIENT_LOGGER_NAME", { enumerable: true, get: function () { return mcpShared_2.DEFAULT_SSE_MCP_CLIENT_LOGGER_NAME; } });
Object.defineProperty(exports, "DEFAULT_STDIO_MCP_CLIENT_LOGGER_NAME", { enumerable: true, get: function () { return mcpShared_2.DEFAULT_STDIO_MCP_CLIENT_LOGGER_NAME; } });
Object.defineProperty(exports, "DEFAULT_STREAMABLE_HTTP_MCP_CLIENT_LOGGER_NAME", { enumerable: true, get: function () { return mcpShared_2.DEFAULT_STREAMABLE_HTTP_MCP_CLIENT_LOGGER_NAME; } });
Object.defineProperty(exports, "MCPTool", { enumerable: true, get: function () { return mcpShared_2.MCPTool; } });
Object.defineProperty(exports, "attachCallToolResultMetadata", { enumerable: true, get: function () { return mcpShared_2.attachCallToolResultMetadata; } });
var mcpToolCache_2 = require("./mcpToolCache.js");
Object.defineProperty(exports, "invalidateServerToolsCache", { enumerable: true, get: function () { return mcpToolCache_2.invalidateServerToolsCache; } });
const MCP_FUNCTION_TOOL_NAME_MAX_LENGTH = 64;
const MCP_FUNCTION_TOOL_HASH_LENGTH = 8;
function snapshotMcpTools(tools) {
return structuredClone(tools);
}
class MCPToolsLifecycleGuard {
generation = 0;
activeOperations = 0;
startLifecycleBranch(operation) {
try {
return operation?.() ?? Promise.resolve();
}
catch (error) {
return Promise.reject(error);
}
}
beginLifecycleOperation() {
this.activeOperations += 1;
this.generation += 1;
}
endLifecycleOperation() {
this.activeOperations -= 1;
}
async runLifecycleOperation(invalidate, operation) {
this.beginLifecycleOperation();
try {
const [invalidationResult, operationResult] = await Promise.allSettled([
this.startLifecycleBranch(invalidate),
this.startLifecycleBranch(operation),
]);
if (invalidationResult.status === 'rejected') {
throw invalidationResult.reason;
}
if (operationResult.status === 'rejected') {
throw operationResult.reason;
}
}
finally {
this.endLifecycleOperation();
}
}
invalidate() {
this.generation += 1;
}
beginListing() {
if (this.activeOperations > 0) {
throw new Error('Cannot list MCP tools while a server lifecycle operation is in progress.');
}
return this.generation;
}
assertListingIsCurrent(listingGeneration) {
if (this.activeOperations > 0 || listingGeneration !== this.generation) {
throw new Error('MCP tool listing became stale before it completed.');
}
}
}
/**
* Public interface of an MCP server that provides tools.
* You can use this class to pass MCP server settings to your agent.
*/
class MCPServerStdio extends mcpShared_1.BaseMCPServerStdio {
underlying;
toolsLifecycle = new MCPToolsLifecycleGuard();
constructor(options) {
super(options);
this.underlying = new _shims_1.MCPServerStdio(options);
}
get name() {
return this.underlying.name;
}
async invalidateToolsCaches() {
this._cachedTools = undefined;
await this.underlying.invalidateToolsCache();
}
connect() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches(), () => this.underlying.connect());
}
close() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches(), () => this.underlying.close());
}
async listTools() {
const listingGeneration = this.toolsLifecycle.beginListing();
if (this.cacheToolsList && this._cachedTools) {
return snapshotMcpTools(this._cachedTools);
}
const tools = await this.underlying.listTools();
this.toolsLifecycle.assertListingIsCurrent(listingGeneration);
if (this.cacheToolsList) {
this._cachedTools = snapshotMcpTools(tools);
return snapshotMcpTools(this._cachedTools);
}
return snapshotMcpTools(tools);
}
async callTool(toolName, args, meta, options) {
return (await this.callToolResult(toolName, args, meta, options)).content;
}
callToolResult(toolName, args, meta, options) {
return this.underlying.callToolResult(toolName, args, meta, options);
}
listResources(params) {
return this.underlying.listResources(params);
}
listResourceTemplates(params) {
return this.underlying.listResourceTemplates(params);
}
readResource(uri) {
return this.underlying.readResource(uri);
}
invalidateToolsCache() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches());
}
}
exports.MCPServerStdio = MCPServerStdio;
class MCPServerStreamableHttp extends mcpShared_1.BaseMCPServerStreamableHttp {
underlying;
_cachedToolsSessionId = undefined;
toolsLifecycle = new MCPToolsLifecycleGuard();
constructor(options) {
super(options);
this.underlying = new _shims_1.MCPServerStreamableHttp(options);
}
clearLocalToolsCache() {
this._cachedTools = undefined;
this._cachedToolsSessionId = undefined;
}
async invalidateToolsCaches() {
this.clearLocalToolsCache();
await this.underlying.invalidateToolsCache();
}
get name() {
return this.underlying.name;
}
get sessionId() {
return this.underlying.sessionId;
}
connect() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches(), () => this.underlying.connect());
}
close() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches(), () => this.underlying.close());
}
async listTools() {
const listingGeneration = this.toolsLifecycle.beginListing();
const sessionId = this.sessionId;
if (sessionId === undefined) {
this.toolsLifecycle.invalidate();
await this.invalidateToolsCaches();
const tools = await this.underlying.listTools();
return snapshotMcpTools(tools);
}
if (this.cacheToolsList &&
this._cachedTools &&
this._cachedToolsSessionId === sessionId) {
return snapshotMcpTools(this._cachedTools);
}
const tools = await this.underlying.listTools();
this.toolsLifecycle.assertListingIsCurrent(listingGeneration);
if (this.cacheToolsList) {
this._cachedTools = snapshotMcpTools(tools);
this._cachedToolsSessionId = sessionId;
return snapshotMcpTools(this._cachedTools);
}
return snapshotMcpTools(tools);
}
async callTool(toolName, args, meta, options) {
return (await this.callToolResult(toolName, args, meta, options)).content;
}
async callToolResult(toolName, args, meta, options) {
const previousSessionId = this.sessionId;
try {
return await this.underlying.callToolResult(toolName, args, meta, options);
}
finally {
if (previousSessionId !== this.sessionId) {
this.toolsLifecycle.invalidate();
await this.invalidateToolsCaches();
}
}
}
listResources(params) {
return this.underlying.listResources(params);
}
listResourceTemplates(params) {
return this.underlying.listResourceTemplates(params);
}
readResource(uri) {
return this.underlying.readResource(uri);
}
invalidateToolsCache() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches());
}
}
exports.MCPServerStreamableHttp = MCPServerStreamableHttp;
class MCPServerSSE extends mcpShared_1.BaseMCPServerSSE {
underlying;
toolsLifecycle = new MCPToolsLifecycleGuard();
constructor(options) {
super(options);
this.underlying = new _shims_1.MCPServerSSE(options);
}
get name() {
return this.underlying.name;
}
async invalidateToolsCaches() {
this._cachedTools = undefined;
await this.underlying.invalidateToolsCache();
}
connect() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches(), () => this.underlying.connect());
}
close() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches(), () => this.underlying.close());
}
async listTools() {
const listingGeneration = this.toolsLifecycle.beginListing();
if (this.cacheToolsList && this._cachedTools) {
return snapshotMcpTools(this._cachedTools);
}
const tools = await this.underlying.listTools();
this.toolsLifecycle.assertListingIsCurrent(listingGeneration);
if (this.cacheToolsList) {
this._cachedTools = snapshotMcpTools(tools);
return snapshotMcpTools(this._cachedTools);
}
return snapshotMcpTools(tools);
}
async callTool(toolName, args, meta, options) {
return (await this.callToolResult(toolName, args, meta, options)).content;
}
callToolResult(toolName, args, meta, options) {
return this.underlying.callToolResult(toolName, args, meta, options);
}
listResources(params) {
return this.underlying.listResources(params);
}
listResourceTemplates(params) {
return this.underlying.listResourceTemplates(params);
}
readResource(uri) {
return this.underlying.readResource(uri);
}
invalidateToolsCache() {
return this.toolsLifecycle.runLifecycleOperation(() => this.invalidateToolsCaches());
}
}
exports.MCPServerSSE = MCPServerSSE;
/**
* Default cache key generator for MCP tools.
* Uses server name, or server+agent if using callable filter.
*/
const defaultMCPToolCacheKey = ({ server, agent, }) => {
if (server.toolFilter && typeof server.toolFilter === 'function' && agent) {
return `${server.name}:${agent.name}`;
}
return server.name;
};
exports.defaultMCPToolCacheKey = defaultMCPToolCacheKey;
function logMcpToolFilterDebug(buildMessage) {
if (!logger_1.logger.dontLogToolData) {
logger_1.logger.debug(buildMessage());
}
}
/**
* Fetches and filters raw MCP tools from a single MCP server.
*/
async function getMcpToolsFromServer({ server, runContext, agent, generateMCPToolCacheKey, tracingParent, }) {
const cacheKey = (generateMCPToolCacheKey || exports.defaultMCPToolCacheKey)({
server,
agent,
runContext,
});
const serverName = server.name;
// Use cache key generator injected from the outside, or the default if absent.
const cachedMcpTools = server.cacheToolsList
? mcpToolCache_1.cachedMcpTools[cacheKey]
: undefined;
const cacheListing = (0, mcpToolCache_1.beginServerToolsCacheListing)(serverName);
const listToolsForServer = async (span) => {
const fetchedMcpTools = snapshotMcpTools(cachedMcpTools ?? (await server.listTools()));
let mcpTools = fetchedMcpTools;
if (server.toolFilter) {
const filteredTools = [];
for (const tool of fetchedMcpTools) {
const filter = server.toolFilter;
if (filter) {
if (typeof filter === 'function') {
if (!runContext || !agent) {
filteredTools.push(tool);
continue;
}
const context = { runContext, agent, serverName: server.name };
const [detachedTool] = snapshotMcpTools([tool]);
const filtered = await filter(context, detachedTool);
if (!filtered) {
logMcpToolFilterDebug(() => `MCP Tool (server: ${(0, mcpLogging_1.getMcpServerExternalName)(server.name)}, tool: ${tool.name}) is blocked by the callable filter.`);
continue;
}
}
else {
const allowedToolNames = filter.allowedToolNames ?? [];
const blockedToolNames = filter.blockedToolNames ?? [];
if (allowedToolNames.length > 0 || blockedToolNames.length > 0) {
const allowed = allowedToolNames.length > 0
? allowedToolNames.includes(tool.name)
: true;
const blocked = blockedToolNames.length > 0
? blockedToolNames.includes(tool.name)
: false;
if (!allowed || blocked) {
if (blocked) {
logMcpToolFilterDebug(() => `MCP Tool (server: ${(0, mcpLogging_1.getMcpServerExternalName)(server.name)}, tool: ${tool.name}) is blocked by the static filter.`);
}
else if (!allowed) {
logMcpToolFilterDebug(() => `MCP Tool (server: ${(0, mcpLogging_1.getMcpServerExternalName)(server.name)}, tool: ${tool.name}) is not allowed by the static filter.`);
}
continue;
}
}
}
}
filteredTools.push(tool);
}
mcpTools = filteredTools;
}
if (span) {
span.spanData.result = mcpTools.map((t) => t.name);
}
// Cache discovery only; filter decisions belong to the current lookup.
if (!cachedMcpTools && server.cacheToolsList && cacheListing.isCurrent()) {
mcpToolCache_1.cachedMcpTools[cacheKey] = snapshotMcpTools(fetchedMcpTools);
if (!mcpToolCache_1.cachedMcpToolKeysByServer[serverName]) {
mcpToolCache_1.cachedMcpToolKeysByServer[serverName] = new Set();
}
mcpToolCache_1.cachedMcpToolKeysByServer[serverName].add(cacheKey);
}
return snapshotMcpTools(mcpTools);
};
try {
if (cachedMcpTools || (!tracingParent && !(0, tracing_1.getCurrentTrace)())) {
return await listToolsForServer();
}
return await (0, tracing_1.withMCPListToolsSpan)(listToolsForServer, {
data: { server: (0, mcpLogging_1.getMcpServerExternalName)(server.name) },
}, tracingParent);
}
finally {
cacheListing.release();
}
}
function convertMcpToolsToFunctionTools({ mcpTools, server, convertSchemasToStrict, toolNameOverrides, serverIndex = null, errorFunction, }) {
return mcpTools.map((mcpTool, index) => {
const converted = mcpToFunctionTool(mcpTool, server, convertSchemasToStrict, {
toolNameOverride: toolNameOverrides?.[index],
errorFunction,
});
return (0, toolIdentity_1.bindMcpTool)(converted, {
...(0, toolIdentity_1.getMcpToolBinding)(converted),
serverIndex,
});
});
}
/**
* Fetches all function tools from a single MCP server.
*/
async function getFunctionToolsFromServer({ server, serverIndex, convertSchemasToStrict, runContext, agent, generateMCPToolCacheKey, errorFunction, tracingParent, }) {
const mcpTools = await getMcpToolsFromServer({
server,
runContext,
agent,
generateMCPToolCacheKey,
tracingParent,
});
return convertMcpToolsToFunctionTools({
mcpTools,
serverIndex,
server,
convertSchemasToStrict,
errorFunction,
});
}
/**
* Returns all MCP tools from the provided servers, using the function tool conversion.
* If runContext and agent are provided, callable tool filters will be applied.
*/
async function getAllMcpTools(mcpServersOrOpts, runContext, agent, convertSchemasToStrict = false) {
const opts = Array.isArray(mcpServersOrOpts)
? {
mcpServers: mcpServersOrOpts,
runContext,
agent,
convertSchemasToStrict,
}
: mcpServersOrOpts;
const { mcpServers, convertSchemasToStrict: convertSchemasToStrictFromOpts = false, runContext: runContextFromOpts, agent: agentFromOpts, generateMCPToolCacheKey, errorFunction, includeServerInToolNames = false, reservedToolNames, tracingParent, } = opts;
const allTools = [];
const toolNames = new Set();
if (includeServerInToolNames) {
const serverToolBatches = await Promise.all(mcpServers.map(async (server, serverIndex) => ({
server,
serverIndex,
mcpTools: await getMcpToolsFromServer({
server,
runContext: runContextFromOpts,
agent: agentFromOpts,
generateMCPToolCacheKey,
tracingParent,
}),
})));
const toolNameOverrides = buildPrefixedToolNameOverrides(serverToolBatches, new Set(reservedToolNames ?? []));
for (const { server, serverIndex, mcpTools } of serverToolBatches) {
const serverTools = convertMcpToolsToFunctionTools({
mcpTools,
server,
serverIndex,
convertSchemasToStrict: convertSchemasToStrictFromOpts,
errorFunction,
toolNameOverrides: mcpTools.map((_, toolIndex) => toolNameOverrides.get(getToolNameOverrideKey(serverIndex, toolIndex))),
});
const duplicateToolNames = findDuplicateToolNames(serverTools.map((t) => t.name), toolNames);
if (duplicateToolNames.length > 0) {
throw new errors_1.UserError(`Duplicate tool names found across MCP servers: ${duplicateToolNames.join(', ')}`);
}
for (const t of serverTools) {
toolNames.add(t.name);
allTools.push(t);
}
}
return allTools;
}
for (const [serverIndex, server] of mcpServers.entries()) {
const serverTools = await getFunctionToolsFromServer({
server,
serverIndex,
convertSchemasToStrict: convertSchemasToStrictFromOpts,
runContext: runContextFromOpts,
agent: agentFromOpts,
generateMCPToolCacheKey,
errorFunction,
tracingParent,
});
const duplicateToolNames = findDuplicateToolNames(serverTools.map((t) => t.name), toolNames);
if (duplicateToolNames.length > 0) {
throw new errors_1.UserError(`Duplicate tool names found across MCP servers: ${duplicateToolNames.join(', ')}`);
}
for (const t of serverTools) {
toolNames.add(t.name);
allTools.push(t);
}
}
return allTools;
}
/**
* Returns the names that repeat within `names` or were already seen in
* `previousNames`.
*/
function findDuplicateToolNames(names, previousNames) {
const seen = new Set(previousNames);
const duplicates = new Set();
for (const name of names) {
if (seen.has(name)) {
duplicates.add(name);
}
seen.add(name);
}
return [...duplicates].sort();
}
function getToolNameOverrideKey(serverIndex, toolIndex) {
return `${serverIndex}:${toolIndex}`;
}
function getSafeToolNamePart(value, fallback) {
const safe = Array.from(value)
.map((char) => {
const code = char.charCodeAt(0);
return code <= 127 && /[A-Za-z0-9_-]/.test(char) ? char : '_';
})
.join('')
.replace(/^[_-]+|[_-]+$/g, '');
return safe || fallback;
}
function getUtf8Bytes(value) {
const encoded = encodeURIComponent(value);
const bytes = [];
for (let index = 0; index < encoded.length; index += 1) {
if (encoded[index] === '%') {
bytes.push(Number.parseInt(encoded.slice(index + 1, index + 3), 16));
index += 2;
}
else {
bytes.push(encoded.charCodeAt(index));
}
}
return bytes;
}
function rotateLeft(value, bits) {
return ((value << bits) | (value >>> (32 - bits))) >>> 0;
}
function getSha1Hex(value) {
const bytes = getUtf8Bytes(value);
const bitLength = bytes.length * 8;
bytes.push(0x80);
while (bytes.length % 64 !== 56) {
bytes.push(0);
}
for (let shift = 56; shift >= 0; shift -= 8) {
bytes.push(Math.floor(bitLength / 2 ** shift) & 0xff);
}
let h0 = 0x67452301;
let h1 = 0xefcdab89;
let h2 = 0x98badcfe;
let h3 = 0x10325476;
let h4 = 0xc3d2e1f0;
for (let offset = 0; offset < bytes.length; offset += 64) {
const words = new Array(80).fill(0);
for (let index = 0; index < 16; index += 1) {
const byteOffset = offset + index * 4;
words[index] =
((bytes[byteOffset] ?? 0) << 24) |
((bytes[byteOffset + 1] ?? 0) << 16) |
((bytes[byteOffset + 2] ?? 0) << 8) |
(bytes[byteOffset + 3] ?? 0);
}
for (let index = 16; index < 80; index += 1) {
words[index] = rotateLeft(words[index - 3] ^
words[index - 8] ^
words[index - 14] ^
words[index - 16], 1);
}
let a = h0;
let b = h1;
let c = h2;
let d = h3;
let e = h4;
for (let index = 0; index < 80; index += 1) {
let f;
let k;
if (index < 20) {
f = (b & c) | (~b & d);
k = 0x5a827999;
}
else if (index < 40) {
f = b ^ c ^ d;
k = 0x6ed9eba1;
}
else if (index < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8f1bbcdc;
}
else {
f = b ^ c ^ d;
k = 0xca62c1d6;
}
const temp = (rotateLeft(a, 5) + f + e + k + (words[index] ?? 0)) >>> 0;
e = d;
d = c;
c = rotateLeft(b, 30);
b = a;
a = temp;
}
h0 = (h0 + a) >>> 0;
h1 = (h1 + b) >>> 0;
h2 = (h2 + c) >>> 0;
h3 = (h3 + d) >>> 0;
h4 = (h4 + e) >>> 0;
}
return [h0, h1, h2, h3, h4]
.map((word) => word.toString(16).padStart(8, '0'))
.join('');
}
function shortenToolName(baseName, seed, forceHash = false) {
if (!forceHash && baseName.length <= MCP_FUNCTION_TOOL_NAME_MAX_LENGTH) {
return baseName;
}
const hashSuffix = getSha1Hex(seed).slice(0, MCP_FUNCTION_TOOL_HASH_LENGTH);
const suffix = `_${hashSuffix}`;
const stemLength = MCP_FUNCTION_TOOL_NAME_MAX_LENGTH - suffix.length;
const stem = baseName.slice(0, stemLength).replace(/[_-]+$/g, '') || 'mcp';
return `${stem}${suffix}`;
}
function buildPrefixedToolBaseName(serverName, toolName) {
const serverPart = getSafeToolNamePart(serverName, 'server');
const toolPart = getSafeToolNamePart(toolName, 'tool');
return `mcp_${serverPart}__${toolPart}`;
}
function buildPrefixedToolNameOverrides(serverToolBatches, reservedNames) {
const baseNameCounts = new Map();
for (const { server, mcpTools } of serverToolBatches) {
const serverName = (0, mcpLogging_1.getMcpServerExternalName)(server.name);
for (const mcpTool of mcpTools) {
const baseName = buildPrefixedToolBaseName(serverName, mcpTool.name);
const publicName = (0, tools_1.toFunctionToolName)(baseName);
baseNameCounts.set(publicName, (baseNameCounts.get(publicName) ?? 0) + 1);
}
}
const candidates = [];
const rawServerNamesBySeed = new Map();
for (const { server, serverIndex, mcpTools } of serverToolBatches) {
const serverName = (0, mcpLogging_1.getMcpServerExternalName)(server.name);
mcpTools.forEach((mcpTool, toolIndex) => {
const baseName = buildPrefixedToolBaseName(serverName, mcpTool.name);
const seed = `${serverName}\0${mcpTool.name}`;
const previousRawServerName = rawServerNamesBySeed.get(seed);
if (previousRawServerName !== undefined &&
previousRawServerName !== server.name) {
throw new errors_1.UserError(`MCP server names are indistinguishable after URL redaction for tool '${mcpTool.name}': '${serverName}'. Configure unique safe server names when includeServerInToolNames is enabled.`);
}
rawServerNamesBySeed.set(seed, server.name);
const functionToolName = (0, tools_1.toFunctionToolName)(baseName);
const forceHash = (baseNameCounts.get(functionToolName) ?? 0) > 1 ||
reservedNames.has(functionToolName);
candidates.push({
batchKey: getToolNameOverrideKey(serverIndex, toolIndex),
baseName,
seed,
initialName: shortenToolName(baseName, seed, forceHash),
serverIndex,
toolIndex,
});
});
}
const usedNames = new Set(reservedNames);
const overrides = new Map();
for (const candidate of candidates.sort((left, right) => {
return (left.initialName.localeCompare(right.initialName) ||
left.seed.localeCompare(right.seed) ||
left.serverIndex - right.serverIndex ||
left.toolIndex - right.toolIndex);
})) {
let publicName = candidate.initialName;
let collisionIndex = 1;
while (usedNames.has((0, tools_1.toFunctionToolName)(publicName))) {
publicName = shortenToolName(candidate.baseName, `${candidate.seed}\0${collisionIndex}`, true);
collisionIndex += 1;
}
usedNames.add((0, tools_1.toFunctionToolName)(publicName));
overrides.set(candidate.batchKey, publicName);
}
return overrides;
}
async function resolveMcpToolMeta(server, runContext, toolName, args) {
const resolver = server.toolMetaResolver;
if (!resolver) {
return undefined;
}
const context = {
runContext,
serverName: server.name,
toolName,
arguments: args,
};
const resolved = await resolver(context);
if (resolved == null) {
return undefined;
}
if (typeof resolved !== 'object' || Array.isArray(resolved)) {
throw new TypeError('MCP tool meta resolver must return an object or null.');
}
return resolved;
}
function mcpToFunctionTool(mcpTool, server, convertSchemasToStrict, options = {}) {
const rawToolName = mcpTool.name;
const toolName = options.toolNameOverride ?? rawToolName;
const binding = {
serverName: (0, mcpLogging_1.getMcpServerExternalName)(server.name),
toolName: rawToolName,
serverIndex: null,
};
const annotations = mcpTool.annotations === undefined
? undefined
: structuredClone(mcpTool.annotations);
function finishConversion(converted) {
if (annotations !== undefined) {
Object.defineProperty(converted, 'mcpAnnotations', {
get: () => structuredClone(annotations),
});
}
return (0, toolIdentity_1.bindMcpTool)(converted, binding);
}
const inputGuardrails = server.toolInputGuardrails
? [...server.toolInputGuardrails]
: undefined;
const outputGuardrails = server.toolOutputGuardrails
? [...server.toolOutputGuardrails]
: undefined;
const customDataByCall = new WeakMap();
const serverErrorFunction = server.errorFunction;
const mcpErrorFunction = serverErrorFunction !== undefined
? serverErrorFunction
: options.errorFunction;
const errorFunction = typeof mcpErrorFunction === 'function'
? (context, error) => mcpErrorFunction({ context, error })
: mcpErrorFunction;
async function invoke(input, runContext, details) {
let args = {};
if (typeof input === 'string' && input) {
args = JSON.parse(input);
}
else if (typeof input === 'object' && input != null) {
args = input;
}
const currentSpan = (0, agentToolRunConfig_1.getToolCallParentSpanFromDetails)(details) ?? (0, tracing_1.getCurrentSpan)();
if (currentSpan) {
currentSpan.spanData['mcp_data'] = {
server: (0, mcpLogging_1.getMcpServerExternalName)(server.name),
};
}
const meta = runContext
? await resolveMcpToolMeta(server, runContext, rawToolName, args)
: undefined;
const callOptions = details?.signal
? { signal: details.signal }
: undefined;
const useFullResult = (server.useStructuredContent === true ||
server.customDataExtractor !== undefined) &&
server.callToolResult !== undefined;
let result;
if (useFullResult) {
result = callOptions
? await server.callToolResult(rawToolName, args, meta, callOptions)
: meta === undefined
? await server.callToolResult(rawToolName, args)
: await server.callToolResult(rawToolName, args, meta);
}
else {
result = {
content: callOptions
? await server.callTool(rawToolName, args, meta, callOptions)
: meta === undefined
? await server.callTool(rawToolName, args)
: await server.callTool(rawToolName, args, meta),
};
}
const content = result.content;
const resultMeta = result._meta ?? content._meta;
const structuredContent = result.structuredContent ?? content.structuredContent;
const isError = result.isError ?? content.isError;
const toolOutput = server.useStructuredContent === true &&
isError !== true &&
structuredContent !== undefined
? JSON.stringify(structuredContent)
: content.length === 1
? content[0]
: content;
if (runContext && details?.toolCall?.callId && server.customDataExtractor) {
let byCall = customDataByCall.get(runContext);
if (!byCall) {
byCall = new Map();
customDataByCall.set(runContext, byCall);
}
byCall.set(details.toolCall.callId, {
runContext,
serverName: server.name,
toolName: rawToolName,
toolDisplayName: toolName,
arguments: cloneMcpCustomDataContextValue(args),
resultMeta: cloneMcpCustomDataContextValue(resultMeta),
structuredContent: cloneMcpCustomDataContextValue(structuredContent),
isError,
toolOutput: cloneMcpCustomDataContextValue(toolOutput),
});
}
return toolOutput;
}
async function extractCustomData(context) {
const mcpContext = getMcpCustomDataContext(customDataByCall, context.runContext, context.toolCall.callId);
return mcpContext
? (0, customData_1.maybeExtractToolOutputCustomData)(server.customDataExtractor, {
...mcpContext,
toolOutput: context.output,
})
: undefined;
}
const inputSchema = mcpTool.inputSchema ?? {};
const inputSchemaIsEmpty = Object.keys(inputSchema).length === 0;
const schema = {
...inputSchema,
type: inputSchema.type ?? 'object',
properties: inputSchema.properties ?? {},
required: inputSchema.required ?? [],
};
const strictSchema = (inputSchemaIsEmpty ? { ...schema, additionalProperties: false } : schema);
let preserveSchemaOnFallback = false;
if (convertSchemasToStrict || schema.additionalProperties === true) {
try {
(0, strictToolSchema_1.assertOpenAIStrictToolSchemaPreservesOpenObjects)(strictSchema);
return finishConversion((0, tool_1.tool)({
name: toolName,
description: mcpTool.description || '',
parameters: strictSchema,
strict: true,
execute: invoke,
errorFunction,
inputGuardrails,
outputGuardrails,
customDataExtractor: extractCustomData,
}));
}
catch (e) {
if (convertSchemasToStrict && (0, strictToolSchema_1.isJsonSchemaDepthError)(e)) {
throw e;
}
preserveSchemaOnFallback = true;
(0, logger_1.logToolActionWarning)(logger_1.logger, 'Error converting MCP schema to strict mode:', e);
}
}
const nonStrictSchema = {
...schema,
additionalProperties: preserveSchemaOnFallback && 'additionalProperties' in schema
? schema.additionalProperties
: true,
};
return finishConversion((0, tool_1.tool)({
name: toolName,
description: mcpTool.description || '',
parameters: nonStrictSchema,
strict: false,
execute: invoke,
errorFunction,
inputGuardrails,
outputGuardrails,
customDataExtractor: extractCustomData,
}));
}
function getMcpCustomDataContext(contexts, runContext, callId) {
const byCall = contexts.get(runContext);
const context = byCall?.get(callId);
byCall?.delete(callId);
return context;
}
function cloneMcpCustomDataContextValue(value) {
try {
return structuredClone(value);
}
catch {
return value;
}
}
//# sourceMappingURL=mcp.js.map