@illuma-ai/agents 1.3.2 → 1.4.0-alpha.1
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/dist/cjs/graphs/Graph.cjs +3 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +3 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +56 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs +288 -0
- package/dist/cjs/providers/a2a/A2ACapabilityProvider.cjs.map +1 -0
- package/dist/cjs/providers/a2a/client.cjs +92 -0
- package/dist/cjs/providers/a2a/client.cjs.map +1 -0
- package/dist/cjs/providers/a2a/config.cjs +38 -0
- package/dist/cjs/providers/a2a/config.cjs.map +1 -0
- package/dist/cjs/providers/capabilityNaming.cjs +43 -0
- package/dist/cjs/providers/capabilityNaming.cjs.map +1 -0
- package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs +244 -0
- package/dist/cjs/providers/mcp/MCPCapabilityProvider.cjs.map +1 -0
- package/dist/cjs/providers/mcp/config.cjs +42 -0
- package/dist/cjs/providers/mcp/config.cjs.map +1 -0
- package/dist/cjs/providers/mcp/transport.cjs +65 -0
- package/dist/cjs/providers/mcp/transport.cjs.map +1 -0
- package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs +121 -0
- package/dist/cjs/providers/tools-server/ToolsServerCapabilityProvider.cjs.map +1 -0
- package/dist/cjs/providers/types.cjs +51 -0
- package/dist/cjs/providers/types.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +3 -0
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/proxyTool.cjs +100 -0
- package/dist/cjs/tools/proxyTool.cjs.map +1 -0
- package/dist/cjs/utils/credentials.cjs +142 -0
- package/dist/cjs/utils/credentials.cjs.map +1 -0
- package/dist/cjs/utils/httpClient.cjs +74 -0
- package/dist/cjs/utils/httpClient.cjs.map +1 -0
- package/dist/cjs/utils/toolManifest.cjs +100 -0
- package/dist/cjs/utils/toolManifest.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +3 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +3 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +13 -0
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs +281 -0
- package/dist/esm/providers/a2a/A2ACapabilityProvider.mjs.map +1 -0
- package/dist/esm/providers/a2a/client.mjs +88 -0
- package/dist/esm/providers/a2a/client.mjs.map +1 -0
- package/dist/esm/providers/a2a/config.mjs +35 -0
- package/dist/esm/providers/a2a/config.mjs.map +1 -0
- package/dist/esm/providers/capabilityNaming.mjs +39 -0
- package/dist/esm/providers/capabilityNaming.mjs.map +1 -0
- package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs +240 -0
- package/dist/esm/providers/mcp/MCPCapabilityProvider.mjs.map +1 -0
- package/dist/esm/providers/mcp/config.mjs +39 -0
- package/dist/esm/providers/mcp/config.mjs.map +1 -0
- package/dist/esm/providers/mcp/transport.mjs +63 -0
- package/dist/esm/providers/mcp/transport.mjs.map +1 -0
- package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs +119 -0
- package/dist/esm/providers/tools-server/ToolsServerCapabilityProvider.mjs.map +1 -0
- package/dist/esm/providers/types.mjs +51 -0
- package/dist/esm/providers/types.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +3 -0
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/proxyTool.mjs +98 -0
- package/dist/esm/tools/proxyTool.mjs.map +1 -0
- package/dist/esm/utils/credentials.mjs +135 -0
- package/dist/esm/utils/credentials.mjs.map +1 -0
- package/dist/esm/utils/httpClient.mjs +70 -0
- package/dist/esm/utils/httpClient.mjs.map +1 -0
- package/dist/esm/utils/toolManifest.mjs +96 -0
- package/dist/esm/utils/toolManifest.mjs.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/providers/a2a/A2ACapabilityProvider.d.ts +89 -0
- package/dist/types/providers/a2a/client.d.ts +47 -0
- package/dist/types/providers/a2a/config.d.ts +18 -0
- package/dist/types/providers/a2a/index.d.ts +6 -0
- package/dist/types/providers/a2a/types.d.ts +173 -0
- package/dist/types/providers/capabilityNaming.d.ts +25 -0
- package/dist/types/providers/index.d.ts +12 -0
- package/dist/types/providers/mcp/MCPCapabilityProvider.d.ts +54 -0
- package/dist/types/providers/mcp/config.d.ts +20 -0
- package/dist/types/providers/mcp/index.d.ts +5 -0
- package/dist/types/providers/mcp/transport.d.ts +18 -0
- package/dist/types/providers/mcp/types.d.ts +112 -0
- package/dist/types/providers/tools-server/ToolsServerCapabilityProvider.d.ts +45 -0
- package/dist/types/providers/tools-server/index.d.ts +1 -0
- package/dist/types/providers/types.d.ts +170 -0
- package/dist/types/tools/proxyTool.d.ts +55 -0
- package/dist/types/utils/credentials.d.ts +77 -0
- package/dist/types/utils/httpClient.d.ts +46 -0
- package/dist/types/utils/index.d.ts +3 -0
- package/dist/types/utils/toolManifest.d.ts +49 -0
- package/package.json +16 -1
- package/src/graphs/Graph.ts +0 -23
- package/src/index.ts +4 -0
- package/src/providers/__tests__/ToolsServerCapabilityProvider.integration.spec.ts +79 -0
- package/src/providers/__tests__/ToolsServerCapabilityProvider.test.ts +206 -0
- package/src/providers/__tests__/types.test.ts +64 -0
- package/src/providers/a2a/A2ACapabilityProvider.ts +384 -0
- package/src/providers/a2a/__tests__/A2ACapabilityProvider.integration.spec.ts +345 -0
- package/src/providers/a2a/__tests__/A2ACapabilityProvider.test.ts +460 -0
- package/src/providers/a2a/client.ts +115 -0
- package/src/providers/a2a/config.ts +40 -0
- package/src/providers/a2a/index.ts +29 -0
- package/src/providers/a2a/types.ts +191 -0
- package/src/providers/capabilityNaming.ts +42 -0
- package/src/providers/index.ts +68 -0
- package/src/providers/mcp/MCPCapabilityProvider.ts +345 -0
- package/src/providers/mcp/__tests__/MCPCapabilityProvider.integration.spec.ts +386 -0
- package/src/providers/mcp/__tests__/MCPCapabilityProvider.test.ts +371 -0
- package/src/providers/mcp/config.ts +45 -0
- package/src/providers/mcp/index.ts +21 -0
- package/src/providers/mcp/transport.ts +76 -0
- package/src/providers/mcp/types.ts +139 -0
- package/src/providers/tools-server/ToolsServerCapabilityProvider.ts +220 -0
- package/src/providers/tools-server/index.ts +1 -0
- package/src/providers/types.ts +187 -0
- package/src/tools/proxyTool.ts +146 -0
- package/src/utils/__tests__/credentials.test.ts +130 -0
- package/src/utils/__tests__/httpClient.test.ts +75 -0
- package/src/utils/__tests__/toolManifest.test.ts +116 -0
- package/src/utils/credentials.ts +157 -0
- package/src/utils/httpClient.ts +92 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/toolManifest.ts +109 -0
- package/src/agents/AgentContext.js.map +0 -1
- package/src/agents/AgentContext.test.js.map +0 -1
- package/src/agents/__tests__/AgentContext.test.js.map +0 -1
- package/src/agents/__tests__/resolveStructuredOutputMode.test.js.map +0 -1
- package/src/common/enum.js.map +0 -1
- package/src/common/index.js.map +0 -1
- package/src/events.js.map +0 -1
- package/src/graphs/Graph.js.map +0 -1
- package/src/graphs/MultiAgentGraph.js.map +0 -1
- package/src/graphs/__tests__/structured-output.integration.test.js.map +0 -1
- package/src/graphs/__tests__/structured-output.test.js.map +0 -1
- package/src/graphs/contextManagement.e2e.test.js.map +0 -1
- package/src/graphs/contextManagement.test.js.map +0 -1
- package/src/graphs/handoffValidation.test.js.map +0 -1
- package/src/graphs/index.js.map +0 -1
- package/src/index.js.map +0 -1
- package/src/instrumentation.js.map +0 -1
- package/src/llm/anthropic/index.js.map +0 -1
- package/src/llm/anthropic/types.js.map +0 -1
- package/src/llm/anthropic/utils/message_inputs.js.map +0 -1
- package/src/llm/anthropic/utils/message_outputs.js.map +0 -1
- package/src/llm/anthropic/utils/output_parsers.js.map +0 -1
- package/src/llm/anthropic/utils/tools.js.map +0 -1
- package/src/llm/bedrock/__tests__/bedrock-caching.test.js.map +0 -1
- package/src/llm/bedrock/index.js.map +0 -1
- package/src/llm/bedrock/types.js.map +0 -1
- package/src/llm/bedrock/utils/index.js.map +0 -1
- package/src/llm/bedrock/utils/message_inputs.js.map +0 -1
- package/src/llm/bedrock/utils/message_outputs.js.map +0 -1
- package/src/llm/fake.js.map +0 -1
- package/src/llm/google/index.js.map +0 -1
- package/src/llm/google/types.js.map +0 -1
- package/src/llm/google/utils/common.js.map +0 -1
- package/src/llm/google/utils/tools.js.map +0 -1
- package/src/llm/google/utils/zod_to_genai_parameters.js.map +0 -1
- package/src/llm/openai/index.js.map +0 -1
- package/src/llm/openai/types.js.map +0 -1
- package/src/llm/openai/utils/index.js.map +0 -1
- package/src/llm/openai/utils/isReasoningModel.test.js.map +0 -1
- package/src/llm/openrouter/index.js.map +0 -1
- package/src/llm/openrouter/reasoning.test.js.map +0 -1
- package/src/llm/providers.js.map +0 -1
- package/src/llm/text.js.map +0 -1
- package/src/llm/vertexai/index.js.map +0 -1
- package/src/messages/__tests__/tools.test.js.map +0 -1
- package/src/messages/cache.js.map +0 -1
- package/src/messages/cache.test.js.map +0 -1
- package/src/messages/content.js.map +0 -1
- package/src/messages/content.test.js.map +0 -1
- package/src/messages/core.js.map +0 -1
- package/src/messages/ensureThinkingBlock.test.js.map +0 -1
- package/src/messages/format.js.map +0 -1
- package/src/messages/formatAgentMessages.test.js.map +0 -1
- package/src/messages/formatAgentMessages.tools.test.js.map +0 -1
- package/src/messages/formatMessage.test.js.map +0 -1
- package/src/messages/ids.js.map +0 -1
- package/src/messages/index.js.map +0 -1
- package/src/messages/labelContentByAgent.test.js.map +0 -1
- package/src/messages/prune.js.map +0 -1
- package/src/messages/reducer.js.map +0 -1
- package/src/messages/shiftIndexTokenCountMap.test.js.map +0 -1
- package/src/messages/summarize.js.map +0 -1
- package/src/messages/summarize.test.js.map +0 -1
- package/src/messages/tools.js.map +0 -1
- package/src/mockStream.js.map +0 -1
- package/src/prompts/collab.js.map +0 -1
- package/src/prompts/index.js.map +0 -1
- package/src/prompts/taskmanager.js.map +0 -1
- package/src/run.js.map +0 -1
- package/src/schemas/index.js.map +0 -1
- package/src/schemas/schema-preparation.test.js.map +0 -1
- package/src/schemas/validate.js.map +0 -1
- package/src/schemas/validate.test.js.map +0 -1
- package/src/scripts/abort.js.map +0 -1
- package/src/scripts/ant_web_search.js.map +0 -1
- package/src/scripts/ant_web_search_edge_case.js.map +0 -1
- package/src/scripts/ant_web_search_error_edge_case.js.map +0 -1
- package/src/scripts/args.js.map +0 -1
- package/src/scripts/bedrock-cache-debug.js.map +0 -1
- package/src/scripts/bedrock-content-aggregation-test.js.map +0 -1
- package/src/scripts/bedrock-merge-test.js.map +0 -1
- package/src/scripts/bedrock-parallel-tools-test.js.map +0 -1
- package/src/scripts/caching.js.map +0 -1
- package/src/scripts/cli.js.map +0 -1
- package/src/scripts/cli2.js.map +0 -1
- package/src/scripts/cli3.js.map +0 -1
- package/src/scripts/cli4.js.map +0 -1
- package/src/scripts/cli5.js.map +0 -1
- package/src/scripts/code_exec.js.map +0 -1
- package/src/scripts/code_exec_files.js.map +0 -1
- package/src/scripts/code_exec_multi_session.js.map +0 -1
- package/src/scripts/code_exec_ptc.js.map +0 -1
- package/src/scripts/code_exec_session.js.map +0 -1
- package/src/scripts/code_exec_simple.js.map +0 -1
- package/src/scripts/content.js.map +0 -1
- package/src/scripts/empty_input.js.map +0 -1
- package/src/scripts/handoff-test.js.map +0 -1
- package/src/scripts/image.js.map +0 -1
- package/src/scripts/memory.js.map +0 -1
- package/src/scripts/multi-agent-chain.js.map +0 -1
- package/src/scripts/multi-agent-conditional.js.map +0 -1
- package/src/scripts/multi-agent-document-review-chain.js.map +0 -1
- package/src/scripts/multi-agent-hybrid-flow.js.map +0 -1
- package/src/scripts/multi-agent-parallel-start.js.map +0 -1
- package/src/scripts/multi-agent-parallel.js.map +0 -1
- package/src/scripts/multi-agent-sequence.js.map +0 -1
- package/src/scripts/multi-agent-supervisor.js.map +0 -1
- package/src/scripts/multi-agent-test.js.map +0 -1
- package/src/scripts/parallel-asymmetric-tools-test.js.map +0 -1
- package/src/scripts/parallel-full-metadata-test.js.map +0 -1
- package/src/scripts/parallel-tools-test.js.map +0 -1
- package/src/scripts/programmatic_exec.js.map +0 -1
- package/src/scripts/programmatic_exec_agent.js.map +0 -1
- package/src/scripts/search.js.map +0 -1
- package/src/scripts/sequential-full-metadata-test.js.map +0 -1
- package/src/scripts/simple.js.map +0 -1
- package/src/scripts/single-agent-metadata-test.js.map +0 -1
- package/src/scripts/stream.js.map +0 -1
- package/src/scripts/test-custom-prompt-key.js.map +0 -1
- package/src/scripts/test-handoff-input.js.map +0 -1
- package/src/scripts/test-handoff-preamble.js.map +0 -1
- package/src/scripts/test-handoff-steering.js.map +0 -1
- package/src/scripts/test-multi-agent-list-handoff.js.map +0 -1
- package/src/scripts/test-parallel-agent-labeling.js.map +0 -1
- package/src/scripts/test-parallel-handoffs.js.map +0 -1
- package/src/scripts/test-thinking-handoff-bedrock.js.map +0 -1
- package/src/scripts/test-thinking-handoff.js.map +0 -1
- package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js.map +0 -1
- package/src/scripts/test-tool-before-handoff-role-order.js.map +0 -1
- package/src/scripts/test-tools-before-handoff.js.map +0 -1
- package/src/scripts/test_code_api.js.map +0 -1
- package/src/scripts/thinking-bedrock.js.map +0 -1
- package/src/scripts/thinking-vertexai.js.map +0 -1
- package/src/scripts/thinking.js.map +0 -1
- package/src/scripts/tool_search.js.map +0 -1
- package/src/scripts/tools.js.map +0 -1
- package/src/specs/agent-handoffs-bedrock.integration.test.js.map +0 -1
- package/src/specs/agent-handoffs.test.js.map +0 -1
- package/src/specs/anthropic.simple.test.js.map +0 -1
- package/src/specs/azure.simple.test.js.map +0 -1
- package/src/specs/cache.simple.test.js.map +0 -1
- package/src/specs/custom-event-await.test.js.map +0 -1
- package/src/specs/deepseek.simple.test.js.map +0 -1
- package/src/specs/emergency-prune.test.js.map +0 -1
- package/src/specs/moonshot.simple.test.js.map +0 -1
- package/src/specs/observability.integration.test.js.map +0 -1
- package/src/specs/openai.simple.test.js.map +0 -1
- package/src/specs/openrouter.simple.test.js.map +0 -1
- package/src/specs/prune.test.js.map +0 -1
- package/src/specs/reasoning.test.js.map +0 -1
- package/src/specs/spec.utils.js.map +0 -1
- package/src/specs/thinking-handoff.test.js.map +0 -1
- package/src/specs/thinking-prune.test.js.map +0 -1
- package/src/specs/token-distribution-edge-case.test.js.map +0 -1
- package/src/specs/token-memoization.test.js.map +0 -1
- package/src/specs/tokens.test.js.map +0 -1
- package/src/specs/tool-error.test.js.map +0 -1
- package/src/splitStream.js.map +0 -1
- package/src/splitStream.test.js.map +0 -1
- package/src/stream.js.map +0 -1
- package/src/stream.test.js.map +0 -1
- package/src/test/mockTools.js.map +0 -1
- package/src/tools/BrowserTools.js.map +0 -1
- package/src/tools/Calculator.js.map +0 -1
- package/src/tools/Calculator.test.js.map +0 -1
- package/src/tools/CodeExecutor.js.map +0 -1
- package/src/tools/ProgrammaticToolCalling.js.map +0 -1
- package/src/tools/StreamingToolCallBuffer.js.map +0 -1
- package/src/tools/ToolNode.js.map +0 -1
- package/src/tools/ToolSearch.js.map +0 -1
- package/src/tools/__tests__/BrowserTools.test.js.map +0 -1
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.js.map +0 -1
- package/src/tools/__tests__/ProgrammaticToolCalling.test.js.map +0 -1
- package/src/tools/__tests__/StreamingToolCallBuffer.test.js.map +0 -1
- package/src/tools/__tests__/ToolApproval.test.js.map +0 -1
- package/src/tools/__tests__/ToolNode.recovery.test.js.map +0 -1
- package/src/tools/__tests__/ToolNode.session.test.js.map +0 -1
- package/src/tools/__tests__/ToolSearch.integration.test.js.map +0 -1
- package/src/tools/__tests__/ToolSearch.test.js.map +0 -1
- package/src/tools/__tests__/handlers.test.js.map +0 -1
- package/src/tools/__tests__/truncation-recovery.integration.test.js.map +0 -1
- package/src/tools/handlers.js.map +0 -1
- package/src/tools/schema.js.map +0 -1
- package/src/tools/search/anthropic.js.map +0 -1
- package/src/tools/search/content.js.map +0 -1
- package/src/tools/search/content.test.js.map +0 -1
- package/src/tools/search/firecrawl.js.map +0 -1
- package/src/tools/search/format.js.map +0 -1
- package/src/tools/search/highlights.js.map +0 -1
- package/src/tools/search/index.js.map +0 -1
- package/src/tools/search/jina-reranker.test.js.map +0 -1
- package/src/tools/search/rerankers.js.map +0 -1
- package/src/tools/search/schema.js.map +0 -1
- package/src/tools/search/search.js.map +0 -1
- package/src/tools/search/serper-scraper.js.map +0 -1
- package/src/tools/search/test.js.map +0 -1
- package/src/tools/search/tool.js.map +0 -1
- package/src/tools/search/types.js.map +0 -1
- package/src/tools/search/utils.js.map +0 -1
- package/src/types/graph.js.map +0 -1
- package/src/types/graph.test.js.map +0 -1
- package/src/types/index.js.map +0 -1
- package/src/types/llm.js.map +0 -1
- package/src/types/messages.js.map +0 -1
- package/src/types/run.js.map +0 -1
- package/src/types/stream.js.map +0 -1
- package/src/types/tools.js.map +0 -1
- package/src/utils/contextAnalytics.js.map +0 -1
- package/src/utils/contextAnalytics.test.js.map +0 -1
- package/src/utils/events.js.map +0 -1
- package/src/utils/graph.js.map +0 -1
- package/src/utils/handlers.js.map +0 -1
- package/src/utils/index.js.map +0 -1
- package/src/utils/llm.js.map +0 -1
- package/src/utils/llmConfig.js.map +0 -1
- package/src/utils/logging.js.map +0 -1
- package/src/utils/misc.js.map +0 -1
- package/src/utils/run.js.map +0 -1
- package/src/utils/schema.js.map +0 -1
- package/src/utils/title.js.map +0 -1
- package/src/utils/tokens.js.map +0 -1
- package/src/utils/toonFormat.js.map +0 -1
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for MCPCapabilityProvider.
|
|
3
|
+
*
|
|
4
|
+
* Uses mocked MCP SDK clients + transports so the provider can be exercised
|
|
5
|
+
* without spinning up a real server. The integration test file covers the
|
|
6
|
+
* live streamable-HTTP path.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
CAPABILITY_NAME_SEPARATOR,
|
|
11
|
+
formatCapabilityName,
|
|
12
|
+
flattenToolCallResponse,
|
|
13
|
+
MCPCapabilityProvider,
|
|
14
|
+
} from '../MCPCapabilityProvider';
|
|
15
|
+
import { CapabilityKind } from '../../types';
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// SDK mocks — jest.mock replaces the MCP SDK with lightweight stubs so we
|
|
19
|
+
// can script connect() / listTools() / callTool() behavior per test.
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
const clientConnect = jest.fn();
|
|
23
|
+
const clientListTools = jest.fn();
|
|
24
|
+
const clientCallTool = jest.fn();
|
|
25
|
+
const clientClose = jest.fn();
|
|
26
|
+
|
|
27
|
+
jest.mock('@modelcontextprotocol/sdk/client/index.js', () => ({
|
|
28
|
+
Client: jest.fn().mockImplementation(() => ({
|
|
29
|
+
connect: clientConnect,
|
|
30
|
+
listTools: clientListTools,
|
|
31
|
+
callTool: clientCallTool,
|
|
32
|
+
close: clientClose,
|
|
33
|
+
})),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
jest.mock('../transport', () => ({
|
|
37
|
+
createTransport: jest
|
|
38
|
+
.fn()
|
|
39
|
+
.mockImplementation(() => ({ __stub: 'transport' })),
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
const resetMocks = (): void => {
|
|
43
|
+
clientConnect.mockReset();
|
|
44
|
+
clientListTools.mockReset();
|
|
45
|
+
clientCallTool.mockReset();
|
|
46
|
+
clientClose.mockReset();
|
|
47
|
+
clientConnect.mockResolvedValue(undefined);
|
|
48
|
+
clientClose.mockResolvedValue(undefined);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
beforeEach(resetMocks);
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Helpers exported from the provider
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
describe('helpers', () => {
|
|
58
|
+
it('formatCapabilityName joins server + tool with separator', () => {
|
|
59
|
+
expect(formatCapabilityName('gh', 'list_repos')).toBe(
|
|
60
|
+
`gh${CAPABILITY_NAME_SEPARATOR}list_repos`
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('flattenToolCallResponse', () => {
|
|
65
|
+
it('concatenates text content parts', () => {
|
|
66
|
+
const r = flattenToolCallResponse({
|
|
67
|
+
content: [
|
|
68
|
+
{ type: 'text', text: 'hello' },
|
|
69
|
+
{ type: 'text', text: 'world' },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
expect(r.text).toBe('hello\n\nworld');
|
|
73
|
+
expect(r.isError).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('serializes non-text parts as JSON', () => {
|
|
77
|
+
const r = flattenToolCallResponse({
|
|
78
|
+
content: [
|
|
79
|
+
{ type: 'text', text: 'x' },
|
|
80
|
+
{ type: 'image', data: 'abc', mimeType: 'image/png' },
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
expect(r.text).toContain('x');
|
|
84
|
+
expect(r.text).toContain('"image"');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('reports isError when the SDK flags it', () => {
|
|
88
|
+
const r = flattenToolCallResponse({
|
|
89
|
+
content: [{ type: 'text', text: 'boom' }],
|
|
90
|
+
isError: true,
|
|
91
|
+
});
|
|
92
|
+
expect(r.isError).toBe(true);
|
|
93
|
+
expect(r.text).toBe('boom');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('handles empty / missing content', () => {
|
|
97
|
+
expect(flattenToolCallResponse({}).text).toBe('(empty response)');
|
|
98
|
+
expect(flattenToolCallResponse(null).text).toBe('(empty response)');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Construction
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
describe('MCPCapabilityProvider construction', () => {
|
|
108
|
+
it('rejects empty server map', () => {
|
|
109
|
+
expect(() => new MCPCapabilityProvider({ servers: {} })).toThrow(
|
|
110
|
+
/at least one server/
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('providerId includes sorted server names', () => {
|
|
115
|
+
const p = new MCPCapabilityProvider({
|
|
116
|
+
servers: {
|
|
117
|
+
zeta: { type: 'stdio', command: 'x' },
|
|
118
|
+
alpha: { type: 'stdio', command: 'y' },
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
expect(p.providerId).toBe('mcp:alpha,zeta');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('applies env-var defaults when clientInfo omitted', () => {
|
|
125
|
+
const prev = process.env.MCP_CLIENT_NAME;
|
|
126
|
+
process.env.MCP_CLIENT_NAME = 'from-env';
|
|
127
|
+
try {
|
|
128
|
+
const p = new MCPCapabilityProvider({
|
|
129
|
+
servers: { s: { type: 'stdio', command: 'echo' } },
|
|
130
|
+
});
|
|
131
|
+
// Exposing this via constructor args is sufficient — we tested the
|
|
132
|
+
// env read path in getMCPEnvDefaults indirectly.
|
|
133
|
+
expect(p.providerId).toContain('s');
|
|
134
|
+
} finally {
|
|
135
|
+
if (prev === undefined) delete process.env.MCP_CLIENT_NAME;
|
|
136
|
+
else process.env.MCP_CLIENT_NAME = prev;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// fetchManifest
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
describe('fetchManifest', () => {
|
|
146
|
+
it('returns capabilities for all tools from a single server', async () => {
|
|
147
|
+
clientListTools.mockResolvedValue({
|
|
148
|
+
tools: [
|
|
149
|
+
{ name: 'search', description: 'Search docs' },
|
|
150
|
+
{
|
|
151
|
+
name: 'get',
|
|
152
|
+
description: 'Get a doc',
|
|
153
|
+
inputSchema: { type: 'object' },
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const p = new MCPCapabilityProvider({
|
|
159
|
+
servers: { docs: { type: 'streamable-http', url: 'http://x' } },
|
|
160
|
+
});
|
|
161
|
+
const caps = await p.fetchManifest();
|
|
162
|
+
|
|
163
|
+
expect(caps).toHaveLength(2);
|
|
164
|
+
expect(caps[0].kind).toBe(CapabilityKind.MCP);
|
|
165
|
+
expect(caps[0].name).toBe(formatCapabilityName('docs', 'search'));
|
|
166
|
+
expect(caps[0].metadata.tags).toEqual(['docs']);
|
|
167
|
+
expect(caps[1].schema).toEqual({ type: 'object' });
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('merges tools from multiple servers', async () => {
|
|
171
|
+
clientListTools
|
|
172
|
+
.mockResolvedValueOnce({ tools: [{ name: 't1' }] })
|
|
173
|
+
.mockResolvedValueOnce({ tools: [{ name: 't2' }] });
|
|
174
|
+
|
|
175
|
+
const p = new MCPCapabilityProvider({
|
|
176
|
+
servers: {
|
|
177
|
+
a: { type: 'stdio', command: 'x' },
|
|
178
|
+
b: { type: 'stdio', command: 'y' },
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
const caps = await p.fetchManifest();
|
|
182
|
+
|
|
183
|
+
const names = caps.map((c) => c.name).sort();
|
|
184
|
+
expect(names).toEqual([
|
|
185
|
+
formatCapabilityName('a', 't1'),
|
|
186
|
+
formatCapabilityName('b', 't2'),
|
|
187
|
+
]);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('continues when one server fails', async () => {
|
|
191
|
+
clientListTools
|
|
192
|
+
.mockRejectedValueOnce(new Error('boom'))
|
|
193
|
+
.mockResolvedValueOnce({ tools: [{ name: 'ok' }] });
|
|
194
|
+
|
|
195
|
+
const p = new MCPCapabilityProvider({
|
|
196
|
+
servers: {
|
|
197
|
+
broken: { type: 'stdio', command: 'x' },
|
|
198
|
+
good: { type: 'stdio', command: 'y' },
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const caps = await p.fetchManifest();
|
|
202
|
+
|
|
203
|
+
expect(caps).toHaveLength(1);
|
|
204
|
+
expect(caps[0].name).toBe(formatCapabilityName('good', 'ok'));
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('short-circuits when filter excludes mcp kind', async () => {
|
|
208
|
+
const p = new MCPCapabilityProvider({
|
|
209
|
+
servers: { s: { type: 'stdio', command: 'x' } },
|
|
210
|
+
});
|
|
211
|
+
const caps = await p.fetchManifest({ kind: CapabilityKind.TOOL });
|
|
212
|
+
expect(caps).toEqual([]);
|
|
213
|
+
expect(clientConnect).not.toHaveBeenCalled();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('applies name filter', async () => {
|
|
217
|
+
clientListTools.mockResolvedValue({
|
|
218
|
+
tools: [{ name: 't1' }, { name: 't2' }, { name: 't3' }],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const p = new MCPCapabilityProvider({
|
|
222
|
+
servers: { s: { type: 'stdio', command: 'x' } },
|
|
223
|
+
});
|
|
224
|
+
const caps = await p.fetchManifest({
|
|
225
|
+
names: [formatCapabilityName('s', 't2')],
|
|
226
|
+
});
|
|
227
|
+
expect(caps).toHaveLength(1);
|
|
228
|
+
expect(caps[0].name).toContain('t2');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('calls getAuthHeaders once per server before connect', async () => {
|
|
232
|
+
clientListTools.mockResolvedValue({ tools: [] });
|
|
233
|
+
const getAuthHeaders = jest
|
|
234
|
+
.fn()
|
|
235
|
+
.mockResolvedValue({ Authorization: 'Bearer xyz' });
|
|
236
|
+
|
|
237
|
+
const p = new MCPCapabilityProvider({
|
|
238
|
+
servers: { s: { type: 'streamable-http', url: 'http://x' } },
|
|
239
|
+
getAuthHeaders,
|
|
240
|
+
});
|
|
241
|
+
await p.fetchManifest();
|
|
242
|
+
|
|
243
|
+
expect(getAuthHeaders).toHaveBeenCalledTimes(1);
|
|
244
|
+
expect(getAuthHeaders).toHaveBeenCalledWith('s');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('caches connections across fetchManifest calls', async () => {
|
|
248
|
+
clientListTools.mockResolvedValue({ tools: [{ name: 't' }] });
|
|
249
|
+
|
|
250
|
+
const p = new MCPCapabilityProvider({
|
|
251
|
+
servers: { s: { type: 'stdio', command: 'x' } },
|
|
252
|
+
});
|
|
253
|
+
await p.fetchManifest();
|
|
254
|
+
await p.fetchManifest();
|
|
255
|
+
|
|
256
|
+
expect(clientConnect).toHaveBeenCalledTimes(1);
|
|
257
|
+
expect(clientListTools).toHaveBeenCalledTimes(1);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// createRunnables
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
describe('createRunnables', () => {
|
|
266
|
+
it('builds one StructuredTool per MCP capability', async () => {
|
|
267
|
+
clientListTools.mockResolvedValue({
|
|
268
|
+
tools: [{ name: 'a' }, { name: 'b' }],
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const p = new MCPCapabilityProvider({
|
|
272
|
+
servers: { s: { type: 'stdio', command: 'x' } },
|
|
273
|
+
});
|
|
274
|
+
const caps = await p.fetchManifest();
|
|
275
|
+
const runnables = await p.createRunnables(caps, {});
|
|
276
|
+
|
|
277
|
+
expect(runnables).toHaveLength(2);
|
|
278
|
+
expect(runnables[0].name).toBe(formatCapabilityName('s', 'a'));
|
|
279
|
+
expect(runnables[1].name).toBe(formatCapabilityName('s', 'b'));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('skips non-MCP capabilities', async () => {
|
|
283
|
+
clientListTools.mockResolvedValue({ tools: [] });
|
|
284
|
+
const p = new MCPCapabilityProvider({
|
|
285
|
+
servers: { s: { type: 'stdio', command: 'x' } },
|
|
286
|
+
});
|
|
287
|
+
const runnables = await p.createRunnables(
|
|
288
|
+
[
|
|
289
|
+
{
|
|
290
|
+
kind: CapabilityKind.TOOL,
|
|
291
|
+
name: 'not_mine',
|
|
292
|
+
description: 'other',
|
|
293
|
+
authConfig: [],
|
|
294
|
+
metadata: {},
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
{}
|
|
298
|
+
);
|
|
299
|
+
expect(runnables).toHaveLength(0);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('proxy tool invokes client.callTool with the parsed tool name', async () => {
|
|
303
|
+
clientListTools.mockResolvedValue({ tools: [{ name: 'search' }] });
|
|
304
|
+
clientCallTool.mockResolvedValue({
|
|
305
|
+
content: [{ type: 'text', text: 'found 3 results' }],
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const p = new MCPCapabilityProvider({
|
|
309
|
+
servers: { docs: { type: 'streamable-http', url: 'http://x' } },
|
|
310
|
+
});
|
|
311
|
+
const caps = await p.fetchManifest();
|
|
312
|
+
const [runnable] = await p.createRunnables(caps, {});
|
|
313
|
+
|
|
314
|
+
const result = await runnable.invoke({ query: 'hello' });
|
|
315
|
+
expect(result).toBe('found 3 results');
|
|
316
|
+
expect(clientCallTool).toHaveBeenCalledWith({
|
|
317
|
+
name: 'search',
|
|
318
|
+
arguments: { query: 'hello' },
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('proxy tool throws when MCP returns isError=true', async () => {
|
|
323
|
+
clientListTools.mockResolvedValue({ tools: [{ name: 'fail' }] });
|
|
324
|
+
clientCallTool.mockResolvedValue({
|
|
325
|
+
content: [{ type: 'text', text: 'nope' }],
|
|
326
|
+
isError: true,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const p = new MCPCapabilityProvider({
|
|
330
|
+
servers: { s: { type: 'stdio', command: 'x' } },
|
|
331
|
+
});
|
|
332
|
+
const [runnable] = await p.createRunnables(await p.fetchManifest(), {});
|
|
333
|
+
await expect(runnable.invoke({})).rejects.toThrow(/s:fail.*nope/);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// close
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
|
|
341
|
+
describe('close', () => {
|
|
342
|
+
it('closes all open connections', async () => {
|
|
343
|
+
clientListTools.mockResolvedValue({ tools: [] });
|
|
344
|
+
|
|
345
|
+
const p = new MCPCapabilityProvider({
|
|
346
|
+
servers: {
|
|
347
|
+
a: { type: 'stdio', command: 'x' },
|
|
348
|
+
b: { type: 'stdio', command: 'y' },
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
await p.fetchManifest();
|
|
352
|
+
await p.close();
|
|
353
|
+
|
|
354
|
+
expect(clientClose).toHaveBeenCalledTimes(2);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('is idempotent if a close throws', async () => {
|
|
358
|
+
clientListTools.mockResolvedValue({ tools: [] });
|
|
359
|
+
clientClose.mockRejectedValueOnce(new Error('bad close'));
|
|
360
|
+
clientClose.mockResolvedValueOnce(undefined);
|
|
361
|
+
|
|
362
|
+
const p = new MCPCapabilityProvider({
|
|
363
|
+
servers: {
|
|
364
|
+
a: { type: 'stdio', command: 'x' },
|
|
365
|
+
b: { type: 'stdio', command: 'y' },
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
await p.fetchManifest();
|
|
369
|
+
await expect(p.close()).resolves.toBeUndefined();
|
|
370
|
+
});
|
|
371
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment-driven configuration defaults for the MCP capability provider.
|
|
3
|
+
*
|
|
4
|
+
* Hosts can override any of these via the provider config object. Env vars
|
|
5
|
+
* exist so deployments can brand or tune the library without code changes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MCPLogger } from './types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Read env values with fallbacks. Kept as a function (not a constant) so
|
|
12
|
+
* tests and hosts that mutate process.env after import get the current
|
|
13
|
+
* value on each call.
|
|
14
|
+
*/
|
|
15
|
+
export function getMCPEnvDefaults(): {
|
|
16
|
+
clientName: string;
|
|
17
|
+
clientVersion: string;
|
|
18
|
+
connectTimeoutMs: number;
|
|
19
|
+
requestTimeoutMs: number;
|
|
20
|
+
} {
|
|
21
|
+
return {
|
|
22
|
+
/**
|
|
23
|
+
* Identity sent to MCP servers on handshake. Precedence:
|
|
24
|
+
* 1. explicit `clientInfo.name` on the provider config
|
|
25
|
+
* 2. `MCP_CLIENT_NAME` env var
|
|
26
|
+
* 3. library fallback identity
|
|
27
|
+
*/
|
|
28
|
+
clientName: process.env.MCP_CLIENT_NAME ?? 'illuma-agents',
|
|
29
|
+
clientVersion: process.env.MCP_CLIENT_VERSION ?? '1.0.0',
|
|
30
|
+
connectTimeoutMs: Number(process.env.MCP_CONNECT_TIMEOUT_MS ?? 30_000),
|
|
31
|
+
requestTimeoutMs: Number(process.env.MCP_REQUEST_TIMEOUT_MS ?? 30_000),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Console-routed default logger. */
|
|
36
|
+
export const consoleLogger: MCPLogger = {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
debug: (...args) => console.debug('[mcp]', ...args),
|
|
39
|
+
// eslint-disable-next-line no-console
|
|
40
|
+
info: (...args) => console.info('[mcp]', ...args),
|
|
41
|
+
// eslint-disable-next-line no-console
|
|
42
|
+
warn: (...args) => console.warn('[mcp]', ...args),
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
error: (...args) => console.error('[mcp]', ...args),
|
|
45
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { MCPCapabilityProvider } from './MCPCapabilityProvider';
|
|
2
|
+
export {
|
|
3
|
+
CAPABILITY_NAME_SEPARATOR,
|
|
4
|
+
formatCapabilityName,
|
|
5
|
+
flattenToolCallResponse,
|
|
6
|
+
} from './MCPCapabilityProvider';
|
|
7
|
+
export { createTransport } from './transport';
|
|
8
|
+
export { consoleLogger as mcpConsoleLogger, getMCPEnvDefaults } from './config';
|
|
9
|
+
export type {
|
|
10
|
+
MCPLogger,
|
|
11
|
+
MCPProviderConfig,
|
|
12
|
+
MCPServerSpec,
|
|
13
|
+
MCPTransportKind,
|
|
14
|
+
StdioSpec,
|
|
15
|
+
SSESpec,
|
|
16
|
+
StreamableHTTPSpec,
|
|
17
|
+
WebSocketSpec,
|
|
18
|
+
MCPToolDescriptor,
|
|
19
|
+
MCPToolCallResult,
|
|
20
|
+
MCPStructuredTool,
|
|
21
|
+
} from './types';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport factory — turns an `MCPServerSpec` into a configured
|
|
3
|
+
* MCP SDK transport. One switch statement per kind.
|
|
4
|
+
*
|
|
5
|
+
* Each transport is constructed with the merged header map (spec headers
|
|
6
|
+
* plus any `getAuthHeaders`-resolved additions) for HTTP-based kinds.
|
|
7
|
+
* Stdio uses `env` + `command` + `args` directly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
11
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
12
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
13
|
+
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
|
|
14
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
15
|
+
|
|
16
|
+
import type { MCPServerSpec } from './types';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build a Transport from a spec + resolved auth headers.
|
|
20
|
+
*
|
|
21
|
+
* @param spec - the server spec (one of stdio / sse / streamable-http / websocket)
|
|
22
|
+
* @param authHeaders - headers merged over `spec.headers` at connect time
|
|
23
|
+
* @returns a configured Transport ready to be passed to `Client.connect()`
|
|
24
|
+
*/
|
|
25
|
+
export function createTransport(
|
|
26
|
+
spec: MCPServerSpec,
|
|
27
|
+
authHeaders?: Record<string, string>
|
|
28
|
+
): Transport {
|
|
29
|
+
switch (spec.type) {
|
|
30
|
+
case 'stdio':
|
|
31
|
+
return new StdioClientTransport({
|
|
32
|
+
command: spec.command,
|
|
33
|
+
args: spec.args,
|
|
34
|
+
env: spec.env,
|
|
35
|
+
cwd: spec.cwd,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
case 'sse': {
|
|
39
|
+
const headers = mergeHeaders(spec.headers, authHeaders);
|
|
40
|
+
return new SSEClientTransport(new URL(spec.url), {
|
|
41
|
+
requestInit: headers ? { headers } : undefined,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
case 'streamable-http': {
|
|
46
|
+
const headers = mergeHeaders(spec.headers, authHeaders);
|
|
47
|
+
return new StreamableHTTPClientTransport(new URL(spec.url), {
|
|
48
|
+
requestInit: headers ? { headers } : undefined,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
case 'websocket':
|
|
53
|
+
// Node's WebSocket doesn't natively accept arbitrary headers; the MCP
|
|
54
|
+
// SDK's transport ignores them. We keep the param for future support
|
|
55
|
+
// but log a warning elsewhere if headers are provided with websocket.
|
|
56
|
+
return new WebSocketClientTransport(new URL(spec.url));
|
|
57
|
+
|
|
58
|
+
default: {
|
|
59
|
+
// Exhaustiveness check — TypeScript errors here if a new spec kind
|
|
60
|
+
// is added without being handled in the switch.
|
|
61
|
+
const _exhaustive: never = spec;
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Unknown MCP transport spec: ${JSON.stringify(_exhaustive)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Merge headers from two sources, with the second taking precedence. */
|
|
70
|
+
function mergeHeaders(
|
|
71
|
+
base: Record<string, string> | undefined,
|
|
72
|
+
overrides: Record<string, string> | undefined
|
|
73
|
+
): Record<string, string> | undefined {
|
|
74
|
+
if (!base && !overrides) return undefined;
|
|
75
|
+
return { ...(base ?? {}), ...(overrides ?? {}) };
|
|
76
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the MCP capability provider.
|
|
3
|
+
*
|
|
4
|
+
* Design notes:
|
|
5
|
+
* - `MCPServerSpec` is a unified, minimal shape describing an MCP server
|
|
6
|
+
* the library can connect to. All config sources (programmatic,
|
|
7
|
+
* YAML-derived, marketplace-derived) normalize to this before reaching
|
|
8
|
+
* the provider.
|
|
9
|
+
* - Auth headers are provided per-request via `getAuthHeaders` on the
|
|
10
|
+
* provider config. The library never orchestrates OAuth — the host
|
|
11
|
+
* resolves tokens however it sees fit and forwards the headers map.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { StructuredToolInterface } from '@langchain/core/tools';
|
|
15
|
+
|
|
16
|
+
/** Transport kinds supported by the MCP SDK. */
|
|
17
|
+
export type MCPTransportKind =
|
|
18
|
+
| 'stdio'
|
|
19
|
+
| 'sse'
|
|
20
|
+
| 'streamable-http'
|
|
21
|
+
| 'websocket';
|
|
22
|
+
|
|
23
|
+
/** Stdio-transport spec — spawn a local subprocess. */
|
|
24
|
+
export interface StdioSpec {
|
|
25
|
+
type: 'stdio';
|
|
26
|
+
command: string;
|
|
27
|
+
args?: string[];
|
|
28
|
+
env?: Record<string, string>;
|
|
29
|
+
/** Working directory for the spawned process. */
|
|
30
|
+
cwd?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** SSE-transport spec (legacy EventSource-based transport). */
|
|
34
|
+
export interface SSESpec {
|
|
35
|
+
type: 'sse';
|
|
36
|
+
url: string;
|
|
37
|
+
/** Static headers (e.g., API key). Forwarded on each HTTP request. */
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Streamable HTTP spec (the modern MCP HTTP transport). */
|
|
42
|
+
export interface StreamableHTTPSpec {
|
|
43
|
+
type: 'streamable-http';
|
|
44
|
+
url: string;
|
|
45
|
+
headers?: Record<string, string>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** WebSocket transport spec. */
|
|
49
|
+
export interface WebSocketSpec {
|
|
50
|
+
type: 'websocket';
|
|
51
|
+
url: string;
|
|
52
|
+
headers?: Record<string, string>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Discriminated union of all supported transports. */
|
|
56
|
+
export type MCPServerSpec =
|
|
57
|
+
| StdioSpec
|
|
58
|
+
| SSESpec
|
|
59
|
+
| StreamableHTTPSpec
|
|
60
|
+
| WebSocketSpec;
|
|
61
|
+
|
|
62
|
+
/** Configuration for the MCP capability provider. */
|
|
63
|
+
export interface MCPProviderConfig {
|
|
64
|
+
/**
|
|
65
|
+
* Map of serverName → spec. Server names are stable identifiers used
|
|
66
|
+
* downstream as the capability name prefix and as keys for
|
|
67
|
+
* `getAuthHeaders` lookups.
|
|
68
|
+
*
|
|
69
|
+
* The host is responsible for normalizing configs from multiple sources
|
|
70
|
+
* (local database, YAML, marketplace catalog) into this unified shape.
|
|
71
|
+
*/
|
|
72
|
+
servers: Record<string, MCPServerSpec>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Optional callback to resolve auth headers for a given server at
|
|
76
|
+
* connect-time. Returns a header map (e.g., `{ Authorization: "Bearer ..." }`)
|
|
77
|
+
* that is merged into the spec's base headers.
|
|
78
|
+
*
|
|
79
|
+
* The library never stores tokens or orchestrates OAuth — the host
|
|
80
|
+
* owns that layer and surfaces the resolved value here.
|
|
81
|
+
*/
|
|
82
|
+
getAuthHeaders?: (
|
|
83
|
+
serverName: string
|
|
84
|
+
) =>
|
|
85
|
+
| Promise<Record<string, string> | undefined>
|
|
86
|
+
| Record<string, string>
|
|
87
|
+
| undefined;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Client identity sent to MCP servers on handshake. Defaults to
|
|
91
|
+
* env vars (`MCP_CLIENT_NAME` / `MCP_CLIENT_VERSION`) or a library
|
|
92
|
+
* fallback if not set. Hosts typically override with their app identity.
|
|
93
|
+
*/
|
|
94
|
+
clientInfo?: { name: string; version: string };
|
|
95
|
+
|
|
96
|
+
/** Connection timeout in ms. Defaults to 30_000. */
|
|
97
|
+
connectTimeoutMs?: number;
|
|
98
|
+
|
|
99
|
+
/** Tool-list / tool-call timeout in ms. Defaults to 30_000. */
|
|
100
|
+
requestTimeoutMs?: number;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Optional logger. When omitted, falls back to a console-based default.
|
|
104
|
+
* Implementations are expected to match the `MCPLogger` minimal shape.
|
|
105
|
+
*/
|
|
106
|
+
logger?: MCPLogger;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Minimal logger interface — matches pino-style signatures. */
|
|
110
|
+
export interface MCPLogger {
|
|
111
|
+
debug: (...args: unknown[]) => void;
|
|
112
|
+
info: (...args: unknown[]) => void;
|
|
113
|
+
warn: (...args: unknown[]) => void;
|
|
114
|
+
error: (...args: unknown[]) => void;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Shape returned by the MCP SDK's `listTools()`. Keeping our own narrow
|
|
119
|
+
* alias so we don't leak the full SDK types through the provider's public
|
|
120
|
+
* surface.
|
|
121
|
+
*/
|
|
122
|
+
export interface MCPToolDescriptor {
|
|
123
|
+
name: string;
|
|
124
|
+
description?: string;
|
|
125
|
+
inputSchema?: unknown;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Result of a tool invocation, flattened from the SDK response. */
|
|
129
|
+
export interface MCPToolCallResult {
|
|
130
|
+
/** Plain-text content joined from the MCP response's content array. */
|
|
131
|
+
text: string;
|
|
132
|
+
/** Raw content array from the SDK response, for advanced consumers. */
|
|
133
|
+
rawContent: unknown;
|
|
134
|
+
/** True when the MCP server flagged the result as an error. */
|
|
135
|
+
isError: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** What the provider returns from createRunnables (re-exported for clarity). */
|
|
139
|
+
export type MCPStructuredTool = StructuredToolInterface;
|