@safe-shape/mcp 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +21 -0
  2. package/README.ru.md +21 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +34 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/handler.d.ts +10 -0
  8. package/dist/handler.d.ts.map +1 -0
  9. package/dist/handler.js +51 -0
  10. package/dist/handler.js.map +1 -0
  11. package/dist/index.d.ts +10 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +6 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/inspection.d.ts +8 -0
  16. package/dist/inspection.d.ts.map +1 -0
  17. package/dist/inspection.js +90 -0
  18. package/dist/inspection.js.map +1 -0
  19. package/dist/json.d.ts +14 -0
  20. package/dist/json.d.ts.map +1 -0
  21. package/dist/json.js +58 -0
  22. package/dist/json.js.map +1 -0
  23. package/dist/limits.d.ts +18 -0
  24. package/dist/limits.d.ts.map +1 -0
  25. package/dist/limits.js +56 -0
  26. package/dist/limits.js.map +1 -0
  27. package/dist/manifest.d.ts +5 -0
  28. package/dist/manifest.d.ts.map +1 -0
  29. package/dist/manifest.js +58 -0
  30. package/dist/manifest.js.map +1 -0
  31. package/dist/registry.d.ts +5 -0
  32. package/dist/registry.d.ts.map +1 -0
  33. package/dist/registry.js +47 -0
  34. package/dist/registry.js.map +1 -0
  35. package/dist/server.d.ts +8 -0
  36. package/dist/server.d.ts.map +1 -0
  37. package/dist/server.js +68 -0
  38. package/dist/server.js.map +1 -0
  39. package/dist/stdio.d.ts +14 -0
  40. package/dist/stdio.d.ts.map +1 -0
  41. package/dist/stdio.js +68 -0
  42. package/dist/stdio.js.map +1 -0
  43. package/dist/tool.d.ts +31 -0
  44. package/dist/tool.d.ts.map +1 -0
  45. package/dist/tool.js +50 -0
  46. package/dist/tool.js.map +1 -0
  47. package/dist/types.d.ts +20 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/version.d.ts +2 -0
  52. package/dist/version.d.ts.map +1 -0
  53. package/dist/version.js +20 -0
  54. package/dist/version.js.map +1 -0
  55. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # @safe-shape/mcp
