@mgsoftwarebv/mg-dashboard-mcp 3.13.0 → 3.14.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 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,393 @@ 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-logs.",
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-logs",
1250
+ description: 'Unified log inspector for Vercel. Use `kind` to pick the source:\n- "build" (default): build / deployment console events (stdout, stderr, command, exit). Requires deploymentId.\n- "runtime": runtime / function logs after a successful build. Requires project + deploymentId.\n- "webhooks": our own vercel_webhook_logs table (Telegram / push delivery history). No deployment needed.\nPick deployments via vercel-deployments first.',
1251
+ inputSchema: {
1252
+ type: "object",
1253
+ properties: {
1254
+ kind: {
1255
+ type: "string",
1256
+ enum: ["build", "runtime", "webhooks"],
1257
+ description: "Which log stream to fetch (default: build)."
1258
+ },
1259
+ project: {
1260
+ type: "string",
1261
+ description: 'Vercel project ID or name (required for kind="runtime").'
1262
+ },
1263
+ deploymentId: {
1264
+ type: "string",
1265
+ description: 'Vercel deployment ID (required for kind="build" or "runtime").'
1266
+ },
1267
+ projectName: {
1268
+ type: "string",
1269
+ description: 'Optional project_name filter (kind="webhooks" only).'
1270
+ },
1271
+ status: {
1272
+ type: "string",
1273
+ description: 'Optional status filter (kind="webhooks" only): sent, skipped, error.'
1274
+ },
1275
+ sinceMinutes: {
1276
+ type: "number",
1277
+ description: 'Time window in minutes (kind="runtime" only, default 60, max 7 days).'
1278
+ },
1279
+ limit: {
1280
+ type: "number",
1281
+ description: "Max entries to return. Defaults per kind: build=500 (max 5000), runtime=200 (max 1000), webhooks=25 (max 200)."
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+ ];
1287
+ var VERCEL_TOOL_NAMES = new Set(VERCEL_TOOLS.map((t) => t.name));
1288
+ var VERCEL_TOOL_MODULE_MAP = {
1289
+ "vercel-projects": "ci_cd",
1290
+ "vercel-deployments": "ci_cd",
1291
+ "vercel-logs": "ci_cd"
1292
+ };
1293
+ async function vercelFetch(token, path) {
1294
+ const res = await fetch(`${VERCEL_API}${path}`, {
1295
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }
1296
+ });
1297
+ const body = await res.text();
1298
+ let parsed = null;
1299
+ try {
1300
+ parsed = body ? JSON.parse(body) : null;
1301
+ } catch {
1302
+ }
1303
+ if (!res.ok) {
1304
+ const msg = parsed?.error?.message ?? parsed?.message ?? body ?? `HTTP ${res.status}`;
1305
+ return { data: null, error: `Vercel API ${res.status}: ${msg}`, status: res.status };
1306
+ }
1307
+ return { data: parsed, error: null, status: res.status };
1308
+ }
1309
+ async function listVercelProjectsAll(token, limit) {
1310
+ const collected = [];
1311
+ let nextTimestamp;
1312
+ while (collected.length < limit) {
1313
+ const params = new URLSearchParams({ limit: String(Math.min(limit - collected.length, 100)) });
1314
+ if (nextTimestamp) params.set("until", String(nextTimestamp));
1315
+ const res = await vercelFetch(token, `/v9/projects?${params.toString()}`);
1316
+ if (res.error) return { projects: [], error: res.error };
1317
+ if (!res.data?.projects?.length) break;
1318
+ collected.push(...res.data.projects);
1319
+ if (!res.data.pagination?.next) break;
1320
+ nextTimestamp = res.data.pagination.next;
1321
+ }
1322
+ return { projects: collected, error: null };
1323
+ }
1324
+ async function listVercelDeployments(token, options) {
1325
+ const params = new URLSearchParams({
1326
+ limit: String(options.limit),
1327
+ projectId: options.projectId
1328
+ });
1329
+ if (options.state) params.set("state", options.state);
1330
+ if (options.target) params.set("target", options.target);
1331
+ const res = await vercelFetch(
1332
+ token,
1333
+ `/v6/deployments?${params.toString()}`
1334
+ );
1335
+ if (res.error) return { deployments: [], error: res.error };
1336
+ return { deployments: res.data?.deployments ?? [], error: null };
1337
+ }
1338
+ async function getDeploymentBuildEvents(token, deploymentId, limit) {
1339
+ const params = new URLSearchParams({
1340
+ limit: String(limit),
1341
+ direction: "forward",
1342
+ follow: "0"
1343
+ });
1344
+ const url = `${VERCEL_API}/v3/deployments/${encodeURIComponent(deploymentId)}/events?${params.toString()}`;
1345
+ const res = await fetch(url, {
1346
+ headers: { Authorization: `Bearer ${token}` }
1347
+ });
1348
+ const body = await res.text();
1349
+ if (!res.ok) {
1350
+ let msg = body;
1351
+ try {
1352
+ const parsed = JSON.parse(body);
1353
+ msg = parsed?.error?.message ?? parsed?.message ?? body;
1354
+ } catch {
1355
+ }
1356
+ return { events: [], error: `Vercel API ${res.status}: ${msg}` };
1357
+ }
1358
+ const events = [];
1359
+ const trimmed = body.trim();
1360
+ if (!trimmed) return { events, error: null };
1361
+ try {
1362
+ const parsed = JSON.parse(trimmed);
1363
+ if (Array.isArray(parsed)) {
1364
+ for (const r of parsed) events.push(r);
1365
+ return { events, error: null };
1366
+ }
1367
+ } catch {
1368
+ }
1369
+ for (const line of trimmed.split("\n")) {
1370
+ const s = line.trim();
1371
+ if (!s) continue;
1372
+ try {
1373
+ events.push(JSON.parse(s));
1374
+ } catch {
1375
+ }
1376
+ }
1377
+ return { events, error: null };
1378
+ }
1379
+ async function getRuntimeLogs(token, projectIdOrName, deploymentId, limit, sinceMs) {
1380
+ const params = new URLSearchParams({
1381
+ limit: String(Math.min(Math.max(limit, 1), 1e3))
1382
+ });
1383
+ if (sinceMs) params.set("since", String(sinceMs));
1384
+ const url = `${VERCEL_API}/v1/projects/${encodeURIComponent(projectIdOrName)}/deployments/${encodeURIComponent(deploymentId)}/runtime-logs?${params.toString()}`;
1385
+ const res = await fetch(url, {
1386
+ headers: { Authorization: `Bearer ${token}` }
1387
+ });
1388
+ const body = await res.text();
1389
+ if (!res.ok) {
1390
+ let msg = body;
1391
+ try {
1392
+ const parsed = JSON.parse(body);
1393
+ msg = parsed?.error?.message ?? parsed?.message ?? body;
1394
+ } catch {
1395
+ }
1396
+ return { logs: [], error: `Vercel API ${res.status}: ${msg}` };
1397
+ }
1398
+ const logs = [];
1399
+ const trimmed = body.trim();
1400
+ if (!trimmed) return { logs, error: null };
1401
+ try {
1402
+ const parsed = JSON.parse(trimmed);
1403
+ if (Array.isArray(parsed)) {
1404
+ for (const r of parsed) logs.push(r);
1405
+ return { logs, error: null };
1406
+ }
1407
+ } catch {
1408
+ }
1409
+ for (const line of trimmed.split("\n")) {
1410
+ const s = line.trim();
1411
+ if (!s) continue;
1412
+ try {
1413
+ logs.push(JSON.parse(s));
1414
+ } catch {
1415
+ }
1416
+ }
1417
+ return { logs, error: null };
1418
+ }
1419
+ async function getVercelToken(deps) {
1420
+ const { data, error } = await deps.supabase.from("app_setting").select("vercel_token_encrypted").maybeSingle();
1421
+ if (error) throw new Error(`Could not read app_setting: ${error.message}`);
1422
+ if (!data?.vercel_token_encrypted) {
1423
+ throw new Error("Vercel API token is not configured. Add it in dashboard Settings.");
1424
+ }
1425
+ try {
1426
+ return deps.decrypt(data.vercel_token_encrypted);
1427
+ } catch {
1428
+ throw new Error("Failed to decrypt the stored Vercel API token.");
1429
+ }
1430
+ }
1431
+ async function resolveProjectId(token, projectInput) {
1432
+ const res = await vercelFetch(
1433
+ token,
1434
+ `/v9/projects/${encodeURIComponent(projectInput)}`
1435
+ );
1436
+ if (res.error || !res.data?.id) {
1437
+ throw new Error(
1438
+ `Could not resolve Vercel project "${projectInput}": ${res.error ?? "not found"}`
1439
+ );
1440
+ }
1441
+ return res.data.id;
1442
+ }
1443
+ function formatTimestamp(ms) {
1444
+ if (!ms) return "";
1445
+ return new Date(ms).toLocaleString("nl-NL", { timeZone: "Europe/Amsterdam" });
1446
+ }
1447
+ function formatProjectsTable(projects) {
1448
+ if (projects.length === 0) return "No Vercel projects found";
1449
+ const lines = projects.map((p) => {
1450
+ const repo = p.link ? `${p.link.org ?? ""}/${p.link.repo ?? ""}` : "";
1451
+ const prodUrl = p.targets?.production?.alias?.[0] ?? p.targets?.production?.url ?? "";
1452
+ const latest = p.latestDeployments?.[0];
1453
+ const latestStr = latest ? `${latest.state} @ ${formatTimestamp(latest.createdAt)}` : "";
1454
+ return `${p.id.padEnd(28)} ${(p.name || "").padEnd(40)} ${(p.framework || "-").padEnd(10)} ${repo.padEnd(40)} ${prodUrl.padEnd(35)} ${latestStr}`;
1455
+ });
1456
+ const header = `${"ID".padEnd(28)} ${"NAME".padEnd(40)} ${"FRAMEWORK".padEnd(10)} ${"REPO".padEnd(40)} ${"PROD URL".padEnd(35)} LATEST`;
1457
+ return `${header}
1458
+ ${"-".repeat(header.length)}
1459
+ ${lines.join("\n")}`;
1460
+ }
1461
+ function formatDeploymentsTable(deployments) {
1462
+ if (deployments.length === 0) return "No deployments found";
1463
+ const lines = deployments.map((d) => {
1464
+ const branch = d.meta?.githubCommitRef ?? "";
1465
+ const sha = d.meta?.githubCommitSha?.slice(0, 7) ?? "";
1466
+ const msg = (d.meta?.githubCommitMessage ?? "").replace(/\n.*/s, "").slice(0, 60);
1467
+ 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}`;
1468
+ });
1469
+ const header = `${"ID".padEnd(28)} ${"STATE".padEnd(11)} ${"TARGET".padEnd(10)} ${"BRANCH".padEnd(20)} ${"COMMIT".padEnd(8)} ${"CREATED".padEnd(20)} MESSAGE`;
1470
+ return `${header}
1471
+ ${"-".repeat(header.length)}
1472
+ ${lines.join("\n")}`;
1473
+ }
1474
+ function formatBuildEvents(events) {
1475
+ if (events.length === 0) return "No build events found for this deployment";
1476
+ const filtered = events.filter(
1477
+ (e) => ["command", "stdout", "stderr", "exit", "delimiter", "deployment-state"].includes(e.type)
1478
+ );
1479
+ return filtered.map((e) => {
1480
+ const time = e.created ? new Date(e.created).toISOString().slice(11, 19) : "--:--:--";
1481
+ const text = (e.payload?.text ?? e.text ?? "").replace(/\s+$/u, "");
1482
+ const prefix = e.type === "stderr" ? "!" : " ";
1483
+ return `${prefix}${time} [${e.type.padEnd(16)}] ${text}`;
1484
+ }).join("\n");
1485
+ }
1486
+ function formatRuntimeLogs(logs) {
1487
+ if (logs.length === 0) {
1488
+ return "No runtime logs found for this deployment in the requested window";
1489
+ }
1490
+ return logs.map((l) => {
1491
+ const ts = l.timestampInMs ?? l.timestamp ?? 0;
1492
+ const time = ts ? new Date(ts).toISOString().slice(11, 23) : "--:--:--";
1493
+ const level = (l.level ?? "info").toUpperCase().padEnd(5);
1494
+ const status = l.proxy?.statusCode ?? l.responseStatusCode;
1495
+ const reqInfo = l.proxy?.method ? `${l.proxy.method} ${l.proxy.path ?? ""} ${status ?? ""}`.trim() : "";
1496
+ const message = (l.message ?? l.text ?? "").replace(/\s+$/u, "");
1497
+ const reqId = l.requestId ? ` [${l.requestId.slice(0, 12)}]` : "";
1498
+ const head = reqInfo ? `${reqInfo}${reqId}` : reqId.trim();
1499
+ return `${time} [${level}] ${head ? head + " " : ""}${message}`;
1500
+ }).join("\n");
1501
+ }
1502
+ function formatWebhookHistory(rows) {
1503
+ if (rows.length === 0) return "No webhook log rows match those filters";
1504
+ const lines = rows.map((r) => {
1505
+ const ts = new Date(r.created_at).toLocaleString("nl-NL", { timeZone: "Europe/Amsterdam" });
1506
+ const note = r.error_message ?? r.message ?? "";
1507
+ 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}`;
1508
+ });
1509
+ const header = `${"WHEN".padEnd(20)} ${"EVENT".padEnd(22)} ${"STATUS".padEnd(8)} ${"PROJECT".padEnd(40)} ${"TARGET".padEnd(10)} ${"DEPLOYMENT".padEnd(28)} NOTE`;
1510
+ return `${header}
1511
+ ${"-".repeat(header.length)}
1512
+ ${lines.join("\n")}`;
1513
+ }
1514
+ async function handleVercelTool(name, args2, deps) {
1515
+ switch (name) {
1516
+ case "vercel-projects": {
1517
+ const token = await getVercelToken(deps);
1518
+ const limit = Math.min(Math.max(Number(args2.limit) || 50, 1), 200);
1519
+ const search = typeof args2.search === "string" ? args2.search.toLowerCase() : null;
1520
+ const { projects, error } = await listVercelProjectsAll(token, limit);
1521
+ if (error) return { content: [{ type: "text", text: `Error: ${error}` }] };
1522
+ const filtered = search ? projects.filter((p) => (p.name || "").toLowerCase().includes(search)) : projects;
1523
+ return { content: [{ type: "text", text: formatProjectsTable(filtered) }] };
1524
+ }
1525
+ case "vercel-deployments": {
1526
+ const token = await getVercelToken(deps);
1527
+ const projectInput = String(args2.project);
1528
+ const limit = Math.min(Math.max(Number(args2.limit) || 20, 1), 100);
1529
+ const projectId = await resolveProjectId(token, projectInput);
1530
+ const { deployments, error } = await listVercelDeployments(token, {
1531
+ projectId,
1532
+ limit,
1533
+ state: args2.state ? String(args2.state) : void 0,
1534
+ target: args2.target ? String(args2.target) : void 0
1535
+ });
1536
+ if (error) return { content: [{ type: "text", text: `Error: ${error}` }] };
1537
+ return { content: [{ type: "text", text: formatDeploymentsTable(deployments) }] };
1538
+ }
1539
+ case "vercel-logs": {
1540
+ const kind = (args2.kind ? String(args2.kind) : "build").toLowerCase();
1541
+ if (kind === "build") {
1542
+ if (!args2.deploymentId) {
1543
+ return { content: [{ type: "text", text: 'Error: kind="build" requires deploymentId.' }] };
1544
+ }
1545
+ const token = await getVercelToken(deps);
1546
+ const deploymentId = String(args2.deploymentId);
1547
+ const limit = Math.min(Math.max(Number(args2.limit) || 500, 1), 5e3);
1548
+ const { events, error } = await getDeploymentBuildEvents(token, deploymentId, limit);
1549
+ if (error) return { content: [{ type: "text", text: `Error: ${error}` }] };
1550
+ return { content: [{ type: "text", text: formatBuildEvents(events) }] };
1551
+ }
1552
+ if (kind === "runtime") {
1553
+ if (!args2.project || !args2.deploymentId) {
1554
+ return {
1555
+ content: [
1556
+ { type: "text", text: 'Error: kind="runtime" requires both project and deploymentId.' }
1557
+ ]
1558
+ };
1559
+ }
1560
+ const token = await getVercelToken(deps);
1561
+ const projectInput = String(args2.project);
1562
+ const deploymentId = String(args2.deploymentId);
1563
+ const limit = Math.min(Math.max(Number(args2.limit) || 200, 1), 1e3);
1564
+ const sinceMinutes = Math.min(Math.max(Number(args2.sinceMinutes) || 60, 1), 7 * 24 * 60);
1565
+ const sinceMs = Date.now() - sinceMinutes * 6e4;
1566
+ const projectId = await resolveProjectId(token, projectInput);
1567
+ const { logs, error } = await getRuntimeLogs(
1568
+ token,
1569
+ projectId,
1570
+ deploymentId,
1571
+ limit,
1572
+ sinceMs
1573
+ );
1574
+ if (error) {
1575
+ 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 kind="webhooks" or the supabase MCP (vercel_deployment_log table) for archived runtime logs.' : "";
1576
+ return { content: [{ type: "text", text: `Error: ${error}${hint}` }] };
1577
+ }
1578
+ return { content: [{ type: "text", text: formatRuntimeLogs(logs) }] };
1579
+ }
1580
+ if (kind === "webhooks") {
1581
+ const limit = Math.min(Math.max(Number(args2.limit) || 25, 1), 200);
1582
+ let query = deps.supabase.from("vercel_webhook_logs").select(
1583
+ "id, event_type, status, project_name, deployment_id, target, message, error_message, created_at"
1584
+ ).order("created_at", { ascending: false }).limit(limit);
1585
+ if (args2.projectName) query = query.eq("project_name", String(args2.projectName));
1586
+ if (args2.status) query = query.eq("status", String(args2.status));
1587
+ const { data, error } = await query;
1588
+ if (error) return { content: [{ type: "text", text: `Error: ${error.message}` }] };
1589
+ return {
1590
+ content: [{ type: "text", text: formatWebhookHistory(data ?? []) }]
1591
+ };
1592
+ }
1593
+ return {
1594
+ content: [
1595
+ { type: "text", text: `Error: unknown kind "${kind}". Use build, runtime, or webhooks.` }
1596
+ ]
1597
+ };
1598
+ }
1599
+ default:
1600
+ return { content: [{ type: "text", text: `Unknown vercel tool: ${name}` }] };
1601
+ }
1602
+ }
1216
1603
  var args = process.argv.slice(2);
