mcpField
P
phi-guard-mcp

phi-guard-mcp

本地服务
github.com安全06 周前更新

关于 phi-guard-mcp

phi-guard-mcp 是一个本地运行的 MCP 服务,用于在代码发布前发现 PHI 流向 LLM 提示词、日志和分析调用。它提供文本脱敏建议与基于同一行规则的代码扫描,并默认遮蔽匹配值。

主要功能

  1. 01检测并遮蔽文本中的 PHI
  2. 02扫描代码中的敏感标识符与风险调用
  3. 03默认不在结果中返回匹配值
  4. 04支持 TypeScript、JavaScript、Python 和 Go

适用场景

  1. 01检查提示词或日志片段中的 PHI
  2. 02发布前扫描代码中的潜在泄漏
  3. 03在不暴露匹配值的情况下审阅扫描结果

MCP 相关

作为本地 stdio MCP 服务向客户端提供 redact_suggest 和 scan_code 两个工具;仅声明 tools 能力,不提供资源或提示词。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"abidit-phi-guard": {
"command": "npx",
"args": [
"-y",
"phi-guard-mcp"
]
}
}
}
启动命令npx -y phi-guard-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmphi-guard-mcp0.2.0

工具

2

收录的工具列表 · 更新于 2 周前

参数类型必填说明
textstring是可能包含 PHI 的原始文本
includeMatchedValuesboolean否在结果中回显匹配到的原始 PHI 值和原始文本。默认关闭:结果会流入调用模型的上下文。

详细介绍

phi-guard-mcp

npm version npm downloads License: MIT

一款本地优先的 MCP 服务,可在 PHI(受保护的健康信息)流入大模型提示词、日志语句和分析调用时,在代码发布前将其拦截。

服务本身不会发起网络调用:它作为本地 stdio 进程运行,绝不会上传你的代码。但它返回的内容则另当别论。发现结果会返回给启动它的 MCP 客户端,因此,如果该客户端是托管助手,这些结果就会进入该模型的上下文。正因如此,默认情况下结果会屏蔽匹配到的 PHI 值——参见安全边界。

为什么要用

在医疗保健代码库中,风险最高的时刻很少发生在数据库里,而是在某一行代码中:患者记录被插入提示词、console.log 或分析事件。这些代码在审查时看起来无害,也不会出现在基础设施扫描中,因为并没有任何配置错误——代码只是在照其所写的方式运行。

演示

scan_code 标记出传入 openai.responses.create 调用中的患者姓名和诊断信息,随后 redact_suggest 返回经过屏蔽的结果

演示过程中的 11 秒:scan_code 标记了第 15 行——患者姓名和诊断信息被插入 openai.responses.create 调用中;redact_suggest 则会屏蔽原始提示词中的相同值。所有显示的值均为合成数据。

用于检查一类错误的 Linter。它不是 HIPAA 认证,不是合规证明,也不是数据流分析器——参见 本工具不是什么。

安装

需要 Node.js 22 或更高版本。入口点使用 JSON 导入属性 (with { type: "json" }),因此较旧的运行时无法启动。已在 Node 22.18.0、24.2.0 和 26.8.2 上验证;不支持且未经测试的版本为 Node 20 及更早版本。

从 npm 安装(推荐)

无需克隆或构建。MCP 客户端会按需运行它:

npx -y phi-guard-mcp --version
从源代码安装
git clone https://github.com/Abidit/phi-guard-mcp.git
cd phi-guard-mcp
npm ci

npm ci 会运行 prepare 脚本来构建 dist/。无需担心遗漏单独的构建步骤。npm install 也可以;npm ci 更具可复现性,因为它会严格安装 package-lock.json 锁定的依赖项。

MCP 配置

复制并粘贴以下任一配置。npm 配置无需路径,也最适合提供给他人。

Claude Code

一条命令,项目范围:

claude mcp add phi-guard -- npx -y phi-guard-mcp

或者在项目根目录提交一个 .mcp.json:

{
  "mcpServers": {
    "phi-guard": {
      "command": "npx",
      "args": ["-y", "phi-guard-mcp"]
    }
  }
}
Claude Desktop

macOS 上的 ~/Library/Application Support/Claude/claude_desktop_config.json, Windows 上的 %APPDATA%\Claude\claude_desktop_config.json:

{
  "mcpServers": {
    "phi-guard": {
      "command": "npx",
      "args": ["-y", "phi-guard-mcp"]
    }
  }
}
Cursor

项目中的 .cursor/mcp.json,或全局配置 ~/.cursor/mcp.json:

