Skip to content

Permissions — agents and grants

An agent (Claude Code, Codex, any MCP client) reaches a workspace's capabilities through one door on the Cowork daemon, signed in with its own login. What it may run is granted, one routine at a time, by the person who owns the workspace. Everything else — the app's panes, a terminal's cowork routine run, schedules, webhooks, ctx.routines.run — is the owner, and grants never gate it.

A capability's routines ARE its tools: each entry in routines says what it does and what it changes, as an MCP tool says it.

{ "name": "clickup", "version": "1.1.0", "kind": "capability",
"credential": { "key": { "fields": ["token"], "url": "https://app.clickup.com/settings/apps", "hint": "Settings › Apps › API Token" } },
"routines": {
"clickup-tasks": { "file": "tasks.ts", "readOnly": true, "description": "Lists the tasks of a list",
"input": { "type": "object", "properties": { "list": { "type": "string", "description": "The list id" } }, "required": ["list"] } },
"clickup-comment": { "file": "comment.ts", "reachesOthers": true, "description": "Comments on a task (its watchers are notified)" },
"clickup-delete": { "file": "delete.ts", "destructive": true, "description": "Deletes a task" }
} }
  • readOnly: true — it changes nothing: no write at the service, no money spent, no user or project code run, nothing written in the workspace but its own cache (no view state, no export, no file a person could have edited). Granted to every agent by default, and an agent's client may run it without asking — so when in doubt it is not a read. A read cannot also be destructive or reachesOthers (then readOnly is ignored, with a line).
  • destructive: true — what it changes cannot be undone. reachesOthers: true — it reaches people beyond the account (sends, posts, pays).
  • description (≤ 400 characters) and input (a JSON Schema {"type": "object", …} of its arguments, ≤ 8 KB) are what the agent reads before calling it — write them for an agent.
  • A bare "id": "file.ts" is a write tool with no description: off for agents until granted.
  • Risk is derived, never stored: a read is low; destructive or reaching others is high; the rest medium. Manage Workspace shows it beside each routine.
  • Only "kind": "capability" packages are offered to agents — an app's own routines and loose routines are not. The capability's SKILL.md is the guide an agent gets with its tools.

Per workspace: a default for each <capability>/<routine>, and an override per agent. The rule: the agent's own setting, else the workspace default, else on only for a read. A routine whose connection is not connected (its credential) is unavailable whatever is granted.

  • Manage Workspace › Permissions › Agent access — Add agent (the token is shown once, with the lines that connect a client), and per agent: each capability's routines with Default / Allow / Deny. Capability defaults — the workspace default, one switch per routine.
  • The CLI (a headless server has no page):
Terminal window
cowork agent create claude # prints the token ONCE + the connect lines
cowork agent list # name, id, last seen, revoked
cowork agent grants [--agent claude] # every routine: the default and each agent's answer
cowork agent grant clickup/clickup-comment allow --agent claude
cowork agent grant clickup/* deny # the default for every agent without its own setting
cowork agent grant clickup/clickup-comment default --agent claude # back to the rule
cowork agent token claude # a new token; the old one stops working
cowork agent revoke claude --yes

Connect the agent from the workspace folder (Claude Code's local scope: private to that folder, never in git — the URL names the workspace):

Terminal window
claude mcp add --transport http cowork 'http://127.0.0.1:47913/api/mcp?ws=%2Fpath%2Fto%2Fws' --header "Authorization: Bearer cwa_…"

(Codex: the [mcp_servers.cowork] block cowork agent create prints, in ~/.codex/config.toml.) The agent then starts a task, lists the capabilities it may use, and calls read (a granted read) or run (anything else granted); a call that is not granted says so and names who to ask.

Every agent call is a routine run like any other — the same off switch, the same audit record — naming the agent and its task: Manage Workspace › Observability › Executions shows Who (you, or the agent) and the task; cowork routine runs lists them. A call that is not granted is recorded denied and never starts.

ctx.identity (routines) is { user: {uid, email} | null, organization: {id, slug, name} | null } — who is signed into Cowork, frozen at the start of the run; use it to stamp who did what. cowork.identity.currentUser() is the same user in a widget. cowork status and cowork org show the organization.

Grants are policy on the owner's own machine: a process holding the daemon token is the owner and can make agents or grants. They keep agents to what you allowed; they are not a wall against the machine's owner.