@multiagentcoordinationprotocol/mcp 0.2.0 → 0.3.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.
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ /**
3
+ * RFC 8785 JSON Canonicalization Scheme (JCS) for the gate's context_id.
4
+ *
5
+ * JCS is defined on top of ECMAScript's own JSON serialization: strings and
6
+ * numbers are emitted exactly as `JSON.stringify` emits them (RFC 8785 §3.2.2),
7
+ * and object members are sorted by their names' UTF-16 code units (§3.2.3),
8
+ * which is what the default `Array.prototype.sort` comparison does.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.CanonicalizationError = void 0;
12
+ exports.canonicalize = canonicalize;
13
+ exports.hasUnsafeInteger = hasUnsafeInteger;
14
+ class CanonicalizationError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'CanonicalizationError';
18
+ }
19
+ }
20
+ exports.CanonicalizationError = CanonicalizationError;
21
+ function canonicalize(value) {
22
+ if (value === null || typeof value === 'boolean')
23
+ return JSON.stringify(value);
24
+ if (typeof value === 'number') {
25
+ if (!Number.isFinite(value))
26
+ throw new CanonicalizationError(`cannot canonicalize non-finite number ${value}`);
27
+ return JSON.stringify(value);
28
+ }
29
+ if (typeof value === 'string')
30
+ return JSON.stringify(value);
31
+ if (Array.isArray(value))
32
+ return `[${value.map(canonicalize).join(',')}]`;
33
+ if (typeof value === 'object') {
34
+ const obj = value;
35
+ const members = Object.keys(obj)
36
+ .filter((key) => obj[key] !== undefined)
37
+ .sort()
38
+ .map((key) => `${JSON.stringify(key)}:${canonicalize(obj[key])}`);
39
+ return `{${members.join(',')}}`;
40
+ }
41
+ throw new CanonicalizationError(`cannot canonicalize a value of type ${typeof value}`);
42
+ }
43
+ /**
44
+ * Whether `value` holds, at any depth, an integral number outside ±(2^53−1).
45
+ * Such a number has no canonical form: JSON.parse may already have rounded it,
46
+ * so an approver would sign off on a value other than the one forwarded.
47
+ */
48
+ function hasUnsafeInteger(value) {
49
+ if (typeof value === 'number')
50
+ return Number.isInteger(value) && !Number.isSafeInteger(value);
51
+ if (Array.isArray(value))
52
+ return value.some(hasUnsafeInteger);
53
+ if (value !== null && typeof value === 'object')
54
+ return Object.values(value).some(hasUnsafeInteger);
55
+ return false;
56
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `macp-mcp gate`: an MCP server that proxies an upstream MCP server's tools
3
+ * and puts the calls a gate policy names behind a MACP session.
4
+ *
5
+ * Tool definitions pass through verbatim. A call no rule matches is forwarded
6
+ * untouched. A call a rule matches opens a session first (see
7
+ * {@link GateApprover}) and reaches the upstream only if that session commits
8
+ * `approve`; the forwarded result then carries the commitment under
9
+ * `_meta["io.multiagentcoordinationprotocol/commitment"]`, alongside any
10
+ * `_meta` keys the upstream set. A refused call is a tool error and the
11
+ * upstream never sees it.
12
+ */
13
+ import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
14
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
15
+ import { type CallToolResult } from '@modelcontextprotocol/sdk/types.js';
16
+ import type { GateApprover } from './approval';
17
+ import { type GateCommitment } from './approval';
18
+ import { type GateConfig } from './config';
19
+ export declare const GATE_META_KEY = "io.multiagentcoordinationprotocol/commitment";
20
+ export declare const GATE_SERVER_NAME: string;
21
+ /** The part of an MCP `Client` connected to the upstream that the gate uses. */
22
+ export type Upstream = Pick<Client, 'request'>;
23
+ /** What the gate needs from its session runner (lets tests pass a fake). */
24
+ export type Approver = Pick<GateApprover, 'decide'>;
25
+ export declare function withCommitment(result: CallToolResult, commitment: GateCommitment): CallToolResult;
26
+ export declare function buildGateServer(options: {
27
+ config: GateConfig;
28
+ upstream: Upstream;
29
+ approver: Approver;
30
+ }): Server;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ /**
3
+ * `macp-mcp gate`: an MCP server that proxies an upstream MCP server's tools
4
+ * and puts the calls a gate policy names behind a MACP session.
5
+ *
6
+ * Tool definitions pass through verbatim. A call no rule matches is forwarded
7
+ * untouched. A call a rule matches opens a session first (see
8
+ * {@link GateApprover}) and reaches the upstream only if that session commits
9
+ * `approve`; the forwarded result then carries the commitment under
10
+ * `_meta["io.multiagentcoordinationprotocol/commitment"]`, alongside any
11
+ * `_meta` keys the upstream set. A refused call is a tool error and the
12
+ * upstream never sees it.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.GATE_SERVER_NAME = exports.GATE_META_KEY = void 0;
16
+ exports.withCommitment = withCommitment;
17
+ exports.buildGateServer = buildGateServer;
18
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
19
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
20
+ const server_1 = require("../server");
21
+ const version_1 = require("../version");
22
+ const approval_1 = require("./approval");
23
+ const config_1 = require("./config");
24
+ const context_id_1 = require("./context-id");
25
+ const contract_1 = require("./contract");
26
+ const jcs_1 = require("./jcs");
27
+ exports.GATE_META_KEY = 'io.multiagentcoordinationprotocol/commitment';
28
+ exports.GATE_SERVER_NAME = contract_1.GATE_SERVER.name;
29
+ function refusalResult(text) {
30
+ return { content: [{ type: 'text', text }], isError: true };
31
+ }
32
+ function withCommitment(result, commitment) {
33
+ return { ...result, _meta: { ...(result._meta ?? {}), [exports.GATE_META_KEY]: { ...commitment } } };
34
+ }
35
+ function buildGateServer(options) {
36
+ const { config, upstream, approver } = options;
37
+ const server = new index_js_1.Server({ name: exports.GATE_SERVER_NAME, title: 'MACP tool-call gate', version: version_1.VERSION }, { capabilities: { tools: {} }, instructions: contract_1.GATE_SERVER.instructions });
38
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, (request) => upstream.request({ method: 'tools/list', params: request.params }, types_js_1.ListToolsResultSchema));
39
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
40
+ const forward = () => upstream.request({ method: 'tools/call', params: request.params }, types_js_1.CallToolResultSchema);
41
+ const { name } = request.params;
42
+ const rule = (0, config_1.matchRule)(config, name);
43
+ if (!rule)
44
+ return forward();
45
+ const args = (request.params.arguments ?? {});
46
+ let commitment;
47
+ try {
48
+ // A number JSON.parse may already have rounded has no canonical form; refuse before any session opens.
49
+ if ((0, jcs_1.hasUnsafeInteger)(args))
50
+ throw (0, approval_1.refusal)('unsafe_integer', { tool: name });
51
+ const preimage = (0, context_id_1.gatePreimage)(config.server, name, args);
52
+ commitment = await approver.decide(rule, {
53
+ server: config.server,
54
+ tool: name,
55
+ preimage,
56
+ contextId: (0, context_id_1.contextIdOf)(preimage),
57
+ });
58
+ }
59
+ catch (err) {
60
+ if (err instanceof approval_1.GateRefusal)
61
+ return refusalResult(`${err.code}: ${err.message}`);
62
+ return refusalResult((0, server_1.errorText)(err) ?? `Error gating tool ${name}: ${err?.message ?? String(err)}`);
63
+ }
64
+ if (commitment.outcome !== 'approve') {
65
+ const refusal = (0, approval_1.denied)(commitment, name);
66
+ return refusalResult(`${refusal.code}: ${refusal.message}`);
67
+ }
68
+ return withCommitment(await forward(), commitment);
69
+ });
70
+ return server;
71
+ }
package/dist/index.d.ts CHANGED
@@ -6,3 +6,13 @@ export type { Contract, ToolContract, ToolAnnotations } from './contract';
6
6
  export type * from './results';
7
7
  export { buildServer, errorText, type BuildServerOptions } from './server';
8
8
  export { VERSION } from './version';
9
+ export { GateApprover, GateRefusal, GATE_ACTION_APPROVE, GATE_ACTION_DENY, GATE_GRACE_MS, GATE_PROPOSAL_ID, settle, } from './gate/approval';
10
+ export type { GateCommitment, GateOutcome, GatedCall } from './gate/approval';
11
+ export { GATE_DEFAULT_TTL_MS, loadGateConfig, matchRule, parseGateConfig, requiredApprovals } from './gate/config';
12
+ export type { GateConfig, GateMode, GateRule } from './gate/config';
13
+ export { contextIdOf, gateContextId, gatePreimage } from './gate/context-id';
14
+ export { GATE_CONTRACT_DIR, loadGateSchema } from './gate/contract';
15
+ export { canonicalize, CanonicalizationError } from './gate/jcs';
16
+ export { GATE_META_KEY, GATE_SERVER_NAME, buildGateServer, withCommitment } from './gate/proxy';
17
+ export type { Approver, Upstream } from './gate/proxy';
18
+ export { parseGateArgs, runGate, upstreamEnv } from './gate/cli';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.VERSION = exports.errorText = exports.buildServer = exports.loadContract = exports.CONTRACT_PATH = exports.REPLAY_MAX_MS = exports.COMMITMENT_HASH_LABEL = exports.stateName = exports.defaultClientFactory = exports.ToolArgumentError = exports.MacpBridge = void 0;
17
+ exports.upstreamEnv = exports.runGate = exports.parseGateArgs = exports.withCommitment = exports.buildGateServer = exports.GATE_SERVER_NAME = exports.GATE_META_KEY = exports.CanonicalizationError = exports.canonicalize = exports.loadGateSchema = exports.GATE_CONTRACT_DIR = exports.gatePreimage = exports.gateContextId = exports.contextIdOf = exports.requiredApprovals = exports.parseGateConfig = exports.matchRule = exports.loadGateConfig = exports.GATE_DEFAULT_TTL_MS = exports.settle = exports.GATE_PROPOSAL_ID = exports.GATE_GRACE_MS = exports.GATE_ACTION_DENY = exports.GATE_ACTION_APPROVE = exports.GateRefusal = exports.GateApprover = exports.VERSION = exports.errorText = exports.buildServer = exports.loadContract = exports.CONTRACT_PATH = exports.REPLAY_MAX_MS = exports.COMMITMENT_HASH_LABEL = exports.stateName = exports.defaultClientFactory = exports.ToolArgumentError = exports.MacpBridge = void 0;
18
18
  var bridge_1 = require("./bridge");
19
19
  Object.defineProperty(exports, "MacpBridge", { enumerable: true, get: function () { return bridge_1.MacpBridge; } });
20
20
  Object.defineProperty(exports, "ToolArgumentError", { enumerable: true, get: function () { return bridge_1.ToolArgumentError; } });
@@ -31,3 +31,36 @@ Object.defineProperty(exports, "buildServer", { enumerable: true, get: function
31
31
  Object.defineProperty(exports, "errorText", { enumerable: true, get: function () { return server_1.errorText; } });
32
32
  var version_1 = require("./version");
33
33
  Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () { return version_1.VERSION; } });
34
+ var approval_1 = require("./gate/approval");
35
+ Object.defineProperty(exports, "GateApprover", { enumerable: true, get: function () { return approval_1.GateApprover; } });
36
+ Object.defineProperty(exports, "GateRefusal", { enumerable: true, get: function () { return approval_1.GateRefusal; } });
37
+ Object.defineProperty(exports, "GATE_ACTION_APPROVE", { enumerable: true, get: function () { return approval_1.GATE_ACTION_APPROVE; } });
38
+ Object.defineProperty(exports, "GATE_ACTION_DENY", { enumerable: true, get: function () { return approval_1.GATE_ACTION_DENY; } });
39
+ Object.defineProperty(exports, "GATE_GRACE_MS", { enumerable: true, get: function () { return approval_1.GATE_GRACE_MS; } });
40
+ Object.defineProperty(exports, "GATE_PROPOSAL_ID", { enumerable: true, get: function () { return approval_1.GATE_PROPOSAL_ID; } });
41
+ Object.defineProperty(exports, "settle", { enumerable: true, get: function () { return approval_1.settle; } });
42
+ var config_1 = require("./gate/config");
43
+ Object.defineProperty(exports, "GATE_DEFAULT_TTL_MS", { enumerable: true, get: function () { return config_1.GATE_DEFAULT_TTL_MS; } });
44
+ Object.defineProperty(exports, "loadGateConfig", { enumerable: true, get: function () { return config_1.loadGateConfig; } });
45
+ Object.defineProperty(exports, "matchRule", { enumerable: true, get: function () { return config_1.matchRule; } });
46
+ Object.defineProperty(exports, "parseGateConfig", { enumerable: true, get: function () { return config_1.parseGateConfig; } });
47
+ Object.defineProperty(exports, "requiredApprovals", { enumerable: true, get: function () { return config_1.requiredApprovals; } });
48
+ var context_id_1 = require("./gate/context-id");
49
+ Object.defineProperty(exports, "contextIdOf", { enumerable: true, get: function () { return context_id_1.contextIdOf; } });
50
+ Object.defineProperty(exports, "gateContextId", { enumerable: true, get: function () { return context_id_1.gateContextId; } });
51
+ Object.defineProperty(exports, "gatePreimage", { enumerable: true, get: function () { return context_id_1.gatePreimage; } });
52
+ var contract_2 = require("./gate/contract");
53
+ Object.defineProperty(exports, "GATE_CONTRACT_DIR", { enumerable: true, get: function () { return contract_2.GATE_CONTRACT_DIR; } });
54
+ Object.defineProperty(exports, "loadGateSchema", { enumerable: true, get: function () { return contract_2.loadGateSchema; } });
55
+ var jcs_1 = require("./gate/jcs");
56
+ Object.defineProperty(exports, "canonicalize", { enumerable: true, get: function () { return jcs_1.canonicalize; } });
57
+ Object.defineProperty(exports, "CanonicalizationError", { enumerable: true, get: function () { return jcs_1.CanonicalizationError; } });
58
+ var proxy_1 = require("./gate/proxy");
59
+ Object.defineProperty(exports, "GATE_META_KEY", { enumerable: true, get: function () { return proxy_1.GATE_META_KEY; } });
60
+ Object.defineProperty(exports, "GATE_SERVER_NAME", { enumerable: true, get: function () { return proxy_1.GATE_SERVER_NAME; } });
61
+ Object.defineProperty(exports, "buildGateServer", { enumerable: true, get: function () { return proxy_1.buildGateServer; } });
62
+ Object.defineProperty(exports, "withCommitment", { enumerable: true, get: function () { return proxy_1.withCommitment; } });
63
+ var cli_1 = require("./gate/cli");
64
+ Object.defineProperty(exports, "parseGateArgs", { enumerable: true, get: function () { return cli_1.parseGateArgs; } });
65
+ Object.defineProperty(exports, "runGate", { enumerable: true, get: function () { return cli_1.runGate; } });
66
+ Object.defineProperty(exports, "upstreamEnv", { enumerable: true, get: function () { return cli_1.upstreamEnv; } });
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.0";
1
+ export declare const VERSION = "0.3.0";
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // x-release-please-start-version
5
- exports.VERSION = '0.2.0';
5
+ exports.VERSION = '0.3.0';
6
6
  // x-release-please-end
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@multiagentcoordinationprotocol/mcp",
3
- "version": "0.2.0",
4
- "description": "MCP server that exposes MACP Decision sessions as tools (TypeScript twin of macp-mcp)",
3
+ "version": "0.3.0",
4
+ "description": "MCP server that exposes MACP Decision sessions as tools and gates upstream MCP tool calls behind MACP sessions (TypeScript twin of macp-mcp)",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
7
7
  "main": "dist/index.js",
@@ -15,11 +15,13 @@
15
15
  "default": "./dist/index.js"
16
16
  },
17
17
  "./contract/tools.json": "./contract/tools.json",
18
- "./package.json": "./package.json"
18
+ "./package.json": "./package.json",
19
+ "./contract/gate/*": "./contract/gate/*"
19
20
  },
20
21
  "files": [
21
22
  "dist",
22
23
  "contract/tools.json",
24
+ "contract/gate",
23
25
  "CHANGELOG.md"
24
26
  ],
25
27
  "scripts": {