@vorim/mcp-server 1.1.3 → 1.1.5

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 (2) hide show
  1. package/build/index.js +64 -16
  2. package/package.json +2 -1
package/build/index.js CHANGED
@@ -21,6 +21,18 @@
21
21
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
22
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
23
  import { z } from "zod";
24
+ // SECURITY: The MCP server uses a single process-wide Vorim API key
25
+ // taken from VORIM_API_KEY at startup. The stdio transport between
26
+ // the MCP host (e.g. Claude Desktop) and this process carries NO
27
+ // per-call auth — every tool call is attributed to the configured
28
+ // key. Two consequences:
29
+ // 1. Scope the key tightly. The key's scopes (audit:read,
30
+ // audit:write, agent:write, etc.) determine which MCP tools
31
+ // will actually work; a least-privilege scoped key reduces
32
+ // blast radius if the host environment is compromised.
33
+ // 2. Do not share a key across MCP installations. Each user /
34
+ // machine should mint its own key so revocation surgically
35
+ // kills exactly one installation.
24
36
  const API_KEY = process.env.VORIM_API_KEY || "";
25
37
  const BASE_URL = (process.env.VORIM_BASE_URL || "https://api.vorim.ai").replace(/\/$/, "");
26
38
  if (!API_KEY) {
@@ -28,16 +40,46 @@ if (!API_KEY) {
28
40
  process.exit(1);
29
41
  }
30
42
  // ─── HTTP Client ──────────────────────────────────────────────────────────
43
+ // Read the package version once so the User-Agent string and the MCP
44
+ // server's advertised version stay in sync with package.json.
45
+ // require() works in the CommonJS build path; for the ESM-only build
46
+ // we fall back to a constant that must match package.json.
47
+ const MCP_VERSION = "1.1.5";
48
+ /**
49
+ * URL-encode a user-supplied path segment. Agent ids, scopes, and
50
+ * chain ids all reach the API via path interpolation; raw slashes or
51
+ * other special characters from a misbehaving caller would otherwise
52
+ * either escape the intended route or be sent verbatim.
53
+ */
54
+ function encId(s) {
55
+ return encodeURIComponent(s);
56
+ }
31
57
  async function vorimRequest(method, path, body) {
32
58
  const response = await fetch(`${BASE_URL}/v1${path}`, {
33
59
  method,
34
60
  headers: {
35
61
  "Authorization": `Bearer ${API_KEY}`,
36
62
  "Content-Type": "application/json",
37
- "User-Agent": "vorim-mcp-server/1.0.0",
63
+ "User-Agent": `vorim-mcp-server/${MCP_VERSION}`,
38
64
  },
39
65
  body: body ? JSON.stringify(body) : undefined,
40
66
  });
67
+ // Hand out clearer error messages for the common cases an MCP user
68
+ // will see. Auth: the API key is wrong or revoked. Scope: the key
69
+ // is missing the scope this route requires (e.g. audit:write).
70
+ if (response.status === 401) {
71
+ throw new Error("Vorim API key is invalid, revoked, or expired. Check VORIM_API_KEY.");
72
+ }
73
+ if (response.status === 403) {
74
+ const j = await response.json().catch(() => ({}));
75
+ const err = j.error;
76
+ const code = err?.code;
77
+ if (code === "INSUFFICIENT_SCOPE") {
78
+ throw new Error(err?.message ||
79
+ "Vorim API key is missing the scope required for this operation. Mint a new key with the required scope and update VORIM_API_KEY.");
80
+ }
81
+ throw new Error(err?.message || "Vorim API rejected with 403");
82
+ }
41
83
  const json = await response.json();
42
84
  if (!response.ok) {
43
85
  const err = json.error;
@@ -64,15 +106,16 @@ function text(data) {
64
106
  // ─── MCP Server ───────────────────────────────────────────────────────────
65
107
  const server = new McpServer({
66
108
  name: "vorim",
67
- version: "1.0.0",
109
+ version: MCP_VERSION,
68
110
  });
69
111
  // ─── Health ───────────────────────────────────────────────────────────────
70
112
  server.registerTool("vorim_ping", {
71
113
  description: "Check Vorim AI API health and connectivity. Returns status, version, and service health.",
72
114
  inputSchema: {},
115
+ annotations: { readOnlyHint: true },
73
116
  }, async () => {
74
117
  const response = await fetch(`${BASE_URL}/health`, {
75
- headers: { "User-Agent": "vorim-mcp-server/1.0.0" },
118
+ headers: { "User-Agent": `vorim-mcp-server/${MCP_VERSION}` },
76
119
  });
77
120
  const data = await response.json();
78
121
  return text(data);
@@ -96,7 +139,7 @@ server.registerTool("vorim_get_agent", {
96
139
  agent_id: z.string().describe("The agent identifier (e.g. agid_acme_a1b2c3d4)"),
97
140
  },
98
141
  }, async ({ agent_id }) => {
99
- const result = await vorimGet(`/agents/${agent_id}`);
142
+ const result = await vorimGet(`/agents/${encId(agent_id)}`);
100
143
  return text(result);
101
144
  });
102
145
  server.registerTool("vorim_list_agents", {
@@ -129,7 +172,7 @@ server.registerTool("vorim_update_agent", {
129
172
  },
130
173
  }, async ({ agent_id, ...updates }) => {
131
174
  const body = Object.fromEntries(Object.entries(updates).filter(([, v]) => v !== undefined));
132
- const result = await vorimPatch(`/agents/${agent_id}`, body);
175
+ const result = await vorimPatch(`/agents/${encId(agent_id)}`, body);
133
176
  return text(result);
134
177
  });
135
178
  server.registerTool("vorim_revoke_agent", {
@@ -137,8 +180,9 @@ server.registerTool("vorim_revoke_agent", {
137
180
  inputSchema: {
138
181
  agent_id: z.string().describe("The agent identifier to revoke"),
139
182
  },
183
+ annotations: { destructiveHint: true, idempotentHint: true, readOnlyHint: false },
140
184
  }, async ({ agent_id }) => {
141
- const result = await vorimDelete(`/agents/${agent_id}`);
185
+ const result = await vorimDelete(`/agents/${encId(agent_id)}`);
142
186
  return text(result);
143
187
  });
144
188
  // ─── Permissions ──────────────────────────────────────────────────────────
@@ -148,8 +192,9 @@ server.registerTool("vorim_check_permission", {
148
192
  agent_id: z.string().describe("The agent identifier"),
149
193
  scope: z.string().describe("Permission scope to check (e.g. agent:read, agent:execute)"),
150
194
  },
195
+ annotations: { readOnlyHint: true },
151
196
  }, async ({ agent_id, scope }) => {
152
- const result = await vorimPost(`/agents/${agent_id}/permissions/verify`, { scope });
197
+ const result = await vorimPost(`/agents/${encId(agent_id)}/permissions/verify`, { scope });
153
198
  return text(result);
154
199
  });
155
200
  server.registerTool("vorim_grant_permission", {
@@ -168,7 +213,7 @@ server.registerTool("vorim_grant_permission", {
168
213
  if (rate_limit_max && rate_limit_window) {
169
214
  body.rate_limit = { max: rate_limit_max, window: rate_limit_window };
170
215
  }
171
- const result = await vorimPost(`/agents/${agent_id}/permissions`, body);
216
+ const result = await vorimPost(`/agents/${encId(agent_id)}/permissions`, body);
172
217
  return text(result);
173
218
  });
174
219
  server.registerTool("vorim_list_permissions", {
@@ -176,8 +221,9 @@ server.registerTool("vorim_list_permissions", {
176
221
  inputSchema: {
177
222
  agent_id: z.string().describe("The agent identifier"),
178
223
  },
224
+ annotations: { readOnlyHint: true },
179
225
  }, async ({ agent_id }) => {
180
- const result = await vorimGet(`/agents/${agent_id}/permissions`);
226
+ const result = await vorimGet(`/agents/${encId(agent_id)}/permissions`);
181
227
  return text(result);
182
228
  });
183
229
  server.registerTool("vorim_revoke_permission", {
@@ -186,13 +232,14 @@ server.registerTool("vorim_revoke_permission", {
186
232
  agent_id: z.string().describe("The agent identifier"),
187
233
  scope: z.string().describe("Permission scope to revoke"),
188
234
  },
235
+ annotations: { destructiveHint: true, idempotentHint: true, readOnlyHint: false },
189
236
  }, async ({ agent_id, scope }) => {
190
- const result = await vorimDelete(`/agents/${agent_id}/permissions/${scope}`);
237
+ const result = await vorimDelete(`/agents/${encId(agent_id)}/permissions/${encId(scope)}`);
191
238
  return text(result);
192
239
  });
193
240
  // ─── Audit ────────────────────────────────────────────────────────────────
194
241
  server.registerTool("vorim_emit_event", {
195
- description: "Log an audit event for an agent action. Every agent action should be logged for compliance and traceability.",
242
+ description: "Log an audit event for an agent action. Every agent action should be logged for compliance and traceability. NOTE: events emitted via the MCP server are sent unsigned because the MCP server does not hold the agent's private key. For tamper-evident audit trails sign client-side via @vorim/sdk before emit.",
196
243
  inputSchema: {
197
244
  agent_id: z.string().describe("The agent that performed the action"),
198
245
  event_type: z.string().describe("Event category: tool_call, api_request, message_sent, permission_change, status_change"),
@@ -217,11 +264,12 @@ server.registerTool("vorim_emit_event", {
217
264
  return text(data);
218
265
  });
219
266
  server.registerTool("vorim_export_audit", {
220
- description: "Export a signed audit bundle for a date range. Returns events with a SHA-256 manifest for tamper-proof verification.",
267
+ description: "Export a signed audit bundle for a date range. Returns events with a SHA-256 manifest for tamper-proof verification. Window must be <= 90 days; the server returns 400 if from > to or the range exceeds the cap. The returned bundle has up to 1,000,000 events; if truncated, the response carries `truncated: true` and the caller should re-export a narrower window.",
221
268
  inputSchema: {
222
- from: z.string().describe("Start date (ISO 8601)"),
223
- to: z.string().describe("End date (ISO 8601)"),
269
+ from: z.string().describe("Start date in ISO 8601 (e.g. 2026-06-01T00:00:00Z)"),
270
+ to: z.string().describe("End date in ISO 8601 (must be on or after `from`; window <= 90 days)"),
224
271
  },
272
+ annotations: { readOnlyHint: true },
225
273
  }, async ({ from, to }) => {
226
274
  const result = await vorimPost("/audit/export", { from, to });
227
275
  return text(result);
@@ -233,8 +281,8 @@ server.registerTool("vorim_verify_trust", {
233
281
  agent_id: z.string().describe("The agent identifier to verify"),
234
282
  },
235
283
  }, async ({ agent_id }) => {
236
- const response = await fetch(`${BASE_URL}/v1/trust/verify/${agent_id}`, {
237
- headers: { "User-Agent": "vorim-mcp-server/1.0.0" },
284
+ const response = await fetch(`${BASE_URL}/v1/trust/verify/${encId(agent_id)}`, {
285
+ headers: { "User-Agent": `vorim-mcp-server/${MCP_VERSION}` },
238
286
  });
239
287
  const json = await response.json();
240
288
  return text(json.data || json);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vorim/mcp-server",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "mcpName": "io.github.Kzino/vorim-mcp-server",
5
5
  "description": "MCP server for Vorim AI — AI agent identity, permissions, and audit trails",
6
6
  "type": "module",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc && chmod 755 build/index.js",
12
+ "prepublishOnly": "npm run build",
12
13
  "dev": "tsx src/index.ts"
13
14
  },
14
15
  "files": [