# MCP

`mcp()` connects an Agent to external Model Context Protocol servers.
It resolves each configured MCP Server and exposes its tools as model-facing Agent tools.

The Capability normalizes MCP tool names with the server name, attaches sanitized MCP metadata, and closes MCP clients created from configs or resolvers after the invocation.
Static direct clients stay application-owned so they can be reused across invocations.
Tool names, descriptions, and schemas stay with the MCP tool contract. Put broader guidance about when to use an MCP server in Agent Driver Instructions.
An optional approved fingerprint map can block added or changed tool definitions before they reach an Agent Driver.

If a server publishes an incomplete or unstable contract, pin the application-owned
part of that contract with `toolOverrides`. Overrides are keyed by the configured
server name and the original MCP tool name. They change the Agent-facing
description or schema; the arguments sent to the remote server remain unchanged.

```ts [server/agents/support.ts]
mcp({
  servers: { support: supportMcpServer },
  toolOverrides: {
    support: {
      threads_get: {
        inputSchema: {
          additionalProperties: false,
          properties: { id: { type: 'string' } },
          required: ['id'],
          type: 'object',
        },
      },
    },
  },
})
```

Use overrides for a contract you own and review. They do not make an unavailable
server available, or change the server's runtime validation. Input overrides use
object JSON Schema, without transforms or coercion. Unknown server names fail
resolution. Unknown tool names fail for servers whose discovery succeeded;
skipped or unavailable servers do not require discovered tool matches.

## Configure MCP servers

Pass a server map.
Each entry can be a static direct MCP client borrowed from the application, or a client config or resolver whose resolved client is owned by the Agent Invocation.
Use `false`, `null`, or `undefined` when a server is not configured for the current deployment or invocation.

```ts [server/agents/support.ts]
import { defineAgent } from 'vite-hub/agent'
import { mcp } from 'vite-hub/agent/capabilities'
import { docsMcpServer } from '../mcp/docs'

export default defineAgent({
  driver: { model },
  capabilities: [
    mcp({
      servers: {
        docs: docsMcpServer,
      },
    }),
  ],
})
```

Optional servers do not need a conditional Capability array. Declare credentials that may be absent as optional in Server Env, for example `analyticsToken: env({ optional: true, secret: true })`, then resolve only the server whose credentials are available:

```ts [server/agents/support.ts]
import { useServerEnv } from '#vitehub/env/server'
import { defineAgent } from 'vite-hub/agent'
import { mcp } from 'vite-hub/agent/capabilities'
import { remoteMcpServer } from 'vite-hub/agent/mcp'

export default defineAgent({
  driver: { model },
  capabilities: [
    mcp({
      servers: {
        analytics: () => {
          const token = useServerEnv().analyticsToken
          return token
            ? remoteMcpServer({
                headers: { Authorization: `Bearer ${token.unseal()}` },
                url: 'https://analytics.example.com/mcp',
              })
            : undefined
        },
      },
    }),
  ],
})
```

## Authenticate through a Connection