2
+
3
+ **English** | [Русский](https://github.com/dev-ik/safe_shape/blob/v3.5.0/packages/mcp/README.ru.md)
4
+
5
+ Runtime contracts for AI tools and a local MCP inspection server. Node >=20.10, ESM.
6
+ Install this package independently for a narrower setup, or install `safe-shape`
7
+ and import these APIs from `safe-shape/mcp`. Core remains SDK-independent; the
8
+ umbrella installs MCP/SDK transitively without loading them through its main entry.
9
+
10
+ ```sh
11
+ npm install @safe-shape/mcp @safe-shape/core
12
+ npx --no-install safe-shape-mcp --workspace . --manifest examples/mcp.manifest.json
13
+ ```
14
+
15
+ Export SafeShape input/output contracts, validate tool boundaries and inspect
16
+ registered contracts over stdio. Invalid arguments never reach the handler;
17
+ invalid results never become successful responses. Trusted schema modules run
18
+ with process permissions; path checks are not a JavaScript sandbox. Deadlines
19
+ are cooperative. No automatic retries or baseline writes.
20
+
21
+ [Guide](https://github.com/dev-ik/safe_shape/blob/v3.5.0/docs/mcp.md) · [API](https://github.com/dev-ik/safe_shape/blob/v3.5.0/docs/api/mcp.md)
package/README.ru.md ADDED
@@ -0,0 +1,21 @@
1
+ # @safe-shape/mcp
2
+
3
+ [English](https://github.com/dev-ik/safe_shape/blob/v3.5.0/packages/mcp/README.md) | **Русский**
4
+
5
+ Контракты для AI-инструментов и локальный MCP-сервер проверки. Node >=20.10, ESM.
6
+ Пакет можно установить отдельно либо получить через `safe-shape` и импортировать
7
+ API из `safe-shape/mcp`. Core не зависит от SDK; umbrella устанавливает MCP/SDK
8
+ транзитивно, но основной импорт их не загружает.
9
+
10
+ ```sh
11
+ npm install @safe-shape/mcp @safe-shape/core
12
+ npx --no-install safe-shape-mcp --workspace . --manifest examples/mcp.manifest.json
13
+ ```
14
+
15
+ Экспортируйте схемы входа/выхода, проверяйте границы инструментов и контракты
16
+ через stdio. Неверные аргументы не доходят до обработчика; неверный результат
17
+ не становится успешным ответом. Доверенные модули работают с правами процесса;
18
+ проверка путей не является песочницей. Deadline кооперативен. Нет автоматических
19
+ повторов и записи baseline.
20
+
21
+ [Руководство](https://github.com/dev-ik/safe_shape/blob/v3.5.0/docs/ru/mcp.md) · [API](https://github.com/dev-ik/safe_shape/blob/v3.5.0/docs/ru/api/mcp.md)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import { loadMcpManifest } from './manifest.js';
3
+ import { BoundedStdioTransport } from './stdio.js';
4
+ import { createSafeShapeMcpServer } from './server.js';
5
+ async function main() {
6
+ const args = process.argv.slice(2);
7
+ if (args.length === 1 && args[0] === '--help') {
8
+ process.stderr.write('safe-shape-mcp --workspace <root> --manifest <file>\n');
9
+ return;
10
+ }
11
+ if (args.length !== 4)
12
+ throw new TypeError('Expected workspace and manifest.');
13
+ const flags = new Map();
14
+ for (let i = 0; i < args.length; i += 2) {
15
+ if (!['--workspace', '--manifest'].includes(args[i]) || flags.has(args[i]))
16
+ throw new TypeError('Invalid arguments.');
17
+ flags.set(args[i], args[i + 1]);
18
+ }
19
+ if (!flags.has('--workspace') || !flags.has('--manifest'))
20
+ throw new TypeError('Missing arguments.');
21
+ const options = await loadMcpManifest(flags.get('--workspace'), flags.get('--manifest'));
22
+ const server = createSafeShapeMcpServer(options);
23
+ const transport = new BoundedStdioTransport(process.stdin, options.limits);
24
+ transport.onerror = () => { process.stderr.write('MCP protocol request failed.\n'); };
25
+ let stopping = false;
26
+ const stop = () => { if (stopping)
27
+ return; stopping = true; void server.close(); process.stdin.destroy(); };
28
+ transport.onclose = stop;
29
+ process.once('SIGINT', stop);
30
+ process.once('SIGTERM', stop);
31
+ await server.connect(transport);
32
+ }
33
+ void main().catch(() => { process.stderr.write('SafeShape MCP startup failed. Check the manifest and trusted schema modules.\n'); process.exitCode = 1; });
34
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAAC,OAAO;IAAC,CAAC;IACzI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACxH,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACrG,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAE,EAAE,KAAK,CAAC,GAAG,CAAC,YAAY,CAAE,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,IAAI,qBAAqB,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,IAAI,GAAG,GAAG,EAAE,GAAG,IAAI,QAAQ;QAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5G,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC5D,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AACD,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gFAAgF,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import type { McpTool } from './tool.js';
3
+ import { type McpLimits } from './limits.js';
4
+ export interface McpHandlerContext {
5
+ readonly signal?: AbortSignal;
6
+ }
7
+ export declare function createValidatedMcpHandler<I, RawI, O, RawO>(tool: McpTool<I, RawI, O, RawO>, handler: (value: NoInfer<I>, context: {
8
+ readonly signal: AbortSignal;
9
+ }) => NoInfer<RawO> | Promise<NoInfer<RawO>>, limits?: Partial<McpLimits>): (argumentsValue: unknown, context?: McpHandlerContext) => Promise<CallToolResult>;
10
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACxF,MAAM,WAAW,iBAAiB;IAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE;AACpE,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EACxD,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAClJ,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAC1B,CAAC,cAAc,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,cAAc,CAAC,CA+BnF"}
@@ -0,0 +1,51 @@
1
+ import { validateSchemaAsync } from '@safe-shape/validation';
2
+ import { copyJson, jsonBytes, McpOperationError, publicError } from './json.js';
3
+ import { checkSignal, ExecutionGate, resolveLimits } from './limits.js';
4
+ export function createValidatedMcpHandler(tool, handler, limits) {
5
+ const gate = new ExecutionGate(resolveLimits(limits));
6
+ return async (argumentsValue, context = {}) => {
7
+ try {
8
+ return await gate.run(async (signal) => {
9
+ copyJson(argumentsValue, gate.limits.depth, false);
10
+ const input = argumentsValue;
11
+ if (jsonBytes(input) > gate.limits.requestBytes)
12
+ throw new McpOperationError('request_too_large', 'Input size limit exceeded.');
13
+ const parsed = await validateSchemaAsync(tool.input, input);
14
+ checkSignal(signal);
15
+ if (!parsed.valid)
16
+ throw new McpOperationError('argument_validation_failed', 'Tool arguments failed validation.', parsed.issues);
17
+ const value = await handler(parsed.data, { signal });
18
+ checkSignal(signal);
19
+ // Inspect raw results before parsing so getters cannot be invoked by a schema.
20
+ copyJson(value, gate.limits.depth, false);
21
+ const raw = value;
22
+ const output = await validateSchemaAsync(tool.output, raw);
23
+ checkSignal(signal);
24
+ if (!output.valid)
25
+ throw new McpOperationError('result_validation_failed', 'Tool result failed validation.', output.issues);
26
+ copyJson(output.data, gate.limits.depth, false);
27
+ const data = output.data;
28
+ if (!data || typeof data !== 'object' || Array.isArray(data))
29
+ throw new McpOperationError('result_object_required', 'MCP structured results must be objects.');
30
+ const result = Object.freeze({ content: Object.freeze([Object.freeze({ type: 'text', text: JSON.stringify(data) })]), structuredContent: data, ...((parsed.warnings?.length || output.warnings?.length) ? { _meta: copyJson({ 'safe-shape/warnings': { input: parsed.warnings ?? [], output: output.warnings ?? [] } }, gate.limits.depth + 6) } : {}) });
31
+ if (jsonBytes(result) > gate.limits.responseBytes)
32
+ throw new McpOperationError('response_too_large', 'Result size limit exceeded.');
33
+ return result;
34
+ }, context.signal);
35
+ }
36
+ catch (error) {
37
+ let safe;
38
+ try {
39
+ safe = copyJson(publicError(error), gate.limits.depth + 6);
40
+ }
41
+ catch {
42
+ safe = { code: 'serialization_failed', message: 'Diagnostics are not lossless JSON.' };
43
+ }
44
+ const result = copyJson({ content: [{ type: 'text', text: JSON.stringify(safe) }], isError: true }, gate.limits.depth + 6);
45
+ if (jsonBytes(result) > gate.limits.responseBytes)
46
+ throw new McpOperationError('response_too_large', 'Error cannot fit response limit.');
47
+ return result;
48
+ }
49
+ };
50
+ }
51
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { createMcpContractRegistry } from "./registry.js";
2
+ export type { McpContractEntry, McpContractRegistry, McpToolCatalogEntry } from "./types.js";
3
+ export { defineMcpTool, safeToMcpToolDefinition } from './tool.js';
4
+ export type { McpTool, McpToolDefinition, McpToolExportResult, McpProfileIssue } from './tool.js';
5
+ export { createValidatedMcpHandler } from './handler.js';
6
+ export type { McpHandlerContext } from './handler.js';
7
+ export { DEFAULT_MCP_LIMITS } from './limits.js';
8
+ export type { McpLimits } from './limits.js';
9
+ export { createSafeShapeMcpServer } from './server.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACnE,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { createMcpContractRegistry } from "./registry.js";
2
+ export { defineMcpTool, safeToMcpToolDefinition } from './tool.js';
3
+ export { createValidatedMcpHandler } from './handler.js';
4
+ export { DEFAULT_MCP_LIMITS } from './limits.js';
5
+ export { createSafeShapeMcpServer } from './server.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAEnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import type { McpContractRegistry } from './types.js';
3
+ import type { Schema } from '@safe-shape/core';
4
+ export type InspectionName = 'list_contracts' | 'describe_contract' | 'export_json_schema' | 'validate_data' | 'compare_contracts' | 'export_tool_definition';
5
+ export declare const inspectionSchemas: Readonly<Record<InspectionName, Schema<any, any>>>;
6
+ export declare function inspectionDefinitions(): readonly Tool[];
7
+ export declare function createInspection(registry: McpContractRegistry): (name: keyof typeof inspectionSchemas, args: any) => Promise<unknown>;
8
+ //# sourceMappingURL=inspection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspection.d.ts","sourceRoot":"","sources":["../src/inspection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,eAAe,GAAG,mBAAmB,GAAG,wBAAwB,CAAC;AAG9J,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAO/E,CAAC;AAaH,wBAAgB,qBAAqB,IAAI,SAAS,IAAI,EAAE,CAOvD;AACD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,IAO9C,MAAM,MAAM,OAAO,iBAAiB,EAAE,MAAM,GAAG,KAAG,OAAO,CAAC,OAAO,CAAC,CAsCjF"}
@@ -0,0 +1,90 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { object, string, integer, unknown as unknownSchema, enum as enumSchema, boolean, array, union, literal, describeContract } from '@safe-shape/core';
3
+ import { toJsonSchema, safeToJsonSchema } from '@safe-shape/json-schema';
4
+ import { validateSchemaAsync } from '@safe-shape/validation';
5
+ import { compareContractsV2, createMigrationDiagnostics } from '@safe-shape/compat';
6
+ import { McpOperationError } from './json.js';
7
+ import { defineMcpTool, safeToMcpToolDefinition } from './tool.js';
8
+ const id = string({ minLength: 1, maxLength: 128 });
9
+ const side = enumSchema(['input', 'output']).optional();
10
+ export const inspectionSchemas = Object.freeze({
11
+ list_contracts: object({ cursor: string({ maxLength: 256 }).optional(), limit: integer({ minimum: 1, maximum: 100 }).optional() }),
12
+ describe_contract: object({ contractId: id }),
13
+ export_json_schema: object({ contractId: id, side, target: enumSchema(['draft-2020-12', 'draft-07']).optional() }),
14
+ validate_data: object({ contractId: id, value: unknownSchema() }),
15
+ compare_contracts: object({ previousId: id, nextId: id, mode: enumSchema(['backward', 'forward', 'full']).optional(), side }),
16
+ export_tool_definition: object({ toolId: id }),
17
+ });
18
+ const messages = {
19
+ list_contracts: 'Discover registered SafeShape contract IDs before inspecting or validating them.',
20
+ describe_contract: 'Inspect the input/output contract graphs and supported operations for a registered contract.',
21
+ export_json_schema: 'Export an exact JSON Schema for a registered contract; unsupported constraints produce diagnostics.',
22
+ validate_data: 'Validate inline JSON against a registered contract and return parsed data or native issues for correction.',
23
+ compare_contracts: 'Check directional compatibility between registered contract versions without changing baselines.',
24
+ export_tool_definition: 'Export MCP input/output schemas for a tool in the developer-configured catalog.',
25
+ };
26
+ const envelope = union([
27
+ object({ ok: literal(true), operation: string(), result: unknownSchema() }),
28
+ object({ ok: literal(false), operation: string(), error: object({ code: string(), message: string(), issues: unknownSchema().optional() }) }),
29
+ ]);
30
+ export function inspectionDefinitions() {
31
+ const schema = toJsonSchema(envelope);
32
+ return Object.freeze(Object.keys(inspectionSchemas).sort().map(name => ({
33
+ name, description: messages[name], inputSchema: toJsonSchema(inspectionSchemas[name]),
34
+ outputSchema: { ...schema, type: 'object' },
35
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
36
+ })));
37
+ }
38
+ export function createInspection(registry) {
39
+ const nonce = randomUUID();
40
+ const get = (contractId) => {
41
+ const entry = registry.getContract(contractId);
42
+ if (!entry)
43
+ throw new McpOperationError('unknown_contract_id', 'Contract ID is not registered.');
44
+ return entry;
45
+ };
46
+ return async (name, args) => {
47
+ switch (name) {
48
+ case 'list_contracts': {
49
+ let start = 0;
50
+ if (args.cursor !== undefined) {
51
+ const decoded = Buffer.from(args.cursor, 'base64url').toString('utf8');
52
+ const prefix = `${nonce}:`;
53
+ const offset = decoded.slice(prefix.length);
54
+ if (!decoded.startsWith(prefix) || !/^[1-9][0-9]*$/.test(offset) || Buffer.from(decoded).toString('base64url') !== args.cursor)
55
+ throw new McpOperationError('invalid_cursor', 'Cursor is invalid or stale.');
56
+ start = Number(offset);
57
+ if (!Number.isSafeInteger(start) || start >= registry.contracts.length)
58
+ throw new McpOperationError('invalid_cursor', 'Cursor is invalid or stale.');
59
+ }
60
+ const end = start + (args.limit ?? 20);
61
+ return { contracts: registry.contracts.slice(start, end).map(({ id, description }) => ({ id, description })), ...(end < registry.contracts.length ? { nextCursor: Buffer.from(`${nonce}:${end}`).toString('base64url') } : {}) };
62
+ }
63
+ case 'describe_contract': {
64
+ const entry = get(args.contractId);
65
+ return { id: entry.id, description: entry.description, contract: describeContract(entry.schema), operations: { validate: true, exportInput: safeToJsonSchema(entry.schema, { side: 'input' }).success, exportOutput: safeToJsonSchema(entry.schema, { side: 'output' }).success, compare: true } };
66
+ }
67
+ case 'export_json_schema': {
68
+ const result = safeToJsonSchema(get(args.contractId).schema, { side: args.side ?? 'input', target: args.target ?? 'draft-2020-12', mode: 'exact' });
69
+ if (!result.success)
70
+ throw new McpOperationError('json_schema_export_failed', 'Contract cannot be exported exactly.', result.issues);
71
+ return { schema: result.schema, warnings: result.warnings };
72
+ }
73
+ case 'validate_data': return validateSchemaAsync(get(args.contractId).schema, args.value);
74
+ case 'compare_contracts': {
75
+ const report = compareContractsV2(get(args.previousId).schema, get(args.nextId).schema, { compatibility: args.mode ?? 'backward', side: args.side ?? 'input' });
76
+ return { report, migration: createMigrationDiagnostics(report) };
77
+ }
78
+ case 'export_tool_definition': {
79
+ const entry = registry.getTool(args.toolId);
80
+ if (!entry)
81
+ throw new McpOperationError('unknown_tool_id', 'Tool ID is not registered.');
82
+ const result = safeToMcpToolDefinition(defineMcpTool({ name: entry.name, description: entry.description, input: get(entry.inputId).schema, output: get(entry.outputId).schema }));
83
+ if (!result.success)
84
+ throw new McpOperationError('tool_definition_export_failed', 'Tool definition cannot be exported exactly.', result.issues);
85
+ return { definition: result.definition, warnings: result.warnings };
86
+ }
87
+ }
88
+ };
89
+ }
90
+ //# sourceMappingURL=inspection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspection.js","sourceRoot":"","sources":["../src/inspection.ts"],"names":[],"mappings":"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"}
package/dist/json.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare class McpOperationError extends Error {
2
+ readonly code: string;
3
+ readonly issues?: unknown | undefined;
4
+ constructor(code: string, message: string, issues?: unknown | undefined);
5
+ }
6
+ /** Copy JSON data without invoking getters, toJSON or lossy serialization. */
7
+ export declare function copyJson(value: unknown, maxDepth?: number, freeze?: boolean): any;
8
+ export declare function jsonBytes(value: unknown): number;
9
+ export declare function publicError(error: unknown): {
10
+ code: string;
11
+ message: string;
12
+ issues?: unknown;
13
+ };
14
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA,qBAAa,iBAAkB,SAAQ,KAAK;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM;IAAmB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO;gBAAxD,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAW,MAAM,CAAC,EAAE,OAAO,YAAA;CAC9E;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAM,EAAE,MAAM,UAAO,GAAG,GAAG,CA2B3E;AACD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAA6D;AAC9G,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAG/F"}
package/dist/json.js ADDED
@@ -0,0 +1,58 @@
1
+ export class McpOperationError extends Error {
2
+ code;
3
+ issues;
4
+ constructor(code, message, issues) {
5
+ super(message);
6
+ this.code = code;
7
+ this.issues = issues;
8
+ }
9
+ }
10
+ /** Copy JSON data without invoking getters, toJSON or lossy serialization. */
11
+ export function copyJson(value, maxDepth = 128, freeze = true) {
12
+ const active = new Set();
13
+ function visit(item, depth) {
14
+ if (depth > maxDepth)
15
+ throw new McpOperationError('depth_exceeded', 'JSON nesting limit exceeded.');
16
+ if (item === null || typeof item === 'string' || typeof item === 'boolean')
17
+ return item;
18
+ if (typeof item === 'number' && Number.isFinite(item))
19
+ return item;
20
+ if (typeof item !== 'object' || item === null || active.has(item))
21
+ throw new McpOperationError('serialization_failed', 'Value is not lossless JSON.');
22
+ const array = Array.isArray(item);
23
+ if (!array && Object.getPrototypeOf(item) !== Object.prototype && Object.getPrototypeOf(item) !== null)
24
+ throw new McpOperationError('serialization_failed', 'Only plain JSON objects are supported.');
25
+ active.add(item);
26
+ try {
27
+ const keys = Reflect.ownKeys(item);
28
+ const result = array ? [] : Object.create(null);
29
+ for (const key of keys) {
30
+ if (array && key === 'length')
31
+ continue;
32
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
33
+ if (typeof key !== 'string' || !descriptor.enumerable || !('value' in descriptor))
34
+ throw new McpOperationError('serialization_failed', 'Only enumerable JSON data properties are supported.');
35
+ if (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= item.length))
36
+ throw new McpOperationError('serialization_failed', 'Array has non-index properties.');
37
+ Object.defineProperty(result, key, { value: visit(descriptor.value, depth + 1), enumerable: true, writable: true, configurable: true });
38
+ }
39
+ if (array && keys.length - 1 !== item.length)
40
+ throw new McpOperationError('serialization_failed', 'Sparse arrays are not JSON.');
41
+ // Ordinary objects preserve native diagnostic/report shapes, including __proto__ data keys.
42
+ if (!array)
43
+ Object.setPrototypeOf(result, Object.prototype);
44
+ return freeze ? Object.freeze(result) : result;
45
+ }
46
+ finally {
47
+ active.delete(item);
48
+ }
49
+ }
50
+ return visit(value, 0);
51
+ }
52
+ export function jsonBytes(value) { return Buffer.byteLength(JSON.stringify(value), 'utf8'); }
53
+ export function publicError(error) {
54
+ if (error instanceof McpOperationError)
55
+ return { code: error.code, message: error.message, ...(error.issues === undefined ? {} : { issues: error.issues }) };
56
+ return { code: 'execution_failed', message: 'Operation failed.' };
57
+ }
58
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface McpLimits {
2
+ readonly requestBytes: number;
3
+ readonly depth: number;
4
+ readonly responseBytes: number;
5
+ readonly concurrency: number;
6
+ readonly deadlineMs: number;
7
+ }
8
+ export declare const DEFAULT_MCP_LIMITS: McpLimits;
9
+ export declare function resolveLimits(input?: Partial<McpLimits>): McpLimits;
10
+ export declare function checkSignal(signal: AbortSignal): void;
11
+ /** A response deadline never releases a slot occupied by an unfinished callback. */
12
+ export declare class ExecutionGate {
13
+ readonly limits: McpLimits;
14
+ private active;
15
+ constructor(limits: McpLimits);
16
+ run<T>(work: (signal: AbortSignal) => Promise<T>, external?: AbortSignal): Promise<T>;
17
+ }
18
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,SAAS;IAAG,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAC/K,eAAO,MAAM,kBAAkB,EAAE,SAA4H,CAAC;AAC9J,wBAAgB,aAAa,CAAC,KAAK,GAAE,OAAO,CAAC,SAAS,CAAM,GAAG,SAAS,CAMvE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAIrD;AACD,oFAAoF;AACpF,qBAAa,aAAa;IAEZ,QAAQ,CAAC,MAAM,EAAE,SAAS;IADtC,OAAO,CAAC,MAAM,CAAK;gBACE,MAAM,EAAE,SAAS;IAChC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;CAiB5F"}
package/dist/limits.js ADDED
@@ -0,0 +1,56 @@
1
+ import { McpOperationError } from './json.js';
2
+ export const DEFAULT_MCP_LIMITS = Object.freeze({ requestBytes: 1048576, depth: 128, responseBytes: 4194304, concurrency: 16, deadlineMs: 30000 });
3
+ export function resolveLimits(input = {}) {
4
+ if (!input || typeof input !== 'object' || Object.keys(input).some(k => !Object.hasOwn(DEFAULT_MCP_LIMITS, k)))
5
+ throw new TypeError('Invalid limits.');
6
+ const result = { ...DEFAULT_MCP_LIMITS, ...input };
7
+ for (const value of Object.values(result))
8
+ if (!Number.isSafeInteger(value) || value <= 0)
9
+ throw new TypeError('Limits must be positive safe integers.');
10
+ if (result.deadlineMs > 2147483647)
11
+ throw new TypeError('Deadline exceeds timer range.');
12
+ return Object.freeze(result);
13
+ }
14
+ const deadlines = new WeakMap();
15
+ export function checkSignal(signal) {
16
+ const deadline = deadlines.get(signal);
17
+ if (deadline && performance.now() >= deadline.end) {
18
+ deadline.abort();
19
+ throw new McpOperationError('deadline_exceeded', 'Operation deadline exceeded.');
20
+ }
21
+ if (signal.aborted)
22
+ throw new McpOperationError('cancelled', 'Operation cancelled or timed out.');
23
+ }
24
+ /** A response deadline never releases a slot occupied by an unfinished callback. */
25
+ export class ExecutionGate {
26
+ limits;
27
+ active = 0;
28
+ constructor(limits) {
29
+ this.limits = limits;
30
+ }
31
+ async run(work, external) {
32
+ if (this.active >= this.limits.concurrency)
33
+ throw new McpOperationError('capacity_exceeded', 'Operation capacity exceeded.');
34
+ this.active++;
35
+ const controller = new AbortController();
36
+ deadlines.set(controller.signal, { end: performance.now() + this.limits.deadlineMs, abort: () => controller.abort() });
37
+ let rejectAbort;
38
+ const abort = new Promise((_, reject) => { rejectAbort = reject; });
39
+ const cancel = () => { controller.abort(); rejectAbort(new McpOperationError('cancelled', 'Operation cancelled.')); };
40
+ external?.addEventListener('abort', cancel, { once: true });
41
+ const timer = setTimeout(() => { controller.abort(); rejectAbort(new McpOperationError('deadline_exceeded', 'Operation deadline exceeded.')); }, this.limits.deadlineMs);
42
+ if (external?.aborted)
43
+ cancel();
44
+ const running = Promise.resolve().then(() => { checkSignal(controller.signal); return work(controller.signal); }).then(value => { checkSignal(controller.signal); return value; });
45
+ // Attach both settlement handlers even when the caller stops waiting.
46
+ void running.then(() => { this.active--; deadlines.delete(controller.signal); }, () => { this.active--; deadlines.delete(controller.signal); });
47
+ try {
48
+ return await Promise.race([running, abort]);
49
+ }
50
+ finally {
51
+ clearTimeout(timer);
52
+ external?.removeEventListener('abort', cancel);
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function loadMcpManifest(workspace: string, manifest: string): Promise<{
2
+ registry: import("./types.js").McpContractRegistry;
3
+ limits: import("./limits.js").McpLimits;
4
+ }>;
5
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAqBA,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;GAyBxE"}
@@ -0,0 +1,58 @@
1
+ import { readFile, realpath } from 'node:fs/promises';
2
+ import { resolve, relative, isAbsolute } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { object, string, array, literal, integer } from '@safe-shape/core';
5
+ import { createMcpContractRegistry } from './registry.js';
6
+ import { resolveLimits } from './limits.js';
7
+ const text = string({ minLength: 1, pattern: "\\S" });
8
+ const id = string({ minLength: 1, maxLength: 128 });
9
+ const positive = integer({ minimum: 1 });
10
+ const schema = object({
11
+ version: literal(1),
12
+ contracts: array(object({ id, description: text, module: text, export: text.optional() })),
13
+ tools: array(object({ id, name: id, description: text, inputId: id, outputId: id })).optional(),
14
+ limits: object({ requestBytes: positive.optional(), depth: positive.optional(), responseBytes: positive.optional(), concurrency: positive.optional(), deadlineMs: positive.optional() }).optional(),
15
+ });
16
+ async function contained(root, path) {
17
+ const canonical = await realpath(path);
18
+ const difference = relative(root, canonical);
19
+ if (difference === '..' || difference.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) || isAbsolute(difference))
20
+ throw new TypeError('Path outside workspace.');
21
+ return canonical;
22
+ }
23
+ export async function loadMcpManifest(workspace, manifest) {
24
+ const root = await realpath(resolve(workspace));
25
+ const path = await contained(root, resolve(root, manifest));
26
+ const parsed = schema.safeParse(JSON.parse(await readFile(path, 'utf8')));
27
+ if (!parsed.success)
28
+ throw new TypeError('Invalid MCP manifest.');
29
+ const data = parsed.data;
30
+ const limits = resolveLimits(data.limits);
31
+ const ids = new Set();
32
+ for (const entry of data.contracts) {
33
+ if (!entry.id.trim() || ids.has(entry.id))
34
+ throw new TypeError('Invalid contract ID.');
35
+ ids.add(entry.id);
36
+ }
37
+ const toolIds = new Set();
38
+ const names = new Set();
39
+ for (const entry of data.tools ?? []) {
40
+ if (!entry.id.trim() || !/^[A-Za-z0-9_.-]+$/.test(entry.name) || toolIds.has(entry.id) || names.has(entry.name) || !ids.has(entry.inputId) || !ids.has(entry.outputId))
41
+ throw new TypeError('Invalid tool catalog.');
42
+ toolIds.add(entry.id);
43
+ names.add(entry.name);
44
+ }
45
+ // Validate every entry path before importing any trusted code.
46
+ const paths = await Promise.all(data.contracts.map(entry => contained(root, resolve(root, entry.module))));
47
+ const entries = [];
48
+ for (let i = 0; i < data.contracts.length; i++) {
49
+ const entry = data.contracts[i];
50
+ const module = await import(pathToFileURL(paths[i]).href);
51
+ const name = entry.export ?? 'default';
52
+ if (!Object.hasOwn(module, name))
53
+ throw new TypeError('Missing schema export.');
54
+ entries.push({ id: entry.id, description: entry.description, schema: module[name] });
55
+ }
56
+ return { registry: createMcpContractRegistry(entries, data.tools), limits };
57
+ }
58
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;AACtD,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;AACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;AACzC,MAAM,MAAM,GAAG,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACnB,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC1F,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC/F,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE;CACpM,CAAC,CAAC;AACH,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY;IACjD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7C,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC7K,OAAO,SAAS,CAAC;AACnB,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB,EAAE,QAAgB;IACvE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,sBAAsB,CAAC,CAAC;QAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAAC,CAAC;IAClJ,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAAC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IACnE,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;QACrN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,+DAA+D;IAC/D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3G,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,IAAI,CAA4B,CAAC;QACtF,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,wBAAwB,CAAC,CAAC;QAChF,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAQ,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,yBAAyB,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { McpContractEntry, McpContractRegistry, McpToolCatalogEntry } from "./types.js";
2
+ export declare function requireId(value: unknown): asserts value is string;
3
+ export declare function requireDescription(value: unknown): asserts value is string;
4
+ export declare function createMcpContractRegistry(entries: readonly McpContractEntry[], tools?: readonly McpToolCatalogEntry[]): McpContractRegistry;
5
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE7F,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAIjE;AACD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAE1E;AACD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EAAE,KAAK,GAAE,SAAS,mBAAmB,EAAO,GAC/E,mBAAmB,CAwBrB"}
@@ -0,0 +1,47 @@
1
+ import { describeContract } from "@safe-shape/core";
2
+ export function requireId(value) {
3
+ if (typeof value !== "string" || value.trim().length === 0 || value.length > 128) {
4
+ throw new TypeError("IDs must be non-empty strings of at most 128 characters.");
5
+ }
6
+ }
7
+ export function requireDescription(value) {
8
+ if (typeof value !== "string" || value.trim().length === 0)
9
+ throw new TypeError("A description is required.");
10
+ }
11
+ export function createMcpContractRegistry(entries, tools = []) {
12
+ if (!Array.isArray(entries) || !Array.isArray(tools))
13
+ throw new TypeError("Registry entries must be arrays.");
14
+ const contracts = new Map();
15
+ for (const entry of entries) {
16
+ if (!entry || typeof entry !== "object")
17
+ throw new TypeError("Invalid contract entry.");
18
+ requireId(entry.id);
19
+ requireDescription(entry.description);
20
+ if (contracts.has(entry.id))
21
+ throw new TypeError("Duplicate contract ID.");
22
+ describeContract(entry.schema);
23
+ contracts.set(entry.id, Object.freeze({ id: entry.id, description: entry.description, schema: entry.schema }));
24
+ }
25
+ const catalog = new Map();
26
+ const names = new Set();
27
+ for (const entry of tools) {
28
+ if (!entry || typeof entry !== "object")
29
+ throw new TypeError("Invalid tool entry.");
30
+ requireId(entry.id);
31
+ requireId(entry.name);
32
+ requireDescription(entry.description);
33
+ if (!/^[A-Za-z0-9_.-]+$/.test(entry.name))
34
+ throw new TypeError("Invalid MCP tool name.");
35
+ requireId(entry.inputId);
36
+ requireId(entry.outputId);
37
+ if (catalog.has(entry.id) || names.has(entry.name))
38
+ throw new TypeError("Duplicate tool ID or name.");
39
+ if (!contracts.has(entry.inputId) || !contracts.has(entry.outputId))
40
+ throw new TypeError("Unknown tool contract ID.");
41
+ catalog.set(entry.id, Object.freeze({ id: entry.id, name: entry.name, description: entry.description, inputId: entry.inputId, outputId: entry.outputId }));
42
+ names.add(entry.name);
43
+ }
44
+ const byId = (values) => Object.freeze([...values].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
45
+ return Object.freeze({ contracts: byId(contracts.values()), tools: byId(catalog.values()), getContract: (id) => contracts.get(id), getTool: (id) => catalog.get(id) });
46
+ }
47
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { type McpLimits } from './limits.js';
3
+ import type { McpContractRegistry } from './types.js';
4
+ export declare function createSafeShapeMcpServer(options: {
5
+ readonly registry: McpContractRegistry;
6
+ readonly limits?: Partial<McpLimits>;
7
+ }): Server;
8
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAExF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;CAAE,GAAG,MAAM,CAuC1I"}
package/dist/server.js ADDED
@@ -0,0 +1,68 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
3
+ import { packageVersion } from './version.js';
4
+ import { validateSchemaAsync } from '@safe-shape/validation';
5
+ import { copyJson, jsonBytes, McpOperationError, publicError } from './json.js';
6
+ import { ExecutionGate, resolveLimits, checkSignal } from './limits.js';
7
+ import { inspectionSchemas, inspectionDefinitions, createInspection } from './inspection.js';
8
+ const version = packageVersion();
9
+ export function createSafeShapeMcpServer(options) {
10
+ const gate = new ExecutionGate(resolveLimits(options.limits));
11
+ const execute = createInspection(options.registry);
12
+ const server = new Server({ name: 'safe-shape', version }, { capabilities: { tools: {} } });
13
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...inspectionDefinitions()] }));
14
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
15
+ const name = request.params.name;
16
+ if (!Object.hasOwn(inspectionSchemas, name))
17
+ throw new McpError(ErrorCode.InvalidParams, 'Unknown inspection tool.');
18
+ const operation = name;
19
+ let payload;
20
+ let isError = false;
21
+ try {
22
+ payload = await gate.run(async (signal) => {
23
+ const args = copyJson(request.params.arguments ?? {}, gate.limits.depth);
24
+ if (jsonBytes(args) > gate.limits.requestBytes)
25
+ throw new McpOperationError('request_too_large', 'Input size limit exceeded.');
26
+ const parsed = await validateSchemaAsync(inspectionSchemas[operation], args);
27
+ checkSignal(signal);
28
+ if (!parsed.valid)
29
+ throw new McpError(ErrorCode.InvalidParams, 'Invalid inspection arguments.');
30
+ const result = await execute(operation, parsed.data);
31
+ checkSignal(signal);
32
+ copyJson(result, gate.limits.depth + 6, false);
33
+ return Object.freeze({ ok: true, operation, result });
34
+ }, extra.signal);
35
+ }
36
+ catch (error) {
37
+ if (error instanceof McpError)
38
+ throw error;
39
+ isError = true;
40
+ try {
41
+ payload = copyJson({ ok: false, operation, error: publicError(error) }, gate.limits.depth + 6);
42
+ }
43
+ catch {
44
+ payload = { ok: false, operation, error: { code: 'serialization_failed', message: 'Diagnostics are not lossless JSON.' } };
45
+ }
46
+ }
47
+ const materialize = (value, failed) => {
48
+ copyJson(value, gate.limits.depth + 6, false);
49
+ return Object.freeze({ content: Object.freeze([Object.freeze({ type: 'text', text: JSON.stringify(value) })]), structuredContent: value, ...(failed ? { isError: true } : {}) });
50
+ };
51
+ let result;
52
+ try {
53
+ result = materialize(payload, isError);
54
+ }
55
+ catch {
56
+ result = materialize({ ok: false, operation, error: { code: 'serialization_failed', message: 'Result cannot be materialized.' } }, true);
57
+ }
58
+ if (jsonBytes(result) > gate.limits.responseBytes)
59
+ result = materialize({ ok: false, operation, error: { code: 'response_too_large', message: 'Result size limit exceeded.' } }, true);
60
+ if (jsonBytes(result) > gate.limits.responseBytes) {
61
+ await server.close();
62
+ throw new McpError(ErrorCode.InternalError, 'Response limit too small.');
63
+ }
64
+ return result;
65
+ });
66
+ return server;
67
+ }
68
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { Transform } from 'node:stream';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
4
+ import type { McpLimits } from './limits.js';
5
+ /** Bounded per-frame buffer and an ASCII structure scanner preceding JSON.parse. */
6
+ export declare function createBoundedInput(maxBytes: number, maxDepth: number): Transform;
7
+ export declare class BoundedStdioTransport extends StdioServerTransport {
8
+ readonly limits: McpLimits;
9
+ constructor(input: NodeJS.ReadableStream & {
10
+ pipe: any;
11
+ }, limits: McpLimits);
12
+ send(message: JSONRPCMessage): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=stdio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CA2BhF;AACD,qBAAa,qBAAsB,SAAQ,oBAAoB;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS;gBAAxE,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,EAAW,MAAM,EAAE,SAAS;IAMrE,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAI5D"}
package/dist/stdio.js ADDED
@@ -0,0 +1,68 @@
1
+ import { Transform } from 'node:stream';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ /** Bounded per-frame buffer and an ASCII structure scanner preceding JSON.parse. */
4
+ export function createBoundedInput(maxBytes, maxDepth) {
5
+ const buffer = Buffer.alloc(maxBytes);
6
+ let length = 0, depth = 0, quoted = false, escaped = false;
7
+ return new Transform({
8
+ transform(chunk, _encoding, callback) {
9
+ try {
10
+ for (const byte of chunk) {
11
+ if (byte === 10) {
12
+ this.push(Buffer.concat([buffer.subarray(0, length), Buffer.from('\n')]));
13
+ length = 0;
14
+ depth = 0;
15
+ quoted = false;
16
+ escaped = false;
17
+ continue;
18
+ }
19
+ if (length >= maxBytes)
20
+ throw new Error('Request byte limit exceeded.');
21
+ buffer[length++] = byte;
22
+ if (quoted) {
23
+ if (escaped)
24
+ escaped = false;
25
+ else if (byte === 92)
26
+ escaped = true;
27
+ else if (byte === 34)
28
+ quoted = false;
29
+ }
30
+ else if (byte === 34)
31
+ quoted = true;
32
+ else if (byte === 123 || byte === 91) {
33
+ if (++depth > maxDepth)
34
+ throw new Error('JSON depth limit exceeded.');
35
+ }
36
+ else if (byte === 125 || byte === 93)
37
+ depth--;
38
+ }
39
+ callback();
40
+ }
41
+ catch (error) {
42
+ callback(error);
43
+ }
44
+ },
45
+ flush(callback) { if (length)
46
+ callback(new Error('Incomplete protocol frame.'));
47
+ else
48
+ callback(); },
49
+ });
50
+ }
51
+ export class BoundedStdioTransport extends StdioServerTransport {
52
+ limits;
53
+ constructor(input, limits) {
54
+ const bounded = createBoundedInput(limits.requestBytes, limits.depth);
55
+ super(bounded, process.stdout, { maxBufferSize: limits.requestBytes + 1 });
56
+ this.limits = limits;
57
+ input.pipe(bounded);
58
+ bounded.on('error', () => { this.onerror?.(new Error('Protocol input limit exceeded.')); void this.close(); });
59
+ }
60
+ async send(message) {
61
+ if (Buffer.byteLength(JSON.stringify(message), 'utf8') > this.limits.responseBytes) {
62
+ await this.close();
63
+ throw new Error('Protocol output limit exceeded.');
64
+ }
65
+ return super.send(message);
66
+ }
67
+ }
68
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"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"}
package/dist/tool.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import type { Schema } from '@safe-shape/core';
2
+ import { type JsonSchema, type JsonSchemaExportIssue } from '@safe-shape/json-schema';
3
+ export interface McpTool<I, RawI, O, RawO> {
4
+ readonly name: string;
5
+ readonly description: string;
6
+ readonly input: Schema<I, RawI>;
7
+ readonly output: Schema<O, RawO>;
8
+ }
9
+ export interface McpToolDefinition {
10
+ readonly name: string;
11
+ readonly description: string;
12
+ readonly inputSchema: JsonSchema;
13
+ readonly outputSchema: JsonSchema;
14
+ }
15
+ export interface McpProfileIssue {
16
+ readonly code: 'mcp.profile.object_required' | 'mcp.profile.serialization_failed';
17
+ readonly message: string;
18
+ readonly path: readonly (string | number)[];
19
+ readonly side: 'input' | 'output';
20
+ }
21
+ export type McpToolExportResult = Readonly<{
22
+ success: true;
23
+ definition: McpToolDefinition;
24
+ warnings: readonly JsonSchemaExportIssue[];
25
+ }> | Readonly<{
26
+ success: false;
27
+ issues: readonly (JsonSchemaExportIssue | McpProfileIssue)[];
28
+ }>;
29
+ export declare function defineMcpTool<I, RawI, O, RawO>(tool: McpTool<I, RawI, O, RawO>): McpTool<I, RawI, O, RawO>;
30
+ export declare function safeToMcpToolDefinition(tool: McpTool<any, any, any, any>): McpToolExportResult;
31
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAoB,KAAK,UAAU,EAAE,KAAK,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGxG,MAAM,WAAW,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI;IAAI,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;CAAE;AACrK,MAAM,WAAW,iBAAiB;IAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAA;CAAE;AAC/J,MAAM,WAAW,eAAe;IAAG,QAAQ,CAAC,IAAI,EAAE,6BAA6B,GAAG,kCAAkC,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;CAAE;AAChO,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAA;CAAE,CAAC,GAAG,QAAQ,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC,qBAAqB,GAAG,eAAe,CAAC,EAAE,CAAA;CAAE,CAAC,CAAC;AACtO,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAK1G;AAyBD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,mBAAmB,CAG9F"}
package/dist/tool.js ADDED
@@ -0,0 +1,50 @@
1
+ import { safeToJsonSchema } from '@safe-shape/json-schema';
2
+ import { copyJson } from './json.js';
3
+ import { requireDescription, requireId } from './registry.js';
4
+ export function defineMcpTool(tool) {
5
+ requireId(tool.name);
6
+ requireDescription(tool.description);
7
+ if (!/^[A-Za-z0-9_.-]+$/.test(tool.name))
8
+ throw new TypeError('Invalid MCP tool name.');
9
+ if (!tool.input || !tool.output || typeof tool.input.safeParseAsync !== 'function' || typeof tool.output.safeParseAsync !== 'function')
10
+ throw new TypeError('SafeShape input/output schemas required.');
11
+ return Object.freeze({ name: tool.name, description: tool.description, input: tool.input, output: tool.output });
12
+ }
13
+ function objectRoot(schema) {
14
+ let node = schema;
15
+ const seen = new Set();
16
+ while (node && typeof node === 'object' && !seen.has(node)) {
17
+ if (node.type === 'object')
18
+ return true;
19
+ seen.add(node);
20
+ if (typeof node.$ref !== 'string' || !node.$ref.startsWith('#/'))
21
+ return false;
22
+ node = node.$ref.slice(2).split('/').map((s) => s.replaceAll('~1', '/').replaceAll('~0', '~')).reduce((value, key) => value && Object.hasOwn(value, key) ? value[key] : undefined, schema);
23
+ }
24
+ return false;
25
+ }
26
+ function exportDefinition(tool) {
27
+ const input = safeToJsonSchema(tool.input, { side: 'input', target: 'draft-2020-12', mode: 'exact' });
28
+ const output = safeToJsonSchema(tool.output, { side: 'output', target: 'draft-2020-12', mode: 'exact' });
29
+ const issues = [];
30
+ for (const [side, result] of [['input', input], ['output', output]]) {
31
+ if (!result.success)
32
+ issues.push(...result.issues);
33
+ else if (!objectRoot(result.schema))
34
+ issues.push({ code: 'mcp.profile.object_required', message: 'MCP wire schemas must have object roots.', side, path: [] });
35
+ }
36
+ if (issues.length)
37
+ return copyJson({ success: false, issues });
38
+ if (!input.success || !output.success)
39
+ throw new Error('Unreachable export result.');
40
+ return copyJson({ success: true, definition: { name: tool.name, description: tool.description, inputSchema: { ...input.schema, type: 'object' }, outputSchema: { ...output.schema, type: 'object' } }, warnings: [...input.warnings, ...output.warnings] });
41
+ }
42
+ export function safeToMcpToolDefinition(tool) {
43
+ try {
44
+ return exportDefinition(tool);
45
+ }
46
+ catch {
47
+ return copyJson({ success: false, issues: [{ code: 'mcp.profile.serialization_failed', message: 'Tool definition cannot be materialized as bounded lossless JSON.', path: [], side: 'input' }] });
48
+ }
49
+ }
50
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { Schema } from "@safe-shape/core";
2
+ export interface McpContractEntry {
3
+ readonly id: string;
4
+ readonly description: string;
5
+ readonly schema: Schema<any, any>;
6
+ }
7
+ export interface McpToolCatalogEntry {
8
+ readonly id: string;
9
+ readonly name: string;
10
+ readonly description: string;
11
+ readonly inputId: string;
12
+ readonly outputId: string;
13
+ }
14
+ export interface McpContractRegistry {
15
+ readonly contracts: readonly McpContractEntry[];
16
+ readonly tools: readonly McpToolCatalogEntry[];
17
+ getContract(id: string): McpContractEntry | undefined;
18
+ getTool(id: string): McpToolCatalogEntry | undefined;
19
+ }
20
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;CACnC;AACD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AACD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;IACtD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAAC;CACtD"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function packageVersion(): string;
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,IAAI,MAAM,CAYvC"}
@@ -0,0 +1,20 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ // Works both in dist and in the repository's compiled test directory.
5
+ export function packageVersion() {
6
+ let directory = dirname(fileURLToPath(import.meta.url));
7
+ while (true) {
8
+ const path = join(directory, 'package.json');
9
+ if (existsSync(path)) {
10
+ const value = JSON.parse(readFileSync(path, 'utf8'));
11
+ if (value.name === '@safe-shape/mcp' && typeof value.version === 'string')
12
+ return value.version;
13
+ }
14
+ const parent = dirname(directory);
15
+ if (parent === directory)
16
+ throw new TypeError('MCP package metadata missing.');
17
+ directory = parent;
18
+ }
19
+ }
20
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,sEAAsE;AACtE,MAAM,UAAU,cAAc;IAC5B,IAAI,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAC7C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAA0C,CAAC;YAC9F,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC,OAAO,CAAC;QAClG,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;QAC/E,SAAS,GAAG,MAAM,CAAC;IACrB,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@safe-shape/mcp",
3
+ "version": "3.5.0",
4
+ "description": "SafeShape contracts and validated tools for MCP agents.",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "engines": {
10
+ "node": ">=20.10"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "README.ru.md"
22
+ ],
23
+ "dependencies": {
24
+ "@safe-shape/core": "3.5.0",
25
+ "@safe-shape/validation": "3.5.0",
26
+ "@safe-shape/json-schema": "3.5.0",
27
+ "@safe-shape/compat": "3.5.0",
28
+ "@modelcontextprotocol/sdk": "1.32.1"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.build.json && node -e \"require('node:fs').chmodSync('dist/cli.js', 0o755)\"",
32
+ "typecheck": "tsc --noEmit",
33
+ "test": "npm run build && tsc -p tsconfig.test.json && node --test .tmp/test/tests/*.test.js"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/dev-ik/safe_shape.git",
38
+ "directory": "packages/mcp"
39
+ },
40
+ "homepage": "https://github.com/dev-ik/safe_shape#readme",
41
+ "bugs": {
42
+ "url": "https://github.com/dev-ik/safe_shape/issues"
43
+ },
44
+ "keywords": [
45
+ "mcp",
46
+ "ai",
47
+ "contracts",
48
+ "validation"
49
+ ],
50
+ "bin": {
51
+ "safe-shape-mcp": "./dist/cli.js"
52
+ }
53
+ }