@ankhorage/flow 0.0.0-stage → 0.4.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +17 -2
  4. package/dist/ai.d.ts +7 -0
  5. package/dist/ai.d.ts.map +1 -0
  6. package/dist/ai.js +7 -0
  7. package/dist/ai.js.map +1 -0
  8. package/dist/features/execution/application/executeFlowAsync.d.ts +4 -0
  9. package/dist/features/execution/application/executeFlowAsync.d.ts.map +1 -0
  10. package/dist/features/execution/application/executeFlowAsync.js +211 -0
  11. package/dist/features/execution/application/executeFlowAsync.js.map +1 -0
  12. package/dist/features/execution/domain/assertFlowGraphSchemasSupported.d.ts +5 -0
  13. package/dist/features/execution/domain/assertFlowGraphSchemasSupported.d.ts.map +1 -0
  14. package/dist/features/execution/domain/assertFlowGraphSchemasSupported.js +11 -0
  15. package/dist/features/execution/domain/assertFlowGraphSchemasSupported.js.map +1 -0
  16. package/dist/features/execution/domain/compileFlowGraph.d.ts +5 -0
  17. package/dist/features/execution/domain/compileFlowGraph.d.ts.map +1 -0
  18. package/dist/features/execution/domain/compileFlowGraph.js +76 -0
  19. package/dist/features/execution/domain/compileFlowGraph.js.map +1 -0
  20. package/dist/features/execution/domain/createDefaultNodeRegistry.d.ts +4 -0
  21. package/dist/features/execution/domain/createDefaultNodeRegistry.d.ts.map +1 -0
  22. package/dist/features/execution/domain/createDefaultNodeRegistry.js +15 -0
  23. package/dist/features/execution/domain/createDefaultNodeRegistry.js.map +1 -0
  24. package/dist/features/execution/domain/validateFlowDataSchema.d.ts +6 -0
  25. package/dist/features/execution/domain/validateFlowDataSchema.d.ts.map +1 -0
  26. package/dist/features/execution/domain/validateFlowDataSchema.js +144 -0
  27. package/dist/features/execution/domain/validateFlowDataSchema.js.map +1 -0
  28. package/dist/features/mcp/McpToolError.d.ts +6 -0
  29. package/dist/features/mcp/McpToolError.d.ts.map +1 -0
  30. package/dist/features/mcp/McpToolError.js +10 -0
  31. package/dist/features/mcp/McpToolError.js.map +1 -0
  32. package/dist/features/mcp/createMcpRemoteTransport.d.ts +8 -0
  33. package/dist/features/mcp/createMcpRemoteTransport.d.ts.map +1 -0
  34. package/dist/features/mcp/createMcpRemoteTransport.js +23 -0
  35. package/dist/features/mcp/createMcpRemoteTransport.js.map +1 -0
  36. package/dist/features/mcp/createMcpToolRegistry.d.ts +10 -0
  37. package/dist/features/mcp/createMcpToolRegistry.d.ts.map +1 -0
  38. package/dist/features/mcp/createMcpToolRegistry.js +185 -0
  39. package/dist/features/mcp/createMcpToolRegistry.js.map +1 -0
  40. package/dist/features/mcp/loadMcpSdkAsync.d.ts +12 -0
  41. package/dist/features/mcp/loadMcpSdkAsync.d.ts.map +1 -0
  42. package/dist/features/mcp/loadMcpSdkAsync.js +20 -0
  43. package/dist/features/mcp/loadMcpSdkAsync.js.map +1 -0
  44. package/dist/features/mcp/prepareMcpProviders.d.ts +4 -0
  45. package/dist/features/mcp/prepareMcpProviders.d.ts.map +1 -0
  46. package/dist/features/mcp/prepareMcpProviders.js +97 -0
  47. package/dist/features/mcp/prepareMcpProviders.js.map +1 -0
  48. package/dist/features/mcp/validateMcpSchema.d.ts +6 -0
  49. package/dist/features/mcp/validateMcpSchema.d.ts.map +1 -0
  50. package/dist/features/mcp/validateMcpSchema.js +220 -0
  51. package/dist/features/mcp/validateMcpSchema.js.map +1 -0
  52. package/dist/features/security/FlowSecurityError.d.ts +6 -0
  53. package/dist/features/security/FlowSecurityError.d.ts.map +1 -0
  54. package/dist/features/security/FlowSecurityError.js +10 -0
  55. package/dist/features/security/FlowSecurityError.js.map +1 -0
  56. package/dist/features/security/application/createFlowSecurityPort.d.ts +12 -0
  57. package/dist/features/security/application/createFlowSecurityPort.d.ts.map +1 -0
  58. package/dist/features/security/application/createFlowSecurityPort.js +208 -0
  59. package/dist/features/security/application/createFlowSecurityPort.js.map +1 -0
  60. package/dist/features/security/domain/validateFlowSecurityTarget.d.ts +4 -0
  61. package/dist/features/security/domain/validateFlowSecurityTarget.d.ts.map +1 -0
  62. package/dist/features/security/domain/validateFlowSecurityTarget.js +57 -0
  63. package/dist/features/security/domain/validateFlowSecurityTarget.js.map +1 -0
  64. package/dist/index.d.ts +9 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +7 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/mcp.d.ts +4 -0
  69. package/dist/mcp.d.ts.map +1 -0
  70. package/dist/mcp.js +3 -0
  71. package/dist/mcp.js.map +1 -0
  72. package/dist/social.d.ts +7 -0
  73. package/dist/social.d.ts.map +1 -0
  74. package/dist/social.js +7 -0
  75. package/dist/social.js.map +1 -0
  76. package/dist/types/execution.d.ts +33 -0
  77. package/dist/types/execution.d.ts.map +1 -0
  78. package/dist/types/execution.js +2 -0
  79. package/dist/types/execution.js.map +1 -0
  80. package/dist/types/mcp.d.ts +61 -0
  81. package/dist/types/mcp.d.ts.map +1 -0
  82. package/dist/types/mcp.js +2 -0
  83. package/dist/types/mcp.js.map +1 -0
  84. package/dist/types/security.d.ts +123 -0
  85. package/dist/types/security.d.ts.map +1 -0
  86. package/dist/types/security.js +2 -0
  87. package/dist/types/security.js.map +1 -0
  88. package/package.json +86 -4
  89. package/src/ai.ts +6 -0
  90. package/src/features/execution/application/executeFlowAsync.ts +338 -0
  91. package/src/features/execution/domain/assertFlowGraphSchemasSupported.ts +17 -0
  92. package/src/features/execution/domain/compileFlowGraph.ts +97 -0
  93. package/src/features/execution/domain/createDefaultNodeRegistry.ts +26 -0
  94. package/src/features/execution/domain/validateFlowDataSchema.ts +218 -0
  95. package/src/features/mcp/McpToolError.ts +10 -0
  96. package/src/features/mcp/createMcpRemoteTransport.ts +34 -0
  97. package/src/features/mcp/createMcpToolRegistry.ts +324 -0
  98. package/src/features/mcp/loadMcpSdkAsync.ts +32 -0
  99. package/src/features/mcp/prepareMcpProviders.ts +135 -0
  100. package/src/features/mcp/validateMcpSchema.ts +269 -0
  101. package/src/features/security/FlowSecurityError.ts +17 -0
  102. package/src/features/security/application/createFlowSecurityPort.ts +339 -0
  103. package/src/features/security/domain/validateFlowSecurityTarget.ts +73 -0
  104. package/src/index.ts +35 -0
  105. package/src/mcp.ts +11 -0
  106. package/src/social.ts +6 -0
  107. package/src/types/execution.ts +41 -0
  108. package/src/types/mcp.ts +70 -0
  109. package/src/types/security.ts +148 -0
