@agentvalet/mcp-server 0.1.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.
- package/dist/index.js +444 -0
- package/package.json +44 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { SignJWT, importPKCS8 } from "jose";
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Startup env validation
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Resolve AGENT_PRIVATE_KEY: accept raw key content or a path via AGENT_PRIVATE_KEY_PATH
|
|
10
|
+
if (!process.env.AGENT_PRIVATE_KEY && process.env.AGENT_PRIVATE_KEY_PATH) {
|
|
11
|
+
try {
|
|
12
|
+
process.env.AGENT_PRIVATE_KEY = readFileSync(process.env.AGENT_PRIVATE_KEY_PATH, "utf-8").trim();
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
process.stderr.write(`[mcp-server] Cannot read AGENT_PRIVATE_KEY_PATH: ${err instanceof Error ? err.message : err}\n`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const REQUIRED_ENV = ["AGENT_PRIVATE_KEY", "AGENT_ID", "OWNER_ID", "PROXY_URL"];
|
|
20
|
+
for (const key of REQUIRED_ENV) {
|
|
21
|
+
if (!process.env[key]) {
|
|
22
|
+
process.stderr.write(`[mcp-server] Missing required environment variable: ${key} (or AGENT_PRIVATE_KEY_PATH)\n`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const AGENT_PRIVATE_KEY_RAW = process.env.AGENT_PRIVATE_KEY;
|
|
27
|
+
const AGENT_ID = process.env.AGENT_ID;
|
|
28
|
+
const OWNER_ID = process.env.OWNER_ID;
|
|
29
|
+
const PROXY_URL = process.env.PROXY_URL.replace(/\/$/, "");
|
|
30
|
+
// If the key is the sentinel value, defer key import — the guard in each tool
|
|
31
|
+
// handler will return a user-friendly error before any signing attempt.
|
|
32
|
+
let privateKey = null;
|
|
33
|
+
if (AGENT_PRIVATE_KEY_RAW !== "PENDING_FIRST_CALL") {
|
|
34
|
+
const rawKey = AGENT_PRIVATE_KEY_RAW.trim();
|
|
35
|
+
const AGENT_PRIVATE_KEY = rawKey.startsWith("-----")
|
|
36
|
+
? rawKey.replace(/\\n/g, "\n")
|
|
37
|
+
: `-----BEGIN PRIVATE KEY-----\n${rawKey}\n-----END PRIVATE KEY-----`;
|
|
38
|
+
try {
|
|
39
|
+
privateKey = await importPKCS8(AGENT_PRIVATE_KEY, "RS256");
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
process.stderr.write(`[mcp-server] Invalid AGENT_PRIVATE_KEY: ${err instanceof Error ? err.message : err}\n`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// JWT signing
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
async function signJWT() {
|
|
50
|
+
if (!privateKey)
|
|
51
|
+
throw new Error("Private key not loaded");
|
|
52
|
+
return new SignJWT({ agent_id: AGENT_ID, owner_id: OWNER_ID })
|
|
53
|
+
.setProtectedHeader({ alg: "RS256" })
|
|
54
|
+
.setIssuedAt()
|
|
55
|
+
.setExpirationTime("60s")
|
|
56
|
+
.sign(privateKey);
|
|
57
|
+
}
|
|
58
|
+
async function notifyBindSecret() {
|
|
59
|
+
try {
|
|
60
|
+
await fetchWithTimeout(`${PROXY_URL}/v1/bind-secret`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { "Content-Type": "application/json" },
|
|
63
|
+
body: JSON.stringify({ agent_id: AGENT_ID, owner_id: OWNER_ID }),
|
|
64
|
+
}, 8_000);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// best-effort — don't block the error response
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function pendingFirstCallResponse() {
|
|
71
|
+
return {
|
|
72
|
+
content: [{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: JSON.stringify({
|
|
75
|
+
error: "Agent not yet activated — owner confirmation pending. Retry in 60 seconds.",
|
|
76
|
+
}),
|
|
77
|
+
}],
|
|
78
|
+
isError: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Tool definitions
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
const ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
85
|
+
const LIST_PLATFORMS_TOOL = {
|
|
86
|
+
name: "list_platforms",
|
|
87
|
+
description: "list_platforms: List the platforms and permission scopes this agent has access to.\nInput: None.\nReturns: platforms array with platformId, platformName, scopes, requireApproval.\nAuth: Bearer JWT.",
|
|
88
|
+
inputSchema: { type: "object", properties: {} },
|
|
89
|
+
};
|
|
90
|
+
const USE_PLATFORM_TOOL = {
|
|
91
|
+
name: "use_platform",
|
|
92
|
+
description: "use_platform: Call an external platform API (Airtable, GitHub, Slack, etc.) through the AgentValet proxy.\nInput: platform (string), endpoint (string), method (GET|POST|PUT|PATCH|DELETE), scope (string), data (object, optional).\nReturns: upstream API response body.\nAuth: Bearer JWT.",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
platform: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Platform ID (e.g. airtable, github, slack)",
|
|
99
|
+
},
|
|
100
|
+
endpoint: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "API path on the target platform (e.g. /v0/meta/bases)",
|
|
103
|
+
},
|
|
104
|
+
method: {
|
|
105
|
+
type: "string",
|
|
106
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
107
|
+
description: "HTTP method to use",
|
|
108
|
+
},
|
|
109
|
+
scope: {
|
|
110
|
+
type: "string",
|
|
111
|
+
description: "Permission scope required for this action (e.g. records:read)",
|
|
112
|
+
},
|
|
113
|
+
data: {
|
|
114
|
+
type: "object",
|
|
115
|
+
description: "Optional request body for POST/PUT/PATCH requests",
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
required: ["platform", "endpoint", "method", "scope"],
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
const AGENT_REGISTER_TOOL = {
|
|
122
|
+
name: "agent_register",
|
|
123
|
+
description: "agent_register: Self-register this agent with an owner. No auth required.\nInput: owner_id (string), agent_name (string), requested_scopes (array of {platformId, scopes}).\nReturns: registration_token, poll_url, client_id, scope, expires_in.\nAuth: None.",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
owner_id: {
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "The owner ID to register this agent under",
|
|
130
|
+
},
|
|
131
|
+
agent_name: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Human-readable name for this agent",
|
|
134
|
+
},
|
|
135
|
+
requested_scopes: {
|
|
136
|
+
type: "array",
|
|
137
|
+
description: "Array of platform scope requests",
|
|
138
|
+
items: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
platformId: { type: "string" },
|
|
142
|
+
scopes: { type: "array", items: { type: "string" } },
|
|
143
|
+
},
|
|
144
|
+
required: ["platformId", "scopes"],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
required: ["owner_id", "agent_name", "requested_scopes"],
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
const AGENT_STATUS_TOOL = {
|
|
152
|
+
name: "agent_status",
|
|
153
|
+
description: "agent_status: Poll registration status using the token from agent_register.\nInput: token (string, required).\nReturns: status (\"pending_approval\"|\"approved\"|\"rejected\"), agent_id (if approved), mcp_config (if approved).\nAuth: None.",
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: "object",
|
|
156
|
+
properties: {
|
|
157
|
+
token: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "Registration token returned by agent_register",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
required: ["token"],
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
const AGENT_PERMISSIONS_TOOL = {
|
|
166
|
+
name: "agent_permissions",
|
|
167
|
+
description: "agent_permissions: List all platforms and permission scopes granted to this agent.\nInput: None.\nReturns: platforms array with platformId, platformName, scopes, requireApproval.\nAuth: Bearer JWT.",
|
|
168
|
+
inputSchema: { type: "object", properties: {} },
|
|
169
|
+
};
|
|
170
|
+
const AUTHZEN_EVALUATE_TOOL = {
|
|
171
|
+
name: "authzen_evaluate",
|
|
172
|
+
description: "authzen_evaluate: Evaluate whether this agent has access to a specific platform scope.\nInput: platform_id (string), scope (string).\nReturns: decision (boolean), reason (\"approved\"|\"denied\"|\"revoked\"|\"scope_not_granted\").\nAuth: None.",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
platform_id: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "The platform identifier (e.g. airtable, github)",
|
|
179
|
+
},
|
|
180
|
+
scope: {
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "The permission scope to evaluate (e.g. records:read)",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
required: ["platform_id", "scope"],
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
const INTENT_RESOLVE_TOOL = {
|
|
189
|
+
name: "intent_resolve",
|
|
190
|
+
description: "intent_resolve: Resolve a natural-language intent to the exact AgentValet API action to call.\nInput: intent (string, required), context (object, optional).\nReturns: resolved (boolean), capability object with endpoint, auth_required, example_request, or suggestions array.\nAuth: None.",
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: "object",
|
|
193
|
+
properties: {
|
|
194
|
+
intent: {
|
|
195
|
+
type: "string",
|
|
196
|
+
description: "Natural-language description of what the agent wants to do",
|
|
197
|
+
},
|
|
198
|
+
context: {
|
|
199
|
+
type: "object",
|
|
200
|
+
description: "Optional context object to aid resolution",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
required: ["intent"],
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// MCP server setup
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
const server = new Server({ name: "agentvalet", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
210
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
211
|
+
tools: [
|
|
212
|
+
LIST_PLATFORMS_TOOL,
|
|
213
|
+
USE_PLATFORM_TOOL,
|
|
214
|
+
AGENT_REGISTER_TOOL,
|
|
215
|
+
AGENT_STATUS_TOOL,
|
|
216
|
+
AGENT_PERMISSIONS_TOOL,
|
|
217
|
+
AUTHZEN_EVALUATE_TOOL,
|
|
218
|
+
INTENT_RESOLVE_TOOL,
|
|
219
|
+
],
|
|
220
|
+
}));
|
|
221
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
222
|
+
const { name, arguments: args } = request.params;
|
|
223
|
+
if (name === "list_platforms") {
|
|
224
|
+
return await handleListPlatforms();
|
|
225
|
+
}
|
|
226
|
+
if (name === "use_platform") {
|
|
227
|
+
if (!args ||
|
|
228
|
+
typeof args.platform !== "string" ||
|
|
229
|
+
typeof args.endpoint !== "string" ||
|
|
230
|
+
typeof args.method !== "string" ||
|
|
231
|
+
typeof args.scope !== "string") {
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: "Invalid or missing tool arguments" }],
|
|
234
|
+
isError: true,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (!ALLOWED_METHODS.includes(args.method)) {
|
|
238
|
+
return {
|
|
239
|
+
content: [{ type: "text", text: `Invalid method: ${args.method}` }],
|
|
240
|
+
isError: true,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return await handleUsePlatform({
|
|
244
|
+
platform: args.platform,
|
|
245
|
+
endpoint: args.endpoint,
|
|
246
|
+
method: args.method,
|
|
247
|
+
scope: args.scope,
|
|
248
|
+
data: args.data,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (name === "agent_register") {
|
|
252
|
+
if (!args || typeof args.owner_id !== "string" || typeof args.agent_name !== "string" || !Array.isArray(args.requested_scopes)) {
|
|
253
|
+
return errorContent("Invalid or missing arguments: owner_id, agent_name, requested_scopes are required");
|
|
254
|
+
}
|
|
255
|
+
return await handleAgentRegister(args);
|
|
256
|
+
}
|
|
257
|
+
if (name === "agent_status") {
|
|
258
|
+
if (!args || typeof args.token !== "string") {
|
|
259
|
+
return errorContent("Invalid or missing argument: token is required");
|
|
260
|
+
}
|
|
261
|
+
return await handleAgentStatus(args.token);
|
|
262
|
+
}
|
|
263
|
+
if (name === "agent_permissions") {
|
|
264
|
+
return await handleAgentPermissions();
|
|
265
|
+
}
|
|
266
|
+
if (name === "authzen_evaluate") {
|
|
267
|
+
if (!args || typeof args.platform_id !== "string" || typeof args.scope !== "string") {
|
|
268
|
+
return errorContent("Invalid or missing arguments: platform_id and scope are required");
|
|
269
|
+
}
|
|
270
|
+
return await handleAuthzenEvaluate(args.platform_id, args.scope);
|
|
271
|
+
}
|
|
272
|
+
if (name === "intent_resolve") {
|
|
273
|
+
if (!args || typeof args.intent !== "string") {
|
|
274
|
+
return errorContent("Invalid or missing argument: intent is required");
|
|
275
|
+
}
|
|
276
|
+
return await handleIntentResolve(args.intent, args.context);
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
280
|
+
isError: true,
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Helpers
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
function fetchWithTimeout(url, init, timeoutMs = 15_000) {
|
|
287
|
+
const ac = new AbortController();
|
|
288
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
289
|
+
return fetch(url, { ...init, signal: ac.signal }).finally(() => clearTimeout(timer));
|
|
290
|
+
}
|
|
291
|
+
async function bearerFetch(url, init) {
|
|
292
|
+
const token = await signJWT();
|
|
293
|
+
return fetchWithTimeout(url, {
|
|
294
|
+
...init,
|
|
295
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...init.headers },
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function errorContent(message) {
|
|
299
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
300
|
+
}
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// Tool handlers
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
async function handleListPlatforms() {
|
|
305
|
+
if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL") {
|
|
306
|
+
await notifyBindSecret();
|
|
307
|
+
return pendingFirstCallResponse();
|
|
308
|
+
}
|
|
309
|
+
let response;
|
|
310
|
+
try {
|
|
311
|
+
response = await bearerFetch(`${PROXY_URL}/v1/agent/permissions`, { method: "GET", headers: {} });
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
return errorContent(`Network error: ${err instanceof Error ? err.message : err}`);
|
|
315
|
+
}
|
|
316
|
+
const body = await response.text();
|
|
317
|
+
if (!response.ok)
|
|
318
|
+
return errorContent(`Proxy error ${response.status}: ${body}`);
|
|
319
|
+
return { content: [{ type: "text", text: body }] };
|
|
320
|
+
}
|
|
321
|
+
async function handleUsePlatform(params) {
|
|
322
|
+
if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL") {
|
|
323
|
+
await notifyBindSecret();
|
|
324
|
+
return pendingFirstCallResponse();
|
|
325
|
+
}
|
|
326
|
+
const requestBody = {
|
|
327
|
+
platform: params.platform,
|
|
328
|
+
endpoint: params.endpoint,
|
|
329
|
+
method: params.method,
|
|
330
|
+
scope: params.scope,
|
|
331
|
+
...(params.data !== undefined && { data: params.data }),
|
|
332
|
+
};
|
|
333
|
+
let response;
|
|
334
|
+
try {
|
|
335
|
+
response = await bearerFetch(`${PROXY_URL}/v1/actions`, {
|
|
336
|
+
method: "POST",
|
|
337
|
+
body: JSON.stringify(requestBody),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
return errorContent(`Network error: ${err instanceof Error ? err.message : err}`);
|
|
342
|
+
}
|
|
343
|
+
const body = await response.text();
|
|
344
|
+
if (!response.ok)
|
|
345
|
+
return errorContent(`Proxy error ${response.status}: ${body}`);
|
|
346
|
+
return { content: [{ type: "text", text: body }] };
|
|
347
|
+
}
|
|
348
|
+
async function handleAgentRegister(args) {
|
|
349
|
+
let response;
|
|
350
|
+
try {
|
|
351
|
+
response = await fetchWithTimeout(`${PROXY_URL}/v1/register`, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
headers: { "Content-Type": "application/json" },
|
|
354
|
+
body: JSON.stringify(args),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
return errorContent(`Network error: ${err instanceof Error ? err.message : err}`);
|
|
359
|
+
}
|
|
360
|
+
const body = await response.text();
|
|
361
|
+
if (!response.ok)
|
|
362
|
+
return errorContent(`Proxy error ${response.status}: ${body}`);
|
|
363
|
+
return { content: [{ type: "text", text: body }] };
|
|
364
|
+
}
|
|
365
|
+
async function handleAgentStatus(token) {
|
|
366
|
+
let response;
|
|
367
|
+
try {
|
|
368
|
+
response = await fetchWithTimeout(`${PROXY_URL}/v1/register/status/${encodeURIComponent(token)}`, {
|
|
369
|
+
method: "GET",
|
|
370
|
+
headers: { "Content-Type": "application/json" },
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
catch (err) {
|
|
374
|
+
return errorContent(`Network error: ${err instanceof Error ? err.message : err}`);
|
|
375
|
+
}
|
|
376
|
+
const body = await response.text();
|
|
377
|
+
if (!response.ok)
|
|
378
|
+
return errorContent(`Proxy error ${response.status}: ${body}`);
|
|
379
|
+
return { content: [{ type: "text", text: body }] };
|
|
380
|
+
}
|
|
381
|
+
async function handleAgentPermissions() {
|
|
382
|
+
if (AGENT_PRIVATE_KEY_RAW === "PENDING_FIRST_CALL") {
|
|
383
|
+
await notifyBindSecret();
|
|
384
|
+
return pendingFirstCallResponse();
|
|
385
|
+
}
|
|
386
|
+
let response;
|
|
387
|
+
try {
|
|
388
|
+
response = await bearerFetch(`${PROXY_URL}/v1/agent/permissions`, { method: "GET", headers: {} });
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
return errorContent(`Network error: ${err instanceof Error ? err.message : err}`);
|
|
392
|
+
}
|
|
393
|
+
const body = await response.text();
|
|
394
|
+
if (!response.ok)
|
|
395
|
+
return errorContent(`Proxy error ${response.status}: ${body}`);
|
|
396
|
+
return { content: [{ type: "text", text: body }] };
|
|
397
|
+
}
|
|
398
|
+
async function handleAuthzenEvaluate(platformId, scope) {
|
|
399
|
+
const authzenBody = {
|
|
400
|
+
subject: { type: "agent", id: AGENT_ID },
|
|
401
|
+
action: { name: "tool_call" },
|
|
402
|
+
resource: { type: "platform_scope", id: `${platformId}:${scope}` },
|
|
403
|
+
};
|
|
404
|
+
let response;
|
|
405
|
+
try {
|
|
406
|
+
response = await fetchWithTimeout(`${PROXY_URL}/v1/authzen/access`, {
|
|
407
|
+
method: "POST",
|
|
408
|
+
headers: { "Content-Type": "application/json" },
|
|
409
|
+
body: JSON.stringify(authzenBody),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
catch (err) {
|
|
413
|
+
return errorContent(`Network error: ${err instanceof Error ? err.message : err}`);
|
|
414
|
+
}
|
|
415
|
+
const body = await response.text();
|
|
416
|
+
if (!response.ok)
|
|
417
|
+
return errorContent(`Proxy error ${response.status}: ${body}`);
|
|
418
|
+
return { content: [{ type: "text", text: body }] };
|
|
419
|
+
}
|
|
420
|
+
async function handleIntentResolve(intent, context) {
|
|
421
|
+
const requestBody = { intent };
|
|
422
|
+
if (context !== undefined)
|
|
423
|
+
requestBody.context = context;
|
|
424
|
+
let response;
|
|
425
|
+
try {
|
|
426
|
+
response = await fetchWithTimeout(`${PROXY_URL}/v1/intent/resolve`, {
|
|
427
|
+
method: "POST",
|
|
428
|
+
headers: { "Content-Type": "application/json" },
|
|
429
|
+
body: JSON.stringify(requestBody),
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
433
|
+
return errorContent(`Network error: ${err instanceof Error ? err.message : err}`);
|
|
434
|
+
}
|
|
435
|
+
const body = await response.text();
|
|
436
|
+
if (!response.ok)
|
|
437
|
+
return errorContent(`Proxy error ${response.status}: ${body}`);
|
|
438
|
+
return { content: [{ type: "text", text: body }] };
|
|
439
|
+
}
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
// Connect transport
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
const transport = new StdioServerTransport();
|
|
444
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentvalet/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AgentValet MCP server — lets AI agents call approved platforms via the AgentValet proxy",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agentvalet-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
22
|
+
"jose": "^5.3.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"tsx": "^4.7.0",
|
|
27
|
+
"typescript": "^5.4.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/MCSEdwin/agentvalet.git",
|
|
35
|
+
"directory": "apps/mcp-server"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"ai",
|
|
39
|
+
"agents",
|
|
40
|
+
"mcp",
|
|
41
|
+
"agentvalet"
|
|
42
|
+
],
|
|
43
|
+
"license": "MIT"
|
|
44
|
+
}
|