返回市场
MCP工具

MCP工具

作者:civicteam2 星标更新:2025-10-27

项目介绍

Civic MCP Hooks

用于模型上下文协议(MCP)的中间件层,能够监控、验证和转换AI工具交互。

理解MCP及其为何需要挂钩

什么是MCP?

模型上下文协议(MCP)是一种标准,允许AI助手(如Claude)与外部工具和服务进行交互。可以将其视为一种通用语言,使AI模型能够:

  • 读写文件
  • 查询数据库
  • 调用API
  • 执行代码
  • 访问各种服务

当你使用支持MCP的AI助手时,它可以代表你执行实际操作,使其在自动化和生产力方面非常强大。

为什么添加中间件层?

虽然MCP的强大功能令人兴奋,但也提出了重要的问题:

  • 安全性:如何确保AI仅访问它应该访问的内容?
  • 审计:如何追踪已采取的操作?
  • 控制:如何防止意外或危险的操作?
  • 定制化:如何根据特定用例修改行为?

这就是挂钩发挥作用的地方。就像Web应用程序使用中间件来处理身份验证、日志记录和请求处理一样,MCP也可以从类似的模式中受益。

这个解决方案是如何工作的

我们的方法引入了一个“透明服务器”,位于AI和实际MCP工具之间:

AI助手 ←→ 透明服务器 ←→ 目标MCP服务器
                       ↓↑
                    [挂钩]

当AI想要使用一个工具时,会发生以下情况:

1. 请求:AI → 透明服务器 → 挂钩1 → 挂钩2 → ... → 目标MCP服务器
2. 响应:AI ← 透明服务器 ← 挂钩2 ← 挂钩1 ← ... ← 目标MMCP服务器

透明服务器:

  1. 拦截来自AI的所有请求
  2. 通过挂钩链处理这些请求
  3. 转发经过批准的请求到实际工具
  4. 返回响应(也通过挂钩处理)

链条中的每个挂钩都可以:

  • 检查请求(AI试图做什么?)
  • 修改请求(更改参数,添加上下文)
  • 批准或拒绝请求(安全性和验证)
  • 执行副作用(审计日志记录,通知,分析)
  • 转换响应(格式化,过滤或增强结果)

实际案例

为什么我们需要特定于MCP的挂钩?这关乎关注点分离以及大型语言模型(LLM)工具使用的独特挑战。

核心问题:MCP服务器设计用于做好一件事——提供工具。它们不应该被身份验证逻辑、审计轨迹或上下文特定的修改所困扰。此外,OAuth(MCP使用)缺乏对LLM交互所需的细粒度控制——无法表达“允许读取文件但仅限于/docs文件夹”或“允许调用API但基于内容进行速率限制”。

示例1:针对LLM的防护措施

你的MCP文件服务器提供了简单的文件操作。但是,LLM需要不同于人类用户的规则:

人类用户:可以精确点击他们需要的文件
LLM:可能会尝试读取整个目录树以“有所帮助”

防护措施挂钩:限制目录遍历深度,防止读取二进制文件,并设置文件大小上限——这些规则仅适用于LLM。

MCP服务器保持简单,而挂钩则增加了针对LLM的安全措施。

示例2:基于上下文的工具描述和提示

同一工具可能需要针对不同用例的不同描述:

标准获取工具:“检索网页内容”

在研究环境中:
“检索网页内容(首选学术来源,检查Sci-Hub)”

在企业环境中:
“检索网页内容(仅内部维基,阻止外部站点)”

自定义描述挂钩会根据您的上下文修改工具描述——这是原始MCP服务器不应处理的事情。

示例3:强制透明性与解释挂钩

LLM可以在不解释原因的情况下使用工具。解释挂钩添加了一个必需的“原因”参数:

没有挂钩:
AI:execute_sql("DROP TABLE users")

有解释挂钩:
AI:execute_sql("DROP TABLE users", reason="用户请求清理数据库")

这种修改发生在中间件层——MCP服务器不需要改变。

示例4:语义审计轨迹

MCP服务器返回原始响应。审计挂钩可以添加语义含义:

MCP服务器返回:{状态:"成功",影响行数:1523}

审计挂钩记录:
- 工具:数据库查询
- 动作:“批量更新客户电子邮件”
- 影响:修改了1523条记录
- 上下文:“作为从旧电子邮件域迁移的一部分”
- 风险级别:高(批量数据修改)

MCP服务器专注于数据库操作,而挂钩则处理合规性日志记录。

可用的软件包

这个单体仓库包含了你需要的一切,以便为MCP添加中间件层:

核心基础设施

@civic/passthrough-mcp-server 主要代理服务器,拦截MCP流量并将其路由到你的挂钩。这是使其他一切成为可能的基础。

@civic/hook-common 构建挂钩的共享实用程序和TypeScript类型。提供AbstractHook基类,使得创建新挂钩变得简单。

示例挂钩

@civic/audit-hook 记录每个请求和响应,用于调试和合规性。非常适合了解你的AI正在做什么并维护审计轨迹。

@civic/guardrail-hook 实现安全规则以过滤和验证请求。包括一个示例,防止访问某些域,但可以根据需要扩展任何验证逻辑。

@civic/simple-log-hook 最简单的挂钩——仅仅记录到控制台。了解挂钩工作原理和构建自己的挂钩的良好起点。

@civic/explain-hook 向所有工具添加“原因”参数,鼓励AI解释其使用每个工具的原因。有助于透明性和调试。

@civic/custom-description-hook 根据配置替换工具描述。对于提供有关工具在你的环境中做什么的上下文特定信息很有用。

@civic/rate-limit-hook 对每个用户的工具调用实施速率限制。每分钟和每小时可配置限制,并带有明确的重试时间响应。

