agent-work-layer-mcp

中文文档
0.9.1发布于 2026/11/03Apache-2.0TypeScriptES Module构建来源已验证

MCP server for Agent Work Layer: hand Work Orders between agent sessions and get checked Work Reports back.

原始使用文档
文档目录12 节
  1. Use with Claude Code
  2. Command
  3. Tools
  4. Shared desk over HTTP
  5. Bring your own authentication
  6. Gateway: enforce an order while the worker works
  7. Staged tools: run writes only after approval
  8. Delegation Passes
  9. Embed the server
  10. Upgrading from agent-work-layer 0.1
  11. Security
  12. License

agent-work-layer-mcp

The Agent Work Layer desk as an MCP server. Agent sessions hand each other Work Orders and get checked Work Reports back.

npm license

Two ways to run it:

  • stdio, for development. Each agent session runs its own desk process. Sessions share one folder, and each is locked to the identity it is started with.
  • HTTP, for shared use. Every agent connects to one desk, and each request's credential decides who it acts as, so a worker cannot approve its own work. See Shared desk over HTTP. It can also sign Delegation Passes.

Only need to validate documents or run a desk in your own code? Use agent-work-layer on its own. It installs 7 packages instead of about 100.

Use with Claude Code

Add it once in the manager's project folder and once in the worker's:

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

Any MCP client that starts stdio servers works the same way.

To enforce an order while the worker works, and to hold back its staged writes until the manager approves, give the worker its real tools through the gateway instead of directly:

claude mcp add work -e AWL_STORE="$HOME/.awl" -- npx -y agent-work-layer-mcp gateway --order wo_123 --config upstream.json

Command

npx -y agent-work-layer-mcp --store ./.awl --actor agent:worker
FlagEnvironmentDefaultMeaning
--store <dir>AWL_STORE./.awlFolder shared by every session
--actor <ref>AWL_ACTORnoneIdentity this session is locked to

Flags win over the environment. Installed locally, the command is awl-desk.

Tools

ToolWhoWhat it does
create_work_ordermanagerIssue an order: goal, criteria, budget, deadline, tools, optional staged tools, handoff, and continues to reissue a finished order
list_work_ordersanyoneFind orders, e.g. to=<you>, state=offered, or continues=<id> to follow a reissued job
get_work_orderanyoneOrder, state, latest report, findings, events; the worker also gets its pass
accept_work_order / decline_work_orderworkerAnswer an offer
start_work_orderworkerBegin work
report_progressworkerPost a progress note, optionally with percent done; changes no state
submit_work_reportworkerReport done, blocked or failed, with evidence, staged effects and an optional handoff
answer_questionmanager or escalation contactUnblock a worker
review_work_reportmanagerApprove (staged effects may then run, or only the effects named), or send back for rework (they are dropped)
allow_effect_retrymanagerAfter approval, let the gateway run once more an effect that never happened (settled not_run, or skipped)
cancel_work_ordermanagerStop an unfinished order
wait_for_updateanyoneWait for the next change to an order, progress notes included
get_track_recordanyoneA worker's record on this desk: outcomes, reworks, reports, spending against budget
renew_work_passworkerA fresh Delegation Pass; listed only when the desk issues passes

When a worker fails or gets stuck, it can fill in handoff (decisions, what it tried, open questions, next step). The manager passes that to the next worker in a new order, so the next worker doesn't start from nothing. See SPEC.md §4.4.

Shared desk over HTTP

npx -y agent-work-layer-mcp serve --keys keys.json --port 8787

keys.json maps API keys to agent refs. Generate long random keys (e.g. openssl rand -hex 32) and keep the file out of version control:

{ "keys": { "4f9c…": "agent:manager", "a71e…": "agent:worker" } }

Each agent connects to http://127.0.0.1:8787/mcp with its own key:

claude mcp add --transport http awl http://127.0.0.1:8787/mcp --header "Authorization: Bearer $AWL_KEY"
  • A request without a valid key gets 401 and runs nothing.
  • The key decides the actor. An actor argument naming anyone else gets FORBIDDEN, so a worker can't approve its own report or cancel its manager's order.
  • Every request is authenticated, not only the first; the desk keeps no sessions.
  • The desk listens on 127.0.0.1. Put a TLS proxy in front before exposing it.
FlagEnvironmentDefaultMeaning
--keys <file>requiredAPI key → agent ref map
--port <n>87870 picks a free port
--host <addr>127.0.0.1Address to listen on
--store <dir>AWL_STORE./.awlThe desk's folder
--pass-key <file>nonePrivate JWK to sign Delegation Passes with
--issuer <iss>noneThe passes' iss; required with --pass-key

Bring your own authentication

