npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@finch.app/mcp-client

v1.1.20

Published

Connect Model Context Protocol (MCP) servers into Finch.

Readme

MCP Client

MCP Client 是 Finch 为小工具提供的 MCP 桥接扩展,用来把 Model Context Protocol(MCP)server 暴露给 Agent 使用。

对小工具作者来说,推荐做法是:

  1. 在 manifest 中声明依赖 mcp.client capability。
  2. contributes.mcpServers 中只声明展示元数据:server name、工具标题、ToolCallCard 展示规则。
  3. 用自己的 setup 工具通过安全表单收集密钥。
  4. 把密钥保存在小工具自己的 ctx.storage 中。
  5. 在运行时通过 mcp.client#registerServer() 注册真实 MCP transport。

Tavily Search 采用的就是这条路径:manifest 只描述 Tavily 工具在 Finch 里如何展示;真正的 MCP server 则在 API Key 可用后,由 activate() 动态注册。

最佳实践:manifest 放 metadata,运行时注册 transport

1. 声明依赖和 MCP 展示元数据

package.json 里声明 requires.capabilities: ["mcp.client"],并添加一个 metadata-only 的 contributes.mcpServers 条目。

当 server 依赖用户配置时,不要在这里写 commandargsurlheaders 或任何密钥。

{
  "finch": {
    "requires": {
      "capabilities": ["mcp.client"]
    },
    "contributes": {
      "mcpServers": [
        {
          "name": "my-service",
          "description": "My Service MCP server. Call setup_my_service to configure it.",
          "toolMeta": {
            "titles": {
              "search": "My Service Search",
              "extract": "My Service Extract"
            }
          },
          "toolDisplay": {
            "tools": {
              "search": {
                "inline": {
                  "mode": "join",
                  "fields": [{ "path": "query", "maxLength": 80 }],
                  "template": "{query}"
                }
              }
            }
          }
        }
      ]
    }
  }
}

toolMeta.titles 控制 Finch 工具卡片上的短标题;toolDisplay.tools 控制工具调用旁边的 inline 摘要。

2. 运行时注册真实 MCP server

在小工具代码中,等配置可用后通过 mcp.client capability 注册 server。

import type * as finch from 'finch';

const SERVER_NAME = 'my-service';
const STORAGE_KEY = 'my-service.setup';

interface StoredSetup {
  apiKey: string;
}

type McpServerConfig =
  | { name: string; command: string; args?: string[]; env?: Record<string, string>; ownerExtensionId?: string; ownerExtensionName?: string }
  | { name: string; url: string; headers?: Record<string, string>; env?: Record<string, string>; ownerExtensionId?: string; ownerExtensionName?: string };

interface McpClientCapability {
  registerServer(config: McpServerConfig): Promise<{ ok: boolean; error?: string }>;
  unregisterServer(name: string): Promise<{ ok: boolean }>;
}

async function readSetup(ctx: finch.ExtensionContext): Promise<StoredSetup | undefined> {
  return ctx.storage.get<StoredSetup>(STORAGE_KEY);
}

async function registerRuntimeServer(ctx: finch.ExtensionContext, setup: StoredSetup): Promise<void> {
  if (!ctx.capabilities.has('mcp.client')) {
    ctx.logger.warn('mcp.client capability is not available');
    return;
  }

  const mcp = ctx.capabilities.get<McpClientCapability>('mcp.client');
  const result = await mcp.registerServer({
    name: SERVER_NAME,
    url: `https://example.com/mcp?apiKey=${encodeURIComponent(setup.apiKey)}`,
    ownerExtensionId: ctx.extension.id,
    ownerExtensionName: ctx.extension.displayName,
  });

  if (!result.ok) {
    ctx.logger.warn('failed to register MCP server', result.error);
  }
}

export function activate(ctx: finch.ExtensionContext): void {
  void readSetup(ctx).then((setup) => {
    if (setup) return registerRuntimeServer(ctx, setup);
  });
}

