Token导航 LogoToken导航TokenDH.com
mcpose (Amir Gorji) logo
AI代理未说明官方级别未说明来源级核验

mcpose (Amir Gorji)

MCP Server

mcpose是一个透明的MCP服务器中间件代理,用于拦截、转换和管理工具调用和工具发现,通过可组合的功能中间件实现。适用于需要跨领域关注点(如PII脱敏和审计日志)的MCP服务器。

工具数

0

提示词数

0

GitHub Stars

1

资源数

0
TypeScriptClaude审计日志ClaudeCursor

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

作者 / 组织

amir-gorji

提供方

amir-gorji

最后核验

2026/5/17 20:21

快速接入

先看主来源和安装命令,再打开仓库或文档;下面只保留这个条目的关键接入事实。

详细介绍

mcpose

](https://www.npmjs.com/package/mcpose) ![license](./LICENSE) ![TypeScript](https://www.typescriptlang.org/) ![CI](https://github.com/amir-gorji/mcpose/actions/workflows/deploy.yml) ![Dependabot](https://github.com/amir-gorji/mcpose/blob/main/.github/dependabot.yml)

MCP服务器的透明中间件代理——通过可组合的功能中间件拦截、转换和管理工具调用和工具发现。

如果你喜欢使用中间件功能或LEGO,你可能会喜欢它。

1.2.0中的新功能

  • onRequest 钩子——HTTP代理上的身份验证/请求门控
  • onError callback--自定义错误处理程序(替换 console.error)
  • maxBodyBytes --机身大小上限,返回413(默认4 MB)
  • maxSessions --并发会话上限,超额回报503
  • sessionTtlMs --具有自动关闭功能的会话TTL
  • createProxyContext 导出用于手动构建上下文

______________________________________________________________________

______________________________________________________________________

背景

mcpose提取自 financial-elastic-mcp-server,为需要在每次工具调用时进行PII编辑和审计日志记录的金融机构构建的Elasticsearch MCP服务器。这些跨领域的问题最初被硬编码到单个服务器中。mcpose将该模式提升到一个可重用、可组合的中间件层中,该层可以封装 任何 上游MCP服务器。

______________________________________________________________________

概念

mcpose是一个 透明代理 在LLM客户端和上游MCP服务器之间。它反映了上游MCP表面,并通过中间件路由支持的呼叫。客户端看到一个正常的MCP服务器;上游看到正常的MCP客户端。

______________________________________________________________________

安装

npm install mcpose

对等依赖 --必须单独安装:

npm install @modelcontextprotocol/sdk@>=1.0.0

______________________________________________________________________

快速开始

import { createBackendClient, startProxy } from 'mcpose';
import type { ToolMiddleware } from 'mcpose';

// 1. Connect to the upstream MCP server (stdio)
const backend = await createBackendClient({
  command: 'node',
  args: ['/path/to/backend-server.mjs'],
});

// 2. Define middleware
const loggingMW: ToolMiddleware = async (req, next) => {
  console.error(`→ ${req.params.name}`);
  const result = await next(req);
  console.error(`← ${req.params.name} done`);
  return result;
};

// 3. Start the proxy on stdio
await startProxy(backend, {
  toolMiddleware: [loggingMW],
});

______________________________________________________________________

代理模型

┌──────────────┐        ┌────────────────────────────────┐        ┌────────────────────┐
│  LLM client  │ ◄────► │  mcpose                        │ ◄────► │  Upstream MCP      │
│  (Claude,    │        │  · visibility filters          │        │  server            │
│   Cursor…)   │        │  · middleware pipelines        │        │  (stdio or HTTP)   │
└──────────────┘        └────────────────────────────────┘        └────────────────────┘

对于每个支持的工具或资源,mcpose会选择三条路由路径之一:

路径选项行为
隐藏hiddenTools / hiddenResources从列表回复中省略;呼叫时出现错误而被拒绝
通过passThroughTools / passThroughResources直接转发到上游--跳过所有中间件
中间件其他一切已全部安排 toolMiddleware / resourceMiddleware 管道

当上游支持提示时,提示会按原样转发。

代理端到端保留了核心请求语义:

  • 广告功能从上游服务器镜像
  • 中止信号被转发给上游工具、资源和提示调用
  • 上游进度更新被中继回下游客户端
  • 当上游支持列表更改通知时,这些通知会被公告并展开
  • list_tools 反应可以通过以下方式转变 listToolsMiddleware 不削弱当地 hiddenTools 保证

______________________________________________________________________

中间件模型

中间件遵循 洋葱模型:外层在之前运行代码 *和* 内层之后。每个中间件接收请求 next 调用管道其余部分的函数,以及一个规范化的 ProxyContext.

  request ──►
             ┌──────────────────────────────────────────┐
             │  outerMW  (enter)                        │
             │  ┌────────────────────────────────────┐  │
             │  │  innerMW  (enter)                  │  │
             │  │  ┌──────────────────────────────┐  │  │
             │  │  │  upstream call               │  │  │
             │  │  └──────────────────────────────┘  │  │
             │  │  innerMW  (exit) ◄── response      │  │
             │  └────────────────────────────────────┘  │
             │  outerMW  (exit) ◄── response            │
             └──────────────────────────────────────────┘
  ◄── response

数组顺序 ProxyOptions 用途 响应处理顺序:第一个元素处理响应 *第一* (最内层)。 ProxyOptions 电话 pipe() 内部——无需手动包装。为确保审计永远不会看到原始PII:

toolMiddleware: [piiMW, auditMW]
// Execution:
// 1. auditMW enter  → capture startTime         (outermost)
// 2. piiMW enter    → transform request
// 3. upstream call
// 4. piiMW exit     → redact PII from response  (processes response first)
// 5. auditMW exit   → log already-clean data    (processes response last)

compose([outerMW, innerMW]) 使用 相反的 (最外层优先)惯例-- ProxyOptions 数组是 可互换 compose() 论据。

中间件可以 短路 不打电话就回来 next,或 处理上游错误 通过包装 await next(req) 尝试/抓住。现有的双参数中间件保持不变; mcpose 补给 ProxyContext 作为可选的第三个论点。

______________________________________________________________________

API 参考

ProxyContext · Middleware · ToolMiddleware · ResourceMiddleware · ListToolsMiddleware · compose() · createProxyContext()

interface ProxyContext {
  requestId: string;
  transport: 'stdio' | 'http';
  sessionId?: string;
  headers?: Readonly>;
  signal?: AbortSignal;
}

// Builds a ProxyContext with a fresh requestId; useful in tests or custom orchestration:
function createProxyContext(overrides?: Partial
): ProxyContext;

type Middleware = (
  req: Req,
  next: (req: Req) => Promise,
  context: ProxyContext,
) => Promise;

// Convenience aliases for the three pipeline types:
type ToolMiddleware     = Middleware;
type ResourceMiddleware = Middleware;
type ListToolsMiddleware = Middleware
;

function compose(
  middlewares: ReadonlyArray>,
): {
  (req: Req, next: (req: Req) => Promise): Promise;
  (req: Req, next: (req: Req) => Promise, context: ProxyContext): Promise;
};

// Type guard — narrows CompatibilityCallToolResult to CallToolResult
// (safe access to .content and .isError without casts):
function hasToolContent(r: CompatibilityCallToolResult): r is CallToolResult;

compose 接受一个数组 最外层优先 订单。使用 hasToolContent 在访问之前的中间件实现中 .content.isError,因为 CompatibilityCallToolResult 还包括遗产 { toolResult } 形状。 ProxyContext.signal 当传输提供下行中止信号时,携带下行中止信号。

______________________________________________________________________

BackendConfig · createBackendClient()

interface BackendConfig {
  command?: string;   // Executable to spawn for stdio transport (e.g., "node")
  args?:    string[]; // Arguments for the spawned process
  url?:     string;   // HTTP endpoint of a running MCP server (takes precedence over stdio)
}

async function createBackendClient(config: BackendConfig): Promise;

BackendClient 是SDK的别名 Client如果两者都没有,它就会抛出 command 也不 url 或者如果连接失败。

______________________________________________________________________

ProxyOptions · startProxy() · createProxyServer()

interface ProxyOptions {
  toolMiddleware?:       ReadonlyArray;
  resourceMiddleware?:   ReadonlyArray;
  listToolsMiddleware?:  ReadonlyArray
;
  passThroughTools?:     ReadonlyArray;
  passThroughResources?: ReadonlyArray;
  hiddenTools?:          ReadonlyArray;
  hiddenResources?:      ReadonlyArray;
}

async function startProxy(backend: BackendClient, options?: ProxyOptions): Promise;
function createProxyServer(backend: BackendClient, options?: ProxyOptions): Server;
选项描述
toolMiddleware用于工具调用的中间件堆栈,按响应处理顺序排列(第一个元素先处理响应)。
resourceMiddleware按响应处理顺序读取资源的中间件堆栈。
listToolsMiddleware中间件堆栈 list_tools,按照响应处理顺序。本地 hiddenTools 过滤仍然在此管道之前和之后运行。
passThroughTools工具名称直接转发到上游——完全跳过了中间件。
passThroughResources资源URI直接转发到上游——完全跳过了中间件。
hiddenTools工具名称已从中删除 list_tools 在通话时被拒绝 MethodNotFound.
hiddenResources已从中删除资源URI list_resources 在通话时被拒绝 InvalidRequest.

createProxyServer 仅反映了所暴露的上游能力 backend.getServerCapabilities()。不支持的提示、资源和工具终结点不会被通告或注册。

startProxy 将代理连接到 StdioServerTransport. createProxyServer 返回已配置的 Server 无需连接,这对于在没有实时传输的情况下测试请求处理程序非常有用。

______________________________________________________________________

HttpProxyOptions · startHttpProxy()

interface HttpProxyOptions {
  port?: number;        // Default: 3000
  host?: string;        // Default: all interfaces
  path?: string;        // Default: '/mcp'
  onRequest?: (req: http.IncomingMessage, res: http.ServerResponse) => boolean | Promise;
  onError?: (err: unknown) => void;
  maxBodyBytes?: number; // Default: 4 MB — returns 413 on excess
  maxSessions?: number;  // Excess requests return 503
  sessionTtlMs?: number; // Sessions auto-close after this duration
}

function startHttpProxy(
  backend: BackendClient,
  options?: ProxyOptions,
  httpOptions?: HttpProxyOptions,
): Promise;

使用有状态会话通过Streamable HTTP启动代理。为每个客户端连接分配一个 mcp-session-id上游列表更改通知(tools/list_changed, resources/list_changed, prompts/list_changed)当上游发布广告时,这些会话被分散到所有活动会话。

import { createBackendClient, startHttpProxy } from 'mcpose';

const backend = await createBackendClient({ url: 'http://upstream-mcp-server/mcp' });
const server = await startHttpProxy(backend, { toolMiddleware: [loggingMW] }, { port: 8080 });
// HTTP server is now listening on port 8080 at /mcp

关闭时,活动代理会话在底层服务器之前关闭 http.Server 结束关闭。

限制:

  • 不支持SSE重新连接重播(否 EventStore).

______________________________________________________________________

mcpose/testing

import { createMockBackendClient, runToolMiddleware } from 'mcpose/testing';

createMockBackendClient() 返回一个带有功能查找和通知挂钩的内存后端存根。它对两者都有效 createProxyServer()startHttpProxy() 测验。

______________________________________________________________________

配方:list_tools重写

使用 listToolsMiddleware 当您想在不更改本地路由保证的情况下重写可见工具目录时:

import type { ListToolsMiddleware } from 'mcpose';

const enrichDescriptions: ListToolsMiddleware = async (req, next, context) => {
  const result = await next(req);
  return {
    ...result,
    tools: result.tools.map((tool) =>
      tool.name === 'wire_transfer'
        ? {
            ...tool,
            description: `${tool.description ?? 'Wire transfer'} (approval required on ${context.transport})`,
          }
        : tool,
    ),
  };
};

hiddenTools 即使有 listToolsMiddleware 尝试将隐藏的工具添加回响应中。

______________________________________________________________________

配方:PII编辑

mcpose的原始用例:一个金融级MCP服务器,其中每个Elasticsearch工具响应在到达LLM或审计日志之前都必须清除PII。

使用工厂来保持中间件的可配置性和可测试性:

import { hasToolContent } from 'mcpose';
import type { ToolMiddleware } from 'mcpose';

function createPiiMiddleware(patterns: RegExp[]): ToolMiddleware {
  return async (req, next) => {
    const result = await next(req);
    if (!hasToolContent(result)) return result;
    return {
      ...result,
      content: result.content.map((item) =>
        item.type === 'text'
          ? { ...item, text: redactPii(item.text, patterns) }
          : item,
      ),
    };
  };
}

function redactPii(text: string, patterns: RegExp[]): string {
  return patterns.reduce((t, re) => t.replace(re, '[REDACTED]'), text);
}

将其与审计中间件堆叠在一起——PII在数组中位于首位,因此审计始终可以看到干净的数据:

await startProxy(backend, {
  toolMiddleware: [
    createPiiMiddleware([/\b\d{9}\b/g, /[A-Z]{2}\d{6}/g]), // SSNs, account numbers
    createAuditMiddleware({ destination: auditLog }),
  ],
});

数组顺序保证:PII被编辑 *之前* 审计层永远不会看到响应。没有原始PII到达日志,满足金融监管要求。

参考实施: elastic-pii-proxy 是这种模式的一个生产示例——一个Elasticsearch MCP代理,它使用mcpose、PII编辑中间件和审计中间件将财务数据安全地提供给LLM代理。

______________________________________________________________________

路线图

  • \[x\] HTTP/SSE服务器传输startHttpProxy() 添加具有有状态会话的可流化HTTP服务器端传输
  • \[ \] ATXP协议支持 --通过实施ATXP(代理交易协议)标准,让工具提供商将定价和计费元数据附加到响应中,实现MCP货币化

______________________________________________________________________

许可证

麻省理工学院

目录标签

目录标签

TypeScriptClaude审计日志中间件代理本地部署MCP服务器工具调用管理PII脱敏

支持客户端

ClaudeCursor

接入字段

传输方式(transport,传输协议)

未说明

鉴权方式(authType,认证方式)

session

工具数量(toolCount,工具数)

0

资源数量(resourceCount,资源数)

0

提示词数量(promptCount,提示词数)

0

权限和风险

未说明session部署方式未说明

接入前请确认传输方式、认证方式和部署位置,并根据实际工具能力限制访问范围。

安装前确认

不要直接授予不必要的文件、网络或账号权限;先核对安装命令和配置内容。

仍需确认:installCommand

来源信息

继续浏览同类 MCP