Skip to main content

MCP integration

Declaratively contribute MCP servers, loaded on demand

MCP integration is a textbook use of the capability mechanism: the official MCP Client mini tool provides mcp.client, and other mini tools consume it.

When to choose MCP

  • The tool set is large (10+) and most of it isn't needed at once — you want Finch to load it on demand.
  • The target service already has an official MCP SDK, and you don't want to reimplement it.

Two-layer design

The static layer (finch.json) holds only metadata, never secrets:

{
  "requires": { "capabilities": ["mcp.client"] },
  "contributes": {
    "mcpServers": [
      { "name": "my-server", "description": "My MCP server." }
    ]
  }
}

The runtime layer, inside activate(), provides the actual transport configuration:

async function registerWhenReady(ctx: finch.MiniToolContext, apiKey: string) {
  // The MCP Client may activate after this tool, so poll briefly
  for (let i = 0; i < 20; i++) {
    if (ctx.capabilities.has('mcp.client')) break;
    await new Promise((r) => setTimeout(r, 250));
  }
  if (!ctx.capabilities.has('mcp.client')) return;

  const mcp = ctx.capabilities.get('mcp.client');
  await mcp.registerServer({
    name: 'my-server',
    command: 'npx',
    args: ['-y', 'my-mcp-server'],
    env: { API_KEY: apiKey },
    ownerExtensionId: ctx.minitool.id,
  });
}

Runtime registration is in-memory and must be redone on every activation.

Where secrets come from

Standard pattern: provide a setup_* tool or a settings-menu item, collect the secret via a form, store it with ctx.secrets, then call registerServer(). See Accounts, settings & OAuth login.