Set `connection` on a client config to use a [Connection](https://vitehub.dev/docs/connections) as the server credential. The Connection sends the `Authorization` header, refreshes the token, checks access for each request, and records activity. The Agent and the MCP client never see the token.

```ts [server/agents/support.ts]
mcp({
  servers: {
    docs: {
      connection: 'docs',
      transport: { type: 'http', url: 'https://docs.example.com/mcp' },
    },
  },
})
```

`connection` requires an `http` or `sse` transport config without `authProvider`. The Connection replaces `transport.fetch`. A resolver can also return a config with `connection`, for example to select the URL for each Invocation. Without `vitehub({ connections: true })`, resolution fails with an error that names the Connection.

Each request maps to a Connection Operation id:

| Request | Operation id | Effect | Activity |
| --- | --- | --- | --- |
| HTTP `GET` or `HEAD` request | `fetch` | read | Every request. |
| HTTP write request, including a tool call or protocol message | `fetch` | write | Every request. |

The MCP transport uses the native Connection `fetch` client. GET and HEAD requests are reads. POST requests, including tool calls and protocol messages, are writes. Without a matching `fetch` rule, writes are denied, so the Agent rule in the Connection must allow the transport:

```ts [server/connections/docs.ts]
access: {
  'agent:support': { read: true, write: ['fetch'], approve: false },
},
```

ViteHub checks access before the tool runs. A denied request fails with `CONNECTION_DENIED`. MCP needs protocol responses in the active session, so its Connection client rejects writes that require approval with `CONNECTION_DENIED` before creating an approval. Set `approve: false` on the Agent access rule; durable approval replay cannot resume MCP discovery or tool calls.

## Executor through `mcp()`

Use `mcp()` to connect an [Executor](https://executor.sh/) tool catalog. Executor keeps the upstream integration credentials and policies behind one MCP endpoint. Copy the exact endpoint from Executor's **Connect** card.

With an OAuth 2 Connection named `executor`:

```ts [server/agents/support.ts]
mcp({
  servers: {
    executor: {
      connection: 'executor',
      transport: { type: 'http', url: 'https://executor.sh/acme/mcp' },
    },
  },
})
```

Allow the transport in the Connection with `access: { 'agent:support': { read: true, write: ['fetch'], approve: false } }`. The `executor` Connection can use an OAuth client for your app, or an [API key Connection](https://vitehub.dev/docs/connections/configure#api-key-connections) with a personal Executor API key.

Without Connections, you can also send an Executor API key from Server Env in a resolver. Use a personal API key: Executor rejects organization keys for MCP sessions.

```ts [server/agents/support.ts]
mcp({
  servers: {
    executor: () => {
      const apiKey = useServerEnv().executorApiKey
      return apiKey
        ? remoteMcpServer({
            headers: { Authorization: `Bearer ${apiKey.unseal()}` },
            url: 'https://executor.sh/acme/mcp',
          })
        : undefined
    },
  },
})
```

The resolver runs for each Agent Invocation, so a rotated key applies to the next Invocation. Executor tools use normalized names such as `mcp_executor_execute`. Pin them with `integrity: { executor: approvedExecutorTools }`.

## How MCP connections work

During resolution, `mcp()` connects to each configured MCP Server and asks for its tool set.
ViteHub prefixes normalized tool names with `mcp_<server>_` and rejects duplicate normalized names.
Pass a resolver or client config for an invocation-owned connection, or a static direct client when the application owns its lifetime.
An absent entry contributes no tools and creates no MCP client. Other configured servers still resolve.
Transient transport failures during resolution or discovery mark the affected server unavailable and let other servers contribute tools. Authentication, configuration, cancellation, protocol, integrity, cleanup, and other resolver failures remain fatal.

Generic MCP client configurations default top-level `protocolVersionDiscovery` to `false` so existing initialize-first servers remain compatible. Set it to `true` on a client configuration when the server supports protocol-version discovery.

The Capability redacts secret-shaped metadata keys before exposing MCP metadata.

## Pin tool definitions

An MCP Server can return a different tool description, title, or input schema after its tools were reviewed.
Use `fingerprintTools()` during a trusted review step, persist the approved result in application code or configuration, then pass it to `integrity` under the matching server name.

```ts [scripts/review-docs-mcp.ts]
import { fingerprintTools } from 'ai'

const approved = await fingerprintTools(await client.tools())
console.log(JSON.stringify(approved, null, 2))
```

Review that output before saving it. Do not generate the baseline during normal application startup, because that would trust whichever definitions the server returns first.

```ts [server/agents/support.ts]
import { defineAgent } from 'vite-hub/agent'
import { mcp } from 'vite-hub/agent/capabilities'
import { docsMcpServer } from '../mcp/docs'
import { docsToolFingerprints } from '../mcp/docs-tool-fingerprints'

export default defineAgent({
  driver: { model },
  capabilities: [
    mcp({
      integrity: {
        docs: docsToolFingerprints,
      },
      servers: {
        docs: docsMcpServer,
      },
    }),
  ],
})
```

ViteHub fingerprints each configured server independently before it normalizes or contributes tools.
Added and changed definitions fail Capability resolution; removal-only changes remain allowed because MCP tool lists can narrow by feature or authorization.
Drift errors include the server name and the added, changed, and removed original tool names.

Fingerprints cover tool names, string descriptions, titles, and resolved input schemas.
They do not prove that the first reviewed definition was safe or detect changed remote behavior behind an unchanged definition.

## Requirements

`mcp({ servers })` requires a server map. Each present entry must resolve to an MCP client or MCP client configuration. `false`, `null`, and `undefined` skip that entry.
MCP client configuration uses the optional `@ai-sdk/mcp` runtime package when ViteHub creates the client from config.
Tool integrity requires `ai` 7.0.19 or newer only when `integrity` is configured.

The external MCP Server owns its own credentials, availability, and tool behavior.

During resolution and tool discovery, transient transport failures make only the affected server unavailable. Other servers retain their tools. HTTP 408, 409, 429, and 5xx responses, recognized network errors, and bounded timeouts produce entries in the Invocation input context at `vitehub.mcp.warnings`. Each entry records the server, phase, and HTTP status when available. Inspection marks the server `Unavailable`.

Authentication, configuration, cancellation, protocol errors, duplicate tools, and tool-definition integrity drift remain fatal.

A server with `connection` also requires the [Connections](https://vitehub.dev/docs/connections) primitive and a connected Connection.

## Driver support

| Agent Driver | Support |
| --- | --- |
| Model-backed | Receives normalized MCP tools. |
| Provider-backed | Receives normalized MCP tools through the provider MCP bridge; runtime connection and cleanup still run around the invocation. |
| Custom-run-backed | Receives prepared context; `driver.run` decides whether to call MCP clients or tools through custom code. |

## Verify MCP connections

Successful invocations expose normalized MCP tools through `agent info` and stream tool steps through `agent dev`.
Confirm that MCP tools use normalized names such as `mcp_docs_search`.

Integrity checks run during invocation resolution. Static Agent inspection metadata does not connect to MCP Servers or claim that a configured baseline currently matches.

Run one invocation with a duplicate normalized tool name during development.
Confirm that the Capability fails before model execution.

## Options

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `integrity` | `Record<string, McpToolFingerprints>` | none | Approved AI SDK tool fingerprints keyed by configured server name. Blocks added or changed definitions. |
| `servers` | `Record<string, McpServerConfig>` | required | MCP clients, client configs, optional absent values, or resolvers keyed by server name. |
| `servers.<name>.connection` | `string` | none | Name of a Connection in `server/connections/` that authorizes an `http` or `sse` client config. |

Cover MCP usage guidance in Agent Driver Instructions with explicit Capability coverage blocks. Keep MCP tool descriptions with the MCP Server because they are structured tool contracts.

## Related pages

- [Official capabilities](https://vitehub.dev/docs/agents/capabilities/official)
- [Custom capabilities](https://vitehub.dev/docs/agents/capabilities/custom)
- [Connections](https://vitehub.dev/docs/connections)
- [AI SDK MCP tool-definition drift](https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools#detecting-tool-definition-drift-rug-pull)

## Inspect servers and tools

Open an Invocation's **Capabilities** tab and select **MCP**. Each configured server has a recorded discovery status, sanitized connection metadata, and its tools. A tool shows its original MCP name, the name exposed to the Agent, its description, and recorded input and output JSON Schema.

The snapshot distinguishes resolved servers, skipped optional servers, and resolution or discovery failures. Resolved servers can have no tools. These are observations from the selected Invocation, not current connection health. Opening the view does not reconnect or call tools. MCP resources mounted through Sources are outside this Capability's inventory.

Enable `configuration: 'content'` on the Invocation journal to retain the custom view and tool contracts. Missing schemas are shown as not recorded. The built-in Console journal enables configuration capture.
