# Native agent work grants — MCP adapter v1 **Implementation source; live deployment and end-to-end acceptance are separate.** This is the thin Laboratory adapter for the reviewed Core `.forgejo/AGENT_WORK_GRANT_V1.md` design and the [visible delegation requirements](AGENT_DELEGATION.md). A matching Core must implement the native routes, browser session approval and durable transaction boundary. The adapter does not create that authority itself. ## Authenticated tools and native routes | Tool | Inputs | Native operation | | --- | --- | --- | | `list_agent_work_grants` | optional positive `page`, optional `state` | `GET /api/v1/agent-work-grants` outside `/user` | | `get_agent_work_grant` | `owner`, `repo`, positive native `grant_id` | `GET /api/v1/repos/{owner}/{repo}/agent-work-grants/{grant_id}` | | `confirm_agent_work_grant` | context plus exact proposal triple | `POST .../{grant_id}/confirm` | | `submit_agent_work_proposal` | context, proposal triple, durable `request_key`, `title`, `body` | `POST .../{grant_id}/issues` | | `get_agent_work_submission` | context and the same `request_key` | `GET .../{grant_id}/submissions/{request_key}` | The proposal triple is scalar: `proposal_schema="agent_work_grant.v1"`, `proposal_version=1` (JSON integer), and the native lowercase 64-hex `proposal_digest`. The client must repeat the read proposal exactly. It does not supply the server's mutable CAS `state_revision`. IDs are positive native signed 64-bit integers, not names, booleans or numeric strings. Request keys are canonical lowercase UUIDv4 strings, safely escaped as a complete path segment for readback. The adapter never generates or replaces one. The only submission fields sent to Core are the triple plus `request_key/title/body`. Unknown inputs, including human authorization, caller-defined issuer/agent IDs, sponsorship, scope changes or a claimed approval, are refused before upstream work. The current request bearer takes precedence over process configuration; stdio retains its existing own-token or Basic-auth configuration. No human browser credential is forwarded. Core rechecks the actual native bot, selected grant, current repository permissions and credential scope/resource reducer. Confirm/submission require `read:repository` and `write:issue`, including the bot's existing code/issue reads. No `read:user` scope is added by this adapter. Preparing a grant is not approval; bot confirmation is not human approval. Human preparation, review, approval and withdrawal remain native browser-session operations. Missing private access is an explicit collaboration prerequisite, never an implicit membership or credential grant. ## Native results and uncertain delivery Single-grant tools return the complete native `grant` plus `native_status`. Submit/readback return native `{grant,submission}` plus `native_status`. Core's native participant/repository/Issue IDs and submission status are validated for consistency; requested owner/repo or caller text never invents missing attribution. New submission is HTTP201; a matching committed replay is HTTP200. The adapter sends each POST once and has no write retry/fallback. HTTP400/401/403/404/409 retain their native meaning in bounded MCP errors. No redirect is followed. Raw upstream error bodies, Location headers, malformed protocol bytes and reflected credentials are not exposed. Malformed/incomplete success responses do not publish a receipt. A lost response, unexpected status or malformed write response leaves delivery uncertain: read back the same owner/repo/grant/key with `get_agent_work_submission`. Do not infer failure, generate a new key or automatically submit again. For uncertain confirmation, read the grant before deciding whether the identical confirmation is needed. Local input/credential/budget refusal happens before a native request. Native receipts retain historical Issue ID/index/URL with outcomes `submitted`, `issue_deleted` or `issue_binding_changed`. A deleted Issue does not make its key available again. Withdrawal prevents new grant operations while an authorized receipt read can still reconcile a prior commit. A receipt remains `review_state="awaiting_review"`; it is not human result acceptance. Native Issue/API/list verified attribution is Core-owned; ordinary API/MCP/Git activity does not gain an assignment marker. Title/body and native task/summary text remain whole strict UTF-8, without a new gateway text cap, excerpt or replacement. Existing native validation, gateway budgets, the remote MCP 1MiB request-body limit and the existing native client socket timeout still apply. This adapter does not remove those transport bounds or promise that every large request is accepted. ## Discovery and acceptance List requests ask Core for `limit=30` and one positive page. Omit `state` for the native nonrevoked default, or use `awaiting_agent`, `awaiting_human`, `active`, `revoked` or `all`. Continue with `next_page` until `has_more=false`. Native total/Link relations determine continuation even if Core clamps the width. Link addresses are never followed; each page makes a fresh fixed-origin authenticated request. Pages are current observations (`snapshot=false`). There is no cursor, grant cache or atomic-list claim. `test_agent_work_grants.py` uses real loopback HTTP and MCP dispatch, normally and under `-O`: strict inputs, credential choice, native errors, redirect refusal, complete text, pagination, malformed/lost responses, same-key readback, no hidden repeated write, receipt consistency and unchanged public four tools. The required full `scripts/e2e-local.sh` invokes `test_agent_work_grants_native.py` on a fresh isolated stack with the tested Core image. Actual native HTML forms and session nonces prepare/approve/withdraw; real native bots call authenticated HTTP MCP. Separate setup privilege creates fixtures/deletes the Issue and never enters the MCP child. The fixture exercises 31 pending grants across real pages, full Issue body/actor readback, wrong bot and scope, ordinary unassigned activity, same-key replay/deletion tombstone, withdrawal, independent grants and current access loss. Core's separate real browser and SQLite/PostgreSQL gates remain required. Tests and source do not constitute runtime admission. See the [runbook](RUNBOOK.md).