{
  "mcpServers": {
    "phi-guard": {
      "command": "npx",
      "args": ["-y", "phi-guard-mcp"]
    }
  }
}
运行本地克隆,而不是 npm 发布版本

将客户端指向构建输出。除非确定客户端会以项目根目录作为工作目录启动服务,否则请使用绝对路径:

{
  "mcpServers": {
    "phi-guard": {
      "command": "node",
      "args": ["/absolute/path/to/phi-guard-mcp/dist/index.js"]
    }
  }
}

本仓库提交的 .mcp.json 使用相对路径(dist/index.js),这样仓库在执行 npm ci 后就可以使用自己的服务。

无论使用哪种配置,都要重启客户端,或者在 Claude Code 中运行 /mcp 并重新连接 phi-guard。仅重新构建无法应用到已经运行的 stdio 进程。

工具

redact_suggest

接收一段原始文本片段——日志行、提示词或错误消息——检测符合 PHI 特征的值,并返回脱敏后的文本以及检测结果。

输入

{ "text": "Patient John Doe (MRN-12345), DOB: 01/01/1980" }

输出

{
  "redacted": "Patient [NAME] ([MRN]), [DOB]",
  "detected": [
    { "type": "mrn",  "confidence": 0.9,  "start": 18, "end": 27 },
    { "type": "dob",  "confidence": 0.85, "start": 30, "end": 45 },
    { "type": "name", "confidence": 0.8,  "start": 8,  "end": 16 }
  ]
}

默认情况下,不会回显匹配到的值,也不会回显未脱敏的 original。工具结果会直接进入调用它的模型的上下文,因此在其中重复原始 PHI 就违背了该工具的初衷。start/end 是原始文本中的偏移量,无需重新陈述匹配内容即可定位匹配项。

detected 按模式排序,而不是按位置排序。

只有在确实需要原始值时(例如使用本地 CLI 或测试框架),才传入 includeMatchedValues: true,此时会返回 detected[].value 和 original:

{ "text": "Patient John Doe (MRN-12345)", "includeMatchedValues": true }

模式及其置信度:

类型置信度匹配示例
ssn0.95123-45-6789
mrn0.90MRN-12345、MRN: 12345
dob0.85DOB: 01/01/1980、born 3/14/75
name0.80Patient John Doe(捕获 John Doe)
phone0.75555-867-5309、(555) 867 5309
email0.70[email protected]

上表列出了所有模式。模式的范围一开始刻意设置得较窄——误报导致人们学会忽略工具,比漏掉匹配更糟。

scan_code

遍历目录,并标记敏感特征标识符与高风险接收点出现在同一行的情况。

敏感标识符——完整列表:

patient、diagnosis、dob、ssn、mrn、birthdate、medicalrecord

匹配时不区分大小写,并要求标识符边界为非字母字符,因此 patient_name 会匹配,而 outpatient 和 inpatient 不会。

接收点类别——完整列表:

类别匹配项
大模型提供商openai、anthropic、bedrock
控制台console.log、console.error、console.warn
日志记录器logger.、winston、pino
分析.track(
错误报告capture(、captureException(、captureMessage(

扫描的语言——完整列表:

扩展名语言
.ts、.tsxTypeScript
.js、.jsxJavaScript
.pyPython
.goGo

检测完全基于词法,因此语言支持表示“会读取这些文件扩展名”。这些语言都不会进行解析,也不会获取类型信息。

跳过 node_modules、dist、build、coverage、out、.next、.turbo,以及任何点文件或点目录。整行的 // 和 # 注释会被跳过,因此仅在文件说明文档中讨论 PHI 处理,不会触发扫描器。

以下是 test/fixtures/leaky-example.ts 中的有效代码行:

const prompt = await openai.responses.create({ input: `Patient: ${patient.name}, diagnosis: ${patient.diagnosis}` });
console.log("Sending patient prompt to LLM:", prompt);

输入

{ "path": "/abs/path/to/repo/test/fixtures" }

输出——节选。该目录总共返回 9 条发现结果:此文件中有 2 条,评估部分所述的正向样例中有 7 条。

[
  {
    "file": "/abs/path/to/repo/test/fixtures/leaky-example.ts",
    "line": 7,
    "severity": "high",
    "issue": "Sensitive-looking identifier passed to a risky sink (LLM call, logger, or analytics)",
    "snippet": "const prompt = await openai.responses.create({ input: `Patient: ${patient.name}, diagnosis: ${patient.diagnosis}` });"
  },
  {
    "file": "/abs/path/to/repo/test/fixtures/leaky-example.ts",
    "line": 8,
    "severity": "high",
    "issue": "Sensitive-looking identifier passed to a risky sink (LLM call, logger, or analytics)",
    "snippet": "console.log(\"Sending patient prompt to LLM:\", prompt);"
  }
]

file 由 path 与条目名称拼接而成,因此返回的形式与传入的形式一致:传入绝对路径,返回的也是绝对路径。severity 始终为 "high"——这里只有一条规则,因此只有一种严重级别。

snippet 是违规行,其中任何字面 PHI 都已屏蔽,原因与 redact_suggest 隐去匹配值相同:发现结果会进入模型的上下文。patient.diagnosis 这类标识符名称不是字面值,不匹配任何 PHI 模式,因此会保持可见——它们正是可采取行动的部分。

空数组表示已成功读取在 path 下发现的所有符合条件的源文件,且没有任何一行同时满足这两个条件。“符合条件”和“发现”都很重要:扩展名不受支持的文件,以及位于跳过目录或点目录下的任何内容,都不会被打开。

如果无法读取目录或文件,扫描会以带有名称的错误失败,而不是返回较短的列表,因为不完整的结果看起来就像没有问题:

No such path: "/nope". Pass an absolute path to a directory that exists on the
machine running this server. (ENOENT: no such file or directory, stat '/nope')
同行规则

两个条件必须同时出现在同一物理行上。无论从哪个方向理解,这都是理解扫描器最重要的一点。

正是这一点让扫描器保持安静。在本仓库自己的 src/ 中,模式定义里密集出现 patient、diagnosis、mrn 和 ssn 等词,但扫描器报告的发现数为 0。

这也是扫描器会漏掉问题的主要原因。下面这段代码会泄露数据,但不会被标记:

const value = patient.diagnosis;   // sensitive identifier, no sink
console.log("audit", value);       // sink, no sensitive identifier

扫描器没有数据流分析、变量跟踪或跨行关联功能。可在 test/fixtures/known-limitations/split-across-lines.ts 中查看一个完整示例;测试套件每次运行都会扫描该文件,让这个缺陷始终清晰可见,不会被遗忘。

评估

npm run verify

只需运行一条命令:先进行类型检查(在严格模式下检查 src/ 和 test/),再构建,最后通过 stdio 使用真实服务运行 fixture 套件。

当前结果:

检测到全部 7 个代表性泄露 fixture,且未标记 5 个干净 fixture 中的任何一个。

所有 fixture 都位于 test/fixtures/,你可以亲自查看,而不必盲目相信这个结果。

这 7 个泄露 fixture 覆盖 5 类接收端(OpenAI、Anthropic、Sentry、Winston、analytics)和 2 种语言(TypeScript、Python)。其中包括 snake_case 标识符(patient_name、patient_diagnosis),朴素的单词边界正则表达式会漏掉这类标识符,而它们是 Python 和 Go 中最常见的命名约定;还包括一个硬编码字面量 fixture,用于验证 scan_code 会在返回的 snippet 中屏蔽字面量 PHI。

这 5 个干净 fixture 包含以下代码:在注释和说明文字中讨论 PHI 政策,但从不泄露相关信息;以及合理处理患者记录、却不会将其发送到任何有风险位置的代码。

套件还会断言:

  • README 中的完整示例仍会准确产生 2 条发现,分别位于第 7 行和第 8 行
  • scan_code 结果中不会出现原始字面量 PHI
  • redact_suggest 默认会省略每个匹配值和原始内容
  • includeMatchedValues: true 确实会返回这些值
  • 路径不存在以及传入文件而非目录时,都会返回带名称的错误

这个数字并不代表什么。 这是一套包含 12 个手写文件的 fixture 测试套件,由本项目编写,用于测试自身规则。它不是检测率,不是基准测试,不是假阳性率,也不是外部验证。它无法说明扫描器在真实医疗健康代码库中的表现;在那里,漏报率尚不明确,而且鉴于上文的同行规则,肯定不为零。要对此进行衡量,需要本项目目前没有的带标签语料库。

其他检查:

npm test          # fixture suite only
npm run typecheck # src/ and test/ under strict mode
npm run test:smoke  # minimal stdio round-trip

也可以通过官方 Inspector 驱动它,而无需使用浏览器:

npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/list
npx @modelcontextprotocol/inspector --cli node dist/index.js \
  --method tools/call --tool-name redact_suggest \
  --tool-arg text="Patient John Doe (MRN-12345)"

该服务仅声明 tools 能力,因此在协议层面,resources/list 和 prompts/list 会返回 -32601 Method not found。Inspector CLI 会将其规范化为 {"resources": []} 和 {"prompts": []},Inspector UI 可能会用红色显示它们。这两种情况都属于预期行为,并非故障。

故障排除

运行服务器时似乎卡住了。 这是正常行为。它是一个 stdio 服务器:会等待 stdin 中的 JSON-RPC 请求,本身不会输出任何内容。如果你只是想确认已安装,可以运行 npx -y phi-guard-mcp --help 或 node dist/index.js --version。

客户端显示 phi-guard 运行失败或已断开。 手动运行配置中的命令并查看 stderr — npx -y phi-guard-mcp --version,或 node /absolute/path/to/dist/index.js --version。如果输出了版本号,问题就在配置路径,而不是服务器。

Cannot find module '.../dist/index.js'。 你正在从克隆的仓库运行,但尚未构建 dist/。dist/ 会被 gitignore 忽略,这是有意为之。运行 npm ci。

启动时出现 SyntaxError: Unexpected identifier 'assert' 或导入属性错误。 你的 Node 版本低于 22。使用 node --version 检查。请注意,MCP 客户端启动的 Node 可能与 shell 中的不同。

代码更改没有生效。 已在运行的 stdio 进程仍会使用旧构建。重新构建,然后重启客户端或重新连接服务器(在 Claude Code 中使用 /mcp)。

scan_code 在我确定有泄漏的仓库中返回 []。 最可能的原因有三种,按可能性从高到低排列:标识符和 sink 位于不同的行(请参阅同一行规则);文件扩展名不在支持列表中;或者目录被跳过(dist、build、任何以点开头的目录)。将扫描器指向此仓库的 test/fixtures 目录,确认扫描器正常运行;它必须返回 9 个发现。

scan_code 报错 No such path。 路径会在运行服务器的机器上解析;相对路径会基于 MCP 客户端为进程指定的工作目录解析。请传入绝对路径。

本工具不是什么

  • 不是托管服务。 它是本地 stdio 进程。没有后端、账户或遥测,服务器也不会自行发起网络请求。但这并不等于“没有任何内容离开你的机器”:发现结果会返回给 MCP 客户端,而客户端可能是托管式助手。请参阅安全边界。
  • 不是 HIPAA 认证、审计或合规证明。 scan_code 运行通过,不能向监管机构证明任何事情。它是针对特定错误类型的代码检查工具,不是合规证据。请将干净的结果理解为“这些特定模式没有触发”,绝不要理解为“此代码库符合 HIPAA 安全要求”。
  • 除了作者之外,未经任何人验证。 测试夹具由此项目编写,用于测试此项目。未经外部审查或第三方审计,也未在真实医疗保健代码库上进行评估。
  • 不是 Prowler、AWS Config 或云安全态势工具的竞争对手。 它们扫描基础设施和配置。本工具读取源代码,查找另一类问题。它们互为补充,本工具不能替代其中任何一种。
  • 并非面面俱到。 基于正则表达式的检测确实存在漏报,且漏报率尚未测量。如果变量名与敏感数据不匹配,或者值来自外部调用,本工具就无法发现其中的 PHI。
  • 无法跟踪跨行的值。 请参阅同一行规则。真正的数据流分析不在本工具范围内;这是有意设定的边界。
  • 无法完全识别注释。 只会跳过整行的 // 和 # 注释。块注释(/* ... */)和行尾注释仍会被扫描,因此即使其中没有任何内容会执行,sink 关键字也可能触发发现。
  • 不是密钥扫描器,也不能代替代码审查。 它只查找一种模式:看起来敏感的名称旁边出现了风险调用。
安全边界

服务器会在其运行的机器上,以启动它的用户权限读取传入的任何目录。它没有沙箱,也没有允许列表。请仅将其指向你有权读取的代码。

服务器不会打开套接字,也不会发送出站请求。但这并不意味着你的数据会留在本地。工具结果会返回给启动服务器的 MCP 客户端;如果该客户端是托管式助手,结果会根据该提供商的条款传输给该提供商并由其处理,而非依据本项目的条款。请将每个 scan_code 和 redact_suggest 结果都视为要发送给模型提供商的内容。

snippet 和 redact_suggest 结果默认会进行掩码处理,目的是只传输标识符名称和位置,而非 PHI 值。掩码使用的正则表达式集合与上文所述相同,因此误判率也相同:这些模式无法识别的值不会被掩码。不要把“已进行掩码处理”视为保证;在确认托管客户端的数据处理方式之前,不要通过该客户端对生产环境中的 PHI 运行此工具。

贡献

npm run verify 必须通过。CI 会在 Node 22 和 24 上运行此命令。

许可证

MIT — 参见 LICENSE。

Mcp 服务器已获批准

已列入 mcpservers.org

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。