EveryAppEveryApp

Managing the workspace: the CLI and the MCP tools

Everything the Gateway's admin UI does (users, invitations, roles, groups, who can open an app, caps, usage, the activity trail and the audit log, tokens, AI connections, the organization's name) as everyapp workspace commands for the operator and as everyapp_workspace_* tools an admin's AI client calls on /mcp, who may do what, and what neither surface ever returns.

The Gateway's admin UI (Manage workspace in the sidebar: Users, Groups, Apps, Tokens, Usage, Activity, Events, plus AI connections in the account menu) is one of three ways to run a workspace. The other two are for agents:

Surface Who acts Authority Where it shows
pnpm everyapp workspace … the operator: the Cloudflare login (or CLOUDFLARE_API_TOKEN) that owns the Gateway's account root. The commands write the Gateway's D1 directly, like email set and provider on (ADR-0003 in the checkout, adr/), with the same guards the UI holds: never the last owner, never an owner by an admin. Nothing is asked of the Gateway over HTTPS, so they work while it is down. the next request to the Gateway reads the change; nothing is written to Admin → Activity (Cloudflare's audit log has the D1 writes)
everyapp_workspace_* tools on https://<gateway>/mcp the person behind an OAuth connection, a personal access token (epat_) or a Cloudflare Access assertion their live role in the organization (re-read on every request) and the connection's access. A tool is hidden below its role; a write tool is hidden from, and refused for, a read-only connection like any app write tool. The services' own rules apply on top and come back as tool errors. one row in Admin → Activity per call, app everyapp, with the client's name, like any tool call

Every row of the parity matrix (which UI action is which command and which tool, and the few with none) is in the checkout at docs/design/2026-10-agent-parity.md; a test (packages/cli/src/docs/parity.test.ts) fails when a UI action gets no twin and no reason.

The CLI: everyapp workspace

Run from anywhere, with --json when an agent drives it (the protocol). Every command takes --gateway and --account-id, and all but rename and role take --org <slug|id|name>; without it a command acts on the install organization (the one the first deploy made). --yes skips the confirmation of remove and group delete. People are named by email or user id, apps by their manifest id (crm) or row id, groups by name or grp_ id, invitations by the invited email or id.

Command What it does
workspace show The organization (name, slug, id), its owners, whether the Gateway can send mail, and counts of members, pending invitations, groups and apps.
workspace users Members with role and state (active, deactivated), and pending invitations with role and expiry.
workspace invite <email> [--role admin] Create the invitation (good for 72 hours) and print its link (https://<gateway>/accept-invitation?token=einv_…). The CLI never mails it: hand the link over. The invitation records the operator as its inviter, so the operator's login email must be a verified member (WORKSPACE_NOT_A_MEMBER otherwise).
workspace invite-link <email|id> A new link for a pending invitation; earlier links keep working until it is used, canceled or expires.
workspace uninvite <email|id> Cancel a pending invitation.
workspace role <email> <owner|admin|member> Change a member's role. An owner is made or demoted only by the operator or, in the UI, by an owner; the last owner can never be demoted.
workspace rename <name> Rename the organization (the slug, and so every Worker name, stays).
workspace deactivate <email> Ban the account: no sign-in, every session ended, personal access tokens and AI connections revoked, in one batch. Never the last live owner.
workspace reactivate <email> Lift the ban. Revoked tokens and connections stay revoked.
workspace remove <email> Remove the membership, their app grants, group memberships, tokens and connections in this organization, and the account itself when it belongs to no other organization. Like the UI, someone Cloudflare Access would let straight back in is deactivated instead (decided from whether an Access auto-join setting is set, since its value is a secret the CLI cannot read; the output says how to delete anyway). Never the last live owner.
workspace reset-link <email> A one-hour, single-use password-reset link to hand over (https://<gateway>/reset-password?token=…), for a member of this organization alone and not deactivated. Unlike the UI it works for an owner: the operator is root, and this is how a sole owner who lost their password gets back in. Mailing one is the UI's or the MCP tool's.
workspace groups The groups with member and app counts.
workspace group create <name> [--description], workspace group set <group> [--name] [--description], workspace group delete <group> A group's name is unique in the organization. Deleting a group takes its app grants with it.
workspace group members <group> [--add <email>…] [--remove <email>…] [--set <email>…] Who is in the group; or change it. --set replaces the whole list.
workspace group apps <group> [--add <app>…] [--remove <app>…] [--set <app>…] Which apps the group opens; or change it.
workspace apps Every app of the organization with who can open it (everyone, or how many people and groups), its setup state and status. status is the operator's view of the same apps (Workers, versions); this is the admin's.
workspace app set <app> [--name] [--description] [--everyone on|off] The catalog entry: the name and description people see, and whether every member can open it. Turning --everyone off keeps the people who already have access.
workspace app access <app> [--add <email>…] [--remove <email>…] [--set <email>…] Who can open the app directly (groups grant it too); or change it.
workspace app cap <app> [<n|off>] [--usd <dollars|off>] [--unlimited] The organization's monthly caps on the app's provider calls: a number of calls (a whole number up to a billion) and --usd dollars of provider-reported cost (0.01 to 1,000,000, whole cents), each a value, off (none of its own: the workspace default of that kind binds it, and without one it is uncapped), or left out to keep the app's own (so --usd 20 alone keeps the call cap); or --unlimited alone (no cap of either kind, even with workspace defaults). A cap of either kind ends --unlimited, the other kind then following its default. The dollar cap counts only the cost a provider reports (OpenRouter's usage.cost, DataForSEO's cost); other providers' calls count toward the call cap alone. The row holds caps or unlimited, never both, every column written in one statement; both caps off deletes it. The semantics are the Usage page's (Manage workspace → Usage). An app past either cap that binds it is refused with app_monthly_cap_exceeded. As in Admin → Usage, the organization's first cap makes the built-in Cap alerts subscription (owners and admins emailed at 80% and at the cap; pnpm everyapp events list shows it, done.data.capAlerts names it the run it is made), and cap.raised is queued when a cap that binds the app ends higher or gone, judged per kind on the effective cap before and after (its own, else the default): a cap set under the default is no raise, --unlimited under a default is a removal, off under a default raises to it; a lowered cap is no event. done.data carries cap, costCapMicros, unlimited, was, wasCostCapMicros, wasUnlimited, effectiveCap, capSource, effectiveCostCapMicros and costCapSource (app, default or null; dollars in millionths). --unlimited with a number, off or --usd is INVALID_VALUE; none of them is MISSING_ARGUMENT; dollars out of range or finer than a cent are INVALID_CAP. On a Gateway from before the workspace default (migration 0014) --unlimited is refused with GATEWAY_PREDATES_DEFAULT_CAPS; a number and off still work. On one from before dollar caps (migration 0018) --usd is refused with GATEWAY_PREDATES_COST_CAPS.
workspace cap default <n|off> The organization's default monthly cap: what binds every app without a cap or --unlimited of its own (Manage workspace → Usage → Default monthly cap). off removes it (the org_settings row goes). The Gateway holds every such app to it within 30 seconds. As in the Gateway, the first cap (an app's or the default) makes the Cap alerts subscription, and the default first set, raised or removed queues cap.raised with no app and source: "default" (a lowered one doesn't), all in the write's own batch. done.data carries cap, was and capAlerts. Refused on a Gateway from before migration 0014 with GATEWAY_PREDATES_DEFAULT_CAPS (run pnpm everyapp update first).
workspace usage [--month YYYY-MM] What the organization's apps spent in a UTC month: per app, per member and per provider. done.data.defaultMonthlyCallCap is the workspace default (or null) and each app row carries its own cap (monthlyCallCap), capUnlimited, the cap that binds it (effectiveMonthlyCallCap) and its source (capSource: app, default or null), and paused (it has reached that cap this month; always false for a past month). The table's CAP column shows the binding cap, (default) when inherited, none (unlimited) for an exempt app and paused. Refused on a Gateway from before migration 0014 with GATEWAY_PREDATES_DEFAULT_CAPS.
workspace activity [--app <app>] [--user <email>] [--decision allowed|blocked] [--risk read|write|destructive] [--since <time>] [--limit <n>] [--before <cursor>] The tool-call audit trail, newest first: who called which tool of which app, through which client, allowed or blocked, and the outcome; the Activity page's filters, and --before takes the previous page's cursor (done.data.nextBefore under --json). Never the arguments or the result.
workspace audit [--actor <who>] [--type <type>] [--since <time>] [--limit <n>] [--before <cursor>] The audit log, newest first: every event the Gateway recorded (members, apps, caps, connections, tokens, Access, MCP apps, providers, apps' own events), who did it (a member, agent · <AI app>, CLI · <operator>, app · <id>, Gateway), the type in plain words and the subject; kept 90 days. --actor is a member by email or id, else an actor id as the rows hold it (a connection's, a token's, an app's, an operator's email); --type one type, a prefix (member.*, app.crm.*) or * (pings and tool calls too; out otherwise); --before the previous page's cursor (done.data.nextBefore). Never an event's data. GATEWAY_OUTDATED before migration 0015.
workspace tokens [--user <email|id>] [--all] Personal access tokens: name, scopes, app, expiry, last use; --all lists revoked and expired ones too. Never a value. The operator sees every member's.
workspace token revoke <id> Revoke one. The next request with it is refused.
workspace connections [--user <email>] AI connections (the OAuth clients people connected): client, access, apps, last use.
workspace connection revoke <id> Revoke one.

What the CLI does not do, and why: it cannot create a personal access token (the Gateway hashes tokens with BETTER_AUTH_SECRET, a Worker secret the CLI never holds; create one in Admin → Tokens), and it sends no mail (the Gateway's sender does; the CLI prints links instead). everyapp delete <app> removes an app's Worker and catalog row, so there is no workspace app delete.

The MCP tools: everyapp_workspace_*

Listed on the Gateway's /mcp to owners and admins beside everyapp_list_apps, everyapp_whoami and everyapp_search_tools (the connector). A member sees none of them and a call answers "Unknown tool". A read-only connection sees the read tools only. A credential narrowed to apps (a personal access token bound to one app, a connection consented for selected apps) sees none of them: it administers nothing. They are listed in the full listing; in the compact listing (a connection past 256 tools or 1 MiB, or X-EveryApp-Tools: compact) a client finds them with everyapp_search_tools and runs them through everyapp_call_read or everyapp_call_write, like app tools. Every call leaves one row in Admin → Activity.

Four tools are for every member, about their own things: everyapp_my_tokens and everyapp_revoke_token (the caller's personal access tokens, never a value), everyapp_my_connections and everyapp_revoke_connection (the caller's AI connections in every organization; revoking the one in use ends it on the next request).

Tool Reads or writes Role What it does
everyapp_workspace_show reads admin The organization, your role, whether the Gateway can send mail, and counts.
everyapp_workspace_users reads admin Members and pending invitations.
everyapp_workspace_invite writes admin email, role (member or admin). Creates the invitation and says whether the Gateway mailed it (emailed). It never returns the link: where the Gateway has no email, the admin copies the link in Admin → Users or the operator runs everyapp workspace invite-link.
everyapp_workspace_uninvite writes admin invitation (id or email): cancel a pending invitation.
everyapp_workspace_role writes admin user, role (member, admin, owner). Only an owner changes an admin or makes an owner; nobody changes their own role; the last owner stays.
everyapp_workspace_deactivate, everyapp_workspace_reactivate writes admin user. Deactivation ends sessions and revokes tokens and connections; only for accounts in this organization alone.
everyapp_workspace_remove writes admin user, confirm: true. Removes the membership (deactivates instead where Cloudflare Access would let them back in, and says so).
everyapp_workspace_reset_email writes admin user: mail a password-reset link to a member (the link goes to them, never to the caller).
everyapp_workspace_groups, everyapp_workspace_group_create, everyapp_workspace_group_set, everyapp_workspace_group_delete reads / writes admin The groups; name, description?; group plus the fields to change; group.
everyapp_workspace_group_members, everyapp_workspace_group_members_set reads / writes admin group; then users (the whole list) or add / remove.
everyapp_workspace_group_apps, everyapp_workspace_group_apps_set reads / writes admin group; then apps (the whole list) or add / remove.
everyapp_workspace_apps reads admin Every app with who can open it, its setup state and status.
everyapp_workspace_app_set writes admin app, name?, description?, everyone? (available to every member).
everyapp_workspace_app_access, everyapp_workspace_app_access_set reads / writes admin app; then users (the whole list) or add / remove.
everyapp_workspace_app_cap writes admin app, cap? (whole calls per month, or null for no call cap of its own), costUsd? (dollars of provider-reported cost per month, 0.01 to 1,000,000 in whole cents, or null for no dollar cap of its own), unlimited? (boolean, alone: no cap of either kind even under workspace defaults). A cap left out is kept, so costUsd alone keeps the call cap; give at least one of the three. A cap and unlimited together are refused (INVALID_INPUT), as is a fraction of a cent. Answers monthlyCallCap, monthlyCostCapMicros, capUnlimited, effectiveMonthlyCallCap and capSource, effectiveMonthlyCostCapMicros and costCapSource (app, default or null).
everyapp_workspace_cap_default writes admin cap? (whole calls per month for every app without a call cap or unlimited of its own, or null for no default), costUsd? (dollars per month the same way, or null); a field left out is kept, give at least one. Answers defaultMonthlyCallCap and defaultMonthlyCostCapMicros. The same rules as Admin → Usage → Default monthly cap: the first cap makes the Cap alerts subscription; set, raised or removed queues cap.raised with no app and source: "default".
everyapp_ai_show reads member {}. What the default model provider resolves to now (provider, model, source, via, the candidates in order); configured and settings for owners and admins only. See /docs/agent/ai.
everyapp_ai_default writes admin provider, model?, or clear: true. The same setting as Admin → Usage → AI default and everyapp ai default: a provider the default can't be is refused (INVALID_INPUT); a change queues ai.default_changed.
everyapp_workspace_app_delete writes admin app, confirm: true. Removes the catalog row; the Worker stays until the operator runs everyapp delete.
everyapp_workspace_usage reads admin month? (YYYY-MM, UTC). Answers defaultMonthlyCallCap and, per app, monthlyCallCap, capUnlimited, effectiveMonthlyCallCap, capSource and paused, as the Usage page shows them.
everyapp_workspace_activity reads admin app?, user?, decision?, risk?, since?, limit?, before? (the previous page's cursor).
everyapp_workspace_audit reads admin actor?, type? (a type, member.*, or *), since?, limit?, before? (the previous page's cursor): the audit log, 90 days.
everyapp_workspace_rename writes admin name.
everyapp_workspace_events reads admin The event subscriptions (Admin → Events): where each delivers (a webhook's host and path, or email), the events it takes, whether it is on, its last delivery. Never a signing secret.
everyapp_workspace_events_add writes admin url or email (owners or admins), events?, name?, secret?. Without secret the Gateway generates one and returns it once, in that answer: it signs deliveries to the URL and opens nothing on the Gateway, so the person stores it in the receiver now.
everyapp_workspace_events_set, everyapp_workspace_events_remove writes admin subscription (id or name) plus enabled?, events?, name?; subscription, confirm: true (a built-in subscription, Cap alerts, is turned off instead, and the answer says so).
everyapp_workspace_events_test writes admin subscription: sends a test event to that one subscription and answers the first attempt (the HTTP status, or the error and the retry).
everyapp_workspace_event_deliveries, everyapp_workspace_event_redeliver reads / writes admin subscription?, dead?, limit?, before?; delivery: send a failed or dead one again now.

Rules every tool holds (toolbus.workspace-tools-role-gated and toolbus.workspace-tools-no-credentials in the threat model's register):

  • No tool returns a credential that opens the Gateway: no invite link, no password-reset link, no token value. None creates a personal access token: a connection that can be revoked must not mint itself a credential that outlives it. The CLI and the UI do those. The one secret a tool returns is the webhook signing secret everyapp_workspace_events_add generates, once: it opens nothing on the Gateway.
  • A refusal is a tool error, CODE: message, with the UI's codes (OWNER_REQUIRED, CANNOT_MANAGE_SELF, CANNOT_MANAGE_OWNER, USER_IN_OTHER_ORGANIZATION, USER_NOT_FOUND, APP_NOT_FOUND, INVALID_INPUT); a tool hidden by role or access is not listed and is "Unknown tool" on call, so a member learns nothing about them.
  • remove, app_delete and mcp_remove are an owner's or admin's (since 2026-10-25; nothing on the connector is owner-only) and take confirm: true; events_remove takes it too; without it they refuse and say so. What stays an owner's is inside the services: an admin never removes an owner (OWNER_REQUIRED), never makes or demotes one.
  • The arguments are checked against each tool's own schema whatever their size, and every call is audited with an argument digest and a byte count, never the arguments themselves.

Which to use

  • An agent on the operator's machine (the one that ran setup): the CLI. It needs no account on the Gateway except to invite.
  • An agent connected as a person (claude.ai, Claude Code, Cursor, a script with a personal token): the tools, with that person's role.
  • Creating a personal access token, copying an invite or reset link, approving an AI app's connection: the UI, by the person.

On this page