io.github.growthbook/growthbook-mcp
Thin MCP server for GrowthBook — skill loader + authenticated API passthrough
Open source Open in the app JSON README (API)
About
Thin MCP server for GrowthBook — skill loader + authenticated API passthrough
Details
- Kind
- MCP servers
- Topic
- No topic detected
- Publisher
- growthbook
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 2.1.0
- Stars
- 23
- Forks
- 24
- Open pull requests
- 13
- Last push
- 2026-08-31T16:20:07Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-08-29 03:02:54
- Updated
- 2026-08-29 03:02:54
- Origin id
io.github.growthbook/growthbook-mcp
README
# GrowthBook MCP Thin
A thin MCP server for GrowthBook with four tools:
| Tool | Purpose |
|------|---------|
| `growthbook_list_skills` | List top-level skill entry points (name + description) |
| `growthbook_read_skill` | Return a listed skill or qualified child workflow (`feature-flags` or `feature-flags/references/flag-create`) |
| `growthbook_api_read` | Authenticated GET passthrough to the GrowthBook API |
| `growthbook_api_write` | Authenticated POST/PUT/PATCH/DELETE passthrough |
Competence lives in the [skills](https://github.com/growthbook/skills) repo and is **bundled at build time**. Capability is split into read vs write API tools (no per-endpoint formatters) so clients can honor `readOnlyHint` / `destructiveHint` correctly.
Tools are prefixed with `growthbook_` so they stay unambiguous when a client has multiple MCP servers loaded.
## Install / run
```bash
npm install
npm run build
```
Point your MCP client at the compiled entrypoint:
```json
{
"mcpServers": {
"growthbook": {
"command": "node",
"args": ["/absolute/path/to/growthbook-mcp/server/index.js"],
"env": {
"GB_API_KEY": "your_api_key_or_pat",
"GB_API_URL": "https://api.growthbook.io"
}
}
}
}
```
Or run the published package:
```bash
npx @growthbook/mcp
```
## Environment variables
| Variable | Required | Default | Purpose |
|----------|----------|---------|---------|
| `GB_API_KEY` | Yes for stdio; optional for HTTP OAuth | — | GrowthBook API key or personal access token |
| `GB_API_URL` | No | `https://api.growthbook.io` | API base URL (self-hosted) and default OAuth AS issuer |
| `GB_MCP_TRANSPORT` | No | `stdio` | `stdio` or `http` |
| `GB_MCP_PORT` | No | `3333` | HTTP listen port (when transport=http) |
| `GB_MCP_HOST` | No | `127.0.0.1` | HTTP bind host |
| `GB_MCP_URL` | Yes for HTTP | — | Public MCP base URL stamped into OAuth resource metadata (server refuses to start in HTTP mode without it) |
| `GB_OAUTH_ISSUER` | No | `GB_API_URL` | GrowthBook OAuth AS issuer URL |
| `GB_HTTP_HEADER_*` | No | — | Extra request headers (e.g. `GB_HTTP_HEADER_CF_ACCESS_TOKEN`) |
| `GB_SKILLS_ENABLED` | No | `true` | Set to `false` / `0` to disable skill tools |
### HTTP + OAuth mode
```bash
OAUTH_AS_ENABLED=1 # on the GrowthBook API
GB_MCP_TRANSPORT=http GB_API_URL=http://localhost:3100 GB_MCP_PORT=3333 npm start
```
Clients connect to:
- `http://127.0.0.1:3333/mcp` — full (skills + API read/write)
- `http://127.0.0.1:3333/mcp/api` — capability-only (`growthbook_api_read` + `growthbook_api_write`)
Unauthenticated requests receive `401` with `WWW-Authenticate` pointing at `/.well-known/oauth-protected-resource`, which advertises the GrowthBook Authorization Server.
Before handling MCP, the server probes GrowthBook REST (`GET /api/v1/`) with the bearer. A `401` from that probe (or later from an API tool) yields HTTP `401` with `error="invalid_token"` so the MCP client can refresh — instead of surfacing `"This API key has expired"` as a tool error. A `403` is treated as an accepted bearer (permission denied ≠ invalid token) so clients are not forced into a refresh loop.
### Capability-only mode
**HTTP (recommended for remote):** point the client at `/mcp/api` instead of `/mcp`:
```json
{
"mcpServers": {
"growthbook": {
"url": "http://127.0.0.1:3333/mcp/api"
}
}
}
```
| Path | Tools |
|------|--------|
| `/mcp` | `growthbook_list_skills`, `growthbook_read_skill`, `growthbook_api_read`, `growthbook_api_write` (unless `GB_SKILLS_ENABLED=false`) |
| `/mcp/api` | `growthbook_api_read`, `growthbook_api_write` only |
**stdio / process-wide:** set env so skills are never registered:
```json
"env": {
"GB_API_KEY": "...",
"GB_SKILLS_ENABLED": "false"
}
```
When skills are disabled, only the API read/write tools are registered. `growthbook_list_skills` and `growthbook_read_skill` are not exposed.
## How skills are bundled
```bash
npm run build # tsc && bundle-skills
```
`scripts/bundle-skills.mjs` copies the top-level skill tree from the canonical skills checkout, preserving structure:
```
skills/<skill>/SKILL.md → server/skills/<skill>/SKILL.md
skills/<skill>/references/<workflow>.md → server/skills/<skill>/references/<workflow>.md
```
Source path resolution:
1. `SKILLS_SRC` env var (path to the skills repo root)
2. `agent-skills.local.json` — `{ "path": "../skills" }`, relative to the repo root. Gitignored; copy `agent-skills.local.json.example`
3. `skills-src/` — what CI and the Docker build vendor
There is no implicit sibling lookup. `../skills` resolves to whatever happens to be at that path, which makes a local build silently disagree with the commit CI builds from.
CI, cloud deploys, and releases all read `agent-skills.lock.json` and check out
that exact skills commit. To ship upstream skill changes, update the commit in
the lock file. Local development can point at any checkout with
`agent-skills.local.json` or `SKILLS_SRC`.
The skills repo stays the source of truth — this package does not maintain a
fork of skill content. New skills flow through automatically, except those named
in the small blocklist in `bundle-skills.mjs`. Currently only `gb-setup` is
blocked because it configures the `gb-call` shell adapter rather than GrowthBook
itself.
Per-skill `scripts/` directories are not copied. Relative
`` `references/foo.md` `` links are rewritten to qualified
`` `feature-flags/references/foo` `` paths so `growthbook_read_skill` can resolve
them.
## Using skills with the API tools
Bundled skills still show workflows as:
```bash
gb-call GET /api/v1/projects
gb-call POST /api/v2/features ./payload.json
```
This MCP server does **not** shell out to `gb-call`. Map `GET` → `growthbook_api_read` and `POST`/`PUT`/`PATCH`/`DELETE` → `growthbook_api_write` with the same path and optional JSON body string. Server instructions and `growthbook_read_skill` output include this bridge note.
## Tools detail
### `growthbook_api_read` / `growthbook_api_write`
```json
{ "path": "/api/v1/projects" }
{ "method": "POST", "path": "/api/v2/features", "body": "{\"id\":\"my-flag\",...}" }
```
- Read: GET only (`readOnlyHint: true`)
- Write: `POST` | `PUT` | `PATCH` | `DELETE` (`destructiveHint: true`)
- Returns raw response body on 2xx
- On non-2xx, returns an actionable error (`isError: true`) covering auth failures, self-hosted 404 hints, and rate limits
- Freeform paths target the [GrowthBook REST API](https://docs.growthbook.io/api)
### `growthbook_list_skills` / `growthbook_read_skill`
Only registered when `GB_SKILLS_ENABLED` is not disabled.
- `growthbook_list_skills` returns top-level skill entry points. An entry may contain a complete workflow or route to child workflows.
- `growthbook_read_skill` accepts a listed top-level name or a qualified child path named by a loaded skill (`feature-flags/references/flag-create`) and returns the full markdown (workflow + guardrails).
## Development
```bash
git clone git@github.com:growthbook/skills.git ../skills
cp agent-skills.local.json.example agent-skills.local.json # edit if not at ../skills
npm install
npm run build
npm start
```
## Standalone HTTP mode
By default the server runs over stdio. Set `GB_MCP_TRANSPORT=http` to run it as a standalone HTTP server that exposes MCP at `/mcp` (skills + API tools) and `/mcp/api` (capability-only), behind an OAuth 2.0 protected-resource surface (RFC 9728 metadata + RFC 6750 `WWW-Authenticate`).
- `GB_MCP_URL` (**required** in HTTP mode) — the server's public base URL. It is stamped into the OAuth resource (audience) and the protected-resource metadata, so it is never derived from request headers. The server refuses to start without it.
- `GB_MCP_PORT` (default `3333`) and `GB_MCP_HOST` (default `127.0.0.1`).
- Incoming bearers are validated by probing the GrowthBook REST API; a rejected token gets HTTP `401` + `WWW-Authenticate` so the client can refresh.
Run it on a trusted network or bound to loopback. For a multi-tenant or public deployment, front it with your own gateway/auth.
## Releases
Cutting a release is deliberate: bump the version in `package.json`, then push a matching `v*` tag:
```bash
git tag v2.0.0
git push origin v2.0.0
```
That tagged commit (with skills frozen at cut time) publishes:
- `@growthbook/mcp` to npm — prereleases (versions with a `-`, e.g. `2.0.0-beta.1`) go under the `beta` dist-tag; stable versions become `latest`
- a multi-arch (`amd64` + `arm64`) image to `ghcr.io/growthbook/growthbook-mcp` (`:<version>`, plus `:<major>`, `:<major>.<minor>`, and `:latest` for stable releases)
- an entry in the MCP registry
- a GitHub Release
Install a release with `npx @growthbook/mcp@<version>` or pull `ghcr.io/growthbook/growthbook-mcp:<version>`.