@truefoundry/trueforge-core 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/agent-session/ITurnResourceResolver.d.ts +8 -7
- package/dist/agent-session/ITurnResourceResolver.d.ts.map +1 -1
- package/dist/agent-session/ITurnResourceResolver.js.map +1 -1
- package/dist/agent-session/SessionHandle.d.ts +12 -0
- package/dist/agent-session/SessionHandle.d.ts.map +1 -1
- package/dist/agent-session/SessionHandle.js +74 -33
- package/dist/agent-session/SessionHandle.js.map +1 -1
- package/dist/agent-session/SessionHandle.mjs +74 -33
- package/dist/agent-session/SessionHandle.mjs.map +1 -1
- package/dist/agent-session/TurnResourceResolver.d.ts +3 -8
- package/dist/agent-session/TurnResourceResolver.d.ts.map +1 -1
- package/dist/agent-session/TurnResourceResolver.js +6 -2
- package/dist/agent-session/TurnResourceResolver.js.map +1 -1
- package/dist/agent-session/TurnResourceResolver.mjs +6 -2
- package/dist/agent-session/TurnResourceResolver.mjs.map +1 -1
- package/dist/agent-session/schemas/turn.d.ts.map +1 -1
- package/dist/agent-session/schemas/turn.js +3 -1
- package/dist/agent-session/schemas/turn.js.map +1 -1
- package/dist/agent-session/schemas/turn.mjs +3 -1
- package/dist/agent-session/schemas/turn.mjs.map +1 -1
- package/dist/agent-session/store/ISessionStore.d.ts +7 -6
- package/dist/agent-session/store/ISessionStore.d.ts.map +1 -1
- package/dist/agent-session/store/ISessionStore.js.map +1 -1
- package/dist/agent-session/store/InMemorySessionStore.d.ts.map +1 -1
- package/dist/agent-session/store/InMemorySessionStore.js +25 -10
- package/dist/agent-session/store/InMemorySessionStore.js.map +1 -1
- package/dist/agent-session/store/InMemorySessionStore.mjs +25 -10
- package/dist/agent-session/store/InMemorySessionStore.mjs.map +1 -1
- package/dist/agent-session/store/OffsetPageToken.d.ts.map +1 -1
- package/dist/agent-session/store/OffsetPageToken.js +3 -1
- package/dist/agent-session/store/OffsetPageToken.js.map +1 -1
- package/dist/agent-session/store/OffsetPageToken.mjs +3 -1
- package/dist/agent-session/store/OffsetPageToken.mjs.map +1 -1
- package/dist/agent-session/store/SessionListPageToken.d.ts.map +1 -1
- package/dist/agent-session/store/SessionListPageToken.js +3 -1
- package/dist/agent-session/store/SessionListPageToken.js.map +1 -1
- package/dist/agent-session/store/SessionListPageToken.mjs +3 -1
- package/dist/agent-session/store/SessionListPageToken.mjs.map +1 -1
- package/dist/core/InstructionBuilder.d.ts.map +1 -1
- package/dist/core/InstructionBuilder.js +12 -4
- package/dist/core/InstructionBuilder.js.map +1 -1
- package/dist/core/InstructionBuilder.mjs +12 -4
- package/dist/core/InstructionBuilder.mjs.map +1 -1
- package/dist/core/capabilities/builtins/AskUserQuestion.d.ts.map +1 -1
- package/dist/core/capabilities/builtins/AskUserQuestion.js +5 -0
- package/dist/core/capabilities/builtins/AskUserQuestion.js.map +1 -1
- package/dist/core/capabilities/builtins/AskUserQuestion.mjs +5 -0
- package/dist/core/capabilities/builtins/AskUserQuestion.mjs.map +1 -1
- package/dist/core/capabilities/builtins/LargeToolResponse.d.ts.map +1 -1
- package/dist/core/capabilities/builtins/LargeToolResponse.js +6 -2
- package/dist/core/capabilities/builtins/LargeToolResponse.js.map +1 -1
- package/dist/core/capabilities/builtins/LargeToolResponse.mjs +6 -2
- package/dist/core/capabilities/builtins/LargeToolResponse.mjs.map +1 -1
- package/dist/core/llm/VercelAILLM.d.ts.map +1 -1
- package/dist/core/llm/VercelAILLM.js +6 -2
- package/dist/core/llm/VercelAILLM.js.map +1 -1
- package/dist/core/llm/VercelAILLM.mjs +6 -2
- package/dist/core/llm/VercelAILLM.mjs.map +1 -1
- package/dist/core/mcp/RemoteMCP.d.ts.map +1 -1
- package/dist/core/mcp/RemoteMCP.js +15 -5
- package/dist/core/mcp/RemoteMCP.js.map +1 -1
- package/dist/core/mcp/RemoteMCP.mjs +15 -5
- package/dist/core/mcp/RemoteMCP.mjs.map +1 -1
- package/dist/core/mcp/ToolSelectorPolicy.d.ts.map +1 -1
- package/dist/core/mcp/ToolSelectorPolicy.js +3 -1
- package/dist/core/mcp/ToolSelectorPolicy.js.map +1 -1
- package/dist/core/mcp/ToolSelectorPolicy.mjs +3 -1
- package/dist/core/mcp/ToolSelectorPolicy.mjs.map +1 -1
- package/dist/core/mcp/ToolSet.d.ts.map +1 -1
- package/dist/core/mcp/ToolSet.js +9 -3
- package/dist/core/mcp/ToolSet.js.map +1 -1
- package/dist/core/mcp/ToolSet.mjs +9 -3
- package/dist/core/mcp/ToolSet.mjs.map +1 -1
- package/dist/core/mcp/convertMCPServers.d.ts.map +1 -1
- package/dist/core/mcp/convertMCPServers.js +6 -2
- package/dist/core/mcp/convertMCPServers.js.map +1 -1
- package/dist/core/mcp/convertMCPServers.mjs +6 -2
- package/dist/core/mcp/convertMCPServers.mjs.map +1 -1
- package/dist/core/mcp/remoteMcpClient.d.ts.map +1 -1
- package/dist/core/mcp/remoteMcpClient.js +21 -7
- package/dist/core/mcp/remoteMcpClient.js.map +1 -1
- package/dist/core/mcp/remoteMcpClient.mjs +21 -7
- package/dist/core/mcp/remoteMcpClient.mjs.map +1 -1
- package/dist/core/mcp/toolSelectors.d.ts.map +1 -1
- package/dist/core/mcp/toolSelectors.js +18 -6
- package/dist/core/mcp/toolSelectors.js.map +1 -1
- package/dist/core/mcp/toolSelectors.mjs +18 -6
- package/dist/core/mcp/toolSelectors.mjs.map +1 -1
- package/dist/core/runtime/AgentThread.d.ts.map +1 -1
- package/dist/core/runtime/AgentThread.js +42 -17
- package/dist/core/runtime/AgentThread.js.map +1 -1
- package/dist/core/runtime/AgentThread.mjs +42 -17
- package/dist/core/runtime/AgentThread.mjs.map +1 -1
- package/dist/core/runtime/AgentThreadOrchestrator.d.ts.map +1 -1
- package/dist/core/runtime/AgentThreadOrchestrator.js +27 -9
- package/dist/core/runtime/AgentThreadOrchestrator.js.map +1 -1
- package/dist/core/runtime/AgentThreadOrchestrator.mjs +27 -9
- package/dist/core/runtime/AgentThreadOrchestrator.mjs.map +1 -1
- package/dist/core/runtime/DeferredTool.d.ts.map +1 -1
- package/dist/core/runtime/DeferredTool.js +15 -5
- package/dist/core/runtime/DeferredTool.js.map +1 -1
- package/dist/core/runtime/DeferredTool.mjs +15 -5
- package/dist/core/runtime/DeferredTool.mjs.map +1 -1
- package/dist/core/runtime/contextUtils.d.ts.map +1 -1
- package/dist/core/runtime/contextUtils.js +3 -1
- package/dist/core/runtime/contextUtils.js.map +1 -1
- package/dist/core/runtime/contextUtils.mjs +3 -1
- package/dist/core/runtime/contextUtils.mjs.map +1 -1
- package/dist/core/sandbox/Sandbox.d.ts.map +1 -1
- package/dist/core/sandbox/Sandbox.js +12 -4
- package/dist/core/sandbox/Sandbox.js.map +1 -1
- package/dist/core/sandbox/Sandbox.mjs +12 -4
- package/dist/core/sandbox/Sandbox.mjs.map +1 -1
- package/dist/core/sandbox/codeMode/CodeModeDispatcher.d.ts.map +1 -1
- package/dist/core/sandbox/codeMode/CodeModeDispatcher.js +9 -3
- package/dist/core/sandbox/codeMode/CodeModeDispatcher.js.map +1 -1
- package/dist/core/sandbox/codeMode/CodeModeDispatcher.mjs +9 -3
- package/dist/core/sandbox/codeMode/CodeModeDispatcher.mjs.map +1 -1
- package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.d.ts.map +1 -1
- package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.js +9 -3
- package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.js.map +1 -1
- package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.mjs +9 -3
- package/dist/core/sandbox/codeMode/nats/CodeModeNatsTransport.mjs.map +1 -1
- package/dist/core/sandbox/provider/DaytonaProvider.d.ts +17 -0
- package/dist/core/sandbox/provider/DaytonaProvider.d.ts.map +1 -1
- package/dist/core/sandbox/provider/DaytonaProvider.js +83 -17
- package/dist/core/sandbox/provider/DaytonaProvider.js.map +1 -1
- package/dist/core/sandbox/provider/DaytonaProvider.mjs +83 -17
- package/dist/core/sandbox/provider/DaytonaProvider.mjs.map +1 -1
- package/dist/core/sandbox/provider/TFYSandboxProvider.d.ts.map +1 -1
- package/dist/core/sandbox/provider/TFYSandboxProvider.js +12 -4
- package/dist/core/sandbox/provider/TFYSandboxProvider.js.map +1 -1
- package/dist/core/sandbox/provider/TFYSandboxProvider.mjs +12 -4
- package/dist/core/sandbox/provider/TFYSandboxProvider.mjs.map +1 -1
- package/dist/core/sandbox/skills/SkillMounter.d.ts.map +1 -1
- package/dist/core/sandbox/skills/SkillMounter.js +3 -1
- package/dist/core/sandbox/skills/SkillMounter.js.map +1 -1
- package/dist/core/sandbox/skills/SkillMounter.mjs +3 -1
- package/dist/core/sandbox/skills/SkillMounter.mjs.map +1 -1
- package/dist/core/util/abort.d.ts.map +1 -1
- package/dist/core/util/abort.js +3 -1
- package/dist/core/util/abort.js.map +1 -1
- package/dist/core/util/abort.mjs +3 -1
- package/dist/core/util/abort.mjs.map +1 -1
- package/package.json +10 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/RemoteMCP.ts"],"sourcesContent":["import type { CallToolRequest } from '@modelcontextprotocol/sdk/types.js';\nimport type { Logger } from 'winston';\nimport { McpConnectionError } from '../errors';\nimport type { MCPServerInitInfo } from '../events/schema';\nimport type { InternalToolCallInfo } from '../llm/LLMTypes';\nimport type { AgentTracing } from '../tracing/AgentTracing';\nimport { NOOP_AGENT_TRACING } from '../tracing/NoopAgentTracing';\nimport {\n type AgentToolSchema,\n type AuthRequiredResponse,\n type CallToolResolvedResponse,\n type ListToolsResolvedResponse,\n type MCPAuthRequired,\n type ToolSource,\n} from './IMCPServer';\nimport { paginateWithCursorGuard } from './pagination';\nimport {\n connectRemoteMcp,\n isSessionExpiredError,\n type RemoteMcpConnection,\n type RemoteMcpTransportType,\n} from './remoteMcpClient';\n\n/** Redacted url for trace spans: scheme + host + path only, so userinfo/query secrets never leak. */\nfunction redactUrlForTrace(url: string): string {\n try {\n const u = new URL(url);\n return `${u.protocol}//${u.host}${u.pathname}`;\n } catch {\n return '';\n }\n}\n\n/** What a headers resolver returns: the headers to send, or a signal that auth is required. */\nexport type ResolveHeadersResult = { headers: Record<string, string> } | { authRequired: MCPAuthRequired };\n\n/** Static headers, or a resolver (invoked at connect) returning headers or signalling auth-required. */\nexport type RemoteMcpHeaders = Record<string, string> | (() => Promise<ResolveHeadersResult>);\n\ntype ExecuteResult<T> = { result: T; wasInitialized: MCPServerInitInfo | undefined } | AuthRequiredResponse;\n\n/**\n * Connection/session half of a remote MCP server: owns the transport (connects itself from `url` +\n * `headers`), session id, raw tool cache, connect single-flight and session-expiry retry. Policy-free\n * — a per-agent {@link ToolSet} layers policy.\n */\nexport class RemoteMCP implements ToolSource {\n readonly name: string;\n readonly id: string;\n readonly description?: string | undefined;\n\n private readonly url: string;\n private readonly headers: RemoteMcpHeaders;\n private readonly signal: AbortSignal;\n private readonly logger: Logger;\n private readonly tracing: AgentTracing;\n private readonly requestTimeoutMs: number;\n private readonly connectTimeoutMs: number;\n // Redacted display url for trace spans (derived from url; unused when tracing is a no-op).\n private readonly traceUrl: string;\n\n private _connection?: RemoteMcpConnection | undefined;\n private isConnected = false;\n private connectPromise: Promise<MCPServerInitInfo | undefined> | undefined;\n // undefined = never connected, string = stateful session id, null = stateless/SSE.\n private sessionId: string | null | undefined;\n private resolvedTransportType?: RemoteMcpTransportType | undefined;\n private cachedTools?: AgentToolSchema[] | undefined;\n\n constructor(params: {\n name: string;\n id: string;\n description?: string | undefined;\n url: string;\n headers: RemoteMcpHeaders;\n logger: Logger;\n tracing?: AgentTracing | undefined;\n sessionId?: string | undefined;\n transportType?: RemoteMcpTransportType | undefined;\n requestTimeoutMs: number;\n connectTimeoutMs: number;\n signal: AbortSignal;\n }) {\n this.name = params.name;\n this.id = params.id;\n this.description = params.description;\n this.url = params.url;\n this.headers = params.headers;\n this.signal = params.signal;\n this.sessionId = params.sessionId;\n this.resolvedTransportType = params.transportType;\n this.requestTimeoutMs = params.requestTimeoutMs;\n this.connectTimeoutMs = params.connectTimeoutMs;\n this.logger = params.logger;\n this.tracing = params.tracing ?? NOOP_AGENT_TRACING;\n this.traceUrl = redactUrlForTrace(params.url);\n }\n\n getSessionId(): string | undefined {\n return this.sessionId ?? undefined;\n }\n\n private get connection(): RemoteMcpConnection {\n if (!this._connection) {\n throw new Error(`Remote MCP '${this.name}' not connected - connectIfNeeded() must run first`);\n }\n return this._connection;\n }\n\n private async resetConnection(): Promise<void> {\n this.isConnected = false;\n this.sessionId = undefined;\n this.cachedTools = undefined;\n this.connectPromise = undefined;\n await this.closeAndClearConnection();\n }\n\n private async closeAndClearConnection(): Promise<void> {\n await this._connection?.close().catch(() => {\n /* no-op */\n });\n this._connection = undefined;\n }\n\n private async resolveHeaders(): Promise<ResolveHeadersResult> {\n if (typeof this.headers !== 'function') return { headers: this.headers };\n return await this.headers();\n }\n\n private async executeWithSessionRetry<T>(operation: () => Promise<T>): Promise<ExecuteResult<T>> {\n // Auth is re-checked on every operation, not only on the first connect: a registered server's OAuth\n // can be revoked or expire mid-request, and callers must get authRequired rather than a generic\n // upstream failure. When already connected the resolved headers are unused (connect is skipped).\n const headersResult = await this.resolveHeaders();\n if ('authRequired' in headersResult) return { authRequired: headersResult.authRequired };\n return this.connectAndRun(headersResult.headers, operation, true);\n }\n\n private async connectAndRun<T>(\n headers: Record<string, string>,\n operation: () => Promise<T>,\n canRetry: boolean,\n ): Promise<ExecuteResult<T>> {\n try {\n const initInfo = await this.connectIfNeeded(headers);\n return { result: await operation(), wasInitialized: initInfo };\n } catch (error) {\n if (canRetry && isSessionExpiredError(error)) {\n this.logger.info(`Session expired for remote MCP ${this.name}, reinitializing...`);\n await this.resetConnection();\n return this.connectAndRun(headers, operation, false);\n }\n throw error;\n }\n }\n\n private async loadTools(): Promise<{ tools: AgentToolSchema[] }> {\n return this.tracing.withRemoteMcpToolSpan(\n { method: 'tools/list', serverName: this.name, serverId: this.id, serverUrl: this.traceUrl, enabled: true },\n async span => {\n const tools = await paginateWithCursorGuard(\n async cursor => {\n const page = await this.connection.listTools(cursor);\n return { items: page.tools, nextCursor: page.nextCursor };\n },\n this.name,\n this.logger,\n );\n this.cachedTools = tools.map(t => ({ ...t, preload: true }));\n span.setNumberOfTools(this.cachedTools.length);\n return { tools: this.cachedTools };\n },\n );\n }\n\n /** Unfiltered tool list (no policy). The per-agent wrapper applies selectors on top. */\n async listTools(): Promise<ListToolsResolvedResponse | AuthRequiredResponse> {\n if (this.cachedTools) {\n return { result: { tools: this.cachedTools }, wasInitialized: undefined };\n }\n const response = await this.executeWithSessionRetry(() => this.loadTools());\n if ('authRequired' in response) return response;\n return { result: { tools: response.result.tools }, wasInitialized: response.wasInitialized };\n }\n\n async callTool(params: CallToolRequest['params']): Promise<CallToolResolvedResponse | AuthRequiredResponse> {\n const response = await this.executeWithSessionRetry(() =>\n this.tracing.withRemoteMcpToolSpan(\n {\n method: 'tools/call',\n serverName: this.name,\n serverId: this.id,\n serverUrl: this.traceUrl,\n toolName: params.name,\n input: JSON.stringify(params.arguments),\n enabled: true,\n },\n async span => {\n const result = await this.connection.callTool(params);\n span.setOutput(JSON.stringify(result));\n return result;\n },\n ),\n );\n if ('authRequired' in response) return response;\n return { result: response.result, wasInitialized: response.wasInitialized };\n }\n\n toolCallInfo(params: CallToolRequest['params'], _resolveUnderlyingTool?: boolean): Promise<InternalToolCallInfo> {\n void _resolveUnderlyingTool;\n return Promise.resolve({\n type: 'mcp',\n original_tool_name: params.name,\n mcp_server_id: this.id,\n mcp_server_name: this.name,\n is_approval_required: false,\n });\n }\n\n private async connectIfNeeded(headers: Record<string, string>): Promise<MCPServerInitInfo | undefined> {\n if (this.isConnected) return undefined;\n if (this.connectPromise) {\n // Wait for the in-flight connect, but only its originator emits the init metadata.\n await this.connectPromise;\n return undefined;\n }\n\n const existingSessionId = this.sessionId;\n this.connectPromise = (async (): Promise<MCPServerInitInfo | undefined> => {\n let connection: RemoteMcpConnection;\n try {\n await this.closeAndClearConnection();\n connection = await this.tracing.withRemoteMcpToolSpan(\n { method: 'initialize', serverName: this.name, serverId: this.id, serverUrl: this.traceUrl, enabled: true },\n async span => {\n const conn = await connectRemoteMcp({\n url: this.url,\n headers,\n sessionId: this.sessionId ?? undefined,\n // Hint from a prior connect\n knownTransportType: this.resolvedTransportType,\n requestTimeoutMs: this.requestTimeoutMs,\n connectTimeoutMs: this.connectTimeoutMs,\n signal: this.signal,\n onClose: () => {\n this.isConnected = false;\n },\n onError: error => {\n this.logger.error(`Error on remote MCP transport ${this.name}`, { error });\n },\n });\n span.setOutput(JSON.stringify({ transport: conn.transportType, stateful: conn.sessionId !== null }));\n return conn;\n },\n );\n } catch (error) {\n await this.closeAndClearConnection();\n throw this.toConnectError(error);\n }\n this._connection = connection;\n this.resolvedTransportType = connection.transportType;\n this.isConnected = true;\n this.sessionId = connection.sessionId;\n if (existingSessionId === this.sessionId) {\n return undefined;\n }\n return {\n name: this.name,\n id: this.id,\n session_id: this.sessionId ?? undefined,\n transport_type: this.resolvedTransportType,\n };\n })().finally(() => {\n this.connectPromise = undefined;\n });\n return this.connectPromise;\n }\n\n private toConnectError(error: unknown): McpConnectionError {\n const statusCode = error instanceof McpConnectionError ? error.statusCode : 502;\n const message = error instanceof Error ? error.message : String(error);\n return new McpConnectionError(`Failed to connect to remote MCP server '${this.name}': ${message}`, statusCode, {\n cause: error,\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/RemoteMCP.ts"],"sourcesContent":["import type { CallToolRequest } from '@modelcontextprotocol/sdk/types.js';\nimport type { Logger } from 'winston';\nimport { McpConnectionError } from '../errors';\nimport type { MCPServerInitInfo } from '../events/schema';\nimport type { InternalToolCallInfo } from '../llm/LLMTypes';\nimport type { AgentTracing } from '../tracing/AgentTracing';\nimport { NOOP_AGENT_TRACING } from '../tracing/NoopAgentTracing';\nimport {\n type AgentToolSchema,\n type AuthRequiredResponse,\n type CallToolResolvedResponse,\n type ListToolsResolvedResponse,\n type MCPAuthRequired,\n type ToolSource,\n} from './IMCPServer';\nimport { paginateWithCursorGuard } from './pagination';\nimport {\n connectRemoteMcp,\n isSessionExpiredError,\n type RemoteMcpConnection,\n type RemoteMcpTransportType,\n} from './remoteMcpClient';\n\n/** Redacted url for trace spans: scheme + host + path only, so userinfo/query secrets never leak. */\nfunction redactUrlForTrace(url: string): string {\n try {\n const u = new URL(url);\n return `${u.protocol}//${u.host}${u.pathname}`;\n } catch {\n return '';\n }\n}\n\n/** What a headers resolver returns: the headers to send, or a signal that auth is required. */\nexport type ResolveHeadersResult = { headers: Record<string, string> } | { authRequired: MCPAuthRequired };\n\n/** Static headers, or a resolver (invoked at connect) returning headers or signalling auth-required. */\nexport type RemoteMcpHeaders = Record<string, string> | (() => Promise<ResolveHeadersResult>);\n\ntype ExecuteResult<T> = { result: T; wasInitialized: MCPServerInitInfo | undefined } | AuthRequiredResponse;\n\n/**\n * Connection/session half of a remote MCP server: owns the transport (connects itself from `url` +\n * `headers`), session id, raw tool cache, connect single-flight and session-expiry retry. Policy-free\n * — a per-agent {@link ToolSet} layers policy.\n */\nexport class RemoteMCP implements ToolSource {\n readonly name: string;\n readonly id: string;\n readonly description?: string | undefined;\n\n private readonly url: string;\n private readonly headers: RemoteMcpHeaders;\n private readonly signal: AbortSignal;\n private readonly logger: Logger;\n private readonly tracing: AgentTracing;\n private readonly requestTimeoutMs: number;\n private readonly connectTimeoutMs: number;\n // Redacted display url for trace spans (derived from url; unused when tracing is a no-op).\n private readonly traceUrl: string;\n\n private _connection?: RemoteMcpConnection | undefined;\n private isConnected = false;\n private connectPromise: Promise<MCPServerInitInfo | undefined> | undefined;\n // undefined = never connected, string = stateful session id, null = stateless/SSE.\n private sessionId: string | null | undefined;\n private resolvedTransportType?: RemoteMcpTransportType | undefined;\n private cachedTools?: AgentToolSchema[] | undefined;\n\n constructor(params: {\n name: string;\n id: string;\n description?: string | undefined;\n url: string;\n headers: RemoteMcpHeaders;\n logger: Logger;\n tracing?: AgentTracing | undefined;\n sessionId?: string | undefined;\n transportType?: RemoteMcpTransportType | undefined;\n requestTimeoutMs: number;\n connectTimeoutMs: number;\n signal: AbortSignal;\n }) {\n this.name = params.name;\n this.id = params.id;\n this.description = params.description;\n this.url = params.url;\n this.headers = params.headers;\n this.signal = params.signal;\n this.sessionId = params.sessionId;\n this.resolvedTransportType = params.transportType;\n this.requestTimeoutMs = params.requestTimeoutMs;\n this.connectTimeoutMs = params.connectTimeoutMs;\n this.logger = params.logger;\n this.tracing = params.tracing ?? NOOP_AGENT_TRACING;\n this.traceUrl = redactUrlForTrace(params.url);\n }\n\n getSessionId(): string | undefined {\n return this.sessionId ?? undefined;\n }\n\n private get connection(): RemoteMcpConnection {\n if (!this._connection) {\n throw new Error(`Remote MCP '${this.name}' not connected - connectIfNeeded() must run first`);\n }\n return this._connection;\n }\n\n private async resetConnection(): Promise<void> {\n this.isConnected = false;\n this.sessionId = undefined;\n this.cachedTools = undefined;\n this.connectPromise = undefined;\n await this.closeAndClearConnection();\n }\n\n private async closeAndClearConnection(): Promise<void> {\n await this._connection?.close().catch(() => {\n /* no-op */\n });\n this._connection = undefined;\n }\n\n private async resolveHeaders(): Promise<ResolveHeadersResult> {\n if (typeof this.headers !== 'function') {\n return { headers: this.headers };\n }\n return await this.headers();\n }\n\n private async executeWithSessionRetry<T>(operation: () => Promise<T>): Promise<ExecuteResult<T>> {\n // Auth is re-checked on every operation, not only on the first connect: a registered server's OAuth\n // can be revoked or expire mid-request, and callers must get authRequired rather than a generic\n // upstream failure. When already connected the resolved headers are unused (connect is skipped).\n const headersResult = await this.resolveHeaders();\n if ('authRequired' in headersResult) {\n return { authRequired: headersResult.authRequired };\n }\n return this.connectAndRun(headersResult.headers, operation, true);\n }\n\n private async connectAndRun<T>(\n headers: Record<string, string>,\n operation: () => Promise<T>,\n canRetry: boolean,\n ): Promise<ExecuteResult<T>> {\n try {\n const initInfo = await this.connectIfNeeded(headers);\n return { result: await operation(), wasInitialized: initInfo };\n } catch (error) {\n if (canRetry && isSessionExpiredError(error)) {\n this.logger.info(`Session expired for remote MCP ${this.name}, reinitializing...`);\n await this.resetConnection();\n return this.connectAndRun(headers, operation, false);\n }\n throw error;\n }\n }\n\n private async loadTools(): Promise<{ tools: AgentToolSchema[] }> {\n return this.tracing.withRemoteMcpToolSpan(\n { method: 'tools/list', serverName: this.name, serverId: this.id, serverUrl: this.traceUrl, enabled: true },\n async span => {\n const tools = await paginateWithCursorGuard(\n async cursor => {\n const page = await this.connection.listTools(cursor);\n return { items: page.tools, nextCursor: page.nextCursor };\n },\n this.name,\n this.logger,\n );\n this.cachedTools = tools.map(t => ({ ...t, preload: true }));\n span.setNumberOfTools(this.cachedTools.length);\n return { tools: this.cachedTools };\n },\n );\n }\n\n /** Unfiltered tool list (no policy). The per-agent wrapper applies selectors on top. */\n async listTools(): Promise<ListToolsResolvedResponse | AuthRequiredResponse> {\n if (this.cachedTools) {\n return { result: { tools: this.cachedTools }, wasInitialized: undefined };\n }\n const response = await this.executeWithSessionRetry(() => this.loadTools());\n if ('authRequired' in response) {\n return response;\n }\n return { result: { tools: response.result.tools }, wasInitialized: response.wasInitialized };\n }\n\n async callTool(params: CallToolRequest['params']): Promise<CallToolResolvedResponse | AuthRequiredResponse> {\n const response = await this.executeWithSessionRetry(() =>\n this.tracing.withRemoteMcpToolSpan(\n {\n method: 'tools/call',\n serverName: this.name,\n serverId: this.id,\n serverUrl: this.traceUrl,\n toolName: params.name,\n input: JSON.stringify(params.arguments),\n enabled: true,\n },\n async span => {\n const result = await this.connection.callTool(params);\n span.setOutput(JSON.stringify(result));\n return result;\n },\n ),\n );\n if ('authRequired' in response) {\n return response;\n }\n return { result: response.result, wasInitialized: response.wasInitialized };\n }\n\n toolCallInfo(params: CallToolRequest['params'], _resolveUnderlyingTool?: boolean): Promise<InternalToolCallInfo> {\n void _resolveUnderlyingTool;\n return Promise.resolve({\n type: 'mcp',\n original_tool_name: params.name,\n mcp_server_id: this.id,\n mcp_server_name: this.name,\n is_approval_required: false,\n });\n }\n\n private async connectIfNeeded(headers: Record<string, string>): Promise<MCPServerInitInfo | undefined> {\n if (this.isConnected) {\n return undefined;\n }\n if (this.connectPromise) {\n // Wait for the in-flight connect, but only its originator emits the init metadata.\n await this.connectPromise;\n return undefined;\n }\n\n const existingSessionId = this.sessionId;\n this.connectPromise = (async (): Promise<MCPServerInitInfo | undefined> => {\n let connection: RemoteMcpConnection;\n try {\n await this.closeAndClearConnection();\n connection = await this.tracing.withRemoteMcpToolSpan(\n { method: 'initialize', serverName: this.name, serverId: this.id, serverUrl: this.traceUrl, enabled: true },\n async span => {\n const conn = await connectRemoteMcp({\n url: this.url,\n headers,\n sessionId: this.sessionId ?? undefined,\n // Hint from a prior connect\n knownTransportType: this.resolvedTransportType,\n requestTimeoutMs: this.requestTimeoutMs,\n connectTimeoutMs: this.connectTimeoutMs,\n signal: this.signal,\n onClose: () => {\n this.isConnected = false;\n },\n onError: error => {\n this.logger.error(`Error on remote MCP transport ${this.name}`, { error });\n },\n });\n span.setOutput(JSON.stringify({ transport: conn.transportType, stateful: conn.sessionId !== null }));\n return conn;\n },\n );\n } catch (error) {\n await this.closeAndClearConnection();\n throw this.toConnectError(error);\n }\n this._connection = connection;\n this.resolvedTransportType = connection.transportType;\n this.isConnected = true;\n this.sessionId = connection.sessionId;\n if (existingSessionId === this.sessionId) {\n return undefined;\n }\n return {\n name: this.name,\n id: this.id,\n session_id: this.sessionId ?? undefined,\n transport_type: this.resolvedTransportType,\n };\n })().finally(() => {\n this.connectPromise = undefined;\n });\n return this.connectPromise;\n }\n\n private toConnectError(error: unknown): McpConnectionError {\n const statusCode = error instanceof McpConnectionError ? error.statusCode : 502;\n const message = error instanceof Error ? error.message : String(error);\n return new McpConnectionError(`Failed to connect to remote MCP server '${this.name}': ${message}`, statusCode, {\n cause: error,\n });\n }\n}\n"],"mappings":";AAEA,SAAS,0BAA0B;AAInC,SAAS,0BAA0B;AACnC,OAOO;AACP,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP,SAAS,kBAAkB,KAAqB;AAC9C,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,GAAG;AACrB,WAAO,GAAG,EAAE,QAAQ,KAAK,EAAE,IAAI,GAAG,EAAE,QAAQ;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeO,IAAM,YAAN,MAAsC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EAET;AAAA,EACA,cAAc;AAAA,EACd;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAaT;AACD,SAAK,OAAO,OAAO;AACnB,SAAK,KAAK,OAAO;AACjB,SAAK,cAAc,OAAO;AAC1B,SAAK,MAAM,OAAO;AAClB,SAAK,UAAU,OAAO;AACtB,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO;AACxB,SAAK,wBAAwB,OAAO;AACpC,SAAK,mBAAmB,OAAO;AAC/B,SAAK,mBAAmB,OAAO;AAC/B,SAAK,SAAS,OAAO;AACrB,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,WAAW,kBAAkB,OAAO,GAAG;AAAA,EAC9C;AAAA,EAEA,eAAmC;AACjC,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA,EAEA,IAAY,aAAkC;AAC5C,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI,MAAM,eAAe,KAAK,IAAI,oDAAoD;AAAA,IAC9F;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,kBAAiC;AAC7C,SAAK,cAAc;AACnB,SAAK,YAAY;AACjB,SAAK,cAAc;AACnB,SAAK,iBAAiB;AACtB,UAAM,KAAK,wBAAwB;AAAA,EACrC;AAAA,EAEA,MAAc,0BAAyC;AACrD,UAAM,KAAK,aAAa,MAAM,EAAE,MAAM,MAAM;AAAA,IAE5C,CAAC;AACD,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,MAAc,iBAAgD;AAC5D,QAAI,OAAO,KAAK,YAAY,YAAY;AACtC,aAAO,EAAE,SAAS,KAAK,QAAQ;AAAA,IACjC;AACA,WAAO,MAAM,KAAK,QAAQ;AAAA,EAC5B;AAAA,EAEA,MAAc,wBAA2B,WAAwD;AAI/F,UAAM,gBAAgB,MAAM,KAAK,eAAe;AAChD,QAAI,kBAAkB,eAAe;AACnC,aAAO,EAAE,cAAc,cAAc,aAAa;AAAA,IACpD;AACA,WAAO,KAAK,cAAc,cAAc,SAAS,WAAW,IAAI;AAAA,EAClE;AAAA,EAEA,MAAc,cACZ,SACA,WACA,UAC2B;AAC3B,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,gBAAgB,OAAO;AACnD,aAAO,EAAE,QAAQ,MAAM,UAAU,GAAG,gBAAgB,SAAS;AAAA,IAC/D,SAAS,OAAO;AACd,UAAI,YAAY,sBAAsB,KAAK,GAAG;AAC5C,aAAK,OAAO,KAAK,kCAAkC,KAAK,IAAI,qBAAqB;AACjF,cAAM,KAAK,gBAAgB;AAC3B,eAAO,KAAK,cAAc,SAAS,WAAW,KAAK;AAAA,MACrD;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,YAAmD;AAC/D,WAAO,KAAK,QAAQ;AAAA,MAClB,EAAE,QAAQ,cAAc,YAAY,KAAK,MAAM,UAAU,KAAK,IAAI,WAAW,KAAK,UAAU,SAAS,KAAK;AAAA,MAC1G,OAAM,SAAQ;AACZ,cAAM,QAAQ,MAAM;AAAA,UAClB,OAAM,WAAU;AACd,kBAAM,OAAO,MAAM,KAAK,WAAW,UAAU,MAAM;AACnD,mBAAO,EAAE,OAAO,KAAK,OAAO,YAAY,KAAK,WAAW;AAAA,UAC1D;AAAA,UACA,KAAK;AAAA,UACL,KAAK;AAAA,QACP;AACA,aAAK,cAAc,MAAM,IAAI,QAAM,EAAE,GAAG,GAAG,SAAS,KAAK,EAAE;AAC3D,aAAK,iBAAiB,KAAK,YAAY,MAAM;AAC7C,eAAO,EAAE,OAAO,KAAK,YAAY;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,YAAuE;AAC3E,QAAI,KAAK,aAAa;AACpB,aAAO,EAAE,QAAQ,EAAE,OAAO,KAAK,YAAY,GAAG,gBAAgB,OAAU;AAAA,IAC1E;AACA,UAAM,WAAW,MAAM,KAAK,wBAAwB,MAAM,KAAK,UAAU,CAAC;AAC1E,QAAI,kBAAkB,UAAU;AAC9B,aAAO;AAAA,IACT;AACA,WAAO,EAAE,QAAQ,EAAE,OAAO,SAAS,OAAO,MAAM,GAAG,gBAAgB,SAAS,eAAe;AAAA,EAC7F;AAAA,EAEA,MAAM,SAAS,QAA6F;AAC1G,UAAM,WAAW,MAAM,KAAK;AAAA,MAAwB,MAClD,KAAK,QAAQ;AAAA,QACX;AAAA,UACE,QAAQ;AAAA,UACR,YAAY,KAAK;AAAA,UACjB,UAAU,KAAK;AAAA,UACf,WAAW,KAAK;AAAA,UAChB,UAAU,OAAO;AAAA,UACjB,OAAO,KAAK,UAAU,OAAO,SAAS;AAAA,UACtC,SAAS;AAAA,QACX;AAAA,QACA,OAAM,SAAQ;AACZ,gBAAM,SAAS,MAAM,KAAK,WAAW,SAAS,MAAM;AACpD,eAAK,UAAU,KAAK,UAAU,MAAM,CAAC;AACrC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,QAAI,kBAAkB,UAAU;AAC9B,aAAO;AAAA,IACT;AACA,WAAO,EAAE,QAAQ,SAAS,QAAQ,gBAAgB,SAAS,eAAe;AAAA,EAC5E;AAAA,EAEA,aAAa,QAAmC,wBAAiE;AAC/G,SAAK;AACL,WAAO,QAAQ,QAAQ;AAAA,MACrB,MAAM;AAAA,MACN,oBAAoB,OAAO;AAAA,MAC3B,eAAe,KAAK;AAAA,MACpB,iBAAiB,KAAK;AAAA,MACtB,sBAAsB;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,gBAAgB,SAAyE;AACrG,QAAI,KAAK,aAAa;AACpB,aAAO;AAAA,IACT;AACA,QAAI,KAAK,gBAAgB;AAEvB,YAAM,KAAK;AACX,aAAO;AAAA,IACT;AAEA,UAAM,oBAAoB,KAAK;AAC/B,SAAK,kBAAkB,YAAoD;AACzE,UAAI;AACJ,UAAI;AACF,cAAM,KAAK,wBAAwB;AACnC,qBAAa,MAAM,KAAK,QAAQ;AAAA,UAC9B,EAAE,QAAQ,cAAc,YAAY,KAAK,MAAM,UAAU,KAAK,IAAI,WAAW,KAAK,UAAU,SAAS,KAAK;AAAA,UAC1G,OAAM,SAAQ;AACZ,kBAAM,OAAO,MAAM,iBAAiB;AAAA,cAClC,KAAK,KAAK;AAAA,cACV;AAAA,cACA,WAAW,KAAK,aAAa;AAAA;AAAA,cAE7B,oBAAoB,KAAK;AAAA,cACzB,kBAAkB,KAAK;AAAA,cACvB,kBAAkB,KAAK;AAAA,cACvB,QAAQ,KAAK;AAAA,cACb,SAAS,MAAM;AACb,qBAAK,cAAc;AAAA,cACrB;AAAA,cACA,SAAS,WAAS;AAChB,qBAAK,OAAO,MAAM,iCAAiC,KAAK,IAAI,IAAI,EAAE,MAAM,CAAC;AAAA,cAC3E;AAAA,YACF,CAAC;AACD,iBAAK,UAAU,KAAK,UAAU,EAAE,WAAW,KAAK,eAAe,UAAU,KAAK,cAAc,KAAK,CAAC,CAAC;AACnG,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,cAAM,KAAK,wBAAwB;AACnC,cAAM,KAAK,eAAe,KAAK;AAAA,MACjC;AACA,WAAK,cAAc;AACnB,WAAK,wBAAwB,WAAW;AACxC,WAAK,cAAc;AACnB,WAAK,YAAY,WAAW;AAC5B,UAAI,sBAAsB,KAAK,WAAW;AACxC,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL,MAAM,KAAK;AAAA,QACX,IAAI,KAAK;AAAA,QACT,YAAY,KAAK,aAAa;AAAA,QAC9B,gBAAgB,KAAK;AAAA,MACvB;AAAA,IACF,GAAG,EAAE,QAAQ,MAAM;AACjB,WAAK,iBAAiB;AAAA,IACxB,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,eAAe,OAAoC;AACzD,UAAM,aAAa,iBAAiB,qBAAqB,MAAM,aAAa;AAC5E,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,WAAO,IAAI,mBAAmB,2CAA2C,KAAK,IAAI,MAAM,OAAO,IAAI,YAAY;AAAA,MAC7G,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolSelectorPolicy.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/ToolSelectorPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAUpD;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAChD;AAED;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAW;IAGnD,OAAO,CAAC,wBAAwB,CAAuB;IAEvD,YAAY,MAAM,EAAE;QAAE,SAAS,EAAE,kBAAkB,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAOtE;IAED,kGAAkG;IAClG,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAGxD;IAED,+FAA+F;IAC/F,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAW7D;IAED,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"ToolSelectorPolicy.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/ToolSelectorPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAUpD;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAChD;AAED;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;IACxC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAW;IAGnD,OAAO,CAAC,wBAAwB,CAAuB;IAEvD,YAAY,MAAM,EAAE;QAAE,SAAS,EAAE,kBAAkB,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAOtE;IAED,kGAAkG;IAClG,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAGxD;IAED,+FAA+F;IAC/F,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAW7D;IAED,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAUlH;IAED;;;OAGG;IACH,sBAAsB,IAAI,MAAM,EAAE,GAAG,SAAS,CAQ7C;IAED;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,GAAG,SAAS,GAAG,OAAO,CAEpF;CACF"}
|
|
@@ -61,7 +61,9 @@ var ToolSelectorPolicy = class {
|
|
|
61
61
|
return filtered.map((tool) => ({ ...tool, preload: this.isToolPreloaded(tool.name, tool.annotations) }));
|
|
62
62
|
}
|
|
63
63
|
isToolPreloaded(toolName, annotations) {
|
|
64
|
-
if (this.preload)
|
|
64
|
+
if (this.preload) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
65
67
|
return (0, import_toolSelectors.toolMatchesAnySelector)(toolName, annotations, this.preloadTools);
|
|
66
68
|
}
|
|
67
69
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/ToolSelectorPolicy.ts"],"sourcesContent":["import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport type { AgentToolSchema } from './IMCPServer';\nimport {\n DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS,\n isToolAllowed,\n literalToolNames,\n selectorsIncludeAll,\n toolMatchesAnySelector,\n toolRequiresApproval,\n} from './toolSelectors';\n\n/**\n * Per-server tool selectors. Enable/disable/preload tags: `@all`, `@read-only`.\n * Approval tags: `@all`, `@write`, `@destructive`.\n */\nexport interface ToolSelectorConfig {\n enableTools: string[];\n disableTools: string[];\n preloadTools: string[];\n requireApprovalForTools?: string[] | undefined;\n}\n\n/**\n * Encapsulates enable/disable/preload/approval selector policy for a single MCP server. Pure: it\n * never throws or does IO — callers decide how to surface a disallowed/missing tool so each can\n * raise its own error type.\n */\nexport class ToolSelectorPolicy {\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly enableTools: string[];\n private readonly disableTools: string[];\n private readonly preloadTools: string[];\n private readonly requireApprovalForTools: string[];\n // Tool names that survived enable/disable filtering on the most recent filterAndAnnotate() call.\n // Used by the sandbox allow-list and as the fast path for callTool gating.\n private resolvedAllowedToolNames: string[] | undefined;\n\n constructor(params: { selectors: ToolSelectorConfig; preload: boolean }) {\n this.enableTools = params.selectors.enableTools;\n this.disableTools = params.selectors.disableTools;\n this.preloadTools = params.selectors.preloadTools;\n this.requireApprovalForTools = params.selectors.requireApprovalForTools ?? DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS;\n this.preload = params.preload;\n this.hasPreloadedTools = params.preload || this.preloadTools.length > 0;\n }\n\n /** Literal enable_tools names that are not present on the server (caller throws if non-empty). */\n missingEnableLiterals(tools: AgentToolSchema[]): string[] {\n const availableNames = new Set(tools.map(t => t.name));\n return literalToolNames(this.enableTools).filter(name => !availableNames.has(name));\n }\n\n /** Filter by enable/disable, record the resolved allow-list, and annotate per-tool preload. */\n filterAndAnnotate(tools: AgentToolSchema[]): AgentToolSchema[] {\n const filtered = tools.filter(tool =>\n isToolAllowed({\n toolName: tool.name,\n annotations: tool.annotations,\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n }),\n );\n this.resolvedAllowedToolNames = filtered.map(t => t.name);\n return filtered.map(tool => ({ ...tool, preload: this.isToolPreloaded(tool.name, tool.annotations) }));\n }\n\n private isToolPreloaded(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n // Fully-preloaded server: every allowed tool is loaded into context.\n if (this.preload) return true;\n // Otherwise only the tools matching `preload_tools` stay eager.\n return toolMatchesAnySelector(toolName, annotations, this.preloadTools);\n }\n\n /**\n * Whether a tool may be called. Uses the resolved allow-list once listTools() has run; before that\n * falls back to matching against the raw selectors, resolving annotations lazily only when needed.\n */\n async isAllowed(toolName: string, resolveAnnotations: () => Promise<ToolAnnotations | undefined>): Promise<boolean> {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames.includes(toolName);\n }\n return isToolAllowed({\n toolName,\n annotations: await resolveAnnotations(),\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n });\n }\n\n /**\n * Best-effort allow-list for the sandbox. After a listTools() call returns the concrete names;\n * before that, `@all` with no disables means \"unrestricted\" (undefined), else the literal names.\n */\n allowedNamesForSandbox(): string[] | undefined {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames;\n }\n if (selectorsIncludeAll(this.enableTools) && this.disableTools.length === 0) {\n return undefined;\n }\n return literalToolNames(this.enableTools);\n }\n\n /**\n * Whether the tool requires human approval. Honors `requireApprovalForTools` exactly: omit /\n * undefined defaults to write+destructive (constructor); an explicit list — including empty —\n * is exact.\n */\n requiresApproval(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n return toolRequiresApproval(toolName, annotations, this.requireApprovalForTools);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,2BAOO;AAkBA,IAAM,qBAAN,MAAyB;AAAA,EACrB;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGT;AAAA,EAER,YAAY,QAA6D;AACvE,SAAK,cAAc,OAAO,UAAU;AACpC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,0BAA0B,OAAO,UAAU,2BAA2B;AAC3E,SAAK,UAAU,OAAO;AACtB,SAAK,oBAAoB,OAAO,WAAW,KAAK,aAAa,SAAS;AAAA,EACxE;AAAA;AAAA,EAGA,sBAAsB,OAAoC;AACxD,UAAM,iBAAiB,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,IAAI,CAAC;AACrD,eAAO,uCAAiB,KAAK,WAAW,EAAE,OAAO,UAAQ,CAAC,eAAe,IAAI,IAAI,CAAC;AAAA,EACpF;AAAA;AAAA,EAGA,kBAAkB,OAA6C;AAC7D,UAAM,WAAW,MAAM;AAAA,MAAO,cAC5B,oCAAc;AAAA,QACZ,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,iBAAiB,KAAK;AAAA,QACtB,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AACA,SAAK,2BAA2B,SAAS,IAAI,OAAK,EAAE,IAAI;AACxD,WAAO,SAAS,IAAI,WAAS,EAAE,GAAG,MAAM,SAAS,KAAK,gBAAgB,KAAK,MAAM,KAAK,WAAW,EAAE,EAAE;AAAA,EACvG;AAAA,EAEQ,gBAAgB,UAAkB,aAAmD;AAE3F,QAAI,KAAK,
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/ToolSelectorPolicy.ts"],"sourcesContent":["import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport type { AgentToolSchema } from './IMCPServer';\nimport {\n DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS,\n isToolAllowed,\n literalToolNames,\n selectorsIncludeAll,\n toolMatchesAnySelector,\n toolRequiresApproval,\n} from './toolSelectors';\n\n/**\n * Per-server tool selectors. Enable/disable/preload tags: `@all`, `@read-only`.\n * Approval tags: `@all`, `@write`, `@destructive`.\n */\nexport interface ToolSelectorConfig {\n enableTools: string[];\n disableTools: string[];\n preloadTools: string[];\n requireApprovalForTools?: string[] | undefined;\n}\n\n/**\n * Encapsulates enable/disable/preload/approval selector policy for a single MCP server. Pure: it\n * never throws or does IO — callers decide how to surface a disallowed/missing tool so each can\n * raise its own error type.\n */\nexport class ToolSelectorPolicy {\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly enableTools: string[];\n private readonly disableTools: string[];\n private readonly preloadTools: string[];\n private readonly requireApprovalForTools: string[];\n // Tool names that survived enable/disable filtering on the most recent filterAndAnnotate() call.\n // Used by the sandbox allow-list and as the fast path for callTool gating.\n private resolvedAllowedToolNames: string[] | undefined;\n\n constructor(params: { selectors: ToolSelectorConfig; preload: boolean }) {\n this.enableTools = params.selectors.enableTools;\n this.disableTools = params.selectors.disableTools;\n this.preloadTools = params.selectors.preloadTools;\n this.requireApprovalForTools = params.selectors.requireApprovalForTools ?? DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS;\n this.preload = params.preload;\n this.hasPreloadedTools = params.preload || this.preloadTools.length > 0;\n }\n\n /** Literal enable_tools names that are not present on the server (caller throws if non-empty). */\n missingEnableLiterals(tools: AgentToolSchema[]): string[] {\n const availableNames = new Set(tools.map(t => t.name));\n return literalToolNames(this.enableTools).filter(name => !availableNames.has(name));\n }\n\n /** Filter by enable/disable, record the resolved allow-list, and annotate per-tool preload. */\n filterAndAnnotate(tools: AgentToolSchema[]): AgentToolSchema[] {\n const filtered = tools.filter(tool =>\n isToolAllowed({\n toolName: tool.name,\n annotations: tool.annotations,\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n }),\n );\n this.resolvedAllowedToolNames = filtered.map(t => t.name);\n return filtered.map(tool => ({ ...tool, preload: this.isToolPreloaded(tool.name, tool.annotations) }));\n }\n\n private isToolPreloaded(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n // Fully-preloaded server: every allowed tool is loaded into context.\n if (this.preload) {\n return true;\n }\n // Otherwise only the tools matching `preload_tools` stay eager.\n return toolMatchesAnySelector(toolName, annotations, this.preloadTools);\n }\n\n /**\n * Whether a tool may be called. Uses the resolved allow-list once listTools() has run; before that\n * falls back to matching against the raw selectors, resolving annotations lazily only when needed.\n */\n async isAllowed(toolName: string, resolveAnnotations: () => Promise<ToolAnnotations | undefined>): Promise<boolean> {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames.includes(toolName);\n }\n return isToolAllowed({\n toolName,\n annotations: await resolveAnnotations(),\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n });\n }\n\n /**\n * Best-effort allow-list for the sandbox. After a listTools() call returns the concrete names;\n * before that, `@all` with no disables means \"unrestricted\" (undefined), else the literal names.\n */\n allowedNamesForSandbox(): string[] | undefined {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames;\n }\n if (selectorsIncludeAll(this.enableTools) && this.disableTools.length === 0) {\n return undefined;\n }\n return literalToolNames(this.enableTools);\n }\n\n /**\n * Whether the tool requires human approval. Honors `requireApprovalForTools` exactly: omit /\n * undefined defaults to write+destructive (constructor); an explicit list — including empty —\n * is exact.\n */\n requiresApproval(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n return toolRequiresApproval(toolName, annotations, this.requireApprovalForTools);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,2BAOO;AAkBA,IAAM,qBAAN,MAAyB;AAAA,EACrB;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGT;AAAA,EAER,YAAY,QAA6D;AACvE,SAAK,cAAc,OAAO,UAAU;AACpC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,0BAA0B,OAAO,UAAU,2BAA2B;AAC3E,SAAK,UAAU,OAAO;AACtB,SAAK,oBAAoB,OAAO,WAAW,KAAK,aAAa,SAAS;AAAA,EACxE;AAAA;AAAA,EAGA,sBAAsB,OAAoC;AACxD,UAAM,iBAAiB,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,IAAI,CAAC;AACrD,eAAO,uCAAiB,KAAK,WAAW,EAAE,OAAO,UAAQ,CAAC,eAAe,IAAI,IAAI,CAAC;AAAA,EACpF;AAAA;AAAA,EAGA,kBAAkB,OAA6C;AAC7D,UAAM,WAAW,MAAM;AAAA,MAAO,cAC5B,oCAAc;AAAA,QACZ,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,iBAAiB,KAAK;AAAA,QACtB,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AACA,SAAK,2BAA2B,SAAS,IAAI,OAAK,EAAE,IAAI;AACxD,WAAO,SAAS,IAAI,WAAS,EAAE,GAAG,MAAM,SAAS,KAAK,gBAAgB,KAAK,MAAM,KAAK,WAAW,EAAE,EAAE;AAAA,EACvG;AAAA,EAEQ,gBAAgB,UAAkB,aAAmD;AAE3F,QAAI,KAAK,SAAS;AAChB,aAAO;AAAA,IACT;AAEA,eAAO,6CAAuB,UAAU,aAAa,KAAK,YAAY;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU,UAAkB,oBAAkF;AAClH,QAAI,KAAK,0BAA0B;AACjC,aAAO,KAAK,yBAAyB,SAAS,QAAQ;AAAA,IACxD;AACA,eAAO,oCAAc;AAAA,MACnB;AAAA,MACA,aAAa,MAAM,mBAAmB;AAAA,MACtC,iBAAiB,KAAK;AAAA,MACtB,kBAAkB,KAAK;AAAA,IACzB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,yBAA+C;AAC7C,QAAI,KAAK,0BAA0B;AACjC,aAAO,KAAK;AAAA,IACd;AACA,YAAI,0CAAoB,KAAK,WAAW,KAAK,KAAK,aAAa,WAAW,GAAG;AAC3E,aAAO;AAAA,IACT;AACA,eAAO,uCAAiB,KAAK,WAAW;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB,UAAkB,aAAmD;AACpF,eAAO,2CAAqB,UAAU,aAAa,KAAK,uBAAuB;AAAA,EACjF;AACF;","names":[]}
|
|
@@ -44,7 +44,9 @@ var ToolSelectorPolicy = class {
|
|
|
44
44
|
return filtered.map((tool) => ({ ...tool, preload: this.isToolPreloaded(tool.name, tool.annotations) }));
|
|
45
45
|
}
|
|
46
46
|
isToolPreloaded(toolName, annotations) {
|
|
47
|
-
if (this.preload)
|
|
47
|
+
if (this.preload) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
48
50
|
return toolMatchesAnySelector(toolName, annotations, this.preloadTools);
|
|
49
51
|
}
|
|
50
52
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/ToolSelectorPolicy.ts"],"sourcesContent":["import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport type { AgentToolSchema } from './IMCPServer';\nimport {\n DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS,\n isToolAllowed,\n literalToolNames,\n selectorsIncludeAll,\n toolMatchesAnySelector,\n toolRequiresApproval,\n} from './toolSelectors';\n\n/**\n * Per-server tool selectors. Enable/disable/preload tags: `@all`, `@read-only`.\n * Approval tags: `@all`, `@write`, `@destructive`.\n */\nexport interface ToolSelectorConfig {\n enableTools: string[];\n disableTools: string[];\n preloadTools: string[];\n requireApprovalForTools?: string[] | undefined;\n}\n\n/**\n * Encapsulates enable/disable/preload/approval selector policy for a single MCP server. Pure: it\n * never throws or does IO — callers decide how to surface a disallowed/missing tool so each can\n * raise its own error type.\n */\nexport class ToolSelectorPolicy {\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly enableTools: string[];\n private readonly disableTools: string[];\n private readonly preloadTools: string[];\n private readonly requireApprovalForTools: string[];\n // Tool names that survived enable/disable filtering on the most recent filterAndAnnotate() call.\n // Used by the sandbox allow-list and as the fast path for callTool gating.\n private resolvedAllowedToolNames: string[] | undefined;\n\n constructor(params: { selectors: ToolSelectorConfig; preload: boolean }) {\n this.enableTools = params.selectors.enableTools;\n this.disableTools = params.selectors.disableTools;\n this.preloadTools = params.selectors.preloadTools;\n this.requireApprovalForTools = params.selectors.requireApprovalForTools ?? DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS;\n this.preload = params.preload;\n this.hasPreloadedTools = params.preload || this.preloadTools.length > 0;\n }\n\n /** Literal enable_tools names that are not present on the server (caller throws if non-empty). */\n missingEnableLiterals(tools: AgentToolSchema[]): string[] {\n const availableNames = new Set(tools.map(t => t.name));\n return literalToolNames(this.enableTools).filter(name => !availableNames.has(name));\n }\n\n /** Filter by enable/disable, record the resolved allow-list, and annotate per-tool preload. */\n filterAndAnnotate(tools: AgentToolSchema[]): AgentToolSchema[] {\n const filtered = tools.filter(tool =>\n isToolAllowed({\n toolName: tool.name,\n annotations: tool.annotations,\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n }),\n );\n this.resolvedAllowedToolNames = filtered.map(t => t.name);\n return filtered.map(tool => ({ ...tool, preload: this.isToolPreloaded(tool.name, tool.annotations) }));\n }\n\n private isToolPreloaded(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n // Fully-preloaded server: every allowed tool is loaded into context.\n if (this.preload) return true;\n // Otherwise only the tools matching `preload_tools` stay eager.\n return toolMatchesAnySelector(toolName, annotations, this.preloadTools);\n }\n\n /**\n * Whether a tool may be called. Uses the resolved allow-list once listTools() has run; before that\n * falls back to matching against the raw selectors, resolving annotations lazily only when needed.\n */\n async isAllowed(toolName: string, resolveAnnotations: () => Promise<ToolAnnotations | undefined>): Promise<boolean> {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames.includes(toolName);\n }\n return isToolAllowed({\n toolName,\n annotations: await resolveAnnotations(),\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n });\n }\n\n /**\n * Best-effort allow-list for the sandbox. After a listTools() call returns the concrete names;\n * before that, `@all` with no disables means \"unrestricted\" (undefined), else the literal names.\n */\n allowedNamesForSandbox(): string[] | undefined {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames;\n }\n if (selectorsIncludeAll(this.enableTools) && this.disableTools.length === 0) {\n return undefined;\n }\n return literalToolNames(this.enableTools);\n }\n\n /**\n * Whether the tool requires human approval. Honors `requireApprovalForTools` exactly: omit /\n * undefined defaults to write+destructive (constructor); an explicit list — including empty —\n * is exact.\n */\n requiresApproval(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n return toolRequiresApproval(toolName, annotations, this.requireApprovalForTools);\n }\n}\n"],"mappings":";AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkBA,IAAM,qBAAN,MAAyB;AAAA,EACrB;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGT;AAAA,EAER,YAAY,QAA6D;AACvE,SAAK,cAAc,OAAO,UAAU;AACpC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,0BAA0B,OAAO,UAAU,2BAA2B;AAC3E,SAAK,UAAU,OAAO;AACtB,SAAK,oBAAoB,OAAO,WAAW,KAAK,aAAa,SAAS;AAAA,EACxE;AAAA;AAAA,EAGA,sBAAsB,OAAoC;AACxD,UAAM,iBAAiB,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,IAAI,CAAC;AACrD,WAAO,iBAAiB,KAAK,WAAW,EAAE,OAAO,UAAQ,CAAC,eAAe,IAAI,IAAI,CAAC;AAAA,EACpF;AAAA;AAAA,EAGA,kBAAkB,OAA6C;AAC7D,UAAM,WAAW,MAAM;AAAA,MAAO,UAC5B,cAAc;AAAA,QACZ,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,iBAAiB,KAAK;AAAA,QACtB,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AACA,SAAK,2BAA2B,SAAS,IAAI,OAAK,EAAE,IAAI;AACxD,WAAO,SAAS,IAAI,WAAS,EAAE,GAAG,MAAM,SAAS,KAAK,gBAAgB,KAAK,MAAM,KAAK,WAAW,EAAE,EAAE;AAAA,EACvG;AAAA,EAEQ,gBAAgB,UAAkB,aAAmD;AAE3F,QAAI,KAAK,
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/ToolSelectorPolicy.ts"],"sourcesContent":["import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport type { AgentToolSchema } from './IMCPServer';\nimport {\n DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS,\n isToolAllowed,\n literalToolNames,\n selectorsIncludeAll,\n toolMatchesAnySelector,\n toolRequiresApproval,\n} from './toolSelectors';\n\n/**\n * Per-server tool selectors. Enable/disable/preload tags: `@all`, `@read-only`.\n * Approval tags: `@all`, `@write`, `@destructive`.\n */\nexport interface ToolSelectorConfig {\n enableTools: string[];\n disableTools: string[];\n preloadTools: string[];\n requireApprovalForTools?: string[] | undefined;\n}\n\n/**\n * Encapsulates enable/disable/preload/approval selector policy for a single MCP server. Pure: it\n * never throws or does IO — callers decide how to surface a disallowed/missing tool so each can\n * raise its own error type.\n */\nexport class ToolSelectorPolicy {\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly enableTools: string[];\n private readonly disableTools: string[];\n private readonly preloadTools: string[];\n private readonly requireApprovalForTools: string[];\n // Tool names that survived enable/disable filtering on the most recent filterAndAnnotate() call.\n // Used by the sandbox allow-list and as the fast path for callTool gating.\n private resolvedAllowedToolNames: string[] | undefined;\n\n constructor(params: { selectors: ToolSelectorConfig; preload: boolean }) {\n this.enableTools = params.selectors.enableTools;\n this.disableTools = params.selectors.disableTools;\n this.preloadTools = params.selectors.preloadTools;\n this.requireApprovalForTools = params.selectors.requireApprovalForTools ?? DEFAULT_REQUIRE_APPROVAL_FOR_TOOLS;\n this.preload = params.preload;\n this.hasPreloadedTools = params.preload || this.preloadTools.length > 0;\n }\n\n /** Literal enable_tools names that are not present on the server (caller throws if non-empty). */\n missingEnableLiterals(tools: AgentToolSchema[]): string[] {\n const availableNames = new Set(tools.map(t => t.name));\n return literalToolNames(this.enableTools).filter(name => !availableNames.has(name));\n }\n\n /** Filter by enable/disable, record the resolved allow-list, and annotate per-tool preload. */\n filterAndAnnotate(tools: AgentToolSchema[]): AgentToolSchema[] {\n const filtered = tools.filter(tool =>\n isToolAllowed({\n toolName: tool.name,\n annotations: tool.annotations,\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n }),\n );\n this.resolvedAllowedToolNames = filtered.map(t => t.name);\n return filtered.map(tool => ({ ...tool, preload: this.isToolPreloaded(tool.name, tool.annotations) }));\n }\n\n private isToolPreloaded(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n // Fully-preloaded server: every allowed tool is loaded into context.\n if (this.preload) {\n return true;\n }\n // Otherwise only the tools matching `preload_tools` stay eager.\n return toolMatchesAnySelector(toolName, annotations, this.preloadTools);\n }\n\n /**\n * Whether a tool may be called. Uses the resolved allow-list once listTools() has run; before that\n * falls back to matching against the raw selectors, resolving annotations lazily only when needed.\n */\n async isAllowed(toolName: string, resolveAnnotations: () => Promise<ToolAnnotations | undefined>): Promise<boolean> {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames.includes(toolName);\n }\n return isToolAllowed({\n toolName,\n annotations: await resolveAnnotations(),\n enableSelectors: this.enableTools,\n disableSelectors: this.disableTools,\n });\n }\n\n /**\n * Best-effort allow-list for the sandbox. After a listTools() call returns the concrete names;\n * before that, `@all` with no disables means \"unrestricted\" (undefined), else the literal names.\n */\n allowedNamesForSandbox(): string[] | undefined {\n if (this.resolvedAllowedToolNames) {\n return this.resolvedAllowedToolNames;\n }\n if (selectorsIncludeAll(this.enableTools) && this.disableTools.length === 0) {\n return undefined;\n }\n return literalToolNames(this.enableTools);\n }\n\n /**\n * Whether the tool requires human approval. Honors `requireApprovalForTools` exactly: omit /\n * undefined defaults to write+destructive (constructor); an explicit list — including empty —\n * is exact.\n */\n requiresApproval(toolName: string, annotations: ToolAnnotations | undefined): boolean {\n return toolRequiresApproval(toolName, annotations, this.requireApprovalForTools);\n }\n}\n"],"mappings":";AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkBA,IAAM,qBAAN,MAAyB;AAAA,EACrB;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGT;AAAA,EAER,YAAY,QAA6D;AACvE,SAAK,cAAc,OAAO,UAAU;AACpC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,eAAe,OAAO,UAAU;AACrC,SAAK,0BAA0B,OAAO,UAAU,2BAA2B;AAC3E,SAAK,UAAU,OAAO;AACtB,SAAK,oBAAoB,OAAO,WAAW,KAAK,aAAa,SAAS;AAAA,EACxE;AAAA;AAAA,EAGA,sBAAsB,OAAoC;AACxD,UAAM,iBAAiB,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,IAAI,CAAC;AACrD,WAAO,iBAAiB,KAAK,WAAW,EAAE,OAAO,UAAQ,CAAC,eAAe,IAAI,IAAI,CAAC;AAAA,EACpF;AAAA;AAAA,EAGA,kBAAkB,OAA6C;AAC7D,UAAM,WAAW,MAAM;AAAA,MAAO,UAC5B,cAAc;AAAA,QACZ,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,iBAAiB,KAAK;AAAA,QACtB,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AACA,SAAK,2BAA2B,SAAS,IAAI,OAAK,EAAE,IAAI;AACxD,WAAO,SAAS,IAAI,WAAS,EAAE,GAAG,MAAM,SAAS,KAAK,gBAAgB,KAAK,MAAM,KAAK,WAAW,EAAE,EAAE;AAAA,EACvG;AAAA,EAEQ,gBAAgB,UAAkB,aAAmD;AAE3F,QAAI,KAAK,SAAS;AAChB,aAAO;AAAA,IACT;AAEA,WAAO,uBAAuB,UAAU,aAAa,KAAK,YAAY;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU,UAAkB,oBAAkF;AAClH,QAAI,KAAK,0BAA0B;AACjC,aAAO,KAAK,yBAAyB,SAAS,QAAQ;AAAA,IACxD;AACA,WAAO,cAAc;AAAA,MACnB;AAAA,MACA,aAAa,MAAM,mBAAmB;AAAA,MACtC,iBAAiB,KAAK;AAAA,MACtB,kBAAkB,KAAK;AAAA,IACzB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,yBAA+C;AAC7C,QAAI,KAAK,0BAA0B;AACjC,aAAO,KAAK;AAAA,IACd;AACA,QAAI,oBAAoB,KAAK,WAAW,KAAK,KAAK,aAAa,WAAW,GAAG;AAC3E,aAAO;AAAA,IACT;AACA,WAAO,iBAAiB,KAAK,WAAW;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB,UAAkB,aAAmD;AACpF,WAAO,qBAAqB,UAAU,aAAa,KAAK,uBAAuB;AAAA,EACjF;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolSet.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/ToolSet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,oCAAoC,CAAC;AAE3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAEnF;;;GAGG;AACH,qBAAa,OAAQ,YAAW,QAAQ;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,YAAY,MAAM,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,kBAAkB,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAW1F;IAED,6BAA6B,IAAI,MAAM,EAAE,GAAG,SAAS,CAEpD;IAEK,SAAS,IAAI,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"ToolSet.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/ToolSet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,oCAAoC,CAAC;AAE3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAEnF;;;GAGG;AACH,qBAAa,OAAQ,YAAW,QAAQ;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,YAAY,MAAM,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,kBAAkB,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAW1F;IAED,6BAA6B,IAAI,MAAM,EAAE,GAAG,SAAS,CAEpD;IAEK,SAAS,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAmB5C;IAEK,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+BxG;IAEK,YAAY,CAChB,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,EACjC,qBAAqB,CAAC,EAAE,OAAO,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CAG/B;YAEa,iBAAiB;YAOjB,iBAAiB;YAWjB,kBAAkB;CAOjC"}
|
package/dist/core/mcp/ToolSet.js
CHANGED
|
@@ -51,7 +51,9 @@ var ToolSet = class {
|
|
|
51
51
|
}
|
|
52
52
|
async listTools() {
|
|
53
53
|
const response = await this.source.listTools();
|
|
54
|
-
if ((0, import_IMCPServer.isAuthRequired)(response))
|
|
54
|
+
if ((0, import_IMCPServer.isAuthRequired)(response)) {
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
55
57
|
const tools = response.result.tools;
|
|
56
58
|
const missingTools = this.policy.missingEnableLiterals(tools);
|
|
57
59
|
if (missingTools.length > 0) {
|
|
@@ -67,7 +69,9 @@ var ToolSet = class {
|
|
|
67
69
|
}
|
|
68
70
|
async callTool(params, decision) {
|
|
69
71
|
const listed = await this.source.listTools();
|
|
70
|
-
if ((0, import_IMCPServer.isAuthRequired)(listed))
|
|
72
|
+
if ((0, import_IMCPServer.isAuthRequired)(listed)) {
|
|
73
|
+
return listed;
|
|
74
|
+
}
|
|
71
75
|
const annotations = findAnnotations(listed.result.tools, params.name);
|
|
72
76
|
await this.assertToolAllowed(params.name, annotations);
|
|
73
77
|
const tool_info = await this.buildToolCallInfo(params, annotations);
|
|
@@ -105,7 +109,9 @@ var ToolSet = class {
|
|
|
105
109
|
}
|
|
106
110
|
async resolveAnnotations(toolName) {
|
|
107
111
|
const listed = await this.source.listTools();
|
|
108
|
-
if ((0, import_IMCPServer.isAuthRequired)(listed))
|
|
112
|
+
if ((0, import_IMCPServer.isAuthRequired)(listed)) {
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
109
115
|
return findAnnotations(listed.result.tools, toolName);
|
|
110
116
|
}
|
|
111
117
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/ToolSet.ts"],"sourcesContent":["import type { CallToolRequest, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport { McpConnectionError } from '../errors';\nimport type { ApprovalDecision } from '../events/schema';\nimport type { InternalToolCallInfo } from '../llm/LLMTypes';\nimport {\n isAuthRequired,\n isCallToolResponseResult,\n toolResultResponse,\n type AgentToolSchema,\n type CallToolResponse,\n type IToolSet,\n type ListToolsResponse,\n type ToolSource,\n} from './IMCPServer';\nimport { ToolSelectorPolicy, type ToolSelectorConfig } from './ToolSelectorPolicy';\n\n/**\n * Per-agent {@link IToolSet} view over a shared, policy-free {@link ToolSource}, applying the\n * enable/disable/preload/approval selectors. Many can wrap one source without policy bleed.\n */\nexport class ToolSet implements IToolSet {\n readonly name: string;\n readonly id: string;\n readonly description?: string | undefined;\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly source: ToolSource;\n private readonly policy: ToolSelectorPolicy;\n\n constructor(params: { source: ToolSource; selectors: ToolSelectorConfig; preload: boolean }) {\n this.source = params.source;\n this.name = params.source.name;\n this.id = params.source.id;\n this.description = params.source.description;\n this.policy = new ToolSelectorPolicy({\n selectors: params.selectors,\n preload: params.preload,\n });\n this.preload = this.policy.preload;\n this.hasPreloadedTools = this.policy.hasPreloadedTools;\n }\n\n getAllowedToolNamesForSandbox(): string[] | undefined {\n return this.policy.allowedNamesForSandbox();\n }\n\n async listTools(): Promise<ListToolsResponse> {\n const response = await this.source.listTools();\n if (isAuthRequired(response)) return response;\n\n const tools = response.result.tools;\n const missingTools = this.policy.missingEnableLiterals(tools);\n if (missingTools.length > 0) {\n throw new McpConnectionError(\n `Requested tools not found in MCP server ${this.name}: ${missingTools.join(', ')}`,\n 422,\n );\n }\n\n return {\n result: { tools: this.policy.filterAndAnnotate(tools) },\n wasInitialized: response.wasInitialized,\n };\n }\n\n async callTool(params: CallToolRequest['params'], decision?: ApprovalDecision): Promise<CallToolResponse> {\n // Prime the source and surface auth-required before the annotation-based allow/approval checks\n // (which would otherwise see missing annotations and wrongly 403).\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) return listed;\n const annotations = findAnnotations(listed.result.tools, params.name);\n\n await this.assertToolAllowed(params.name, annotations);\n\n const tool_info = await this.buildToolCallInfo(params, annotations);\n if (!decision && tool_info.is_approval_required) {\n return { approvalRequired: { tool_info } };\n }\n\n if (decision?.status === 'deny') {\n const reason = decision.reason ?? 'no reason provided';\n return toolResultResponse({\n text: JSON.stringify({ error: `User denied tool call: ${reason}` }),\n isError: true,\n });\n }\n\n const response = await this.source.callTool(params, decision);\n // The priming listTools() may have captured the first-connect init; carry it forward since the\n // subsequent callTool sees an already-open connection and reports none.\n if (listed.wasInitialized && isCallToolResponseResult(response) && !response.wasInitialized) {\n return { ...response, wasInitialized: listed.wasInitialized };\n }\n return response;\n }\n\n async toolCallInfo(\n params: CallToolRequest['params'],\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n const annotations = await this.resolveAnnotations(params.name);\n return this.buildToolCallInfo(params, annotations, resolveUnderlyingTool);\n }\n\n private async assertToolAllowed(toolName: string, annotations: ToolAnnotations | undefined): Promise<void> {\n const allowed = await this.policy.isAllowed(toolName, () => Promise.resolve(annotations));\n if (!allowed) {\n throw new McpConnectionError(`Tool '${toolName}' is not allowed on MCP server ${this.name}`, 403);\n }\n }\n\n private async buildToolCallInfo(\n params: CallToolRequest['params'],\n annotations: ToolAnnotations | undefined,\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n return {\n ...(await this.source.toolCallInfo(params, resolveUnderlyingTool)),\n is_approval_required: this.policy.requiresApproval(params.name, annotations),\n };\n }\n\n private async resolveAnnotations(toolName: string): Promise<ToolAnnotations | undefined> {\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) return undefined;\n return findAnnotations(listed.result.tools, toolName);\n }\n}\n\nfunction findAnnotations(tools: AgentToolSchema[], toolName: string): ToolAnnotations | undefined {\n return tools.find(t => t.name === toolName)?.annotations;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,oBAAmC;AAGnC,wBASO;AACP,gCAA4D;AAMrD,IAAM,UAAN,MAAkC;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EAEjB,YAAY,QAAiF;AAC3F,SAAK,SAAS,OAAO;AACrB,SAAK,OAAO,OAAO,OAAO;AAC1B,SAAK,KAAK,OAAO,OAAO;AACxB,SAAK,cAAc,OAAO,OAAO;AACjC,SAAK,SAAS,IAAI,6CAAmB;AAAA,MACnC,WAAW,OAAO;AAAA,MAClB,SAAS,OAAO;AAAA,IAClB,CAAC;AACD,SAAK,UAAU,KAAK,OAAO;AAC3B,SAAK,oBAAoB,KAAK,OAAO;AAAA,EACvC;AAAA,EAEA,gCAAsD;AACpD,WAAO,KAAK,OAAO,uBAAuB;AAAA,EAC5C;AAAA,EAEA,MAAM,YAAwC;AAC5C,UAAM,WAAW,MAAM,KAAK,OAAO,UAAU;AAC7C,YAAI,kCAAe,QAAQ,
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/ToolSet.ts"],"sourcesContent":["import type { CallToolRequest, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport { McpConnectionError } from '../errors';\nimport type { ApprovalDecision } from '../events/schema';\nimport type { InternalToolCallInfo } from '../llm/LLMTypes';\nimport {\n isAuthRequired,\n isCallToolResponseResult,\n toolResultResponse,\n type AgentToolSchema,\n type CallToolResponse,\n type IToolSet,\n type ListToolsResponse,\n type ToolSource,\n} from './IMCPServer';\nimport { ToolSelectorPolicy, type ToolSelectorConfig } from './ToolSelectorPolicy';\n\n/**\n * Per-agent {@link IToolSet} view over a shared, policy-free {@link ToolSource}, applying the\n * enable/disable/preload/approval selectors. Many can wrap one source without policy bleed.\n */\nexport class ToolSet implements IToolSet {\n readonly name: string;\n readonly id: string;\n readonly description?: string | undefined;\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly source: ToolSource;\n private readonly policy: ToolSelectorPolicy;\n\n constructor(params: { source: ToolSource; selectors: ToolSelectorConfig; preload: boolean }) {\n this.source = params.source;\n this.name = params.source.name;\n this.id = params.source.id;\n this.description = params.source.description;\n this.policy = new ToolSelectorPolicy({\n selectors: params.selectors,\n preload: params.preload,\n });\n this.preload = this.policy.preload;\n this.hasPreloadedTools = this.policy.hasPreloadedTools;\n }\n\n getAllowedToolNamesForSandbox(): string[] | undefined {\n return this.policy.allowedNamesForSandbox();\n }\n\n async listTools(): Promise<ListToolsResponse> {\n const response = await this.source.listTools();\n if (isAuthRequired(response)) {\n return response;\n }\n\n const tools = response.result.tools;\n const missingTools = this.policy.missingEnableLiterals(tools);\n if (missingTools.length > 0) {\n throw new McpConnectionError(\n `Requested tools not found in MCP server ${this.name}: ${missingTools.join(', ')}`,\n 422,\n );\n }\n\n return {\n result: { tools: this.policy.filterAndAnnotate(tools) },\n wasInitialized: response.wasInitialized,\n };\n }\n\n async callTool(params: CallToolRequest['params'], decision?: ApprovalDecision): Promise<CallToolResponse> {\n // Prime the source and surface auth-required before the annotation-based allow/approval checks\n // (which would otherwise see missing annotations and wrongly 403).\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) {\n return listed;\n }\n const annotations = findAnnotations(listed.result.tools, params.name);\n\n await this.assertToolAllowed(params.name, annotations);\n\n const tool_info = await this.buildToolCallInfo(params, annotations);\n if (!decision && tool_info.is_approval_required) {\n return { approvalRequired: { tool_info } };\n }\n\n if (decision?.status === 'deny') {\n const reason = decision.reason ?? 'no reason provided';\n return toolResultResponse({\n text: JSON.stringify({ error: `User denied tool call: ${reason}` }),\n isError: true,\n });\n }\n\n const response = await this.source.callTool(params, decision);\n // The priming listTools() may have captured the first-connect init; carry it forward since the\n // subsequent callTool sees an already-open connection and reports none.\n if (listed.wasInitialized && isCallToolResponseResult(response) && !response.wasInitialized) {\n return { ...response, wasInitialized: listed.wasInitialized };\n }\n return response;\n }\n\n async toolCallInfo(\n params: CallToolRequest['params'],\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n const annotations = await this.resolveAnnotations(params.name);\n return this.buildToolCallInfo(params, annotations, resolveUnderlyingTool);\n }\n\n private async assertToolAllowed(toolName: string, annotations: ToolAnnotations | undefined): Promise<void> {\n const allowed = await this.policy.isAllowed(toolName, () => Promise.resolve(annotations));\n if (!allowed) {\n throw new McpConnectionError(`Tool '${toolName}' is not allowed on MCP server ${this.name}`, 403);\n }\n }\n\n private async buildToolCallInfo(\n params: CallToolRequest['params'],\n annotations: ToolAnnotations | undefined,\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n return {\n ...(await this.source.toolCallInfo(params, resolveUnderlyingTool)),\n is_approval_required: this.policy.requiresApproval(params.name, annotations),\n };\n }\n\n private async resolveAnnotations(toolName: string): Promise<ToolAnnotations | undefined> {\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) {\n return undefined;\n }\n return findAnnotations(listed.result.tools, toolName);\n }\n}\n\nfunction findAnnotations(tools: AgentToolSchema[], toolName: string): ToolAnnotations | undefined {\n return tools.find(t => t.name === toolName)?.annotations;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -31,7 +31,9 @@ var ToolSet = class {
|
|
|
31
31
|
}
|
|
32
32
|
async listTools() {
|
|
33
33
|
const response = await this.source.listTools();
|
|
34
|
-
if (isAuthRequired(response))
|
|
34
|
+
if (isAuthRequired(response)) {
|
|
35
|
+
return response;
|
|
36
|
+
}
|
|
35
37
|
const tools = response.result.tools;
|
|
36
38
|
const missingTools = this.policy.missingEnableLiterals(tools);
|
|
37
39
|
if (missingTools.length > 0) {
|
|
@@ -47,7 +49,9 @@ var ToolSet = class {
|
|
|
47
49
|
}
|
|
48
50
|
async callTool(params, decision) {
|
|
49
51
|
const listed = await this.source.listTools();
|
|
50
|
-
if (isAuthRequired(listed))
|
|
52
|
+
if (isAuthRequired(listed)) {
|
|
53
|
+
return listed;
|
|
54
|
+
}
|
|
51
55
|
const annotations = findAnnotations(listed.result.tools, params.name);
|
|
52
56
|
await this.assertToolAllowed(params.name, annotations);
|
|
53
57
|
const tool_info = await this.buildToolCallInfo(params, annotations);
|
|
@@ -85,7 +89,9 @@ var ToolSet = class {
|
|
|
85
89
|
}
|
|
86
90
|
async resolveAnnotations(toolName) {
|
|
87
91
|
const listed = await this.source.listTools();
|
|
88
|
-
if (isAuthRequired(listed))
|
|
92
|
+
if (isAuthRequired(listed)) {
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
89
95
|
return findAnnotations(listed.result.tools, toolName);
|
|
90
96
|
}
|
|
91
97
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/ToolSet.ts"],"sourcesContent":["import type { CallToolRequest, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport { McpConnectionError } from '../errors';\nimport type { ApprovalDecision } from '../events/schema';\nimport type { InternalToolCallInfo } from '../llm/LLMTypes';\nimport {\n isAuthRequired,\n isCallToolResponseResult,\n toolResultResponse,\n type AgentToolSchema,\n type CallToolResponse,\n type IToolSet,\n type ListToolsResponse,\n type ToolSource,\n} from './IMCPServer';\nimport { ToolSelectorPolicy, type ToolSelectorConfig } from './ToolSelectorPolicy';\n\n/**\n * Per-agent {@link IToolSet} view over a shared, policy-free {@link ToolSource}, applying the\n * enable/disable/preload/approval selectors. Many can wrap one source without policy bleed.\n */\nexport class ToolSet implements IToolSet {\n readonly name: string;\n readonly id: string;\n readonly description?: string | undefined;\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly source: ToolSource;\n private readonly policy: ToolSelectorPolicy;\n\n constructor(params: { source: ToolSource; selectors: ToolSelectorConfig; preload: boolean }) {\n this.source = params.source;\n this.name = params.source.name;\n this.id = params.source.id;\n this.description = params.source.description;\n this.policy = new ToolSelectorPolicy({\n selectors: params.selectors,\n preload: params.preload,\n });\n this.preload = this.policy.preload;\n this.hasPreloadedTools = this.policy.hasPreloadedTools;\n }\n\n getAllowedToolNamesForSandbox(): string[] | undefined {\n return this.policy.allowedNamesForSandbox();\n }\n\n async listTools(): Promise<ListToolsResponse> {\n const response = await this.source.listTools();\n if (isAuthRequired(response)) return response;\n\n const tools = response.result.tools;\n const missingTools = this.policy.missingEnableLiterals(tools);\n if (missingTools.length > 0) {\n throw new McpConnectionError(\n `Requested tools not found in MCP server ${this.name}: ${missingTools.join(', ')}`,\n 422,\n );\n }\n\n return {\n result: { tools: this.policy.filterAndAnnotate(tools) },\n wasInitialized: response.wasInitialized,\n };\n }\n\n async callTool(params: CallToolRequest['params'], decision?: ApprovalDecision): Promise<CallToolResponse> {\n // Prime the source and surface auth-required before the annotation-based allow/approval checks\n // (which would otherwise see missing annotations and wrongly 403).\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) return listed;\n const annotations = findAnnotations(listed.result.tools, params.name);\n\n await this.assertToolAllowed(params.name, annotations);\n\n const tool_info = await this.buildToolCallInfo(params, annotations);\n if (!decision && tool_info.is_approval_required) {\n return { approvalRequired: { tool_info } };\n }\n\n if (decision?.status === 'deny') {\n const reason = decision.reason ?? 'no reason provided';\n return toolResultResponse({\n text: JSON.stringify({ error: `User denied tool call: ${reason}` }),\n isError: true,\n });\n }\n\n const response = await this.source.callTool(params, decision);\n // The priming listTools() may have captured the first-connect init; carry it forward since the\n // subsequent callTool sees an already-open connection and reports none.\n if (listed.wasInitialized && isCallToolResponseResult(response) && !response.wasInitialized) {\n return { ...response, wasInitialized: listed.wasInitialized };\n }\n return response;\n }\n\n async toolCallInfo(\n params: CallToolRequest['params'],\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n const annotations = await this.resolveAnnotations(params.name);\n return this.buildToolCallInfo(params, annotations, resolveUnderlyingTool);\n }\n\n private async assertToolAllowed(toolName: string, annotations: ToolAnnotations | undefined): Promise<void> {\n const allowed = await this.policy.isAllowed(toolName, () => Promise.resolve(annotations));\n if (!allowed) {\n throw new McpConnectionError(`Tool '${toolName}' is not allowed on MCP server ${this.name}`, 403);\n }\n }\n\n private async buildToolCallInfo(\n params: CallToolRequest['params'],\n annotations: ToolAnnotations | undefined,\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n return {\n ...(await this.source.toolCallInfo(params, resolveUnderlyingTool)),\n is_approval_required: this.policy.requiresApproval(params.name, annotations),\n };\n }\n\n private async resolveAnnotations(toolName: string): Promise<ToolAnnotations | undefined> {\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) return undefined;\n return findAnnotations(listed.result.tools, toolName);\n }\n}\n\nfunction findAnnotations(tools: AgentToolSchema[], toolName: string): ToolAnnotations | undefined {\n return tools.find(t => t.name === toolName)?.annotations;\n}\n"],"mappings":";AACA,SAAS,0BAA0B;AAGnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP,SAAS,0BAAmD;AAMrD,IAAM,UAAN,MAAkC;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEQ;AAAA,EACA;AAAA,EAEjB,YAAY,QAAiF;AAC3F,SAAK,SAAS,OAAO;AACrB,SAAK,OAAO,OAAO,OAAO;AAC1B,SAAK,KAAK,OAAO,OAAO;AACxB,SAAK,cAAc,OAAO,OAAO;AACjC,SAAK,SAAS,IAAI,mBAAmB;AAAA,MACnC,WAAW,OAAO;AAAA,MAClB,SAAS,OAAO;AAAA,IAClB,CAAC;AACD,SAAK,UAAU,KAAK,OAAO;AAC3B,SAAK,oBAAoB,KAAK,OAAO;AAAA,EACvC;AAAA,EAEA,gCAAsD;AACpD,WAAO,KAAK,OAAO,uBAAuB;AAAA,EAC5C;AAAA,EAEA,MAAM,YAAwC;AAC5C,UAAM,WAAW,MAAM,KAAK,OAAO,UAAU;AAC7C,QAAI,eAAe,QAAQ,
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/ToolSet.ts"],"sourcesContent":["import type { CallToolRequest, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport { McpConnectionError } from '../errors';\nimport type { ApprovalDecision } from '../events/schema';\nimport type { InternalToolCallInfo } from '../llm/LLMTypes';\nimport {\n isAuthRequired,\n isCallToolResponseResult,\n toolResultResponse,\n type AgentToolSchema,\n type CallToolResponse,\n type IToolSet,\n type ListToolsResponse,\n type ToolSource,\n} from './IMCPServer';\nimport { ToolSelectorPolicy, type ToolSelectorConfig } from './ToolSelectorPolicy';\n\n/**\n * Per-agent {@link IToolSet} view over a shared, policy-free {@link ToolSource}, applying the\n * enable/disable/preload/approval selectors. Many can wrap one source without policy bleed.\n */\nexport class ToolSet implements IToolSet {\n readonly name: string;\n readonly id: string;\n readonly description?: string | undefined;\n readonly preload: boolean;\n readonly hasPreloadedTools: boolean;\n\n private readonly source: ToolSource;\n private readonly policy: ToolSelectorPolicy;\n\n constructor(params: { source: ToolSource; selectors: ToolSelectorConfig; preload: boolean }) {\n this.source = params.source;\n this.name = params.source.name;\n this.id = params.source.id;\n this.description = params.source.description;\n this.policy = new ToolSelectorPolicy({\n selectors: params.selectors,\n preload: params.preload,\n });\n this.preload = this.policy.preload;\n this.hasPreloadedTools = this.policy.hasPreloadedTools;\n }\n\n getAllowedToolNamesForSandbox(): string[] | undefined {\n return this.policy.allowedNamesForSandbox();\n }\n\n async listTools(): Promise<ListToolsResponse> {\n const response = await this.source.listTools();\n if (isAuthRequired(response)) {\n return response;\n }\n\n const tools = response.result.tools;\n const missingTools = this.policy.missingEnableLiterals(tools);\n if (missingTools.length > 0) {\n throw new McpConnectionError(\n `Requested tools not found in MCP server ${this.name}: ${missingTools.join(', ')}`,\n 422,\n );\n }\n\n return {\n result: { tools: this.policy.filterAndAnnotate(tools) },\n wasInitialized: response.wasInitialized,\n };\n }\n\n async callTool(params: CallToolRequest['params'], decision?: ApprovalDecision): Promise<CallToolResponse> {\n // Prime the source and surface auth-required before the annotation-based allow/approval checks\n // (which would otherwise see missing annotations and wrongly 403).\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) {\n return listed;\n }\n const annotations = findAnnotations(listed.result.tools, params.name);\n\n await this.assertToolAllowed(params.name, annotations);\n\n const tool_info = await this.buildToolCallInfo(params, annotations);\n if (!decision && tool_info.is_approval_required) {\n return { approvalRequired: { tool_info } };\n }\n\n if (decision?.status === 'deny') {\n const reason = decision.reason ?? 'no reason provided';\n return toolResultResponse({\n text: JSON.stringify({ error: `User denied tool call: ${reason}` }),\n isError: true,\n });\n }\n\n const response = await this.source.callTool(params, decision);\n // The priming listTools() may have captured the first-connect init; carry it forward since the\n // subsequent callTool sees an already-open connection and reports none.\n if (listed.wasInitialized && isCallToolResponseResult(response) && !response.wasInitialized) {\n return { ...response, wasInitialized: listed.wasInitialized };\n }\n return response;\n }\n\n async toolCallInfo(\n params: CallToolRequest['params'],\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n const annotations = await this.resolveAnnotations(params.name);\n return this.buildToolCallInfo(params, annotations, resolveUnderlyingTool);\n }\n\n private async assertToolAllowed(toolName: string, annotations: ToolAnnotations | undefined): Promise<void> {\n const allowed = await this.policy.isAllowed(toolName, () => Promise.resolve(annotations));\n if (!allowed) {\n throw new McpConnectionError(`Tool '${toolName}' is not allowed on MCP server ${this.name}`, 403);\n }\n }\n\n private async buildToolCallInfo(\n params: CallToolRequest['params'],\n annotations: ToolAnnotations | undefined,\n resolveUnderlyingTool?: boolean,\n ): Promise<InternalToolCallInfo> {\n return {\n ...(await this.source.toolCallInfo(params, resolveUnderlyingTool)),\n is_approval_required: this.policy.requiresApproval(params.name, annotations),\n };\n }\n\n private async resolveAnnotations(toolName: string): Promise<ToolAnnotations | undefined> {\n const listed = await this.source.listTools();\n if (isAuthRequired(listed)) {\n return undefined;\n }\n return findAnnotations(listed.result.tools, toolName);\n }\n}\n\nfunction findAnnotations(tools: AgentToolSchema[], toolName: string): ToolAnnotations | undefined {\n return tools.find(t => t.name === toolName)?.annotations;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convertMCPServers.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/convertMCPServers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,cAAc,CAAC;AAG7D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,QAAQ,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,8BAA8B;IAC7C,cAAc,EAAE,kBAAkB,CAAC;IACnC,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC,mBAAmB,EAAE,eAAe,EAAE,CAAC;CACxC;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC9B,WAAW,EAAE,QAAQ,EAAE,CAAC;CACzB,GAAG,OAAO,CAAC,8BAA8B,CAAC,
|
|
1
|
+
{"version":3,"file":"convertMCPServers.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/convertMCPServers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,cAAc,CAAC;AAG7D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,QAAQ,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,8BAA8B;IAC7C,cAAc,EAAE,kBAAkB,CAAC;IACnC,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;IACxC,mBAAmB,EAAE,eAAe,EAAE,CAAC;CACxC;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC9B,WAAW,EAAE,QAAQ,EAAE,CAAC;CACzB,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAqE1C"}
|
|
@@ -41,7 +41,9 @@ async function convertMCPServersToTools(params) {
|
|
|
41
41
|
);
|
|
42
42
|
for (let i = 0; i < allServers.length; i++) {
|
|
43
43
|
const listResult = listResults[i];
|
|
44
|
-
if (!listResult)
|
|
44
|
+
if (!listResult) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
45
47
|
const { serverName, response: listResponse } = listResult;
|
|
46
48
|
if ((0, import_IMCPServer.isAuthRequired)(listResponse)) {
|
|
47
49
|
authRequirementInfo.push(listResponse.authRequired);
|
|
@@ -57,7 +59,9 @@ async function convertMCPServersToTools(params) {
|
|
|
57
59
|
}
|
|
58
60
|
const sortedTools = [...mcpTools.tools].sort((a, b) => a.name > b.name ? 1 : -1);
|
|
59
61
|
for (const mcpTool of sortedTools) {
|
|
60
|
-
if (!mcpTool.preload)
|
|
62
|
+
if (!mcpTool.preload) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
61
65
|
const { name: toolName } = (0, import_toolNames.getUniqueSanitizedToolName)(mcpTool.name, registeredNames);
|
|
62
66
|
registeredNames.add(toolName);
|
|
63
67
|
const description = `mcp server: ${serverName}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/convertMCPServers.ts"],"sourcesContent":["import type { ChatCompletionTool } from 'openai/resources/chat';\nimport type { MCPServerInitInfo } from '../events/schema';\nimport type { MCPAuthRequired } from '../mcp/IMCPServer';\nimport { type IToolSet, isAuthRequired } from './IMCPServer';\nimport { getUniqueSanitizedToolName } from './toolNames';\n\nexport interface MappedMCPTool {\n toolSet: IToolSet;\n originalToolName: string;\n}\n\nexport interface ConvertToolsResult {\n tools: ChatCompletionTool[];\n toolMapping: Map<string, MappedMCPTool>;\n}\n\nexport interface ConvertMCPServersToToolsResult {\n convertedTools: ConvertToolsResult;\n initializationInfo: MCPServerInitInfo[];\n authRequirementInfo: MCPAuthRequired[];\n}\n\nexport async function convertMCPServersToTools(params: {\n tfyManagedServers: IToolSet[];\n userServers: IToolSet[];\n}): Promise<ConvertMCPServersToToolsResult> {\n const { tfyManagedServers, userServers } = params;\n const tools: ChatCompletionTool[] = [];\n const toolMapping = new Map<string, MappedMCPTool>();\n const initializationInfo: MCPServerInitInfo[] = [];\n const authRequirementInfo: MCPAuthRequired[] = [];\n const registeredNames = new Set<string>();\n\n // Skip non-preloaded servers that have no preloaded tools, to avoid unnecessary listTools() calls and OAuth triggers.\n // Non-preloaded servers with some preloaded tools are kept so those tools can be registered.\n const filteredUserServers = userServers.filter(s => s.preload || s.hasPreloadedTools);\n\n // TFY-managed servers first so they claim their original tool names before user servers.\n const sortedTfyServers = [...tfyManagedServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const sortedUserServers = [...filteredUserServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const allServers = [...sortedTfyServers, ...sortedUserServers];\n\n const listResults = await Promise.all(\n allServers.map(async server => ({ serverName: server.name, response: await server.listTools() })),\n );\n\n for (let i = 0; i < allServers.length; i++) {\n const listResult = listResults[i];\n if (!listResult) continue;\n const { serverName, response: listResponse } = listResult;\n if (isAuthRequired(listResponse)) {\n authRequirementInfo.push(listResponse.authRequired);\n continue;\n }\n const { result: mcpTools, wasInitialized } = listResponse;\n if (wasInitialized) {\n initializationInfo.push(wasInitialized);\n }\n\n const toolSet = allServers[i];\n if (toolSet === undefined) {\n throw new Error(`Unreachable: missing tool set at index ${String(i)}`);\n }\n const sortedTools = [...mcpTools.tools].sort((a, b) => (a.name > b.name ? 1 : -1));\n for (const mcpTool of sortedTools) {\n if (!mcpTool.preload) continue;\n const { name: toolName } = getUniqueSanitizedToolName(mcpTool.name, registeredNames);\n registeredNames.add(toolName);\n const description = `mcp server: ${serverName}\\n${mcpTool.description ?? ''}`;\n tools.push({\n type: 'function',\n function: {\n name: toolName,\n description: description,\n parameters: {\n type: 'object',\n properties: mcpTool.inputSchema.properties ?? {},\n required: mcpTool.inputSchema.required ?? [],\n },\n },\n });\n\n toolMapping.set(toolName, {\n toolSet,\n originalToolName: mcpTool.name,\n });\n }\n }\n\n return { convertedTools: { tools, toolMapping }, initializationInfo, authRequirementInfo };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,wBAA8C;AAC9C,uBAA2C;AAkB3C,eAAsB,yBAAyB,QAGH;AAC1C,QAAM,EAAE,mBAAmB,YAAY,IAAI;AAC3C,QAAM,QAA8B,CAAC;AACrC,QAAM,cAAc,oBAAI,IAA2B;AACnD,QAAM,qBAA0C,CAAC;AACjD,QAAM,sBAAyC,CAAC;AAChD,QAAM,kBAAkB,oBAAI,IAAY;AAIxC,QAAM,sBAAsB,YAAY,OAAO,OAAK,EAAE,WAAW,EAAE,iBAAiB;AAGpF,QAAM,mBAAmB,CAAC,GAAG,iBAAiB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AACzF,QAAM,oBAAoB,CAAC,GAAG,mBAAmB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AAC5F,QAAM,aAAa,CAAC,GAAG,kBAAkB,GAAG,iBAAiB;AAE7D,QAAM,cAAc,MAAM,QAAQ;AAAA,IAChC,WAAW,IAAI,OAAM,YAAW,EAAE,YAAY,OAAO,MAAM,UAAU,MAAM,OAAO,UAAU,EAAE,EAAE;AAAA,EAClG;AAEA,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,aAAa,YAAY,CAAC;AAChC,QAAI,CAAC,
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/convertMCPServers.ts"],"sourcesContent":["import type { ChatCompletionTool } from 'openai/resources/chat';\nimport type { MCPServerInitInfo } from '../events/schema';\nimport type { MCPAuthRequired } from '../mcp/IMCPServer';\nimport { type IToolSet, isAuthRequired } from './IMCPServer';\nimport { getUniqueSanitizedToolName } from './toolNames';\n\nexport interface MappedMCPTool {\n toolSet: IToolSet;\n originalToolName: string;\n}\n\nexport interface ConvertToolsResult {\n tools: ChatCompletionTool[];\n toolMapping: Map<string, MappedMCPTool>;\n}\n\nexport interface ConvertMCPServersToToolsResult {\n convertedTools: ConvertToolsResult;\n initializationInfo: MCPServerInitInfo[];\n authRequirementInfo: MCPAuthRequired[];\n}\n\nexport async function convertMCPServersToTools(params: {\n tfyManagedServers: IToolSet[];\n userServers: IToolSet[];\n}): Promise<ConvertMCPServersToToolsResult> {\n const { tfyManagedServers, userServers } = params;\n const tools: ChatCompletionTool[] = [];\n const toolMapping = new Map<string, MappedMCPTool>();\n const initializationInfo: MCPServerInitInfo[] = [];\n const authRequirementInfo: MCPAuthRequired[] = [];\n const registeredNames = new Set<string>();\n\n // Skip non-preloaded servers that have no preloaded tools, to avoid unnecessary listTools() calls and OAuth triggers.\n // Non-preloaded servers with some preloaded tools are kept so those tools can be registered.\n const filteredUserServers = userServers.filter(s => s.preload || s.hasPreloadedTools);\n\n // TFY-managed servers first so they claim their original tool names before user servers.\n const sortedTfyServers = [...tfyManagedServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const sortedUserServers = [...filteredUserServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const allServers = [...sortedTfyServers, ...sortedUserServers];\n\n const listResults = await Promise.all(\n allServers.map(async server => ({ serverName: server.name, response: await server.listTools() })),\n );\n\n for (let i = 0; i < allServers.length; i++) {\n const listResult = listResults[i];\n if (!listResult) {\n continue;\n }\n const { serverName, response: listResponse } = listResult;\n if (isAuthRequired(listResponse)) {\n authRequirementInfo.push(listResponse.authRequired);\n continue;\n }\n const { result: mcpTools, wasInitialized } = listResponse;\n if (wasInitialized) {\n initializationInfo.push(wasInitialized);\n }\n\n const toolSet = allServers[i];\n if (toolSet === undefined) {\n throw new Error(`Unreachable: missing tool set at index ${String(i)}`);\n }\n const sortedTools = [...mcpTools.tools].sort((a, b) => (a.name > b.name ? 1 : -1));\n for (const mcpTool of sortedTools) {\n if (!mcpTool.preload) {\n continue;\n }\n const { name: toolName } = getUniqueSanitizedToolName(mcpTool.name, registeredNames);\n registeredNames.add(toolName);\n const description = `mcp server: ${serverName}\\n${mcpTool.description ?? ''}`;\n tools.push({\n type: 'function',\n function: {\n name: toolName,\n description: description,\n parameters: {\n type: 'object',\n properties: mcpTool.inputSchema.properties ?? {},\n required: mcpTool.inputSchema.required ?? [],\n },\n },\n });\n\n toolMapping.set(toolName, {\n toolSet,\n originalToolName: mcpTool.name,\n });\n }\n }\n\n return { convertedTools: { tools, toolMapping }, initializationInfo, authRequirementInfo };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,wBAA8C;AAC9C,uBAA2C;AAkB3C,eAAsB,yBAAyB,QAGH;AAC1C,QAAM,EAAE,mBAAmB,YAAY,IAAI;AAC3C,QAAM,QAA8B,CAAC;AACrC,QAAM,cAAc,oBAAI,IAA2B;AACnD,QAAM,qBAA0C,CAAC;AACjD,QAAM,sBAAyC,CAAC;AAChD,QAAM,kBAAkB,oBAAI,IAAY;AAIxC,QAAM,sBAAsB,YAAY,OAAO,OAAK,EAAE,WAAW,EAAE,iBAAiB;AAGpF,QAAM,mBAAmB,CAAC,GAAG,iBAAiB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AACzF,QAAM,oBAAoB,CAAC,GAAG,mBAAmB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AAC5F,QAAM,aAAa,CAAC,GAAG,kBAAkB,GAAG,iBAAiB;AAE7D,QAAM,cAAc,MAAM,QAAQ;AAAA,IAChC,WAAW,IAAI,OAAM,YAAW,EAAE,YAAY,OAAO,MAAM,UAAU,MAAM,OAAO,UAAU,EAAE,EAAE;AAAA,EAClG;AAEA,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,aAAa,YAAY,CAAC;AAChC,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AACA,UAAM,EAAE,YAAY,UAAU,aAAa,IAAI;AAC/C,YAAI,kCAAe,YAAY,GAAG;AAChC,0BAAoB,KAAK,aAAa,YAAY;AAClD;AAAA,IACF;AACA,UAAM,EAAE,QAAQ,UAAU,eAAe,IAAI;AAC7C,QAAI,gBAAgB;AAClB,yBAAmB,KAAK,cAAc;AAAA,IACxC;AAEA,UAAM,UAAU,WAAW,CAAC;AAC5B,QAAI,YAAY,QAAW;AACzB,YAAM,IAAI,MAAM,0CAA0C,OAAO,CAAC,CAAC,EAAE;AAAA,IACvE;AACA,UAAM,cAAc,CAAC,GAAG,SAAS,KAAK,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AACjF,eAAW,WAAW,aAAa;AACjC,UAAI,CAAC,QAAQ,SAAS;AACpB;AAAA,MACF;AACA,YAAM,EAAE,MAAM,SAAS,QAAI,6CAA2B,QAAQ,MAAM,eAAe;AACnF,sBAAgB,IAAI,QAAQ;AAC5B,YAAM,cAAc,eAAe,UAAU;AAAA,EAAK,QAAQ,eAAe,EAAE;AAC3E,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,UACR,MAAM;AAAA,UACN;AAAA,UACA,YAAY;AAAA,YACV,MAAM;AAAA,YACN,YAAY,QAAQ,YAAY,cAAc,CAAC;AAAA,YAC/C,UAAU,QAAQ,YAAY,YAAY,CAAC;AAAA,UAC7C;AAAA,QACF;AAAA,MACF,CAAC;AAED,kBAAY,IAAI,UAAU;AAAA,QACxB;AAAA,QACA,kBAAkB,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,gBAAgB,EAAE,OAAO,YAAY,GAAG,oBAAoB,oBAAoB;AAC3F;","names":[]}
|
|
@@ -17,7 +17,9 @@ async function convertMCPServersToTools(params) {
|
|
|
17
17
|
);
|
|
18
18
|
for (let i = 0; i < allServers.length; i++) {
|
|
19
19
|
const listResult = listResults[i];
|
|
20
|
-
if (!listResult)
|
|
20
|
+
if (!listResult) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
21
23
|
const { serverName, response: listResponse } = listResult;
|
|
22
24
|
if (isAuthRequired(listResponse)) {
|
|
23
25
|
authRequirementInfo.push(listResponse.authRequired);
|
|
@@ -33,7 +35,9 @@ async function convertMCPServersToTools(params) {
|
|
|
33
35
|
}
|
|
34
36
|
const sortedTools = [...mcpTools.tools].sort((a, b) => a.name > b.name ? 1 : -1);
|
|
35
37
|
for (const mcpTool of sortedTools) {
|
|
36
|
-
if (!mcpTool.preload)
|
|
38
|
+
if (!mcpTool.preload) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
37
41
|
const { name: toolName } = getUniqueSanitizedToolName(mcpTool.name, registeredNames);
|
|
38
42
|
registeredNames.add(toolName);
|
|
39
43
|
const description = `mcp server: ${serverName}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/convertMCPServers.ts"],"sourcesContent":["import type { ChatCompletionTool } from 'openai/resources/chat';\nimport type { MCPServerInitInfo } from '../events/schema';\nimport type { MCPAuthRequired } from '../mcp/IMCPServer';\nimport { type IToolSet, isAuthRequired } from './IMCPServer';\nimport { getUniqueSanitizedToolName } from './toolNames';\n\nexport interface MappedMCPTool {\n toolSet: IToolSet;\n originalToolName: string;\n}\n\nexport interface ConvertToolsResult {\n tools: ChatCompletionTool[];\n toolMapping: Map<string, MappedMCPTool>;\n}\n\nexport interface ConvertMCPServersToToolsResult {\n convertedTools: ConvertToolsResult;\n initializationInfo: MCPServerInitInfo[];\n authRequirementInfo: MCPAuthRequired[];\n}\n\nexport async function convertMCPServersToTools(params: {\n tfyManagedServers: IToolSet[];\n userServers: IToolSet[];\n}): Promise<ConvertMCPServersToToolsResult> {\n const { tfyManagedServers, userServers } = params;\n const tools: ChatCompletionTool[] = [];\n const toolMapping = new Map<string, MappedMCPTool>();\n const initializationInfo: MCPServerInitInfo[] = [];\n const authRequirementInfo: MCPAuthRequired[] = [];\n const registeredNames = new Set<string>();\n\n // Skip non-preloaded servers that have no preloaded tools, to avoid unnecessary listTools() calls and OAuth triggers.\n // Non-preloaded servers with some preloaded tools are kept so those tools can be registered.\n const filteredUserServers = userServers.filter(s => s.preload || s.hasPreloadedTools);\n\n // TFY-managed servers first so they claim their original tool names before user servers.\n const sortedTfyServers = [...tfyManagedServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const sortedUserServers = [...filteredUserServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const allServers = [...sortedTfyServers, ...sortedUserServers];\n\n const listResults = await Promise.all(\n allServers.map(async server => ({ serverName: server.name, response: await server.listTools() })),\n );\n\n for (let i = 0; i < allServers.length; i++) {\n const listResult = listResults[i];\n if (!listResult) continue;\n const { serverName, response: listResponse } = listResult;\n if (isAuthRequired(listResponse)) {\n authRequirementInfo.push(listResponse.authRequired);\n continue;\n }\n const { result: mcpTools, wasInitialized } = listResponse;\n if (wasInitialized) {\n initializationInfo.push(wasInitialized);\n }\n\n const toolSet = allServers[i];\n if (toolSet === undefined) {\n throw new Error(`Unreachable: missing tool set at index ${String(i)}`);\n }\n const sortedTools = [...mcpTools.tools].sort((a, b) => (a.name > b.name ? 1 : -1));\n for (const mcpTool of sortedTools) {\n if (!mcpTool.preload) continue;\n const { name: toolName } = getUniqueSanitizedToolName(mcpTool.name, registeredNames);\n registeredNames.add(toolName);\n const description = `mcp server: ${serverName}\\n${mcpTool.description ?? ''}`;\n tools.push({\n type: 'function',\n function: {\n name: toolName,\n description: description,\n parameters: {\n type: 'object',\n properties: mcpTool.inputSchema.properties ?? {},\n required: mcpTool.inputSchema.required ?? [],\n },\n },\n });\n\n toolMapping.set(toolName, {\n toolSet,\n originalToolName: mcpTool.name,\n });\n }\n }\n\n return { convertedTools: { tools, toolMapping }, initializationInfo, authRequirementInfo };\n}\n"],"mappings":";AAGA,SAAwB,sBAAsB;AAC9C,SAAS,kCAAkC;AAkB3C,eAAsB,yBAAyB,QAGH;AAC1C,QAAM,EAAE,mBAAmB,YAAY,IAAI;AAC3C,QAAM,QAA8B,CAAC;AACrC,QAAM,cAAc,oBAAI,IAA2B;AACnD,QAAM,qBAA0C,CAAC;AACjD,QAAM,sBAAyC,CAAC;AAChD,QAAM,kBAAkB,oBAAI,IAAY;AAIxC,QAAM,sBAAsB,YAAY,OAAO,OAAK,EAAE,WAAW,EAAE,iBAAiB;AAGpF,QAAM,mBAAmB,CAAC,GAAG,iBAAiB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AACzF,QAAM,oBAAoB,CAAC,GAAG,mBAAmB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AAC5F,QAAM,aAAa,CAAC,GAAG,kBAAkB,GAAG,iBAAiB;AAE7D,QAAM,cAAc,MAAM,QAAQ;AAAA,IAChC,WAAW,IAAI,OAAM,YAAW,EAAE,YAAY,OAAO,MAAM,UAAU,MAAM,OAAO,UAAU,EAAE,EAAE;AAAA,EAClG;AAEA,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,aAAa,YAAY,CAAC;AAChC,QAAI,CAAC,
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/convertMCPServers.ts"],"sourcesContent":["import type { ChatCompletionTool } from 'openai/resources/chat';\nimport type { MCPServerInitInfo } from '../events/schema';\nimport type { MCPAuthRequired } from '../mcp/IMCPServer';\nimport { type IToolSet, isAuthRequired } from './IMCPServer';\nimport { getUniqueSanitizedToolName } from './toolNames';\n\nexport interface MappedMCPTool {\n toolSet: IToolSet;\n originalToolName: string;\n}\n\nexport interface ConvertToolsResult {\n tools: ChatCompletionTool[];\n toolMapping: Map<string, MappedMCPTool>;\n}\n\nexport interface ConvertMCPServersToToolsResult {\n convertedTools: ConvertToolsResult;\n initializationInfo: MCPServerInitInfo[];\n authRequirementInfo: MCPAuthRequired[];\n}\n\nexport async function convertMCPServersToTools(params: {\n tfyManagedServers: IToolSet[];\n userServers: IToolSet[];\n}): Promise<ConvertMCPServersToToolsResult> {\n const { tfyManagedServers, userServers } = params;\n const tools: ChatCompletionTool[] = [];\n const toolMapping = new Map<string, MappedMCPTool>();\n const initializationInfo: MCPServerInitInfo[] = [];\n const authRequirementInfo: MCPAuthRequired[] = [];\n const registeredNames = new Set<string>();\n\n // Skip non-preloaded servers that have no preloaded tools, to avoid unnecessary listTools() calls and OAuth triggers.\n // Non-preloaded servers with some preloaded tools are kept so those tools can be registered.\n const filteredUserServers = userServers.filter(s => s.preload || s.hasPreloadedTools);\n\n // TFY-managed servers first so they claim their original tool names before user servers.\n const sortedTfyServers = [...tfyManagedServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const sortedUserServers = [...filteredUserServers].sort((a, b) => (a.name > b.name ? 1 : -1));\n const allServers = [...sortedTfyServers, ...sortedUserServers];\n\n const listResults = await Promise.all(\n allServers.map(async server => ({ serverName: server.name, response: await server.listTools() })),\n );\n\n for (let i = 0; i < allServers.length; i++) {\n const listResult = listResults[i];\n if (!listResult) {\n continue;\n }\n const { serverName, response: listResponse } = listResult;\n if (isAuthRequired(listResponse)) {\n authRequirementInfo.push(listResponse.authRequired);\n continue;\n }\n const { result: mcpTools, wasInitialized } = listResponse;\n if (wasInitialized) {\n initializationInfo.push(wasInitialized);\n }\n\n const toolSet = allServers[i];\n if (toolSet === undefined) {\n throw new Error(`Unreachable: missing tool set at index ${String(i)}`);\n }\n const sortedTools = [...mcpTools.tools].sort((a, b) => (a.name > b.name ? 1 : -1));\n for (const mcpTool of sortedTools) {\n if (!mcpTool.preload) {\n continue;\n }\n const { name: toolName } = getUniqueSanitizedToolName(mcpTool.name, registeredNames);\n registeredNames.add(toolName);\n const description = `mcp server: ${serverName}\\n${mcpTool.description ?? ''}`;\n tools.push({\n type: 'function',\n function: {\n name: toolName,\n description: description,\n parameters: {\n type: 'object',\n properties: mcpTool.inputSchema.properties ?? {},\n required: mcpTool.inputSchema.required ?? [],\n },\n },\n });\n\n toolMapping.set(toolName, {\n toolSet,\n originalToolName: mcpTool.name,\n });\n }\n }\n\n return { convertedTools: { tools, toolMapping }, initializationInfo, authRequirementInfo };\n}\n"],"mappings":";AAGA,SAAwB,sBAAsB;AAC9C,SAAS,kCAAkC;AAkB3C,eAAsB,yBAAyB,QAGH;AAC1C,QAAM,EAAE,mBAAmB,YAAY,IAAI;AAC3C,QAAM,QAA8B,CAAC;AACrC,QAAM,cAAc,oBAAI,IAA2B;AACnD,QAAM,qBAA0C,CAAC;AACjD,QAAM,sBAAyC,CAAC;AAChD,QAAM,kBAAkB,oBAAI,IAAY;AAIxC,QAAM,sBAAsB,YAAY,OAAO,OAAK,EAAE,WAAW,EAAE,iBAAiB;AAGpF,QAAM,mBAAmB,CAAC,GAAG,iBAAiB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AACzF,QAAM,oBAAoB,CAAC,GAAG,mBAAmB,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AAC5F,QAAM,aAAa,CAAC,GAAG,kBAAkB,GAAG,iBAAiB;AAE7D,QAAM,cAAc,MAAM,QAAQ;AAAA,IAChC,WAAW,IAAI,OAAM,YAAW,EAAE,YAAY,OAAO,MAAM,UAAU,MAAM,OAAO,UAAU,EAAE,EAAE;AAAA,EAClG;AAEA,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,aAAa,YAAY,CAAC;AAChC,QAAI,CAAC,YAAY;AACf;AAAA,IACF;AACA,UAAM,EAAE,YAAY,UAAU,aAAa,IAAI;AAC/C,QAAI,eAAe,YAAY,GAAG;AAChC,0BAAoB,KAAK,aAAa,YAAY;AAClD;AAAA,IACF;AACA,UAAM,EAAE,QAAQ,UAAU,eAAe,IAAI;AAC7C,QAAI,gBAAgB;AAClB,yBAAmB,KAAK,cAAc;AAAA,IACxC;AAEA,UAAM,UAAU,WAAW,CAAC;AAC5B,QAAI,YAAY,QAAW;AACzB,YAAM,IAAI,MAAM,0CAA0C,OAAO,CAAC,CAAC,EAAE;AAAA,IACvE;AACA,UAAM,cAAc,CAAC,GAAG,SAAS,KAAK,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAG;AACjF,eAAW,WAAW,aAAa;AACjC,UAAI,CAAC,QAAQ,SAAS;AACpB;AAAA,MACF;AACA,YAAM,EAAE,MAAM,SAAS,IAAI,2BAA2B,QAAQ,MAAM,eAAe;AACnF,sBAAgB,IAAI,QAAQ;AAC5B,YAAM,cAAc,eAAe,UAAU;AAAA,EAAK,QAAQ,eAAe,EAAE;AAC3E,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,UACR,MAAM;AAAA,UACN;AAAA,UACA,YAAY;AAAA,YACV,MAAM;AAAA,YACN,YAAY,QAAQ,YAAY,cAAc,CAAC;AAAA,YAC/C,UAAU,QAAQ,YAAY,YAAY,CAAC;AAAA,UAC7C;AAAA,QACF;AAAA,MACF,CAAC;AAED,kBAAY,IAAI,UAAU;AAAA,QACxB;AAAA,QACA,kBAAkB,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,gBAAgB,EAAE,OAAO,YAAY,GAAG,oBAAoB,oBAAoB;AAC3F;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteMcpClient.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/remoteMcpClient.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAI1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,iGAAiG;AAEjG,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,KAAK,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,sBAAsB,CAAC;IAC/C,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;IAC9F,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAoCD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"remoteMcpClient.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/remoteMcpClient.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAI1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,iGAAiG;AAEjG,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,KAAK,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,sBAAsB,CAAC;IAC/C,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;IAC9F,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAoCD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQ7D;AAgED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,kBAAkB,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IACxD,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAChD,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4C/B"}
|
|
@@ -52,13 +52,21 @@ function createTransport(type, url, headers, sessionId) {
|
|
|
52
52
|
return new import_sse.SSEClientTransport(url, { requestInit });
|
|
53
53
|
}
|
|
54
54
|
function isSessionExpiredError(error) {
|
|
55
|
-
if (!(error instanceof Error))
|
|
56
|
-
|
|
55
|
+
if (!(error instanceof Error)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if ("code" in error && error.code === 404) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
57
61
|
return error.message.includes("HTTP 404") || error.message.toLowerCase().includes("session");
|
|
58
62
|
}
|
|
59
63
|
function isAuthError(error) {
|
|
60
|
-
if (!(error instanceof Error))
|
|
61
|
-
|
|
64
|
+
if (!(error instanceof Error)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if ("code" in error && error.code === 401) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
62
70
|
return error.message.includes("HTTP 401");
|
|
63
71
|
}
|
|
64
72
|
function stampTraceHeaders(headers) {
|
|
@@ -68,7 +76,9 @@ function getTransportSessionId(transport) {
|
|
|
68
76
|
return transport instanceof import_streamableHttp.StreamableHTTPClientTransport ? transport.sessionId ?? null : null;
|
|
69
77
|
}
|
|
70
78
|
function toConnectError(error) {
|
|
71
|
-
if (error instanceof import_errors.McpConnectionError)
|
|
79
|
+
if (error instanceof import_errors.McpConnectionError) {
|
|
80
|
+
return error;
|
|
81
|
+
}
|
|
72
82
|
if (isAuthError(error)) {
|
|
73
83
|
return new import_errors.McpConnectionError("upstream returned 401 Unauthorized (check x-tfy-mcp-headers credentials)", 401, {
|
|
74
84
|
cause: error
|
|
@@ -119,8 +129,12 @@ async function connectRemoteMcp(params) {
|
|
|
119
129
|
} catch (error) {
|
|
120
130
|
await client.close().catch(() => {
|
|
121
131
|
});
|
|
122
|
-
if (isAuthError(error))
|
|
123
|
-
|
|
132
|
+
if (isAuthError(error)) {
|
|
133
|
+
throw toConnectError(error);
|
|
134
|
+
}
|
|
135
|
+
if (params.sessionId && isSessionExpiredError(error)) {
|
|
136
|
+
throw toConnectError(error);
|
|
137
|
+
}
|
|
124
138
|
failures.push({ transport: transportType, error: error instanceof Error ? error.message.trim() : String(error) });
|
|
125
139
|
continue;
|
|
126
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/mcp/remoteMcpClient.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\n// SSE remains required during the Streamable HTTP migration (some servers still speak SSE only).\n\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { CallToolRequest, CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { context, propagation } from '@opentelemetry/api';\nimport { McpConnectionError } from '../errors';\nimport { withTimeout } from '../util/promiseUtils';\nimport type { ToolSchema } from './IMCPServer';\n\n/** Networking for remote (url-based) MCP servers, kept separate so it can be mocked in tests. */\n\nexport type RemoteMcpTransportType = 'streamable-http' | 'sse';\n\nexport interface RemoteMcpConnection {\n readonly transportType: RemoteMcpTransportType;\n /** Session id for a stateful server, or null for a stateless one. */\n readonly sessionId: string | null;\n listTools(cursor?: string): Promise<{ tools: ToolSchema[]; nextCursor?: string | undefined }>;\n callTool(params: CallToolRequest['params']): Promise<CallToolResult>;\n close(): Promise<void>;\n}\n\n// SSE transport type kept for dual-probe support during migration.\n// eslint-disable-next-line @typescript-eslint/no-deprecated -- see TRANSPORT_PROBE_ORDER\ntype McpTransport = StreamableHTTPClientTransport | SSEClientTransport;\n\nconst CLIENT_INFO = { name: 'tfy-agent-mcp-client', version: '1.0.0' } as const;\nconst TRANSPORT_PROBE_ORDER: RemoteMcpTransportType[] = ['streamable-http', 'sse'];\n\nclass McpClientWithTimeout extends Client {\n constructor(private readonly requestTimeoutMs: number) {\n super(CLIENT_INFO, { capabilities: {} });\n }\n\n // SDK Client.request is loosely typed; forward with an explicit timeout.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP SDK request typing\n override request(req: any, schema: any, options?: any): Promise<any> {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- MCP SDK request typing\n return super.request(req, schema, { ...options, timeout: this.requestTimeoutMs });\n }\n}\n\nfunction createTransport(\n type: RemoteMcpTransportType,\n url: URL,\n headers: Record<string, string>,\n sessionId?: string,\n): McpTransport {\n const requestInit = { headers };\n if (type === 'streamable-http') {\n return new StreamableHTTPClientTransport(url, { requestInit, ...(sessionId !== undefined ? { sessionId } : {}) });\n }\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- dual-transport probe; see TRANSPORT_PROBE_ORDER\n return new SSEClientTransport(url, { requestInit });\n}\n\nexport function isSessionExpiredError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n if ('code' in error && error.code === 404) return true;\n return error.message.includes('HTTP 404') || error.message.toLowerCase().includes('session');\n}\n\nfunction isAuthError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n if ('code' in error && error.code === 401) return true;\n return error.message.includes('HTTP 401');\n}\n\n/** Inject the active otel trace context so each request propagates its span. */\nfunction stampTraceHeaders(headers: Record<string, string>): void {\n propagation.inject(context.active(), headers);\n}\n\nfunction getTransportSessionId(transport: McpTransport): string | null {\n return transport instanceof StreamableHTTPClientTransport ? (transport.sessionId ?? null) : null;\n}\n\nfunction toConnectError(error: unknown): McpConnectionError {\n if (error instanceof McpConnectionError) return error;\n if (isAuthError(error)) {\n return new McpConnectionError('upstream returned 401 Unauthorized (check x-tfy-mcp-headers credentials)', 401, {\n cause: error,\n });\n }\n const message = error instanceof Error ? error.message : String(error);\n return new McpConnectionError(message, 502, { cause: error });\n}\n\nfunction buildConnection(\n client: McpClientWithTimeout,\n transport: McpTransport,\n transportType: RemoteMcpTransportType,\n headers: Record<string, string>,\n requestOptions: { signal: AbortSignal },\n): RemoteMcpConnection {\n return {\n transportType,\n sessionId: getTransportSessionId(transport),\n listTools: async (cursor?: string) => {\n stampTraceHeaders(headers);\n const response = await client.listTools(cursor ? { cursor } : undefined, requestOptions);\n return {\n tools: response.tools,\n nextCursor: response.nextCursor,\n };\n },\n callTool: async (callParams: CallToolRequest['params']): Promise<CallToolResult> => {\n stampTraceHeaders(headers);\n return (await client.callTool(callParams, undefined, requestOptions)) as CallToolResult;\n },\n close: async (): Promise<void> => {\n await client.close().catch(() => {\n /* no-op */\n });\n },\n };\n}\n\n/**\n * Connect to a remote MCP server, keeping the first transport that connects. `sessionId` is passed to\n * every attempt so a stateful session resumes in place instead of opening a throwaway one.\n *\n * `knownTransportType` is a hint (from a prior connect / persisted state): it's tried first for a fast\n * path, but on failure we still fall back to probing the remaining transports, so a stale or wrong\n * hint (server switched transports, bad resume data) self-heals instead of failing every turn.\n */\nexport async function connectRemoteMcp(params: {\n url: string;\n headers: Record<string, string>;\n sessionId?: string | undefined;\n knownTransportType?: RemoteMcpTransportType | undefined;\n requestTimeoutMs: number;\n connectTimeoutMs: number;\n signal: AbortSignal;\n onClose?: (() => void) | undefined;\n onError?: ((error: Error) => void) | undefined;\n}): Promise<RemoteMcpConnection> {\n const url = new URL(params.url);\n const requestOptions = { signal: params.signal };\n const candidates = params.knownTransportType\n ? [params.knownTransportType, ...TRANSPORT_PROBE_ORDER.filter(t => t !== params.knownTransportType)]\n : TRANSPORT_PROBE_ORDER;\n const failures: { transport: RemoteMcpTransportType; error: string }[] = [];\n\n for (const transportType of candidates) {\n const transport = createTransport(transportType, url, params.headers, params.sessionId);\n const client = new McpClientWithTimeout(params.requestTimeoutMs);\n try {\n stampTraceHeaders(params.headers);\n await withTimeout(\n // Concrete transports use sessionId: string|undefined; Transport uses an optional\n // property — exactOptionalPropertyTypes rejects assignability without this cast.\n client.connect(transport as Parameters<Client['connect']>[0], requestOptions),\n params.connectTimeoutMs,\n transportType,\n );\n } catch (error) {\n await client.close().catch(() => {\n /* no-op */\n });\n if (isAuthError(error)) throw toConnectError(error);\n // A session-expired error means the transport is right but the session is stale: surface it so\n // the caller reconnects fresh instead of falling through to a different transport.\n if (params.sessionId && isSessionExpiredError(error)) throw toConnectError(error);\n failures.push({ transport: transportType, error: error instanceof Error ? error.message.trim() : String(error) });\n continue;\n }\n\n // Set on the client (not the transport) so the SDK's own onclose/onerror cleanup still runs; the\n // SDK invokes these from inside it. Only wired on the kept connection, so failed attempts stay quiet.\n client.onclose = () => params.onClose?.();\n client.onerror = error => params.onError?.(error);\n return buildConnection(client, transport, transportType, params.headers, requestOptions);\n }\n\n throw new McpConnectionError(`failed to connect (tried ${candidates.join(', ')}): ${JSON.stringify(failures)}`, 502);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/mcp/remoteMcpClient.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\n// SSE remains required during the Streamable HTTP migration (some servers still speak SSE only).\n\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { CallToolRequest, CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { context, propagation } from '@opentelemetry/api';\nimport { McpConnectionError } from '../errors';\nimport { withTimeout } from '../util/promiseUtils';\nimport type { ToolSchema } from './IMCPServer';\n\n/** Networking for remote (url-based) MCP servers, kept separate so it can be mocked in tests. */\n\nexport type RemoteMcpTransportType = 'streamable-http' | 'sse';\n\nexport interface RemoteMcpConnection {\n readonly transportType: RemoteMcpTransportType;\n /** Session id for a stateful server, or null for a stateless one. */\n readonly sessionId: string | null;\n listTools(cursor?: string): Promise<{ tools: ToolSchema[]; nextCursor?: string | undefined }>;\n callTool(params: CallToolRequest['params']): Promise<CallToolResult>;\n close(): Promise<void>;\n}\n\n// SSE transport type kept for dual-probe support during migration.\n// eslint-disable-next-line @typescript-eslint/no-deprecated -- see TRANSPORT_PROBE_ORDER\ntype McpTransport = StreamableHTTPClientTransport | SSEClientTransport;\n\nconst CLIENT_INFO = { name: 'tfy-agent-mcp-client', version: '1.0.0' } as const;\nconst TRANSPORT_PROBE_ORDER: RemoteMcpTransportType[] = ['streamable-http', 'sse'];\n\nclass McpClientWithTimeout extends Client {\n constructor(private readonly requestTimeoutMs: number) {\n super(CLIENT_INFO, { capabilities: {} });\n }\n\n // SDK Client.request is loosely typed; forward with an explicit timeout.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP SDK request typing\n override request(req: any, schema: any, options?: any): Promise<any> {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- MCP SDK request typing\n return super.request(req, schema, { ...options, timeout: this.requestTimeoutMs });\n }\n}\n\nfunction createTransport(\n type: RemoteMcpTransportType,\n url: URL,\n headers: Record<string, string>,\n sessionId?: string,\n): McpTransport {\n const requestInit = { headers };\n if (type === 'streamable-http') {\n return new StreamableHTTPClientTransport(url, { requestInit, ...(sessionId !== undefined ? { sessionId } : {}) });\n }\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- dual-transport probe; see TRANSPORT_PROBE_ORDER\n return new SSEClientTransport(url, { requestInit });\n}\n\nexport function isSessionExpiredError(error: unknown): boolean {\n if (!(error instanceof Error)) {\n return false;\n }\n if ('code' in error && error.code === 404) {\n return true;\n }\n return error.message.includes('HTTP 404') || error.message.toLowerCase().includes('session');\n}\n\nfunction isAuthError(error: unknown): boolean {\n if (!(error instanceof Error)) {\n return false;\n }\n if ('code' in error && error.code === 401) {\n return true;\n }\n return error.message.includes('HTTP 401');\n}\n\n/** Inject the active otel trace context so each request propagates its span. */\nfunction stampTraceHeaders(headers: Record<string, string>): void {\n propagation.inject(context.active(), headers);\n}\n\nfunction getTransportSessionId(transport: McpTransport): string | null {\n return transport instanceof StreamableHTTPClientTransport ? (transport.sessionId ?? null) : null;\n}\n\nfunction toConnectError(error: unknown): McpConnectionError {\n if (error instanceof McpConnectionError) {\n return error;\n }\n if (isAuthError(error)) {\n return new McpConnectionError('upstream returned 401 Unauthorized (check x-tfy-mcp-headers credentials)', 401, {\n cause: error,\n });\n }\n const message = error instanceof Error ? error.message : String(error);\n return new McpConnectionError(message, 502, { cause: error });\n}\n\nfunction buildConnection(\n client: McpClientWithTimeout,\n transport: McpTransport,\n transportType: RemoteMcpTransportType,\n headers: Record<string, string>,\n requestOptions: { signal: AbortSignal },\n): RemoteMcpConnection {\n return {\n transportType,\n sessionId: getTransportSessionId(transport),\n listTools: async (cursor?: string) => {\n stampTraceHeaders(headers);\n const response = await client.listTools(cursor ? { cursor } : undefined, requestOptions);\n return {\n tools: response.tools,\n nextCursor: response.nextCursor,\n };\n },\n callTool: async (callParams: CallToolRequest['params']): Promise<CallToolResult> => {\n stampTraceHeaders(headers);\n return (await client.callTool(callParams, undefined, requestOptions)) as CallToolResult;\n },\n close: async (): Promise<void> => {\n await client.close().catch(() => {\n /* no-op */\n });\n },\n };\n}\n\n/**\n * Connect to a remote MCP server, keeping the first transport that connects. `sessionId` is passed to\n * every attempt so a stateful session resumes in place instead of opening a throwaway one.\n *\n * `knownTransportType` is a hint (from a prior connect / persisted state): it's tried first for a fast\n * path, but on failure we still fall back to probing the remaining transports, so a stale or wrong\n * hint (server switched transports, bad resume data) self-heals instead of failing every turn.\n */\nexport async function connectRemoteMcp(params: {\n url: string;\n headers: Record<string, string>;\n sessionId?: string | undefined;\n knownTransportType?: RemoteMcpTransportType | undefined;\n requestTimeoutMs: number;\n connectTimeoutMs: number;\n signal: AbortSignal;\n onClose?: (() => void) | undefined;\n onError?: ((error: Error) => void) | undefined;\n}): Promise<RemoteMcpConnection> {\n const url = new URL(params.url);\n const requestOptions = { signal: params.signal };\n const candidates = params.knownTransportType\n ? [params.knownTransportType, ...TRANSPORT_PROBE_ORDER.filter(t => t !== params.knownTransportType)]\n : TRANSPORT_PROBE_ORDER;\n const failures: { transport: RemoteMcpTransportType; error: string }[] = [];\n\n for (const transportType of candidates) {\n const transport = createTransport(transportType, url, params.headers, params.sessionId);\n const client = new McpClientWithTimeout(params.requestTimeoutMs);\n try {\n stampTraceHeaders(params.headers);\n await withTimeout(\n // Concrete transports use sessionId: string|undefined; Transport uses an optional\n // property — exactOptionalPropertyTypes rejects assignability without this cast.\n client.connect(transport as Parameters<Client['connect']>[0], requestOptions),\n params.connectTimeoutMs,\n transportType,\n );\n } catch (error) {\n await client.close().catch(() => {\n /* no-op */\n });\n if (isAuthError(error)) {\n throw toConnectError(error);\n }\n // A session-expired error means the transport is right but the session is stale: surface it so\n // the caller reconnects fresh instead of falling through to a different transport.\n if (params.sessionId && isSessionExpiredError(error)) {\n throw toConnectError(error);\n }\n failures.push({ transport: transportType, error: error instanceof Error ? error.message.trim() : String(error) });\n continue;\n }\n\n // Set on the client (not the transport) so the SDK's own onclose/onerror cleanup still runs; the\n // SDK invokes these from inside it. Only wired on the kept connection, so failed attempts stay quiet.\n client.onclose = () => params.onClose?.();\n client.onerror = error => params.onError?.(error);\n return buildConnection(client, transport, transportType, params.headers, requestOptions);\n }\n\n throw new McpConnectionError(`failed to connect (tried ${candidates.join(', ')}): ${JSON.stringify(failures)}`, 502);\n}\n"],"mappings":"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","names":[]}
|