@civic/local-tools-hook 一个编程挂钩,允许直接将本地工具添加到透明MCP服务器,而无需单独的MCP服务器。非常适合在应用代码中定义自定义工具。

测试工具

@civic/fetch-docs 一个简单的MCP服务器,抓取网页并将其转换为markdown。作为测试目标包含在内,用于透明服务器和挂钩。

@civic/whoami-server 一个集成Civic Auth的MCP服务器,识别认证用户。提供一个“whoami”工具,返回当前用户的信息,对于测试认证流程很有用。

快速开始

先决条件

  • Node.js 18+
  • pnpm(推荐)或npm

快速入门

  1. 克隆并安装:
git clone https://github.com/civicteam/mcp-hooks.git
cd mcp-hooks
pnpm install
pnpm build
  1. 尝试简单示例:
# 终端1:启动fetch-docs MCP服务器
cd packages/fetch-docs
pnpm start

# 终端2:启动审计挂钩
cd packages/audit-hook
pnpm start

# 终端3:启动透明服务器
cd packages/passthrough-mcp-server
export TARGET_SERVER_URL="http://localhost:33005"
export HOOKS="http://localhost:33004"
pnpm start

现在,连接到端口34000的任何MCP客户端都会先由审计挂钩记录所有请求,然后再转发到fetch-docs服务器。

使用包含的示例进行测试

test/目录包含随时可用的配置:

cd test
./test.sh simple-log-passthrough.json  # 观察基本日志记录
./test.sh audit-passthrough.json       # 测试审计轨迹功能
./test.sh guardrail-passthrough.json   # 测试安全过滤

创建你自己的挂钩

构建自定义挂钩很简单:

  1. 创建一个新的包:
mkdir packages/my-hook
cd packages/my-hook
pnpm init
  1. 安装依赖项:
pnpm add @civic/hook-common @trpc/server
  1. 实现你的挂钩:
import { AbstractHook, createHookRouter, ToolCallRequestHookResult } from "@civic/hook-common";
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import type { CallToolRequest } from "@modelcontextprotocol/sdk/types.js";

class MyHook extends AbstractHook {
  get name(): string {
    return "my-hook";
  }

  async processCallToolRequest(request: CallToolRequest): Promise<ToolCallRequestHookResult> {
    // 你的逻辑在这里
    console.log(`处理:${toolCall.params.name}`);
    
    // 继续请求
    return {
      resultType: "continue",
      request: toolCall
    };
  }
}

// 启动服务器
const hook = new MyHook();
const router = createHookRouter(hook);
const server = createHTTPServer({ router, createContext: () => ({}) });
server.listen(33007);
  1. 使用你的挂钩:
export HOOKS="http://localhost:33007"
pnpm start

高级配置

多个挂钩

挂钩可以串联在一起:

export HOOKS="http://localhost:33004,http://localhost:33005,http://localhost:33006"

它们按顺序处理请求,按相反顺序处理响应。

挂钩响应类型

挂钩可以返回三种类型的响应:

  • 继续:继续(可能已修改)的请求
  • 中止:停止处理并返回错误
  • 响应:不调用目标服务器就返回响应

环境变量

  • PORT:透明服务器的HTTP端口(默认:34000)
  • TARGET_SERVER_URL:要转发请求的目标MCP服务器
  • TARGET_SERVER_TRANSPORT:传输类型(httpStream,sse,stdio)
  • HOOKS:逗号分隔的挂钩URL列表

架构细节

技术栈

  • TypeScript:所有包中的全类型安全
  • tRPC:挂钩和服务器之间的类型安全通信
  • fastMCP:高性能的MCP服务器实现
  • Turborepo:单体仓库构建编排
  • Biome:快速、现代的代码风格检查和格式化
  • Vitest:单元测试框架

项目结构

mcp-hooks/
├── packages/
│   ├── passthrough-mcp-server/   # 主代理服务器
│   ├── hook-common/              # 共享类型和实用程序
│   ├── audit-hook/               # 示例:日志记录挂钩
│   ├── guardrail-hook/           # 示例:安全挂钩
│   ├── simple-log-hook/          # 示例:最小挂钩
│   ├── explain-hook/             # 示例:透明挂钩
│   ├── custom-description-hook/  # 示例:转换挂钩
│   ├── rate-limit-hook/          # 示例:速率限制挂钩
│   ├── local-tools-hook/         # 示例:编程工具挂钩
│   ├── fetch-docs/               # 测试MCP服务器
│   └── whoami-server/            # 测试认证服务器
├── test/                         # 测试配置
└── docs/                         # 额外文档

挂钩接口

所有挂钩都实现了这个简单的接口:

interface Hook {
  name: string;
  processCallToolRequest?(request: CallToolRequest): Promise<ToolCallRequestHookResult>;
  processCallToolResult?(response: CallToolResult, originalCallToolRequest: CallToolRequest): Promise<ToolCallResponseHookResult>;
  processToolsList?(request: ListToolsRequest): Promise<ListToolsRequestHookResult>;
  processListToolsResult?(response: ListToolsResult, originalRequest: ListToolsRequest): Promise<ListToolsResponseHookResult>;
  processToolCallTransportError?(error: unknown, originalCallToolRequest: CallToolRequest): Promise<ToolCallTransportErrorHookResult>;
  processToolsListTransportError?(error: unknown, originalRequest: ListToolsRequest): Promise<ListToolsTransportErrorHookResult>;
}

贡献

我们欢迎贡献!请参阅我们的贡献指南以获取详细信息。

开发命令

pnpm install          # 安装依赖项
pnpm build           # 构建所有包
pnpm test            # 运行测试
pnpm lint            # 检查代码风格
pnpm dev             # 在监视模式下启动

许可证

MIT - 详情见LICENSE