---
title: Add the Vercel MCP
description: Give Foreman build logs, runtime errors, and deployment history for the app your target repository deploys, app-scoped so unattended runs can use it.
type: guide
summary: One CLI command, an app-scoped connector, and a read-only tool list.
related:
  - /docs/tools
  - /docs/trust-model
  - /docs/configuration
---

# Add the Vercel MCP



<CopyPrompt
  className="block"
  text={`Help me customize the eve Software Factory template. I want to add Vercel's hosted MCP so Foreman can read build logs, runtime errors, and deployment history for the app my target repository deploys. It has to work during unattended factory runs, so use app scope rather than per-user OAuth.

Before writing any code, ask me for three things and wait for my answer: my Vercel team ID or slug, the project ID or slug of the project the TARGET repository deploys (not the project Foreman itself runs in), and whether that target repository is public or private. Both IDs are in .vercel/project.json as orgId and projectId.

Ground truth first: read AGENTS.md in the repository root, read agent/connections/linear.ts as the in-repo example of an app-scoped MCP connection with an approval predicate, read agent/lib/constants.ts for how linearAuth is built, and read node_modules/eve/docs/connections/mcp.mdx for tool filters, toolCall.providedArguments, and approval gates. Read agent/lib/trust.ts before writing any policy.

1. Run: eve add connection/vercel
 Show me the generated agent/connections/vercel.ts before changing it.
2. Add a vercelAuth constant to agent/lib/constants.ts following the linearAuth pattern, doc comment included: connect({ connector: requireEnv("VERCEL_CONNECTOR", "vercel/foreman-agent"), principalType: "app" }). Do not use connect("vercel"), which is user-scoped and unusable on unattended runs. Add VERCEL_TEAM_ID and VERCEL_PROJECT_ID alongside it, both through requireEnv.
3. Point the connection at vercelAuth and set tools.allow to exactly these eight: get_deployment_build_logs, get_runtime_errors, get_runtime_logs, list_deployments, get_deployment, list_projects, get_project, list_teams. Use allow, not block. Everything else stays undiscovered, including deploy_to_vercel and the buy_pro, buy_credits, buy_addon, and buy_domain purchase tools.
4. Set teamId and projectId through toolCall.providedArguments, so the model never supplies them itself. After that change, a get_runtime_logs call from the model carries only its own filters, like level, and eve adds teamId and projectId as the call goes out. The point is that an issue body cannot name a different project, because the model has no field to put one in. Note in the doc comment that this is the same idea as context: factoryRepo in agent/extensions/github.ts.
5. Add a connection-wide approval predicate modeled on the one in agent/connections/linear.ts, but allowing more: return "not-applicable" for isAutonomous, isTrusted, and isScheduleAppAuth callers, and "user-approval" for everyone else. Use those predicates from agent/lib/trust.ts. In the doc comment, say that unattended runs are allowed because the allowlist carries no write tools, and that the untrusted caller is the pull-request summary session.
6. If I said the target repository is public, leave get_runtime_logs and get_runtime_errors out of the allowlist, and tell me you did and why.
7. Tell me the setup commands to run myself, and do not run them:
 vercel link, vercel connect create api-key --name vercel, vercel env pull.
 Tell me the token I supply must be team-scoped.
8. Add the three variables to .env.example in the surrounding comment style, noting they are required at build time.
9. Do not change agent/lib/trust.ts, agent/lib/github/approval.ts, or agent/extensions/github.ts, and do not mount this connection under agent/subagents/. If you think any of that is needed, stop and explain instead.

Then check one thing rather than assuming it: with the IDs pinned, confirm an allowed tool still returns data in pnpm dev. Configured arguments apply to every tool on the connection, so tell me if any allowed tool rejects them.

Finish by running pnpm validate and confirming 0 errors and 0 warnings, then run npx eve info and show me the vercel connection and its tools in the discovered surface. Do not deploy.

Full recipe, with the reasoning behind each step: https://ask-foreman.dev/recipes/add-the-vercel-mcp`}
/>

