Concept
Before MCP, every integration between an LLM application and an external system (GitHub, a database, an internal API) was its own bespoke set of hand-written tool definitions and handler code, a search_issues tool for GitHub in one codebase looked nothing like a similarly-purposed tool in another, even though they were solving the same underlying problem. The Model Context Protocol (MCP) is an open standard that fixes this: a server built once, following the MCP spec, can be connected to by any MCP-capable client (Claude, another vendor's model host, an IDE) without either side needing custom integration code for the other. An MCP server exposes three kinds of capability, tools (callable functions), prompts (reusable prompt templates), and resources (readable data), through one standardized wire protocol.
The practical benefit for a frontend/application developer: instead of writing a bespoke tool definition and handler for "search GitHub issues," you connect to an existing MCP server that already implements it (Anthropic-hosted, or your own), and get the tool for free.
Path 1, The Messages API's MCP connector (single request, no hosted agent)
For a single Messages API call, the MCP connector (beta) lets Claude call tools on a remote MCP server directly, with Anthropic making the connection server-side, you never proxy the MCP traffic through your own backend.
const response = await client.beta.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
betas: ["mcp-client-2025-11-20"],
mcp_servers: [
{ type: "url", url: "https://mcp.example.com/sse", name: "example-mcp" },
],
tools: [
{ type: "mcp_toolset", mcp_server_name: "example-mcp" },
],
messages: [{ role: "user", content: "List open issues labeled 'bug' in this repo." }],
});Two parameters are required together, and this is the single most common integration mistake: mcp_servers declares the connection (URL, name), while tools must include an mcp_toolset entry whose mcp_server_name matches that connection's name exactly. Declaring mcp_servers alone, with no matching mcp_toolset entry in tools, is rejected as a validation error, the server exists, but nothing tells Claude it's allowed to use its tools.
Path 2, Managed Agents: persisted mcp_servers + vault credentials
For a hosted, stateful agent session (rather than a single request), MCP integrates at the agent level, with authentication handled by a separate vault resource, this split exists specifically to keep secrets out of reusable, versioned agent configs.
// 1. Create the agent, declares WHICH MCP servers to connect to, no auth here
const agent = await client.beta.agents.create({
name: "GitHub Triage Agent",
model: "claude-opus-4-8",
mcp_servers: [
{ type: "url", name: "github", url: "https://api.githubcopilot.com/mcp/" },
],
tools: [
{ type: "agent_toolset_20260401" },
{ type: "mcp_toolset", mcp_server_name: "github" },
],
});
// 2. Create a vault and store the MCP OAuth credential separately
const vault = await client.beta.vaults.create({ name: "github-credentials" });
await client.beta.vaults.credentials.create(vault.id, {
The credential is matched to the MCP server by URL, not by name, Anthropic auto-refreshes an mcp_oauth credential using the stored refresh_token before it expires, so a long-running agent session doesn't lose access mid-task. This vault split matters for a concrete reason: it means an agent definition (which is a shared, version-controlled, potentially-public-in-a-repo object) never contains a secret, the secret lives in a separate, workspace-scoped vault resource referenced only at session-creation time.
A critical, easy-to-miss distinction: MCP auth tokens are not REST API tokens
Hosted MCP servers (like mcp.linear.app or mcp.notion.com) typically require OAuth bearer tokens obtained through an OAuth flow specific to that MCP server, not the same API key you'd use to call that service's ordinary REST API. A Linear personal API key, for example, authenticates against Linear's REST API but will not work as a vault credential for Linear's MCP server; these are different, non-interchangeable auth systems even though they both ultimately grant access to "the same" underlying service.