runtime 注册是内存态,并绑定到小工具生命周期。小工具禁用或卸载后,这个 runtime server 会一起消失,不会在 MCP Client 的用户配置里留下孤儿条目。

3. 用 setup 工具收集密钥

用 setup 工具通过 Finch 安全表单收集密钥,写入自己扩展的 storage,然后调用 registerServer()

ctx.subscriptions.push(ctx.tools.register({
  name: 'setup_my_service',
  title: 'Set up My Service',
  description: 'Collect the API key and register the My Service MCP server.',
  inputSchema: { type: 'object', properties: {} },
  risk: 'medium',
  async execute(_input, exec) {
    const form = await exec.ui.requestForm({
      title: 'Set up My Service',
      fields: [
        { key: 'apiKey', label: 'API Key', type: 'password', secret: true, required: true },
      ],
    });

    if (!form.submitted) {
      return { content: [{ type: 'text', text: 'Setup cancelled.' }] };
    }

    const apiKey = String(form.values.apiKey ?? '').trim();
    if (!apiKey) {
      return { content: [{ type: 'text', text: 'No API key was provided.' }], isError: true };
    }

    const setup = { apiKey };
    await ctx.storage.set(STORAGE_KEY, setup);
    await registerRuntimeServer(ctx, setup);

    return { content: [{ type: 'text', text: 'My Service MCP server is configured.' }] };
  },
}));

密钥不要写进 package.json,不要提交进小工具包,也不要在工具结果里回传给模型。

Server name 匹配规则

MCP Client 会用归一化后的 server name,把 runtime server config 和静态 contribution 合并起来。仅大小写不同可以兼容,但仍建议两边使用同一个稳定名称:

contributes.mcpServers[].name = "My Service"
registerServer({ name: "my-service", ... })
→ 内部会按归一化后的名称匹配

模型可见的 MCP 工具名不会加小工具 id 前缀,格式是:

mcp__<server>__<tool>

Finch 只在内部保留类似 my-plugin.my-service 的 owner-qualified key,用于 UI 归属和所有权展示,不作为模型看到的工具名。

MCP Client 提供什么

mcp.client capability 主要给小工具使用,核心方法是:

interface McpClientCapability {
  listServers(): Promise<string[]>;
  getServerStatuses?(): Promise<Array<{ name: string; status: string; toolCount: number; ownerExtensionId?: string; qualifiedName?: string }>>;
  listTools(server: string): Promise<Array<{ name: string; title?: string; description?: string; inputSchema?: Record<string, unknown> }>>;
  registerServer(config: McpServerConfig): Promise<{ ok: boolean; error?: string }>;
  unregisterServer(name: string): Promise<{ ok: boolean }>;
}

普通小工具应优先使用 registerServer(),不要直接写 MCP Client 的配置文件。

手动 MCP 配置:支持,但不推荐给小工具使用

MCP Client 仍然支持用户手写 servers.json,用于本地高级配置和排障。这适合高级用户手动配置,但小工具不应该写这个文件:否则卸载小工具后会留下孤儿 server 配置。

路径:

~/.finch/extension-data/mcp/servers.json

开发模式下根目录为 ~/.finch-dev/

stdio server

{
  "servers": [
    {
      "name": "filesystem",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/Documents"],
      "env": {
        "NODE_ENV": "production"
      }
    }
  ]
}

HTTP Stream server

{
  "servers": [
    {
      "name": "remote-search",
      "url": "https://example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${MCP_TOKEN}"
      },
      "env": {
        "MCP_TOKEN": "你的本地 token"
      }
    }
  ]
}

HTTP Stream 中的 env 只用于替换 headers 里的 ${KEY} 占位,不会作为请求 body 发送。

Agent 使用方式

Agent 不应该在工具尚未激活前直接调用 mcp__<server>__<tool>。它应先调用 Finch 的 ToolSearch,并设置 source: "mcp";MCP Client 会连接匹配的 server、发现工具,并把命中的 MCP 工具注入当前 run。

这是 Agent 运行时优化细节。小工具作者通常只需要关注上面的 contribution + registerServer() 模式。