agent-work-layer-mcp
这个 Agent工作层 作为MCP服务器。 代理会话相互传递工作订单并获得检查后的工作报告。
运行它的两种方法:
- 工作室,用于开发。 每个代理会话运行自己的桌面进程。会话共享一个文件夹,每个会话都被锁定到它开始时的身份。
- HTTP,用于共享使用。 每个代理连接到一个桌面,每个请求的凭据决定它作为谁,因此工作人员不能批准自己的工作。看到 基于HTTP的共享桌面. 它也可以签名 代表团传递.
只需要在自己的代码中验证文档或运行桌面?使用 agent-work-layer 它自己。它安装了7个软件包,而不是大约100个。
与Claude代码一起使用
将它分别添加到经理的项目文件夹和工作人员的文件夹中:
claude mcp add awl -e AWL_STORE="$HOME/.awl" -e AWL_ACTOR=agent:manager -- npx -y agent-work-layer-mcp
claude mcp add awl -e AWL_STORE="$HOME/.awl" -e AWL_ACTOR=agent:worker -- npx -y agent-work-layer-mcp
任何启动工作室服务器的MCP客户端都以相同的方式工作。
在工人工作时执行命令,并阻止命令的执行 上演了 写直到经理批准,给工人其真正的工具通过 网关 而不是直接:
claude mcp add work -e AWL_STORE="$HOME/.awl" -- npx -y agent-work-layer-mcp gateway --order wo_123 --config upstream.json
命令
npx -y agent-work-layer-mcp --store ./.awl --actor agent:worker
| 国旗 | 环境 | 默认的 | 意义 |
|---|---|---|---|
--store <dir> | AWL_STORE | ./.awl | 每个会话共享的文件夹 |
--actor <ref> | AWL_ACTOR | 无 | 此会话被锁定到的标识 |
旗帜战胜了环境。在本地安装时,命令为 awl-desk.
工具
| 工具 | 谁 | 它是做什么的 |
|---|---|---|
create_work_order | 经理 | 发布订单:目标,标准,预算,截止日期,工具,可选 staged 工具, handoff, continues 重新发出已完成的订单 |
list_work_orders | 任何人 | 查找订单,例如: to=<you>, state=offered,或者 continues=<id> 跟踪重新发布的作业 |
get_work_order | 任何人 | 秩序、状态、最新报告、调查结果、事件;工人也得到了报酬 通过 |
accept_work_order / decline_work_order | 工人 | 接受工作邀请 |
start_work_order | 工人 | 开始工作 |
report_progress | 工人 | 张贴进度记录,可选择标注完成百分比;不改变状态 |
submit_work_report | 工人 | 报告 done, blocked 或者 failed,有证据,上演 effects 还有一个可选的 handoff |
answer_question | 经理或升级联系人 | 解除一个worker的阻塞 |
review_work_report | 经理 | 然后,批准(阶段)效果可以运行,或者只运行 effects 命名),或者发回重做(它们被丢弃) |
allow_effect_retry | 经理 | 批准后,让门户再次运行,从未发生过的效果(解决) not_run,或跳过) |
cancel_work_order | 经理 | 停止未完成的订单 |
wait_for_update | 任何人 | 等待订单的下一次更改,包括进度记录 |
get_track_record | 任何人 | 这张桌子上是员工的记录:成果、返工、报告、预算支出 |
renew_work_pass | 工人 | 一个新鲜的 代表团通过;只有在办公桌发出通行证时才会列出 |
当一个工人失败或陷入困境时,它可以填补 handoff (决定,尝试了什么,开放的问题,下一步)。经理将新订单传递给下一个工人,这样下一个工人就不会从零开始。看到 SPEC.md§4.4.
基于HTTP的共享桌面
npx -y agent-work-layer-mcp serve --keys keys.json --port 8787
keys.json 将API密钥映射到代理引用。生成长随机密钥(例如: openssl rand -hex 32),并保持文件不受版本控制:
{ "keys": { "4f9c…": "agent:manager", "a71e…": "agent:worker" } }
每个代理连接到 http://127.0.0.1:8787/mcp 有自己的钥匙:
claude mcp add --transport http awl http://127.0.0.1:8787/mcp --header "Authorization: Bearer $AWL_KEY"
- 没有有效密钥的请求得到401,并且不运行。
- 关键决定演员。一个
actor给别人命名FORBIDDEN,因此员工不能批准自己的报告,也不能取消经理的订单。 - 每个请求都经过身份验证,不仅仅是第一个请求;这张桌子没有会议。
- 桌子在监听
127.0.0.1. 在暴露TLS代理之前,将其放在前面。
| 国旗 | 环境 | 默认的 | 意义 |
|---|---|---|---|
--keys <file> | 要求 | API key→代理refmap | |
--port <n> | 8787 | 0 选择一个空闲端口 | |
--host <addr> | 127.0.0.1 | 收听地址 | |
--store <dir> | AWL_STORE | ./.awl | 桌子上的文件夹 |
--pass-key <file> | 无 | 私人JWK签名 代表团传递 与 | |
--issuer <iss> | 无 | 通过的 iss;需要 --pass-key |
带上你自己的身份证明
在代码中,传递an authenticate(request) 钩。它返回代理ref,或者抛出 AuthError (默认为401,或 { status: 403 } 对于不是代理的有效凭证)。任何其他错误也拒绝请求,而不显示其消息。
import { createServer } from "node:http";
import { createDeskHttpHandler, apiKeyAuth } from "agent-work-layer-mcp";
const managerKey = process.env.MANAGER_KEY;
if (!managerKey) throw new Error("MANAGER_KEY is not set");
const handle = createDeskHttpHandler({ desk, authenticate: apiKeyAuth({ [managerKey]: "agent:manager" }) });
createServer(handle).listen(8787);
对于OAuth 2.1访问令牌,如 MCP授权规范 描述,使用 bearerAuth. 您验证令牌的签名和发行者(这里使用 穆); 然后桌子检查它是否有 exp 在未来和将来 aud 包括桌子和地图 sub 对代理ref:
import { createRemoteJWKSet, jwtVerify } from "jose";
import { bearerAuth, createDeskHttpHandler, resourceMetadataUrl } from "agent-work-layer-mcp";
const resource = "https://desk.example.com/mcp";
const jwks = createRemoteJWKSet(new URL("https://auth.example.com/.well-known/jwks.json"));
const handle = createDeskHttpHandler({
desk,
authenticate: bearerAuth({
verify: async (token) => (await jwtVerify(token, jwks, { issuer: "https://auth.example.com" })).payload,
audience: resource,
agents: (claims) => (claims.sub ? `agent:${claims.sub}` : undefined), // or a map { "<sub>": "agent:manager" }; default: sub as is
resourceMetadataUrl: resourceMetadataUrl(resource),
}),
resourceMetadata: { resource, authorization_servers: ["https://auth.example.com"] },
});
与 resourceMetadata,桌子在 /.well-known/oauth-protected-resource/mcp, 401个答案将客户端指向那里,以便他们可以找到授权服务器。主题不映射到任何代理的令牌得到403。桌面不发放令牌或存储密钥;看到 SPEC.md§10.1.
网关:在工人工作时执行命令
前台检查 tools 和 budget 当报告到达时。网关在每次工具调用时都强制执行它们。对于一个订单,它位于worker和它的真实MCP服务器之间:
claude mcp add work -e AWL_STORE="$HOME/.awl" -- npx -y agent-work-layer-mcp gateway --order wo_123 --config upstream.json
upstream.json 列出实际服务器,形状与 .mcp.json:
{ "mcpServers": {
"filings": { "command": "npx", "args": ["-y", "filings-mcp"] },
"calc": { "url": "https://calc.example.com/mcp" } } }
- 工具显示为
<server>__<tool>,而且只有那些有秩序的人tools允许的。呼叫其他人被拒绝。 - 电话被拒绝一次
budget.tool_calls或者budget.seconds都花了,而命令却没有working. 订单的子订单持有的工具调用不算作自己的工具调用:有20个工具调用和一个子订单持有5个,订单本身可能有15个。一个accepted订单在第一次调用时开始。 - 每个结果都以证据ref结尾,例如
filings/search#3. 引用如下{ "type": "tool_call", "ref": "filings/search#3" }. awl_usage返回tools_used,cost和举行effects复制到submit_work_report到目前为止的每一个电话。- 通话记录为
<store>/calls/<id>.jsonl. 日志在重新启动时重新加载,因此重新启动不会重置预算。
| 国旗 | 环境 | 默认的 | 意义 |
|---|---|---|---|
--order <id> | 要求 | 执行命令 | |
--config <file> | 要求 | 真正的MCP服务器 | |
--store <dir> | AWL_STORE | ./.awl | 桌子上的文件夹;通话记录进去了 calls/ |
--actor <ref> | AWL_ACTOR | 订单的工作人员 | 一定是订单的工作人员 |
--observe | 从 | 只记录,不拒绝任何东西(见下文) | |
--commit | 从 | 运行已批准的报告的分段效果,打印结果并退出(见下文) |
已经使用您自己的网关或策略引擎强制访问了吗? 运行 --observe (在代码中, enforce: false). 网关然后列出每个工具,没有拒绝任何东西。它仍然记录和记录每一次通话,并报告使用情况。命令将拒绝的呼叫记录为 violation (TOOL_NOT_ALLOWED, BUDGET_SPENT, ORDER_NOT_WORKING 或者 PENDING_REFERENCE),而桌子上的报告中仍然捕捉到了它们(R5, R7)。
大门无法测量 usd 或者 tokens;当报告到达时,前台仍然会检查这些。看到 SPEC.md§9.1.
每个记录在案的电话,每个结果 _meta,还记录其运行的权限:订单id (awl/order),正在进行的报告尝试(awl/attempt),当桌子发出通行证时,通行证就是通行证 jti (awl/jti)。
分级工具:只在批准后运行写操作
一个订单的 staged 工具的调用应该等待经理的审查(SPEC.md§4.6). 网关不转发它们:
- 工人呼叫
gmail__send. 网关将该呼叫计入tool_calls,记录其参数,并返回pending:gmail/send#4而不是结果(_meta["awl/staged"]是true). 其他工具照常运行。 awl_usage将正在等待的呼叫列表为effects. 工作程序按照它们应该运行的顺序将它们复制到报告中。- 经理审阅报告,包括效果。 返工 把它们都丢掉;如果工人仍然需要它们,则再次进入阶段。一个
blocked报告保存它们。 - 一旦秩序井然 批准,运行它们:
npx -y agent-work-layer-mcp gateway --order wo_123 --config upstream.json --commit
--commit (在代码中, gateway.commit())不运行任何东西,除非订单被批准,并且运行的每个效果都与日志中的阶段调用相匹配:自上次返工以来所做的相同的ref, tool和arguments。如果经理只批准了一些效果,则只运行这些效果。它按照报告的顺序运行它们,并记录每个结果 commit: true. 失败的效果将被记录,而不会重试,并且之后的效果仍然运行。运行 --commit 再次不运行两次。每个调用都带有效果的ref和order id in _meta["awl/ref"] 和 _meta["awl/order"],所以接收服务可以删除一个副本,你可以在那里查看效果。如果调用没有得到应答(连接断开或超时),则效果的结果为 unknown:它可能已经发生了,所以它永远不会再运行;检查接收服务。每次通话前 --commit 还记录了 running 行,所以如果进程在调用过程中死亡,下一个 --commit 将效果记录为 unknown 而不是再运行一次。当提交运行时,一个 <log>.commit-lock 文件阻止第二个启动。锁记录了持有它的进程,因此,如果该进程已经死亡,在同一台机器上的下一次运行将接管它;由活动进程或其他机器持有的锁将被单独放置。报告里遗漏的临时电话从来都没打过。每次运行还将其结果记录在订单上 commit 事件,以便经理看到它们。
要使用先前阶段调用的结果,在进行阶段调用时将引用作为参数传递: {"$ref": "github/create_issue#3", "pointer": "/structuredContent/number"}. --commit 从效果的结果a中填充它 CallToolResult. 如果该效果失败,被跳过或有未知的结果,或者指针上没有任何内容,则使用它的效果将被跳过(outcome: "skipped",有理由)。 当一种影响的结果是未知的。 从接收服务中找出(寻找效果) awl/ref),然后记录下来(SPEC.md§6.4):
# It happened; optionally give its result so later effects that use it can run.
awl-desk gateway --order wo_1 --config servers.json --settle github/create_issue#3 --outcome ok --note "issue #812 has awl/ref github/create_issue#3"
# It did not happen.
awl-desk gateway --order wo_1 --config servers.json --settle github/create_issue#3 --outcome not_run --note "no issue with that awl/ref"
和解是没有用的。要再次运行效果,管理器允许使用 allow_effect_retry (desk.allowRetry()),然后 --retry (gateway.retry())运行一次,只有在它被解决的情况下 not_run 或者被跳过了。重试重新检查批准和阶段调用,并发送相同的请求 awl/ref 与 _meta["awl/try"]: 2,因此,重复数据删除的接收方仍然会丢失一个副本。
在对工具的调用中对阶段调用的引用 不 上演是被拒绝的 PENDING_REFERENCE,因为该结果只有在获得批准后才存在;被拒绝的电话不算数 tool_calls.
代表团传递
通行证是由工作台签名的订单工具、预算和截止日期,因此网关或另一个团队的系统可以在不读取工作台的情况下检查工人的权限(SPEC.md§10.2)。
共享桌面在给定私钥(P-256或Ed25519 JWK)时发出它们:
npx -y agent-work-layer-mcp serve --keys keys.json --pass-key priv.json --issuer https://desk.example.com
start_work_order和get_work_order返回工人的电流pass. 没有人看到它。- 通行证持续15分钟,永远不会超过截止日期。
renew_work_pass点完餐再来一杯新鲜的working;在回答或返工后也会发新的。 - 订单被取消、到期或离开
working,不会发出新的通行证。
然后,网关可以从通行证而不是桌面的存储中工作。给它桌子的公钥和通行证:
npx -y agent-work-layer-mcp gateway --order wo_123 --config upstream.json --actor agent:worker \
--pass-keys pub.json --pass-file pass.txt --issuer https://desk.example.com
- 每个调用都需要此订单和worker的有效通行证。如果没有,或者有一个过期的,签名错误的,或者是另一个工人,订单或发行者,呼叫将被拒绝
PASS_INVALID. - 工具和
tool_calls从关口过来。seconds,以及该工具调用子订单保持,都留给了桌面的报告检查,因为通过并没有说明工作何时开始或存在哪些子订单。 --pass-file在每次调用时都读取,因此worker通过在那里写入新通行证来更新。AWL_PASS适用于固定的通行证。一个电话的_meta["awl/pass"]赢得了双方的支持。
| 国旗 | 环境 | 意义 |
|---|---|---|
--pass-keys <file> | 桌面的公钥:一个JWK、一个JWK集合或一个数组 | |
--pass-file <file> | AWL_PASS (通行证本身) | 电流通道在哪里 |
--issuer <iss> | 要求 iss |
在代码: createGateway({ orderId, upstreams, actor, passKeys, pass, issuer }),没有 desk.
嵌入服务器
import { WorkDesk, FileStore } from "agent-work-layer";
import { createDeskServer } from "agent-work-layer-mcp";
const server = createDeskServer({ desk: new WorkDesk({ store: new FileStore("./.awl") }), actor: "agent:manager" });
await server.connect(transport); // any MCP transport
网关将连接的MCP客户端作为实服务器:
import { createGateway } from "agent-work-layer-mcp";
const { server, usage, commit } = await createGateway({ desk, orderId: "wo_123", upstreams: { filings: filingsClient }, logFile: "./calls.jsonl" });
await server.connect(transport);
// After the manager approves: run the report's staged effects.
const results = await commit();
从代理工作层0.1升级
| 0.1 | 0.2 |
|---|---|
npx -y agent-work-layer desk | npx -y agent-work-layer-mcp |
import { createDeskServer } from "agent-work-layer/mcp" | import { createDeskServer } from "agent-work-layer-mcp" |
存储的订单继续工作。办公桌读取0.1个文档,写入0.2个文档。
安全
在工作室中,桌面信任它启动时的身份:任何可以启动桌面或写入存储文件夹的人都可以充当任何代理。将其用于开发。在生产环境中,运行 HTTP上的一张桌子,它从每个请求的凭据中获取参与者。授权通行证是工作人员权限的承载令牌:使它们保持短暂(默认值),并将签名密钥仅保留在桌面上。看到 SPEC.md§10.
许可证
apache - 2.0