# Connect an agent to tools

Connect a tool server to your project's MCP gateway, grant a tool to an agent, and call it with the agent's access token.

In this tutorial you connect a tool server to your project's MCP gateway, grant one of its tools to an agent, and call that tool with the agent's access token.

Each project has a gateway that speaks the Model Context Protocol (MCP), the protocol AI agents use to call tools. Your agents call tools through the gateway instead of holding each tool's credentials themselves.

```text
https://acme.authfi.io/mcp
```

The gateway checks every call against what the agent was granted, and against any limit on the grant. It holds the tool's credential, so the credential never reaches the agent. It makes a call wait for a person when a rule says so, and it writes each call to the audit record with its decision and reason.

## Before you begin

You need:

- An AuthFI project and its issuer, the project's URL. This tutorial uses `https://acme.authfi.io`.
- An agent, and an access token for it whose `resource` is the gateway. [Give an AI agent an identity](/quickstart/agent) ends with exactly that token.
- A tool server that speaks MCP, and its URL. This tutorial uses a server with tools for support tickets.
- Permission to manage the MCP gateway in the AuthFI console.

```bash
export ACCESS_TOKEN=<the agent's access token>
```

<Steps>

### Connect the tool server

In the console, open **Integrations > MCP Gateway**, select the **Servers** tab, and select **Connect a server**. Fill in these fields.

| Field                                | What to enter                                                         |
| ------------------------------------ | --------------------------------------------------------------------- |
| **Namespace**                        | One word of letters, digits and dashes. This tutorial uses `tickets`. |
| **Server URL**                       | Where the tool server is reached                                      |
| **Transport**                        | **Streamable HTTP** or **SSE**, whichever the tool server uses        |
| **How it authenticates the gateway** | **Bearer token**                                                      |
| **Whose credential**                 | **One shared credential**                                             |
| **Credential**                       | The token the tool server expects                                     |

Select **Register**. The dialog closes and a message says the server is registered and that the gateway will discover its tools.

The namespace prefixes every tool name. A tool named `reply` on this server appears as `tickets.reply`.

### Grant a tool to the agent

On the **Tools** tab, select **Say who may call a tool**. Fill in the dialog.

| Field        | What to choose       |
| ------------ | -------------------- |
| Caller type  | **An AI agent**      |
| **Caller**   | Your agent           |
| **May call** | Tick `tickets.reply` |

Select **Grant 1 tool**. A message confirms that the agent may now call the tool.

An agent can call only the tools it was granted, and a grant is on one tool. Granting `tickets.reply` does not grant any other tool on the server.

### Find the gateway URL

Open the **Overview** tab. Under **Where the agents connect**, **Gateway** is the URL your agent calls. For the example project it is `https://acme.authfi.io/mcp`.

### List the tools

The gateway takes JSON-RPC 2.0 over HTTP `POST`, with the agent's access token as a Bearer credential. It speaks MCP revision `2025-06-18` and answers three methods: `initialize`, `tools/list` and `tools/call`.

<Endpoint method="POST" path="/mcp" />

Send `tools/list` to see the tools on every server you connected.

```bash
curl https://acme.authfi.io/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

Each tool in the answer has this shape.

```json
{
  "name": "tickets.reply",
  "description": "Reply to a ticket",
  "inputSchema": { "type": "object" },
  "annotations": { "io.authfi/governance": "no_policy_restriction" }
}
```

The `io.authfi/governance` annotation says what would happen if this agent called the tool. `no_policy_restriction` means this agent may call it.

### Call a tool

Send `tools/call` with the tool's name and its arguments.

```bash
curl https://acme.authfi.io/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": { "name": "tickets.reply", "arguments": { "ticket": "4821", "text": "On it." } }
  }'
```

The gateway checks the call, forwards it to the tool server with the tool's own credential, and returns the tool's result.

</Steps>

## Check that it works

In the console, open **Integrations > MCP Gateway** and select the **Tape** tab. The tape lists each call with the columns **When**, **Caller**, **Tool**, **Verdict**, **Why** and **Acted for**.

Find the row whose **Caller** is your agent and whose **Tool** is `tickets.reply`. Its **Verdict** reads **allowed**. The gateway checked the call and forwarded it.

## Troubleshooting

| What you see                                   | Cause                                                               | Fix                                                                 |
| ---------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `401` with a `WWW-Authenticate` header         | The request has no access token, or the token is expired or revoked | [Sign the agent in](/quickstart/agent) again and send the new token |
| A result with `isError` and `Denied by policy` | The agent is not granted the tool                                   | Grant the tool with **Say who may call a tool**                     |
| JSON-RPC error `-32601`                        | The method is not one of the three the gateway answers              | Use `initialize`, `tools/list` or `tools/call`                      |
| JSON-RPC error `-32700`                        | The body is not valid JSON                                          | Check the quoting of the request body                               |

A denied call is an ordinary tool result with `isError` set, so the model reads it and can change course.

```json
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "isError": true,
    "content": [{ "type": "text", "text": "Denied by policy: agent:agt_7f3a9c21 is not granted payments.refund" }]
  }
}
```

A `401` points to where the agent signs in.

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://acme.authfi.io/.well-known/oauth-protected-resource"
```

The document at that URL names the project as the authorization server for the gateway, as RFC 9728 describes.

```json
{
  "resource": "https://acme.authfi.io/mcp",
  "authorization_servers": ["https://acme.authfi.io"]
}
```

## Next steps

<Cards>
  <Card href="/agents/mcp-gateway" title="The MCP gateway">
    Every check the gateway makes, per-person credentials, and calls that need approval.
  </Card>
  <Card href="/agents/limits" title="Limit what an agent may do">
    Add limits and rules that deny a call or send it to a person.
  </Card>
  <Card href="/agents/approvals" title="Ask before a sensitive action">
    Have a person approve an action before the agent takes it.
  </Card>
  <Card href="/security/audit" title="Use the audit record">
    Find each tool call, with its decision and reason.
  </Card>
</Cards>