@@ -0,0 +1,144 @@
1
+ const maximumSchemaDepth = 64;
2
+ /*** Validate serializable data against the supported portable schema vocabulary. */
3
+ export const validateFlowDataSchema = (value, schema, schemas) => {
4
+ assertFlowDataSchemaSupported(schema, schemas);
5
+ return matchesSchema(value, schema, schemas, createTraversalState());
6
+ };
7
+ /*** Reject unsupported, unresolved, cyclic, or excessively nested portable schemas before use. */
8
+ export const assertFlowDataSchemaSupported = (schema, schemas) => assertSupportedSchema(schema, schemas, createTraversalState());
9
+ /*** Resolve a schema reference only from the explicit execution registry. */
10
+ const resolveSchema = (schema, schemas, state) => {
11
+ if (!schema.ref)
12
+ return schema;
13
+ if (state.referenceIds.has(schema.ref.id))
14
+ throw new Error(`FLOW_SCHEMA_REFERENCE_CYCLE:${schema.ref.id}`);
15
+ const resolved = schemas?.[schema.ref.id];
16
+ if (!resolved)
17
+ throw new Error(`FLOW_SCHEMA_REFERENCE_UNRESOLVED:${schema.ref.id}`);
18
+ return resolved;
19
+ };
20
+ /*** Apply portable schema constraints without coercing a JSON-compatible value. */
21
+ const matchesSchema = (value, schema, schemas, state) => {
22
+ const resolved = resolveSchema(schema, schemas, state);
23
+ const nextState = advanceTraversalState(resolved, schema.ref?.id, state);
24
+ return (matchesTypes(value, resolved) &&
25
+ matchesValues(value, resolved) &&
26
+ matchesComposition(value, resolved, schemas, nextState) &&
27
+ matchesStructure(value, resolved, schemas, nextState));
28
+ };
29
+ /*** Reject portable annotations that require a validator capability this engine does not own. */
30
+ const assertSupportedSchema = (schema, schemas, state) => {
31
+ if (schema.format !== undefined)
32
+ throw new Error(`FLOW_SCHEMA_UNSUPPORTED_CONSTRAINT:format`);
33
+ const nextState = advanceTraversalState(schema, undefined, state);
34
+ visitChildSchemas(schema, schemas, nextState);
35
+ if (!schema.ref)
36
+ return;
37
+ const resolved = resolveSchema(schema, schemas, state);
38
+ assertSupportedSchema(resolved, schemas, {
39
+ ...nextState,
40
+ referenceIds: new Set([...state.referenceIds, schema.ref.id]),
41
+ });
42
+ };
43
+ /*** Visit every schema child so unsupported declarations fail before value matching begins. */
44
+ const visitChildSchemas = (schema, schemas, state) => {
45
+ const children = [
46
+ ...(schema.items ? [schema.items] : []),
47
+ ...(typeof schema.additionalProperties === 'object' ? [schema.additionalProperties] : []),
48
+ ...Object.values(schema.properties ?? {}),
49
+ ...(schema.allOf ?? []),
50
+ ...(schema.anyOf ?? []),
51
+ ...(schema.oneOf ?? []),
52
+ ];
53
+ for (const child of children)
54
+ assertSupportedSchema(child, schemas, state);
55
+ };
56
+ /*** Match the declared primitive type union and explicit nullability. */
57
+ const matchesTypes = (value, schema) => {
58
+ if (value === null && schema.nullable)
59
+ return true;
60
+ if (schema.type === undefined)
61
+ return true;
62
+ if (typeof schema.type === 'string')
63
+ return matchesType(value, schema.type);
64
+ return schema.type.some((type) => matchesType(value, type));
65
+ };
66
+ /*** Match one JSON primitive category without coercion. */
67
+ const matchesType = (value, type) => {
68
+ if (type === 'array')
69
+ return isDataArray(value);
70
+ if (type === 'integer')
71
+ return typeof value === 'number' && Number.isInteger(value);
72
+ if (type === 'null')
73
+ return value === null;
74
+ if (type === 'object')
75
+ return isDataRecord(value);
76
+ return typeof value === type;
77
+ };
78
+ /*** Apply constant and enumeration constraints using structural JSON equality. */
79
+ const matchesValues = (value, schema) => (schema.const === undefined || structurallyEqual(value, schema.const)) &&
80
+ (!schema.enum || schema.enum.some((candidate) => structurallyEqual(value, candidate)));
81
+ /*** Apply logical schema composition with its standard cardinality semantics. */
82
+ const matchesComposition = (value, schema, schemas, state) => (!schema.allOf ||
83
+ schema.allOf.every((candidate) => matchesSchema(value, candidate, schemas, state))) &&
84
+ (!schema.anyOf ||
85
+ schema.anyOf.some((candidate) => matchesSchema(value, candidate, schemas, state))) &&
86
+ (!schema.oneOf ||
87
+ schema.oneOf.filter((candidate) => matchesSchema(value, candidate, schemas, state)).length ===
88
+ 1);
89
+ /*** Recursively apply object and array member constraints. */
90
+ const matchesStructure = (value, schema, schemas, state) => matchesItems(value, schema.items, schemas, state) &&
91
+ (!isDataRecord(value) || matchesProperties(value, schema, schemas, state));
92
+ /*** Validate array elements only when an item schema is declared. */
93
+ const matchesItems = (value, items, schemas, state) => !items ||
94
+ !isDataArray(value) ||
95
+ value.every((item) => matchesSchema(item, items, schemas, state));
96
+ /*** Validate required object members and declared or additional property schemas. */
97
+ const matchesProperties = (value, schema, schemas, state) => {
98
+ const properties = schema.properties ?? {};
99
+ return ((schema.required ?? []).every((key) => Object.entries(value).some(([name]) => name === key)) &&
100
+ Object.entries(value).every(([key, item]) => {
101
+ const property = Object.entries(properties).find(([name]) => name === key)?.[1];
102
+ if (property)
103
+ return matchesSchema(item, property, schemas, state);
104
+ if (schema.additionalProperties === false)
105
+ return false;
106
+ return (typeof schema.additionalProperties !== 'object' ||
107
+ matchesSchema(item, schema.additionalProperties, schemas, state));
108
+ }));
109
+ };
110
+ /*** Start bounded schema traversal without retaining mutable validation state. */
111
+ const createTraversalState = () => ({
112
+ depth: 0,
113
+ referenceIds: new Set(),
114
+ schemas: new Set(),
115
+ });
116
+ /*** Advance a schema traversal while rejecting direct cycles and excessive nesting deterministically. */
117
+ const advanceTraversalState = (schema, referenceId, state) => {
118
+ if (state.depth >= maximumSchemaDepth)
119
+ throw new Error('FLOW_SCHEMA_MAX_DEPTH');
120
+ if (state.schemas.has(schema))
121
+ throw new Error('FLOW_SCHEMA_CYCLE');
122
+ return {
123
+ depth: state.depth + 1,
124
+ referenceIds: referenceId ? new Set([...state.referenceIds, referenceId]) : state.referenceIds,
125
+ schemas: new Set([...state.schemas, schema]),
126
+ };
127
+ };
128
+ /*** Compare JSON-compatible values independently of object key order. */
129
+ const structurallyEqual = (left, right) => JSON.stringify(normalizeValue(left)) === JSON.stringify(normalizeValue(right));
130
+ /*** Canonicalize object member ordering for structural comparison. */
131
+ const normalizeValue = (value) => {
132
+ if (isDataArray(value))
133
+ return value.map(normalizeValue);
134
+ if (isDataRecord(value))
135
+ return Object.fromEntries(Object.entries(value)
136
+ .sort(([left], [right]) => left.localeCompare(right))
137
+ .map(([key, item]) => [key, normalizeValue(item)]));
138
+ return value;
139
+ };
140
+ /*** Narrow a JSON-compatible object to a non-array member record. */
141
+ const isDataRecord = (value) => value !== null && typeof value === 'object' && !isDataArray(value);
142
+ /*** Narrow a JSON-compatible array without losing its serializable member type. */
143
+ const isDataArray = (value) => Array.isArray(value);
144
+ //# sourceMappingURL=validateFlowDataSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateFlowDataSchema.js","sourceRoot":"","sources":["../../../../src/features/execution/domain/validateFlowDataSchema.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DataContractValue, DataSchema, DataSchemaRegistry } from '@ankhorage/contracts/data';\n\nconst maximumSchemaDepth = 64;\n\n/*** Validate serializable data against the supported portable schema vocabulary. */\nexport const validateFlowDataSchema = (\n value: DataContractValue,\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n): boolean => {\n assertFlowDataSchemaSupported(schema, schemas);\n return matchesSchema(value, schema, schemas, createTraversalState());\n};\n\n/*** Reject unsupported, unresolved, cyclic, or excessively nested portable schemas before use. */\nexport const assertFlowDataSchemaSupported = (\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n): void => assertSupportedSchema(schema, schemas, createTraversalState());\n\n/*** Resolve a schema reference only from the explicit execution registry. */\nconst resolveSchema = (\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): DataSchema => {\n if (!schema.ref) return schema;\n if (state.referenceIds.has(schema.ref.id))\n throw new Error(`FLOW_SCHEMA_REFERENCE_CYCLE:${schema.ref.id}`);\n const resolved = schemas?.[schema.ref.id];\n if (!resolved) throw new Error(`FLOW_SCHEMA_REFERENCE_UNRESOLVED:${schema.ref.id}`);\n return resolved;\n};\n\n/*** Apply portable schema constraints without coercing a JSON-compatible value. */\nconst matchesSchema = (\n value: DataContractValue,\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): boolean => {\n const resolved = resolveSchema(schema, schemas, state);\n const nextState = advanceTraversalState(resolved, schema.ref?.id, state);\n return (\n matchesTypes(value, resolved) &&\n matchesValues(value, resolved) &&\n matchesComposition(value, resolved, schemas, nextState) &&\n matchesStructure(value, resolved, schemas, nextState)\n );\n};\n\n/*** Reject portable annotations that require a validator capability this engine does not own. */\nconst assertSupportedSchema = (\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): void => {\n if (schema.format !== undefined) throw new Error(`FLOW_SCHEMA_UNSUPPORTED_CONSTRAINT:format`);\n const nextState = advanceTraversalState(schema, undefined, state);\n visitChildSchemas(schema, schemas, nextState);\n if (!schema.ref) return;\n const resolved = resolveSchema(schema, schemas, state);\n assertSupportedSchema(resolved, schemas, {\n ...nextState,\n referenceIds: new Set([...state.referenceIds, schema.ref.id]),\n });\n};\n\n/*** Visit every schema child so unsupported declarations fail before value matching begins. */\nconst visitChildSchemas = (\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): void => {\n const children = [\n ...(schema.items ? [schema.items] : []),\n ...(typeof schema.additionalProperties === 'object' ? [schema.additionalProperties] : []),\n ...Object.values(schema.properties ?? {}),\n ...(schema.allOf ?? []),\n ...(schema.anyOf ?? []),\n ...(schema.oneOf ?? []),\n ];\n for (const child of children) assertSupportedSchema(child, schemas, state);\n};\n\n/*** Match the declared primitive type union and explicit nullability. */\nconst matchesTypes = (value: DataContractValue, schema: DataSchema): boolean => {\n if (value === null && schema.nullable) return true;\n if (schema.type === undefined) return true;\n if (typeof schema.type === 'string') return matchesType(value, schema.type);\n return schema.type.some((type) => matchesType(value, type));\n};\n\n/*** Match one JSON primitive category without coercion. */\nconst matchesType = (value: DataContractValue, type: string): boolean => {\n if (type === 'array') return isDataArray(value);\n if (type === 'integer') return typeof value === 'number' && Number.isInteger(value);\n if (type === 'null') return value === null;\n if (type === 'object') return isDataRecord(value);\n return typeof value === type;\n};\n\n/*** Apply constant and enumeration constraints using structural JSON equality. */\nconst matchesValues = (value: DataContractValue, schema: DataSchema): boolean =>\n (schema.const === undefined || structurallyEqual(value, schema.const)) &&\n (!schema.enum || schema.enum.some((candidate) => structurallyEqual(value, candidate)));\n\n/*** Apply logical schema composition with its standard cardinality semantics. */\nconst matchesComposition = (\n value: DataContractValue,\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): boolean =>\n (!schema.allOf ||\n schema.allOf.every((candidate) => matchesSchema(value, candidate, schemas, state))) &&\n (!schema.anyOf ||\n schema.anyOf.some((candidate) => matchesSchema(value, candidate, schemas, state))) &&\n (!schema.oneOf ||\n schema.oneOf.filter((candidate) => matchesSchema(value, candidate, schemas, state)).length ===\n 1);\n\n/*** Recursively apply object and array member constraints. */\nconst matchesStructure = (\n value: DataContractValue,\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): boolean =>\n matchesItems(value, schema.items, schemas, state) &&\n (!isDataRecord(value) || matchesProperties(value, schema, schemas, state));\n\n/*** Validate array elements only when an item schema is declared. */\nconst matchesItems = (\n value: DataContractValue,\n items: DataSchema | undefined,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): boolean =>\n !items ||\n !isDataArray(value) ||\n value.every((item) => matchesSchema(item, items, schemas, state));\n\n/*** Validate required object members and declared or additional property schemas. */\nconst matchesProperties = (\n value: Readonly<Record<string, DataContractValue>>,\n schema: DataSchema,\n schemas: DataSchemaRegistry | undefined,\n state: SchemaTraversalState,\n): boolean => {\n const properties = schema.properties ?? {};\n return (\n (schema.required ?? []).every((key) => Object.entries(value).some(([name]) => name === key)) &&\n Object.entries(value).every(([key, item]) => {\n const property = Object.entries(properties).find(([name]) => name === key)?.[1];\n if (property) return matchesSchema(item, property, schemas, state);\n if (schema.additionalProperties === false) return false;\n return (\n typeof schema.additionalProperties !== 'object' ||\n matchesSchema(item, schema.additionalProperties, schemas, state)\n );\n })\n );\n};\n\ninterface SchemaTraversalState {\n readonly depth: number;\n readonly referenceIds: ReadonlySet<string>;\n readonly schemas: ReadonlySet<DataSchema>;\n}\n\n/*** Start bounded schema traversal without retaining mutable validation state. */\nconst createTraversalState = (): SchemaTraversalState => ({\n depth: 0,\n referenceIds: new Set(),\n schemas: new Set(),\n});\n\n/*** Advance a schema traversal while rejecting direct cycles and excessive nesting deterministically. */\nconst advanceTraversalState = (\n schema: DataSchema,\n referenceId: string | undefined,\n state: SchemaTraversalState,\n): SchemaTraversalState => {\n if (state.depth >= maximumSchemaDepth) throw new Error('FLOW_SCHEMA_MAX_DEPTH');\n if (state.schemas.has(schema)) throw new Error('FLOW_SCHEMA_CYCLE');\n return {\n depth: state.depth + 1,\n referenceIds: referenceId ? new Set([...state.referenceIds, referenceId]) : state.referenceIds,\n schemas: new Set([...state.schemas, schema]),\n };\n};\n\n/*** Compare JSON-compatible values independently of object key order. */\nconst structurallyEqual = (left: DataContractValue, right: DataContractValue): boolean =>\n JSON.stringify(normalizeValue(left)) === JSON.stringify(normalizeValue(right));\n\n/*** Canonicalize object member ordering for structural comparison. */\nconst normalizeValue = (value: DataContractValue): DataContractValue => {\n if (isDataArray(value)) return value.map(normalizeValue);\n if (isDataRecord(value))\n return Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, item]) => [key, normalizeValue(item)]),\n );\n return value;\n};\n\n/*** Narrow a JSON-compatible object to a non-array member record. */\nconst isDataRecord = (\n value: DataContractValue,\n): value is Readonly<Record<string, DataContractValue>> =>\n value !== null && typeof value === 'object' && !isDataArray(value);\n\n/*** Narrow a JSON-compatible array without losing its serializable member type. */\nconst isDataArray = (value: DataContractValue): value is readonly DataContractValue[] =>\n Array.isArray(value);\n"]}
@@ -0,0 +1,6 @@
1
+ /*** Represents a stable, redaction-safe MCP failure. */
2
+ export declare class McpToolError extends Error {
3
+ readonly code: string;
4
+ constructor(code: string, message: string);
5
+ }
6
+ //# sourceMappingURL=McpToolError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"McpToolError.d.ts","sourceRoot":"","sources":["../../../src/features/mcp/McpToolError.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,qBAAa,YAAa,SAAQ,KAAK;IAEnC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM;CAKlB"}
@@ -0,0 +1,10 @@
1
+ /*** Represents a stable, redaction-safe MCP failure. */
2
+ export class McpToolError extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = 'McpToolError';
8
+ }
9
+ }
10
+ //# sourceMappingURL=McpToolError.js.map
@@ -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"}