Skip to content
Application identity

Run on Vercel without secrets

Deploy the self-serve starter MCP server or agent to Vercel, where it authenticates to Keycard with Vercel's OIDC token instead of a client secret.

This guide walks through the Deploy step of the Keycard Console onboarding guide on Vercel: deploying the starter MCP server or agent you enrolled, then giving Keycard the values it needs to trust the deployment. The MCP server answers your agent’s requests; the agent runs once a day on Vercel Cron. It’s written for people who don’t use Vercel every day, and it covers the places where that step usually goes wrong.

Vercel mints a short-lived OIDC token for every function invocation. Keycard trusts your Vercel team’s issuer and pins a Workload Identity credential on your Application to the project’s production environment. The deployment presents that token on each token exchange, so it holds no client secret. Authenticate deployed workloads explains the model; this page is the Vercel walkthrough.

  • You finished the onboarding guide’s earlier steps, and the starter runs locally:

    You cloned the starter (git clone https://github.com/keycardai/getting-started-mcp local-tools) and ran keycard new mcp in it, which wrote the .env file this guide pushes from.

  • A Vercel account. The Hobby plan works.

  • Homebrew or npm, to install the Vercel CLI.

Run every command below from the starter’s folder (local-tools or agent).

  1. Install the Vercel CLI and sign in

    Terminal window
    brew install vercel-cli
    vercel login

    Without Homebrew, npm i -g vercel installs the same CLI. vercel login finishes in your browser.

  2. Link the folder to a Vercel project

    Terminal window
    vercel link

    The CLI asks which scope (team) to use and whether to link an existing project or create one. Note both answers: the Deploy form asks for the team and the project name. The link is saved in .vercel/project.json, which holds IDs rather than names, so it isn’t where to read them back from.

  3. Push your settings to the project

    Terminal window
    make env-push

    This copies one value from .env to the project’s production environment: KEYCARD_ZONE_URL, the starter’s name for your Zone’s Issuer URL. The deployed server reads it at startup and returns a 500 without it. The agent starter copies three values: KEYCARD_ZONE_URL, KEYCARD_RESOURCE_URL and ANTHROPIC_API_KEY. Either way, KEYCARD_CLIENT_ID and KEYCARD_CLIENT_SECRET stay in your local .env, because the deployment authenticates with Vercel’s workload identity. A successful run prints set KEYCARD_ZONE_URL -> production.

    If it prints This directory is not linked to a Vercel project, run vercel link first.

  4. Make a local commit as yourself

    Vercel only creates a production deployment from a commit whose author has access to your Vercel team, and every commit in the cloned starter is authored by Keycard. Make an empty commit with the email on your Vercel account:

    Terminal window
    git commit --allow-empty -m "Deploy to Vercel" --author="Abed Nadir <abed.nadir@acme.com>"

    Use your own name and Vercel email. Don’t push the commit: vercel deploy uploads your local folder, so it never needs to reach GitHub.

  5. Deploy to production

    Terminal window
    vercel deploy --prod

    The --prod flag matters. Without it, Vercel makes a preview deployment, which the Workload Identity credential doesn’t trust (see Preview deployments don’t authenticate).

When the deploy finishes, the CLI prints two URLs:

  • Production: the URL of this one deployment, with a generated suffix, for example https://local-tools-a1b2c3d4e-acme-corp.vercel.app. It changes on every deploy.
  • Aliased: the project’s stable production domain, for example https://local-tools.vercel.app. This is the one to give Keycard.

The production domain is usually https://<project-name>.vercel.app, but not always. .vercel.app names are global, so when another Vercel user already holds your project’s name, Vercel adds a suffix: a project named local-tools can land on https://local-tools-ten.vercel.app. Don’t type the URL from the project name; copy the Aliased line, or the domain listed on the project’s Domains page in the Vercel dashboard. A custom domain replaces it too, if you add one. The agent only needs it to run a cycle on demand.

Back in the onboarding guide, the form depends on the starter:

The Tell Keycard where it deployed form asks for three values:

Field What to enter Where to find it
Vercel team The team slug, exactly as Vercel spells it The part of your team’s URL after vercel.com/, for example acme-corp in vercel.com/acme-corp, or the ID column of vercel teams ls. Not the team’s display name.
Project name The project name As in vercel.com/acme-corp/local-tools, or the name vercel project ls lists. It can differ from the deployed hostname.
Deployed URL The production domain The Aliased URL from the deploy. With or without /mcp; Keycard adds it.

From these, Keycard creates:

  • A Provider for your team’s issuer, https://oidc.vercel.com/<team-slug>
  • A Workload Identity credential on your Application, with the subject owner:<team-slug>:project:<project-name>:environment:production
  • A Resource for the deployed endpoint, <deployed-url>/mcp

Vercel builds the token’s subject from the team slug and the project name, not from the hostname. That’s why the form asks for all three: a wrong team or project breaks authentication, and a wrong URL registers an endpoint that nothing serves.

Point your agent at the deployed endpoint, then ask it to use one of the server’s tools:

Terminal window
claude mcp add --transport http --scope local local-tools-prod <deployed-url>/mcp

Adding the server leaves no trace in Keycard. The first tool call does: it appears on the Application’s Activity tab, authenticated with the Vercel identity.

The team slug or project name doesn’t match

Section titled “The team slug or project name doesn’t match”

Keycard compares the token’s issuer and subject character for character. The slug in vercel.com/<team-slug> is often different from the team name Vercel displays (Acme Corp versus acme-corp). A project renamed after the deploy also stops matching, because its tokens carry the new name. Submit the form again with the values Vercel shows now.

Keycard expects Vercel’s Team issuer mode, which puts the team slug in the issuer. If the project’s OIDC issuer mode is set to Global, its tokens carry https://oidc.vercel.com as the issuer and match no Provider. Switch it back to Team and redeploy.

Entering https://<project-name>.vercel.app without checking registers a Resource that nothing answers on whenever Vercel suffixed the hostname. Your agent then can’t connect to it, even though the credential works. Copy the Aliased URL from the deploy output instead.

The credential’s subject ends in environment:production. Tokens from a preview deployment, which is what vercel deploy makes without --prod, end in environment:preview and don’t match it, so their token exchanges fail with invalid_client. Deploy with vercel deploy --prod. make env-push also writes KEYCARD_ZONE_URL to production only, so a preview deployment is missing it as well.

Vercel’s Deployment Protection can put a Vercel sign-in page in front of a deployment. When it covers the URL you gave Keycard, your agent gets Vercel’s login page instead of the MCP server and can’t connect. Open the deployed URL in a private browser window: if you see a Vercel sign-in, go to the project’s Settings → Deployment Protection and turn off Vercel Authentication, or make sure you registered the production domain rather than the per-deployment URL. Claude Code users can also install the Vercel plugin (/plugin install vercel), which can reach a deployment that still has Vercel Authentication on.

If vercel deploy --prod rejects the deploy because the Git author doesn’t have access to the team, the latest commit is still the starter’s. Make the empty commit from step 4 with the email on your Vercel account and deploy again.

KEYCARD_ZONE_URL isn’t set on the deployment. (The agent instead answers with the settings it’s missing.) Run make env-push, then vercel deploy --prod again: Vercel applies environment variables at deploy time, so a deployment made before the push never sees them. make env-list shows what the project has.

No Workload Identity credential matched the token. The error names the issuer and subject exactly as Keycard read them, so compare them with what you entered in the form; one of the pitfalls above is almost always the cause.