Give Foreman the build log behind a failed deployment, the errors firing in production, and the deployment history for the app your target repository deploys.

The red-CI fix loop currently works from GitHub's check-run summary, which carries a check name and a status. That tells Foreman something broke, not what broke.

## When to use it

Reach for this when your target repository deploys to Vercel and the factory keeps hitting failures it cannot explain, either red CI on its own pull requests or bug reports about production behavior.

The connection mounts eight read tools from Vercel's hosted Model Context Protocol (MCP) server:

| Tool                           | What Foreman uses it for                               |
| ------------------------------ | ------------------------------------------------------ |
| `get_deployment_build_logs`    | Read the actual build output when a deployment fails   |
| `get_runtime_errors`           | Check a bug report against the errors firing right now |
| `get_runtime_logs`             | Trace a failing request path or status code            |
| `list_deployments`             | Find the deployment carrying a branch or commit        |
| `get_deployment`               | Read one deployment's state and metadata               |
| `list_projects`, `get_project` | Resolve the project the target repository deploys      |
| `list_teams`                   | Resolve the team that owns it                          |

The red-CI fix loop is the clearest beneficiary. It already runs on the factory's own pull requests, and it can now read why the build broke instead of inferring it from a check name.

## What it touches

| File                          | Change                                                                          |
| ----------------------------- | ------------------------------------------------------------------------------- |
| `agent/connections/vercel.ts` | New connection, written by `eve add connection/vercel`, then filtered and gated |
| `agent/lib/constants.ts`      | `vercelAuth`, plus the team and project IDs                                     |
| `.env.example`                | `VERCEL_CONNECTOR`, `VERCEL_TEAM_ID`, `VERCEL_PROJECT_ID`                       |

## Before you run it

The three new variables are read at module load, the same way `LINEAR_CONNECTOR` already is, so set them in the Vercel project before redeploying or discovery fails.

`VERCEL_PROJECT_ID` should be the project your target repository deploys, not the project Foreman itself runs in. Two projects are in play, so confirm which one the ID belongs to before you set it.

The connector's token is team-scoped, but it keeps the permissions of whoever created it. The allowlist, not the token, is what limits the model.

<Callout title="Runtime logs on a public repository">
  An unattended run can comment on its intake issue, so anything it reads can be quoted there. Runtime logs and errors carry request paths, headers, and user data. On a public repository, leave `get_runtime_logs` and `get_runtime_errors` out and keep the build logs.
</Callout>

## Why app scope

Vercel's entry in the eve registry offers two auth shapes. `connect("vercel")` is user-scoped, so each person authorizes in their own browser before their first tool call. That works at a terminal and does nothing for an unattended run, where nobody is there to complete it.

App scope makes the agent the authenticated identity, with no per-user consent:

```ts
auth: connect({ connector: "vercel/foreman-agent", principalType: "app" })
```

That is the shape `linearAuth` already uses in `agent/lib/constants.ts`, giving one installation whose tokens resolve per call and never reach the model. App scope is what lets the factory-label run and the red-CI fix loop use the connection at all, which is where deployment context is worth the most.

## What stays out

Vercel's MCP serves more than the eight tools above. It also exposes `deploy_to_vercel`, which deploys to preview or production and creates the project if it does not exist, and a set of purchase tools (`buy_pro`, `buy_credits`, `buy_addon`, `buy_domain`) that charge the team's payment method.

The connection lists what it wants through `tools: { allow: [...] }`, so those are never discovered and the model cannot call them. The GitHub extension already works this way, with `include` as an allowlist that has no preset and merge tools absent rather than gated.

Keeping that boundary matters here because app scope removes the sign-in step. An unattended run is driven by an issue body, and the allowlist is what decides how far that input can reach.

## Who can use it

