@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.
- package/build/index.js +64 -16
- 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":
|
|
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:
|
|
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":
|
|
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
|
|
223
|
-
to: z.string().describe("End date
|
|
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":
|
|
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
|
+
"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": [
|