Skip to main content
Glama
vaggeliskls

release-notes-mcp

by vaggeliskls

release-notes-mcp

A small, generic MCP server that combines GitHub releases from several repositories into a single product release note. The server just fetches and bundles raw data; the LLM synthesizes the final notes.

Nothing is architecture-specific:

  • provider — which forge to read releases from: github (default), gitlab, or gitea/Forgejo. Release fetching goes through a small adapter, so adding a forge means normalizing its release JSON — a contained change.

  • repos — the repos the server is allowed to read releases from.

  • contextSources — arbitrary URLs loaded as background context (a style guide, a versions file, feature names — anything). The server assigns no meaning; what each source is is decided by what you put behind the URL.

Configuration

Config holds no secrets — only the repo set and context. Provider and auth come from the environment.

// config.json — non-sensitive (required; the server errors if it's missing)
{
  "repos": [
    "myorg/auth-service",
    "myorg/web"
  ],
  "contextSources": [
    {
      "name": "release-info",
      "url": "https://example.github.io/whatever/release.json",
      "description": "Extra context to consult when assembling release notes"
    }
  ]
}

Environment (provider-agnostic, set in .env or your shell):

Var

Purpose

Default

TOKEN

Auth token for the provider — never in config

(empty; ok for public repos)

PROVIDER

github | gitlab | gitea (overrides config)

github

BASE_URL

API base — only for self-hosted GitLab / Gitea

provider default

  • format on a context source is optional — auto-detected from Content-Type / URL extension / content sniffing. Override only when wrong.

Token permissions

The server only ever reads releases (GET /repos/{owner}/{repo}/releases…), so give TOKEN the minimum read scope — never write access.

Provider

Public repos

Private repos

GitHub — fine-grained PAT

no token needed

Contents: Read-only (releases live under Contents), for each repo you list

GitHub — classic PAT

no token needed (or public_repo)

repo scope

GitLab

no token needed

read_api scope

Gitea / Forgejo

no token needed

read:repository scope

For GitHub, a fine-grained PAT scoped to just the repos in config.json with Contents → Read-only is the tightest setup and is all this server requires.

The config (repos + contextSources) must come from one of two places — the server errors on startup if neither is set:

Source

Use it for

RELEASE_MCP_CONFIG_JSON

The config as inline JSON. No file needed — ideal for uvx / MCP hubs where everything is an env var.

RELEASE_MCP_CONFIG

Path to a config.json file (default ./config.json). Used by the container, which mounts a real file.

Inline JSON wins when both are set. Copy config.example.json to get started with the file approach.

Tools

Tool

Purpose

list_repos()

The configured repos

list_releases(repo, limit)

Recent releases for one repo

get_latest_version(repo)

Newest release for one repo

get_release(repo, tag)

Full notes for one tag

compare_releases(repo, from_tag, to_tag)

All releases between two versions

gather_release_notes(selections[])

Bundle raw notes from N (repo, tag) pairs (concurrent)

get_context(name?)

Load configured context URLs (auto-detected format)

Selection is dynamic — you (or Claude) pass the (repo, tag) pairs to combine. The server's instructions tell Claude to call get_context() first.

Run

The server runs in a container over HTTP transport on localhost:8000. First create the config and env files (both runs need them):

cp config.example.json config.json   # edit repos + contextSources (no secrets)
cp .env.example .env                  # set TOKEN (+ PROVIDER / BASE_URL if needed)

Normal run

docker compose up -d

Local development — docker compose watch

For local dev, docker compose watch keeps the server live while you edit:

docker compose watch

Change

Action

server.py

sync + restart — copied into the container, process restarts

requirements.txt, Dockerfile

rebuild — image is rebuilt automatically

config.json

bind-mounted (live); run docker compose restart to reload it

Run with uvx (no clone, no container)

The server is published to PyPI, so a client can launch it on demand with uvx — no checkout and no Docker:

uvx release-notes-mcp

uvx talks to the server over stdio (the default transport). Since there's no file to mount, pass the config inline as JSON via RELEASE_MCP_CONFIG_JSON (everything is env-only — ideal for MCP hubs):

RELEASE_MCP_CONFIG_JSON='{"repos":["myorg/web"],"contextSources":[]}' \
  TOKEN=ghp_... uvx release-notes-mcp

Prefer a file? Point RELEASE_MCP_CONFIG at an absolute path instead (uvx runs from an unknown working directory, so a relative path won't resolve):

RELEASE_MCP_CONFIG=/abs/path/config.json TOKEN=ghp_... uvx release-notes-mcp

Register with Claude Code

HTTP (container) — point Claude Code at the running server by its URL:

claude mcp add --transport http release-notes http://localhost:8000/mcp

stdio (uvx) — let Claude Code launch the server as a subprocess:

claude mcp add release-notes \
  --env RELEASE_MCP_CONFIG=/abs/path/config.json \
  --env TOKEN=ghp_... \
  -- uvx release-notes-mcp

Then ask Claude: "Combine the latest releases of auth-service and web into a product release note."