1217
1604
  function getArg2(name) {
1218
1605
  return args.find((a) => a.startsWith(`--${name}=`))?.split("=").slice(1).join("=");
@@ -1388,7 +1775,8 @@ var TOOL_MODULE_MAP = {
1388
1775
  "dns-update": "domains",
1389
1776
  "dns-delete": "domains",
1390
1777
  ...TRIGGER_TOOL_MODULE_MAP,
1391
- ...AGENT_TOOL_MODULE_MAP
1778
+ ...AGENT_TOOL_MODULE_MAP,
1779
+ ...VERCEL_TOOL_MODULE_MAP
1392
1780
  };
1393
1781
  var authContext = null;
1394
1782
  async function validateApiKey(key) {
@@ -1691,7 +2079,7 @@ function decrypt(payload) {
1691
2079
  decrypted += decipher.final("utf8");
1692
2080
  return decrypted;
1693
2081
  }
1694
- var VERCEL_API = "https://api.vercel.com";
2082
+ var VERCEL_API2 = "https://api.vercel.com";
1695
2083
  function parseEnvContent(content) {
1696
2084
  const result = {};
1697
2085
  for (const line of content.split("\n")) {
@@ -1721,7 +2109,7 @@ function getVercelEnvSyncTargetings(stageType, customEnvId) {
1721
2109
  async function syncEnvVarsToVercel(token, projectId, envVars) {
1722
2110
  if (envVars.length === 0) return { created: 0, error: null };
1723
2111
  const res = await fetch(
1724
- `${VERCEL_API}/v10/projects/${encodeURIComponent(projectId)}/env?upsert=true`,
2112
+ `${VERCEL_API2}/v10/projects/${encodeURIComponent(projectId)}/env?upsert=true`,
1725
2113
  {
1726
2114
  method: "POST",
1727
2115
  headers: {
@@ -3792,9 +4180,11 @@ var TOOLS = [
3792
4180
  // ----- Trigger.dev -----
3793
4181
  ...TRIGGER_TOOLS,
3794
4182
  // ----- Agent Reporting -----
3795
- ...AGENT_TOOLS
4183
+ ...AGENT_TOOLS,
4184
+ // ----- Vercel -----
4185
+ ...VERCEL_TOOLS
3796
4186
  ];
3797
- var MCP_VERSION = "3.7.0";
4187
+ var MCP_VERSION = "3.14.0";
3798
4188
  async function handleListTools() {
3799
4189
  if (!authContext) return { tools: TOOLS };
3800
4190
  const accessible = TOOLS.filter((tool) => {
@@ -5120,6 +5510,9 @@ ${lines.join("\n")}` }] };
5120
5510
  if (AGENT_TOOL_NAMES.has(name)) {
5121
5511
  return handleAgentTool(name, a);
5122
5512
  }
5513
+ if (VERCEL_TOOL_NAMES.has(name)) {
5514
+ return handleVercelTool(name, a, { supabase, decrypt });
5515
+ }
5123
5516
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
5124
5517
  }
5125
5518
  } catch (err) {