Skip to content
Agentic Control Plane

Govern a Vercel AI SDK App

You’re building with Vercel AI SDK. Your app calls tools — search, database queries, API actions. Now you need identity, permissions, and audit trails on every tool call without rewriting your app.

ACP integrates with Vercel AI SDK as an MCP tool provider. Your existing code stays the same — you just point tool discovery at your ACP endpoint, and every call gets governed automatically.


What you need

  • A Vercel AI SDK application (Next.js, SvelteKit, or standalone)
  • An ACP Cloud workspace (sign up)
  • An identity provider configured in ACP (Auth0, Okta, or Entra ID)

Step 1: Install the MCP client

npm install @modelcontextprotocol/sdk

Vercel AI SDK supports MCP tool providers natively. ACP is a standard MCP server — no custom SDK needed.


Step 2: Connect to ACP

In your server-side route handler (e.g., app/api/chat/route.ts):

import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { experimental_createMCPClient } from "ai";

export async function POST(req: Request) {
  const { messages } = await req.json();
  const userJwt = req.headers.get("authorization")?.split(" ")[1];

  // Connect to ACP as an MCP server
  const mcpClient = await experimental_createMCPClient({
    transport: {
      type: "sse",
      url: "https://api.makeagents.run/your-slug",
      headers: {
        Authorization: `Bearer ${userJwt}`,
      },
    },
  });

  const tools = await mcpClient.tools();

  const result = streamText({
    model: openai("gpt-4o"),
    messages,
    tools,
  });

  return result.toDataStreamResponse();
}

ACP returns only the tools the authenticated user has permission to use. A user with salesforce:read sees query tools. A user with salesforce:write also sees create/update tools.


Step 3: Pass user identity from the frontend

On the client side, include the user’s JWT in requests to your API route:

import { useChat } from "ai/react";

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: "/api/chat",
    headers: {
      Authorization: `Bearer ${session.accessToken}`,
    },
  });

  return (
    <form onSubmit={handleSubmit}>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}
      <input value={input} onChange={handleInputChange} />
    </form>
  );
}

The JWT flows from your frontend → your API route → ACP. ACP verifies it on every tool call.


Step 4: Configure tool scopes in ACP

In your ACP dashboard under Policies → Tool Scopes:

{
  "salesforce.query": ["salesforce:read"],
  "salesforce.createRecord": ["salesforce:write"],
  "github.listRepos": ["github:read"],
  "slack.postMessage": ["slack:write"]
}

When the LLM tries to call a tool the user doesn’t have access to, ACP rejects it with 403 Insufficient scope. The LLM sees the rejection and adjusts its response.


What happens on every tool call

User message → Your API route → LLM → Tool call → ACP → Backend
                                                     ↓
                                               Identity verified
                                               Scopes checked
                                               PII scanned
                                               Action logged
  1. JWT verification — RS256 signature check against your IdP’s JWKS endpoint
  2. Scope enforcement — user’s permissions checked against tool requirements
  3. Immutable rules — SSN, credit card, and SSRF patterns blocked (bypass-immune)
  4. Content scanning — configurable PII detection on inputs and outputs
  5. Rate limiting — per-user limits prevent abuse
  6. Execution — tool runs with the user’s own OAuth credentials
  7. Audit logging — full attribution: who, what tool, when, success/failure

Edge runtime compatibility

ACP uses standard HTTP (Streamable HTTP or SSE transport). It works with Vercel’s Edge runtime — no Node.js-specific dependencies required.

export const runtime = "edge";

export async function POST(req: Request) {
  // Same code as above — works on Edge
}

Multiple users, isolated sessions

Each user’s JWT creates an isolated session. User A’s tool calls are scoped to their permissions and credentials. User B’s are completely separate. There’s no shared state.

This means you don’t need to manage per-user API keys, token storage, or permission checks in your application code. ACP handles all of it.


Back to guides · Add governance to LangChain → · PII detection →