In code, pass an authenticate(request) hook. It returns the agent ref, or throws an AuthError (401 by default, or { status: 403 } for a valid credential that is not an agent). Any other error also refuses the request, without showing its message.

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);

For OAuth 2.1 access tokens, as the MCP authorization spec describes, use bearerAuth. You verify the token's signature and issuer (here with jose); the desk then checks that it has an exp in the future and an aud that includes the desk, and maps sub to an agent 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"] },
});

With resourceMetadata, the desk serves it at /.well-known/oauth-protected-resource/mcp, and 401 answers point clients there so they can find the authorization server. A token whose subject maps to no agent gets 403. The desk does not issue tokens or store keys; see SPEC.md §10.1.

Gateway: enforce an order while the worker works

The desk checks tools and budget when the report arrives. The gateway enforces them at every tool call. It sits between the worker and its real MCP servers, for one order:

claude mcp add work -e AWL_STORE="$HOME/.awl" -- npx -y agent-work-layer-mcp gateway --order wo_123 --config upstream.json

upstream.json lists the real servers, in the same shape as .mcp.json:

{ "mcpServers": {
    "filings": { "command": "npx", "args": ["-y", "filings-mcp"] },
    "calc": { "url": "https://calc.example.com/mcp" } } }
  • Tools appear as <server>__<tool>, and only those the order's tools allow. Calls to others are refused.
  • Calls are refused once budget.tool_calls or budget.seconds is spent, and while the order is not working. Tool calls the order's sub-orders hold don't count as its own: with 20 tool calls and a sub-order holding 5, the order itself may make 15. An accepted order is started on its first call.
  • Each result ends with an evidence ref such as filings/search#3. Cite it as { "type": "tool_call", "ref": "filings/search#3" }.
  • awl_usage returns tools_used, cost and staged effects to copy into submit_work_report, and every call so far.
  • Calls are logged to <store>/calls/<id>.jsonl. The log is reloaded on restart, so restarting does not reset the budget.
FlagEnvironmentDefaultMeaning
--order <id>requiredThe order to enforce
--config <file>requiredThe real MCP servers
--store <dir>AWL_STORE./.awlThe desk's folder; the call log goes in calls/
--actor <ref>AWL_ACTORthe order's workerMust be the order's worker
--observeoffLog only, refuse nothing (see below)
--commitoffRun the approved report's staged effects, print the results and exit (see below)

Already enforce access with your own gateway or policy engine? Run with --observe (in code, enforce: false). The gateway then lists every tool and refuses nothing. It still numbers and logs every call and reports usage. Calls the order would have refused are logged with a violation (TOOL_NOT_ALLOWED, BUDGET_SPENT, ORDER_NOT_WORKING or PENDING_REFERENCE), and the desk still catches them in the report (R5, R7).

The gateway cannot measure usd or tokens; the desk still checks those when the report arrives. See SPEC.md §9.1.

Each logged call, and each result's _meta, also records the authority it ran under: the order id (awl/order), the report attempt in progress (awl/attempt) and, when the desk issues passes, the pass's jti (awl/jti).

Staged tools: run writes only after approval

The worker's read goes through the gateway at once; its staged write is held and logged, and the worker gets pending:gmail/send#3. The report lists it in effects; on approve, --commit runs it once; on rework it is dropped

An order's staged tools are ones whose calls should wait for the manager's review (SPEC.md §4.6). The gateway doesn't forward them:

  1. The worker calls gmail__send. The gateway counts the call against tool_calls, logs it with its arguments, and returns pending:gmail/send#4 instead of a result (_meta["awl/staged"] is true). Other tools run as usual.
  2. awl_usage lists the calls waiting as effects. The worker copies the ones it wants into its report, in the order they should run.
  3. The manager reviews the report, effects included. Rework drops them all; the worker stages again if it still wants them. A blocked report keeps them.
  4. Once the order is approved, run them:
npx -y agent-work-layer-mcp gateway --order wo_123 --config upstream.json --commit

--commit (in code, gateway.commit()) runs nothing unless the order is approved and every effect to run matches a staged call in the log: same ref, tool and arguments, made since the last rework. If the manager approved only some effects, only those run. It runs them in the report's order and logs each result with commit: true. A failed effect is recorded, not retried, and the effects after it still run. Running --commit again runs nothing twice. Each call carries the effect's ref and order id in _meta["awl/ref"] and _meta["awl/order"], so the receiving service can drop a duplicate and you can look the effect up there. If a call gets no answer (the connection drops or times out), the effect's outcome is unknown: it may have happened, so it is never run again; check the receiving service. Before each call --commit also logs a running line, so if the process dies mid-call, the next --commit records that effect as unknown too instead of running it again. While a commit runs, a <log>.commit-lock file keeps a second one from starting. The lock records the process holding it, so if that process has died, the next run on the same machine takes it over; a lock held by a live process, or from another machine, is left alone. Staged calls the report leaves out never run. Each run also records its outcomes on the order as a commit event, so the manager sees them.

