MCP Patterns

MCP Server Lifecycle

Connect, close, and reconnect MCP servers at the right application boundary.

MCP servers expose external tools to an agent. Treat each MCP connection as infrastructure: decide whether it is required, when it connects, how it is closed, and what happens when it fails.

Scenario

An agent needs tools from a filesystem MCP server, an internal docs MCP server, and a remote CRM MCP server. Some are required for startup. Others are optional and should degrade gracefully.

When to Use It

Use this pattern whenever an agent registers tools with .mcp(...) or wraps MCP tools with local Anvia tools.

Architecture Shape

BoundaryPattern
required startup dependencyconnect once during application boot and fail fast on error
optional dependencyattempt connection, log failure, run agent without that server
request-scoped serverconnect inside try/finally and always close
long-lived processclose all servers during shutdown
reconnectclose previous server, connect next server, rebuild agents or update tool sets

Connect at Startup

import { connectMcp, mcp, type McpConnection, type McpServer } from "@anvia/core";

export async function connectMcpServers() {
  const filesystem = await connectMcp(
    mcp.stdio({
      name: "filesystem",
      command: "npx",
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
    }),
  );

  const docs = await connectMcp(
    mcp.http({
      name: "docs",
      url: "https://mcp.example.com/mcp",
    }),
  );

  return { filesystem, docs };
}

Register connected servers on an agent.

const { filesystem, docs } = await connectMcpServers();

const agent = new AgentBuilder("research", model)
  .instructions("Use MCP tools when external context is needed.")
  .mcp([filesystem, docs])
  .defaultMaxTurns(4)
  .build();

Optional Servers

Optional servers should not block the whole application.

async function connectOptional(connection: McpConnection) {
  try {
    return await connectMcp(connection);
  } catch (error) {
    logger.warn({ error, server: connection.name }, "optional MCP server unavailable");
    return undefined;
  }
}

const docs = await connectOptional(
  mcp.http({ name: "docs", url: "https://mcp.example.com/mcp" }),
);

const mcpServers = [docs].filter((server): server is McpServer => server !== undefined);

Request-Scoped Connections

Use request scope only when the connection depends on request-local credentials or a short-lived resource.

const server = await connectMcp(connection);

try {
  const agent = new AgentBuilder("tenant-docs", model)
    .mcp([server])
    .defaultMaxTurns(3)
    .build();

  return await agent.prompt(message).send();
} finally {
  await server.close();
}

Most application servers should prefer startup connections over request-scoped connections.

Registry and Reconnect

type McpRegistry = {
  get(name: string): McpServer | undefined;
  set(server: McpServer): void;
  closeAll(): Promise<void>;
};

async function reconnect(connection: McpConnection, registry: McpRegistry) {
  const previous = registry.get(connection.name);
  await previous?.close();

  const next = await connectMcp(connection);
  registry.set(next);

  return next;
}

After reconnecting, create new agents or update the shared tool catalog used by future runs. A built agent keeps the tools it was built with unless it uses a shared mutable ToolSet.

Failure Modes

FailureFix
app starts without required MCP toolsfail fast during startup
optional MCP outage breaks all agentsomit optional server and log degraded capability
server process leaksclose on shutdown or in finally
reconnected tools not visiblerebuild agents or update the shared tool set
tool call loops on remote errorskeep turn limits low and expose clear tool error text

Test Checklist

  • Test required server connection failure at startup.
  • Test optional server failure produces a reduced agent capability set.
  • Test request-scoped connections close in finally.
  • Test reconnect closes the previous server before replacing it.
  • Use Studio or /agents/:agentId/mcps to inspect registered MCP servers.