Skip to main content
Glama

platform-mcp

MCP server for the infrastructure services of the sonar-prod cluster — Argo CD, Vault, and Keycloak — with SSO login (GitLab for Argo/Vault, FreeIPA for Keycloak).

Why

The agent in the editor needs access to Argo CD, Vault, and Keycloak, but giving it a service account is not an option: the audit trail would show a shared account instead of a person, and the permissions would be broader than those of any individual developer.

This package is installed locally and performs a regular SSO login through the browser. It then executes commands on behalf of the logged-in user: the audit logs show the real login, and the permissions are exactly those granted by group membership.

There is exactly one tool per service — argocd_exec, vault_exec, and keycloak_exec, which accept command-line arguments. Under the hood are the official CLIs (argocd, vault, kcadm), so everything they can do is available. A new service is added with a single interface implementation.

Related MCP server: mcp-read-only-argocd

Installation

Step 1. Access to the package registry

Needed once and for all methods below: the package lives in the npm registry of this GitLab project, not in public npm. Get a token with the read_package_registry permission (a personal access token or a project deploy token) and add it to ~/.npmrc:

@sonar:registry=https://git.sonar-corp.ru/api/v4/projects/98/packages/npm/
//git.sonar-corp.ru/api/v4/projects/98/packages/npm/:_authToken=<ваш gitlab токен>

Step 2. Connecting to the editor

Claude Code and Cursor — via the plugin. The repository is itself a plugin catalog, so two commands are enough:

/plugin marketplace add https://github.com/K-manankov/platform-mcp.git
/plugin install platform-mcp

The address is GitHub, not GitLab, and that is not a typo — see Why the plugin catalog is on GitHub.

The Argo CD, Vault, and Keycloak addresses are already set in the plugin — nothing to configure. Updates arrive on their own: the plugin starts the server via npx -y, meaning it always uses the latest published version. To update the plugin itself — /plugin marketplace update.

Claude Desktop does not install plugins of this format, so there the entry is made manually. Install the package globally:

npm install -g @sonar/platform-mcp

and add it to claude_desktop_config.json (Settings → Developer → Edit Config). The path to node and to the server must be absolute: GUI applications on macOS do not inherit PATH from the shell. Check your paths with which node and which platform-mcp:

{
  "mcpServers": {
    "platform": {
      "command": "/opt/homebrew/bin/node",
      "args": ["/opt/homebrew/lib/node_modules/@sonar/platform-mcp/dist/index.js"],
      "env": {
        "ARGOCD_BASE_URL": "https://argocd.infra.sonar-corp.ru",
        "VAULT_ADDR": "https://vault.infra.sonar-corp.ru",
        "KEYCLOAK_BASE_URL": "https://auth.infra.sonar-corp.ru",
        "PLATFORM_MCP_INSECURE": "true"
      }
    }
  }
}

Argo CD, Vault, and Keycloak do not need to be installed separately in any of the options: the server itself downloads the required CLI versions on first use (see Where the CLIs come from). For kcadm, the machine must have Java 17+.

Why the plugin catalog is on GitHub

Claude Desktop only connects plugin catalogs from GitHub. In addition, our GitLab lives on the internal network and is not reachable from outside at all, so it would not even reach git.sonar-corp.ru.

That is why the source code stays in GitLab, while github.com/K-manankov/platform-mcp has a mirror of protected branches configured. Only one branch is protected — main, and that is exactly what goes to GitHub on every push. There is no reverse sync: changes are made only in GitLab; the GitHub copy exists for plugin installation purposes.

The mirror itself does not expose anything extra — it contains the same public npm package and the addresses of internal services, which still only resolve from inside the network. There are no secrets in the repository and there should not be: the server keeps access tokens in ~/.config/platform-mcp/, and the package registry token is set up by each person themselves in ~/.npmrc.

To update the installed plugin after changes:

/plugin marketplace update sonar-infra
/plugin update platform-mcp

Login

