parallight-lecture-9-agentify
Provides tools for interacting with an ERPNext instance, enabling AI agents to manage business documents such as sales orders, purchase invoices, employees, and inventory while respecting ERPNext's permission model and business validations.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@parallight-lecture-9-agentifyList all draft purchase invoices with risk scores."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
lecture-9-agentify —— 把一个已有工程项目 Agent 化(ERPNext 实战靶场)
一句话主线
Agent 化 ≠ 重写,是"包裹"(wrap)。 一个成熟系统(这里是 ERPNext)已经自带四样护栏——
权限内核、业务校验、状态机(draft→submit)、审计流水。天真的 fork 把这四样全绕过,得到一个
能干活但会闯祸的 agent。正确的做法是让 agent 降权继承这四样护栏,再在其上加一层
agent 专属安全中间件:身份透传(层2)、风险分级 + 人审批闸(层3)、隔离边界 + 幂等/
爆炸半径(层4),能力映射本身(层1)只留 5 个窄工具,不管宿主有多少种单据类型。六个现场
demo(A–F)分别把这条主线的一个失效模式讲透;第三份东西(gates/)把"学生 fork 出来的东西
是不是真的做了这件事"变成一套可核对的证据要求,而不是"读起来像"就算数。
Related MCP server: ERPNext MCP Server
§6.0 真实事故锚点表
每个 demo 开头都要先讲这一行的真实事故——学员先在这个 ERPNext 靶场上复现同一类故障, 再修掉,最后在 trace/代码行里验证到底是哪一层挡住的。
Demo | 真实锚点 | 学员要"感受到"的事 |
A | Supabase + Cursor agent 事故(2025 中):service-role 特权 + 支持工单里的用户输入被当指令 → 集成 token 外泄到公开工单。"lethal trifecta":特权访问 × 不可信输入 × 对外信道 | admin key 让 agent 无所不能,而"无所不能"本身就是事故 |
B | GitHub MCP 事故(2025-05,Invariant Labs):公共仓一条恶意 issue 就能让接了 GitHub MCP 的 agent 把私有仓内容、个人数据、工资信息吐出去 | 权限不是 prompt 里一句"你不许看工资",是宿主系统里可验证的拒绝路径 |
C | agent 重试风暴(生产案例):90 秒内重试 17 次,每次措辞微调 → 幂等键跟着漂移 → 数百条重复记录;及"回执 200 但其实没落库 / 超时后其实已落库" | 幂等键必须来自业务语义,不能来自 LLM 生成的请求体;回执不可信,必须回查宿主状态 |
D | 同 A 的 lethal trifecta 收口:发票 | 防注入不是让模型"自觉",是信道隔离 + 能力最小化 + 人闸三层叠加,trace 里能指出挡住它的确切代码行 |
E | 这门课自己的问题(无外部事故):一份把安全声明写得很周全、却一行代码都没强制的 fork,读起来和真正做了架构工作的 fork 很像 | 评审不能只读 README/系统提示词,要问"如果把这一行删掉/顺序换一下,会不会有一个独立信号告诉我出事了" |
F | 工具膨胀 / 上下文轰炸(企业 MCP 普遍病):开满 93 工具的 GitHub MCP 一启动吃约 55,000 token;企业级把多个 MCP server 接在一起,用户还没打第一个字,上下文就可能已经烧掉六七成 | "把宿主全部 DocType 自动生成工具"是最天真的 agent 化;能力映射的第一动作是删,不是加 |
来源与逐条展开见各 demo 文档自己的"现实锚点"小节(Demo F 的行业数字额外标了出处,不与本仓 实测数字混在一起)。
环境准备
先说两件读者容易搞错的事:
ERPNext + MariaDB 很重。 这套栈是在一台 6 GiB / 4 CPU 的 Docker VM(本机用 Colima:
colima start --cpus 4 --memory 6 --vm-type vz --vz-rosetta,或等价配置;本机colima list实测确认当前就是4 CPUs / 6GiB)上 构建并验证的——9 个容器(backend/db/redis-cache/redis-queue/websocket/queue-short/ queue-long/scheduler/frontend)。给少了会更慢,不代表跑不起来,但不要指望在 2 GiB 的虚机上顺畅跑。建站要等,不要碰见报错就重试。
make create-site内部有一个真正的就绪轮询(等 backend 能跑bench --version才继续,最多等 40×3 秒),第一次跑还要现拉 HRMS 源码、 跑 DocType migration——诚实的指令是"跑make create-site然后等",不是"报错了就重跑"。 已实测(docker/Makefile的注释里写明了原因):首次冷启动(第一次拉镜像 + 首次bench get-appclone HRMS)约 8 分钟;镜像已在本机缓存后的干净重建(make nuke && make up && make create-site)约 2 分钟(2:07.07实测)。给学生的预算:第一次 跑留 5–10 分钟。(这两个数字来自这个仓库自己 Task 2 的干净态实测记录,不是编的估算; 写这份 README 时栈已经在跑,没有为了重新计时而make nuke重建。)
git clone <this-repo> lecture-9-agentify
cd lecture-9-agentify
npm install # 装 connector + MCP server 依赖(node >=20)
cd docker
cp .env.example .env # SITE_NAME / DB_ROOT_PASSWORD / ADMIN_PASSWORD /
# AGENT_SVC_PASSWORD 都是本地沙箱占位符,不要在课堂外复用
make up # 起 9 个容器,几秒钟(镜像已缓存的前提下)
make create-site # 建站 + 装 erpnext + 拉装 hrms + developer_mode;等它
# 自己的就绪轮询走完,预算 5–10 分钟(首次)/ ~2 分钟(缓存后)
make seed # 灌种子:4 个角色化用户、10 个商品/7 个客户/7 个供应商、
# 8 名员工+工资单、期初库存、销售/采购单据(草稿+已提交都
# 有)、两条双语投毒 draft 采购发票;幂等,可重复跑种子数据现在长什么样(seed/seed.py,2026-08-01 从"能跑通"扩到"能逛",细节见
.superpowers/sdd/2026-07-31-lecture-9-agentify-erpnext/demo-data-report.md):Company 1 个
(Agentify Demo Co);Fiscal Year 5 个(当前年份 ±2,从 frappe.utils.today() 派生,不再
是硬编码的字面量 2024——这正是这次改动修的真事故:讲师现场撞过"当天日期落在任何财年之外,
docstatus 0 的草稿都插不进去");Item 10 个(跨 Standard Items/Consumables 两个分组,8 个
库存 + 2 个非库存);Customer 7 个、Supplier 7 个(中文公司名居多;ACME/Globex 这两个被
投毒单和 Demo C 幂等用例点名的老面孔原样保留,一个字段没改);Employee 8 个(Alice 的
Salary Slip,net_pay 10000,原样保留——加 7 个新员工,各自挂了部门/职位,三档底薪
8000/10000/15000,共 8 张 Salary Slip);Bin 8 条(Material Receipt 灌的期初库存);
Sales Order 8 / Sales Invoice 6 / Purchase Order 6 / Purchase Invoice 8(含种子自带的 2 条
投毒 draft)——草稿和已提交混着来,draft→submit 状态机在列表页肉眼可见。
逛 UI 请用 Administrator 登录,不要用四个种子用户:sales_user/accounts_user/
hr_user/agent_svc 是刻意收窄权限的角色化身份(Demo B 的跨域拒绝就是靠这个拦的),拿它们
逛 desk 会到处撞权限墙——这是这门课的设计意图,不是 bug,讲师现场就踩过这一下。要看"这
是个真实系统"的完整效果(列表页、看板、报表都有数据可看),登录页填 Administrator /
docker/.env 里的 ADMIN_PASSWORD。
跑完之后:http://localhost:8080(或 http://erp.localhost:8080,两个 host 都路由到同一
站点)应该是 ERPNext 登录页;docker ps 能看到 9 个 l9-erpnext-* 容器 Up(db 那条带
(healthy))。不要 make seed 第二次去"确认一下"——种子数据是全部 6 个 demo 共用的固定
靶子,幂等归幂等,但没有必要重复跑;也不要在 demo 之间 make down/make nuke,容器会一直
跑到课程结束。
一处没打通的开关,先别踩:不要单独去改 .env 的 AGENT_SVC_PASSWORD。seed/seed.py:104
建 agent_svc@example.com 这个用户时读的是 ERPNext 站点配置里的 agent_svc_password 键,
而 docker/Makefile 的 create-site 从来没有把这个键写进站点配置——所以不管 .env 里写
什么,种出来的真实密码永远是硬编码的 agent-svc-pw。改了 .env 的这个值(哪怕连带改了
MCP server 侧的 env)只会让 MCP server 用一个新密码去登录一个密码没变的用户,登录会静默
失败。细节和"这一步该怎么补"见 agent/claude_code.md 的对应小节。
目标语区现在是简体中文:make seed 现在还会把 System Settings.language 和四个种子
用户的 User.language 都设成 "zh"(seed/seed.py"1.5) 目标语言"一节)——纯配置,镜像本身
自带 84 种语言,不装任何新东西。desk UI(/app/...)、Frappe 报错文本(权限拒绝/校验失败)
现在都是中文,但 connector 五个工具的 docType 参数(connector/src/types.ts)依然是英文
字面量,完全不受影响——这条"label 会变、identity 不会变"的对照,和"host 报错现在是中文、
quarantine 边界依然扛住"这两条,分别写在 experiments/demo_b_permission_passthrough.md
Step 6 和 experiments/demo_d_prompt_injection.md Finding 3 的现场印证里。切换前留了一份
完整数据库备份:sites/erp.localhost/private/backups/20260801_105602-erp_localhost- database.sql.gz(还原整站,不是只回退语言这一个字段)。
双前端接入
同一个 MCP server(npm run --silent server,stdio 传输,5 个 erp.* 工具)可以接两种
前端——--silent 不是可选项:裸 npm run server 会把 npm 自己的 banner 打到
stdout,而 stdout 就是 MCP 的 JSON-RPC 传输通道,这会污染协议流(细节和真实报错见
agent/claude_code.md):
Claude Code —— 已验证跑通,见
agent/claude_code.md(.mcp.json配置 + 真实 JSON-RPC 握手记录)。OpenWorker(Andrew Ng,MIT,本机跑的开源 agent)—— 见
agent/openworker.md。 2026-08-01 已在本仓做了两轮端到端联调:第一轮跑 OpenWorker 自己的 MCP 客户端代码, 第二轮跑它的无头 server REST API(openworker-server)——这条 API 也正是桌面 GUI 的Manage → Integrations粘贴框在代码层面调用的同一条路径,所以 GUI 背后那条数据通道 也已经验证过,但 Tauri 桌面应用本身的界面/点击交互没有打开验证过。文档里逐条标了 哪些是查得到出处的公开事实、哪些是实测确认的、哪些仍未验证——现场用之前先看文档末尾 "这次任务验证的是什么、没验证的是什么"一节。
六个 demo 怎么跑
Demo | 文件 | 要不要 ERPNext 起着 | 怎么跑 |
A —— 天真世界:admin key + 裸工具 |
| 要(讲师现场 | 照 runbook 里的 |
B —— 权限透传:四个身份,宿主自己拦 |
| 要(同上) | 同一批任务分别以 sales/accounts/hr/agent_svc 四个种子用户登录跑,看 ERPNext |
C —— 重试风暴 / 幂等 / 回执不可信 |
| 不要——全程离线, |
|
D —— 压轴:注入双弹头(付款+外泄),双语 |
| 不要——全程离线;种子库里确实种了这条 payload 的真身,但断言不依赖它在不在线 |
|
E —— anti-shallow-fork 三道闸对照 |
| 不要——不需要 | 照 runbook 现场 |
F —— 工具膨胀:全量映射 vs 窄工具 |
| 不要——全程离线,纯函数 |
|
Demo A/B 需要真栈时,experiments/scripts/demoAB.int.test.ts 有对应的可跑对照用例(走真实
HttpErpClient/Dispatcher),默认是 describe.skip + 命中 vitest.config.ts 的
exclude: ['**/*.int.test.ts'] 双重挡住,讲师现场演示前手动把 describe.skip 改回
describe 再跑,跑完记得改回去——不是学生日常 npm test 会碰到的路径。
npm test 说明:离线单测 vs 需要真栈的集成测试
vitest.config.ts 定义了默认 npm test 的范围:
test: { include: ['connector/test/**/*.test.ts', 'experiments/**/*.test.ts'], exclude: ['**/*.int.test.ts'] }默认
npm test:跑connector/test/**/*.test.ts(10 个文件:identity/risk/limits/approval/quarantine五个源码文件各自的单测——这五个文件把层1–层4 的安全 中间件落地成了五份代码,"四层"是教学模型,"五个文件"是实现,不是两套不同的东西——外加fakeErpClient/naiveTool/smoke/tools/server-read-quarantine五个 dispatcher/ 基础设施测试)加上experiments/**/*.test.ts(Demo C/D/F 的离线脚本,共 3 个文件,都基于FakeErpClient)。全程不碰真实 ERPNext,不需要docker ps。本次任务实测:13 个 文件,79 个测试,全绿(栈当前正跑着,未受影响)。被排除的
*.int.test.ts:connector/test/erpClient.int.test.ts(HttpErpClient对 真实 ERPNext)和experiments/scripts/demoAB.int.test.ts(Demo A/B 的真栈对照)都命中.int.test.ts后缀,被exclude挡在默认npm test之外;两个文件内部还各自套了一层describe.skip作为第二道保险。需要真的跑它们,走上面"六个 demo 怎么跑"里 Demo A/B 那 一行的手动解 skip 流程,不是切个 config 就完事。一处已经修好的坑,写在这里省得学生去翻旧报告:
package.json曾经有一条npm run test:int脚本,指向一个仓库里从未存在过的vitest.integration.config.ts,跑起来 直接报Could not resolve .../vitest.integration.config.ts退出——这条死脚本已经被移除 (commit8673d5a),package.json里现在只有test/test:watch/typecheck/server四条。如果你看到的教学材料还提这条命令,那是过时的;上面描述的手动解describe.skip流程才是当前唯一验证过、真的能跑通.int.test.ts的路径。
npm test # 默认:离线,13 文件 / 79 测试
npm run typecheck # tsc --noEmit
npm run --silent server # 启动 MCP server(stdio)——--silent 不可省,见 agent/claude_code.md三道 anti-shallow-fork 质量闸
gates/ 下三份文件是喂给一个 evaluator(subagent 或人)的判据文件,各管一段,互不重复,
但引用同一批本仓源码作证据:
gates/architecture_gate.md—— AG-1~AG-4:授权是不是委托给宿主判、身份是不是真的 降权、检查是否结构性地先于执行(要求学生把顺序对调,粘一份变红的测试输出)、层与层是否 真的独立可测。AG-1/AG-2/AG-3 任一 FAIL,整个闸 FAIL。gates/safety_gate.md—— SG-1~SG-4:拦截是否发生在代码里(不是"模型这次没上当")、 转义堵的是一整类字符还是几个已知字符串、测试是不是能在 revert 之后真的变红、host 数据 能到模型面前的每一条通道是否都被枚举过(含错误通道,不只是正常数据)。四条全过才算 PASS。gates/correctness_gate.md—— CG-1~CG-3:幂等键是否只来自业务语义字段、写后回查是不是 真的在跑而不是死代码、"调用失败"是否被误判成"什么都没发生"。三条全过才算 PASS,且 CG-1 过不代表 CG-3 自动过,两个是独立失败模式。
三道闸的共同要求:不接受没有 file:line 引用的 PASS,证据必须可重跑(测试输出、
trace、revert-and-fail 对比),不接受"我们人工试了几次都没问题"这种采样当证明。
experiments/demo_e_anti_shallow_fork/README.md 是把三道闸走一遍的 worked example——
拿本仓自己的反面教材(connector/src/naiveTool.ts + 一段专门写来示范"说得好听但没代码"的
系统提示词)和本仓自己的架构级实现做对照,11 条子判据逐条打分,浅 fork 全 FAIL,本仓全
PASS。用它当模板去评一份真实的学生 fork。
仓库结构
docker/ ERPNext v15 + HRMS 靶场(Makefile: up / create-site / seed / down / nuke / logs)
seed/ 角色化用户 + 主数据 + 工资 fixture + 双语投毒 draft 采购发票
connector/src/ agent 化层:identity/risk/limits/approval/quarantine 五个源码文件(对应
教学模型里的层1–层4)+ tools.ts 的 Dispatcher + server.ts(MCP server 接线)
+ naiveTool.ts(Demo A 反面教材)
connector/test/ 上面每一层各自的单测(+ 2 个 *.int.test.ts,见"npm test 说明")
experiments/ 六个 demo 的 runbook(A–F)+ 离线可跑脚本(experiments/scripts/)
gates/ 三道 anti-shallow-fork 质量闸的判据文件
agent/ 两个前端的接入说明:claude_code.md(已验证)/ openworker.md(已验证,
2026-08-01 两轮:MCP 客户端代码 + 无头二进制 REST API(含 GUI 背后同一
条路径),不含 Tauri 桌面 GUI 本身的界面/点击交互)快速核对清单(从干净 checkout 到能跑第一个 demo)
npm install(根目录)。cd docker && cp .env.example .env。make up(几秒)→make create-site(等,见上)→make seed。npm test(根目录,离线,应为全绿)—— 到这一步 Demo C/D/E/F 已经能跑。想跑 Demo A/B:装
curl/jq/python3,照experiments/demo_a_naive_admin_key.md的 "前提"小节确认 9 个l9-erpnext-*容器Up。想接 Claude Code:照
agent/claude_code.md写.mcp.json,claude里/mcp确认 5 个erp.*工具在列。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityCmaintenanceA secure, audit-logged MCP server for Frappe that exposes specific DocTypes and operations to AI agents with granular permissions and field-level control.Last updated3MIT
- Alicense-qualityBmaintenanceEnables ERPNext management, file operations, read-only database access, and ERPNext API integration through a standardized MCP server.Last updated4MIT
- Flicense-qualityCmaintenanceA governed MCP server for integrating AI agents with customer data, featuring role-based access control, field redaction, and human-in-the-loop approval for secure support operations.Last updated1
- Alicense-qualityDmaintenanceA comprehensive MCP server for ERPNext providing generic, doctype-agnostic access to any ERPNext document type with robust permission controls, audit logging, and enterprise-grade security.Last updatedMIT
Related MCP Connectors
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
MCP Server for agents to onboard, pay, and provision services autonomously with InFlow
MCP server teaching AI agents to implement TideCloak: auth, E2EE, IGA, security analysis
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mrvgao/parallight-lecture-9-agentify'
If you have feedback or need assistance with the MCP directory API, please join our Discord server