Skip to main content
Glama
ls2212895

business-action-lab

by ls2212895

Business Action Lab

中文 · English · 演示回放 · 工程案例

一个本地运行的业务动作参考实现:Agent 可以查询合成工单、追加工作日志;业务服务验证权限、状态和重复请求。

要验证的问题:一次业务写入被重试时,怎样保持一条业务日志,并明确返回拒绝或冲突?

当前状态

  • Python + SQLite 业务服务、开发 CLI、两个 stdio MCP 工具已经运行验证。

  • 测试覆盖正常写入、权限拒绝、状态校验、重试、参数冲突、多个进程并发和事务失败。

  • MCP 演示启动真实服务子进程,验证工具发现、调用、错误标记和重启后的重复请求。

  • 另已完成真实 Codex CLI 模型调用:正常写入、提交后超时重试、只读身份拒绝,各观测一次。

  • 新增启动端固定操作编号的模式:模型只提供工单和正文,不能在重试时换键。

  • 当前本地版本为 0.2.0,56 项确定性测试通过。真实 Agent 演示记录模型调用;验收记录记录当前版本;首轮记录保留历史结果。

三步跑起来

需要 uv。在本目录运行:

uv sync --locked
uv run --locked pytest -q
uv run --locked bal-demo

Python 版本由 .python-version 指定为 3.12.13;依赖版本和下载校验值由 uv.lock 固定。 演示不需要模型密钥。每次演示在 .local/demo-<随机标识>/ 创建新的合成数据库、身份配置和 report.json,保留此前记录。

预期看到 12 条 PASS,末尾显示:

{
  "passed": true,
  "counts": {
    "work_orders": 3,
    "work_logs": 1,
    "idempotency_records": 1,
    "audit_events": 8
  }
}

报告记录原始 MCP 返回、工具定义、数据库回读和运行环境。只有业务写入实际成功且回读一致,演示才成功退出。

想看真实模型的行为,先读 提交后超时演示。其中的可选运行命令需要已有 Codex 登录并使用账号额度;基础演示不调用模型。

固定操作编号模式也有独立演示:

uv run --locked bal-demo --fixed-operation

预期 8 条 PASS,回读 2 条日志、2 条幂等记录、4 条审计。两条日志分别来自启动端明确选择的两个操作;同一操作的重试和重启不增加日志。

自己操作一次

初始化会保留现有业务日志和工单状态;不会重置数据库。

uv run --locked bal init
uv run --locked bal --identity examples/reader.json get-work-order WO-1001
uv run --locked bal --identity examples/operator.json add-work-log WO-1001 \
  --text '合成演示:检查完成。' --key my-operation-1
uv run --locked bal --identity examples/operator.json add-work-log WO-1001 \
  --text '合成演示:检查完成。' --key my-operation-1
uv run --locked bal inspect

两次写入返回相同 work_log_id、时间戳和 audit_idinspect 中只有一条业务日志。 把第二次正文改掉会得到 IDEMPOTENCY_CONFLICT;换成 reader.json 会得到 FORBIDDEN。 CLI 业务失败返回退出码 1,并输出 JSON 错误。inspect 是开发者回读入口,不向 MCP 暴露。

合成工单

状态 / 用途

WO-1001

开放,可用于合法写入

WO-1002

已关闭,用于验证业务拒绝

WO-1003

开放,但不在两种演示身份的授权范围内

WO-1004

身份配置允许查询该编号,但数据库中不存在,用于验证 NOT_FOUND

这些是本项目自行定义的演示规则,与真实企业系统无关。

接入一个现有 MCP 客户端

先运行 bal init。以项目虚拟环境中的 Python 启动 business_action_lab.mcp_server,提供数据库和身份文件的绝对路径

{
  "mcpServers": {
    "business-action-lab": {
      "command": "/absolute/path/business-action-lab/.venv/bin/python",
      "args": [
        "-m", "business_action_lab.mcp_server",
        "--db", "/absolute/path/business-action-lab/.local/lab.sqlite3",
        "--identity", "/absolute/path/business-action-lab/examples/reader.json"
      ]
    }
  }
}

这是通用 MCP 配置示例,具体配置位置和格式以所用客户端为准。模板保存在 examples/mcp-config.example.json。 初始示例选择只读身份。操作者若要演示合法写入,在启动配置中选择 operator.json 并重启服务。 不要同时把两个身份都当作可供模型切换的权限选项。

工具参数只有:

工具

参数

get_work_order

work_order_id

add_work_log

work_order_id, text, idempotency_key

服务拒绝多余参数。业务错误同时返回 MCP isError=true 和结构化的 ok=false / 错误码。 启动配置只读入一次;角色或范围变更需要重启服务。身份 JSON 是本地演示配置,不是远程身份凭据。

让启动端管理一次操作

对于已经明确的一次写入,在服务启动参数中增加 --operation-key example-operation-1。这时 add_work_log 只接受 work_order_idtext,键由适配层补入。模型尝试额外传入键会被拒绝。

启动者为每个独立操作分配唯一编号,并将它与任务一起保存;重试、重连和重启都使用原编号。只有明确开始另一个操作时才换号。服务不会推断两段自然语言是否表示同一意图,也不会在重启时自动分配新号。

这种配置一次承载一个写入意图,可多次查询和重试;通用多操作会话仍须由调用方管理各自的键。 参见 操作编号的生命周期配置示例

关键语义

  • reader 可查询授权范围内的工单;operator 可为范围内的开放工单追加日志。

  • 正文保留原样,允许 1–2000 个字符,拒绝纯空白、NUL 和无效 UTF-8。

  • 幂等键为 1–128 个 ASCII 字符,首字符为字母或数字,其后允许字母、数字、._:-

  • 同一主体 + 动作 + 键,相同正文和目标返回原结果;不同参数返回冲突。不同键表示不同操作。

  • 成功日志、幂等记录、成功审计在一个事务中提交。并发写入使用 SQLite 写锁和复合主键约束。

  • 工单随后关闭时,原请求仍能返回历史结果;新的请求被拒绝。权限校验在重复请求检查之前执行。

  • 查询完全只读;成功重试引用原审计。进入业务服务的拒绝和失败写入尝试会尽力单独记录审计。

详细取舍、失败边界见 设计说明

适用边界

全部数据均为合成数据。拥有宿主机 Shell、配置或数据库控制权的客户端能绕过这些工具;本项目不提供这种隔离。 幂等保证仅限当前本地数据库,不覆盖外部系统或分布式“恰好一次”。

这是使用 AI 辅助完成的个人参考实现,署名 ls2212895。首轮只做有界验证,不承诺即时维护。 许可文本见 MIT 许可证,实现与依赖来源见说明。 外部复现和同行回复不作为验收条件。可直接阅读工程案例,了解问题、取舍与证据。

展示与讲解

仓库准备了 GitHub 自动检查配置,运行测试和两套确定性演示,不调用模型。远程检查尚未运行,已验证平台仍以验收记录为准。

框架事实依据:FastMCP 授权工具定义stdio 客户端。 代码没有依赖 stdio 下的 OAuth 授权检查;角色和工单权限始终由业务服务判断。

Latest Blog Posts

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/ls2212895/business-action-lab'

If you have feedback or need assistance with the MCP directory API, please join our Discord server