@decionis/mcp 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,12 +4,13 @@ import {
4
4
  PolicyFileRepository,
5
5
  normalizeVerdict,
6
6
  sha256Hex
7
- } from "./chunk-56VFPJNL.js";
7
+ } from "./chunk-KY5AVRL6.js";
8
8
 
9
9
  // src/Server.ts
10
10
  import { createInterface } from "readline";
11
- var SUPPORTED_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
12
- var DEFAULT_PROTOCOL_VERSION = "2025-06-18";
11
+ var SUPPORTED_PROTOCOL_VERSIONS = ["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"];
12
+ var MCP_SERVER_VERSION = "0.1.2";
13
+ var DEFAULT_PROTOCOL_VERSION = "2025-11-25";
13
14
  var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
14
15
  function rpcResult(id, result) {
15
16
  return { jsonrpc: "2.0", id, result };
@@ -46,11 +47,15 @@ var McpMessageHandler = class {
46
47
  return rpcResult(id, {});
47
48
  case "tools/list":
48
49
  return rpcResult(id, {
49
- tools: this.options.tools.map(({ name, description, inputSchema }) => ({
50
- name,
51
- description,
52
- inputSchema
53
- }))
50
+ tools: this.options.tools.map(
51
+ ({ name, title, description, inputSchema, annotations }) => ({
52
+ name,
53
+ title,
54
+ description,
55
+ inputSchema,
56
+ annotations
57
+ })
58
+ )
54
59
  });
55
60
  case "tools/call": {
56
61
  const name = typeof params.name === "string" ? params.name : "";
@@ -169,6 +174,7 @@ var DecionisTools = class {
169
174
  return [
170
175
  {
171
176
  name: "decionis_read_policy",
177
+ title: "Read Repository Policy",
172
178
  description: "Read and compile the repo's DECIONIS_POLICY.md governance policy: path, sha256, and the enforceable rules (name, verdict, priority, domain) or compile errors. Use this to learn what this repository allows, blocks, restrains, or escalates.",
173
179
  inputSchema: {
174
180
  type: "object",
@@ -180,6 +186,12 @@ var DecionisTools = class {
180
186
  },
181
187
  additionalProperties: false
182
188
  },
189
+ annotations: {
190
+ readOnlyHint: true,
191
+ destructiveHint: false,
192
+ idempotentHint: true,
193
+ openWorldHint: false
194
+ },
183
195
  handler: async (args) => {
184
196
  const file = await this.policyFiles.read(args.path);
185
197
  if (!file.ok) return { content: text(file.message), isError: true };
@@ -202,6 +214,7 @@ var DecionisTools = class {
202
214
  },
203
215
  {
204
216
  name: "decionis_evaluate",
217
+ title: "Evaluate Candidate Action Locally",
205
218
  description: "Evaluate a candidate action against the repo's DECIONIS_POLICY.md through the real Decionis protocol evaluator, entirely locally (no network, no credentials, nothing recorded). Call this BEFORE committing, deploying, migrating, or any risky action, and abide by the verdict: allow \u2192 proceed; block \u2192 do not proceed; review/escalate \u2192 stop and involve a human.",
206
219
  inputSchema: {
207
220
  type: "object",
@@ -220,6 +233,12 @@ var DecionisTools = class {
220
233
  },
221
234
  additionalProperties: false
222
235
  },
236
+ annotations: {
237
+ readOnlyHint: true,
238
+ destructiveHint: false,
239
+ idempotentHint: true,
240
+ openWorldHint: false
241
+ },
223
242
  handler: async (args) => {
224
243
  if (!isPlainObject2(args.payload)) {
225
244
  return {
@@ -280,8 +299,15 @@ var DecionisTools = class {
280
299
  },
281
300
  {
282
301
  name: "decionis_verdict_help",
302
+ title: "Explain Decionis Verdicts",
283
303
  description: "Reference for Decionis verdicts (allow/block/restrain/escalate), the rules-block grammar and operators, and how an agent should behave on each verdict.",
284
304
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
305
+ annotations: {
306
+ readOnlyHint: true,
307
+ destructiveHint: false,
308
+ idempotentHint: true,
309
+ openWorldHint: false
310
+ },
285
311
  handler: async () => ({ content: text(VERDICT_HELP) })
286
312
  }
287
313
  ];
@@ -294,7 +320,7 @@ function createDecionisTools(gateway2) {
294
320
  // src/index.ts
295
321
  var gateway = new LocalDecionisGateway();
296
322
  var handler = createMcpHandler({
297
- serverInfo: { name: "decionis-mcp", version: "0.1.0" },
323
+ serverInfo: { name: "decionis-mcp", version: MCP_SERVER_VERSION },
298
324
  tools: createDecionisTools(gateway)
299
325
  });
300
326
  runStdioServer(handler).then(async () => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Server.ts","../src/Tools.ts","../src/index.ts"],"sourcesContent":["/**\n * Minimal MCP (Model Context Protocol) server core: JSON-RPC 2.0 over\n * newline-delimited stdio. Hand-rolled (no SDK dependency), matching the\n * protocol service's hand-rolled remote MCP surface. The message handler is\n * transport-agnostic and unit-testable; the stdio loop is a thin shell.\n */\n\nimport { createInterface } from \"node:readline\";\n\nexport const SUPPORTED_PROTOCOL_VERSIONS = [\"2025-06-18\", \"2025-03-26\", \"2024-11-05\"];\nconst DEFAULT_PROTOCOL_VERSION = \"2025-06-18\";\n\nexport interface ToolContent {\n type: \"text\";\n text: string;\n}\n\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n handler: (\n args: Record<string, unknown>,\n ) => Promise<{ content: ToolContent[]; isError?: boolean }>;\n}\n\nexport interface ServerInfo {\n name: string;\n version: string;\n}\n\ntype JsonRpcResponse = Record<string, unknown>;\n\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nfunction rpcResult(id: unknown, result: unknown): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, result };\n}\n\nfunction rpcError(id: unknown, code: number, message: string): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, error: { code, message } };\n}\n\n/**\n * The MCP message handler. Returns `null` for notifications (no reply) and\n * a JSON-RPC response object otherwise.\n */\nexport class McpMessageHandler {\n private readonly toolsByName: Map<string, ToolDefinition>;\n\n constructor(private readonly options: { serverInfo: ServerInfo; tools: ToolDefinition[] }) {\n this.toolsByName = new Map(options.tools.map((tool) => [tool.name, tool]));\n }\n\n async handle(message: unknown): Promise<JsonRpcResponse | null> {\n if (\n !isPlainObject(message) ||\n message.jsonrpc !== \"2.0\" ||\n typeof message.method !== \"string\"\n ) {\n const id = isPlainObject(message) && \"id\" in message ? message.id : null;\n return rpcError(id, -32600, \"Invalid JSON-RPC 2.0 request\");\n }\n const { method, id } = message;\n const isNotification = !(\"id\" in message);\n const params = isPlainObject(message.params) ? message.params : {};\n\n if (isNotification) return null; // notifications/initialized, cancellations, …\n\n switch (method) {\n case \"initialize\": {\n const requested = typeof params.protocolVersion === \"string\" ? params.protocolVersion : \"\";\n const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requested)\n ? requested\n : DEFAULT_PROTOCOL_VERSION;\n return rpcResult(id, {\n protocolVersion,\n capabilities: { tools: { listChanged: false } },\n serverInfo: this.options.serverInfo,\n });\n }\n case \"ping\":\n return rpcResult(id, {});\n case \"tools/list\":\n return rpcResult(id, {\n tools: this.options.tools.map(({ name, description, inputSchema }) => ({\n name,\n description,\n inputSchema,\n })),\n });\n case \"tools/call\": {\n const name = typeof params.name === \"string\" ? params.name : \"\";\n const tool = this.toolsByName.get(name);\n if (!tool) return rpcError(id, -32602, `Unknown tool: ${name || \"(missing name)\"}`);\n const args = isPlainObject(params.arguments) ? params.arguments : {};\n try {\n const result = await tool.handler(args);\n return rpcResult(id, { content: result.content, isError: result.isError ?? false });\n } catch (err) {\n return rpcResult(id, {\n content: [\n {\n type: \"text\",\n text: `Tool ${name} failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n });\n }\n }\n default:\n return rpcError(id, -32601, `Method not found: ${method}`);\n }\n }\n}\n\n/**\n * Runs a message handler over stdio: one JSON message per line in, one per\n * line out. Only protocol JSON is ever written to stdout; diagnostics go to\n * stderr.\n */\nexport class McpStdioServer {\n static run(\n handler: (message: unknown) => Promise<JsonRpcResponse | null>,\n streams: { input?: NodeJS.ReadableStream; output?: NodeJS.WritableStream } = {},\n ): Promise<void> {\n const input = streams.input ?? process.stdin;\n const output = streams.output ?? process.stdout;\n const rl = createInterface({ input, crlfDelay: Infinity });\n const inFlight = new Set<Promise<void>>();\n\n return new Promise((resolve) => {\n rl.on(\"line\", (line) => {\n const trimmed = line.trim();\n if (!trimmed) return;\n const work = (async () => {\n let message: unknown;\n try {\n message = JSON.parse(trimmed);\n } catch {\n output.write(`${JSON.stringify(rpcError(null, -32700, \"Parse error\"))}\\n`);\n return;\n }\n try {\n const response = await handler(message);\n if (response !== null) output.write(`${JSON.stringify(response)}\\n`);\n } catch (err) {\n process.stderr.write(`decionis-mcp: handler error: ${String(err)}\\n`);\n }\n })();\n inFlight.add(work);\n void work.finally(() => inFlight.delete(work));\n });\n rl.on(\"close\", () => {\n // stdin EOF: drain in-flight handlers so no response is lost.\n void Promise.allSettled([...inFlight]).then(() => resolve());\n });\n });\n }\n}\n\n/**\n * Build the message handler. Returns `null` for notifications (no reply) and\n * a JSON-RPC response object otherwise.\n */\nexport function createMcpHandler(options: { serverInfo: ServerInfo; tools: ToolDefinition[] }) {\n const handler = new McpMessageHandler(options);\n return async function handleMessage(message: unknown): Promise<JsonRpcResponse | null> {\n return handler.handle(message);\n };\n}\n\n/**\n * Run the handler over stdio: one JSON message per line in, one per line out.\n * Only protocol JSON is ever written to stdout; diagnostics go to stderr.\n */\nexport function runStdioServer(\n handler: (message: unknown) => Promise<JsonRpcResponse | null>,\n streams: { input?: NodeJS.ReadableStream; output?: NodeJS.WritableStream } = {},\n): Promise<void> {\n return McpStdioServer.run(handler, streams);\n}\n","/**\n * The Decionis MCP tools: read the repo's DECIONIS_POLICY.md, evaluate a\n * candidate action against it through the real protocol evaluator, and\n * explain the verdict vocabulary — so coding agents can check governance\n * constraints BEFORE committing or triggering anything.\n */\n\nimport { LocalDecionisGateway, normalizeVerdict } from \"./Gateway.js\";\nimport { PolicyFileRepository } from \"./agent/PolicyFileRepository.js\";\nimport { sha256Hex } from \"./RulesBlock.js\";\nimport type { ToolContent, ToolDefinition } from \"./Server.js\";\n\nconst text = (value: unknown): ToolContent[] => [\n { type: \"text\", text: typeof value === \"string\" ? value : JSON.stringify(value, null, 2) },\n];\n\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nconst VERDICT_HELP = `# Decionis verdicts and rule grammar\n\n**Verdicts** (a rule's \\`action\\`): \\`allow\\` (execute), \\`block\\` (never execute),\n\\`restrain\\` (hold for human review), \\`escalate\\` (require an authorized approver).\nThe protocol reports outcomes as APPROVE / REJECT / REVIEW / ESCALATE; this server\nalso returns the normalized verdict (allow / block / review / escalate). When no\nrule matches, the platform holds the decision for review (outcome REVIEW).\n\n**Rules block** (in DECIONIS_POLICY.md):\n\\`\\`\\`decionis\n{ \"version\": 1, \"rules\": [\n { \"name\": \"Block deploys during a change freeze\",\n \"priority\": 100, \"domain\": \"*\",\n \"all\": [{ \"field\": \"context.change_freeze\", \"op\": \"eq\", \"value\": true }],\n \"action\": \"block\" } ] }\n\\`\\`\\`\n\n- Each rule has exactly one of \\`all\\` (AND) / \\`any\\` (OR).\n- Operators: eq, ne/neq, gt, gte, lt, lte, in (values array), not_in (values array),\n exists, contains, matches (case-insensitive regex). Equality coerces numbers,\n booleans, and lowercased text; arrays match on any element.\n- Fields resolve against the decision payload: \\`context.<key>\\` reads payload keys,\n \\`decision_type\\` derives from the payload's \\`action\\` label, then the workflow key.\n- Give every rule an explicit \\`priority\\` (higher evaluates first) and keep\n \\`\"domain\": \"*\"\\` so it applies to every decision the repo gates.\n\n**Before committing or running anything risky**, call \\`decionis_evaluate\\` with the\naction you intend to take (e.g. {\"action\": \"production-deploy\", \"agent_generated\": true})\nand abide by the verdict: allow → proceed; block → do not proceed; review/escalate →\nstop and ask a human.`;\n\n/** Builds the Decionis tool set bound to a local gateway. */\nexport class DecionisTools {\n constructor(\n private readonly gateway: LocalDecionisGateway,\n private readonly policyFiles: PolicyFileRepository = new PolicyFileRepository(),\n ) {}\n\n build(): ToolDefinition[] {\n const gateway = this.gateway;\n return [\n {\n name: \"decionis_read_policy\",\n description:\n \"Read and compile the repo's DECIONIS_POLICY.md governance policy: path, sha256, and the \" +\n \"enforceable rules (name, verdict, priority, domain) or compile errors. Use this to learn \" +\n \"what this repository allows, blocks, restrains, or escalates.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description:\n \"Policy file path (default: $DECIONIS_POLICY_PATH or ./DECIONIS_POLICY.md)\",\n },\n },\n additionalProperties: false,\n },\n handler: async (args) => {\n const file = await this.policyFiles.read(args.path);\n if (!file.ok) return { content: text(file.message), isError: true };\n const compiled = gateway.compile(file.content);\n return {\n content: text({\n path: file.path,\n sha256: sha256Hex(file.content),\n bytes: Buffer.byteLength(file.content, \"utf8\"),\n rules:\n compiled === null\n ? \"no ```decionis rules block — the file is prose-only documentation\"\n : compiled.errors.length > 0\n ? { compile_errors: compiled.errors }\n : compiled.rules.map((rule) => ({\n name: rule.name,\n verdict: rule.then.action,\n priority: rule.priority,\n domain: rule.decision_domain,\n when: rule.when,\n })),\n }),\n };\n },\n },\n {\n name: \"decionis_evaluate\",\n description:\n \"Evaluate a candidate action against the repo's DECIONIS_POLICY.md through the real Decionis \" +\n \"protocol evaluator, entirely locally (no network, no credentials, nothing recorded). Call this \" +\n \"BEFORE committing, deploying, migrating, or any risky action, and abide by the verdict: \" +\n \"allow → proceed; block → do not proceed; review/escalate → stop and involve a human.\",\n inputSchema: {\n type: \"object\",\n required: [\"payload\"],\n properties: {\n payload: {\n type: \"object\",\n description:\n 'The candidate action, e.g. {\"action\": \"production-deploy\", \"change_freeze\": true, \"agent_generated\": true}',\n },\n workflow_key: {\n type: \"string\",\n description: \"Optional workflow key for the gated surface.\",\n },\n mode: { type: \"string\", enum: [\"shadow\", \"enforce\"], description: \"Default: shadow.\" },\n path: { type: \"string\", description: \"Policy file path override.\" },\n },\n additionalProperties: false,\n },\n handler: async (args) => {\n if (!isPlainObject(args.payload)) {\n return {\n content: text(\"`payload` must be a JSON object describing the action.\"),\n isError: true,\n };\n }\n const file = await this.policyFiles.read(args.path);\n if (!file.ok) return { content: text(file.message), isError: true };\n const workflowKey = typeof args.workflow_key === \"string\" ? args.workflow_key : null;\n\n const published = await gateway.publishPolicy(file.content, { workflowKey });\n if (!published.ok) {\n return {\n content: text({\n error: published.reason,\n details: published.errors,\n hint:\n published.reason === \"no_rules_block\"\n ? \"Add a ```decionis rules block to make the policy enforceable — see decionis_verdict_help.\"\n : \"Fix the rules block so it compiles; the platform enforces all-or-nothing.\",\n }),\n isError: true,\n };\n }\n\n const evaluated = await gateway.evaluate({\n payload: args.payload,\n workflowKey,\n mode: args.mode === \"enforce\" ? \"enforce\" : \"shadow\",\n });\n if (evaluated.status !== 200) {\n return {\n content: text({\n error: `protocol evaluation failed (HTTP ${evaluated.status})`,\n body: evaluated.body,\n }),\n isError: true,\n };\n }\n\n const body = evaluated.body;\n const policyEvaluation = isPlainObject(body.policy_evaluation)\n ? body.policy_evaluation\n : {};\n const evaluatedRuleCount =\n typeof policyEvaluation.evaluated_rule_count === \"number\"\n ? policyEvaluation.evaluated_rule_count\n : null;\n\n return {\n content: text({\n verdict: normalizeVerdict(body.outcome),\n outcome: body.outcome,\n resolution: policyEvaluation.resolution ?? null,\n selected_rule: policyEvaluation.selected_rule_name ?? null,\n matched_rule_count: policyEvaluation.matched_rule_count ?? null,\n evaluated_rule_count: evaluatedRuleCount,\n decision_domain: policyEvaluation.decision_domain ?? body.decision_domain ?? null,\n policy_version: published.version,\n policy_sha256: published.sha256,\n confidence: body.confidence ?? null,\n note:\n \"Local sandbox evaluation — nothing was recorded and no signed dossier was minted. \" +\n \"CI runs through decionis/govern produce the signed, verifiable record.\",\n ...(published.ruleCount > 0 && evaluatedRuleCount === 0\n ? {\n hint:\n `All ${published.ruleCount} compiled rule(s) were filtered out by the decision-domain ` +\n 'match before evaluation. Add \"domain\": \"*\" to each rule so it applies to every ' +\n \"decision this repo gates.\",\n }\n : {}),\n }),\n };\n },\n },\n {\n name: \"decionis_verdict_help\",\n description:\n \"Reference for Decionis verdicts (allow/block/restrain/escalate), the rules-block grammar and \" +\n \"operators, and how an agent should behave on each verdict.\",\n inputSchema: { type: \"object\", properties: {}, additionalProperties: false },\n handler: async () => ({ content: text(VERDICT_HELP) }),\n },\n ];\n }\n}\n\n/** Functional alias for {@link DecionisTools.build}. */\nexport function createDecionisTools(gateway: LocalDecionisGateway): ToolDefinition[] {\n return new DecionisTools(gateway).build();\n}\n","// Decionis local MCP server — stdio entrypoint.\n// Lets AI coding agents (Claude Code, Cursor, …) read DECIONIS_POLICY.md and\n// evaluate candidate actions through the real @decionis/protocol evaluator\n// before they commit, deploy, or migrate anything.\n\nimport { LocalDecionisGateway } from \"./Gateway.js\";\nimport { createMcpHandler, runStdioServer } from \"./Server.js\";\nimport { createDecionisTools } from \"./Tools.js\";\n\nconst gateway = new LocalDecionisGateway();\nconst handler = createMcpHandler({\n serverInfo: { name: \"decionis-mcp\", version: \"0.1.0\" },\n tools: createDecionisTools(gateway),\n});\n\nrunStdioServer(handler)\n .then(async () => {\n await gateway.close();\n process.exit(0);\n })\n .catch(async (err) => {\n process.stderr.write(`decionis-mcp: fatal: ${String(err)}\\n`);\n await gateway.close().catch(() => {});\n process.exit(1);\n });\n"],"mappings":"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","names":["id","handler","isPlainObject","gateway"]}
1
+ {"version":3,"sources":["../src/Server.ts","../src/Tools.ts","../src/index.ts"],"sourcesContent":["/**\n * Minimal MCP (Model Context Protocol) server core: JSON-RPC 2.0 over\n * newline-delimited stdio. Hand-rolled (no SDK dependency), matching the\n * protocol service's hand-rolled remote MCP surface. The message handler is\n * transport-agnostic and unit-testable; the stdio loop is a thin shell.\n */\n\nimport { createInterface } from \"node:readline\";\n\nexport const SUPPORTED_PROTOCOL_VERSIONS = [\"2025-11-25\", \"2025-06-18\", \"2025-03-26\", \"2024-11-05\"];\nexport const MCP_SERVER_VERSION = \"0.1.2\";\nconst DEFAULT_PROTOCOL_VERSION = \"2025-11-25\";\n\nexport interface ToolContent {\n type: \"text\";\n text: string;\n}\n\nexport interface ToolDefinition {\n name: string;\n title: string;\n description: string;\n inputSchema: Record<string, unknown>;\n annotations: {\n readOnlyHint: boolean;\n destructiveHint: boolean;\n idempotentHint: boolean;\n openWorldHint: boolean;\n };\n handler: (\n args: Record<string, unknown>,\n ) => Promise<{ content: ToolContent[]; isError?: boolean }>;\n}\n\nexport interface ServerInfo {\n name: string;\n version: string;\n}\n\ntype JsonRpcResponse = Record<string, unknown>;\n\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nfunction rpcResult(id: unknown, result: unknown): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, result };\n}\n\nfunction rpcError(id: unknown, code: number, message: string): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, error: { code, message } };\n}\n\n/**\n * The MCP message handler. Returns `null` for notifications (no reply) and\n * a JSON-RPC response object otherwise.\n */\nexport class McpMessageHandler {\n private readonly toolsByName: Map<string, ToolDefinition>;\n\n constructor(private readonly options: { serverInfo: ServerInfo; tools: ToolDefinition[] }) {\n this.toolsByName = new Map(options.tools.map((tool) => [tool.name, tool]));\n }\n\n async handle(message: unknown): Promise<JsonRpcResponse | null> {\n if (\n !isPlainObject(message) ||\n message.jsonrpc !== \"2.0\" ||\n typeof message.method !== \"string\"\n ) {\n const id = isPlainObject(message) && \"id\" in message ? message.id : null;\n return rpcError(id, -32600, \"Invalid JSON-RPC 2.0 request\");\n }\n const { method, id } = message;\n const isNotification = !(\"id\" in message);\n const params = isPlainObject(message.params) ? message.params : {};\n\n if (isNotification) return null; // notifications/initialized, cancellations, …\n\n switch (method) {\n case \"initialize\": {\n const requested = typeof params.protocolVersion === \"string\" ? params.protocolVersion : \"\";\n const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requested)\n ? requested\n : DEFAULT_PROTOCOL_VERSION;\n return rpcResult(id, {\n protocolVersion,\n capabilities: { tools: { listChanged: false } },\n serverInfo: this.options.serverInfo,\n });\n }\n case \"ping\":\n return rpcResult(id, {});\n case \"tools/list\":\n return rpcResult(id, {\n tools: this.options.tools.map(\n ({ name, title, description, inputSchema, annotations }) => ({\n name,\n title,\n description,\n inputSchema,\n annotations,\n }),\n ),\n });\n case \"tools/call\": {\n const name = typeof params.name === \"string\" ? params.name : \"\";\n const tool = this.toolsByName.get(name);\n if (!tool) return rpcError(id, -32602, `Unknown tool: ${name || \"(missing name)\"}`);\n const args = isPlainObject(params.arguments) ? params.arguments : {};\n try {\n const result = await tool.handler(args);\n return rpcResult(id, { content: result.content, isError: result.isError ?? false });\n } catch (err) {\n return rpcResult(id, {\n content: [\n {\n type: \"text\",\n text: `Tool ${name} failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n });\n }\n }\n default:\n return rpcError(id, -32601, `Method not found: ${method}`);\n }\n }\n}\n\n/**\n * Runs a message handler over stdio: one JSON message per line in, one per\n * line out. Only protocol JSON is ever written to stdout; diagnostics go to\n * stderr.\n */\nexport class McpStdioServer {\n static run(\n handler: (message: unknown) => Promise<JsonRpcResponse | null>,\n streams: { input?: NodeJS.ReadableStream; output?: NodeJS.WritableStream } = {},\n ): Promise<void> {\n const input = streams.input ?? process.stdin;\n const output = streams.output ?? process.stdout;\n const rl = createInterface({ input, crlfDelay: Infinity });\n const inFlight = new Set<Promise<void>>();\n\n return new Promise((resolve) => {\n rl.on(\"line\", (line) => {\n const trimmed = line.trim();\n if (!trimmed) return;\n const work = (async () => {\n let message: unknown;\n try {\n message = JSON.parse(trimmed);\n } catch {\n output.write(`${JSON.stringify(rpcError(null, -32700, \"Parse error\"))}\\n`);\n return;\n }\n try {\n const response = await handler(message);\n if (response !== null) output.write(`${JSON.stringify(response)}\\n`);\n } catch (err) {\n process.stderr.write(`decionis-mcp: handler error: ${String(err)}\\n`);\n }\n })();\n inFlight.add(work);\n void work.finally(() => inFlight.delete(work));\n });\n rl.on(\"close\", () => {\n // stdin EOF: drain in-flight handlers so no response is lost.\n void Promise.allSettled([...inFlight]).then(() => resolve());\n });\n });\n }\n}\n\n/**\n * Build the message handler. Returns `null` for notifications (no reply) and\n * a JSON-RPC response object otherwise.\n */\nexport function createMcpHandler(options: { serverInfo: ServerInfo; tools: ToolDefinition[] }) {\n const handler = new McpMessageHandler(options);\n return async function handleMessage(message: unknown): Promise<JsonRpcResponse | null> {\n return handler.handle(message);\n };\n}\n\n/**\n * Run the handler over stdio: one JSON message per line in, one per line out.\n * Only protocol JSON is ever written to stdout; diagnostics go to stderr.\n */\nexport function runStdioServer(\n handler: (message: unknown) => Promise<JsonRpcResponse | null>,\n streams: { input?: NodeJS.ReadableStream; output?: NodeJS.WritableStream } = {},\n): Promise<void> {\n return McpStdioServer.run(handler, streams);\n}\n","/**\n * The Decionis MCP tools: read the repo's DECIONIS_POLICY.md, evaluate a\n * candidate action against it through the real protocol evaluator, and\n * explain the verdict vocabulary — so coding agents can check governance\n * constraints BEFORE committing or triggering anything.\n */\n\nimport { LocalDecionisGateway, normalizeVerdict } from \"./Gateway.js\";\nimport { PolicyFileRepository } from \"./agent/PolicyFileRepository.js\";\nimport { sha256Hex } from \"./RulesBlock.js\";\nimport type { ToolContent, ToolDefinition } from \"./Server.js\";\n\nconst text = (value: unknown): ToolContent[] => [\n { type: \"text\", text: typeof value === \"string\" ? value : JSON.stringify(value, null, 2) },\n];\n\nconst isPlainObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nconst VERDICT_HELP = `# Decionis verdicts and rule grammar\n\n**Verdicts** (a rule's \\`action\\`): \\`allow\\` (execute), \\`block\\` (never execute),\n\\`restrain\\` (hold for human review), \\`escalate\\` (require an authorized approver).\nThe protocol reports outcomes as APPROVE / REJECT / REVIEW / ESCALATE; this server\nalso returns the normalized verdict (allow / block / review / escalate). When no\nrule matches, the platform holds the decision for review (outcome REVIEW).\n\n**Rules block** (in DECIONIS_POLICY.md):\n\\`\\`\\`decionis\n{ \"version\": 1, \"rules\": [\n { \"name\": \"Block deploys during a change freeze\",\n \"priority\": 100, \"domain\": \"*\",\n \"all\": [{ \"field\": \"context.change_freeze\", \"op\": \"eq\", \"value\": true }],\n \"action\": \"block\" } ] }\n\\`\\`\\`\n\n- Each rule has exactly one of \\`all\\` (AND) / \\`any\\` (OR).\n- Operators: eq, ne/neq, gt, gte, lt, lte, in (values array), not_in (values array),\n exists, contains, matches (case-insensitive regex). Equality coerces numbers,\n booleans, and lowercased text; arrays match on any element.\n- Fields resolve against the decision payload: \\`context.<key>\\` reads payload keys,\n \\`decision_type\\` derives from the payload's \\`action\\` label, then the workflow key.\n- Give every rule an explicit \\`priority\\` (higher evaluates first) and keep\n \\`\"domain\": \"*\"\\` so it applies to every decision the repo gates.\n\n**Before committing or running anything risky**, call \\`decionis_evaluate\\` with the\naction you intend to take (e.g. {\"action\": \"production-deploy\", \"agent_generated\": true})\nand abide by the verdict: allow → proceed; block → do not proceed; review/escalate →\nstop and ask a human.`;\n\n/** Builds the Decionis tool set bound to a local gateway. */\nexport class DecionisTools {\n constructor(\n private readonly gateway: LocalDecionisGateway,\n private readonly policyFiles: PolicyFileRepository = new PolicyFileRepository(),\n ) {}\n\n build(): ToolDefinition[] {\n const gateway = this.gateway;\n return [\n {\n name: \"decionis_read_policy\",\n title: \"Read Repository Policy\",\n description:\n \"Read and compile the repo's DECIONIS_POLICY.md governance policy: path, sha256, and the \" +\n \"enforceable rules (name, verdict, priority, domain) or compile errors. Use this to learn \" +\n \"what this repository allows, blocks, restrains, or escalates.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: {\n type: \"string\",\n description:\n \"Policy file path (default: $DECIONIS_POLICY_PATH or ./DECIONIS_POLICY.md)\",\n },\n },\n additionalProperties: false,\n },\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n handler: async (args) => {\n const file = await this.policyFiles.read(args.path);\n if (!file.ok) return { content: text(file.message), isError: true };\n const compiled = gateway.compile(file.content);\n return {\n content: text({\n path: file.path,\n sha256: sha256Hex(file.content),\n bytes: Buffer.byteLength(file.content, \"utf8\"),\n rules:\n compiled === null\n ? \"no ```decionis rules block — the file is prose-only documentation\"\n : compiled.errors.length > 0\n ? { compile_errors: compiled.errors }\n : compiled.rules.map((rule) => ({\n name: rule.name,\n verdict: rule.then.action,\n priority: rule.priority,\n domain: rule.decision_domain,\n when: rule.when,\n })),\n }),\n };\n },\n },\n {\n name: \"decionis_evaluate\",\n title: \"Evaluate Candidate Action Locally\",\n description:\n \"Evaluate a candidate action against the repo's DECIONIS_POLICY.md through the real Decionis \" +\n \"protocol evaluator, entirely locally (no network, no credentials, nothing recorded). Call this \" +\n \"BEFORE committing, deploying, migrating, or any risky action, and abide by the verdict: \" +\n \"allow → proceed; block → do not proceed; review/escalate → stop and involve a human.\",\n inputSchema: {\n type: \"object\",\n required: [\"payload\"],\n properties: {\n payload: {\n type: \"object\",\n description:\n 'The candidate action, e.g. {\"action\": \"production-deploy\", \"change_freeze\": true, \"agent_generated\": true}',\n },\n workflow_key: {\n type: \"string\",\n description: \"Optional workflow key for the gated surface.\",\n },\n mode: { type: \"string\", enum: [\"shadow\", \"enforce\"], description: \"Default: shadow.\" },\n path: { type: \"string\", description: \"Policy file path override.\" },\n },\n additionalProperties: false,\n },\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n handler: async (args) => {\n if (!isPlainObject(args.payload)) {\n return {\n content: text(\"`payload` must be a JSON object describing the action.\"),\n isError: true,\n };\n }\n const file = await this.policyFiles.read(args.path);\n if (!file.ok) return { content: text(file.message), isError: true };\n const workflowKey = typeof args.workflow_key === \"string\" ? args.workflow_key : null;\n\n const published = await gateway.publishPolicy(file.content, { workflowKey });\n if (!published.ok) {\n return {\n content: text({\n error: published.reason,\n details: published.errors,\n hint:\n published.reason === \"no_rules_block\"\n ? \"Add a ```decionis rules block to make the policy enforceable — see decionis_verdict_help.\"\n : \"Fix the rules block so it compiles; the platform enforces all-or-nothing.\",\n }),\n isError: true,\n };\n }\n\n const evaluated = await gateway.evaluate({\n payload: args.payload,\n workflowKey,\n mode: args.mode === \"enforce\" ? \"enforce\" : \"shadow\",\n });\n if (evaluated.status !== 200) {\n return {\n content: text({\n error: `protocol evaluation failed (HTTP ${evaluated.status})`,\n body: evaluated.body,\n }),\n isError: true,\n };\n }\n\n const body = evaluated.body;\n const policyEvaluation = isPlainObject(body.policy_evaluation)\n ? body.policy_evaluation\n : {};\n const evaluatedRuleCount =\n typeof policyEvaluation.evaluated_rule_count === \"number\"\n ? policyEvaluation.evaluated_rule_count\n : null;\n\n return {\n content: text({\n verdict: normalizeVerdict(body.outcome),\n outcome: body.outcome,\n resolution: policyEvaluation.resolution ?? null,\n selected_rule: policyEvaluation.selected_rule_name ?? null,\n matched_rule_count: policyEvaluation.matched_rule_count ?? null,\n evaluated_rule_count: evaluatedRuleCount,\n decision_domain: policyEvaluation.decision_domain ?? body.decision_domain ?? null,\n policy_version: published.version,\n policy_sha256: published.sha256,\n confidence: body.confidence ?? null,\n note:\n \"Local sandbox evaluation — nothing was recorded and no signed dossier was minted. \" +\n \"CI runs through decionis/govern produce the signed, verifiable record.\",\n ...(published.ruleCount > 0 && evaluatedRuleCount === 0\n ? {\n hint:\n `All ${published.ruleCount} compiled rule(s) were filtered out by the decision-domain ` +\n 'match before evaluation. Add \"domain\": \"*\" to each rule so it applies to every ' +\n \"decision this repo gates.\",\n }\n : {}),\n }),\n };\n },\n },\n {\n name: \"decionis_verdict_help\",\n title: \"Explain Decionis Verdicts\",\n description:\n \"Reference for Decionis verdicts (allow/block/restrain/escalate), the rules-block grammar and \" +\n \"operators, and how an agent should behave on each verdict.\",\n inputSchema: { type: \"object\", properties: {}, additionalProperties: false },\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n },\n handler: async () => ({ content: text(VERDICT_HELP) }),\n },\n ];\n }\n}\n\n/** Functional alias for {@link DecionisTools.build}. */\nexport function createDecionisTools(gateway: LocalDecionisGateway): ToolDefinition[] {\n return new DecionisTools(gateway).build();\n}\n","// Decionis local MCP server — stdio entrypoint.\n// Lets AI coding agents (Claude Code, Cursor, …) read DECIONIS_POLICY.md and\n// evaluate candidate actions through the real @decionis/protocol evaluator\n// before they commit, deploy, or migrate anything.\n\nimport { LocalDecionisGateway } from \"./Gateway.js\";\nimport { createMcpHandler, MCP_SERVER_VERSION, runStdioServer } from \"./Server.js\";\nimport { createDecionisTools } from \"./Tools.js\";\n\nconst gateway = new LocalDecionisGateway();\nconst handler = createMcpHandler({\n serverInfo: { name: \"decionis-mcp\", version: MCP_SERVER_VERSION },\n tools: createDecionisTools(gateway),\n});\n\nrunStdioServer(handler)\n .then(async () => {\n await gateway.close();\n process.exit(0);\n })\n .catch(async (err) => {\n process.stderr.write(`decionis-mcp: fatal: ${String(err)}\\n`);\n await gateway.close().catch(() => {});\n process.exit(1);\n });\n"],"mappings":"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","names":["id","handler","isPlainObject","gateway"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decionis/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Decionis MCP policy tools and native Codex, Copilot, and Claude execution hooks.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -48,20 +48,6 @@
48
48
  "publishConfig": {
49
49
  "access": "public"
50
50
  },
51
- "scripts": {
52
- "dev": "tsx src/index.ts",
53
- "agent-hook": "tsx src/AgentHookCli.ts",
54
- "managed-agent-hook": "tsx src/managed/ManagedAgentHookCli.ts",
55
- "build": "tsup",
56
- "package:managed": "pnpm run build && node scripts/PackageManagedArtifacts.mjs",
57
- "npm:smoke": "node scripts/NpmPackSmoke.mjs",
58
- "start": "node dist/index.js",
59
- "test": "vitest run",
60
- "typecheck": "tsc -p tsconfig.json --noEmit",
61
- "lint": "eslint .",
62
- "format": "prettier --check . --ignore-path ../../.prettierignore",
63
- "format:fix": "prettier --write . --ignore-path ../../.prettierignore"
64
- },
65
51
  "dependencies": {
66
52
  "ajv": "^8.17.1",
67
53
  "ajv-formats": "^3.0.1",
@@ -69,14 +55,28 @@
69
55
  "fastify": "^5.10.0"
70
56
  },
71
57
  "devDependencies": {
72
- "@decionis/protocol": "workspace:*",
73
- "@decionis/shared": "workspace:*",
74
58
  "@types/node": "^20.11.30",
75
59
  "@typescript-eslint/eslint-plugin": "^8.44.0",
76
60
  "@typescript-eslint/parser": "^8.44.0",
77
61
  "tsup": "^8.0.2",
78
62
  "tsx": "^4.19.2",
79
63
  "typescript": "^5.6.3",
80
- "vitest": "^2.1.8"
64
+ "vitest": "^2.1.8",
65
+ "@decionis/protocol": "0.0.0",
66
+ "@decionis/shared": "0.0.0"
67
+ },
68
+ "scripts": {
69
+ "dev": "tsx src/index.ts",
70
+ "agent-hook": "tsx src/AgentHookCli.ts",
71
+ "managed-agent-hook": "tsx src/managed/ManagedAgentHookCli.ts",
72
+ "build": "tsup",
73
+ "package:managed": "pnpm run build && node scripts/PackageManagedArtifacts.mjs",
74
+ "npm:smoke": "node scripts/NpmPackSmoke.mjs",
75
+ "start": "node dist/index.js",
76
+ "test": "vitest run",
77
+ "typecheck": "tsc -p tsconfig.json --noEmit",
78
+ "lint": "eslint .",
79
+ "format": "prettier --check . --ignore-path ../../.prettierignore",
80
+ "format:fix": "prettier --write . --ignore-path ../../.prettierignore"
81
81
  }
82
- }
82
+ }