@mgsoftwarebv/mg-dashboard-mcp 3.13.0 → 3.13.1

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
@@ -9,7 +9,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
9
9
  import { ListToolsRequestSchema, CallToolRequestSchema, isInitializeRequest, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListToolsResultSchema, CallToolResultSchema, ListPromptsResultSchema, GetPromptResultSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, EmptyResultSchema } from '@modelcontextprotocol/sdk/types.js';
10
10
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
11
11
  import { createServer } from 'http';
12
- import { randomUUID, createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
12
+ import { randomUUID, createHash, randomBytes, createDecipheriv, createCipheriv } from 'crypto';
13
13
  import { createClient } from '@supabase/supabase-js';
14
14
  import { readFile, mkdtemp, writeFile, rm } from 'fs/promises';
15
15
  import { tmpdir } from 'os';
@@ -1213,6 +1213,383 @@ LinkedIn: ${pageLinkedIn.join(", ")}`;
1213
1213
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
1214
1214
  }
1215
1215
  }
1216
+
1217
+ // src/vercel-tools.ts
1218
+ var VERCEL_API = "https://api.vercel.com";
1219
+ var VERCEL_TOOLS = [
1220
+ {
1221
+ name: "vercel-projects",
1222
+ description: "List Vercel projects in the configured account/team. Returns id, name, framework, GitHub repo link, production URL, and the latest deployment summary. Use the project name or id as input to vercel-deployments.",
1223
+ inputSchema: {
1224
+ type: "object",
1225
+ properties: {
1226
+ limit: { type: "number", description: "Max projects to return (default 50, max 200)" },
1227
+ search: { type: "string", description: "Substring filter on project name (case-insensitive)" }
1228
+ }
1229
+ }
1230
+ },
1231
+ {
1232
+ name: "vercel-deployments",
1233
+ description: "List recent deployments for a Vercel project. Returns deployment ID, state, target, branch, commit and timestamps. Use the deployment ID with vercel-build-log or vercel-runtime-log.",
1234
+ inputSchema: {
1235
+ type: "object",
1236
+ properties: {
1237
+ project: { type: "string", description: "Vercel project ID or name (from vercel-projects)" },
1238
+ state: {
1239
+ type: "string",
1240
+ description: "Optional state filter: BUILDING, ERROR, INITIALIZING, QUEUED, READY, CANCELED"
1241
+ },
1242
+ target: { type: "string", description: "Optional target filter: production or preview" },
1243
+ limit: { type: "number", description: "Max deployments to return (default 20, max 100)" }
1244
+ },
1245
+ required: ["project"]
1246
+ }
1247
+ },
1248
+ {
1249
+ name: "vercel-build-log",
1250
+ description: "Get build / deployment console events (stdout, stderr, command, exit) for a Vercel deployment. Use this when a deployment failed at build time.",
1251
+ inputSchema: {
1252
+ type: "object",
1253
+ properties: {
1254
+ deploymentId: { type: "string", description: "Vercel deployment ID (from vercel-deployments)" },
1255
+ limit: { type: "number", description: "Max events to return (default 500, max 5000)" }
1256
+ },
1257
+ required: ["deploymentId"]
1258
+ }
1259
+ },
1260
+ {
1261
+ name: "vercel-runtime-log",
1262
+ description: "Get runtime / function logs for a deployment (the application output after the build succeeds). Requires both project and deployment IDs.",
1263
+ inputSchema: {
1264
+ type: "object",
1265
+ properties: {
1266
+ project: { type: "string", description: "Vercel project ID or name" },
1267
+ deploymentId: { type: "string", description: "Vercel deployment ID" },
1268
+ limit: { type: "number", description: "Max log entries to return (default 200, max 1000)" },
1269
+ sinceMinutes: {
1270
+ type: "number",
1271
+ description: "Only return logs from the last N minutes (default 60)"
1272
+ }
1273
+ },
1274
+ required: ["project", "deploymentId"]
1275
+ }
1276
+ },
1277
+ {
1278
+ name: "vercel-webhook-history",
1279
+ description: "List recent Vercel deployment webhook deliveries (from the vercel_webhook_logs table). Useful to confirm Telegram / push notifications were sent and to see the original payload metadata.",
1280
+ inputSchema: {
1281
+ type: "object",
1282
+ properties: {
1283
+ projectName: { type: "string", description: "Optional filter on project name" },
1284
+ status: {
1285
+ type: "string",
1286
+ description: "Optional status filter: sent, skipped, error"
1287
+ },
1288
+ limit: { type: "number", description: "Max rows to return (default 25, max 200)" }
1289
+ }
1290
+ }
1291
+ }
1292
+ ];
1293
+ var VERCEL_TOOL_NAMES = new Set(VERCEL_TOOLS.map((t) => t.name));
1294
+ var VERCEL_TOOL_MODULE_MAP = {
1295
+ "vercel-projects": "ci_cd",
1296
+ "vercel-deployments": "ci_cd",
1297
+ "vercel-build-log": "ci_cd",
1298
+ "vercel-runtime-log": "ci_cd",
1299
+ "vercel-webhook-history": "ci_cd"
1300
+ };
1301
+ async function vercelFetch(token, path) {
1302
+ const res = await fetch(`${VERCEL_API}${path}`, {
1303
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }
1304
+ });
1305
+ const body = await res.text();
1306
+ let parsed = null;
1307
+ try {
1308
+ parsed = body ? JSON.parse(body) : null;
1309
+ } catch {
1310
+ }
1311
+ if (!res.ok) {
1312
+ const msg = parsed?.error?.message ?? parsed?.message ?? body ?? `HTTP ${res.status}`;
1313
+ return { data: null, error: `Vercel API ${res.status}: ${msg}`, status: res.status };
1314
+ }
1315
+ return { data: parsed, error: null, status: res.status };
1316
+ }
1317
+ async function listVercelProjectsAll(token, limit) {
1318
+ const collected = [];
1319
+ let nextTimestamp;
1320
+ while (collected.length < limit) {
1321
+ const params = new URLSearchParams({ limit: String(Math.min(limit - collected.length, 100)) });
1322
+ if (nextTimestamp) params.set("until", String(nextTimestamp));
1323
+ const res = await vercelFetch(token, `/v9/projects?${params.toString()}`);
1324
+ if (res.error) return { projects: [], error: res.error };
1325
+ if (!res.data?.projects?.length) break;
1326
+ collected.push(...res.data.projects);
1327
+ if (!res.data.pagination?.next) break;
1328
+ nextTimestamp = res.data.pagination.next;
1329
+ }
1330
+ return { projects: collected, error: null };
1331
+ }
1332
+ async function listVercelDeployments(token, options) {
1333
+ const params = new URLSearchParams({
1334
+ limit: String(options.limit),
1335
+ projectId: options.projectId
1336
+ });
1337
+ if (options.state) params.set("state", options.state);
1338
+ if (options.target) params.set("target", options.target);
1339
+ const res = await vercelFetch(
1340
+ token,
1341
+ `/v6/deployments?${params.toString()}`
1342
+ );
1343
+ if (res.error) return { deployments: [], error: res.error };
1344
+ return { deployments: res.data?.deployments ?? [], error: null };
1345
+ }
1346
+ async function getDeploymentBuildEvents(token, deploymentId, limit) {
1347
+ const params = new URLSearchParams({
1348
+ limit: String(limit),
1349
+ direction: "forward",
1350
+ follow: "0"
1351
+ });
1352
+ const url = `${VERCEL_API}/v3/deployments/${encodeURIComponent(deploymentId)}/events?${params.toString()}`;
1353
+ const res = await fetch(url, {
1354
+ headers: { Authorization: `Bearer ${token}` }
1355
+ });
1356
+ const body = await res.text();
1357
+ if (!res.ok) {
1358
+ let msg = body;
1359
+ try {
1360
+ const parsed = JSON.parse(body);
1361
+ msg = parsed?.error?.message ?? parsed?.message ?? body;
1362
+ } catch {
1363
+ }
1364
+ return { events: [], error: `Vercel API ${res.status}: ${msg}` };
1365
+ }
1366
+ const events = [];
1367
+ const trimmed = body.trim();
1368
+ if (!trimmed) return { events, error: null };
1369
+ try {
1370
+ const parsed = JSON.parse(trimmed);
1371
+ if (Array.isArray(parsed)) {
1372
+ for (const r of parsed) events.push(r);
1373
+ return { events, error: null };
1374
+ }
1375
+ } catch {
1376
+ }
1377
+ for (const line of trimmed.split("\n")) {
1378
+ const s = line.trim();
1379
+ if (!s) continue;
1380
+ try {
1381
+ events.push(JSON.parse(s));
1382
+ } catch {
1383
+ }
1384
+ }
1385
+ return { events, error: null };
1386
+ }
1387
+ async function getRuntimeLogs(token, projectIdOrName, deploymentId, limit, sinceMs) {
1388
+ const params = new URLSearchParams({
1389
+ limit: String(Math.min(Math.max(limit, 1), 1e3))
1390
+ });
1391
+ if (sinceMs) params.set("since", String(sinceMs));
1392
+ const url = `${VERCEL_API}/v1/projects/${encodeURIComponent(projectIdOrName)}/deployments/${encodeURIComponent(deploymentId)}/runtime-logs?${params.toString()}`;
1393
+ const res = await fetch(url, {
1394
+ headers: { Authorization: `Bearer ${token}` }
1395
+ });
1396
+ const body = await res.text();
1397
+ if (!res.ok) {
1398
+ let msg = body;
1399
+ try {
1400
+ const parsed = JSON.parse(body);
1401
+ msg = parsed?.error?.message ?? parsed?.message ?? body;
1402
+ } catch {
1403
+ }
1404
+ return { logs: [], error: `Vercel API ${res.status}: ${msg}` };
1405
+ }
1406
+ const logs = [];
1407
+ const trimmed = body.trim();
1408
+ if (!trimmed) return { logs, error: null };
1409
+ try {
1410
+ const parsed = JSON.parse(trimmed);
1411
+ if (Array.isArray(parsed)) {
1412
+ for (const r of parsed) logs.push(r);
1413
+ return { logs, error: null };
1414
+ }
1415
+ } catch {
1416
+ }
1417
+ for (const line of trimmed.split("\n")) {
1418
+ const s = line.trim();
1419
+ if (!s) continue;
1420
+ try {
1421
+ logs.push(JSON.parse(s));
1422
+ } catch {
1423
+ }
1424
+ }
1425
+ return { logs, error: null };
1426
+ }
1427
+ async function getVercelToken(deps) {
1428
+ const { data, error } = await deps.supabase.from("app_setting").select("vercel_token_encrypted").maybeSingle();
1429
+ if (error) throw new Error(`Could not read app_setting: ${error.message}`);
1430
+ if (!data?.vercel_token_encrypted) {
1431
+ throw new Error("Vercel API token is not configured. Add it in dashboard Settings.");
1432
+ }
1433
+ try {
1434
+ return deps.decrypt(data.vercel_token_encrypted);
1435
+ } catch {
1436
+ throw new Error("Failed to decrypt the stored Vercel API token.");
1437
+ }
1438
+ }
1439
+ async function resolveProjectId(token, projectInput) {
1440
+ const res = await vercelFetch(
1441
+ token,
1442
+ `/v9/projects/${encodeURIComponent(projectInput)}`
1443
+ );
1444
+ if (res.error || !res.data?.id) {
1445
+ throw new Error(
1446
+ `Could not resolve Vercel project "${projectInput}": ${res.error ?? "not found"}`
1447
+ );
1448
+ }
1449
+ return res.data.id;
1450
+ }
1451
+ function formatTimestamp(ms) {
1452
+ if (!ms) return "";
1453
+ return new Date(ms).toLocaleString("nl-NL", { timeZone: "Europe/Amsterdam" });
1454
+ }
1455
+ function formatProjectsTable(projects) {
1456
+ if (projects.length === 0) return "No Vercel projects found";
1457
+ const lines = projects.map((p) => {
1458
+ const repo = p.link ? `${p.link.org ?? ""}/${p.link.repo ?? ""}` : "";
1459
+ const prodUrl = p.targets?.production?.alias?.[0] ?? p.targets?.production?.url ?? "";
1460
+ const latest = p.latestDeployments?.[0];
1461
+ const latestStr = latest ? `${latest.state} @ ${formatTimestamp(latest.createdAt)}` : "";
1462
+ return `${p.id.padEnd(28)} ${(p.name || "").padEnd(40)} ${(p.framework || "-").padEnd(10)} ${repo.padEnd(40)} ${prodUrl.padEnd(35)} ${latestStr}`;
1463
+ });
1464
+ const header = `${"ID".padEnd(28)} ${"NAME".padEnd(40)} ${"FRAMEWORK".padEnd(10)} ${"REPO".padEnd(40)} ${"PROD URL".padEnd(35)} LATEST`;
1465
+ return `${header}
1466
+ ${"-".repeat(header.length)}
1467
+ ${lines.join("\n")}`;
1468
+ }
1469
+ function formatDeploymentsTable(deployments) {
1470
+ if (deployments.length === 0) return "No deployments found";
1471
+ const lines = deployments.map((d) => {
1472
+ const branch = d.meta?.githubCommitRef ?? "";
1473
+ const sha = d.meta?.githubCommitSha?.slice(0, 7) ?? "";
1474
+ const msg = (d.meta?.githubCommitMessage ?? "").replace(/\n.*/s, "").slice(0, 60);
1475
+ return `${d.uid.padEnd(28)} ${d.state.padEnd(11)} ${(d.target || "-").padEnd(10)} ${branch.padEnd(20)} ${sha.padEnd(8)} ${formatTimestamp(d.created).padEnd(20)} ${msg}`;
1476
+ });
1477
+ const header = `${"ID".padEnd(28)} ${"STATE".padEnd(11)} ${"TARGET".padEnd(10)} ${"BRANCH".padEnd(20)} ${"COMMIT".padEnd(8)} ${"CREATED".padEnd(20)} MESSAGE`;
1478
+ return `${header}
1479
+ ${"-".repeat(header.length)}
1480
+ ${lines.join("\n")}`;
1481
+ }
1482
+ function formatBuildEvents(events) {
1483
+ if (events.length === 0) return "No build events found for this deployment";
1484
+ const filtered = events.filter(
1485
+ (e) => ["command", "stdout", "stderr", "exit", "delimiter", "deployment-state"].includes(e.type)
1486
+ );
1487
+ return filtered.map((e) => {
1488
+ const time = e.created ? new Date(e.created).toISOString().slice(11, 19) : "--:--:--";
1489
+ const text = (e.payload?.text ?? e.text ?? "").replace(/\s+$/u, "");
1490
+ const prefix = e.type === "stderr" ? "!" : " ";
1491
+ return `${prefix}${time} [${e.type.padEnd(16)}] ${text}`;
1492
+ }).join("\n");
1493
+ }
1494
+ function formatRuntimeLogs(logs) {
1495
+ if (logs.length === 0) {
1496
+ return "No runtime logs found for this deployment in the requested window";
1497
+ }
1498
+ return logs.map((l) => {
1499
+ const ts = l.timestampInMs ?? l.timestamp ?? 0;
1500
+ const time = ts ? new Date(ts).toISOString().slice(11, 23) : "--:--:--";
1501
+ const level = (l.level ?? "info").toUpperCase().padEnd(5);
1502
+ const status = l.proxy?.statusCode ?? l.responseStatusCode;
1503
+ const reqInfo = l.proxy?.method ? `${l.proxy.method} ${l.proxy.path ?? ""} ${status ?? ""}`.trim() : "";
1504
+ const message = (l.message ?? l.text ?? "").replace(/\s+$/u, "");
1505
+ const reqId = l.requestId ? ` [${l.requestId.slice(0, 12)}]` : "";
1506
+ const head = reqInfo ? `${reqInfo}${reqId}` : reqId.trim();
1507
+ return `${time} [${level}] ${head ? head + " " : ""}${message}`;
1508
+ }).join("\n");
1509
+ }
1510
+ function formatWebhookHistory(rows) {
1511
+ if (rows.length === 0) return "No webhook log rows match those filters";
1512
+ const lines = rows.map((r) => {
1513
+ const ts = new Date(r.created_at).toLocaleString("nl-NL", { timeZone: "Europe/Amsterdam" });
1514
+ const note = r.error_message ?? r.message ?? "";
1515
+ return `${ts.padEnd(20)} ${r.event_type.padEnd(22)} ${r.status.padEnd(8)} ${(r.project_name ?? "").padEnd(40)} ${(r.target ?? "").padEnd(10)} ${(r.deployment_id ?? "").padEnd(28)} ${note}`;
1516
+ });
1517
+ const header = `${"WHEN".padEnd(20)} ${"EVENT".padEnd(22)} ${"STATUS".padEnd(8)} ${"PROJECT".padEnd(40)} ${"TARGET".padEnd(10)} ${"DEPLOYMENT".padEnd(28)} NOTE`;
1518
+ return `${header}
1519
+ ${"-".repeat(header.length)}
1520
+ ${lines.join("\n")}`;
1521
+ }
1522
+ async function handleVercelTool(name, args2, deps) {
1523
+ switch (name) {
1524
+ case "vercel-projects": {
1525
+ const token = await getVercelToken(deps);
1526
+ const limit = Math.min(Math.max(Number(args2.limit) || 50, 1), 200);
1527
+ const search = typeof args2.search === "string" ? args2.search.toLowerCase() : null;
1528
+ const { projects, error } = await listVercelProjectsAll(token, limit);
1529
+ if (error) return { content: [{ type: "text", text: `Error: ${error}` }] };
1530
+ const filtered = search ? projects.filter((p) => (p.name || "").toLowerCase().includes(search)) : projects;
1531
+ return { content: [{ type: "text", text: formatProjectsTable(filtered) }] };
1532
+ }
1533
+ case "vercel-deployments": {
1534
+ const token = await getVercelToken(deps);
1535
+ const projectInput = String(args2.project);
1536
+ const limit = Math.min(Math.max(Number(args2.limit) || 20, 1), 100);
1537
+ const projectId = await resolveProjectId(token, projectInput);
1538
+ const { deployments, error } = await listVercelDeployments(token, {
1539
+ projectId,
1540
+ limit,
1541
+ state: args2.state ? String(args2.state) : void 0,
1542
+ target: args2.target ? String(args2.target) : void 0
1543
+ });
1544
+ if (error) return { content: [{ type: "text", text: `Error: ${error}` }] };
1545
+ return { content: [{ type: "text", text: formatDeploymentsTable(deployments) }] };
1546
+ }
1547
+ case "vercel-build-log": {
1548
+ const token = await getVercelToken(deps);
1549
+ const deploymentId = String(args2.deploymentId);
1550
+ const limit = Math.min(Math.max(Number(args2.limit) || 500, 1), 5e3);
1551
+ const { events, error } = await getDeploymentBuildEvents(token, deploymentId, limit);
1552
+ if (error) return { content: [{ type: "text", text: `Error: ${error}` }] };
1553
+ return { content: [{ type: "text", text: formatBuildEvents(events) }] };
1554
+ }
1555
+ case "vercel-runtime-log": {
1556
+ const token = await getVercelToken(deps);
1557
+ const projectInput = String(args2.project);
1558
+ const deploymentId = String(args2.deploymentId);
1559
+ const limit = Math.min(Math.max(Number(args2.limit) || 200, 1), 1e3);
1560
+ const sinceMinutes = Math.min(Math.max(Number(args2.sinceMinutes) || 60, 1), 7 * 24 * 60);
1561
+ const sinceMs = Date.now() - sinceMinutes * 6e4;
1562
+ const projectId = await resolveProjectId(token, projectInput);
1563
+ const { logs, error } = await getRuntimeLogs(
1564
+ token,
1565
+ projectId,
1566
+ deploymentId,
1567
+ limit,
1568
+ sinceMs
1569
+ );
1570
+ if (error) {
1571
+ const hint = error.includes("404") || error.includes("400") ? "\n\nThis endpoint requires both project ID and deployment ID and may not be available on every Vercel plan. Use vercel-webhook-history or the supabase MCP (vercel_deployment_log table) for archived runtime logs." : "";
1572
+ return { content: [{ type: "text", text: `Error: ${error}${hint}` }] };
1573
+ }
1574
+ return { content: [{ type: "text", text: formatRuntimeLogs(logs) }] };
1575
+ }
1576
+ case "vercel-webhook-history": {
1577
+ const limit = Math.min(Math.max(Number(args2.limit) || 25, 1), 200);
1578
+ let query = deps.supabase.from("vercel_webhook_logs").select(
1579
+ "id, event_type, status, project_name, deployment_id, target, message, error_message, created_at"
1580
+ ).order("created_at", { ascending: false }).limit(limit);
1581
+ if (args2.projectName) query = query.eq("project_name", String(args2.projectName));
1582
+ if (args2.status) query = query.eq("status", String(args2.status));
1583
+ const { data, error } = await query;
1584
+ if (error) return { content: [{ type: "text", text: `Error: ${error.message}` }] };
1585
+ return {
1586
+ content: [{ type: "text", text: formatWebhookHistory(data ?? []) }]
1587
+ };
1588
+ }
1589
+ default:
1590
+ return { content: [{ type: "text", text: `Unknown vercel tool: ${name}` }] };
1591
+ }
1592
+ }
1216
1593
  var args = process.argv.slice(2);
1217
1594
  function getArg2(name) {
1218
1595
  return args.find((a) => a.startsWith(`--${name}=`))?.split("=").slice(1).join("=");
@@ -1388,7 +1765,8 @@ var TOOL_MODULE_MAP = {
1388
1765
  "dns-update": "domains",
1389
1766
  "dns-delete": "domains",
1390
1767
  ...TRIGGER_TOOL_MODULE_MAP,
1391
- ...AGENT_TOOL_MODULE_MAP
1768
+ ...AGENT_TOOL_MODULE_MAP,
1769
+ ...VERCEL_TOOL_MODULE_MAP
1392
1770
  };
1393
1771
  var authContext = null;
1394
1772
  async function validateApiKey(key) {
@@ -1691,7 +2069,7 @@ function decrypt(payload) {
1691
2069
  decrypted += decipher.final("utf8");
1692
2070
  return decrypted;
1693
2071
  }
1694
- var VERCEL_API = "https://api.vercel.com";
2072
+ var VERCEL_API2 = "https://api.vercel.com";
1695
2073
  function parseEnvContent(content) {
1696
2074
  const result = {};
1697
2075
  for (const line of content.split("\n")) {
@@ -1721,7 +2099,7 @@ function getVercelEnvSyncTargetings(stageType, customEnvId) {
1721
2099
  async function syncEnvVarsToVercel(token, projectId, envVars) {
1722
2100
  if (envVars.length === 0) return { created: 0, error: null };
1723
2101
  const res = await fetch(
1724
- `${VERCEL_API}/v10/projects/${encodeURIComponent(projectId)}/env?upsert=true`,
2102
+ `${VERCEL_API2}/v10/projects/${encodeURIComponent(projectId)}/env?upsert=true`,
1725
2103
  {
1726
2104
  method: "POST",
1727
2105
  headers: {
@@ -3792,9 +4170,11 @@ var TOOLS = [
3792
4170
  // ----- Trigger.dev -----
3793
4171
  ...TRIGGER_TOOLS,
3794
4172
  // ----- Agent Reporting -----
3795
- ...AGENT_TOOLS
4173
+ ...AGENT_TOOLS,
4174
+ // ----- Vercel -----
4175
+ ...VERCEL_TOOLS
3796
4176
  ];
3797
- var MCP_VERSION = "3.7.0";
4177
+ var MCP_VERSION = "3.13.1";
3798
4178
  async function handleListTools() {
3799
4179
  if (!authContext) return { tools: TOOLS };
3800
4180
  const accessible = TOOLS.filter((tool) => {
@@ -5120,6 +5500,9 @@ ${lines.join("\n")}` }] };
5120
5500
  if (AGENT_TOOL_NAMES.has(name)) {
5121
5501
  return handleAgentTool(name, a);
5122
5502
  }
5503
+ if (VERCEL_TOOL_NAMES.has(name)) {
5504
+ return handleVercelTool(name, a, { supabase, decrypt });
5505
+ }
5123
5506
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
5124
5507
  }
5125
5508
  } catch (err) {