@ankhorage/flow 0.0.0-stage → 0.5.0
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/CHANGELOG.md +40 -0
- package/LICENSE +21 -0
- package/README.md +19 -2
- package/dist/ai.d.ts +7 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +7 -0
- package/dist/ai.js.map +1 -0
- package/dist/execution.d.ts +5 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +5 -0
- package/dist/execution.js.map +1 -0
- package/dist/features/execution/adapters/FileFlowPersistenceAdapter.d.ts +4 -0
- package/dist/features/execution/adapters/FileFlowPersistenceAdapter.d.ts.map +1 -0
- package/dist/features/execution/adapters/FileFlowPersistenceAdapter.js +219 -0
- package/dist/features/execution/adapters/FileFlowPersistenceAdapter.js.map +1 -0
- package/dist/features/execution/application/cancelDurableRunAsync.d.ts +8 -0
- package/dist/features/execution/application/cancelDurableRunAsync.d.ts.map +1 -0
- package/dist/features/execution/application/cancelDurableRunAsync.js +22 -0
- package/dist/features/execution/application/cancelDurableRunAsync.js.map +1 -0
- package/dist/features/execution/application/createDurableRunAsync.d.ts +12 -0
- package/dist/features/execution/application/createDurableRunAsync.d.ts.map +1 -0
- package/dist/features/execution/application/createDurableRunAsync.js +8 -0
- package/dist/features/execution/application/createDurableRunAsync.js.map +1 -0
- package/dist/features/execution/application/executeFlowAsync.d.ts +4 -0
- package/dist/features/execution/application/executeFlowAsync.d.ts.map +1 -0
- package/dist/features/execution/application/executeFlowAsync.js +211 -0
- package/dist/features/execution/application/executeFlowAsync.js.map +1 -0
- package/dist/features/execution/application/runDurableWorkerAsync.d.ts +52 -0
- package/dist/features/execution/application/runDurableWorkerAsync.d.ts.map +1 -0
- package/dist/features/execution/application/runDurableWorkerAsync.js +195 -0
- package/dist/features/execution/application/runDurableWorkerAsync.js.map +1 -0
- package/dist/features/execution/domain/assertFlowGraphSchemasSupported.d.ts +5 -0
- package/dist/features/execution/domain/assertFlowGraphSchemasSupported.d.ts.map +1 -0
- package/dist/features/execution/domain/assertFlowGraphSchemasSupported.js +11 -0
- package/dist/features/execution/domain/assertFlowGraphSchemasSupported.js.map +1 -0
- package/dist/features/execution/domain/calculateRetryDueAt.d.ts +4 -0
- package/dist/features/execution/domain/calculateRetryDueAt.d.ts.map +1 -0
- package/dist/features/execution/domain/calculateRetryDueAt.js +14 -0
- package/dist/features/execution/domain/calculateRetryDueAt.js.map +1 -0
- package/dist/features/execution/domain/compileFlowGraph.d.ts +5 -0
- package/dist/features/execution/domain/compileFlowGraph.d.ts.map +1 -0
- package/dist/features/execution/domain/compileFlowGraph.js +76 -0
- package/dist/features/execution/domain/compileFlowGraph.js.map +1 -0
- package/dist/features/execution/domain/createDefaultNodeRegistry.d.ts +4 -0
- package/dist/features/execution/domain/createDefaultNodeRegistry.d.ts.map +1 -0
- package/dist/features/execution/domain/createDefaultNodeRegistry.js +15 -0
- package/dist/features/execution/domain/createDefaultNodeRegistry.js.map +1 -0
- package/dist/features/execution/domain/createDurableRun.d.ts +7 -0
- package/dist/features/execution/domain/createDurableRun.d.ts.map +1 -0
- package/dist/features/execution/domain/createDurableRun.js +10 -0
- package/dist/features/execution/domain/createDurableRun.js.map +1 -0
- package/dist/features/execution/domain/createRetryWork.d.ts +4 -0
- package/dist/features/execution/domain/createRetryWork.d.ts.map +1 -0
- package/dist/features/execution/domain/createRetryWork.js +17 -0
- package/dist/features/execution/domain/createRetryWork.js.map +1 -0
- package/dist/features/execution/domain/isCronDueAt.d.ts +7 -0
- package/dist/features/execution/domain/isCronDueAt.d.ts.map +1 -0
- package/dist/features/execution/domain/isCronDueAt.js +114 -0
- package/dist/features/execution/domain/isCronDueAt.js.map +1 -0
- package/dist/features/execution/domain/validateFlowDataSchema.d.ts +6 -0
- package/dist/features/execution/domain/validateFlowDataSchema.d.ts.map +1 -0
- package/dist/features/execution/domain/validateFlowDataSchema.js +144 -0
- package/dist/features/execution/domain/validateFlowDataSchema.js.map +1 -0
- package/dist/features/execution/ports/FlowPersistencePort.d.ts +34 -0
- package/dist/features/execution/ports/FlowPersistencePort.d.ts.map +1 -0
- package/dist/features/execution/ports/FlowPersistencePort.js +2 -0
- package/dist/features/execution/ports/FlowPersistencePort.js.map +1 -0
- package/dist/features/mcp/McpToolError.d.ts +6 -0
- package/dist/features/mcp/McpToolError.d.ts.map +1 -0
- package/dist/features/mcp/McpToolError.js +10 -0
- package/dist/features/mcp/McpToolError.js.map +1 -0
- package/dist/features/mcp/createMcpRemoteTransport.d.ts +8 -0
- package/dist/features/mcp/createMcpRemoteTransport.d.ts.map +1 -0
- package/dist/features/mcp/createMcpRemoteTransport.js +23 -0
- package/dist/features/mcp/createMcpRemoteTransport.js.map +1 -0
- package/dist/features/mcp/createMcpToolRegistry.d.ts +10 -0
- package/dist/features/mcp/createMcpToolRegistry.d.ts.map +1 -0
- package/dist/features/mcp/createMcpToolRegistry.js +185 -0
- package/dist/features/mcp/createMcpToolRegistry.js.map +1 -0
- package/dist/features/mcp/loadMcpSdkAsync.d.ts +12 -0
- package/dist/features/mcp/loadMcpSdkAsync.d.ts.map +1 -0
- package/dist/features/mcp/loadMcpSdkAsync.js +20 -0
- package/dist/features/mcp/loadMcpSdkAsync.js.map +1 -0
- package/dist/features/mcp/prepareMcpProviders.d.ts +4 -0
- package/dist/features/mcp/prepareMcpProviders.d.ts.map +1 -0
- package/dist/features/mcp/prepareMcpProviders.js +97 -0
- package/dist/features/mcp/prepareMcpProviders.js.map +1 -0
- package/dist/features/mcp/validateMcpSchema.d.ts +6 -0
- package/dist/features/mcp/validateMcpSchema.d.ts.map +1 -0
- package/dist/features/mcp/validateMcpSchema.js +220 -0
- package/dist/features/mcp/validateMcpSchema.js.map +1 -0
- package/dist/features/security/FlowSecurityError.d.ts +6 -0
- package/dist/features/security/FlowSecurityError.d.ts.map +1 -0
- package/dist/features/security/FlowSecurityError.js +10 -0
- package/dist/features/security/FlowSecurityError.js.map +1 -0
- package/dist/features/security/application/createFlowSecurityPort.d.ts +12 -0
- package/dist/features/security/application/createFlowSecurityPort.d.ts.map +1 -0
- package/dist/features/security/application/createFlowSecurityPort.js +208 -0
- package/dist/features/security/application/createFlowSecurityPort.js.map +1 -0
- package/dist/features/security/domain/validateFlowSecurityTarget.d.ts +4 -0
- package/dist/features/security/domain/validateFlowSecurityTarget.d.ts.map +1 -0
- package/dist/features/security/domain/validateFlowSecurityTarget.js +57 -0
- package/dist/features/security/domain/validateFlowSecurityTarget.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +3 -0
- package/dist/mcp.js.map +1 -0
- package/dist/social.d.ts +7 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +7 -0
- package/dist/social.js.map +1 -0
- package/dist/types/durableExecution.d.ts +55 -0
- package/dist/types/durableExecution.d.ts.map +1 -0
- package/dist/types/durableExecution.js +2 -0
- package/dist/types/durableExecution.js.map +1 -0
- package/dist/types/execution.d.ts +33 -0
- package/dist/types/execution.d.ts.map +1 -0
- package/dist/types/execution.js +2 -0
- package/dist/types/execution.js.map +1 -0
- package/dist/types/mcp.d.ts +61 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +2 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/types/security.d.ts +123 -0
- package/dist/types/security.d.ts.map +1 -0
- package/dist/types/security.js +2 -0
- package/dist/types/security.js.map +1 -0
- package/package.json +90 -4
- package/src/ai.ts +6 -0
- package/src/execution.ts +9 -0
- package/src/features/execution/adapters/FileFlowPersistenceAdapter.ts +333 -0
- package/src/features/execution/application/cancelDurableRunAsync.ts +28 -0
- package/src/features/execution/application/createDurableRunAsync.ts +14 -0
- package/src/features/execution/application/executeFlowAsync.ts +338 -0
- package/src/features/execution/application/runDurableWorkerAsync.ts +337 -0
- package/src/features/execution/domain/assertFlowGraphSchemasSupported.ts +17 -0
- package/src/features/execution/domain/calculateRetryDueAt.ts +21 -0
- package/src/features/execution/domain/compileFlowGraph.ts +97 -0
- package/src/features/execution/domain/createDefaultNodeRegistry.ts +26 -0
- package/src/features/execution/domain/createDurableRun.ts +14 -0
- package/src/features/execution/domain/createRetryWork.ts +22 -0
- package/src/features/execution/domain/isCronDueAt.ts +131 -0
- package/src/features/execution/domain/validateFlowDataSchema.ts +218 -0
- package/src/features/execution/ports/FlowPersistencePort.ts +38 -0
- package/src/features/mcp/McpToolError.ts +10 -0
- package/src/features/mcp/createMcpRemoteTransport.ts +34 -0
- package/src/features/mcp/createMcpToolRegistry.ts +324 -0
- package/src/features/mcp/loadMcpSdkAsync.ts +32 -0
- package/src/features/mcp/prepareMcpProviders.ts +135 -0
- package/src/features/mcp/validateMcpSchema.ts +269 -0
- package/src/features/security/FlowSecurityError.ts +17 -0
- package/src/features/security/application/createFlowSecurityPort.ts +339 -0
- package/src/features/security/domain/validateFlowSecurityTarget.ts +73 -0
- package/src/index.ts +49 -0
- package/src/mcp.ts +11 -0
- package/src/social.ts +6 -0
- package/src/types/durableExecution.ts +61 -0
- package/src/types/execution.ts +41 -0
- package/src/types/mcp.ts +70 -0
- package/src/types/security.ts +148 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpToolError.js","sourceRoot":"","sources":["../../../src/features/mcp/McpToolError.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,MAAM,OAAO,YAAa,SAAQ,KAAK;IAE1B;IADX,YACW,IAAY,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,SAAI,GAAJ,IAAI,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF","sourcesContent":["/*** Represents a stable, redaction-safe MCP failure. */\nexport class McpToolError extends Error {\n constructor(\n readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = 'McpToolError';\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
2
|
+
import type { McpProviderDefinition } from '../../types/mcp.js';
|
|
3
|
+
import type { FlowOutboundTransportPort } from '../../types/security.js';
|
|
4
|
+
/*** Opens remote MCP only through the host-owned transport that controls every peer connection. */
|
|
5
|
+
export declare const createMcpRemoteTransportAsync: (provider: Extract<McpProviderDefinition, {
|
|
6
|
+
readonly transport: "remote";
|
|
7
|
+
}>, outboundTransport: FlowOutboundTransportPort | undefined) => Promise<StreamableHTTPClientTransport>;
|
|
8
|
+
//# sourceMappingURL=createMcpRemoteTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMcpRemoteTransport.d.ts","sourceRoot":"","sources":["../../../src/features/mcp/createMcpRemoteTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAExG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAIzE,mGAAmG;AACnG,eAAO,MAAM,6BAA6B,GACxC,UAAU,OAAO,CAAC,qBAAqB,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAA;CAAE,CAAC,EAC1E,mBAAmB,yBAAyB,GAAG,SAAS,KACvD,OAAO,CAAC,6BAA6B,CAsBvC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { loadMcpSdkAsync } from './loadMcpSdkAsync.js';
|
|
2
|
+
import { McpToolError } from './McpToolError.js';
|
|
3
|
+
/*** Opens remote MCP only through the host-owned transport that controls every peer connection. */
|
|
4
|
+
export const createMcpRemoteTransportAsync = async (provider, outboundTransport) => {
|
|
5
|
+
if (!outboundTransport)
|
|
6
|
+
throw new McpToolError('remote_security_boundary_unavailable', 'Remote MCP requires a host-owned Flow security transport boundary.');
|
|
7
|
+
const { StreamableHTTPClientTransport } = await loadMcpSdkAsync();
|
|
8
|
+
return new StreamableHTTPClientTransport(new URL(provider.endpoint), {
|
|
9
|
+
fetch: async (url, init) => outboundTransport.fetchAsync({
|
|
10
|
+
allowedHosts: provider.allowedHosts,
|
|
11
|
+
...(init === undefined ? {} : { init }),
|
|
12
|
+
url: url.toString(),
|
|
13
|
+
}),
|
|
14
|
+
redirectPolicy: 'same-origin',
|
|
15
|
+
reconnectionOptions: {
|
|
16
|
+
initialReconnectionDelay: 100,
|
|
17
|
+
maxReconnectionDelay: 1_000,
|
|
18
|
+
reconnectionDelayGrowFactor: 1,
|
|
19
|
+
maxRetries: 0,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=createMcpRemoteTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMcpRemoteTransport.js","sourceRoot":"","sources":["../../../src/features/mcp/createMcpRemoteTransport.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,mGAAmG;AACnG,MAAM,CAAC,MAAM,6BAA6B,GAAG,KAAK,EAChD,QAA0E,EAC1E,iBAAwD,EAChB,EAAE;IAC1C,IAAI,CAAC,iBAAiB;QACpB,MAAM,IAAI,YAAY,CACpB,sCAAsC,EACtC,oEAAoE,CACrE,CAAC;IACJ,MAAM,EAAE,6BAA6B,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;IAClE,OAAO,IAAI,6BAA6B,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QACnE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CACzB,iBAAiB,CAAC,UAAU,CAAC;YAC3B,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;YACvC,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE;SACpB,CAAC;QACJ,cAAc,EAAE,aAAa;QAC7B,mBAAmB,EAAE;YACnB,wBAAwB,EAAE,GAAG;YAC7B,oBAAoB,EAAE,KAAK;YAC3B,2BAA2B,EAAE,CAAC;YAC9B,UAAU,EAAE,CAAC;SACd;KACF,CAAC,CAAC;AACL,CAAC,CAAC","sourcesContent":["import type { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\n\nimport type { McpProviderDefinition } from '../../types/mcp.js';\nimport type { FlowOutboundTransportPort } from '../../types/security.js';\nimport { loadMcpSdkAsync } from './loadMcpSdkAsync.js';\nimport { McpToolError } from './McpToolError.js';\n\n/*** Opens remote MCP only through the host-owned transport that controls every peer connection. */\nexport const createMcpRemoteTransportAsync = async (\n provider: Extract<McpProviderDefinition, { readonly transport: 'remote' }>,\n outboundTransport: FlowOutboundTransportPort | undefined,\n): Promise<StreamableHTTPClientTransport> => {\n if (!outboundTransport)\n throw new McpToolError(\n 'remote_security_boundary_unavailable',\n 'Remote MCP requires a host-owned Flow security transport boundary.',\n );\n const { StreamableHTTPClientTransport } = await loadMcpSdkAsync();\n return new StreamableHTTPClientTransport(new URL(provider.endpoint), {\n fetch: async (url, init) =>\n outboundTransport.fetchAsync({\n allowedHosts: provider.allowedHosts,\n ...(init === undefined ? {} : { init }),\n url: url.toString(),\n }),\n redirectPolicy: 'same-origin',\n reconnectionOptions: {\n initialReconnectionDelay: 100,\n maxReconnectionDelay: 1_000,\n reconnectionDelayGrowFactor: 1,\n maxRetries: 0,\n },\n });\n};\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpProviderDefinition, McpToolRegistry, McpTrustedStdioProvider } from '../../types/mcp.js';
|
|
2
|
+
import type { FlowOutboundTransportPort, FlowSecurityPort } from '../../types/security.js';
|
|
3
|
+
/*** Creates a snapshotting, allowlist-only MCP registry. Real invocation fails closed until Issue #4 releases its approval boundary. */
|
|
4
|
+
export declare const createMcpToolRegistry: (input: {
|
|
5
|
+
readonly providers: readonly McpProviderDefinition[];
|
|
6
|
+
readonly outboundTransport?: FlowOutboundTransportPort;
|
|
7
|
+
readonly security?: FlowSecurityPort;
|
|
8
|
+
readonly trustedStdioProviders?: Readonly<Record<string, McpTrustedStdioProvider>>;
|
|
9
|
+
}) => McpToolRegistry;
|
|
10
|
+
//# sourceMappingURL=createMcpToolRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMcpToolRegistry.d.ts","sourceRoot":"","sources":["../../../src/features/mcp/createMcpToolRegistry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,qBAAqB,EAIrB,eAAe,EAEf,uBAAuB,EACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAW3F,wIAAwI;AACxI,eAAO,MAAM,qBAAqB,GAAI,OAAO;IAC3C,QAAQ,CAAC,SAAS,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACrD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,yBAAyB,CAAC;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,CAAC;CACpF,KAAG,eAwCH,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { createMcpRemoteTransportAsync } from './createMcpRemoteTransport.js';
|
|
2
|
+
import { loadMcpSdkAsync } from './loadMcpSdkAsync.js';
|
|
3
|
+
import { McpToolError } from './McpToolError.js';
|
|
4
|
+
import { prepareMcpProviders } from './prepareMcpProviders.js';
|
|
5
|
+
import { validateMcpSchema } from './validateMcpSchema.js';
|
|
6
|
+
const MAX_PAGES = 20;
|
|
7
|
+
const MAX_TOOLS = 100;
|
|
8
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
9
|
+
/*** Creates a snapshotting, allowlist-only MCP registry. Real invocation fails closed until Issue #4 releases its approval boundary. */
|
|
10
|
+
export const createMcpToolRegistry = (input) => {
|
|
11
|
+
const providers = prepareMcpProviders(input.providers);
|
|
12
|
+
const trusted = new Map(Object.entries(input.trustedStdioProviders ?? {}).map(([id, value]) => [
|
|
13
|
+
id,
|
|
14
|
+
{
|
|
15
|
+
command: value.command,
|
|
16
|
+
...(value.args ? { args: [...value.args] } : {}),
|
|
17
|
+
},
|
|
18
|
+
]));
|
|
19
|
+
const snapshot = new Map();
|
|
20
|
+
return {
|
|
21
|
+
discoverAsync: async (signal) => {
|
|
22
|
+
const groups = await Promise.all([...providers.values()].map((provider) => discoverAsync(provider, trusted, input.outboundTransport, signal)));
|
|
23
|
+
const tools = groups.flat();
|
|
24
|
+
const entries = tools.map((tool) => [key(tool.providerId, tool.name), tool]);
|
|
25
|
+
if (new Set(entries.map(([identifier]) => identifier)).size !== entries.length)
|
|
26
|
+
throw new McpToolError('duplicate_tool', 'MCP discovery returned duplicate provider tool identifiers.');
|
|
27
|
+
snapshot.clear();
|
|
28
|
+
entries.forEach(([identifier, tool]) => snapshot.set(identifier, tool));
|
|
29
|
+
return tools;
|
|
30
|
+
},
|
|
31
|
+
invokeAsync: (invocation) => invokeAsync(providers, trusted, snapshot, input.security, input.outboundTransport, invocation),
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/*** Discovers bounded tool metadata from one trusted provider connection. */
|
|
35
|
+
const discoverAsync = async (provider, trusted, outboundTransport, signal) => {
|
|
36
|
+
const connection = await connectAsync(provider, trusted, outboundTransport, signal);
|
|
37
|
+
try {
|
|
38
|
+
const tools = await listToolsAsync(connection.client, timeoutOf(provider), signal);
|
|
39
|
+
return tools
|
|
40
|
+
.filter((tool) => permissionFor(provider, tool.name))
|
|
41
|
+
.map((tool) => toMetadata(provider, tool));
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
await connection.closeAsync().catch(() => undefined);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/*** Validates, approves, and invokes one discovered tool through the server-only security port. */
|
|
48
|
+
const invokeAsync = async (providers, trusted, snapshot, security, outboundTransport, invocation) => {
|
|
49
|
+
const provider = providers.get(invocation.providerId);
|
|
50
|
+
const tool = snapshot.get(key(invocation.providerId, invocation.toolName));
|
|
51
|
+
if (!provider || !tool || !permissionFor(provider, invocation.toolName))
|
|
52
|
+
throw new McpToolError('tool_not_discovered', 'The requested MCP tool is not allowlisted.');
|
|
53
|
+
if (tool.schemaFingerprint !== invocation.schemaFingerprint)
|
|
54
|
+
throw new McpToolError('schema_changed', 'The MCP tool schema changed; rediscover it before invoking.');
|
|
55
|
+
const permission = permissionFor(provider, invocation.toolName);
|
|
56
|
+
if (!permission || !sameScopes(permission.grantedScopes, invocation.scopes))
|
|
57
|
+
throw new McpToolError('scope_denied', 'MCP tool invocation scopes are not explicitly granted.');
|
|
58
|
+
if (!validateMcpSchema(tool.inputSchema).matches(invocation.arguments))
|
|
59
|
+
throw new McpToolError('invalid_arguments', 'MCP tool arguments do not satisfy the discovered schema.');
|
|
60
|
+
if (!security)
|
|
61
|
+
throw new McpToolError('authorization_boundary_unavailable', 'MCP tool execution is unavailable until Flow security approvals are installed.');
|
|
62
|
+
const permit = await security.authorizeAsync({
|
|
63
|
+
...invocation.security,
|
|
64
|
+
capability: `mcp:${provider.id}:${tool.name}`,
|
|
65
|
+
...(provider.transport === 'remote'
|
|
66
|
+
? { target: { allowedHosts: provider.allowedHosts, url: provider.endpoint } }
|
|
67
|
+
: {}),
|
|
68
|
+
});
|
|
69
|
+
return permit.executeAsync(async () => callToolAsync(provider, trusted, outboundTransport, invocation));
|
|
70
|
+
};
|
|
71
|
+
/*** Opens a bounded provider connection only after the approval permit was issued. */
|
|
72
|
+
const callToolAsync = async (provider, trusted, outboundTransport, invocation) => {
|
|
73
|
+
const connection = await connectAsync(provider, trusted, outboundTransport, invocation.signal);
|
|
74
|
+
try {
|
|
75
|
+
const result = await boundedAsync((signal) => connection.client.callTool({ name: invocation.toolName, arguments: invocation.arguments }, undefined, { signal }), timeoutOf(provider), invocation.signal);
|
|
76
|
+
if (!Array.isArray(result.content))
|
|
77
|
+
throw new McpToolError('invalid_execution_response', 'The MCP provider returned an invalid tool result.');
|
|
78
|
+
return {
|
|
79
|
+
content: result.content,
|
|
80
|
+
...(isRecord(result.structuredContent)
|
|
81
|
+
? { structuredContent: result.structuredContent }
|
|
82
|
+
: {}),
|
|
83
|
+
isError: result.isError === true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
await connection.closeAsync().catch(() => undefined);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
/*** Connects with a bounded timeout and an abort signal propagated to the SDK transport. */
|
|
91
|
+
const connectAsync = async (provider, trusted, outboundTransport, signal) => {
|
|
92
|
+
const { Client, StdioClientTransport } = await loadMcpSdkAsync();
|
|
93
|
+
const client = new Client({ name: '@ankhorage/flow', version: '0.1.0' });
|
|
94
|
+
const transport = provider.transport === 'stdio'
|
|
95
|
+
? stdioTransport(provider, trusted, StdioClientTransport)
|
|
96
|
+
: await createMcpRemoteTransportAsync(provider, outboundTransport);
|
|
97
|
+
await boundedAsync((timeoutSignal) => client.connect(transport, { signal: timeoutSignal, timeout: timeoutOf(provider) }), timeoutOf(provider), signal);
|
|
98
|
+
return { client, closeAsync: () => transport.close() };
|
|
99
|
+
};
|
|
100
|
+
/*** Resolves executable details exclusively from trusted host configuration. */
|
|
101
|
+
const stdioTransport = (provider, trusted, StdioClientTransport) => {
|
|
102
|
+
const host = trusted.get(provider.id);
|
|
103
|
+
if (!host)
|
|
104
|
+
throw new McpToolError('untrusted_stdio_provider', 'STDIO MCP providers require trusted host command configuration.');
|
|
105
|
+
return new StdioClientTransport({
|
|
106
|
+
command: host.command,
|
|
107
|
+
args: host.args ? [...host.args] : undefined,
|
|
108
|
+
stderr: 'pipe',
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
/*** Lists a bounded, non-cyclic sequence of paginated tools. */
|
|
112
|
+
const listToolsAsync = async (client, timeoutMs, signal) => {
|
|
113
|
+
const tools = [];
|
|
114
|
+
const cursors = new Set();
|
|
115
|
+
let cursor;
|
|
116
|
+
do {
|
|
117
|
+
if (cursors.size >= MAX_PAGES)
|
|
118
|
+
throw new McpToolError('pagination_limit', 'MCP tool discovery exceeded the page limit.');
|
|
119
|
+
if (cursor) {
|
|
120
|
+
if (cursors.has(cursor))
|
|
121
|
+
throw new McpToolError('pagination_cycle', 'MCP tool discovery returned a repeated cursor.');
|
|
122
|
+
cursors.add(cursor);
|
|
123
|
+
}
|
|
124
|
+
const page = await boundedAsync((timeoutSignal) => client.listTools(cursor ? { cursor } : undefined, { signal: timeoutSignal }), timeoutMs, signal);
|
|
125
|
+
if (!Array.isArray(page.tools))
|
|
126
|
+
throw new McpToolError('invalid_discovery_response', 'The MCP provider returned invalid tool metadata.');
|
|
127
|
+
tools.push(...page.tools);
|
|
128
|
+
if (tools.length > MAX_TOOLS)
|
|
129
|
+
throw new McpToolError('tool_limit', 'MCP tool discovery exceeded the tool limit.');
|
|
130
|
+
cursor = page.nextCursor;
|
|
131
|
+
} while (cursor);
|
|
132
|
+
return tools;
|
|
133
|
+
};
|
|
134
|
+
/*** Converts valid protocol metadata into a stable Flow descriptor. */
|
|
135
|
+
const toMetadata = (provider, tool) => {
|
|
136
|
+
if (!tool.name || !isRecord(tool.inputSchema))
|
|
137
|
+
throw new McpToolError('invalid_schema', 'The MCP tool has no valid input schema.');
|
|
138
|
+
const permission = permissionFor(provider, tool.name);
|
|
139
|
+
if (!permission)
|
|
140
|
+
throw new McpToolError('tool_not_discovered', 'The requested MCP tool is not allowlisted.');
|
|
141
|
+
const schema = validateMcpSchema(tool.inputSchema);
|
|
142
|
+
return {
|
|
143
|
+
providerId: provider.id,
|
|
144
|
+
name: tool.name,
|
|
145
|
+
...(tool.title ? { title: tool.title } : {}),
|
|
146
|
+
...(tool.description ? { description: tool.description } : {}),
|
|
147
|
+
inputSchema: tool.inputSchema,
|
|
148
|
+
schemaFingerprint: schema.fingerprint,
|
|
149
|
+
scopes: permission.grantedScopes,
|
|
150
|
+
readOnly: tool.annotations?.readOnlyHint === true,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
/*** Propagates cancellation and a fixed timeout into an asynchronous SDK operation. */
|
|
154
|
+
const boundedAsync = async (operation, timeoutMs, signal) => {
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
const timer = setTimeout(() => controller.abort(new DOMException('Timeout', 'TimeoutError')), timeoutMs);
|
|
157
|
+
const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
158
|
+
try {
|
|
159
|
+
return await operation(combined);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (controller.signal.aborted)
|
|
163
|
+
throw new McpToolError('timed_out', 'MCP operation timed out.');
|
|
164
|
+
if (combined.aborted)
|
|
165
|
+
throw new McpToolError('cancelled', 'MCP operation was cancelled.');
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
/*** Finds one explicit permission for a protocol tool. */
|
|
173
|
+
const permissionFor = (provider, name) => provider.allowedTools.find((item) => item.name === name);
|
|
174
|
+
/*** Requires every and only the configured granted scope. */
|
|
175
|
+
const sameScopes = (granted, requested) => requested.length > 0 &&
|
|
176
|
+
requested.length === granted.length &&
|
|
177
|
+
new Set(requested).size === requested.length &&
|
|
178
|
+
requested.every((scope) => granted.includes(scope));
|
|
179
|
+
/*** Builds a provider-scoped tool identifier. */
|
|
180
|
+
const key = (providerId, toolName) => `${providerId}:${toolName}`;
|
|
181
|
+
/*** Narrows unknown data to a non-array object. */
|
|
182
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
183
|
+
/*** Resolves a configured timeout to its bounded default. */
|
|
184
|
+
const timeoutOf = (provider) => provider.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
185
|
+
//# sourceMappingURL=createMcpToolRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMcpToolRegistry.js","sourceRoot":"","sources":["../../../src/features/mcp/createMcpToolRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Client as McpClient } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StdioClientTransport as McpStdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\n\nimport type {\n McpProviderDefinition,\n McpToolInvocation,\n McpToolMetadata,\n McpToolPermission,\n McpToolRegistry,\n McpToolResult,\n McpTrustedStdioProvider,\n} from '../../types/mcp.js';\nimport type { FlowOutboundTransportPort, FlowSecurityPort } from '../../types/security.js';\nimport { createMcpRemoteTransportAsync } from './createMcpRemoteTransport.js';\nimport { loadMcpSdkAsync } from './loadMcpSdkAsync.js';\nimport { McpToolError } from './McpToolError.js';\nimport { prepareMcpProviders } from './prepareMcpProviders.js';\nimport { validateMcpSchema } from './validateMcpSchema.js';\n\nconst MAX_PAGES = 20;\nconst MAX_TOOLS = 100;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\n/*** Creates a snapshotting, allowlist-only MCP registry. Real invocation fails closed until Issue #4 releases its approval boundary. */\nexport const createMcpToolRegistry = (input: {\n readonly providers: readonly McpProviderDefinition[];\n readonly outboundTransport?: FlowOutboundTransportPort;\n readonly security?: FlowSecurityPort;\n readonly trustedStdioProviders?: Readonly<Record<string, McpTrustedStdioProvider>>;\n}): McpToolRegistry => {\n const providers = prepareMcpProviders(input.providers);\n const trusted = new Map(\n Object.entries(input.trustedStdioProviders ?? {}).map(([id, value]) => [\n id,\n {\n command: value.command,\n ...(value.args ? { args: [...value.args] } : {}),\n },\n ]),\n );\n const snapshot = new Map<string, McpToolMetadata>();\n return {\n discoverAsync: async (signal) => {\n const groups = await Promise.all(\n [...providers.values()].map((provider) =>\n discoverAsync(provider, trusted, input.outboundTransport, signal),\n ),\n );\n const tools = groups.flat();\n const entries = tools.map((tool) => [key(tool.providerId, tool.name), tool] as const);\n if (new Set(entries.map(([identifier]) => identifier)).size !== entries.length)\n throw new McpToolError(\n 'duplicate_tool',\n 'MCP discovery returned duplicate provider tool identifiers.',\n );\n snapshot.clear();\n entries.forEach(([identifier, tool]) => snapshot.set(identifier, tool));\n return tools;\n },\n invokeAsync: (invocation) =>\n invokeAsync(\n providers,\n trusted,\n snapshot,\n input.security,\n input.outboundTransport,\n invocation,\n ),\n };\n};\n\n/*** Discovers bounded tool metadata from one trusted provider connection. */\nconst discoverAsync = async (\n provider: McpProviderDefinition,\n trusted: ReadonlyMap<string, McpTrustedStdioProvider>,\n outboundTransport: FlowOutboundTransportPort | undefined,\n signal?: AbortSignal,\n): Promise<readonly McpToolMetadata[]> => {\n const connection = await connectAsync(provider, trusted, outboundTransport, signal);\n try {\n const tools = await listToolsAsync(connection.client, timeoutOf(provider), signal);\n return tools\n .filter((tool) => permissionFor(provider, tool.name))\n .map((tool) => toMetadata(provider, tool));\n } finally {\n await connection.closeAsync().catch(() => undefined);\n }\n};\n\n/*** Validates, approves, and invokes one discovered tool through the server-only security port. */\nconst invokeAsync = async (\n providers: ReadonlyMap<string, McpProviderDefinition>,\n trusted: ReadonlyMap<string, McpTrustedStdioProvider>,\n snapshot: ReadonlyMap<string, McpToolMetadata>,\n security: FlowSecurityPort | undefined,\n outboundTransport: FlowOutboundTransportPort | undefined,\n invocation: McpToolInvocation,\n): Promise<McpToolResult> => {\n const provider = providers.get(invocation.providerId);\n const tool = snapshot.get(key(invocation.providerId, invocation.toolName));\n if (!provider || !tool || !permissionFor(provider, invocation.toolName))\n throw new McpToolError('tool_not_discovered', 'The requested MCP tool is not allowlisted.');\n if (tool.schemaFingerprint !== invocation.schemaFingerprint)\n throw new McpToolError(\n 'schema_changed',\n 'The MCP tool schema changed; rediscover it before invoking.',\n );\n const permission = permissionFor(provider, invocation.toolName);\n if (!permission || !sameScopes(permission.grantedScopes, invocation.scopes))\n throw new McpToolError(\n 'scope_denied',\n 'MCP tool invocation scopes are not explicitly granted.',\n );\n if (!validateMcpSchema(tool.inputSchema).matches(invocation.arguments))\n throw new McpToolError(\n 'invalid_arguments',\n 'MCP tool arguments do not satisfy the discovered schema.',\n );\n if (!security)\n throw new McpToolError(\n 'authorization_boundary_unavailable',\n 'MCP tool execution is unavailable until Flow security approvals are installed.',\n );\n const permit = await security.authorizeAsync({\n ...invocation.security,\n capability: `mcp:${provider.id}:${tool.name}`,\n ...(provider.transport === 'remote'\n ? { target: { allowedHosts: provider.allowedHosts, url: provider.endpoint } }\n : {}),\n });\n return permit.executeAsync(async () =>\n callToolAsync(provider, trusted, outboundTransport, invocation),\n );\n};\n\n/*** Opens a bounded provider connection only after the approval permit was issued. */\nconst callToolAsync = async (\n provider: McpProviderDefinition,\n trusted: ReadonlyMap<string, McpTrustedStdioProvider>,\n outboundTransport: FlowOutboundTransportPort | undefined,\n invocation: McpToolInvocation,\n): Promise<McpToolResult> => {\n const connection = await connectAsync(provider, trusted, outboundTransport, invocation.signal);\n try {\n const result = await boundedAsync(\n (signal) =>\n connection.client.callTool(\n { name: invocation.toolName, arguments: invocation.arguments },\n undefined,\n { signal },\n ),\n timeoutOf(provider),\n invocation.signal,\n );\n if (!Array.isArray(result.content))\n throw new McpToolError(\n 'invalid_execution_response',\n 'The MCP provider returned an invalid tool result.',\n );\n return {\n content: result.content,\n ...(isRecord(result.structuredContent)\n ? { structuredContent: result.structuredContent }\n : {}),\n isError: result.isError === true,\n };\n } finally {\n await connection.closeAsync().catch(() => undefined);\n }\n};\n\ninterface ListedTool {\n readonly name: string;\n readonly title?: string;\n readonly description?: string;\n readonly inputSchema: Readonly<Record<string, unknown>>;\n readonly annotations?: { readonly readOnlyHint?: boolean };\n}\ninterface Connection {\n readonly client: McpClient;\n closeAsync(): Promise<void>;\n}\n\n/*** Connects with a bounded timeout and an abort signal propagated to the SDK transport. */\nconst connectAsync = async (\n provider: McpProviderDefinition,\n trusted: ReadonlyMap<string, McpTrustedStdioProvider>,\n outboundTransport: FlowOutboundTransportPort | undefined,\n signal?: AbortSignal,\n): Promise<Connection> => {\n const { Client, StdioClientTransport } = await loadMcpSdkAsync();\n const client = new Client({ name: '@ankhorage/flow', version: '0.1.0' });\n const transport =\n provider.transport === 'stdio'\n ? stdioTransport(provider, trusted, StdioClientTransport)\n : await createMcpRemoteTransportAsync(provider, outboundTransport);\n await boundedAsync(\n (timeoutSignal) =>\n client.connect(transport, { signal: timeoutSignal, timeout: timeoutOf(provider) }),\n timeoutOf(provider),\n signal,\n );\n return { client, closeAsync: () => transport.close() };\n};\n\n/*** Resolves executable details exclusively from trusted host configuration. */\nconst stdioTransport = (\n provider: Extract<McpProviderDefinition, { readonly transport: 'stdio' }>,\n trusted: ReadonlyMap<string, McpTrustedStdioProvider>,\n StdioClientTransport: typeof McpStdioClientTransport,\n): McpStdioClientTransport => {\n const host = trusted.get(provider.id);\n if (!host)\n throw new McpToolError(\n 'untrusted_stdio_provider',\n 'STDIO MCP providers require trusted host command configuration.',\n );\n return new StdioClientTransport({\n command: host.command,\n args: host.args ? [...host.args] : undefined,\n stderr: 'pipe',\n });\n};\n\n/*** Lists a bounded, non-cyclic sequence of paginated tools. */\nconst listToolsAsync = async (\n client: McpClient,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<readonly ListedTool[]> => {\n const tools: ListedTool[] = [];\n const cursors = new Set<string>();\n let cursor: string | undefined;\n do {\n if (cursors.size >= MAX_PAGES)\n throw new McpToolError('pagination_limit', 'MCP tool discovery exceeded the page limit.');\n if (cursor) {\n if (cursors.has(cursor))\n throw new McpToolError(\n 'pagination_cycle',\n 'MCP tool discovery returned a repeated cursor.',\n );\n cursors.add(cursor);\n }\n const page = await boundedAsync(\n (timeoutSignal) =>\n client.listTools(cursor ? { cursor } : undefined, { signal: timeoutSignal }),\n timeoutMs,\n signal,\n );\n if (!Array.isArray(page.tools))\n throw new McpToolError(\n 'invalid_discovery_response',\n 'The MCP provider returned invalid tool metadata.',\n );\n tools.push(...page.tools);\n if (tools.length > MAX_TOOLS)\n throw new McpToolError('tool_limit', 'MCP tool discovery exceeded the tool limit.');\n cursor = page.nextCursor;\n } while (cursor);\n return tools;\n};\n\n/*** Converts valid protocol metadata into a stable Flow descriptor. */\nconst toMetadata = (provider: McpProviderDefinition, tool: ListedTool): McpToolMetadata => {\n if (!tool.name || !isRecord(tool.inputSchema))\n throw new McpToolError('invalid_schema', 'The MCP tool has no valid input schema.');\n const permission = permissionFor(provider, tool.name);\n if (!permission)\n throw new McpToolError('tool_not_discovered', 'The requested MCP tool is not allowlisted.');\n const schema = validateMcpSchema(tool.inputSchema);\n return {\n providerId: provider.id,\n name: tool.name,\n ...(tool.title ? { title: tool.title } : {}),\n ...(tool.description ? { description: tool.description } : {}),\n inputSchema: tool.inputSchema,\n schemaFingerprint: schema.fingerprint,\n scopes: permission.grantedScopes,\n readOnly: tool.annotations?.readOnlyHint === true,\n };\n};\n\n/*** Propagates cancellation and a fixed timeout into an asynchronous SDK operation. */\nconst boundedAsync = async <T>(\n operation: (signal: AbortSignal) => Promise<T>,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<T> => {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(new DOMException('Timeout', 'TimeoutError')),\n timeoutMs,\n );\n const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;\n try {\n return await operation(combined);\n } catch (error) {\n if (controller.signal.aborted) throw new McpToolError('timed_out', 'MCP operation timed out.');\n if (combined.aborted) throw new McpToolError('cancelled', 'MCP operation was cancelled.');\n throw error;\n } finally {\n clearTimeout(timer);\n }\n};\n/*** Finds one explicit permission for a protocol tool. */\nconst permissionFor = (\n provider: McpProviderDefinition,\n name: string,\n): McpToolPermission | undefined => provider.allowedTools.find((item) => item.name === name);\n/*** Requires every and only the configured granted scope. */\nconst sameScopes = (granted: readonly string[], requested: readonly string[]): boolean =>\n requested.length > 0 &&\n requested.length === granted.length &&\n new Set(requested).size === requested.length &&\n requested.every((scope) => granted.includes(scope));\n/*** Builds a provider-scoped tool identifier. */\nconst key = (providerId: string, toolName: string): string => `${providerId}:${toolName}`;\n/*** Narrows unknown data to a non-array object. */\nconst isRecord = (value: unknown): value is Readonly<Record<string, unknown>> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n/*** Resolves a configured timeout to its bounded default. */\nconst timeoutOf = (provider: McpProviderDefinition): number =>\n provider.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import type { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import type { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
4
|
+
interface McpSdk {
|
|
5
|
+
readonly Client: typeof Client;
|
|
6
|
+
readonly StdioClientTransport: typeof StdioClientTransport;
|
|
7
|
+
readonly StreamableHTTPClientTransport: typeof StreamableHTTPClientTransport;
|
|
8
|
+
}
|
|
9
|
+
/*** Loads the optional MCP SDK only when an approved MCP connection needs to be opened. */
|
|
10
|
+
export declare const loadMcpSdkAsync: () => Promise<McpSdk>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=loadMcpSdkAsync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadMcpSdkAsync.d.ts","sourceRoot":"","sources":["../../../src/features/mcp/loadMcpSdkAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAIxG,UAAU,MAAM;IACd,QAAQ,CAAC,MAAM,EAAE,OAAO,MAAM,CAAC;IAC/B,QAAQ,CAAC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAC3D,QAAQ,CAAC,6BAA6B,EAAE,OAAO,6BAA6B,CAAC;CAC9E;AAED,2FAA2F;AAC3F,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,MAAM,CAkBtD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { McpToolError } from './McpToolError.js';
|
|
2
|
+
/*** Loads the optional MCP SDK only when an approved MCP connection needs to be opened. */
|
|
3
|
+
export const loadMcpSdkAsync = async () => {
|
|
4
|
+
try {
|
|
5
|
+
const [client, stdio, streamableHttp] = await Promise.all([
|
|
6
|
+
import('@modelcontextprotocol/sdk/client/index.js'),
|
|
7
|
+
import('@modelcontextprotocol/sdk/client/stdio.js'),
|
|
8
|
+
import('@modelcontextprotocol/sdk/client/streamableHttp.js'),
|
|
9
|
+
]);
|
|
10
|
+
return {
|
|
11
|
+
Client: client.Client,
|
|
12
|
+
StdioClientTransport: stdio.StdioClientTransport,
|
|
13
|
+
StreamableHTTPClientTransport: streamableHttp.StreamableHTTPClientTransport,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new McpToolError('optional_sdk_unavailable', 'MCP execution requires the optional @modelcontextprotocol/sdk peer dependency.');
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=loadMcpSdkAsync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadMcpSdkAsync.js","sourceRoot":"","sources":["../../../src/features/mcp/loadMcpSdkAsync.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAQjD,2FAA2F;AAC3F,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,IAAqB,EAAE;IACzD,IAAI,CAAC;QACH,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxD,MAAM,CAAC,2CAA2C,CAAC;YACnD,MAAM,CAAC,2CAA2C,CAAC;YACnD,MAAM,CAAC,oDAAoD,CAAC;SAC7D,CAAC,CAAC;QACH,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;YAChD,6BAA6B,EAAE,cAAc,CAAC,6BAA6B;SAC5E,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CACpB,0BAA0B,EAC1B,gFAAgF,CACjF,CAAC;IACJ,CAAC;AACH,CAAC,CAAC","sourcesContent":["import type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\n\nimport { McpToolError } from './McpToolError.js';\n\ninterface McpSdk {\n readonly Client: typeof Client;\n readonly StdioClientTransport: typeof StdioClientTransport;\n readonly StreamableHTTPClientTransport: typeof StreamableHTTPClientTransport;\n}\n\n/*** Loads the optional MCP SDK only when an approved MCP connection needs to be opened. */\nexport const loadMcpSdkAsync = async (): Promise<McpSdk> => {\n try {\n const [client, stdio, streamableHttp] = await Promise.all([\n import('@modelcontextprotocol/sdk/client/index.js'),\n import('@modelcontextprotocol/sdk/client/stdio.js'),\n import('@modelcontextprotocol/sdk/client/streamableHttp.js'),\n ]);\n return {\n Client: client.Client,\n StdioClientTransport: stdio.StdioClientTransport,\n StreamableHTTPClientTransport: streamableHttp.StreamableHTTPClientTransport,\n };\n } catch {\n throw new McpToolError(\n 'optional_sdk_unavailable',\n 'MCP execution requires the optional @modelcontextprotocol/sdk peer dependency.',\n );\n }\n};\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpProviderDefinition } from '../../types/mcp.js';
|
|
2
|
+
/*** Snapshots and validates explicit MCP provider authority before any connection is opened. */
|
|
3
|
+
export declare const prepareMcpProviders: (configured: readonly McpProviderDefinition[]) => ReadonlyMap<string, McpProviderDefinition>;
|
|
4
|
+
//# sourceMappingURL=prepareMcpProviders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareMcpProviders.d.ts","sourceRoot":"","sources":["../../../src/features/mcp/prepareMcpProviders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAKhE,gGAAgG;AAChG,eAAO,MAAM,mBAAmB,GAC9B,YAAY,SAAS,qBAAqB,EAAE,KAC3C,WAAW,CAAC,MAAM,EAAE,qBAAqB,CAQ3C,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { McpToolError } from './McpToolError.js';
|
|
2
|
+
const MAX_TIMEOUT_MS = 30_000;
|
|
3
|
+
/*** Snapshots and validates explicit MCP provider authority before any connection is opened. */
|
|
4
|
+
export const prepareMcpProviders = (configured) => {
|
|
5
|
+
const providers = new Map(configured.map(cloneProvider).map((provider) => [provider.id, provider]));
|
|
6
|
+
if (!providers.size || providers.size !== configured.length)
|
|
7
|
+
throw new McpToolError('invalid_provider', 'MCP provider identifiers must be unique.');
|
|
8
|
+
providers.forEach(validateProvider);
|
|
9
|
+
return providers;
|
|
10
|
+
};
|
|
11
|
+
/*** Clones provider data so later workflow-object mutation cannot expand authority. */
|
|
12
|
+
const cloneProvider = (provider) => {
|
|
13
|
+
const common = {
|
|
14
|
+
id: provider.id,
|
|
15
|
+
allowedTools: provider.allowedTools.map((item) => ({
|
|
16
|
+
name: item.name,
|
|
17
|
+
grantedScopes: [...item.grantedScopes],
|
|
18
|
+
})),
|
|
19
|
+
grantedScopes: [...provider.grantedScopes],
|
|
20
|
+
...(provider.timeoutMs === undefined ? {} : { timeoutMs: provider.timeoutMs }),
|
|
21
|
+
};
|
|
22
|
+
return provider.transport === 'stdio'
|
|
23
|
+
? { ...common, transport: 'stdio' }
|
|
24
|
+
: {
|
|
25
|
+
...common,
|
|
26
|
+
transport: 'remote',
|
|
27
|
+
endpoint: provider.endpoint,
|
|
28
|
+
allowedHosts: [...provider.allowedHosts],
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/*** Requires non-empty scopes, unique tool permissions, and safe transport configuration. */
|
|
32
|
+
const validateProvider = (provider) => {
|
|
33
|
+
if (!provider.id || !uniqueStrings(provider.grantedScopes) || !provider.allowedTools.length)
|
|
34
|
+
throw new McpToolError('invalid_provider', 'MCP providers need an id, explicit granted scopes, and tool permissions.');
|
|
35
|
+
if (!uniqueStrings(provider.allowedTools.map((item) => item.name)))
|
|
36
|
+
throw new McpToolError('invalid_provider', 'MCP tool permission names must be unique.');
|
|
37
|
+
if (provider.allowedTools.some((item) => !uniqueStrings(item.grantedScopes) ||
|
|
38
|
+
!item.grantedScopes.every((scope) => provider.grantedScopes.includes(scope))))
|
|
39
|
+
throw new McpToolError('invalid_provider', 'MCP tools require explicitly granted provider scopes.');
|
|
40
|
+
if (provider.timeoutMs !== undefined &&
|
|
41
|
+
(!Number.isSafeInteger(provider.timeoutMs) ||
|
|
42
|
+
provider.timeoutMs < 100 ||
|
|
43
|
+
provider.timeoutMs > MAX_TIMEOUT_MS))
|
|
44
|
+
throw new McpToolError('invalid_provider', 'MCP timeout must be bounded.');
|
|
45
|
+
if (provider.transport === 'remote')
|
|
46
|
+
validateRemote(provider);
|
|
47
|
+
};
|
|
48
|
+
/*** Rejects credentials, private literals, and endpoints outside their explicit host allowlist. */
|
|
49
|
+
const validateRemote = (provider) => {
|
|
50
|
+
let url;
|
|
51
|
+
try {
|
|
52
|
+
url = new URL(provider.endpoint);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
throw new McpToolError('unsafe_endpoint', 'Remote MCP endpoint must be an absolute URL.');
|
|
56
|
+
}
|
|
57
|
+
const host = url.hostname.replace(/^\[|\]$/g, '').toLowerCase();
|
|
58
|
+
if (url.username || url.password || url.hash)
|
|
59
|
+
throw new McpToolError('unsafe_endpoint', 'Remote MCP endpoints cannot embed credentials or fragments.');
|
|
60
|
+
if (url.protocol !== 'https:' ||
|
|
61
|
+
privateIpv4(host) ||
|
|
62
|
+
privateIpv6(host) ||
|
|
63
|
+
!uniqueStrings(provider.allowedHosts) ||
|
|
64
|
+
!provider.allowedHosts.includes(host))
|
|
65
|
+
throw new McpToolError('unsafe_endpoint', 'Remote MCP endpoints require HTTPS, a public host allowlist, and no private address.');
|
|
66
|
+
};
|
|
67
|
+
/*** Detects loopback, link-local, unique-local, and unspecified IPv6 literals. */
|
|
68
|
+
const privateIpv6 = (host) => {
|
|
69
|
+
const normalized = host.replace(/^\[|\]$/g, '').toLowerCase();
|
|
70
|
+
return (normalized === '::' ||
|
|
71
|
+
normalized === '::1' ||
|
|
72
|
+
normalized.startsWith('fc') ||
|
|
73
|
+
normalized.startsWith('fd') ||
|
|
74
|
+
normalized.startsWith('fe8') ||
|
|
75
|
+
normalized.startsWith('fe9') ||
|
|
76
|
+
normalized.startsWith('fea') ||
|
|
77
|
+
normalized.startsWith('feb') ||
|
|
78
|
+
normalized.startsWith('::ffff:'));
|
|
79
|
+
};
|
|
80
|
+
/*** Detects private, reserved, and local IPv4 literals. */
|
|
81
|
+
const privateIpv4 = (host) => {
|
|
82
|
+
const parts = host.split('.').map(Number);
|
|
83
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255))
|
|
84
|
+
return false;
|
|
85
|
+
const [first, second] = parts;
|
|
86
|
+
return (first === 0 ||
|
|
87
|
+
first === 10 ||
|
|
88
|
+
first === 127 ||
|
|
89
|
+
first >= 224 ||
|
|
90
|
+
(first === 169 && second === 254) ||
|
|
91
|
+
(first === 172 && second >= 16 && second <= 31) ||
|
|
92
|
+
(first === 192 && second === 168));
|
|
93
|
+
};
|
|
94
|
+
/*** Checks unique non-empty strings. */
|
|
95
|
+
const uniqueStrings = (values) => values.every((value) => typeof value === 'string' && value.length > 0) &&
|
|
96
|
+
new Set(values).size === values.length;
|
|
97
|
+
//# sourceMappingURL=prepareMcpProviders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareMcpProviders.js","sourceRoot":"","sources":["../../../src/features/mcp/prepareMcpProviders.ts"],"names":[],"mappings":"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","sourcesContent":["import type { McpProviderDefinition } from '../../types/mcp.js';\nimport { McpToolError } from './McpToolError.js';\n\nconst MAX_TIMEOUT_MS = 30_000;\n\n/*** Snapshots and validates explicit MCP provider authority before any connection is opened. */\nexport const prepareMcpProviders = (\n configured: readonly McpProviderDefinition[],\n): ReadonlyMap<string, McpProviderDefinition> => {\n const providers = new Map(\n configured.map(cloneProvider).map((provider) => [provider.id, provider]),\n );\n if (!providers.size || providers.size !== configured.length)\n throw new McpToolError('invalid_provider', 'MCP provider identifiers must be unique.');\n providers.forEach(validateProvider);\n return providers;\n};\n\n/*** Clones provider data so later workflow-object mutation cannot expand authority. */\nconst cloneProvider = (provider: McpProviderDefinition): McpProviderDefinition => {\n const common = {\n id: provider.id,\n allowedTools: provider.allowedTools.map((item) => ({\n name: item.name,\n grantedScopes: [...item.grantedScopes],\n })),\n grantedScopes: [...provider.grantedScopes],\n ...(provider.timeoutMs === undefined ? {} : { timeoutMs: provider.timeoutMs }),\n };\n return provider.transport === 'stdio'\n ? { ...common, transport: 'stdio' }\n : {\n ...common,\n transport: 'remote',\n endpoint: provider.endpoint,\n allowedHosts: [...provider.allowedHosts],\n };\n};\n\n/*** Requires non-empty scopes, unique tool permissions, and safe transport configuration. */\nconst validateProvider = (provider: McpProviderDefinition): void => {\n if (!provider.id || !uniqueStrings(provider.grantedScopes) || !provider.allowedTools.length)\n throw new McpToolError(\n 'invalid_provider',\n 'MCP providers need an id, explicit granted scopes, and tool permissions.',\n );\n if (!uniqueStrings(provider.allowedTools.map((item) => item.name)))\n throw new McpToolError('invalid_provider', 'MCP tool permission names must be unique.');\n if (\n provider.allowedTools.some(\n (item) =>\n !uniqueStrings(item.grantedScopes) ||\n !item.grantedScopes.every((scope) => provider.grantedScopes.includes(scope)),\n )\n )\n throw new McpToolError(\n 'invalid_provider',\n 'MCP tools require explicitly granted provider scopes.',\n );\n if (\n provider.timeoutMs !== undefined &&\n (!Number.isSafeInteger(provider.timeoutMs) ||\n provider.timeoutMs < 100 ||\n provider.timeoutMs > MAX_TIMEOUT_MS)\n )\n throw new McpToolError('invalid_provider', 'MCP timeout must be bounded.');\n if (provider.transport === 'remote') validateRemote(provider);\n};\n\n/*** Rejects credentials, private literals, and endpoints outside their explicit host allowlist. */\nconst validateRemote = (\n provider: Extract<McpProviderDefinition, { readonly transport: 'remote' }>,\n): void => {\n let url: URL;\n try {\n url = new URL(provider.endpoint);\n } catch {\n throw new McpToolError('unsafe_endpoint', 'Remote MCP endpoint must be an absolute URL.');\n }\n const host = url.hostname.replace(/^\\[|\\]$/g, '').toLowerCase();\n if (url.username || url.password || url.hash)\n throw new McpToolError(\n 'unsafe_endpoint',\n 'Remote MCP endpoints cannot embed credentials or fragments.',\n );\n if (\n url.protocol !== 'https:' ||\n privateIpv4(host) ||\n privateIpv6(host) ||\n !uniqueStrings(provider.allowedHosts) ||\n !provider.allowedHosts.includes(host)\n )\n throw new McpToolError(\n 'unsafe_endpoint',\n 'Remote MCP endpoints require HTTPS, a public host allowlist, and no private address.',\n );\n};\n\n/*** Detects loopback, link-local, unique-local, and unspecified IPv6 literals. */\nconst privateIpv6 = (host: string): boolean => {\n const normalized = host.replace(/^\\[|\\]$/g, '').toLowerCase();\n return (\n normalized === '::' ||\n normalized === '::1' ||\n normalized.startsWith('fc') ||\n normalized.startsWith('fd') ||\n normalized.startsWith('fe8') ||\n normalized.startsWith('fe9') ||\n normalized.startsWith('fea') ||\n normalized.startsWith('feb') ||\n normalized.startsWith('::ffff:')\n );\n};\n\n/*** Detects private, reserved, and local IPv4 literals. */\nconst privateIpv4 = (host: string): boolean => {\n const parts = host.split('.').map(Number);\n if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255))\n return false;\n const [first, second] = parts as [number, number, number, number];\n return (\n first === 0 ||\n first === 10 ||\n first === 127 ||\n first >= 224 ||\n (first === 169 && second === 254) ||\n (first === 172 && second >= 16 && second <= 31) ||\n (first === 192 && second === 168)\n );\n};\n\n/*** Checks unique non-empty strings. */\nconst uniqueStrings = (values: readonly unknown[]): values is readonly string[] =>\n values.every((value) => typeof value === 'string' && value.length > 0) &&\n new Set(values).size === values.length;\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/*** Validates a closed supported JSON Schema and returns its stable argument matcher. */
|
|
2
|
+
export declare const validateMcpSchema: (schema: Readonly<Record<string, unknown>>) => {
|
|
3
|
+
fingerprint: string;
|
|
4
|
+
matches: (value: unknown) => boolean;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=validateMcpSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateMcpSchema.d.ts","sourceRoot":"","sources":["../../../src/features/mcp/validateMcpSchema.ts"],"names":[],"mappings":"AAgCA,yFAAyF;AACzF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;qBAItD,OAAO,KAAG,OAAO;CAErC,CAAC"}
|