renovate-mcp
by tibuntu
README.md
# renovate-mcp
[](https://www.npmjs.com/package/renovate-mcp)
[](https://github.com/tibuntu/renovate-mcp/actions/workflows/ci.yml)
[](https://nodejs.org/)
[](./LICENSE)
An MCP server for designing [Renovate](https://github.com/renovatebot/renovate) configurations interactively. Point it at a local repo and let an LLM help you read, validate, preview, and save `renovate.json`.
## Quick start
Needs Node.js ≥ 24.11 on Linux or macOS. With [Claude Code](https://docs.claude.com/en/docs/claude-code):
```bash
npm install -g renovate-mcp
claude mcp add renovate -s user -- renovate-mcp
claude mcp list # renovate should show as connected
```
Then try the prompt: *"List the namespaces available under `renovate://presets`."* A response listing `config`, `docker`, `npm`, … confirms the server is reachable. No tokens are needed for the offline tools. Other clients, tokens and upgrades are under [Install](#install) below.
## What you can do
- **Read and explain configs** — locate the active `renovate.json*`, expand every `extends` preset offline, and trace which preset set each field.
- **Discover presets from intent** — describe what you want ("automerge patches, group dev deps") and get ranked built-in (and your own local-repo) presets, plus a draft config skeleton when nothing fits.
- **Preview custom managers** before running Renovate — regex and JSONata, with Renovate's `managerFilePatterns` glob/regex file selection, file/line hits and extracted dep info.
- **Validate and lint** — schema validation plus a semantic lint pass for Renovate-specific footguns (unwrapped regexes, unknown manager names, deprecated keys).
- **Dry-run** against a local checkout or a remote GitHub/GitLab — see exactly which PRs Renovate would open.
- **Save back atomically** — round-trip writes preserve comments and key order in existing JSON-with-comments files.
## Tools & resources
Nineteen tools, seven resources, and five workflow prompts. Each tool name below links to its full reference in [`docs/tools.md`](docs/tools.md).
| Tool | Purpose |
| --- | --- |
| [`check_setup`](docs/tools.md#check_setup) | Report Renovate CLI + validator availability, versions, install hints, and a `platformContext` block for env diagnosis. Also runs at startup. Pass an optional `repoPath` to add a `repoContext` block diagnosing the repo's git origin, config endpoint/platform, endpoint reachability, and token coverage. |
| [`get_version`](docs/tools.md#get_version) | Report the renovate-mcp server version and whether it's a released or local/dev build. |
| [`read_config`](docs/tools.md#read_config) | Locate and parse a repo's Renovate config in Renovate's own discovery order. |
| [`suggest_presets`](docs/tools.md#suggest_presets) | Search built-in (and optional local-repo) presets by natural-language intent; ranks matches and sketches an unvalidated draft config skeleton for multi-facet or weakly-covered intents. Offline. |
| [`resolve_config`](docs/tools.md#resolve_config) | Expand every `extends` preset offline. Opt in to fetching `github>` / `gitlab>` / `gitea>` / `forgejo>` presets over HTTPS, or Bitbucket Cloud presets via `local>` + `platform`. |
| [`explain_config`](docs/tools.md#explain_config) | Inverse of `resolve_config`: annotate every leaf field with the chain of presets that set it. |
| [`resolve_config_diff`](docs/tools.md#resolve_config_diff) | Offline structural diff of two fully-resolved configs — changed fields plus an order-insensitive set diff of array keys (`packageRules`, `customManagers`, …). The refactor-friendly counterpart to `dry_run_diff`. |
| [`test_package_rules`](docs/tools.md#test_package_rules) | Offline what-if: which `packageRules` match a hypothetical dependency, which matcher decided each, and what each contributes — plus the `branchName` / `commitMessage` / `prTitle` the matched config would render for it. Faithful matching (Renovate's real matchers in a worker); fields you don't supply are reported as unevaluatable, not non-matches, and the rendered names list the post-lookup template variables they could not fill. |
| [`preview_custom_manager`](docs/tools.md#preview_custom_manager) | Preview a `customManagers` entry (regex or JSONata) against a local repo. Offline. |
| [`validate_config`](docs/tools.md#validate_config) | Run `renovate-config-validator` against a file or inline object. |
| [`lint_config`](docs/tools.md#lint_config) | Semantic lint pass for Renovate-specific footguns the schema validator declares valid. Offline. |
| [`dry_run`](docs/tools.md#dry_run) | Run Renovate with `--dry-run` and return the structured JSON report. Local-by-default, flattening `local>` presets with Renovate's own resolver into a temporary copy first; remote with `platform` + `endpoint` + `token` + `repository`. No PRs, no pushes. |
| [`dry_run_diff`](docs/tools.md#dry_run_diff) | Stateless semantic diff between two `dry_run` reports — added / removed / changed updates. |
| [`annotate_dry_run`](docs/tools.md#annotate_dry_run) | Attribute each proposed update in a `dry_run` report to the `packageRules` that caused it; flags rules that never matched and report fields the matchers needed but the report lacked. Stateless + offline. |
| [`explain_dependency`](docs/tools.md#explain_dependency) | Answer "why was/wasn't dependency X updated?" from a `dry_run` report: per-occurrence `skipReason`, proposed `updates`, `warnings`, a one-line verdict and remedy hints; optionally the `packageRules` that matched it. Stateless + offline. |
| [`explain_schedule`](docs/tools.md#explain_schedule) | Answer "when will this `schedule` actually run?": active-now plus the next run windows in UTC and the config's `timezone`, computed with Renovate's own schedule matcher; invalid schedules return Renovate's message and the fact that it then runs at any time. Offline. |
| [`explain_paths`](docs/tools.md#explain_paths) | Show which files Renovate scans under `includePaths` / `ignorePaths` (preset-added ones included, each attributed to its source), flag dead patterns and `ignorePaths` entries that drop files only by substring (`test` dropping `src/latest/`), and count each `matchFileNames` entry against the real repo. Offline. |
| [`migrate_config`](docs/tools.md#migrate_config) | Apply Renovate's built-in migrations and return the migrated config plus a unified diff. Does not write. |
| [`write_config`](docs/tools.md#write_config) | Validate, then atomically write a config to disk. Preserves comments/key order on existing JSON-with-comments files; for `package.json` edits only the nested `renovate` key. |
| [`renovate://presets`](docs/tools.md#renovatepresets) (resource) | Markdown index of all built-in presets grouped by namespace. |
| [`renovate://presets/{namespace}`](docs/tools.md#renovatepresetsnamespace) (resource) | Markdown listing for a single namespace. |
| [`renovate://preset/{name}`](docs/tools.md#renovatepresetname) (resource) | Expanded JSON body for one preset. |
| [`renovate://options`](docs/tools.md#renovateoptions) (resource) | Markdown index of all Renovate config options, split into repository and global/self-hosted-only sections. |
| [`renovate://option/{name}`](docs/tools.md#renovateoptionname) (resource) | Full definition of one config option as JSON. |
| [`renovate://managers`](docs/tools.md#renovatemanagers) (resource) | Markdown list of all manager names, including custom managers. |
| [`renovate://datasources`](docs/tools.md#renovatedatasources) (resource) | Markdown list of all built-in datasource ids. |
**Prompts.** Five workflow prompts package the tool sequences above into ready-to-run starting points. Claude Code surfaces MCP prompts as slash commands (e.g. `/mcp__renovate__design-renovate-config`); other MCP clients expose them through their own prompt picker.
| Prompt | Drives |
| --- | --- |
| [`design-renovate-config`](docs/tools.md#design-renovate-config) | `check_setup` → `read_config` → `suggest_presets` → `resolve_config`/`explain_config` → `validate_config`/`lint_config` → `dry_run` → `write_config` on confirmation. |
| [`debug-package-rule`](docs/tools.md#debug-package-rule) | `check_setup` → `read_config` → `test_package_rules` (matches + rendered names) → `dry_run` (with `reportOutputPath`) → `annotate_dry_run` → `explain_dependency` → a verdict naming rule indices and matchers. |
| [`author-custom-manager`](docs/tools.md#author-custom-manager) | Draft a `customManagers` entry → `preview_custom_manager` iteratively → `validate_config`/`lint_config` → `dry_run` → `write_config` on confirmation. |
| [`onboard-repo`](docs/tools.md#onboard-repo) | For repos without a config: `check_setup` → `read_config` (expect none) → `dry_run` (extract mode) → `suggest_presets` → `lint_config`/`validate_config` → `write_config` on confirmation → full `dry_run`. |
| [`check-config-after-upgrade`](docs/tools.md#check-config-after-upgrade) | After a Renovate bot upgrade: `check_setup` (version comparison) → `read_config` → `migrate_config`/`lint_config`/`validate_config` → `dry_run` (extract mode) → new features from release notes or `renovate://options` → `write_config` on confirmation. |
## Requirements
- **Linux or macOS.** Windows is not supported — `package.json` declares `"os": ["darwin", "linux"]`, so `npm i` surfaces an `EBADPLATFORM` warning on Windows and the server exits with a clear stderr message at startup. Use WSL2 or a Linux/macOS host instead.
- **Node.js ≥ 24.11** (aligns with Renovate's own engine requirement).
Renovate ships bundled — the `renovate` package is a runtime dependency, so `validate_config`, `dry_run`, and `write_config` work out of the box with no separate install. The offline tools (`read_config`, `suggest_presets`, `resolve_config`, `explain_config`, `resolve_config_diff`, `test_package_rules`, `preview_custom_manager`, `lint_config`, `dry_run_diff`, `annotate_dry_run`, `explain_dependency`, `explain_schedule`, `explain_paths`, `migrate_config`) never spawn Renovate at all — nor do `check_setup` (by default it reads version metadata from the bundled install instead of spawning) or `get_version` (it only reports the renovate-mcp server's own version).
**Optional env vars:**
- `RENOVATE_BIN` / `RENOVATE_CONFIG_VALIDATOR_BIN` — override the bundled binaries. When set, the override always wins.
- `RENOVATE_MCP_REQUIRE_CLI=false` — suppress the startup "partial availability" notice when you only intend to use the offline tools.
- Platform + token env vars — see [`docs/platform-setup.md`](docs/platform-setup.md) for the per-platform matrix. Preset fetching and `dry_run` bind every token to its own host; `RENOVATE_TOKEN` only covers the home host named by `RENOVATE_PLATFORM` / `RENOVATE_ENDPOINT`. A `platform` / `endpoint` passed on a call routes requests but never moves a token.
- `RENOVATE_MCP_HOST_TOKENS` — JSON array of `{matchHost, hostType, tokenEnv}` naming the env var that holds the token for an extra host (never the value itself). See [`docs/security.md`](docs/security.md#token-resolution).
- `GITHUB_COM_TOKEN` — authenticates github.com *datasource* lookups (release notes, `github-tags` / `github-releases` / `github-actions`). Distinct from the platform token and never auto-derived from it; set it when github.com-hosted deps are resolved under the default `dry_run` `platform: local`. See [`docs/security.md`](docs/security.md#github_com_token--a-separate-role).
> Heads up: MCP servers do **not** inherit your shell env. Set every env var via the `env` key in `.mcp.json` / `claude_desktop_config.json`. See [`docs/security.md`](docs/security.md#where-env-vars-must-live).
## Install
### 1. Install the package
```bash
npm install -g renovate-mcp
```
Install before you register the server. The package bundles Renovate (about 670 packages, 350 MB), so a client that starts `npx -y renovate-mcp` against a cold npm cache downloads all of that during server startup and can time out.
Upgrade with `npm install -g renovate-mcp@latest` and restart your client. With nvm or fnm, global packages belong to one Node version — reinstall after switching.
### 2. Register it with your client
**Claude Code:**
```bash
claude mcp add renovate -s user -- renovate-mcp
```
`-s user` makes the server available in every project. Leave it out to register it for the current project only, or use `-s project` to write a shared `.mcp.json`.
**Claude Desktop and other GUI clients** don't load your shell's PATH, so with nvm, fnm or Homebrew they may find no Node or an older one. Use absolute paths:
```bash
which node # → "command"
echo "$(npm root -g)/renovate-mcp/dist/index.js" # → "args"
```
Claude Desktop reads `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"renovate": {
"command": "/Users/you/.nvm/versions/node/v24.14.1/bin/node",
"args": ["/Users/you/.nvm/versions/node/v24.14.1/lib/node_modules/renovate-mcp/dist/index.js"]
}
}
}
```
Any other client that can launch a stdio MCP server works with the same command.
### 3. Add tokens (optional)
The offline tools need nothing. Add env vars only for what you use:
| Use case | Env vars |
| --- | --- |
| Local `dry_run` on a repo with github.com dependencies | `GITHUB_COM_TOKEN` (read-only PAT, avoids anonymous rate limits) |
| `dry_run` against GitHub, or private `github>` presets | `RENOVATE_PLATFORM=github` + `GITHUB_TOKEN` |
| `dry_run` against GitLab, or private `gitlab>` presets | `RENOVATE_PLATFORM=gitlab` + `GITLAB_TOKEN` |
| Private presets on another host | `RENOVATE_MCP_HOST_TOKENS` + the variable it names |
Self-hosted GitLab and GitHub Enterprise also need `RENOVATE_ENDPOINT` — see [Platform setup](#platform-setup). Each token is only sent to its own platform's host.
In Claude Code, re-register with `-e`:
```bash
claude mcp remove renovate -s user
claude mcp add renovate -s user -e GITHUB_COM_TOKEN=ghp_xxx -- renovate-mcp
```
- `-e` stores the value in plain text in `~/.claude.json`. Keep tokens in `user` or `local` scope, never in a `project`-scope `.mcp.json` that gets committed.
- `-e` accepts several values, so keep the `--` before `renovate-mcp`.
- Other clients take the same variables in an `"env": { … }` block next to `"args"`.
### Alternatives
- **npx without a global install** — register `npx -y renovate-mcp` instead, after warming the npm cache once with `npm exec --yes --package=renovate-mcp -- node -e 0`.
- **Installer script** — `curl -fsSL https://raw.githubusercontent.com/tibuntu/renovate-mcp/main/install.sh | bash` runs steps 1 and 2 interactively: it checks Node, asks for `npx` or global mode, verifies the server with an MCP `initialize` handshake, and registers it via `claude mcp add` when the Claude Code CLI is on PATH. Flags (after `bash -s --`): `--global`, `--npx`, `--no-mcp-add`, `--mcp-scope=user|project|local`, `--version=X.Y.Z`; also accepted as env vars (`RENOVATE_MCP_GLOBAL=1`, `RENOVATE_MCP_NO_MCP_ADD=1`, `RENOVATE_MCP_VERSION=…`).
- **From source** — clone, run `npm install && npm run build`, and point your client at `node /absolute/path/to/renovate-mcp/dist/index.js`.
## Platform setup
For `dry_run` against a remote platform or `resolve_config` with `externalPresets: true`:
| Setup | `RENOVATE_PLATFORM` | `RENOVATE_ENDPOINT` | Token |
| --- | --- | --- | --- |
| github.com | `github` | (omit) | `RENOVATE_TOKEN` *or* `GITHUB_TOKEN` |
| GitHub Enterprise | `github` | `https://github.example.com/api/v3/` | `RENOVATE_TOKEN` *or* `GITHUB_TOKEN` |
| gitlab.com | `gitlab` | (omit) | `RENOVATE_TOKEN` *or* `GITLAB_TOKEN` |
| Self-hosted GitLab | `gitlab` | `https://gitlab.example.com/api/v4/` | `RENOVATE_TOKEN` *or* `GITLAB_TOKEN` |
| gitea.com / Forgejo (code.forgejo.org) | — | (omit) | `GITEA_TOKEN` / `FORGEJO_TOKEN` |
| Self-hosted Gitea / Forgejo | `gitea` / `forgejo` | `https://forgejo.example.com/api/v1` | `RENOVATE_TOKEN` *or* `GITEA_TOKEN` / `FORGEJO_TOKEN` |
| Bitbucket Cloud | — | (omit) | `BITBUCKET_TOKEN` |
The Gitea / Forgejo / Bitbucket Cloud rows apply to the `resolve_config` family only: `dry_run` runs Renovate as `local` / `github` / `gitlab` and only passes those tokens on as host rules for their hosts. `platform` (`gitea` / `forgejo` / `bitbucket`) and `endpoint` can also be passed as tool inputs, but they only route `local>` presets: a token goes only to its platform's public host or to the env home (`RENOVATE_PLATFORM` at `RENOVATE_ENDPOINT`, where `RENOVATE_TOKEN` comes first), so an endpoint passed only per call needs a `RENOVATE_MCP_HOST_TOKENS` entry. `endpoint` routes `local>` presets only, as in Renovate.
See [`docs/platform-setup.md`](docs/platform-setup.md) for a worked self-hosted GitLab example, `local>` preset handling, and private-registry credentials.
## Example prompts
Once the server is wired up, try prompts like these. Written for Claude but work with any MCP-capable client.
**Understanding an existing config**
- "Read the Renovate config in this repo and summarize what it actually does — expand every preset so I can see the real effective behavior."
- "Resolve my config and list anything that landed in `presetsUnresolved`, with the reason for each."
- "Why is my `prCreation` set to `not-pending`? Use `explain_config` to trace which preset set it."
**Discovering & browsing presets**
- "I want to automerge patch and minor updates and group my dev dependencies — suggest presets for that, search my `../renovate-presets` repo too, and draft a config I can validate."
- "List the presets in the `config` namespace." (uses the `renovate://presets/config` sub-resource — cheaper than pulling the whole index)
- "What does `config:recommended` actually enable? Show me its expanded JSON."
- "Find a built-in preset that pins GitHub Actions digests." (or use `suggest_presets` with that intent)
**Self-hosted GitLab / GitHub Enterprise** (env set per [Platform setup](#platform-setup))
- "Resolve my config with external presets enabled. Its `local>platform/renovate-presets` presets live on our self-hosted GitLab at `https://gitlab.example.com/api/v4`."
- "Resolve my config with external presets enabled. Its `local>acme/renovate-config//base` preset lives on our GitHub Enterprise at `https://github.acme.corp/api/v3`."
- "Dry-run `infrastructure/kubernetes/our-platform` (a nested-group GitLab project) so Renovate can fetch the `local>` presets the config extends."
**Authoring a custom manager (regex or JSONata)**
- "I have `# renovate: datasource=docker depName=...` comments above image tags in my Dockerfiles. Draft a `customManagers` regex entry with `managerFilePatterns: ["**/Dockerfile"]` and preview it against this repo so I can see what it extracts."
- "Here's a `customManagers` entry — preview it and tell me which files match `managerFilePatterns`, which lines hit each `matchStrings` regex, and what dep info gets extracted."
- "My custom manager still uses `fileMatch`. Lint the config, then run `migrate_config` so it becomes `managerFilePatterns`."
- "Draft a `customType: \"jsonata\"` customManager with `fileFormat: \"yaml\"` to extract Helm chart dependencies (`name`, `version`, `repository`) from `Chart.yaml`, and preview it against this repo."
**Validating, previewing, saving**
- "Validate this proposed config against Renovate's schema without writing it anywhere."
- "Do a dry run and show me which PRs Renovate would open — no pushes."
- "Why didn't Renovate update `lodash`? Run a dry run and explain that dependency."
- "Add `:semanticCommits` to my `extends`, validate it, and save back to `renovate.json`."
## Example session
A transcript-style walkthrough: design a Dockerfile custom manager from scratch, validate it, dry-run, and save. Turns are abbreviated — your client will show the actual tool-call JSON.
> **You:** I've got `# renovate: datasource=docker depName=<image>` comments above `FROM` lines in my Dockerfiles. Draft a `customManagers` entry and preview it against this repo.
>
> **Claude** calls `preview_custom_manager` with a first-draft `managerFilePatterns` (`**/Dockerfile`) + `matchStrings`.
> → 4 Dockerfiles matched `managerFilePatterns`, 0 lines matched `matchStrings`. The regex anchored on `ARG`, but the Dockerfiles use `FROM`.
>
> **You:** Rewrite `matchStrings` to anchor on the renovate comment, then `FROM <image>:<version>` on the next line.
>
> **Claude** calls `preview_custom_manager` again with the fixed regex.
> → 4 files, 4 line hits. Extracted: `postgres:15.3-alpine`, `redis:7.2`, `nginx:1.25`, `node:20.11`. Named groups `depName` and `currentValue` populated on every hit.
>
> **You:** Good. Validate the full config inline, with this `customManagers` entry alongside my existing `extends`.
>
> **Claude** calls `validate_config` with the inline config. → valid.
>
> **You:** Now dry-run so I can see what Renovate would actually open.
>
> **Claude** calls `dry_run`. → 2 updates: `postgres` 15.3-alpine → 15.5-alpine, `redis` 7.2 → 7.4. No entries in `problems`.
>
> **You:** Save it.
>
> **Claude** calls `write_config` on `renovate.json`. → validated, written atomically; because the file already existed and parsed as JSON-with-comments, the round-trip serializer preserved the surrounding comments and key order — only the `customManagers` slice was edited in place.
## Development
```bash
npm run dev # build watch mode
npm run typecheck # tsc --noEmit
npm run build # compile to dist/
npm start # run the built server over stdio
npm test # vitest run (builds first)
npm run test:watch # vitest watch mode
npm run test:coverage # vitest run --coverage
npm run generate:presets # regenerate src/data/presets.generated.ts
npm run generate:managers # regenerate src/data/managers.generated.ts
npm run generate:migrations # regenerate src/data/migrations.generated.ts
npm run generate:options # regenerate src/data/options.generated.ts
npm run generate:datasources # regenerate src/data/datasources.generated.ts
npm run sync:jsonata-pin # sync package.json's jsonata pin to Renovate's own
npm run check:snapshot-versions # fail if any src/data/*.generated.ts is stale vs installed renovate
```
See [`docs/development.md`](docs/development.md) for snapshot-file mechanics, CI matrix, the nightly upstream-drift workflow, dependency review, and integration-test setup.
## Further reading
- [`docs/tools.md`](docs/tools.md) — full per-tool reference with all inputs, outputs, and edge cases.
- [`docs/platform-setup.md`](docs/platform-setup.md) — per-platform env vars, worked example, `local>` presets, private-registry credentials.
- [`docs/security.md`](docs/security.md) — token handling, env-var precedence, endpoint allowlist, redirect/body-cap policy, inline-secrets warning.
- [`docs/operations.md`](docs/operations.md) — timeouts, caps, large-report escape hatch, `ok` semantics, RE2 / nodeEnv handling, progress notifications.
- [`docs/architecture.md`](docs/architecture.md) — shell-out vs import, worker isolation, round-trip writer, preset catalogue.
- [`docs/development.md`](docs/development.md) — snapshot mechanics, CI matrix, nightly drift workflow, integration testing.
## What this is NOT
- **Not a Renovate replacement.** This server doesn't open PRs, run scheduled updates, or execute in CI — it's a design-time companion for a local `renovate.json`. Use the real Renovate for the actual dependency-update pipeline.
- **`resolve_config` merges faithfully.** Preset expansion runs offline against a committed snapshot, then folds with Renovate's real `mergeChildConfig` in a worker thread — `mergeQuality: "faithful"` (it falls back to an approximate in-process merge as `"preview"` only if the worker is unavailable). Template substitution still implements only positional `{{argN}}` placeholders — non-positional tokens and Handlebars helpers are flagged in `warnings` and pass through verbatim. For full config resolution (datasource lookups, etc.), run `dry_run`.
- **`preview_custom_manager` is a subset of Renovate's custom managers.** It covers `customType: "regex"` (with `matchStringsStrategy` of `any` / `combination` / `recursive`) and `customType: "jsonata"` (with `fileFormat: "json" | "yaml" | "toml"`). File selection follows Renovate's `managerFilePatterns` semantics (globs or `/regex/`, entries unioned like Renovate's extract phase); the deprecated `fileMatch` is still accepted with a warning. Template substitution is `{{groupName}}` only — full Handlebars helpers/conditionals are not implemented. Other custom types (e.g. `html`) are out of scope. Use it for fast iteration; confirm with `dry_run`. Full coverage matrix in [`docs/tools.md#preview_custom_manager`](docs/tools.md#preview_custom_manager).
- **`dry_run` still uses fake binaries in CI.** The bundled Renovate is available (it's a runtime dependency), and `validate_config` / `write_config` have real-validator integration tests (`test/integration/realValidator.test.ts`) exercising the actual bundled `renovate-config-validator` — but `dry_run` itself still runs per-PR against fake binaries via `RENOVATE_BIN` / `RENOVATE_CONFIG_VALIDATOR_BIN` env overrides, for determinism and speed. A separate nightly workflow re-runs the full suite against `renovate@latest` end-to-end. Run `dry_run` locally against a real config to validate behaviour.
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessLimited data