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.