MCP Server
The Akuity MCP Server lets MCP-compatible AI agents work with the Akuity Platform, Argo CD, and Kargo through authenticated tools. On a self-hosted installation running v0.34.0 or later, every organization can use it without a license change. It is served by your own portal and instance hostnames.
This page covers the parts that are specific to self-hosted installations: the portal URL, identity provider registration, outbound network access, private certificate authorities, instance endpoint checks, and monitoring. Everything else works as described in the MCP Server section:
- Configure MCP access for the organization endpoint toggle, instance access, and permissions.
- Governance Controls for the guardrail level and audit logging.
- Connect MCP clients for client setup.
- Argo CD MCP Access and Kargo MCP Access for direct instance endpoints.
Requirements
- Akuity Platform v0.34.0 or later. Your existing license works; MCP does not need a license change.
portal.urlset to the HTTPS URL that agents use to reach the portal, with no path component. MCP clients refuse a plaintext authorization server, and the OAuth callback the portal registers with your identity provider is derived from this value.- HTTPS hostnames for Argo CD and Kargo instances. The wildcard certificate from Getting Started covers them.
- Outbound HTTPS access from the portal server to your identity provider and, for some clients, to the client's metadata host. See Outbound network access.
Every organization can use MCP, but the organization's platform endpoint and each instance's MCP access are separate settings, both off by default. An authorized user turns them on as described in Configure MCP access.
Enable MCP
-
If the portal signs users in through your own identity provider or Auth0, register the MCP callback and set the matching
sso.oidc.mcpClientIDorsso.auth0.mcpClientIDHelm value before upgrading. See Portal sign-in. Bundled Dex needs no extra registration. -
Upgrade the installation to v0.34.0 or later. On a multi-region installation, upgrade every region before you turn on MCP access for instances, because a region that still runs an older release does not publish the instance's
/mcproute. -
In the portal, open Organization Settings → MCP Access. Turn on the platform endpoint and the instances agents may reach, then click Save. An instance serves its
/mcproute only after MCP access is turned on for that instance and the platform controller reconciles it. See Check instance endpoints. -
Connect a client with the snippets the portal shows on the MCP Access page. They use your platform endpoint,
https://<PORTAL_HOSTNAME>/mcp, in place of thehttps://akuity.cloud/mcpendpoint shown in the general client instructions. The Akuity agent plugin takes the same endpoint, for exampleclaude plugin install akuity@akuity --config endpoint=https://<PORTAL_HOSTNAME>/mcp, andcodex mcp add akuity --url https://<PORTAL_HOSTNAME>/mcppoints Codex at it.
Users must have signed in to the portal at least once before they connect an MCP client. The first
portal sign-in creates the user account; an MCP sign-in for an unknown user fails with
user not found.
Portal sign-in
Interactive sign-in on the platform endpoint uses the same identity provider as portal login. What you need to register depends on how the portal is configured.
| Portal login | What to do |
|---|---|
Bundled Dex under the portal URL (sso.dex.enabled: true, sso.dex.issuerSubPath: true) | Nothing to register. The chart declares a public PKCE client named akuity-platform-mcp with the callback <portal.url>/mcp/oauth/callback, and the portal server reaches Dex inside the cluster. |
Bundled Dex on its own host (sso.dex.enabled: true, sso.dex.issuerSubPath: false) | Nothing to register. The chart declares the same client. Serve the Dex ingress over HTTPS, because agents are sent to it to sign in. |
Your own OIDC provider (sso.dex.enabled: false) | Create a separate public application that uses PKCE and has no client secret. Register https://<PORTAL_HOSTNAME>/mcp/oauth/callback as its only redirect URI and set sso.oidc.mcpClientID to its client ID. sso.oidc.issuer must be an HTTPS URL. |
Auth0 (sso.auth0.enabled: true) | Create a separate public application that uses PKCE, add https://<PORTAL_HOSTNAME>/mcp/oauth/callback to its allowed callback URLs, and set sso.auth0.mcpClientID to its client ID. |
Register the callback once per portal hostname. Individual MCP client callbacks are never registered with the identity provider: the portal validates them and presents only its own callback upstream.
Do not reuse the portal's login client as the MCP client. For direct OIDC, leaving
sso.oidc.mcpClientID empty makes MCP sign-in unavailable. See the
SSO parameters for the chart values.
Microsoft Entra ID (direct OIDC)
Use two app registrations in the same tenant:
- Keep the portal login app's Web redirect URI at
https://<PORTAL_HOSTNAME>/api/auth/callback. Its client ID and secret go insso.oidc.clientIDandsso.oidc.clientSecret. - Create a separate MCP app. Under Authentication → Add a platform → Mobile and desktop
applications, add
https://<PORTAL_HOSTNAME>/mcp/oauth/callbackas a custom redirect URI. This app uses Authorization Code with PKCE and no client secret. Put its client ID insso.oidc.mcpClientID. It needs only OIDC sign-in permissions. - On both apps, add the email optional claim to the ID token under Token
configuration. Confirm that the user's directory account has an email address and that the
issued ID token contains it. Requesting the
emailscope alone does not guarantee an email claim. Without it, portal login can fail withno_email. - Use the tenant-specific issuer and disable bundled Dex and Auth0:
portal:
url: https://akuity.example.com
sso:
oidc:
enabled: true
issuer: https://login.microsoftonline.com/<TENANT_ID>/v2.0
clientID: <PORTAL_CLIENT_ID>
clientSecret: <PORTAL_LOGIN_CLIENT_SECRET>
mcpClientID: <MCP_CLIENT_ID>
scopes: "openid,profile,email,offline_access"
dex:
enabled: false
auth0:
enabled: false
Entra sends groups through the app's token-claim configuration; do not add a groups scope to
this example. For registration details, see Microsoft's
redirect URI
and optional claims
guides. The callbacks for direct Argo CD and Kargo MCP endpoints are configured separately under
Instance sign-in.
Bundled Dex with Okta
This example uses Dex's OIDC connector to sign in through Okta. In Okta, create an OIDC Web
Application for the connector, enable the Authorization Code grant, and register
https://<PORTAL_HOSTNAME>/dex/callback as its sign-in redirect URI. Assign the users or groups
who may sign in. Configure the app to return an email and the groups needed for role mapping.
The two callbacks serve different connections: Okta redirects to /dex/callback, and Dex
redirects MCP sign-ins to /mcp/oauth/callback. The chart registers the MCP client in Dex;
you do not create another MCP app in Okta for this setup.
Supply sso.dex.secret.data.OKTA_CLIENT_SECRET through your secret manager. The chart stores
it in the dex Kubernetes Secret and makes it available to the connector as an environment
variable. Keep secret values out of version control, and keep the portal's existing client ID
and secret when adding the connector.
portal:
url: https://akuity.example.com
sso:
oidc:
enabled: true
clientID: <PORTAL_CLIENT_ID>
clientSecret: <PORTAL_LOGIN_CLIENT_SECRET>
scopes: "openid,profile,email,offline_access,groups"
auth0:
enabled: false
dex:
enabled: true
issuerSubPath: true
secret:
data:
OKTA_CLIENT_SECRET: <OKTA_CONNECTOR_CLIENT_SECRET>
config:
connectors:
- type: oidc
id: okta
name: Okta
config:
issuer: https://<OKTA_DOMAIN>
clientID: <OKTA_CONNECTOR_CLIENT_ID>
clientSecret: $OKTA_CLIENT_SECRET
redirectURI: https://akuity.example.com/dex/callback
scopes: [openid, profile, email, groups]
getUserInfo: true
insecureEnableGroups: true
Use your Okta authorization server's exact issuer; a custom authorization server includes
/oauth2/<AUTHORIZATION_SERVER_ID>. Configure its groups claim and access policy for the
connector app. Keep email verification enabled and confirm that Okta supplies a verified email.
Dex's insecureEnableGroups allows the OIDC connector to pass group claims through. Those
claims can remain stale across refreshes; users may need to sign in again after their groups
change. See the Dex OIDC connector documentation.
The bundled Dex appends sso.dex.config after its own staticClients list. Put connectors and
other settings there, but do not add a second staticClients key, because it can replace the
list that contains the portal login client and akuity-platform-mcp.
For multiple regions, read the bundled-Dex limitation before deploying this configuration outside the primary region.
Auth0 setup
Disable OIDC and bundled Dex when selecting Auth0. If an OIDC client ID is still configured through the enabled OIDC settings, the portal uses OIDC before Auth0.
sso:
oidc:
enabled: false
dex:
enabled: false
auth0:
enabled: true
domain: example.us.auth0.com
audience: <AUDIENCE>
clientID: <PORTAL_CLIENT_ID>
cliClientID: <CLI_CLIENT_ID>
mcpClientID: <MCP_CLIENT_ID>
Use a Regular Web Application for portal login and a separate Native application for MCP. Set the MCP application's authentication method to None, allow Authorization Code and Refresh Token grants, and register the callback shown above. The account's email must be verified.
If the API identified by sso.auth0.audience uses per-app authorization, grant the portal
application User-Delegated Access under Applications → APIs → Application Access.
Enable Allow Offline Access on the API for refresh tokens. Basic portal and MCP sign-in
needs no Auth0 Management API permissions. See
Auth0 application access.
The current chart does not render AUTH0_CLIENT_SECRET. Supply the portal client secret through
an existing Kubernetes Secret, then add a Secret reference to the portal Deployment through your
post-renderer or Kustomize layer. For example, create Secret auth0-portal with key clientSecret
using your secret manager, and save this patch as auth0-portal-env.yaml:
spec:
template:
spec:
containers:
- name: portal-server
env:
- name: AUTH0_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: auth0-portal
key: clientSecret
kubectl --context <CONTEXT> --namespace akuity-platform patch deployment portal-server \
--type strategic --patch-file auth0-portal-env.yaml
Keep the patch in your deployment configuration and reapply it after Helm upgrades. Do not put
the secret in portal.env, because that value is rendered in a ConfigMap.
Scopes and group mappings
With your own OIDC provider, MCP sign-in requests sso.oidc.scopes, the same scopes portal
login uses. If the provider requires a scope to return groups, include it before using
sso.roleFromGroups; bundled Dex requires groups. Check that both the portal and MCP apps
receive the groups claim. Auth0 group mapping uses the ID-token claim
https://auth.akuity.io/groups, which can be set by a post-login Action.
Global sso.roleFromGroups mappings apply to every organization in the installation. Use explicit
organization membership when access should be limited to one organization. The current
sso.roleFromGroups.admin setting is ignored; use supported member/owner membership and assign
workspace permissions explicitly instead.
Instance sign-in
Direct instance endpoints use the instance's own authentication. The instance MCP Access page shows the exact callback and client to register.
- Argo CD with
oidc.config: registerhttps://<ARGO_CD_HOSTNAME>/mcp/oauth/callbackon the public client. The portal hands outcliClientID, orclientIDwhen noclientSecretis set. Without a public client the direct endpoint is unavailable. - Kargo with OIDC: register
https://<KARGO_HOSTNAME>/mcp/oauth/callbackoncliClientId, orclientIdwhencliClientIdis not set. - Instance Dex (Argo CD
dex.configor Kargo's embedded Dex): nothing to register. Only loopback callbacks onlocalhostare accepted. - Local accounts: when the instance has no SSO configured, Argo CD local accounts and the Kargo admin account sign in through a form the portal serves. This is not a fallback for an instance whose SSO is configured but cannot serve MCP clients; such an instance stays unavailable.
See Argo CD MCP Access and Kargo MCP Access for the full behavior of each shape, and Argo CD SSO and Kargo SSO for how to configure the instance itself.
API keys
An Akuity API key works on the platform endpoint with every sign-in shape. Authenticating with it needs no browser, no identity provider registration, and no outbound call from the portal server to an identity provider or a client metadata host. Tool calls still need the portal server to reach the instances and services they target. Send the key ID and secret as one bearer value:
Authorization: Bearer <API_KEY_ID>:<API_KEY_SECRET>
This applies to the platform endpoint only. A direct instance endpoint accepts the instance's
native Argo CD or Kargo token instead. For a complete client configuration, use the
Claude Code API-key example
and replace its URL with https://<PORTAL_HOSTNAME>/mcp.
Outbound network access
Interactive sign-in makes the portal server send these outbound requests. API key authentication does not send them.
- Identity provider discovery and token endpoints, when the portal or an instance uses your own OIDC provider.
- Auth0 token and revocation endpoints, when the portal uses Auth0.
- Client metadata documents. Some clients identify themselves with a URL instead of a
registered client ID, and the portal server fetches that document from the client's host. Claude
Code uses this method with a document hosted on
claude.ai. When the fetch is blocked, the sign-in is rejected withfetch_failed. In testing, Claude Code did not fall back to dynamic client registration, so the sign-in did not complete.
The bundled Dex is reached inside the cluster and needs no outbound access from the portal server.
For interactive sign-in, allow outbound HTTPS from the portal server to the required hosts.
External OIDC discovery and token requests and client metadata fetches do not use HTTPS_PROXY;
they need direct access. Auth0 token and revocation requests honor HTTP_PROXY, HTTPS_PROXY, and
NO_PROXY. Configuring a proxy for Auth0 does not make client metadata fetches work through that
proxy. If the required requests cannot reach their destinations, use API keys.
DNS proxies that return synthetic 198.18.0.0/15 addresses also trigger the SSRF guard.
Exclude the identity provider and client metadata domains from that DNS mode so they resolve
to their real public addresses; keep the SSRF checks enabled.
Agents that run on a user's machine, such as Claude Code, Codex, and Cursor, reach a private installation over the user's network. Hosted connectors, such as the Claude.ai and Claude Desktop custom connector, connect from the vendor's service and need the portal to be reachable from there.
Private certificate authorities
When the portal, the instances, or the identity provider use certificates from a private CA:
- MCP clients must trust that CA. For Claude Code, set
NODE_EXTRA_CA_CERTSto a PEM file containing the CA before starting it, as described in its network configuration guide. Other clients have their own setting. Do not turn off certificate verification. - The portal server must trust the identity provider's CA for its own outbound calls. Portal login already requires this for your own OIDC provider, and the same trust covers MCP.
- The portal server must also trust the CA of the instance ingress certificates. Platform tool calls that target an instance in another region go over that region's public HTTPS hostname. Those hostnames must be reachable from the primary region, and if the instance uses an IP allowlist, the primary's egress IPs must be allowed. See IP Allowlists.
The current chart has no extra-CA mount setting for portal-server. Use a ConfigMap and a
Deployment patch to mount a bundle containing the image's public roots and your private CA.
The example below uses namespace akuity-platform; replace it and <CONTEXT> with your values.
Repeat it for each region's portal server that needs the CA.
-
Save your private CA certificates as
company-ca.pem. Copy the running image's public roots, combine them with your CA, and generate the ConfigMap manifest:kubectl --context <CONTEXT> -n akuity-platform exec deployment/portal-server -c portal-server -- \cat /etc/ssl/certs/ca-certificates.crt > public-ca.pemcat public-ca.pem company-ca.pem > ca-bundle.pemkubectl --context <CONTEXT> -n akuity-platform create configmap portal-ca-bundle \--from-file=ca-bundle.pem --dry-run=client -o yaml > portal-ca-bundle.yaml -
Save the following strategic merge patch as
portal-ca-patch.yaml. It adds the mount without replacing the Deployment's existing volumes or environment variables:apiVersion: apps/v1kind: Deploymentmetadata:name: portal-servernamespace: akuity-platformspec:template:spec:containers:- name: portal-serverenv:- name: SSL_CERT_FILEvalue: /etc/akuity-ca/ca-bundle.pemvolumeMounts:- name: portal-ca-bundlemountPath: /etc/akuity-careadOnly: truevolumes:- name: portal-ca-bundleconfigMap:name: portal-ca-bundle -
Apply the ConfigMap and patch, then wait for the portal server to restart:
kubectl --context <CONTEXT> apply -f portal-ca-bundle.yamlkubectl --context <CONTEXT> -n akuity-platform patch deployment portal-server \--type strategic --patch-file portal-ca-patch.yamlkubectl --context <CONTEXT> -n akuity-platform rollout status deployment/portal-server
Keep both manifests in your deployment configuration. Add the ConfigMap as a resource and the Deployment patch to your existing Kustomize overlay or Helm post-renderer so upgrades retain them. If you apply the patch manually, check and reapply it after each Helm upgrade. Rebuild the bundle from the new image's public roots when the platform image changes. After updating only the ConfigMap, restart portal-server so it reloads the CA bundle.
Verify the change by running a read against an instance in another region. If an external
identity provider uses the CA, also complete a fresh sign-in. Setting SSL_CERT_FILE without
mounting the bundle does not establish trust.
Check instance endpoints
To check that a route exists, inspect the instance's MCP IngressRoute and connect an MCP client.
A GET to /mcp without credentials returns 401 only when the route is published. Any other answer means the route is not there yet: Argo CD returns 404, and a Kargo hostname falls through to its normal ingress and returns the Kargo UI (200), or 405 for a POST. A 401 still does not prove that MCP works end to end.
Follow Verify the connection,
including an application or project read from an enabled instance in each region. Listing
instances only reads platform records and does not check the connection to each instance.
If users connect to direct instance endpoints, repeat the read through those connections too.
If MCP access is on for an instance but its route is still missing, for example because MCP access was turned on while a region still ran an older release, reconcile the instances once every region runs the new release. The command runs inside the portal server pod, re-applies all instances in one batch, and then waits for them to stay healthy:
kubectl -n akuity-platform exec deployment/portal-server -c portal-server -- \
akputil instance gradual-reconcile --percentage=100 --prune --skip-confirmation
--prune also removes routes that are no longer rendered. Replace akuity-platform with your installation's namespace.
Monitoring
With monitoring enabled, the portal server ServiceMonitor scrapes
the MCP metrics. portal_mcp_tool_calls_total counts every tool call on the platform and instance
endpoints:
| Label | Values |
|---|---|
surface | akp, argocd-instance, kargo-instance |
tool | The tool name, limited to names in the tool catalog |
domain | argocd, kargo, kubernetes, agent, organization |
actor_type | user, apikey, serviceaccount, argocd, kargo, unknown |
read_only | true, false |
outcome | success, tool_error, rejected, oversized |
Calls and reads by endpoint over the last seven days:
sum by (surface, read_only) (increase(portal_mcp_tool_calls_total[7d]))
The most used tools:
topk(10, sum by (tool) (increase(portal_mcp_tool_calls_total[7d])))
The counter has no organization label, because every organization would add a long-lived series. For per-organization activity, use the audit log, which tags changes made through MCP.
Two more counters help with sign-in problems: portal_mcp_external_issuer_failures_total counts
identity provider discovery lookups that failed, and portal_mcp_cimd_rejections_total counts
rejected client metadata documents, both by surface and reason.
Known limitations
- The current bundled-Dex chart points a non-primary region at a local Dex that is not installed there, which can prevent its portal server from starting. See Multi-region SSO.
- Argo CD instances configured with a base path serve the direct endpoint only when they use
dex.config. Withoidc.configor local accounts, the direct endpoint is unavailable. - An identity provider whose hostname resolves to a non-public address works for portal login but not for MCP sign-in with your own OIDC provider. The MCP discovery and token requests block private, loopback, CGNAT (
100.64.0.0/10), and benchmark (198.18.0.0/15) addresses. Fake-IP DNS proxies hand out addresses from the benchmark range. The bundled Dex is not affected. - The bundled Dex keeps tokens in memory. When its pod restarts, MCP refresh tokens stop working and each client signs in again.
portal.urlwith a path component is not supported for MCP.
Troubleshooting
- MCP Access is missing from Organization Settings: the portal runs a version earlier than v0.34.0. Check the running portal version.
- A tool call fails with
the MCP server is not enabled for this organization: the page is there but the platform endpoint is off. Turn it on under MCP Access → Endpoints and save. If the page is missing, see the previous item. - An instance's
/mcpreturns404, or the Kargo UI or405on a Kargo instance: confirm MCP access is on for the instance, wait for reconciliation, and if the route is still missing, run the reconcile command in Check instance endpoints. Argo CD instances with a base path serve the direct endpoint only withdex.config. - Sign-in on the platform endpoint returns
404or never starts: check the matching MCP client ID and callback, and confirm the issuer is HTTPS. The portal server logs aplatform MCPline at startup for your own OIDC provider, for the bundled Dex on its own host, and for Auth0 whensso.auth0.mcpClientIDis not set. The line names the resolved configuration or the missing setting. Auth0 withsso.auth0.mcpClientIDset and the bundled Dex under/dexlog no such line, because nothing needs registering. Look forportal MCP server enabledto confirm the endpoint is on. - The identity provider shows a redirect mismatch: the callback is not registered on the
public client, or it was registered for a different hostname than agents use. The portal builds
the callback from the hostname of the request, so
portal.urland the registered URI must match that hostname exactly. - Claude Code sign-in is rejected or waits forever: check the portal server log for
fetch_failed. If it appears, the portal server could not fetch the client metadata document: allow outbound HTTPS to the client's metadata host or use an API key. If it does not appear, work through the other sign-in items here and in the general troubleshooting page. - The portal server log shows
dial blocked: destination is not a public unicast address: the SSRF guard refused a host that resolved to a non-public address, such as your identity provider or a client metadata host. Make that host resolve to its public address. If a fake-IP DNS proxy answers with198.18.x.xaddresses, see Outbound network access. - Certificate errors in the client or the portal server log: add the private CA to the client and to the portal server. See Private certificate authorities.
no_emailduring portal login: configure the identity provider to include an email in the ID token and check the user's directory email. For Entra, see Microsoft Entra ID (direct OIDC).user not foundafter a successful identity provider login: sign in to the portal in a browser once, then reconnect the client.- A Kargo tool is listed but fails just after an upgrade or after MCP access is turned on: wait for instance initialization and reconciliation to finish. Healthy workloads, a present route, and a successful tools list do not prove that the instance's MCP service-account token is ready. If the error persists, record its reference and inspect both the portal server and platform controller logs. Validate recovery with an actual tool call.
- Every client signed out at once: the bundled Dex restarted. Sign in again.
For sign-in, permission, and guardrail problems that are not specific to self-hosted installations, see MCP Troubleshooting.