想象一下,让一个AI助手对数千张医学图像进行去标识化处理。它运行整个流程、跟踪进度、总结每一个决策,并告诉你哪些文件需要人工审核,而整个过程从未看过任何一像素的患者数据。
乍一听这似乎不可能。AI助手通常需要访问它们帮你处理的数据。
在本教程中,你将构建一个不查看敏感医学图像的AI代理。相反,它通过精心设计的工具来编排本地去标识化流程,让患者数据完全保留在你的机器上。
实现这一目标的技术是模型上下文协议(Model Context Protocol,简称MCP),这是一个开放标准,允许AI模型调用外部工具,而不是仅仅依赖其内置能力。
在我之前的文章如何为临床研究构建AI驱动的医学图像去标识化流程中,我们已经了解了如何构建去标识化工具——Aegis,这是一个使用MONAI(PyTorch)流程构建的开源工具,通过OCR和NER从DICOM元数据和图像像素中移除受保护健康信息(PHI)。
此后我又为其添加了本地MCP(模型上下文协议)服务器支持,在本文中我们将使用FastMCP从头构建该服务器。然后将其连接到Claude Desktop,把Claude变成一个可以通过自然对话运行、监控和审计去标识化任务的AI代理。
在开始之前有一点需要说明:虽然本文以Aegis为例,但本教程中的模式适用于任何你想让AI代理访问的Python工具。如果你有自己的流程、命令行工具或库,也可以按照同样的方法将其封装起来。
学完本教程后,你将拥有:
一个本地MCP服务器,将Aegis去标识化流程暴露为六个工具。
连接到该服务器的Claude Desktop,每个操作都有人工审批环节(human-in-the-loop)。
一个可以用日常英语与之对话的代理:"对这个文件夹进行去标识化处理,并告诉我是否有需要人工审核的内容。"
磁盘上可验证的审计追踪记录,你可以据此核对代理报告的所有内容。
要学习本教程,你需要具备:
中级Python经验
Aegis代码仓库(或你自己的待封装Python工具):https://github.com/lakshmi-mahabaleshwara/aegis
Python 3.10或更高版本
已安装Claude Desktop(macOS或Windows);免费的Claude账户即可用于本地MCP服务器
Node.js(仅用于测试工具,服务器本身不需要)
我们将使用:
MCP Python SDK(mcp)
FastMCP(包含在SDK中)
用于测试的MCP Inspector
作为MCP主机的Claude Desktop
如果你没有读过之前的文章,也不需要从头重建整个流程。克隆Aegis代码仓库就足够了,不过之前的文章会解释该流程在底层究竟做了什么。
Aegis是一个医学图像去标识化流程,它会从DICOM元数据和图像像素中移除PHI,将每个操作记录在审计报告中,并将不确定的病例转交人工审核。
在构建服务器之前,先安装Aegis。我们在下一步编写的服务器会导入这个包,所以必须先把它准备好。
# Get the code
git clone https://github.com/lakshmi-mahabaleshwara/aegis.git
cd aegis
# Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate
# On Windows: venv\Scripts\activate
# Install Aegis (editable) plus the MCP server in one step.
# The [mcp] pulls in the MCP SDK; it also installs the
# `aegis-mcp` console command you'll point Claude Desktop at.
pip install -e ".[mcp]"
# One-time: download the OCR and NER model weights
python scripts/prefetch_models.py
可通过编辑模式安装(pip install -e)让 monai_aegis 包可以从任何目录导入,并将 aegis-mcp 控制台命令添加到你的 PATH(位于虚拟环境内)。MCP 服务器依赖于此,因为 Claude Desktop 是从它自己的工作目录启动服务器的,而不是从仓库根目录。
安装好包之后,我们就可以开始构建公开该包的服务器了。
模型上下文协议(MCP)是一种开放标准,允许 AI 应用程序调用外部工具。AI 模型无需仅依赖上下文中的信息来尝试解决所有问题,而是可以调用由外部程序暴露的函数。
涉及三种角色:
主机(Host)——AI 应用程序(在我们的场景中是 Claude Desktop)
服务器(Server)——你编写的一个暴露工具的小程序
工具(Tools)——带有名称、参数和描述的 Python 函数,AI 可以调用它们
当 Claude Desktop 启动时,它会启动你的 MCP 服务器并发现其暴露的工具。它只能看到每个工具的名称、参数模式和 docstring,而看不到你的实现代码。实际上,你的 docstring 就成了提示词,帮助 Claude 决定何时调用某个工具。
为什么要使用 MCP? 如果你熟悉 Python,你可以在自己的脚本中直接调用 Aegis 库。当你希望 AI 助手通过自然对话来操作该流程时,MCP 就变得很有价值。你无需编写脚本或记住命令行选项,只需简单地问:
“对这个文件夹去标识化。”
“批处理完成了吗?”
“哪些文件需要人工审核?”
“总结一下今天的运行情况。”
底层流程从未改变。MCP 只是在 AI 与你的软件之间提供了一个安全接口,让模型能够编排工作流,而实际处理仍在你本地的 Python 应用程序中完成。
我们将使用 Claude Desktop 作为主机,因为它原生支持 MCP。无需配置桥梁、额外服务或网络端口——服务器作为本地子进程通过标准输入/输出与 Claude Desktop 通信,整个设置通过一个 JSON 文件配置。
Claude Desktop 与 MCP 服务器通信,服务器在本地调用 Aegis 流水线。流水线在你的机器上处理医学图像,而只有计数、决策和文件路径等汇总信息会被返回给 Claude。
在编写代码之前,先确定智能体可以执行哪些操作。我们的服务器暴露了六个工具:
| 工具 | 用途 |
|---|---|
warm_up |
预加载 Aegis 中的 OCR 和 NER 模型,使第一次真实调用变得快速 |
deidentify_file |
使用 Aegis 流水线对单个 DICOM/JPEG/PNG 文件进行去标识化 |
start_batch_job |
在后台发现并处理目录中的所有 DICOM/图像文件 |
get_job_status |
检查之前启动的批处理任务的进度 |
summarize_run |
根据报告文件审计一次已完成的运行 |
list_review_queue |
列出转交给人工审核的文件 |
我们围绕三个简单原则设计了这些工具:
返回汇总信息,而不是敏感数据。
对长时间运行的任务使用后台作业。
保持工具接口小而专注。
现在,让我们把这个设计变成一个可用的 MCP 服务器。我们将一次构建一个部分,以便你可以在自己的 Python 工具中复用同样的模式。完整实现位于 src/monai_aegis/mcp_server.py 中;下面的章节展示了它是如何组合在一起的。
FastMCP(随 MCP Python SDK 一起提供)可以将一个带装饰器的 Python 函数转换为工具。
from mcp.server.fastmcp import FastMCP
# creates a FastMCP server instance
mcp = FastMCP("aegis-mcp")
字符串 "aegis-mcp" 只是服务器的名称。它是 Claude Desktop 在其工具列表中显示的内容。我们从这里添加的每个工具都是一个用 @mcp.tool() 装饰的函数。
工具是一个装饰器、一个类型化签名和一个文档字符串。下面这个是真正执行单个文件去标识化工作的工具:
@mcp.tool()
def deidentify_file(input_path: str, output_dir: str = "") -> dict:
"""De-identify a single medical image (DICOM, JPEG, or PNG).
Scrubs DICOM header PHI and redacts burned-in pixel PHI using
OCR and NER. Returns summary statistics and the output location
only, never the redacted text or any pixel data.
"""
...
return {
"status": "success",
"source_file": src.name,
"output_dir": str(out),
"pixel_regions_detected": len(pixel_rows),
"pixel_decisions": decisions, # e.g. {"redacted": 4, "safelisted": 10}
"header_tags_scrubbed": tags_scrubbed,
"needs_manual_review": decisions.get("low_confidence", 0) > 0,
}
请注意,Claude 只能看到文档字符串,而工具返回的是摘要统计信息,而不是图像数据或提取的文本。
stdout不要在 MCP 服务器中使用 print(),因为 stdout 是为 JSON-RPC 保留的。请改用 stderr 发送日志。
处理整个目录可能需要几分钟,这超过了 MCP 工具调用可以阻塞的时间。为了避免同步等待,start_batch_job 会创建一个后台线程,立即返回一个 job_id,并让 Claude 使用 get_job_status() 轮询进度。
import threading, uuid
_jobs = {}
@mcp.tool()
def start_batch_job(input_dir: str, output_dir: str = "", mode: str = "auto") -> dict:
"""Start a background job that de-identifies all DICOM/image files
in a directory. Returns immediately with a job_id. Use get_job_status
to check progress, do not wait synchronously.
"""
job_id = uuid.uuid4().hex[:8]
_jobs[job_id] = {"job_id": job_id, "state": "queued",
"processed": 0, "total": None,
"decisions": {}, "errors": []}
threading.Thread(
target=_batch_worker, args=(job_id, input_dir, output_dir),
daemon=True,
).start()
return {"status": "started", "job_id": job_id,
"next_step": f"Call get_job_status with job_id '{job_id}'."}
工作线程在处理每个文件时会更新_jobs[job_id],轮询工具只需将其读回:
@mcp.tool()
def get_job_status(job_id: str) -> dict:
"""Return the current state, progress, and decision counts for a job."""
return _jobs.get(job_id, {"status": "unknown", "job_id": job_id})
由于MCP服务器是一个长时间运行的进程,它可以在整个对话期间将作业注册表保存在内存中。如果服务器重启,活动作业ID会丢失,但去标识化文件和审计报告仍安全地保留在磁盘上。
Aegis加载EasyOCR和斯坦福NER模型需要时间。服务器以懒加载方式构建管道并缓存它,因此只需付出一次成本,并暴露一个warm_up工具,这样第一次真正的调用不会因模型加载而遇到超时:
@mcp.tool()
def warm_up() -> dict:
"""Preload the OCR and NER models so the first real call is fast."""
_get_pipeline() # builds and caches the pipeline on first use
return {"status": "ready"}
其余工具读取Aegis已生成的报告和审查文件夹。由于它们是对现有审计记录的汇总,而非重新处理图像,因此Claude无需访问底层医学图像即可回答有关已完成运行的问题。
@mcp.tool()
def summarize_run(run_dir: str) -> dict:
"""Audit a run from its CSV reports. Returns counts only — never text or tag values."""
run = Path(run_dir).expanduser().resolve()
pixels = _read_csv_rows(run / "aegis_pixel_detections.csv")
tags = _read_csv_rows(run / "aegis_tag_actions.csv")
return {
"pixel_decisions": Counter(r["decision"] for r in pixels), # redacted / safelisted / low_confidence
"tag_actions": Counter(r["action"] for r in tags), # REMOVE / REMAP / ZERO / DUMMY / ATTEST
@mcp.tool()
def list_review_queue() -> dict:
"""List files quarantined for manual review — names only, never contents."""
names = sorted(f for _, _, fs in os.walk(REVIEW_DIR) for f in fs if not f.startswith("."))
return {"count": len(names), "files": names[:50]}
如果你对这一切持怀疑态度(我曾经也是),那么这一步就是为你准备的。MCP Inspector是一个调试UI,它连接到你的服务器,让你手动点击这些工具。
npx @modelcontextprotocol/inspector //aegis/venv/bin/aegis-mcp
检查器在服务器屏幕上打开,其中列出了你的aegis-mcp服务器。点击切换按钮进行连接,当服务器正在运行时,它会变为绿色。
接下来,打开工具选项卡。你将看到 MCP 服务器公开的六个工具。首先运行warm_up,并在你启动检查器的终端中查看 OCR 和 NER 模型的加载过程。
接下来,使用测试图像的路径运行deidentify_file。结果面板显示工具的 JSON 响应,而消息面板显示与服务器交换的请求和响应。
打开Claude Desktop,然后转到设置 → 开发者 → 编辑配置。这将打开(或创建)MCP 配置文件。在 macOS 上,它位于 ~/Library/Application Support/Claude/claude_desktop_config.json,而在 Windows 上位于 %APPDATA%\Claude\claude_desktop_config.json。
在 mcpServers 下添加以下 aegis 条目:
{
"mcpServers": {
"aegis": {
"command": "//aegis/venv/bin/aegis-mcp",
"args": [],
"env": {
"AEGIS_OUTPUT_DIR": "//aegis/staging_output",
"AEGIS_REVIEW_DIR": "//aegis/staging_not_processed",
"AEGIS_DEVICE": "mps"
}
}
}
}
用于将 Claude Desktop 连接到 Aegis MCP 服务器的配置。
保存前需要检查以下几点:
为 command、AEGIS_OUTPUT_DIR 和 AEGIS_REVIEW_DIR 使用绝对路径。
将 command 指向虚拟环境中的 aegis-mcp 可执行文件,以便 Claude 使用正确的 Python 安装和依赖项。
将 AEGIS_DEVICE 设置为 mps(适用于 Apple Silicon)、cpu(适用于 Intel Mac 或 Linux),或 cuda(如果你有 NVIDIA GPU)。
保存文件后,完全退出并重新启动 Claude Desktop(仅关闭窗口是不够的)。在新聊天中,打开 Search & Tools 菜单,现在你应该能看到 Aegis MCP 服务器及其六个可用工具。
是时候开始第一次对话了。发送:
预热 Aegis 去标识化服务器。
Claude 会在调用工具之前请求许可。这个提示是一个特性:你赋予智能体的每项能力都需要你的明确批准,你可以按每次调用或按工具授予权限。
一旦获得批准,模型就会加载,Claude 会汇报所用的时间。
对单个文件进行去标识化:
对文件 进行去标识化。
Claude 调用 deidentify_file 并回答各项计数:检测到多少文本区域,有多少被涂黑,有多少被列为临床文本而安全放行,清除了哪些 DICOM 标签,以及是否有任何内容需要审核。它在从未看到图像的情况下完整叙述所有这些信息。
接下来,处理一个批次:
现在为 启动一个批量去标识化任务。
该工具会立即返回一个作业 ID,处理在后台继续进行。稍后询问:
那个作业进展如何?
Claude 会在多轮对话中记住作业 ID,并轮询 get_job_status,报告已处理的文件、运行中的决策计数以及任何错误。
作业完成后,你可以让 Claude 总结本次运行、列出需要人工审核的文件,或者为你的审核会议生成报告,所有这些都来自管道的审计记录。
总结已完成的运行。有什么需要人工审核的吗?
对 test_ultrasound.dcm 具体做了什么?哪些头部标签被修改了?
为本次运行起草一份简短的去标识化摘要,适用于审核会议。包括总数、决策分类,以及待人工审核的文件。
下面这些摘要中的每一项都来自工具结果,计数和决策均读取自管道自身的记录。
将 Claude 的摘要与 CSV 报告和 job_summary_<job_id>.json 进行对比。数字应保持一致,这为你提供了一种简单的方法来验证智能体报告的所有内容。
不会。这些工具从不返回图像像素、OCR 提取的文本或 DICOM 标签值,因此 Claude 无法访问底层的 PHI。
例如,如果你问:
给我看看该文件中检测到的患者姓名。
Claude 无法回答,因为 MCP 工具从不公开这些信息。
然而,对话元数据确实会到达模型。文件名、目录路径、计数和工具输出会像任何其他聊天内容一样被 Claude 处理。请记住以下几点:
避免在文件名中包含 PHI。 如果文件名包含患者姓名或标识符,请重命名文件。
保持错误消息干净。 不要在工具返回的异常中包含敏感的 DICOM 值。
在开发时使用合成数据。 本教程使用虚构的 PHI。在处理真实患者数据之前,请遵循你所在组织的安全和合规要求。
如果你要将此模式用于自己的工具,请考虑以下最佳实践:
限制文件访问。 将工具限制在批准的目录中,而不是允许任何可读路径。
审查工具权限。 对于像 get_job_status 这样的只读工具,始终允许是合理的,但对于修改文件的工具,请保留审批提示。
返回结构化结果。 优先使用计数和分类而不是原始文本,以减少暴露敏感信息的可能性。
固定模型版本。 使用固定的 OCR 和 NER 模型版本可以使你的管道更具可重复性和可预测性。
在 HIPAA 等法规中,去标识化具有特定的法律含义和明确要求。本教程演示如何为去标识化管道构建 AI 智能体接口,而不是如何认证法规合规性。该管道有意将不确定的案例转交人工审核,任何实际部署都应根据你所在组织的政策和适用法规进行验证。
MCP 并不会改变 Aegis 检测 PHI 的效果,它改变的是人们与之交互的方式。用户无需使用命令行,而是可以用自然语言运行作业、查看结果并提出问题。
对于诸如夜间批处理之类的自动化工作流,CLI 仍然是更好的选择。MCP 最适合交互式、人在回路中的任务,而 CLI 仍然是计划任务的首选。在这两种情况下,磁盘上的文件和审计报告都是事实来源。
以下是你可以继续探索的几个方向:
完全本地运行。 将相同的 MCP 服务器与 Ollama 和 Open WebUI 配对,使管道和 AI 模型都保留在你的机器上。
加强安全性。在共享环境中部署之前,将工具限制在批准的目录内。
构建数据集准备工作流。使用智能体对数据进行去标识化、总结结果,并为机器学习准备数据集。
添加下游分析。 对去标识化的输出运行视觉模型,而不是对原始图像运行。
在其他地方复用该模式。同一架构可以编排管道,从法律文件、日志、财务记录或其他机密数据中移除敏感信息。
我们使用MCP将现有的Python管道转变为一个AI可访问的工具。关键设计原则是模型编排工作流,但永远不会看到敏感数据。
这种模式不仅限于医学影像。任何处理敏感信息的管道,例如法律文件、财务记录或个人照片,都可以暴露安全、结构化的工具,同时保持底层数据私密。
您可以在Aegis仓库中找到完整实现:https://github.com/lakshmi-mahabaleshwara/aegis。如果您觉得本教程有用,请考虑给这个仓库加星标,以帮助其他人发现它。
——
一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。