---
title: MCP
description: Register Model Context Protocol servers, authorize discovered tools one at a time, and invoke them through the gateway.
---

Model Context Protocol servers expose tools to agents. Veefore treats an MCP
server as an untrusted tool source: you register it, you discover what it
offers, and then **a human authorizes each tool individually**.

:::security Discovery and authorization are separate steps
A newly discovered MCP tool is unauthorized until someone authorizes it.

If discovery granted access, an MCP server could widen its own reach by adding a
tool — you would connect to it for one capability and silently acquire however
many it chose to expose later. Separating the steps means the server proposes
and you decide.
:::

## Register a server

```bash
curl -X POST https://veefore.example.com/v1/mcp/servers \
  -H "Authorization: Bearer $VEEFORE_API_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "key": "internal-tools",
    "name": "Internal tooling MCP server",
    "transport": "HTTP",
    "endpoint_url": "https://mcp.internal.example.com",
    "credential_id": "cred_01JAV…"
  }'
```

The server starts `PENDING`. It cannot be used until it is approved, and no
agent can reach anything through it in the meantime.

## Discover its tools

```bash
curl -X POST https://veefore.example.com/v1/mcp/servers/mcp_01ABC/discover \
  -H "Authorization: Bearer $VEEFORE_API_KEY"
```

Returns the tool list the server advertises, with each tool's schema and
description. Every one of them is **unauthorized**.

Read the list before authorizing anything. A tool named `run_query` with free-
text SQL is a different proposition from `get_ticket`, and the names are chosen
by the server, not by you.

## Authorize, one at a time

```bash
curl -X POST https://veefore.example.com/v1/mcp/servers/mcp_01ABC/tools/tool_01XYZ/authorize \
  -H "Authorization: Bearer $VEEFORE_API_KEY"
```

An authorized MCP tool behaves like any other tool from here: it requires a
capability, it is subject to policy, and it goes through the full pipeline.

Authorize what your agents actually need. "All of them" is not a decision.

## Call it

:::tabs

:::tab TypeScript
```ts
const decision = await agent.veefore.mcpCall({
  server: 'internal-tools',
  tool: 'search_tickets',
  arguments: { query: 'refund failed', limit: 20 },
});
```
:::

:::tab Python
```python
decision = agent.veefore.mcp_call(
    server="internal-tools",
    tool="search_tickets",
    arguments={"query": "refund failed", "limit": 20},
)
```
:::

:::

Over the wire: `POST /gateway/v1/mcp/call`. The same twelve-stage pipeline
applies — same decisions, same action record, same audit trail. An MCP tool is
not a side door.

## Re-discovery

Servers add and remove tools. Running discovery again updates the list, and
**newly discovered tools arrive unauthorized** — including on a server whose
other tools you have already authorized.

Make re-discovery a routine thing you do and look at, rather than something that
happens implicitly. A tool appearing in the list is the server telling you it
would like more reach.

## Disable a tool

```bash
curl -X POST https://veefore.example.com/v1/mcp/servers/mcp_01ABC/tools/tool_01XYZ/disable \
  -H "Authorization: Bearer $VEEFORE_API_KEY"
```

Immediate. Refused with `TOOL_DISABLED`.

To cut off a whole server, disable its tools or revoke its credential. A server
that is not trusted is refused with `MCP_SERVER_NOT_TRUSTED`.

## Transport

`veefore doctor` audits MCP transport and authorization, and reports:

- A server reachable over plaintext HTTP to a non-loopback host
- Tools authorized but never used — reach you are carrying for nothing
- Servers in `PENDING` for a long time — someone started and did not finish
- A server with no credential, calling an endpoint that presumably wants one

## Policy on MCP

`tool.kind` is `MCP` and `tool.protocol` is `MCP`, so you can write rules that
apply to MCP specifically:

```json
{
  "id": "rule-mcp-needs-approval-in-prod",
  "name": "MCP tools need approval in production",
  "priority": 600,
  "enabled": true,
  "when": {
    "kind": "group",
    "id": "g-mcp-prod",
    "operator": "and",
    "children": [
      { "kind": "condition", "id": "c-kind", "field": "tool.kind", "operator": "eq", "value": "MCP" },
      {
        "kind": "condition",
        "id": "c-env",
        "field": "environment.type",
        "operator": "eq",
        "value": "PRODUCTION"
      }
    ]
  },
  "then": {
    "decision": "REQUIRE_APPROVAL",
    "reason": "Third-party MCP tools require a human decision in production.",
    "approval": { "routingMode": "SINGLE", "requiredApprovals": 1, "ttlSeconds": 900 }
  }
}
```

A good default for a server you do not operate yourself. It can be relaxed per
tool once you have watched specific ones behave.

## Practical advice

**Treat an MCP server as a dependency with privileges.** It gets a credential,
it gets reached over the network, and its tool list is under its control. Review
it the way you would review a library you are about to add.

**Authorize narrowly, and revisit.** A tool authorized for a one-off task and
never revoked is standing reach nobody remembers granting.

**Prefer MCP servers you run.** A server on your own infrastructure with its own
credential is a very different risk profile from a public one.

## Related

:::cards

:::card A2A | /build/a2a
The other external-party protocol, with passport verification.
:::

:::card MCP API | /reference/api/mcp
Register, discover, authorize, disable.
:::

:::card MCP architecture | /architecture/adr/0009-mcp-architecture
Why discovery and authorization are separate.
:::

:::card Register a tool | /build/register-a-tool
How a tool works in general.
:::

:::
