内容
# 最后一个EHR
[](https://github.com/cbetz/last-ehr/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.npmjs.com/package/@lastehr/mcp)
[](https://registry.modelcontextprotocol.io/?q=io.github.cbetz%2Flast-ehr)
**开放源代码的FHIR代理参考实现。** 一个有权限的代理,负责患者的病历:它读取病历并提出写入建议,但除非您批准,否则不会保存任何内容。您可以自带后端和自己的模型密钥,以便使用真正的代理 - 或者从零密钥的本地合成演练开始。
> **Last EHR是一个层,而不是EHR。** 它运行在**无头FHIR后端**(Medplum或本地合成评估的HAPI FHIR)之上,并通过FHIR API与其通信。它不是记录系统,不存储任何PHI,并且永远不会捆绑或分叉后端。
**状态:早期/alpha。** API、结构和范围将发生变化。请仅使用合成数据。 · 许可证: [Apache-2.0](./LICENSE)
**[试用实时演示](https://www.lastehr.com/demo)**:无需注册,合成数据,每次写入都经过批准关卡。

## 从这里开始
| 目标 | 从这里开始 |
| --- | --- |
| 现在查看批准循环 | [试用实时合成数据演示](https://www.lastehr.com/demo) - 无需注册。 |
| 为MCP客户端提供有限的Medplum病历读取 | `npx -y @lastehr/mcp init --client claude-code` |
| 在没有FHIR凭证或提供商API密钥的情况下,在本地尝试MCP固定装置 | `npm run mcp:demo -- --client claude-code` |
| 在本地证明合成的网络代理工作流程 | `npm run eval` |
| 在没有帐户或模型密钥的情况下,在本地检查完整流程 | `npm run demo:local` |
### 30秒合成数据演练
1. 打开 [实时演示](https://www.lastehr.com/demo)。
2. 询问:`查找名为Smith的患者`。
3. 询问:`记录Maria Garcia的心率72 bpm`。
4. 检查建议的写入,然后批准它。数据是合成的,批准卡片显示将保存的内容。
对于MCP,`@lastehr/mcp`被刻意设计为一个单独的**只读**界面:`search_patients`和`show_patient_info`。在使用真实项目连接之前,请配置具有最小权限的Medplum令牌;完整设置在 [MCP指南](./docs/mcp.md)和 [官方MCP注册列表](https://registry.modelcontextprotocol.io/?q=io.github.cbetz%2Flast-ehr)。
想要在配置Medplum之前检查这两个MCP工具吗?从检出中,`npm run mcp:demo`启动包含的本地HAPI堆栈,重置四个合成固定图表,并打印Claude Code/Cursor配置。
该检出专属的**MCP本地实验室**不需要FHIR/Medplum凭证或模型提供商API密钥,并且仅限于固定患者。您的MCP客户端仍使用其正常帐户,并且可能会将合成结果发送到其模型提供商。它不是发布的软件包,通用HAPI支持或PHI路径。
对于确定性的安全机制检查,`npm run eval`创建单独的合成目标,证明提案/批准和拒绝路径,验证图表关联,清理并编写清除报告。它不是临床,授权或合规认证;请参阅 [评估指南](./docs/evals.md)。
## 它做什么
- 一个带有FHIR工具的聊天代理(Vercel AI SDK)。它 **读取** 病历(搜索患者,查看患者病历)和 **写入** 到它(添加注释,记录观察结果),流式传输并渲染为结构化卡片。
- **写入是确认门控的**:代理建议写入,您批准它,然后它才被保存。没有任何内容在您的点击之前触及病历。
- 身份验证,多租户和访问控制被委派给您的 **Medplum** 项目(`Project` = 租户,`ProjectMembership` = 用户,`AccessPolicy` = RBAC)。Last EHR不会重新实现任何这些,并且写入受您的AccessPolicy限制。
## 支持状态
Last EHR **支持Medplum**进行身份验证部署,并包括 **本地,无认证的HAPI FHIR模式**用于合成数据评估。其他FHIR R4后端在支持之前需要适配器。请参阅完整的 [支持矩阵](./docs/support.md),了解Web,SMART,MCP,身份验证和评估边界,然后选择路径。
## 它不是什么
- 不是图表EHR,不是记录系统,也不是Medplum替代品。它是一个薄的代理层:在您的FHIR后端上具有小型,增长的工具集。
- 不是保证。批准关卡是人类在环中的边界,而不是安全证明:它停止单方面写入,但依赖您阅读您批准的内容。模型可以提出错误或捏造的临床事实,批准疲劳是真实的。Last EHR提供关卡;您提供审查。
## 它如何工作
Next.js 15(App Router)+ React 19。代理位于 `app/api/chat/route.ts`(`streamText` + FHIR工具);FHIR调用通过小型后端接口 ([`lib/fhir/backend.ts`](./lib/fhir/backend.ts)),具有两个内置适配器:Medplum(托管或自托管,令牌身份验证)和本地HAPI FHIR用于合成评估。接口是四个方法加上合同注释,因此另一个无头EHR的适配器是一个小型,范围明确的贡献。请参阅 [docs/adapters.md](./docs/adapters.md)和 [路线图](./ROADMAP.md)。
```mermaid
flowchart LR
B["浏览器聊天"] --> A["/api/chat<br/>streamText + 4 FHIR工具"]
A -- "读取<br/>search_patients, show_patient_info" --> M[("您的FHIR后端<br/>(Medplum或HAPI)")]
A -- "写入<br/>add_note, record_observation" --> C{"批准卡片"}
C -- "批准并保存" --> M
C -- "取消" --> N["未保存"]
M -. "每个调用都以登录用户身份运行<br/>,受您的AccessPolicy限制。<br/>该层存储无PHI。" .-> A
```
在公共演示上,写入也会使用您的会话标记,因此您只会看到种子数据加上您自己的编辑。
## 快速入门
**最快**:在 [lastehr.com/demo](https://www.lastehr.com/demo)上尝试托管的演示。无需帐户,无密钥,合成数据。
**一键部署**,使用您自己的密钥:
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fcbetz%2Flast-ehr&env=OPENAI_API_KEY,MEDPLUM_CLIENT_ID,MEDPLUM_CLIENT_SECRET,NEXT_PUBLIC_QUICKSTART&envDescription=%E4%B8%AA%E6%A8%A1%E5%9E%8B%E5%AF%86%E9%92%A5%20%2B%20Medplum%20ClientApplication%20%E5%87%AD%E8%AF%81%E4%BF%A1%E6%88%B8%E9%89%B4%E8%AF%81%E5%90%8E%E7%9A%84%E5%90%8A%E5%90%8A%E5%BF%AB%E9%A1%B5&envLink=https%3A%2F%2Fgithub.com%2Fcbetz%2Flast-ehr%2Fblob%2Fmain%2F.env.example)
您仍然需要一个 **Medplum** 项目,并使用合成患者种子(下面为 `npm run seed`),演示才能有数据。
**在本地运行。** 先决条件:Node 22.18+(或24.2+),一个 **Medplum** 项目(Medplum托管的 [免费层](https://app.medplum.com/) 或您自己的),以及一个工具可用的模型API密钥(OpenAI,Anthropic或Amazon Bedrock)。
```bash
git clone https://github.com/cbetz/last-ehr.git
cd last-ehr
npm install
cp .env.example .env.local # 然后编辑.env.local(参见下文)
npm run seed # 将合成患者加载到您的Medplum中
npm run dev # http://localhost:3000/demo
```
至少设置,在 `.env.local` 中:
- 模型密钥:`OPENAI_API_KEY`(默认提供商),**或** `ANTHROPIC_API_KEY`,带有 `AI_PROVIDER=anthropic`,**或** AWS凭证 + `AI_PROVIDER=bedrock` 和 `MODEL_ID`;
- `NEXT_PUBLIC_MEDPLUM_BASE_URL` / `MEDPLUM_BASE_URL`(如果您指向自己的Medplum,请留空以使用Medplum的托管API);
- `MEDPLUM_CLIENT_ID` + `MEDPLUM_CLIENT_SECRET`(一个Medplum [ClientApplication](https://www.medplum.com/docs/auth/methods/client-credentials)):用于 `npm run seed`,以及 **无需注册的快速入门**,当您还设置 `NEXT_PUBLIC_QUICKSTART=true` 时。或者,设置 `NEXT_PUBLIC_MEDPLUM_GOOGLE_CLIENT_ID` 以通过Medplum的Google OAuth登录。
`npm run seed` 加载一个小型 **合成** 患者集(`scripts/fixtures/patients.ts`:四个患者,包含病况,药物,过敏,免疫接种和生命体征/实验室检查,两名命名为“Smith”)。它每次运行都会擦除和重新创建这些患者,因此可以安全地重新运行。然后打开 `/demo`并询问:*“查找名为Smith的患者”*。仅使用合成数据。
**本地FHIR堆栈,无Medplum帐户或模型密钥:** 仓库附带一个Docker Compose堆栈,包含HAPI FHIR和Postgres,以及一个明确的脚本演练,在不调用外部模型提供商的情况下运行批准循环。
```bash
npm install
npm run demo:local # HAPI,种子数据和http://localhost:3000/demo
```
`demo:local` 强制使用本地HAPI +脚本配置,而不创建或覆盖 `.env.local`。此固定,无网络演练始终查找种子Maria Garcia记录,并建议一个`心率:72 bpm`观察结果。它不解释您的提示,浏览图表或调用模型提供商; FHIR包装器仅允许该合成记录和观察结果。按Ctrl-C停止Next.js;使用 `npm run demo:local:down` 删除本地堆栈。
诚实的范围:本地HAPI服务器在 **无身份验证**的情况下运行,因此此模式仅用于本地,单租户使用;每个浏览器会话隔离是客户端过滤,而不是安全边界。发布的`@lastehr/mcp`软件包仍然需要Medplum凭证;单独的检出专属`npm run mcp:demo`本地实验室另外公开两个合成,只读图表,无需凭证。
## 文档
- [快速入门](./docs/quickstart.md):托管演示,Medplum本地运行和本地HAPI评估。
- [支持矩阵](./docs/support.md):当前支持哪些后端和接口。
- [架构](./docs/architecture.md):聊天路由,工具,后端适配器和数据边界。
- [后端适配器](./docs/adapters.md):适配器合同,工具,检查清单和贡献路径。
- [适配器启动器](./examples/fhir-adapter-starter):具有合同套件的可执行承载令牌FHIR REST基线。
- [批准门控写入](./docs/approval-gates.md):批准门控保护的内容和不保护的内容。
- [MCP服务器](./docs/mcp.md):发布的,只读Medplum图表工具以及检出专属的合成本地实验室。
- [FHIR代理安全评估](./docs/evals.md):提案,批准,拒绝,关联和清理机制的处置合成工作流报告。
- [部署](./docs/deployment.md):环境变量,速率限制,Docker和公共演示强化。
- [威胁模型](./docs/threat-model.md):信任边界和已知限制。
- [路线图](./ROADMAP.md):当前,下一个和故意不在范围内的内容。
## 从Medplum应用(SMART on FHIR)启动
Last EHR可以直接从app.medplum.com启动。在您的Medplum项目中,通过创建具有以下内容的 **ClientApplication** 注册它:
- `launchUri` = `https://<your-deploy>/launch`
- `redirectUri` = `https://<your-deploy>/launch/callback`
然后在部署中设置 `SMART_CLIENT_ID` 为该ClientApplication的id。 Last EHR出现在 **每个患者和病历页面的应用选项卡**;启动它会打开聊天,已经限定为该患者,重用您的Medplum登录(SMART App Launch with PKCE,公共客户端,无密钥)。令牌受授予的SMART范围和您的AccessPolicy限制,写入仍然在批准卡片中停止。
## 使用MCP进行图表读取
[`@lastehr/mcp`](./packages/mcp)是用于Medplum的小型独立MCP服务器。它为Claude Code,Cursor和其他MCP客户端提供两个限定图表读取工具-`search_patients`和`show_patient_info`-在`0.1.x`行中没有写入工具。
```bash
npx -y @lastehr/mcp init --client claude-code```
使用具有最小权限的`MEDPLUM_ACCESS_TOKEN`或`MEDPLUM_CLIENT_ID` + `MEDPLUM_CLIENT_SECRET`进行身份验证;为自托管的Medplum实例设置`MEDPLUM_BASE_URL`。 读取访问仍然可以返回PHI,因此在将其连接到真实项目之前,请查看MCP客户端的模型提供商和数据处理边界。
从检出中,`npm run mcp`构建并启动相同的软件包。完整的设置,客户端配置和支持边界在 [MCP指南](./docs/mcp.md)中。 [官方MCP注册列表](https://registry.modelcontextprotocol.io/?q=io.github.cbetz%2Flast-ehr)是确切的,已验证的发布记录。
要评估MCP表面,请使用存储库检出处的单独本地实验室:
```bash
npm install
npm run mcp:demo -- --client claude-code
```
它启动本地HAPI,重新加载存储库的合成固定记录,并打印客户端注册命令。生成的MCP进程具有相同的两个只读工具名称,但只能解析这些固定患者。它不包含在`@lastehr/mcp`中,不使HAPI成为支持的软件包后端,并且必须永远指向真实数据。本地实验室服务器不需要FHIR凭证或提供商API密钥,但MCP客户端本身仍需要其正常帐户,并且可能会将合成工具输出传输到该模型。
## 配置
每个变量都在 [.env.example](./.env.example)中记录。实际代理路径是提供者不可知的,并且有一个硬性规则:**此处提供的每个外部提供商都可以携带BAA**,因为此项目的部署将面向实际的临床数据,尽管演示仅限合成。 `scripted`选项是一个单独的显式零密钥本地HAPI演练 - 不是模型提供商或PHI就绪模式。
| AI_PROVIDER | 密钥 | 示例MODEL_ID | BAA路径 |
|---|---|---|---|
| `scripted` | 无 | 固定的本地合成序列 | 无外部提供商调用。 需要显式本地HAPI标志;不能作为通用代理。 |
| `openai`(默认) | `OPENAI_API_KEY` | `gpt-4.1-mini` | OpenAI使用零保留选项为API流量签署BAA。 |
| `anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-4-6` | Anthropic使用零保留选项为API流量签署BAA。 |
| `bedrock` | AWS凭证env + `AWS_REGION` | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | Amazon Bedrock在合格服务的可用的AWS BAA下可用;设置显式模型ID或推理配置文件。 |
BAA要求适用于外部提供商行:签署一个是操作员的步骤,而默认情况下,裸API密钥不是PHI就绪的。
聚合商无法签署BAA(OpenRouter,根据他们的当前公共条款)被故意不提供。分析(PostHog)和营销站点等(Neon)是可选的并且是lastehr.com特定的。## 安全与数据
Last EHR存储自己的患者数据;所有内容都存在于您的FHIR后端。但是,请明确,批准关卡是 **写入安全控制,而不是隐私控制**。在外部模型模式下,代理从图表读取的内容会在您的API密钥下发送到您的模型提供商的上下文发送给模型提供商,没有批准步骤。脚本演示使用不请求模型,而是仅限于一个播种的合成记录;它不是真实数据的路径。
**仅使用合成数据,除非您有实际协议。** 指向真实的PHI需要至少:
- 您的**模型提供商**的BAA涵盖您的API流量(消费者计划不适用);
- 一个具有自己的BAA的**HIPAA合规FHIR后端**;
- 您自己的合规审查。本项目是alpha且 **不是** HIPAA涵盖的服务;PHI处理是操作员的责任。
请参阅 [SECURITY.md](./SECURITY.md) 了解完整的姿态以及如何报告漏洞。
## 开源
免费且Apache-2.0自托管。托管层(托管Medplum + 签署BAA,多租户,计费)可能会在开源核心获得关注后推出,仅在此基础上构建。
## 无关联
个人开源项目。 与Medplum,Vercel或任何雇主无关。
## 许可证
[Apache-2.0](./LICENSE)。 请参阅 [NOTICE](./NOTICE) 了解第三方属性, [CONTRIBUTING.md](./CONTRIBUTING.md) 提交贡献, [GOVERNANCE.md](./GOVERNANCE.md) 了解项目管理方式。