VPN is required: the names argocd.infra.sonar-corp.ru, vault.infra.sonar-corp.ru, and auth.infra.sonar-corp.ru only resolve from inside the network. From outside, the public wildcard *.infra.sonar-corp.ru catches them, and the request silently goes somewhere else — the check dig +short argocd.infra.sonar-corp.ru should return 192.168.88.106.

The easiest way is to log in right from the dialog: ask the agent to call argocd_login, vault_login, or keycloak_login, open the link it gives you, and complete the login. No need to restart the editor.

The same from the terminal, if the package is installed globally:

export ARGOCD_BASE_URL=https://argocd.infra.sonar-corp.ru
export VAULT_ADDR=https://vault.infra.sonar-corp.ru
export KEYCLOAK_BASE_URL=https://auth.infra.sonar-corp.ru
export PLATFORM_MCP_INSECURE=true   # пока нет настоящих сертификатов, см. TLS

platform-mcp login             # во все настроенные сервисы подряд
platform-mcp login keycloak    # только в один

A browser will open: for Argo CD and Vault — GitLab SSO, for Keycloak — FreeIPA in the realm master (client platform-mcp-cli, see bootstrap in infra). Sessions are stored in ~/.config/platform-mcp/ with 0600 permissions and are shared across all editors: log in once, and you are logged in everywhere.

Over SSH or in a devcontainer where there is no browser:

platform-mcp login --no-browser

The link from the output must be opened on your own machine; the port 8085 (Argo CD), 8250 (Vault), or 8280 (Keycloak) must be forwarded to the host where the command is running.

Logging in as a Vault administrator

A regular login goes to the oidc mount point, where the policy is granted based on membership in a subgroup. Full storage permissions live in a separate oidc-admin mount and are only available to Owners of the infra/k8s group — why that is is described in platform/vault-config/40-groups.yaml:

VAULT_OIDC_MOUNT=oidc-admin platform-mcp login vault

Configuration

Changing anything is optional — the addresses are already set in the plugin.

Cursor. Plugins → Configure for platform-mcp: Argo CD, Vault, and Keycloak URLs, PLATFORM_MCP_INSECURE, and the Vault OIDC mount (oidc — regular login, oidc-admin — full permissions for Owners of infra/k8s). The defaults match the sonar-prod cluster.

Claude Code and manual config. If you need something different (your own instance, oidc-admin, your own restrictions), override with environment variables in the editor config or put them in ~/.config/platform-mcp/config.json:

{
  "argocdUrl": "https://argocd.infra.sonar-corp.ru",
  "vaultUrl": "https://vault.infra.sonar-corp.ru",
  "keycloakUrl": "https://auth.infra.sonar-corp.ru",
  "vaultOidcMount": "oidc",
  "policy": {
    "requireConfirmation": true,
    "denyVaultPaths": ["kv/infra/"]
  }
}

It is enough to set the address of at least one service — the rest simply will not appear in the list of tools.

If there is no session or it has expired, the tools return a clear error, and the agent can call argocd_login / vault_login / keycloak_login right from the dialog — no need to restart the editor. These tools open a browser and immediately return the link, without waiting for the login to complete: a person takes minutes going through SSO, while the request timeout for MCP clients is usually 60 seconds. The result is checked with a separate *_auth_status call.

Commands

platform-mcp                    # MCP-сервер поверх stdio (так его запускает редактор)
platform-mcp login [сервис]     # интерактивный вход, --no-browser для headless
platform-mcp status [сервис]    # кто вошёл и до какого момента действует токен
platform-mcp logout [сервис]    # удалить сохранённую сессию

The service is argocd, vault, or keycloak; without it, the command applies to all configured services.

Tools

For each service: <service>_exec, <service>_login, <service>_auth_status, <service>_logout.

argocd_exec, vault_exec, and keycloak_exec accept args — an array of command-line arguments:

argocd_exec { "args": ["app", "list", "-o", "json"] }
argocd_exec { "args": ["app", "sync", "team-a-api"] }
vault_auth_status   # сначала: username, role, policies
vault_exec  { "args": ["token", "lookup"] }
vault_exec  { "args": ["kv", "list", "kv/teams"] }
vault_exec  { "args": ["kv", "get", "kv/teams/team-a/postgres"] }
keycloak_exec { "args": ["get", "realms"] }
keycloak_exec { "args": ["get", "users", "-r", "sonar-prod", "-q", "username=alice"] }

For Vault, start with vault_auth_status: the policies immediately show whether KV access is available. ["token","lookup"] is the CLI canonical form (not lookup-self). sys/mounts is often 403 for regular OIDC users — do not use it for discovery. Exit code 2 from kv list usually means "empty or no list ACL", not "try a different mount".

Arguments are always passed as an array and never joined into a string: the shell is not involved, so ; and $(...) in arguments remain plain text.

The address and token are substituted by the server. Flags that override them (--server, --auth-token, --config, --core for Argo CD; -address, -tls-skip-verify for Vault; --server, --config, --no-config for Keycloak) are forbidden — otherwise the working token from the child process environment could be sent to a foreign host.

Confirmation of dangerous operations

Read-only commands execute immediately. For Argo CD and Vault, everything else requires user confirmation.

Any command that is not recognized as read-only is considered mutating: the list of verbs is closed on the safe side, so an unfamiliar command will fall under confirmation rather than slip past it.

If the client supports MCP elicitation, a regular dialog appears. If not, a fallback scheme works: the first call returns a description of the consequences and a one-time token; the second call with that token performs the operation. The token lives for 5 minutes and is tied to the specific arguments, so "confirmed one thing, executed another" will not pass, and the agent cannot invent it on its own.

Keycloak is an exception: mutations execute immediately, but a warning is added to the response to the agent — the config goes through CR/operator, and manual edits via kcadm may be overwritten by the operator during sync. Manifests in Git are preferred.

The following are completely forbidden:

  • login and logout (argocd login, vault login, kcadm config …) — the session is managed by the server itself;

  • commands that never finish: vault server|agent|proxy|monitor, argocd app logs --follow;

  • argocd admin — managing Argo CD itself;

  • vault operator seal|step-down|init|rekey|generate-root|migrate — failure of any of them takes down the entire storage;

  • modifying Argo CD infrastructure applications (argocd, vault, keycloak, cert-manager, ingress-nginx, …): they are managed from Git via merge requests, not from a dialog with the agent. Reading them is allowed.

The lists are configurable in config.json (policy.denyApplications, policy.denyVaultPaths).

This is protection against agent mistakes, not a security boundary. A member of the infra/k8s group is already an Argo CD administrator (g, infra/k8s, role:admin) and can do the same through the UI. Real permission limits can only be achieved by separating roles in argocd-rbac-cm and Vault policies.

Secrets do not enter the model context

Secret values are stripped from responses, while key names and metadata remain:

  • Vault — values from kv get, read on a KV path, and unwrap. Responses from kv list, kv metadata get, policy read, sys/mounts are not touched: they contain no secrets, and stripping would make them useless.

  • Argo CDdata and stringData of Secret resources, including inside the manifest, liveState, targetState fields, where Argo CD returns manifests as strings with JSON inside. base64 is not encryption.

Workarounds are closed: vault kv get -field=password prints the bare value bypassing JSON, and -format=table gives nothing to strip from — both are rejected with an explanation.

If the values are genuinely needed in the dialog:

export PLATFORM_MCP_ALLOW_SECRET_VALUES=true

A deliberate opt-in: after it, secret contents go to the model provider. By default, view secrets directly in Vault.

Additionally: responses longer than 100 KB are truncated with a hint on how to narrow the request, and the output is marked as data from the cluster — manifests, annotations, and logs are written by people, and the agent must not follow instructions encountered there.

Where argocd, vault, and kcadm come from

The server does not work through a hand-written REST client but through the official CLIs: Argo CD has no Node client at all, Vault's official one is a Go library and the same binary, and the Keycloak Admin API uses kcadm from the distribution. Feature completeness is therefore equal to that of the CLI.