| Caller                                     | Vercel tools                          |
| ------------------------------------------ | ------------------------------------- |
| Unattended run (factory label, red-CI fix) | Run                                   |
| Schedule turn                              | Run                                   |
| Trusted mention or Linear session          | Run                                   |
| Dev TUI, pull-request summary session      | Stop and wait for a person to approve |

This is the opposite of the Linear connection, which denies unattended runs. Linear mounts write tools, so a prompt-injected issue body reaching it could fan out into the tracker. After the allowlist, this connection reads only, and the worst an unattended run can do is read its own project's deployment data.

The last row covers the pull-request summary session, which is untrusted and runs on pull requests from outside contributors. That session stops and waits for a person to approve the call, so a pull request description cannot talk it into quoting production logs into a public comment.

## Verify

```bash
pnpm validate
npx eve info
```

`npx eve info` should list the `vercel` connection with only the allowed tools. If `deploy_to_vercel` or any `buy_` tool shows up, the filter is not applied.

Then ask for a recent deployment's build logs in the `pnpm dev` TUI. The dev principal is untrusted, so the call stops and asks you to approve it. Approve it and confirm the result comes back for the pinned project.

## What to expect

Deployment context earns its place on build failures and production bug reports. On a refactor, Foreman leaves these tools alone.

## FAQ

<Accordions type="single">
  <Accordion title="Can I let Foreman deploy?">
    Not through this recipe. `deploy_to_vercel` targets production and creates projects that do not exist, and the callers most likely to reach it are unattended runs driven by issue bodies.

    The template's shape is that unattended work stops at a draft pull request and merging is a person's call. Deploying sits past that line, so it belongs in its own recipe, gated on `isTrusted` and held until a person approves it.
  </Accordion>

  <Accordion title="Why not include search_vercel_documentation?">
    It is a public read tool with nothing to guard against, so the allowlist is not what excludes it.

    This connection is mounted at the root, which means Foreman gets it and the stations do not. Foreman routes work and assembles results; it does not write code, and it already has the `researcher` subagent for external facts. Vercel's documentation is useful to whoever is implementing.

    There is also a mechanical catch. Configured arguments apply to every tool on a connection, and doc search takes neither `teamId` nor `projectId`, so the pin may make the server reject it.

    If you want it, give it to the Implementer or Analyst as its own connection file under that station, with the tool allowed and no pin. The tool needs no auth, so that file needs no connector.
  </Accordion>

  <Accordion title="What stops it reading another team's projects?">
    `get_runtime_logs` takes `projectId` and `teamId` as parameters, and normally the model decides what goes in them. That is the hole. An issue body reading "check the logs for prj\_abc" is an instruction the model can follow.

    So the connection supplies those two values itself, through `toolCall.providedArguments`. eve deletes them from the tool description the model sees, then fills in your values as the call goes out:

    ```text
    Model can send:      { level: ["error"] }
    eve sends to Vercel: { projectId: "<yours>", teamId: "<yours>", level: ["error"] }
    ```

    The model has no field left to name another project in.

    The connector's token is also team-scoped, so nothing outside the team is reachable either way. Without `toolCall.providedArguments`, that team scope is the only protection left, which leaves every project in the team readable.
  </Accordion>

  <Accordion title="Can a station use it instead of the orchestrator?">
    Yes. Declared subagents treat their own directory as an agent root and inherit nothing from the root's connections, so the station needs its own `agent/subagents/<id>/connections/vercel.ts`.

    App scope is what makes that work, since there is no sign-in for a task-mode station to park on. The Reviewer has the clearest case, because it judges whether the work holds up.
  </Accordion>

  <Accordion title="How do I undo it?">
    Delete `agent/connections/vercel.ts`. The tools stop existing for the model, since a connection is only discoverable while its file is there.

    Remove `vercelAuth` and the two IDs from `agent/lib/constants.ts` as well, or keep the variables set, because the `requireEnv` calls still run at module load.
  </Accordion>
</Accordions>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)