# Connecting agents

> Connect Claude Code, Cursor, or any MCP client to your Graft project, on your machine or over HTTP, and control what it may do.

*Point Claude Code, Cursor, or any MCP client at your project.*

This page gets an agent connected to your Graft project and explains what it is
allowed to do once connected. There are two ways to connect, and both reach the
same tools:

- **On your machine (stdio).** The agent starts `graft mcp` as a local process.
  This is the usual setup for Claude Code and Cursor. Start here.
- **Over HTTP.** The agent connects to your deployed app at `POST /api/mcp`,
  for example a hosted agent working on your live site.

## Connect an agent on your machine

**Register Graft with your agent**

Most clients read an `mcpServers` map. Add Graft to it:

`.mcp.json (Claude Code) or .cursor/mcp.json (Cursor)`:

```json
{
  "mcpServers": {
    "graft": {
      "command": "npx",
      "args": ["graft", "mcp"]
    }
  }
}
```

Put the file in your project's root folder. Graft looks for `graft.config.ts`
and `.env` starting from the folder it runs in, so the client must start it in
your project.

The project needs the CLI installed (`npm i -D @usegraft/cli`; Getting started
does this). Without it, `npx graft` fetches an unrelated npm package named
`graft` and the agent fails to connect.

**Check that it works**

Ask the agent to call `list_collections`. It needs no arguments and no
permissions. On the static engine it needs no database either, so if it fails,
the problem is the connection. On Postgres, `graft mcp` needs `DATABASE_URL`
before it starts, so a failure can also be a missing variable or a database
that is down.

From there, `describe_schema` tells the agent everything it needs to write
content, and `explain_error` turns any failure into a next step.

To work on a [preview branch](/docs/branching-previews), add `--branch` to the
arguments: `["graft", "mcp", "--branch", "preview"]`.

If you wrap `graft mcp` in a script, never print anything from the script.
The client reads standard output as the protocol, and stray text breaks it.

### What a local agent may do

The agent is a process you started on your own machine, so Graft trusts it to
write content: it can call `write_content`, `delete_content`, and `put_asset`
with no permission setup. Each still needs its backend: `delete_content` files
an approval, which needs the Postgres engine, and `put_asset` needs asset
storage (the `S3_*` variables in [Assets](/docs/assets)).

It cannot approve its own destructive actions. A delete waits for you to run
`graft approve` in another terminal, unless you start the server with
`--elicit-approvals`:

```sh
graft mcp --elicit-approvals
```

Then the agent's client asks you to confirm right there, and approving finishes
the call. See [Audit & approvals](/docs/audit-and-approvals#asking-in-band-opt-in).

To let the agent run functions that require scopes, add them to
`GRAFT_DEV_SCOPES` in `.env`. If `GRAFT_DEV_TOKEN` is set, the server uses it as
the agent's identity for those calls, and the token itself never appears in the
agent's conversation.

## Connect an agent over HTTP

A deployed app serves MCP at `POST /api/mcp`. `graft serve` mounts it for you:

```sh
graft serve --port 3903
```

To mount it inside your own app, use `createGraftMcpHandler`. Your
[framework guide](/docs/next#serve-functions-and-mcp-from-your-app) shows
the route.

The endpoint is stateless: each request is handled on its own, so it runs on
serverless platforms too. It only answers `POST`. A `GET` returns `405`, which
MCP clients understand as "this server does not push messages".

### Give the agent an identity

Over HTTP, the agent sends a token when it connects, just like a user of your
app. Graft checks it the same way, then uses it for every function the agent
runs. [Auth](/docs/auth) covers setting up the providers Graft trusts.

An agent that holds a user's token can act as that user for a single call by
passing it as the `authorization` argument to `run_function`.

### Agents that run shell commands

An agent that works in a terminal can use the graft CLI instead of an MCP
connection. `graft content`, `graft schema` and `graft fn` run the same tools,
`--json` prints exactly what each tool returns, and `--remote` sends them to a
hosted site with a token saved by `graft login`. See
[Script the CLI](/docs/cli).

### Anonymous agents

Whether an agent without a token is allowed depends on where the server
listens:

| The server listens on | Agents without a token are |
| --- | --- |
| Your machine only (loopback) | Allowed, so local development needs no setup |
| Anything else | Refused, unless you set `GRAFT_MCP_ALLOW_ANONYMOUS=1` |

Only set that variable if your own proxy checks identity in front of Graft. The
server warns at startup while it is set.

> **Missing auth fails at startup.**
> `createGraftMcpHandler` refuses to start unless you give it an `actor`
> resolver or set `allowAnonymous: true`. This endpoint can write content,
> upload files, and decide approvals, so forgetting auth stops the server with a
> fix line instead of leaving the endpoint open.

## Permissions an agent needs

| To | The agent needs |
| --- | --- |
| Read content, the schema, functions, and the registry | Nothing beyond reaching the endpoint |
| Write or delete content, or upload files | The `content:write` scope |
| Approve or deny actions | The `approvals:decide` scope, on an authenticated connection |
| Run a function | Whatever that function's `access` rule requires |

Give each token the fewest scopes that work. Writing content is a separate
scope from reading, so you can hand every user of your app a read token without
letting them edit content.

## A separate, public endpoint for your docs

A project can also publish its documentation to agents at `/mcp`, with no
token. It is a different server with no access to content writes, functions,
or approvals. See [Public docs MCP](/docs/docs-mcp).

## Next steps

- [MCP tools](/docs/mcp-tools) lists everything an agent can call.
- [Resources & prompts](/docs/mcp-resources-and-prompts) covers documents and
  workflows an agent can attach.
- [Auth](/docs/auth) explains how tokens are checked.