You do not need to install them manually:

  1. If argocd / vault / kcadm (kcadm.sh) is already in PATH — that one is used, nothing is downloaded.

  2. Otherwise, on first use, a pinned version is downloaded from the official releases (github.com/argoproj/argo-cd, releases.hashicorp.com, github.com/keycloak/keycloak) for the current platform. For Keycloak — the entire zip distribution (~170 MB): kcadm is a Java script, not a standalone Go binary.

  3. The checksum is verified before unpacking and before chmod +x. Without this step, everything would boil down to "download from the internet and execute".

  4. The file is placed in ~/.config/platform-mcp/bin/ and reused from then on.

For kcadm, the machine needs Java 17+ (java in PATH or JAVA_HOME). Without it, the server returns a clear error.

The download happens on first use, not in postinstall: postinstall scripts are widely disabled (npm ci --ignore-scripts), and the installation would silently remain incomplete.

The versions are pinned in src/config.ts and match those deployed in the cluster (Argo CD v3.4.5, Vault 2.0.3, Keycloak 26.6.4). When the cluster is updated, they need to be bumped here as well.

TLS

argocd.infra.sonar-corp.ru, vault.infra.sonar-corp.ru, and auth.infra.sonar-corp.ru currently have no real certificates: the Ingress has no certificate secret specified, so ingress-nginx serves its default self-signed one (CN=Kubernetes Ingress Controller Fake Certificate, SAN ingress.local).

Until that changes, an explicit opt-in is required:

export PLATFORM_MCP_INSECURE=true

It disables certificate validation for Node (OIDC login) and prints a warning on every run. The connection remains encrypted, but the server's authenticity is not confirmed, and access tokens travel over this channel. kcadm has skip certificate validation enabled when there's no truststore in the config (warning in CLI stderr).

NODE_EXTRA_CA_CERTS won't help here: the certificate's SAN (ingress.local) doesn't match the hostname, so name validation will fail even with a trusted root CA.

After proper certificates are issued, the option needs to be removed. If they're signed by an internal CA, pointing to the root one is enough — variables are inherited by child CLIs:

export NODE_EXTRA_CA_CERTS=/path/to/internal-ca.pem   # для самого сервера (Node)
export SSL_CERT_FILE=/path/to/internal-ca.pem         # для argocd и vault (Go)

How it works

редактор ──stdio──▶ platform-mcp ──argv+env──▶ argocd ──▶ Argo CD
                    (OIDC, политика,  vault  ──▶ Vault
                     вырезание секретов) kcadm ──▶ Keycloak

Argo CD. Login is Authorization Code + PKCE via Dex. The public client argo-cd-cli is used, which Argo CD registers in Dex automatically along with the redirect URI http://localhost:8085/auth/callback, so there's no need to change argocd-cm for setup. Argo CD accepts id_token as Bearer, not access_token — the latter is opaque in Dex and isn't validated by the API server. The token is refreshed via a refresh token.

The CLI is launched with --grpc-web: ingress-nginx proxies plain HTTP/1.1 to argocd-server (configs.params.server.insecure: true), and raw gRPC doesn't reach it.

Vault. The flow is simpler: PKCE isn't needed because Vault itself exchanges the code for a token — the OAuth app secret is stored in it. The client only needs to raise a listener on http://localhost:8250/oidc/callback (it's pre-registered in allowedRedirectURIs) and return code, state, and client_nonce. The state parameter is generated by Vault itself and placed inside the issued link — that's where it's taken from to validate the redirect. The token is renewed via auth/token/renew-self while it's renewable.

