@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,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"}
@@ -0,0 +1,220 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { McpToolError } from './McpToolError.js';
3
+ const MAX_DEPTH = 16;
4
+ const TYPES = new Set(['null', 'boolean', 'number', 'integer', 'string', 'array', 'object']);
5
+ const KEYS = new Set([
6
+ '$schema',
7
+ 'title',
8
+ 'description',
9
+ 'type',
10
+ 'const',
11
+ 'enum',
12
+ 'properties',
13
+ 'required',
14
+ 'additionalProperties',
15
+ 'minProperties',
16
+ 'maxProperties',
17
+ 'items',
18
+ 'minItems',
19
+ 'maxItems',
20
+ 'uniqueItems',
21
+ 'minLength',
22
+ 'maxLength',
23
+ 'pattern',
24
+ 'minimum',
25
+ 'maximum',
26
+ 'exclusiveMinimum',
27
+ 'exclusiveMaximum',
28
+ 'multipleOf',
29
+ ]);
30
+ /*** Validates a closed supported JSON Schema and returns its stable argument matcher. */
31
+ export const validateMcpSchema = (schema) => {
32
+ validateSchema(schema, 0);
33
+ return {
34
+ fingerprint: createHash('sha256').update(stableStringify(schema)).digest('hex'),
35
+ matches: (value) => matches(schema, value),
36
+ };
37
+ };
38
+ /*** Rejects unsupported JSON Schema features instead of weakening validation. */
39
+ const validateSchema = (schema, depth) => {
40
+ if (depth > MAX_DEPTH)
41
+ throw new McpToolError('invalid_schema', 'MCP schema exceeds depth limit.');
42
+ if (Object.keys(schema).some((key) => !KEYS.has(key)))
43
+ throw new McpToolError('unsupported_schema', 'The MCP tool schema uses unsupported constraints.');
44
+ const { type } = schema;
45
+ if (typeof type !== 'string' || !TYPES.has(type))
46
+ throw new McpToolError('invalid_schema', 'MCP schemas require one supported type.');
47
+ validateJsonConstraints(schema);
48
+ if (type === 'object')
49
+ validateObject(schema, depth);
50
+ if (type === 'array')
51
+ validateArray(schema, depth);
52
+ if (type === 'string')
53
+ validateString(schema);
54
+ if (type === 'number' || type === 'integer')
55
+ validateNumber(schema);
56
+ };
57
+ /*** Validates portable const and enum declarations. */
58
+ const validateJsonConstraints = (schema) => {
59
+ if ('const' in schema && !isJson(schema.const))
60
+ throw new McpToolError('invalid_schema', 'MCP schema const must be JSON data.');
61
+ if ('enum' in schema &&
62
+ (!Array.isArray(schema.enum) || !schema.enum.length || !schema.enum.every(isJson)))
63
+ throw new McpToolError('invalid_schema', 'MCP schema enum must contain JSON data.');
64
+ };
65
+ /*** Requires a closed object schema with validated declared properties. */
66
+ const validateObject = (schema, depth) => {
67
+ const { properties, required, additionalProperties } = schema;
68
+ if (additionalProperties !== false)
69
+ throw new McpToolError('unsupported_schema', 'MCP object schemas must set additionalProperties to false.');
70
+ if (!isRecord(properties))
71
+ throw new McpToolError('invalid_schema', 'MCP object schemas need property definitions.');
72
+ Object.values(properties).forEach((property) => {
73
+ if (!isRecord(property))
74
+ throw new McpToolError('invalid_schema', 'MCP property schemas must be objects.');
75
+ validateSchema(property, depth + 1);
76
+ });
77
+ if (required !== undefined &&
78
+ (!isUniqueStrings(required) || required.some((key) => !(key in properties))))
79
+ throw new McpToolError('invalid_schema', 'MCP required properties must be declared exactly once.');
80
+ validateBounds(schema.minProperties, schema.maxProperties, 'minProperties', 'maxProperties', true);
81
+ };
82
+ /*** Requires an item schema so every array value is validated. */
83
+ const validateArray = (schema, depth) => {
84
+ if (!isRecord(schema.items))
85
+ throw new McpToolError('unsupported_schema', 'MCP array schemas require an item schema.');
86
+ validateSchema(schema.items, depth + 1);
87
+ if (schema.uniqueItems !== undefined && typeof schema.uniqueItems !== 'boolean')
88
+ throw new McpToolError('invalid_schema', 'MCP uniqueItems must be boolean.');
89
+ validateBounds(schema.minItems, schema.maxItems, 'minItems', 'maxItems', true);
90
+ };
91
+ /*** Validates string bounds and regular-expression patterns. */
92
+ const validateString = (schema) => {
93
+ validateBounds(schema.minLength, schema.maxLength, 'minLength', 'maxLength', true);
94
+ if (schema.pattern !== undefined) {
95
+ if (typeof schema.pattern !== 'string')
96
+ throw new McpToolError('invalid_schema', 'MCP pattern must be a string.');
97
+ try {
98
+ new RegExp(schema.pattern, 'u');
99
+ }
100
+ catch {
101
+ throw new McpToolError('invalid_schema', 'MCP pattern must compile.');
102
+ }
103
+ }
104
+ };
105
+ /*** Validates finite numeric constraints. */
106
+ const validateNumber = (schema) => {
107
+ const values = [
108
+ schema.minimum,
109
+ schema.maximum,
110
+ schema.exclusiveMinimum,
111
+ schema.exclusiveMaximum,
112
+ schema.multipleOf,
113
+ ];
114
+ if (values.some((value) => value !== undefined && (typeof value !== 'number' || !Number.isFinite(value))))
115
+ throw new McpToolError('invalid_schema', 'MCP numeric constraints must be finite numbers.');
116
+ if (typeof schema.multipleOf === 'number' && schema.multipleOf <= 0)
117
+ throw new McpToolError('invalid_schema', 'MCP multipleOf must be positive.');
118
+ };
119
+ /*** Validates ordered numeric or integer bounds. */
120
+ const validateBounds = (minimum, maximum, minimumKey, maximumKey, integers) => {
121
+ const valid = (value) => typeof value === 'number' &&
122
+ Number.isFinite(value) &&
123
+ (!integers || (Number.isSafeInteger(value) && value >= 0));
124
+ if (minimum !== undefined && !valid(minimum))
125
+ throw new McpToolError('invalid_schema', `MCP ${minimumKey} is invalid.`);
126
+ if (maximum !== undefined && !valid(maximum))
127
+ throw new McpToolError('invalid_schema', `MCP ${maximumKey} is invalid.`);
128
+ if (typeof minimum === 'number' && typeof maximum === 'number' && minimum > maximum)
129
+ throw new McpToolError('invalid_schema', `MCP ${minimumKey} cannot exceed ${maximumKey}.`);
130
+ };
131
+ /*** Matches supported JSON Schema constraints without coercion. */
132
+ const matches = (schema, value) => {
133
+ const { type } = schema;
134
+ if (typeof type !== 'string' || !matchesType(type, value))
135
+ return false;
136
+ if ('const' in schema && stableStringify(schema.const) !== stableStringify(value))
137
+ return false;
138
+ if (Array.isArray(schema.enum) &&
139
+ !schema.enum.some((item) => stableStringify(item) === stableStringify(value)))
140
+ return false;
141
+ if (type === 'object')
142
+ return matchesObject(schema, value);
143
+ if (type === 'array')
144
+ return matchesArray(schema, value);
145
+ if (type === 'string')
146
+ return matchesString(schema, value);
147
+ return (type !== 'number' && type !== 'integer') || matchesNumber(schema, value);
148
+ };
149
+ /*** Matches a closed object and each declared property schema. */
150
+ const matchesObject = (schema, value) => {
151
+ const { properties, required } = schema;
152
+ const minProperties = numberOr(schema.minProperties, 0);
153
+ const maxProperties = numberOr(schema.maxProperties, Infinity);
154
+ if (!isRecord(value) || !isRecord(properties))
155
+ return false;
156
+ const entries = Object.entries(value);
157
+ return (entries.length >= minProperties &&
158
+ entries.length <= maxProperties &&
159
+ (!Array.isArray(required) ||
160
+ required.every((key) => typeof key === 'string' && key in value)) &&
161
+ entries.every(([key, item]) => {
162
+ const property = Object.entries(properties).find(([name]) => name === key)?.[1];
163
+ return isRecord(property) && matches(property, item);
164
+ }));
165
+ };
166
+ /*** Matches array items, bounds, and deterministic uniqueness. */
167
+ const matchesArray = (schema, value) => {
168
+ const { items, uniqueItems = false } = schema;
169
+ const minItems = numberOr(schema.minItems, 0);
170
+ const maxItems = numberOr(schema.maxItems, Infinity);
171
+ return (Array.isArray(value) &&
172
+ isRecord(items) &&
173
+ value.length >= minItems &&
174
+ value.length <= maxItems &&
175
+ (!uniqueItems || new Set(value.map(stableStringify)).size === value.length) &&
176
+ value.every((item) => matches(items, item)));
177
+ };
178
+ /*** Matches string constraints. */
179
+ const matchesString = (schema, value) => typeof value === 'string' &&
180
+ [...value].length >= numberOr(schema.minLength, 0) &&
181
+ [...value].length <= numberOr(schema.maxLength, Infinity) &&
182
+ (typeof schema.pattern !== 'string' || new RegExp(schema.pattern, 'u').test(value));
183
+ /*** Matches finite number constraints. */
184
+ const matchesNumber = (schema, value) => (typeof schema.minimum !== 'number' || value >= schema.minimum) &&
185
+ (typeof schema.maximum !== 'number' || value <= schema.maximum) &&
186
+ (typeof schema.exclusiveMinimum !== 'number' || value > schema.exclusiveMinimum) &&
187
+ (typeof schema.exclusiveMaximum !== 'number' || value < schema.exclusiveMaximum) &&
188
+ (typeof schema.multipleOf !== 'number' || Number.isInteger(value / schema.multipleOf));
189
+ /*** Matches a JSON Schema primitive type. */
190
+ const matchesType = (type, value) => (type === 'null' && value === null) ||
191
+ (type === 'array' && Array.isArray(value)) ||
192
+ (type === 'object' && isRecord(value)) ||
193
+ (type === 'integer' && Number.isSafeInteger(value)) ||
194
+ (type === 'number' && typeof value === 'number' && Number.isFinite(value)) ||
195
+ (type === 'string' && typeof value === 'string') ||
196
+ (type === 'boolean' && typeof value === 'boolean');
197
+ /*** Narrows unknown data to a non-array object. */
198
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
199
+ /*** Confirms a value is portable JSON data. */
200
+ const isJson = (value) => value === null ||
201
+ ['string', 'boolean'].includes(typeof value) ||
202
+ (typeof value === 'number' && Number.isFinite(value)) ||
203
+ (Array.isArray(value) && value.every(isJson)) ||
204
+ (isRecord(value) && Object.values(value).every(isJson));
205
+ /*** Confirms a value is an array of unique non-empty strings. */
206
+ const isUniqueStrings = (value) => Array.isArray(value) &&
207
+ value.every((item) => typeof item === 'string' && item.length > 0) &&
208
+ new Set(value).size === value.length;
209
+ /*** Returns a numeric schema value or its validated default. */
210
+ const numberOr = (value, fallback) => typeof value === 'number' ? value : fallback;
211
+ /*** Serializes JSON data deterministically. */
212
+ const stableStringify = (value) => Array.isArray(value)
213
+ ? `[${value.map(stableStringify).join(',')}]`
214
+ : isRecord(value)
215
+ ? `{${Object.entries(value)
216
+ .sort(([left], [right]) => left.localeCompare(right))
217
+ .map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`)
218
+ .join(',')}}`
219
+ : JSON.stringify(value);
220
+ //# sourceMappingURL=validateMcpSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateMcpSchema.js","sourceRoot":"","sources":["../../../src/features/mcp/validateMcpSchema.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\n\nimport { McpToolError } from './McpToolError.js';\n\nconst MAX_DEPTH = 16;\nconst TYPES = new Set(['null', 'boolean', 'number', 'integer', 'string', 'array', 'object']);\nconst KEYS = new Set([\n '$schema',\n 'title',\n 'description',\n 'type',\n 'const',\n 'enum',\n 'properties',\n 'required',\n 'additionalProperties',\n 'minProperties',\n 'maxProperties',\n 'items',\n 'minItems',\n 'maxItems',\n 'uniqueItems',\n 'minLength',\n 'maxLength',\n 'pattern',\n 'minimum',\n 'maximum',\n 'exclusiveMinimum',\n 'exclusiveMaximum',\n 'multipleOf',\n]);\n\n/*** Validates a closed supported JSON Schema and returns its stable argument matcher. */\nexport const validateMcpSchema = (schema: Readonly<Record<string, unknown>>) => {\n validateSchema(schema, 0);\n return {\n fingerprint: createHash('sha256').update(stableStringify(schema)).digest('hex'),\n matches: (value: unknown): boolean => matches(schema, value),\n };\n};\n\n/*** Rejects unsupported JSON Schema features instead of weakening validation. */\nconst validateSchema = (schema: Readonly<Record<string, unknown>>, depth: number): void => {\n if (depth > MAX_DEPTH)\n throw new McpToolError('invalid_schema', 'MCP schema exceeds depth limit.');\n if (Object.keys(schema).some((key) => !KEYS.has(key)))\n throw new McpToolError(\n 'unsupported_schema',\n 'The MCP tool schema uses unsupported constraints.',\n );\n const { type } = schema;\n if (typeof type !== 'string' || !TYPES.has(type))\n throw new McpToolError('invalid_schema', 'MCP schemas require one supported type.');\n validateJsonConstraints(schema);\n if (type === 'object') validateObject(schema, depth);\n if (type === 'array') validateArray(schema, depth);\n if (type === 'string') validateString(schema);\n if (type === 'number' || type === 'integer') validateNumber(schema);\n};\n\n/*** Validates portable const and enum declarations. */\nconst validateJsonConstraints = (schema: Readonly<Record<string, unknown>>): void => {\n if ('const' in schema && !isJson(schema.const))\n throw new McpToolError('invalid_schema', 'MCP schema const must be JSON data.');\n if (\n 'enum' in schema &&\n (!Array.isArray(schema.enum) || !schema.enum.length || !schema.enum.every(isJson))\n )\n throw new McpToolError('invalid_schema', 'MCP schema enum must contain JSON data.');\n};\n\n/*** Requires a closed object schema with validated declared properties. */\nconst validateObject = (schema: Readonly<Record<string, unknown>>, depth: number): void => {\n const { properties, required, additionalProperties } = schema;\n if (additionalProperties !== false)\n throw new McpToolError(\n 'unsupported_schema',\n 'MCP object schemas must set additionalProperties to false.',\n );\n if (!isRecord(properties))\n throw new McpToolError('invalid_schema', 'MCP object schemas need property definitions.');\n Object.values(properties).forEach((property) => {\n if (!isRecord(property))\n throw new McpToolError('invalid_schema', 'MCP property schemas must be objects.');\n validateSchema(property, depth + 1);\n });\n if (\n required !== undefined &&\n (!isUniqueStrings(required) || required.some((key) => !(key in properties)))\n )\n throw new McpToolError(\n 'invalid_schema',\n 'MCP required properties must be declared exactly once.',\n );\n validateBounds(\n schema.minProperties,\n schema.maxProperties,\n 'minProperties',\n 'maxProperties',\n true,\n );\n};\n\n/*** Requires an item schema so every array value is validated. */\nconst validateArray = (schema: Readonly<Record<string, unknown>>, depth: number): void => {\n if (!isRecord(schema.items))\n throw new McpToolError('unsupported_schema', 'MCP array schemas require an item schema.');\n validateSchema(schema.items, depth + 1);\n if (schema.uniqueItems !== undefined && typeof schema.uniqueItems !== 'boolean')\n throw new McpToolError('invalid_schema', 'MCP uniqueItems must be boolean.');\n validateBounds(schema.minItems, schema.maxItems, 'minItems', 'maxItems', true);\n};\n\n/*** Validates string bounds and regular-expression patterns. */\nconst validateString = (schema: Readonly<Record<string, unknown>>): void => {\n validateBounds(schema.minLength, schema.maxLength, 'minLength', 'maxLength', true);\n if (schema.pattern !== undefined) {\n if (typeof schema.pattern !== 'string')\n throw new McpToolError('invalid_schema', 'MCP pattern must be a string.');\n try {\n new RegExp(schema.pattern, 'u');\n } catch {\n throw new McpToolError('invalid_schema', 'MCP pattern must compile.');\n }\n }\n};\n\n/*** Validates finite numeric constraints. */\nconst validateNumber = (schema: Readonly<Record<string, unknown>>): void => {\n const values = [\n schema.minimum,\n schema.maximum,\n schema.exclusiveMinimum,\n schema.exclusiveMaximum,\n schema.multipleOf,\n ];\n if (\n values.some(\n (value) => value !== undefined && (typeof value !== 'number' || !Number.isFinite(value)),\n )\n )\n throw new McpToolError('invalid_schema', 'MCP numeric constraints must be finite numbers.');\n if (typeof schema.multipleOf === 'number' && schema.multipleOf <= 0)\n throw new McpToolError('invalid_schema', 'MCP multipleOf must be positive.');\n};\n\n/*** Validates ordered numeric or integer bounds. */\nconst validateBounds = (\n minimum: unknown,\n maximum: unknown,\n minimumKey: string,\n maximumKey: string,\n integers: boolean,\n): void => {\n const valid = (value: unknown): value is number =>\n typeof value === 'number' &&\n Number.isFinite(value) &&\n (!integers || (Number.isSafeInteger(value) && value >= 0));\n if (minimum !== undefined && !valid(minimum))\n throw new McpToolError('invalid_schema', `MCP ${minimumKey} is invalid.`);\n if (maximum !== undefined && !valid(maximum))\n throw new McpToolError('invalid_schema', `MCP ${maximumKey} is invalid.`);\n if (typeof minimum === 'number' && typeof maximum === 'number' && minimum > maximum)\n throw new McpToolError('invalid_schema', `MCP ${minimumKey} cannot exceed ${maximumKey}.`);\n};\n\n/*** Matches supported JSON Schema constraints without coercion. */\nconst matches = (schema: Readonly<Record<string, unknown>>, value: unknown): boolean => {\n const { type } = schema;\n if (typeof type !== 'string' || !matchesType(type, value)) return false;\n if ('const' in schema && stableStringify(schema.const) !== stableStringify(value)) return false;\n if (\n Array.isArray(schema.enum) &&\n !schema.enum.some((item) => stableStringify(item) === stableStringify(value))\n )\n return false;\n if (type === 'object') return matchesObject(schema, value);\n if (type === 'array') return matchesArray(schema, value);\n if (type === 'string') return matchesString(schema, value);\n return (type !== 'number' && type !== 'integer') || matchesNumber(schema, value as number);\n};\n\n/*** Matches a closed object and each declared property schema. */\nconst matchesObject = (schema: Readonly<Record<string, unknown>>, value: unknown): boolean => {\n const { properties, required } = schema;\n const minProperties = numberOr(schema.minProperties, 0);\n const maxProperties = numberOr(schema.maxProperties, Infinity);\n if (!isRecord(value) || !isRecord(properties)) return false;\n const entries = Object.entries(value);\n return (\n entries.length >= minProperties &&\n entries.length <= maxProperties &&\n (!Array.isArray(required) ||\n required.every((key) => typeof key === 'string' && key in value)) &&\n entries.every(([key, item]) => {\n const property = Object.entries(properties).find(([name]) => name === key)?.[1];\n return isRecord(property) && matches(property, item);\n })\n );\n};\n\n/*** Matches array items, bounds, and deterministic uniqueness. */\nconst matchesArray = (schema: Readonly<Record<string, unknown>>, value: unknown): boolean => {\n const { items, uniqueItems = false } = schema;\n const minItems = numberOr(schema.minItems, 0);\n const maxItems = numberOr(schema.maxItems, Infinity);\n return (\n Array.isArray(value) &&\n isRecord(items) &&\n value.length >= minItems &&\n value.length <= maxItems &&\n (!uniqueItems || new Set(value.map(stableStringify)).size === value.length) &&\n value.every((item) => matches(items, item))\n );\n};\n\n/*** Matches string constraints. */\nconst matchesString = (schema: Readonly<Record<string, unknown>>, value: unknown): boolean =>\n typeof value === 'string' &&\n [...value].length >= numberOr(schema.minLength, 0) &&\n [...value].length <= numberOr(schema.maxLength, Infinity) &&\n (typeof schema.pattern !== 'string' || new RegExp(schema.pattern, 'u').test(value));\n\n/*** Matches finite number constraints. */\nconst matchesNumber = (schema: Readonly<Record<string, unknown>>, value: number): boolean =>\n (typeof schema.minimum !== 'number' || value >= schema.minimum) &&\n (typeof schema.maximum !== 'number' || value <= schema.maximum) &&\n (typeof schema.exclusiveMinimum !== 'number' || value > schema.exclusiveMinimum) &&\n (typeof schema.exclusiveMaximum !== 'number' || value < schema.exclusiveMaximum) &&\n (typeof schema.multipleOf !== 'number' || Number.isInteger(value / schema.multipleOf));\n\n/*** Matches a JSON Schema primitive type. */\nconst matchesType = (type: string, value: unknown): boolean =>\n (type === 'null' && value === null) ||\n (type === 'array' && Array.isArray(value)) ||\n (type === 'object' && isRecord(value)) ||\n (type === 'integer' && Number.isSafeInteger(value)) ||\n (type === 'number' && typeof value === 'number' && Number.isFinite(value)) ||\n (type === 'string' && typeof value === 'string') ||\n (type === 'boolean' && typeof value === 'boolean');\n\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/*** Confirms a value is portable JSON data. */\nconst isJson = (value: unknown): boolean =>\n value === null ||\n ['string', 'boolean'].includes(typeof value) ||\n (typeof value === 'number' && Number.isFinite(value)) ||\n (Array.isArray(value) && value.every(isJson)) ||\n (isRecord(value) && Object.values(value).every(isJson));\n/*** Confirms a value is an array of unique non-empty strings. */\nconst isUniqueStrings = (value: unknown): value is readonly string[] =>\n Array.isArray(value) &&\n value.every((item) => typeof item === 'string' && item.length > 0) &&\n new Set(value).size === value.length;\n/*** Returns a numeric schema value or its validated default. */\nconst numberOr = (value: unknown, fallback: number): number =>\n typeof value === 'number' ? value : fallback;\n/*** Serializes JSON data deterministically. */\nconst stableStringify = (value: unknown): string =>\n Array.isArray(value)\n ? `[${value.map(stableStringify).join(',')}]`\n : isRecord(value)\n ? `{${Object.entries(value)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`)\n .join(',')}}`\n : JSON.stringify(value);\n"]}
@@ -0,0 +1,6 @@
1
+ /*** Represents a stable, payload-redacted Flow security denial. */
2
+ export declare class FlowSecurityError extends Error {
3
+ readonly code: 'approval_pending' | 'approval_rejected' | 'idempotency_conflict' | 'invalid_target' | 'permission_denied' | 'rate_limited' | 'secret_unavailable';
4
+ constructor(code: 'approval_pending' | 'approval_rejected' | 'idempotency_conflict' | 'invalid_target' | 'permission_denied' | 'rate_limited' | 'secret_unavailable', message: string);
5
+ }
6
+ //# sourceMappingURL=FlowSecurityError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FlowSecurityError.d.ts","sourceRoot":"","sources":["../../../src/features/security/FlowSecurityError.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,qBAAa,iBAAkB,SAAQ,KAAK;aAExB,IAAI,EAChB,kBAAkB,GAClB,mBAAmB,GACnB,sBAAsB,GACtB,gBAAgB,GAChB,mBAAmB,GACnB,cAAc,GACd,oBAAoB;gBAPR,IAAI,EAChB,kBAAkB,GAClB,mBAAmB,GACnB,sBAAsB,GACtB,gBAAgB,GAChB,mBAAmB,GACnB,cAAc,GACd,oBAAoB,EACxB,OAAO,EAAE,MAAM;CAKlB"}
@@ -0,0 +1,10 @@
1
+ /*** Represents a stable, payload-redacted Flow security denial. */
2
+ export class FlowSecurityError extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = 'FlowSecurityError';
8
+ }
9
+ }
10
+ //# sourceMappingURL=FlowSecurityError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FlowSecurityError.js","sourceRoot":"","sources":["../../../src/features/security/FlowSecurityError.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAExB;IADlB,YACkB,IAOQ,EACxB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAVC,SAAI,GAAJ,IAAI,CAOI;QAIxB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF","sourcesContent":["/*** Represents a stable, payload-redacted Flow security denial. */\nexport class FlowSecurityError extends Error {\n public constructor(\n public readonly code:\n | 'approval_pending'\n | 'approval_rejected'\n | 'idempotency_conflict'\n | 'invalid_target'\n | 'permission_denied'\n | 'rate_limited'\n | 'secret_unavailable',\n message: string,\n ) {\n super(message);\n this.name = 'FlowSecurityError';\n }\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import type { SecretStoreAdapter } from '@ankhorage/contracts/secrets';
2
+ import type { FlowApprovalStorePort, FlowOperationStorePort, FlowRateLimitPort, FlowSecurityAuditPort, FlowSecurityGrant, FlowSecurityPort } from '../../../types/security.js';
3
+ /*** Creates the provider-neutral server-side authorization, approval, audit, and secret boundary. */
4
+ export declare const createFlowSecurityPort: (input: {
5
+ readonly approvalStore: FlowApprovalStorePort;
6
+ readonly audit: FlowSecurityAuditPort;
7
+ readonly grants: readonly FlowSecurityGrant[];
8
+ readonly operationStore: FlowOperationStorePort;
9
+ readonly rateLimit: FlowRateLimitPort;
10
+ readonly secretStore: SecretStoreAdapter;
11
+ }) => FlowSecurityPort;
12
+ //# sourceMappingURL=createFlowSecurityPort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createFlowSecurityPort.d.ts","sourceRoot":"","sources":["../../../../src/features/security/application/createFlowSecurityPort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElF,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EAOjB,qBAAqB,EACrB,iBAAiB,EAIjB,gBAAgB,EAEjB,MAAM,4BAA4B,CAAC;AAYpC,qGAAqG;AACrG,eAAO,MAAM,sBAAsB,GAAI,OAAO;IAC5C,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC;IAC9C,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC9C,QAAQ,CAAC,cAAc,EAAE,sBAAsB,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;CAC1C,KAAG,gBAKF,CAAC"}