To use an earlier staged call's result, pass a reference as an argument when making the staged call: {"$ref": "github/create_issue#3", "pointer": "/structuredContent/number"}. --commit fills it in from that effect's result, a CallToolResult. If that effect failed, was skipped or has an unknown outcome, or has nothing at the pointer, the effect using it is skipped (outcome: "skipped", with the reason). When an effect's outcome is unknown. Find out from the receiving service (look for the effect's awl/ref), then record it (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"

Settling runs nothing. To run an effect again, the manager allows it with allow_effect_retry (desk.allowRetry()), and then --retry (gateway.retry()) runs it once, only if it is settled not_run or was skipped. The retry rechecks the approval and the staged call, and sends the same awl/ref with _meta["awl/try"]: 2, so a receiver that dedupes still drops a duplicate.

A reference to a staged call in a call to a tool that is not staged is refused with PENDING_REFERENCE, since that result exists only after approval; the refused call doesn't count against tool_calls.

Delegation Passes

A pass is the order's tools, budget and deadline signed by the desk, so a gateway or another team's system can check a worker's authority without reading the desk (SPEC.md §10.2).

The shared desk issues them when given a private key (a P-256 or Ed25519 JWK):

npx -y agent-work-layer-mcp serve --keys keys.json --pass-key priv.json --issuer https://desk.example.com
  • start_work_order and get_work_order return the worker's current pass. Nobody else sees it.
  • A pass lasts 15 minutes, never past the deadline. renew_work_pass gets a fresh one while the order is working; a new one is also issued after an answer or a rework.
  • When the order is cancelled, expires or leaves working, no new passes are issued.

The gateway can then work from the pass instead of the desk's store. Give it the desk's public key and the pass:

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
  • Every call needs a valid pass for this order and worker. Without one, or with one that is expired, badly signed, or for another worker, order or issuer, the call is refused with PASS_INVALID.
  • Tools and tool_calls come from the pass. seconds, and the tool calls sub-orders hold, are left to the desk's report checks, since a pass doesn't say when work started or what sub-orders exist.
  • --pass-file is read on every call, so the worker renews by writing the new pass there. AWL_PASS works for a fixed pass. A call's _meta["awl/pass"] wins over both.
FlagEnvironmentMeaning
--pass-keys <file>The desk's public key: a JWK, a JWK Set or an array
--pass-file <file>AWL_PASS (the pass itself)Where the current pass is
--issuer <iss>Required iss

In code: createGateway({ orderId, upstreams, actor, passKeys, pass, issuer }), with no desk.

Embed the server

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

The gateway takes connected MCP clients for the real servers:

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();

Upgrading from agent-work-layer 0.1

0.10.2
npx -y agent-work-layer desknpx -y agent-work-layer-mcp
import { createDeskServer } from "agent-work-layer/mcp"import { createDeskServer } from "agent-work-layer-mcp"

Stored orders keep working. The desk reads 0.1 documents and writes 0.2.

Security

Over stdio, the desk trusts the identity it is started with: anyone who can start a desk or write to the store folder can act as any agent. Use it for development. In production, run one desk over HTTP, which takes the actor from each request's credential. Delegation Passes are bearer tokens for a worker's authority: keep them short-lived (the default), and keep the signing key on the desk only. See SPEC.md §10.

License

Apache-2.0

版本与发布记录

agent-work-layer-mcp 共发布 13 个版本,首次发布于 2026/10/10;过去 12 个月发布了 13 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.1latest2026/11/03今天
0.9.02026/10/277 天前
0.8.12026/10/277 天前
0.8.02026/10/277 天前
0.7.12026/10/259 天前
0.7.02026/10/259 天前

关于 agent-work-layer-mcp 的常见问题

agent-work-layer-mcp 是什么?

代理工作层的MCP服务器:在代理会话之间手动工作订单并获得检查后的工作报告。

如何安装 agent-work-layer-mcp?

在项目目录运行 npm install agent-work-layer-mcp,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 awl-desk,可以用 npx awl-desk 直接运行。

agent-work-layer-mcp 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

agent-work-layer-mcp 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

agent-work-layer-mcp 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.11。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

agent-work-layer-mcp 可以商用吗?

agent-work-layer-mcp 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

agent-work-layer-mcp 还在维护吗?

最新版本 0.9.1 发布于 2026/11/03(今天);至今共发布 13 个版本,其中过去 12 个月发布了 13 个;首次发布于 2026/10/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。