Keycloak. Authorization Code + PKCE via the public client platform-mcp-cli in the master realm (set up once in bootstrap, redirect http://localhost:8280/oidc/callback). Login is via FreeIPA. The access_token (Admin API) is placed in the session. Before kcadm, the server writes a private kcadm.config to ~/.config/platform-mcp/ — not the shared ~/.keycloak/kcadm.config.

Tokens are passed to child processes only via the environment (Argo/Vault) or via a private config file (Keycloak): in argv they'd be visible in ps to any process of the user. The environment isn't inherited entirely — the CLI gets exactly what it needs, without secrets of neighboring services.

Sessions are stored in their own files, not in ~/.config/argocd/config, ~/.vault-token, and ~/.keycloak/kcadm.config: the provider rotates the token on refresh, and a shared file would cause the regular CLIs in the terminal and this server to invalidate each other's sessions.

Development

npm install
npm run build
npm test

Tests cover command classification and prohibitions, secret redaction, one-time confirmation tokens, the absence of a shell when launching the CLI, and a custom ZIP unpacker (needed because HashiCorp delivers vault as an archive, and Node has no built-in unpacker).

Plugin

The repository is both a plugin catalog and the plugin itself:

.claude-plugin/marketplace.json      каталог для Claude Code
.cursor-plugin/marketplace.json      каталог для Cursor
plugins/platform-mcp/
  .claude-plugin/plugin.json         манифест для Claude Code
  .cursor-plugin/plugin.json         манифест для Cursor
  .mcp.json                          сервер для Claude Code — ПЛОСКАЯ карта
  mcp.json                           тот же сервер для Cursor — с обёрткой mcpServers

The server description is duplicated in two forms, and that's not carelessness. Claude Code reads .mcp.json as a flat "name → server" map: with the mcpServers wrapper it silently doesn't pick up the server — the plugin installs and shows as enabled, but no tools appear. Cursor, on the other hand, takes the file by path from mcpServers in its plugin.json, and working plugins for it use the wrapped form. command/args and env keys match; Cursor's env values are ${VAR} placeholders (the variables schema in plugin.json, Configure in the UI), Claude's are literal defaults. npm run check:manifests keeps the forms from diverging.

The server code isn't copied into the plugin: both files launch the published package via npx, so the plugin stays a few small files and doesn't require a rebuild when the server changes.

You can verify changes before pushing by connecting the directory from a local path:

/plugin marketplace add /путь/к/platform-mcp
/plugin install platform-mcp

Publishing

CI (.gitlab-ci.yml) publishes the package to this project's GitLab npm registry automatically on a tag like vX.Y.Z; authentication is via the built-in CI_JOB_TOKEN, no personal tokens are needed in CI.

The version is duplicated in the plugin manifests and needs to be bumped there too:

npm version <major|minor|patch> --no-git-tag-version   # только package.json
# поправить version в обоих plugins/platform-mcp/*/plugin.json
npm run check:manifests                                # сверить
git commit -am "0.X.Y" && git tag v0.X.Y && git push --follow-tags

CI will catch a mismatch: the test job checks versions in the three manifests and the consistency of the two server descriptions, and publish checks the tag version against package.json. Without this, the plugin would remain "unchanged" for the user with a fresh server: both Claude Code and Cursor decide whether to update the plugin by its version.

There's no separate place to publish the plugin: a push to main goes to GitHub via a protected-branch mirror, and users pick up changes via /plugin marketplace update. Note that the plugin is installed from a branch, not a tag: as soon as a change lands in main, it's already available to everyone — even if the version hasn't been released with a tag yet.

F
license - not found
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    An MCP (Model Context Protocol) server that integrates with the ArgoCD API, enabling AI assistants and large language models to manage ArgoCD applications and resources through natural language interactions.
    10
    12
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    A secure MCP server providing read-only access to Argo CD instances using browser session cookies, enabling querying of applications, projects, clusters, and repositories.
    14
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    A Model Context Protocol (MCP) server that enables secure execution of shell commands with a dynamic approval system, audit logging, and command revocation.
    41
    Apache 2.0

View all related MCP servers

Related MCP Connectors

  • Go MCP server for GitLab: 2 dynamic tools reach 1000+ REST/GraphQL actions. Free/CE, no paid tier.

  • MCP server for Argo RPG Platform — connects AI assistants to campaign data via OAuth2

  • A paid remote MCP for CLI tool MCP, built to return verdicts, receipts, usage logs, and audit-ready

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/K-manankov/platform-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server