十、MCP 桥接模式
mcporter serve 将守护进程管理的 keep-alive 服务器重新暴露为一个统一的 MCP 服务器,让不支持 HTTP 的客户端也能使用。
10.1 stdio 桥接
bash
mcporter serve --stdio
适用于 Claude Code 、Codex 等仅支持 stdio 的客户端。所有 keep-alive 服务器的工具以 server__tool 的命名空间形式暴露。
10.2 HTTP 桥接
bash
mcporter serve --http 3000
| 端点 | 说明 |
|---|---|
http://127.0.0.1:3000/mcp |
聚合端点,工具名:chrome-devtools__list_pages |
http://127.0.0.1:3000/mcp/<server> |
单服务器端点,保留原始工具名 |
10.3 桥接架构
#mermaid-svg-D6fFgHhukuLI5huJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-D6fFgHhukuLI5huJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-D6fFgHhukuLI5huJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-D6fFgHhukuLI5huJ .error-icon{fill:#552222;}#mermaid-svg-D6fFgHhukuLI5huJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-D6fFgHhukuLI5huJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-D6fFgHhukuLI5huJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-D6fFgHhukuLI5huJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-D6fFgHhukuLI5huJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-D6fFgHhukuLI5huJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-D6fFgHhukuLI5huJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-D6fFgHhukuLI5huJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-D6fFgHhukuLI5huJ .marker.cross{stroke:#333333;}#mermaid-svg-D6fFgHhukuLI5huJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-D6fFgHhukuLI5huJ p{margin:0;}#mermaid-svg-D6fFgHhukuLI5huJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-D6fFgHhukuLI5huJ .cluster-label text{fill:#333;}#mermaid-svg-D6fFgHhukuLI5huJ .cluster-label span{color:#333;}#mermaid-svg-D6fFgHhukuLI5huJ .cluster-label span p{background-color:transparent;}#mermaid-svg-D6fFgHhukuLI5huJ .label text,#mermaid-svg-D6fFgHhukuLI5huJ span{fill:#333;color:#333;}#mermaid-svg-D6fFgHhukuLI5huJ .node rect,#mermaid-svg-D6fFgHhukuLI5huJ .node circle,#mermaid-svg-D6fFgHhukuLI5huJ .node ellipse,#mermaid-svg-D6fFgHhukuLI5huJ .node polygon,#mermaid-svg-D6fFgHhukuLI5huJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-D6fFgHhukuLI5huJ .rough-node .label text,#mermaid-svg-D6fFgHhukuLI5huJ .node .label text,#mermaid-svg-D6fFgHhukuLI5huJ .image-shape .label,#mermaid-svg-D6fFgHhukuLI5huJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-D6fFgHhukuLI5huJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-D6fFgHhukuLI5huJ .rough-node .label,#mermaid-svg-D6fFgHhukuLI5huJ .node .label,#mermaid-svg-D6fFgHhukuLI5huJ .image-shape .label,#mermaid-svg-D6fFgHhukuLI5huJ .icon-shape .label{text-align:center;}#mermaid-svg-D6fFgHhukuLI5huJ .node.clickable{cursor:pointer;}#mermaid-svg-D6fFgHhukuLI5huJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-D6fFgHhukuLI5huJ .arrowheadPath{fill:#333333;}#mermaid-svg-D6fFgHhukuLI5huJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-D6fFgHhukuLI5huJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-D6fFgHhukuLI5huJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-D6fFgHhukuLI5huJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-D6fFgHhukuLI5huJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-D6fFgHhukuLI5huJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-D6fFgHhukuLI5huJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-D6fFgHhukuLI5huJ .cluster text{fill:#333;}#mermaid-svg-D6fFgHhukuLI5huJ .cluster span{color:#333;}#mermaid-svg-D6fFgHhukuLI5huJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-D6fFgHhukuLI5huJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-D6fFgHhukuLI5huJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-D6fFgHhukuLI5huJ .icon-shape,#mermaid-svg-D6fFgHhukuLI5huJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-D6fFgHhukuLI5huJ .icon-shape p,#mermaid-svg-D6fFgHhukuLI5huJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-D6fFgHhukuLI5huJ .icon-shape .label rect,#mermaid-svg-D6fFgHhukuLI5huJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-D6fFgHhukuLI5huJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-D6fFgHhukuLI5huJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-D6fFgHhukuLI5huJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🌉 mcporter serve
🖥️ MCP 客户端
👻 Keep-Alive 守护进程
Chrome DevTools
Playwright
Linear API
Claude Code
(stdio)
自定义 Agent
(HTTP)
--stdio
--http 3000
10.4 仅暴露指定服务器
bash
mcporter serve --http 3000 --servers chrome-devtools,playwright
10.5 Chrome DevTools 扩展中继
当使用 chrome-devtools-mcp --autoConnect 时,Chrome 的远程调试握手可能每次会话都弹出 "Allow remote debugging?" 对话框。如果同一主机上配对了 OpenClaw Chrome 扩展中继,mcporter 可以使用扩展支持的 Chrome 控制路径代替。
配置选项:
| 策略 | 行为 |
|---|---|
prefer(默认) |
优先使用 v2 扩展中继,失败时回退到原始 Chrome --autoConnect |
require |
强制使用 v2 扩展中继,任何失败都直接报错 |
off |
禁用中继探测,使用原始 auto-connect |
jsonc
{
"mcpServers": {
"chrome-devtools": {
"command": "npx",
"args": ["-y", "chrome-devtools-mcp@latest", "--autoConnect"],
"lifecycle": "keep-alive",
"chromeDevtoolsRelay": "require"
}
}
}
十一、代理技能模式
11.1 核心理念
🎯 推荐为每个 MCP 服务器或工作流编写一个小型技能文件,而非一个通用技能。
一个通用技能必须教会代理如何发现、选择和调用每个配置的服务器------这重现了 mcporter 试图避免的大型 Schema 上下文问题。per-server 技能保持精简,让技能作者描述该服务器的安全、有用的工作流。
11.2 推荐工作流
#mermaid-svg-nSsaJwq6uWwVSht1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nSsaJwq6uWwVSht1 .error-icon{fill:#552222;}#mermaid-svg-nSsaJwq6uWwVSht1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nSsaJwq6uWwVSht1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nSsaJwq6uWwVSht1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nSsaJwq6uWwVSht1 .marker.cross{stroke:#333333;}#mermaid-svg-nSsaJwq6uWwVSht1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nSsaJwq6uWwVSht1 p{margin:0;}#mermaid-svg-nSsaJwq6uWwVSht1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nSsaJwq6uWwVSht1 .cluster-label text{fill:#333;}#mermaid-svg-nSsaJwq6uWwVSht1 .cluster-label span{color:#333;}#mermaid-svg-nSsaJwq6uWwVSht1 .cluster-label span p{background-color:transparent;}#mermaid-svg-nSsaJwq6uWwVSht1 .label text,#mermaid-svg-nSsaJwq6uWwVSht1 span{fill:#333;color:#333;}#mermaid-svg-nSsaJwq6uWwVSht1 .node rect,#mermaid-svg-nSsaJwq6uWwVSht1 .node circle,#mermaid-svg-nSsaJwq6uWwVSht1 .node ellipse,#mermaid-svg-nSsaJwq6uWwVSht1 .node polygon,#mermaid-svg-nSsaJwq6uWwVSht1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nSsaJwq6uWwVSht1 .rough-node .label text,#mermaid-svg-nSsaJwq6uWwVSht1 .node .label text,#mermaid-svg-nSsaJwq6uWwVSht1 .image-shape .label,#mermaid-svg-nSsaJwq6uWwVSht1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-nSsaJwq6uWwVSht1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nSsaJwq6uWwVSht1 .rough-node .label,#mermaid-svg-nSsaJwq6uWwVSht1 .node .label,#mermaid-svg-nSsaJwq6uWwVSht1 .image-shape .label,#mermaid-svg-nSsaJwq6uWwVSht1 .icon-shape .label{text-align:center;}#mermaid-svg-nSsaJwq6uWwVSht1 .node.clickable{cursor:pointer;}#mermaid-svg-nSsaJwq6uWwVSht1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nSsaJwq6uWwVSht1 .arrowheadPath{fill:#333333;}#mermaid-svg-nSsaJwq6uWwVSht1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nSsaJwq6uWwVSht1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nSsaJwq6uWwVSht1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nSsaJwq6uWwVSht1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nSsaJwq6uWwVSht1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nSsaJwq6uWwVSht1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nSsaJwq6uWwVSht1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nSsaJwq6uWwVSht1 .cluster text{fill:#333;}#mermaid-svg-nSsaJwq6uWwVSht1 .cluster span{color:#333;}#mermaid-svg-nSsaJwq6uWwVSht1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nSsaJwq6uWwVSht1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nSsaJwq6uWwVSht1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-nSsaJwq6uWwVSht1 .icon-shape,#mermaid-svg-nSsaJwq6uWwVSht1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nSsaJwq6uWwVSht1 .icon-shape p,#mermaid-svg-nSsaJwq6uWwVSht1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nSsaJwq6uWwVSht1 .icon-shape .label rect,#mermaid-svg-nSsaJwq6uWwVSht1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nSsaJwq6uWwVSht1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nSsaJwq6uWwVSht1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nSsaJwq6uWwVSht1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 添加/导入 MCP 服务器
2. 检查工具表面
3. 编写小型技能文件
4. 调用相关工具
5. 需要分享?生成专用 CLI
11.3 技能文件示例
markdown
---
name: docs-mcp
description: Fetch package and framework docs through the configured docs MCP server.
---
# 📚 Docs MCP
Use `npx mcporter call docs.resolve-library-id query=<task> libraryName=<name>` to resolve a package,
then call `npx mcporter call docs.query-docs ...` with the resolved ID and docs query.
11.4 完整步骤示例
bash
# 1. 添加 MCP 服务器
npx mcporter config add docs https://mcp.context7.com/mcp --scope home
# 2. 检查工具表面
npx mcporter list docs --brief
npx mcporter list docs --schema
# 3. 编写技能文件(见上方示例)
# 4. 在 Agent 中使用技能文件
# 5. 对于可分享工作流,生成专用 CLI
npx mcporter generate-cli docs --bundle dist/docs-mcp.js
11.5 工具过滤
使用 allowedTools 或 blockedTools 限制暴露给代理的工具:
jsonc
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"allowedTools": ["search_repositories", "get_file_contents"]
}
}
}
十二、TypeScript 运行时 API
12.1 创建服务器代理(推荐)
createServerProxy() 将 MCP 工具名映射为 camelCase 方法,自动应用 JSON Schema 默认值,验证必填参数,返回带有 .text()、.markdown()、.json()、.images()、.content() 辅助方法的 CallResult。
typescript
import { createServerProxy } from "mcporter";
const proxy = await createServerProxy("linear");
const result = await proxy.createIssue({
title: "Bug report",
team: "ENG",
});
// 多种输出格式
console.log(result.text()); // 纯文本
console.log(result.markdown()); // Markdown
console.log(result.json()); // JSON
console.log(result.images()); // 图片数组
console.log(result.content()); // 原始内容
12.2 一次性调用
callOnce() 处理单次调用和清理,适合脚本场景:
typescript
import { callOnce } from "mcporter";
const result = await callOnce("linear", "createIssue", {
title: "Bug report",
team: "ENG",
});
console.log(result.text());
12.3 底层运行时
createRuntime() 当你需要显式控制服务器定义、连接复用或多个调用时:
typescript
import { createRuntime } from "mcporter";
const runtime = await createRuntime({
servers: [{
name: "context7",
command: {
kind: "http",
url: new URL("https://mcp.context7.com/mcp")
}
}],
});
try {
const tools = await runtime.listTools("context7");
console.log(tools.map((tool) => tool.name));
} finally {
await runtime.close();
}
12.4 API 对比
| API | 适用场景 | 连接管理 |
|---|---|---|
createServerProxy() |
大多数场景,类型友好的方法调用 | 自动管理 |
callOnce() |
单次调用脚本 | 自动创建和关闭 |
createRuntime() |
复杂场景,需要精细控制 | 手动 runtime.close() |
十三、日志与诊断
13.1 守护进程日志
bash
# 启动守护进程并记录日志
mcporter daemon start --log
# 指定日志文件
mcporter daemon start --log --log-file /path/to/log
# 限制日志范围到特定服务器
mcporter daemon start --log --log-servers chrome-devtools,playwright
13.2 环境变量日志级别
bash
# 查看结构化连接决策日志
MCPORTER_LOG_LEVEL=info mcporter list
13.3 调试挂起调用
当 mcporter call 打印了工具响应但进程永不退出时,通常是 Node 仍有活跃句柄在等待(最常见的是子进程 MCP 服务器保持 stdio 传输存活)。
快速检查清单:
- 在 tmux 中运行 --- 启动命令在 tmux 中,这样即使 Cursor 或其他代理超时,你也能检查面板内容
- 启用挂起诊断 --- 设置
MCPORTER_DEBUG_HANG=1 - 检查句柄列表 --- 查找
ChildProcess (pid=...)条目 - 捕获面板输出 ---
tmux capture-pane -p -t <session> -S -200 - 使用
--timeout--- 如果工具本身挂起,设置超时快速失败 - 限制 OAuth 等待 ---
--oauth-timeout <ms>或MCPORTER_OAUTH_TIMEOUT_MS
示例诊断会话:
bash
tmux new-session -d -s mcphang 'cd /path/to/project && MCPORTER_DEBUG_HANG=1 mcporter call chrome-devtools list_pages'
sleep 5
tmux capture-pane -p -t mcphang -S -200
典型诊断输出:
text
[mcporter] [debug] after call (object result): 6 active handle(s), 0 request(s)
[mcporter] [debug] handle => ChildProcess (pid=78480)
[mcporter] [debug] beginning runtime.close()
[mcporter] [debug] after runtime.close: 6 active handle(s), 0 request(s)
[mcporter] [debug] forcibly killed child pid=78480 (runtime.finally)
💡 CLI 默认在清理后强制
process.exit(0),防止 Node 在泄漏句柄上挂起。调试时设置MCPORTER_NO_FORCE_EXIT=1保持进程存活。
13.4 Stdio 服务器日志
Stdio 服务器的 stderr 输出默认被抑制。设置以下环境变量查看:
bash
MCPORTER_STDIO_LOGS=1 mcporter call <server>.<tool>
当子进程以非零状态退出时,其日志也会自动显示。
十四、最佳实践
14.1 项目 vs 机器层配置
#mermaid-svg-OfQkGLRrN2xRuTgs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OfQkGLRrN2xRuTgs .error-icon{fill:#552222;}#mermaid-svg-OfQkGLRrN2xRuTgs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OfQkGLRrN2xRuTgs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OfQkGLRrN2xRuTgs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OfQkGLRrN2xRuTgs .marker.cross{stroke:#333333;}#mermaid-svg-OfQkGLRrN2xRuTgs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OfQkGLRrN2xRuTgs p{margin:0;}#mermaid-svg-OfQkGLRrN2xRuTgs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OfQkGLRrN2xRuTgs .cluster-label text{fill:#333;}#mermaid-svg-OfQkGLRrN2xRuTgs .cluster-label span{color:#333;}#mermaid-svg-OfQkGLRrN2xRuTgs .cluster-label span p{background-color:transparent;}#mermaid-svg-OfQkGLRrN2xRuTgs .label text,#mermaid-svg-OfQkGLRrN2xRuTgs span{fill:#333;color:#333;}#mermaid-svg-OfQkGLRrN2xRuTgs .node rect,#mermaid-svg-OfQkGLRrN2xRuTgs .node circle,#mermaid-svg-OfQkGLRrN2xRuTgs .node ellipse,#mermaid-svg-OfQkGLRrN2xRuTgs .node polygon,#mermaid-svg-OfQkGLRrN2xRuTgs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OfQkGLRrN2xRuTgs .rough-node .label text,#mermaid-svg-OfQkGLRrN2xRuTgs .node .label text,#mermaid-svg-OfQkGLRrN2xRuTgs .image-shape .label,#mermaid-svg-OfQkGLRrN2xRuTgs .icon-shape .label{text-anchor:middle;}#mermaid-svg-OfQkGLRrN2xRuTgs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OfQkGLRrN2xRuTgs .rough-node .label,#mermaid-svg-OfQkGLRrN2xRuTgs .node .label,#mermaid-svg-OfQkGLRrN2xRuTgs .image-shape .label,#mermaid-svg-OfQkGLRrN2xRuTgs .icon-shape .label{text-align:center;}#mermaid-svg-OfQkGLRrN2xRuTgs .node.clickable{cursor:pointer;}#mermaid-svg-OfQkGLRrN2xRuTgs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OfQkGLRrN2xRuTgs .arrowheadPath{fill:#333333;}#mermaid-svg-OfQkGLRrN2xRuTgs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OfQkGLRrN2xRuTgs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OfQkGLRrN2xRuTgs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OfQkGLRrN2xRuTgs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OfQkGLRrN2xRuTgs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OfQkGLRrN2xRuTgs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OfQkGLRrN2xRuTgs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OfQkGLRrN2xRuTgs .cluster text{fill:#333;}#mermaid-svg-OfQkGLRrN2xRuTgs .cluster span{color:#333;}#mermaid-svg-OfQkGLRrN2xRuTgs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OfQkGLRrN2xRuTgs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OfQkGLRrN2xRuTgs rect.text{fill:none;stroke-width:0;}#mermaid-svg-OfQkGLRrN2xRuTgs .icon-shape,#mermaid-svg-OfQkGLRrN2xRuTgs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OfQkGLRrN2xRuTgs .icon-shape p,#mermaid-svg-OfQkGLRrN2xRuTgs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OfQkGLRrN2xRuTgs .icon-shape .label rect,#mermaid-svg-OfQkGLRrN2xRuTgs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OfQkGLRrN2xRuTgs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OfQkGLRrN2xRuTgs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OfQkGLRrN2xRuTgs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🔄 CI/CD
📁 项目层(纳入版本控制)
config/mcporter.json
schema 引用
{LINEAR_API_KEY}
环境变量占位符
🖥️ 机器层(不纳入版本控制)
~/.mcporter/mcporter.json
OAuth 令牌
~/.mcporter/credentials.json
~/.mcporter/local.json
机器特定配置
--config config/mcporter.json
14.2 配置管理清单
| 实践 | 说明 |
|---|---|
| ✅ 版本控制 | 将 config/mcporter.json 纳入版本控制 |
| ✅ 敏感数据 | 使用环境变量(${LINEAR_API_KEY})而非内联密钥 |
| ✅ 预注册 OAuth | 配置中存储公开的 oauthClientId,使用 oauthClientSecretEnv 指向本地环境变量 |
| ✅ 机器特定配置 | 放在 ~/.mcporter/local.json,使用 --config 指定 |
| ✅ CI 环境 | 始终传递 --config config/mcporter.json 确保确定性行为 |
| ❌ 不提交令牌 | OAuth 令牌保留在 ~/.mcporter/... 或 XDG_*_HOME/mcporter/...,绝不纳入仓库 |
14.3 安全建议
- 非 HTTPS 端点 :仅在内网使用
--allow-http,生产环境始终使用 HTTPS - Stdio 命令确认:首次遇到新 stdio 命令时,mcporter 会打印确认摘要,仔细审查
- 工具白名单 :对于暴露敏感操作的服务器,使用
allowedTools限制可用工具 - 令牌刷新 :使用
refreshable_bearer自动管理令牌生命周期,避免手动更新
十五、故障排除
15.1 常见问题速查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 🔴 环境占位符缺失 | 配置中使用了 ${VAR} 但环境变量未设置 |
添加环境变量,或使用 ${VAR:-fallback} 提供默认值 |
| 🔴 配置来源不明 | 不确定服务器从哪个配置文件加载 | mcporter config get <name> 显示服务器来源和完整路径 |
| 🔴 导入冲突 | 多个编辑器配置了同名服务器 | 重新排序 imports 数组,或设为 [] 禁用所有导入 |
| 🔴 OAuth 问题 | 浏览器流程失败或无头环境 | mcporter auth <name> --no-browser 在无头环境完成认证 |
| 🔴 守护进程问题 | 连接状态异常 | mcporter daemon status 查看状态,mcporter daemon restart 重启 |
| 🔴 调用挂起 | 子进程未正确退出 | 使用 --timeout,或 MCPORTER_DEBUG_HANG=1 诊断 |
| 🔴 EADDRINUSE | 端口 localhost:3334 被占用 |
检查是否有其他 mcporter 实例在运行 |
15.2 诊断命令速查
bash
# 查看 mcporter 版本
mcporter --version
# 查看服务器来源和配置
mcporter config get <server-name>
# 查看守护进程状态
mcporter daemon status
# 启用挂起诊断
MCPORTER_DEBUG_HANG=1 mcporter call <server>.<tool>
# 查看 stdio 服务器日志
MCPORTER_STDIO_LOGS=1 mcporter call <server>.<tool>
# 结构化日志输出
MCPORTER_LOG_LEVEL=info mcporter list
# 验证配置
mcporter config doctor