MCP
Let AI clients like Claude drive your Alaf Server instance as tools, over the Model Context Protocol.
MCP (Model Context Protocol) is a standard way for AI clients — Claude, Cursor, VS Code Copilot, and others — to call an app's features as tools. Point one of those clients at Alaf Server and it can list your projects, kick off deployments, check logs, add domains, and more — using the exact same permissions you'd have yourself.
The important part: it's not a second, looser door into Alaf Server. Every tool call re-runs the full auth and permission stack, so an agent can only ever see and do what its credential is allowed to — nothing more.
Where this lives in the dashboard
Everything below is set up under Settings → MCP. That tab shows the exact endpoint for your instance, a per-client "add" recipe (Claude Code, Cursor, VS Code, Claude Desktop, Windsurf, Zed), and the list of clients you've already connected.
The endpoint
There's one endpoint, and it's the same on self-hosted and Alaf Server Cloud:
POST /api/mcpSo on your instance that's https://<your-host>/api/mcp. A few things worth knowing up front:
- It's a stateless Streamable-HTTP JSON-RPC 2.0 endpoint — request in, response out.
- It's
POST-only. AGETreturns405(there's no server→client stream to open). - No batching — send one JSON-RPC message per request, not an array.
You rarely type this URL by hand. Settings → MCP shows it pre-filled and gives you a copy button.
Connect a client
There are two ways to authenticate, and most clients pick one for you. Use the tab that matches your client.
Alaf Server is a standards-compliant OAuth 2.1 MCP server, so capable clients need nothing but the URL.
Paste the endpoint into your client
Give the client your endpoint — https://<your-host>/api/mcp. In Claude Code that's one command:
claude mcp add --transport http alaf server https://<your-host>/api/mcpOther clients take it through their own UI; Settings → MCP has the exact snippet for each one.
Approve it in the browser
The first time the client connects, a browser window opens. There you choose what the client may do — read-only or full control, and either all resources or a specific set of projects, servers, and repositories. No credential to copy or paste.
You're connected
The client can now call tools within exactly the scope you approved. The connection shows up under Settings → MCP → Connected clients, where you can disconnect it at any time.
The scope you pick is enforced, not advisory
Your choices are stored as the client's grant — the same grant model as a scoped personal access token. The client can never exceed what you granted, or what you can access yourself.
For clients without OAuth support — or when you want a fixed, narrowly-scoped credential in a config file — authenticate with a personal access token as a bearer credential. There's no separate MCP credential.
Create the token under Settings → Tokens (see the Tokens API), then drop the endpoint and token into your client's config:
{
"mcpServers": {
"alaf server": {
"url": "https://<your-host>/api/mcp",
"headers": { "Authorization": "Bearer opsh_pat_…" }
}
}
}For Claude Code, add the header on the command:
claude mcp add --transport http alaf server https://<your-host>/api/mcp \
--header "Authorization: Bearer opsh_pat_…"Give an agent a narrow token
Mint a read-only or scoped token for agents rather than handing over your full access. A read-only token limits the agent to read tools; a scoped token confines it to exactly the projects, servers, and repositories you grant — even below your own role.
Every call is re-checked
This is the heart of the security model, and it's why MCP access is as safe as your token:
- Authenticating once resolves your capability, but that's used only to filter
tools/list— so a client isn't shown tools its token could never use. - The real gate runs on every
tools/call: the endpoint builds an internal request, forwards your bearer, and dispatches it through the same routes, validation, auth middleware, and per-resource permission check as a normal HTTP call.
The practical upshot: a read-only token can only reach read tools, and a scoped token stays boxed into its
granted resources. A scoped token isn't even shown the org-wide list and create tools — get_projects
(list every project) simply won't appear in its tools/list; it sees only the per-resource tools for the
resource types it was granted. And if a tools/call touches something outside that scope, it comes back
404 — the scope doing its job. For the full protocol details (JSON-RPC methods, negotiation, error codes),
see the MCP API reference.
Which tools map to which API
Tools aren't a separate feature set — each one maps onto a permission-tagged REST route, and the tool name
is built from that route's method and path. So get_projects is GET /api/projects, post_domains is
POST /api/domains, and patch_projects_by_id is PATCH /api/projects/:id. Call tools/list to see the
exact set your token can use.
Not every route is a tool: a route becomes one only when it opts in, so the set is a curated slice of the
API rather than the whole surface. Each exposed tool also carries two hints a client can surface —
readOnlyHint (the tool only reads) and destructiveHint (it deletes or tears something down) — so an agent
UI can flag a destructive call before it runs.
| Tool name (examples) | REST route | Reference |
|---|---|---|
get_projects, patch_projects_by_id | /api/projects | Projects API |
get_deployments, post_deployments_build_access | /api/deployments | Deployments API |
get_projects_by_id_services, post_projects_by_id_services | /api/projects/:id/services | Services API |
get_domains, post_domains | /api/domains | Domains API |
get_github_repos | /api/github | GitHub API |
get_analytics | /api/analytics | Analytics API |
get_notifications_channels | /api/notifications | Notifications API |
Credentials and auth are never exposed
The tokens, auth, and mcp modules are hard-blocked from ever becoming tools — a full-access MCP token
can't mint itself a fresh credential or escape its own scope. Manage tokens and connected clients from the
dashboard or the Tokens API, not from an agent.
Verify from the terminal
To confirm a token works and see what it can do, ask for the tool list:
curl -s https://<your-host>/api/mcp \
-H "Authorization: Bearer opsh_pat_…" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'This returns exactly the tools that token is allowed to call.
If something goes wrong
401 — Missing or invalid access token
There's no bearer credential, or it's expired or revoked. The response includes a WWW-Authenticate header
so OAuth clients can start discovery; if you're using a static token, mint a fresh one under
Settings → Tokens and update your client config.
405 on a GET request
Expected. This is a request/response server with no server→client stream, so GET /api/mcp isn't supported.
Always POST your JSON-RPC message.
A tool call returns 404 for a project, server, or repo
tools/list worked but a specific tools/call reports 404 — that's the token's scope working as intended.
It wasn't granted that resource. Re-authorize the client (or mint a token) with the resource included.
Manage connected clients
Every OAuth client you've approved is listed under Settings → MCP → Connected clients, tagged with its access level (read-only or full control) and how many resources it can reach. Disconnect revokes the client's issued tokens immediately and drops its consent, so it has to re-authorize next time. The same list and controls are available programmatically via the Tokens API.