agent-work-layer
Work Orders and Work Reports for AI agents. A typed SDK, validator and lifecycle engine for handing work from one agent to another, with checkable results. The MCP server is a separate package, agent-work-layer-mcp.
Power your agent
คำสั่งของผู้เผยแพร่เอง ไม่ใช่เอนด์พอยต์ของ PlugMCP
Host a copy with our keys and logs.
npx -y [email protected]Claude Code ไม่มีลิงก์ติดตั้งสำหรับคำสั่งแบบโลคัล ให้คัดลอกคำสั่งด้านล่างแล้วรันในเทอร์มินัล
claude mcp add agent-work-layer -- npx -y agent-work-layer@0.9.1รันในเทอร์มินัล
{ "mcpServers": { "agent-work-layer": { "command": "npx", "args": [ "-y", "[email protected]" ] } }}claude_desktop_config.json
URL แบบรีโมตหรือคำสั่งบนเครื่องของคุณ? การติดตั้งสองแบบต่างกันอย่างไร
รันบน PlugMCP
A new server you create. It is not this listing's official endpoint. Use the publisher install when you want their URL and their credentials.
เปิดจากผู้เผยแพร่
Work Orders and Work Reports for AI agents. A typed SDK, validator and lifecycle engine for handing work from one agent to another, with checkable results. The MCP server is a separate package, agent-work-layer-mcp.
Work Orders and Work Reports for AI agents: a typed SDK, validator and lifecycle desk. The MCP server is in agent-work-layer-mcp.
Agent Cards say who an agent is. A2A carries messages between agents. MCP connects agents to tools. AWL describes the job itself, so any manager can hand work to any worker and check what came back. The format is defined in SPEC.md.
Install only the part you use:
Node.js 20.11 or later. ESM only. Types included. The Python package needs Python 3.9 or later.
import { WorkDesk } from "agent-work-layer";
const desk = new WorkDesk(); // in-memory; use FileStore, SqliteStore or your own DeskStore to persist
// Manager issues a job
const { order } = await desk.create(
{
to: "agent:researcher",
goal: "Summarize the main risk factors in the latest annual report",
criteria: [
{ id: "c1", text: "Lists three risk factors, each quoted from the filing" },
{ id: "c2", text: "States total debt and its fiscal year" },
],
budget: { usd: 2, tool_calls: 20 },
deadline: new Date(Date.now() + 3_600_000).toISOString(),
tools: ["filings/*"],
},
"agent:manager",
);
// Worker accepts, does the work, reports with evidence
await desk.accept(order.id, "agent:researcher");
const { record } = await desk.submitReport(
order.id,
{
status: "done",
result: { summary: "Refinancing, customer concentration and FX risk. Debt 1.2B USD (FY2025)." },
evidence: [
{ criterion: "c1", type: "tool_call", ref: "filings/get_section#3" },
{ criterion: "c2", type: "citation", ref: "filings/get_section#5" },
],
cost: { usd: 0.84, tool_calls: 7 },
tools_used: ["filings/search", "filings/get_section"],
},
"agent:researcher",
);
// Manager approves, or sends it back with a reason
await desk.review(order.id, "agent:manager", "approve");
For a runnable version, clone the repo, run npm install, then run node examples/two-agents.ts (Node 22.18+). examples/two-agents.ts walks one job through a rejected report, a rework request and approval, printing each event as it happens.
The desk enforces the spec as you go:
Optionally, it also signs the worker's authority into a Delegation Pass that gateways and other systems can check without the desk.
When a worker fails, gets blocked or is cancelled, it can say what the next worker should know. The manager passes that along in the next order:
await desk.submitReport(order.id, {
status: "failed",
error: "Export runs out of memory on files over 2 GB",
handoff: {
decisions: [{ text: "Stream rows instead of buffering", why: "Memory limit" }],
tried: [{ text: "xlsx library", result: "Too slow on large files" }],
next_step: "Add tests for null cells",
},
/* result, evidence, cost, tools_used */
}, "agent:researcher");
const { handoff } = (await desk.get(order.id)).reports.at(-1)!;
await desk.cancel(order.id, "agent:manager", "reissuing"); // a continued order must be finished
await desk.create({ ...nextJob, to: "agent:other-worker", handoff, continues: order.id }, "agent:manager");
await desk.list({ continues: order.id }); // the orders that pick up from this one
context lists what to read; handoff says what was decided and already tried; continues links the new order to the one it replaces, so the chain of orders is the job's full history. Unlike parent_id, it doesn't narrow anything: the new order can have a different worker, budget and tools. See SPEC.md §4.4 and §4.5.
Rejecting a report doesn't unsend an email. For tools whose effects you want to review first, list them in staged:
await desk.create({ ...job, tools: ["filings/*", "gmail/*"], staged: ["gmail/send"] }, "agent:manager");
The worker's MCP gateway then holds back calls to gmail/send: it counts and logs each one and returns a placeholder (pending:gmail/send#4). The worker lists them in its report's effects, which the manager reviews with the rest of the report. Approving lets the gateway run them; rework drops them, and since they never ran, there is nothing to undo. Reads still run normally. A sub-order can't allow a tool its parent stages (C5).
A staged call can use an earlier one's result: pass { "$ref": "github/create_issue#3", "pointer": "/structuredContent/number" } as an argument, and the gateway fills it in when it runs the effects. The manager can also approve only some effects:
await desk.review(order.id, "agent:manager", "approve", "Send the CFO copy only", { effects: ["gmail/send#4"] });
See SPEC.md §4.6.
await desk.progress(order.id, "agent:researcher", "Risk section read; checking the debt note", 60);
await desk.trackRecord("agent:researcher");
// { orders: 12, approved: 9, approved_first_time: 7, reworks: 3, expired: 1, ..., spent: { usd: 8.4 }, budget: { usd: 24 } }
Progress notes are progress events: they change no state, but waitForChange and wait_for_update wake on them. A track record counts what happened to the orders a desk holds for one worker. See SPEC.md §6.2 and §6.3.
import { validateOrder, validateReport, hasInvalid } from "agent-work-layer";
const findings = validateReport(report, order);
// [{ rule: "R4", kind: "invalid", path: "/evidence", message: 'no evidence for criterion "c2"' }, ...]
if (hasInvalid(findings)) reject(findings);
Every finding carries the rule id from SPEC.md §7. invalid findings reject a document. limit findings (over budget, past deadline) are recorded and expire the order.
Writing a validator in another language? Run it against the JSON test vectors in conformance/: documents with the findings each rule must report.
The JSON Schemas are exported too: workOrderSchema, workReportSchema, or agent-work-layer/schemas/work-order.schema.json.
agent-work-layer/validate has the same checks, types and schemas without the desk. It imports nothing from Node, so it runs in browsers, Deno and Bun. (Cloudflare Workers are not supported: the validator compiles schemas to code at runtime, which Workers block.)
The server lives in its own package, agent-work-layer-mcp, so people who only validate documents don't download the MCP SDK.
For development, over stdio. Each agent session runs its own desk process. Sessions share one folder, and each is locked to the identity it is started with.
# Claude Code: a manager session and a worker session, each added in its own project folder
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
For shared use, over HTTP. One desk serves every agent, and each request's credential decides who it acts as, so a worker cannot approve its own work (details):
npx -y agent-work-layer-mcp serve --keys keys.json # { "keys": { "<key>": "agent:manager", ... } }
claude mcp add --transport http awl http://127.0.0.1:8787/mcp --header "Authorization: Bearer $AWL_KEY"
Tools: create_work_order, list_work_orders, get_work_order, accept_work_order, decline_work_order, start_work_order, report_progress, submit_work_report, answer_question, review_work_report, cancel_work_order, wait_for_update, get_track_record, and renew_work_pass when the desk issues Delegation Passes.
To enforce an order while the worker works, and to hold its staged writes until you approve, connect the worker to its real MCP servers through the gateway: npx -y agent-work-layer-mcp gateway --order <id> --config upstream.json. After approval, --commit runs the approved writes. See the gateway docs.
To embed the server in your own process or transport (npm install agent-work-layer agent-work-layer-mcp):
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);
npx agent-work-layer new order > order.json
npx agent-work-layer new report > report.json
npx agent-work-layer validate order.json report.json # exit 1 on invalid, prints rule ids
npx agent-work-layer validate child.json --parent order.json
npx agent-work-layer validate order.json report.json --json
npx agent-work-layer schema order
npx -y agent-work-layer-mcp --store ./.awl --actor agent:worker # the MCP desk on stdio (was "awl desk" before 0.2)
npx -y agent-work-layer-mcp serve --keys keys.json # one shared desk over HTTP; add --pass-key/--issuer to sign passes
npx -y agent-work-layer-mcp gateway --order <id> --config <file> # enforce an order at every tool call; add --pass-keys to use passes
new order starts with one criterion, a USD 1 budget, no allowed tools and a
deadline one hour after creation. new report starts as blocked, with a question
and no evidence of completed work. Both templates validate together as printed.
Replace the placeholder goal, identities, criterion and report contents before
using them; keep the report's order_id equal to the order's id.
offered ──accept──▶ accepted ──start──▶ working ──report done/failed──▶ submitted ──approve──▶ approved
│ │ ▲ │
decline blocked │ │ answer │ rework (with reason)
▼ ▼ │ ▼
declined blocked working
Any active state ──cancel──▶ cancelled · deadline or budget exceeded ──▶ expired
While working, the worker can post progress notes. They are recorded as events but don't change the state (SPEC.md §6.2).
WorkDesk options: store, clock (for tests), newId, onEvent(event, record) (webhooks, logging, metrics), passes: { key, issuer, ttlSeconds? } (sign Delegation Passes).
NOT_FOUND, ALREADY_EXISTS, FORBIDDEN, INVALID_TRANSITION, VALIDATION_FAILED (with findings), CONFLICT (a concurrent writer got there first; re-read and retry), BAD_REQUEST.
PassError codes: MALFORMED, UNSUPPORTED, BAD_SIGNATURE, EXPIRED, NOT_YET_VALID, WRONG_ISSUER, WRONG_WORKER, WRONG_ORDER, WRONG_PARENT, WIDER_THAN_PARENT (with findings from C1–C3 and C5).
MemoryStore keeps records in memory, FileStore writes one JSON file per order, and SqliteStore keeps them in a SQLite database using Node's built-in node:sqlite (Node 22.13+, no extra dependency):
import { WorkDesk, SqliteStore } from "agent-work-layer";
const desk = new WorkDesk({ store: new SqliteStore("./awl.db") }); // or ":memory:"
To use another database, implement DeskStore:
import type { DeskStore, OrderRecord } from "agent-work-layer";
class PostgresStore implements DeskStore {
async read(id: string): Promise<OrderRecord | null> { /* SELECT */ }
async insert(record: OrderRecord) { /* INSERT, throw AwlError("ALREADY_EXISTS") on conflict */ }
async update(record: OrderRecord, expectedSeq: number) { /* UPDATE ... WHERE last_seq = expectedSeq, else AwlError("CONFLICT") */ }
async list() { /* SELECT */ }
}
A stdio desk trusts the actor it is started with. In production, run one desk over HTTP that takes the actor from each request's credential: npx -y agent-work-layer-mcp serve --keys keys.json, or createDeskHttpHandler with your own authenticate hook (OAuth 2.1 bearer tokens, API keys, mTLS). See the MCP package README. For real-time enforcement, run the MCP gateway (npx -y agent-work-layer-mcp gateway), which checks tools and budget at each tool call, not only when the report arrives. When the gateway or another team's system can't read the desk, give it Delegation Passes instead. See SPEC.md §9.1 and §10.
With stdio, each agent runs its own desk and declares its own actor, so a worker could start one as its manager. Over HTTP, every agent connects to one desk, which takes the actor from the request's credential through a pluggable authenticate(request) hook. Teams plug in what they already use: OAuth 2.1 bearer tokens, API keys or mTLS. An actor argument naming anyone else is refused (#16, SPEC.md §10.1).
When a worker starts, the desk signs a short-lived pass (a JWS) carrying the order's tools, budget and deadline. The worker presents it with each tool call, and a gateway or another team's system verifies it on its own, without calling the desk. A sub-order gets a narrower pass that names its parent, so the delegation rules (C1–C3, C5) can be checked offline (#17, SPEC.md §10.2).
working, and the worker renews it while it works.import { WorkDesk, verifyPass } from "agent-work-layer";
const desk = new WorkDesk({ passes: { key: privateJwk, issuer: "https://desk.example.com" } });
// ...create, accept...
const { pass } = await desk.start(id, "agent:worker"); // renew with desk.issuePass(id, "agent:worker")
// Anywhere else, with only the desk's public key:
const verified = await verifyPass(pass, publicJwk, { issuer: "https://desk.example.com", worker: "agent:worker", order: id });
verified.allows("filings/search"); // true
const child = await verifyPass(childPass, publicJwk, { parent: verified }); // throws WIDER_THAN_PARENT if it breaks C1–C3 or C5
Keys are P-256 or Ed25519 JWKs (or WebCrypto keys) that you supply; the SDK does no key management. signPass and verifyPass use WebCrypto only, so they are also in agent-work-layer/validate for browsers, Deno and Bun. To make a key pair in Node:
const { privateKey, publicKey } = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
console.log(JSON.stringify(await crypto.subtle.exportKey("jwk", privateKey)), JSON.stringify(await crypto.subtle.exportKey("jwk", publicKey)));
Over MCP, awl-desk serve --pass-key priv.json --issuer <url> issues passes, and awl-desk gateway --pass-keys pub.json enforces them; see the MCP package README.
The repo is an npm workspace with two packages: the core in packages/core and the MCP server in packages/mcp. The spec, schemas and examples stay at the root; the core copies them in when it is packed.
npm install
npm test # core and MCP tests (builds the core first)
npm run test:coverage
npm run typecheck
npm run test:package # packs both packages, installs them in clean projects and uses them
npm run check # all of the above; runs automatically before npm publish
npm run generate # after editing schemas/*.json: refreshes packages/core/src/generated.ts
The Python package in python/ is separate from the npm workspace:
cd python
python -m venv .venv && .venv/bin/pip install -e ".[test]"
.venv/bin/pytest
Issues, ideas and pull requests are welcome. See CONTRIBUTING.md and the roadmap. Spec changes start as a Spec proposal issue.
Apache-2.0. You can use, modify and ship it, including commercially. The license includes a patent grant.
Work Orders and Work Reports for AI agents: a typed SDK, validator and lifecycle desk. The MCP server is in agent-work-layer-mcp.
{ "mcpServers": { "agent-work-layer": { "command": "npx", "args": [ "-y", "[email protected]" ] } }}เครื่องมือ
ยังไม่มีรายการเครื่องมือ เซิร์ฟเวอร์แบบรีโมตจะได้รายการหลังการตรวจสอบสถานะครั้งแรก
FAQ
คัดลอกคอนฟิก stdio สำหรับ Cursor, Claude Desktop, Claude Code หรือ VS Code ได้ในหน้านี้ คอนฟิกนี้ใช้การติดตั้งของผู้เผยแพร่ ไม่ใช่พร็อกซีของ PlugMCP
ระเบียนนี้ไม่ได้ระบุตัวแปรสภาพแวดล้อมที่จำเป็น
Agent Work Layer ทำงานแบบโลคัลผ่าน stdio
ตรวจสอบล่าสุดเมื่อ 2026-11-12 Work Orders and Work Reports for AI agents: a typed SDK, validator and lifecycle desk. The MCP server is in agent-work-layer-mcp.