Authentication
Gavana has two authentication paths, and which one you use is decided entirely by which server you connect to.
| Hosted MCP | Local stdio MCP | |
|---|---|---|
| Endpoint | https://app.gavana.ai/mcp | npx -y @gavana.ai/mcp@0.2.0 on your machine |
| Credential | Browser OAuth, PKCE, no token to copy | Agent Access token, or a saved CLI profile |
| Where it lives | The client’s own connector storage | Your MCP client’s private environment, or ~/.config/gavana/agent.json |
| Expiry | No automatic expiry; it remains active until you disconnect or revoke it | Never by default; optionally choose a custom 1–90 day expiry when you create the token |
| Revocation | Revoke the OAuth grant | Revoke the token on the Agent Access screen |
If you can use the hosted endpoint, use it. Nothing is safer than a credential you never have to handle.
Hosted: browser OAuth
You add an endpoint URL to your client, click connect, sign in to Gavana in a browser, review a consent screen, and approve. No token is ever pasted into a chat window, a config file, or a shell command.
What the flow supports
The authorization server metadata is published at https://app.gavana.ai/.well-known/oauth-authorization-server, and the hosted endpoint publishes protected-resource metadata at /.well-known/oauth-protected-resource/mcp.
Any MCP client that implements standard OAuth discovery finds everything it needs there. The declared surface is:
| Property | Value |
|---|---|
| Authorization endpoint | https://app.gavana.ai/oauth/authorize |
| Token endpoint | https://app.gavana.ai/oauth/token |
| Registration endpoint | https://app.gavana.ai/oauth/register |
| Revocation endpoint | https://app.gavana.ai/oauth/revoke |
| Grant types | authorization_code |
| Response types | code |
| Response modes | query |
| PKCE | S256 required |
| Client authentication | none — public clients with PKCE only |
| Bearer method | header |
Dynamic client registration is open, with real constraints: 1–10 redirect URIs, each an exact HTTPS or local-loopback callback, and a token_endpoint_auth_method of none. A registration asking for a client secret is rejected. There is no implicit grant and no password grant.
The consent screen
https://app.gavana.ai/mcp advertises all eight scopes: canvas:read, canvas:write, asset:read, element:read, element:write, image:generate, video:generate, job:manage. There is no partial approval on this path — the consent screen lists all eight, and approving grants all of them.
Approving grants the client the ability to write canvases, manage Elements, and start provider-backed work. Read the list before you click through. The server’s own instructions tell the model to ask for explicit current-turn approval before paid tools, but the capability is present once you approve.
Browser origins
The hosted endpoints accept browser requests from https://chatgpt.com, https://claude.ai, and the Gavana origin itself, plus anything the deployment explicitly allows. A request carrying some other Origin header is refused with 403 before authentication is even attempted. Non-browser clients that send no Origin header are unaffected.
Local: Agent Access tokens
The local stdio server has no browser, so it uses a scoped, revocable Agent Access token — or, if you have already logged in with the CLI, the profile that login saved.
Creating one
The full walkthrough is on Create an Agent Access Token. In short: open the account menu, choose Personal Access Tokens, name the token after the client that will use it, keep the default full permissions and Never expiry or make a deliberate narrower choice, then copy the value — Gavana shows it exactly once.
Two details that matter later:
- The token’s name is written into provenance. A node created through a token named
Claude · MCPrecords that name, so canvas history can tell your clients apart without trusting a caller-supplied label. Use one descriptively named token per client rather than sharing one. - Expiry defaults to Never. Choose a custom 1–90 day expiry for a temporary credential. Scopes and expiry are fixed after creation, so replace the token to change either.
Tokens begin with cba_. That prefix is a stable compatibility identifier that predates the Gavana rename — a token starting with cba_ is current, not stale.
Configuring the server
The local server reads either environment variables or the saved CLI profile:
GAVANA_BASE_URL=https://app.gavana.ai
GAVANA_AGENT_TOKEN=<your Agent Access token>or ~/.config/gavana/agent.json, written by gavana auth login.
CRAFTBOARD_BASE_URL, CRAFTBOARD_AGENT_TOKEN, and CRAFTBOARD_AGENT_CONFIG_FILE still work as transition aliases, and the legacy ~/.config/craftboard/agent.json is read when the new path does not exist. Gavana-named variables take precedence.
Put the token in your MCP client’s private environment or secret store. Never in a shared configuration file, a repository, a chat message, or a shell command that lands in history. The install pages use read -rs prompts precisely so the token never appears in your shell history.
Logging in with the CLI instead
If the CLI is already set up, the local MCP server inherits its profile and you configure no token at all:
read -rs 'GAVANA_AGENT_TOKEN?Paste Agent Access token: '; printf '\n'; printf '%s' "$GAVANA_AGENT_TOKEN" | gavana auth login --base-url 'https://app.gavana.ai' --token-stdin; unset GAVANA_AGENT_TOKENThe token goes in over stdin, so it never becomes a command-line argument visible to other processes or to your shell history.
The six scopes
The same six scopes apply to OAuth grants and to Agent Access tokens.
| Scope | Grants | Depends on |
|---|---|---|
canvas:read | Read canvas graphs, revisions, and node contents | — |
canvas:write | Change canvases | canvas:read |
asset:read | Read asset references | — |
image:generate | Generate and edit images, write image bytes into Gavana storage, and start any Recipe | canvas:read, canvas:write, asset:read |
video:generate | Discover connected video models and start video work | canvas:read, asset:read |
job:manage | Observe, resume, wait for, and cancel Runs and Jobs | — |
Dependencies are enforced when the token is created, not silently added at call time. Asking for image:generate without asset:read returns an error naming the missing scopes; the token creation form selects dependants for you.
image:generate is the scope that governs writing image bytes into Gavana, not just generating them. That is why save_image_to_canvas requires it even though it generates nothing. A token scoped to canvas:read and canvas:write alone cannot save an image.
job:manage is easy to leave off and painful to be without. Without it, a client can start work but cannot poll, wait for, or cancel the Run it started. Every hosted tool that waits — run_canvas_workflow, generate_image_in_canvas, generate_video — and every tool that resumes — get_canvas_workflow_run, get_video_job — requires it.
Which scopes each hosted tool needs
| Tool | Scopes |
|---|---|
guide_search | none |
guide_get | none |
canvas_list | canvas:read |
canvas_get | canvas:read |
canvas_validate | canvas:read |
get_canvas_image | canvas:read |
open_canvas | canvas:read |
canvas_apply_batch | canvas:read, canvas:write |
create_canvas_workflow | canvas:read, canvas:write |
save_image_to_canvas | canvas:read, canvas:write, asset:read, image:generate |
run_canvas_workflow | canvas:read, canvas:write, asset:read, image:generate, job:manage |
generate_image_in_canvas | canvas:read, canvas:write, asset:read, image:generate, job:manage |
get_canvas_workflow_run | canvas:read, job:manage |
find_video_models | video:generate |
generate_video | canvas:read, asset:read, video:generate, job:manage |
get_video_job | job:manage |
The two guide tools require no scope at all, which is why an agent can always read the rules regardless of how narrowly it is provisioned.
Reading an authentication error
| Response | Means | Do this |
|---|---|---|
401 invalid_token | No bearer token, a malformed one, or one that expired or was revoked | Reconnect the client. For a local token created with a custom expiry, create a replacement when that expiry passes. |
403 insufficient_scope | The token is valid but lacks a scope the tool needs | The error names the missing scopes. Reconnect or recreate the token with those added. |
403 access_denied | The browser Origin is not allowed | You are calling from an origin Gavana does not accept. |
Both 401 and 403 come back with a WWW-Authenticate header pointing at the protected-resource metadata and listing the required scopes, so a well-behaved client can prompt you to reconnect rather than failing silently.
A scope error at the MCP layer is refused before the handler runs. Nothing partial happens, and no paid work starts.
Credential hygiene
- Never paste a token into a chat, an issue, a commit, or a screenshot.
- Never put a token in a URL — Gavana takes bearer tokens in the
Authorizationheader only. - Treat a scoped preview URL as a credential. The
previewUrlthatget_canvas_imageand successful image runs return carries a single-usetokenquery parameter, and anyone holding that URL can fetch the image until it expires. Do not paste one into a shared document, a ticket, or a public log. Hand people anopen_canvasreview link instead. - Never expose a connected provider’s configuration — keys, endpoints, or account details — in a transcript or a report.
- Use one named token per client so revoking one does not break the others, and so provenance stays readable.
- Use a custom expiry for a temporary or less-trusted device; otherwise revoke the token when that client or device is no longer trusted.
- When you report a problem to support, share the
X-Request-IDvalue from the failing response and nothing else. It identifies the request without exposing a credential or canvas content. - Webhook signing secrets are deliberately kept out of MCP tool arguments so they cannot enter a model-visible transcript. If you need callbacks, use the CLI’s
--webhook-secret-envoption or call the API from a trusted backend.