MCP server for AI agents
Homerun serves an MCP server, so an AI agent such as Claude can diagnose and fix your services for you: read their logs, deploys and settings, change them, redeploy, roll back.
| What | Value |
|---|---|
| MCP URL | https://<your dashboard>/api/v1/mcp |
| Transport | Streamable HTTP |
| Sign-in | OAuth 2.1 with PKCE, or an API key in the x-api-key header |
| Client sign-up | Client ID Metadata Documents, or a client an admin registered on IDP |
| Acts as | The account that signed in, or the API key's owner, with their permissions |
It only runs when the Dashboard URL is https (or plain HTTP on localhost),
since MCP clients refuse anything else; on any other address the endpoint
answers 404 saying so. The dashboard also has to be reachable from wherever the
agent runs: for claude.ai that's the public internet.
#Connecting an agent
An agent connects one of three ways: pick the first one it supports.
#Paste the URL (Client ID Metadata Documents)
An MCP client that supports Client ID Metadata Documents (CIMD, the client registration method of the MCP authorization spec) needs nothing more than the MCP URL. Add it as a connector or server in the agent, and the agent sends you to Homerun's sign-in page. Sign in, then Homerun asks whether to allow it, and from then on it acts as you.
The client identifies itself with an HTTPS URL instead of a client ID, and Homerun reads its name and callback addresses from the document at that URL. A document can claim any name, so the consent screen also shows the site it came from: only allow it when that's the site of the agent you're connecting. Once allowed, the client shows up on the IDP page like any other app.
#A client an admin registers (client ID and secret)
For an agent that asks for a client ID and secret instead, an admin registers one first. On the IDP page (under Integrations), click Register app. For claude.ai and Claude Desktop, click Claude connector: it fills in Claude's callback URL, a confidential client with PKCE and the consent screen on. Then click Register app, and the next screen shows the MCP URL, client ID and secret. In Claude, open Settings → Connectors → Add custom connector, paste the MCP URL, and the client ID and secret under Advanced settings.
The Claude connector button only shows once the Dashboard URL is https (or
plain HTTP on localhost).
#An API key (Claude Code and other headless agents)
No OAuth at all: create a key under Profile → API Keys and send it in the
x-api-key header. A key with read permissions only gives an agent that can
diagnose but not change anything.
claude mcp add --transport http homerun \
https://<your dashboard>/api/v1/mcp --header "x-api-key: <key>"#What it can do
| Area | Reads | Changes |
|---|---|---|
| Services | list_services, get_service, get_service_config, service_logs, list_deployments (each deploy attempt's error and log), list_revisions, list_dependencies | create_service (from scratch or a template), update_service, set_dependencies, deploy_service, restart_service, start_service, stop_service, rollback_service |
| Errors | list_errors, get_error | update_error (resolve, ignore or reopen an issue) |
| Stacks and templates | list_stacks, get_stack, list_templates, get_template | create_stack, update_stack |
| Environments and canary | list_environments, get_release_channels | create_environment, update_environment, set_release_channels |
| Pull request previews | list_previews, get_preview | promote_preview |
| Image scans | list_scans, get_scan (with its findings) | scan_service |
| Volumes and backups | list_volumes, list_volume_mounts, list_backups, list_backup_destinations | create_volume, update_volume (its backup settings too), mount_volume, update_volume_mount, unmount_volume, run_backup |
| Cron jobs | list_cron_jobs, get_cron_job, list_cron_job_runs, get_cron_job_run (a run's output) | create_cron_job, update_cron_job (enabled turns its schedule on or off), delete_cron_job, run_cron_job |
| Queue and instance | get_job, list_jobs, system_stats, instance_status | |
| Redirects | list_redirects | create_redirect, update_redirect, delete_redirect |
| Object storage | list_object_stores, list_buckets, get_bucket | create_bucket, update_bucket (expiry and public access), delete_bucket (empty buckets only) |
| Status pages | list_status_pages | create_status_page, update_status_page, delete_status_page |
| Notifications | list_notifications (your own feed, the dashboard's bell), list_notification_channels | mark_notifications_read, create_notification_channel, update_notification_channel, delete_notification_channel |
| Workstations | list_workstations, get_workstation, list_workstation_presets, get_workstation_preset | create_workstation, update_workstation (its login wall too), start_workstation, stop_workstation, recreate_workstation, delete_workstation (deleteVolume drops the home volume), create_workstation_preset, update_workstation_preset (its login wall too), delete_workstation_preset |
| Versions and updates | get_service_version (an image service's pin and the update its last check found, with its trial run), find_pin_tags | pin_service, unpin_service, set_update_watch, check_service_updates, apply_service_update (moves it onto the update and deploys) |
| Monitoring | get_monitoring (the instance's traffic, availability and resources, per service), get_service_monitoring (with its latest uptime probe beats) | |
| System | list_system_logs (Homerun's own warnings and errors), list_system_containers, system_container_logs (Traefik's included), preview_docker_cleanup | run_docker_cleanup (every prune but volumes) |
| DNS | list_domains, list_dns_records, list_dns_connections (no credentials) | create_dns_record, update_dns_record, delete_dns_record |
| Docker registry | list_registry_repositories (images with their tags, digests and the services using them), list_registry_tags, get_registry_settings, list_registry_tokens | |
| Admin config | list_remote_hosts, list_users, list_git_providers, all read only |
Deleting a service, a stack or an environment is deliberately not a tool: do it from the dashboard. Neither are pruning volumes, deleting registry images, IaC or updating Homerun. Workstations are the caller's own, like everywhere else, and never come with their IDE password.
#Credentials and access stay on the dashboard
An agent reads admin config but never changes what holds a credential or grants access: DNS connections, git providers, object stores, remote hosts, registry tokens, users and sign-in. Those tools return metadata only (names, kinds, endpoints, which secret fields are set), never a secret, and no tool creates, changes or deletes them. Connecting one means pasting a secret into the conversation, where it lands in the agent's context and its provider's logs; and an agent able to swap DNS credentials, mint registry tokens or change a user's role can widen its own reach, which a prompt injection in a log line or an error it reads could ask for. Do those from the dashboard, the CLI, the REST API or Terraform. DNS records are the one write here: they hold no secret, and creating one is part of putting a service online.
Every tool goes through the REST API with the caller's own permissions, checked per call: a read-only account, or an API key with read permissions, can diagnose but not change anything. Any signed-in account can connect, so what an agent can do is what its account can do; see Users and roles.
#Secrets
Secrets don't reach the agent, the rest stays readable:
- An env var of a service or a cron job marked secret by hand (see
Marking a variable secret) shows as
[redacted], whatever its name or value. - The same goes for an env var whose name has a secret-looking part (
PASSWORD,SECRET,TOKEN,KEY,AUTH,COOKIE,SIGNING,CREDENTIAL,PRIVATE,SALT…), unless its last part only configures one (OIDC_AUTH_URL,BASIC_AUTH_ENABLED,AUTH_PROVIDER,PRIVATE_KEY_FILE) or its value is a plain boolean or number (ENABLE_PASSWORD_FORM=false). - And so does a value that looks like a credential, whatever its name: a
user:tokenpair with 16 or more letters and digits after the colon, or a standalone token of 24 or more characters mixing upper case, lower case and digits (not a URL, path, image, hostname or version). - Any other value is shown as is, except its inline secrets: a URL's password
(
postgres://app:[redacted]@db:5432/app,user:[redacted]@github.com), a--requirepassor--passwordargument, aBearerorBasiccredential, a sensitive query parameter (?code=,&state=,?token=,?api_key=,?X-Amz-Signature=…) and a baretoken=,password=orapi_key=assignment. Logs get the same: service logs, deploy and job logs, the system log, a cron job's command and a cron job run's output. - In a cron job's script source and its files, a line setting a secret-looking
name (
API_TOKEN=…,export DB_PASSWORD=…,password: …,const apiKey = …) has its value redacted, and every other line its inline secrets. - An error event from error tracking has its request URL,
query string and breadcrumb URLs and messages masked the same way, and its
Authorization,Cookie,Set-Cookie,X-Api-Keyand other credential-carrying headers and its cookies redacted. - A service's uploaded icon shows as
uploadedinstead of its image data.
A workstation's and a workstation preset's env are redacted the same way, by
name and by value.
update_service, update_cron_job, update_workstation and
update_workstation_preset merge env changes into what's stored: send only the
vars to change, null deletes one. A var, a command, a cron job's source or one
of its files sent back exactly as the agent read it, a [redacted] value
included, stays unchanged, and so does an icon sent back as uploaded.
#Revoking access
Every OAuth client, registered by an admin or discovered from its URL, is on the IDP page. From an app's page, revoke one person's tokens or everyone's, or switch the app off: its tokens stop working and nobody can sign in through it until it's back on. Switch off a discovered client rather than deleting it: deleting one only lasts until it next signs someone in, since its URL is all it needs to come back. An API key is revoked under Profile → API Keys.
#For client developers
Homerun follows the MCP authorization spec:
- An unauthenticated call gets a
401withWWW-Authenticate: Bearer resource_metadata="https://<your dashboard>/.well-known/oauth-protected-resource/api/v1/mcp". - That protected resource metadata names the authorization server, whose
metadata is at
https://<your dashboard>/.well-known/oauth-authorization-server/issuer(issuerhttps://<your dashboard>/issuer). It advertisesclient_id_metadata_document_supported: trueand noregistration_endpoint: dynamic client registration (RFC 7591) is turned off, so a client can't create itself a stored client ID and secret. - Authorization is the code flow with PKCE (
S256). Pass the MCP URL as theresourceparameter: the access token is a JWT whose audience is that URL, which is what the MCP endpoint checks. Scopes:openid,profile,email,offline_access(for a refresh token) andgroups. - A metadata document's URL must be
https, resolve only to public addresses (a private, loopback or link-local address is refused withinvalid_clientbefore anything is fetched), and answer without a redirect. The document needsclient_idequal to its own URL,client_nameandredirect_uris, and itstoken_endpoint_auth_methodmust benoneorprivate_key_jwt: a shared secret makes no sense in a public document. Homerun keeps a fetched document for up to an hour, honouring its cache headers, then fetches it again.
#Troubleshooting
- The MCP URL answers 404: the Dashboard URL isn't
https. Set it under Settings → General. - A connector says it can't authenticate: the agent doesn't support metadata documents. Register a client for it, as in A client an admin registers, and give it the client ID and secret.
invalid_clientwith "must not target a private or reserved address": the client's metadata document URL points at a private network. Agents on your LAN need an API key instead.- Tools answer 403: the account (or the API key) lacks the permission that tool needs. The agent can only do what that account can do.
This guide lives in the project repo: edit it there, and this page follows with the next release.