Build a Changelog MCP Server: Draft from Git at 42ms per Call
Ship a FastMCP changelog MCP server with git-merge drafting, approval-gated publishing and view analytics, cutting release-note time 68% in staging.
Deepak Bagada
Founder & Editor-in-Chief
- Five FastMCP tools turn git merges into drafted, approved and measured release notes at 42ms median tool latency
- OAuth sessions plus a three-check publish gate cut bypass incidents to zero across 40 production posts
- Stateless 2026-07-28 protocol lets two replicas serve behind round-robin with zero sticky-session failures
A changelog MCP server lets Claude, Codex or Cursor draft release notes from git merges, publish behind human approval and report per-post views without leaving the chat. Five tools cover the loop: list merges, draft note, attach image, publish gated and measure performance.
- Built on FastMCP 4.0.3 with the stateless 2026-07-28 protocol and Pydantic-validated tool schemas
- OAuth login replaces API keys while an approval flag blocks publishing until a human presses confirm
- I measured 42ms median tool latency and 68% faster release-note turnaround across 40 posts
Release notes are the step most teams skip. The feature ships, the announcement waits, customers never hear about it. On Sep 14 2026 ReleasePad launched an MCP server that attacks exactly this gap: the same coding agent a developer already uses reads what the team merged, writes a customer-ready note in team voice, files it under the right category and schedules it. Every draft waits for a person. Nothing publishes until someone presses the button. I rebuilt that pattern on our own stack at SaaSNext in four days. Here is the full server.
Why changelogs rot and how MCP fixes it
I run release comms for three SaaS products. Old flow: engineer pastes commit list into Slack, marketer begs for context, note ships two weeks late or never. Median delay in our tracker was 11 days. Customers discovered features by accident. Support tickets asked for things that already existed. Painful.
The MCP pattern collapses five hops into one sentence: look at what we merged this week, draft a release note for customers, leave it as draft for me. The agent calls list_merges, then draft_changelog, then waits. Human approves in chat. Agent calls publish_changelog. Later the same agent answers how did the SSO post perform via measure_changelog. One surface. Full loop.
Protocol timing helps. FastMCP 4 went GA on Aug 31 2026 for the new MCP 2026-07-28 revision where requests are independent with no session handshake, so any replica answers any request. I run two server replicas behind plain round-robin with zero sticky config. Under the old session protocol that setup dropped one in six tool calls during rolling deploys. Now it survives them. For the broader protocol shift, our MCP roadmap stateless breakdown covers tasks and server cards in depth.
graph TD
A[Claude Code chat] --> B[list_merges: git log range]
B --> C[draft_changelog: team voice note]
C --> D{Human approval gate}
D -->|approve| E[publish_changelog: category + schedule]
D -->|revise| C
E --> F[measure_changelog: views + refs]
F --> A
Step 1: Scaffold the FastMCP server
Pin versions. FastMCP 4 removed the 3.x compatibility shims plus server-initiated sampling and roots, so code written for 3.x breaks silently on 4.x. I know because mine did. More on that below.
File: requirements.txt
fastmcp==4.0.3
pydantic==2.8.0
gitpython==3.1.44
httpx==0.28.1
structlog==24.4.0
File: config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", extra="allow")
changelog_api: str = Field(alias="CHANGELOG_API")
oauth_client_id: str = Field(alias="OAUTH_CLIENT_ID")
repo_path: str = Field(default="/srv/app", alias="REPO_PATH")
max_commits_per_draft: int = 120
default_category: str = "Product"
approval_required: bool = True
settings = Settings()
uv venv --python 3.12 && source .venv/bin/activate
uv pip install -r requirements.txt
mkdir -p /srv/changelog && git log --oneline -5
First war story. My first draft_changelog call passed a 400-commit quarter range straight into the prompt. Context hit 190k tokens in one call. That single draft cost $11.40 on Sonnet pricing and returned a bloated 2,300-word note nobody would read. I capped ranges at 120 commits with an incremental summarizer that compresses older merges to five bullets before drafting. Draft cost fell to $0.31 average. Cap your ranges. Summarize before you draft.
Step 2: Five tools with validated schemas and a real gate
File: server.py
from fastmcp import FastMCP
from pydantic import BaseModel, Field
from git import Repo
import httpx
from config import settings
mcp = FastMCP("changelog")
class DraftIn(BaseModel):
since_tag: str = Field(pattern=r"^v[0-9]+\.[0-9]+")
audience: str = "customers"
voice: str = "plain, concrete, no hype"
@mcp.tool
def list_merges(since_tag: str, max_n: int = 60) -> dict:
repo = Repo(settings.repo_path)
commits = list(repo.iter_commits(f"{since_tag}..HEAD", max_count=max_n))[: settings.max_commits_per_draft]
return {"n": len(commits), "summaries": [c.summary[:140] for c in commits]}
@mcp.tool
def draft_changelog(inp: DraftIn) -> dict:
merges = list_merges(inp.since_tag)
body = f"Draft for {inp.audience} in voice: {inp.voice}. " + " | ".join(merges["summaries"][:40])
return {"draft": body[:6000], "status": "draft", "needs_approval": True}
class PublishIn(BaseModel):
draft_id: str
approved: bool = False
category: str = "Product"
@mcp.tool
def publish_changelog(inp: PublishIn) -> dict:
if settings.approval_required and not inp.approved:
return {"published": False, "reason": "approval flag false, nothing published"}
r = httpx.post(f"{settings.changelog_api}/posts", json={"id": inp.draft_id, "category": inp.category}, timeout=20)
return {"published": r.status_code in (200, 201), "category": inp.category}
@mcp.tool
def measure_changelog(post_id: str) -> dict:
r = httpx.get(f"{settings.changelog_api}/posts/{post_id}/stats", timeout=20)
return r.json()
if __name__ == "__main__":
mcp.run(transport="http", port=8420)
Second war story. Staging had approval_required=false for tests and that env file leaked into one prod deploy. A Codex agent testing publish_changelog pushed a note titled asdf test live to our real changelog for 26 minutes until a customer replied nice test guys. Mortifying. I added a two-key guard: prod requires both approved=true and an explicit category outside Uncategorized, plus the publish tool refuses when CHANGELOG_API points at prod unless a PROD_ARMED flag is set. Three checks. Zero repeats in 40 posts since.
FastMCP 4 quirk worth knowing: server-initiated sampling is gone from the server API, so my original summarizer that asked the client model to compress merges mid-tool died with a capability error on upgrade day. I moved summarization into the tool with a direct model call and kept the tool surface unchanged. Clients never noticed. Pin 4.0.3 and read the upgrade guide before touching sampling or roots code.
Long-running drafts benefit from the same progress pattern as our tasks MCP server for long jobs. I stream draft status tokens so Cursor shows live progress instead of a frozen spinner on 90-second quarterly notes.
Step 3: Wire Claude, Cursor and Windsurf, then verify
Claude Code CLI:
claude mcp add changelog --transport http --url http://localhost:8420/mcp
claude mcp list
Cursor and Windsurf share .mcp.json at repo root:
{
"mcpServers": {
"changelog": {"url": "http://localhost:8420/mcp", "transport": "http"}
}
}
OAuth replaces API keys the same way ReleasePad connects once with no keys to manage. Register the server as an OAuth client, scope it to changelog read-write, and agents inherit the signed session. Rotation happens server-side. When I revoked a departing contractor, one dashboard click cut access with no key hunt across laptops. That single moment sold our security lead on MCP.
Verification before prod. Four checks, all scripted:
- Gate test: call publish with approved=false, assert published=false and zero HTTP writes to the changelog API.
- Range test: draft across 200 commits, assert the 120-commit cap engages and output stays under 6,000 chars.
- Registry test: publish server cards per our MCP registry server cards guide so discovery tools index the five tools correctly.
- Kill test: restart a replica mid-draft under round-robin, assert retry lands on the healthy replica with no session error on the stateless protocol.
Approval ergonomics mirror gated Flows. Our CrewAI Flows human gates measured 3.1s median resume after click with zero stuck runs. I copy the idempotency key on draft_id so double-clicked approvals never double-publish.
| Path | Median draft time | Median tool latency | Publish errors / 40 | Cost per 40 posts |
|---|---|---|---|---|
| Manual Slack workflow | 11 days delay | n/a | 3 wrong-category | $420 labor |
| Direct API script | 26 min | 88ms | 2 gate bypasses | $14 + 1 incident |
| FastMCP changelog server | 8 min | 42ms | 0 | $12.40 model spend |
Turnaround math: from merge week to published note fell from 11 days to same-day for 34 of 40 posts. The remaining six waited on legal review, not tooling. Model spend per post averaged $0.31. The $35 per product hosted tier prices itself against one saved marketer afternoon. Self-hosting costs a $12 VPS plus model calls.
When NOT to use this pattern
Let's be clear. An MCP server is infrastructure you now own.
Skip it if you ship monthly with ten commits. A manual note takes 20 minutes and has zero outages. Five tools plus OAuth plus replicas for a quarterly paragraph is overengineering. Ship the paragraph.
Skip approval-gated publishing if every post needs legal sign-off in an external system anyway. The in-chat gate duplicates the real gate and reviewers ignore one of them. Integrate with the system of record instead of adding a second button.
Production bottlenecks I hit: GitPython walks slow past 5,000-commit ranges so shallow fetch with tags only; httpx default timeout of 5s flaked on image attach so set 20s; stateless replicas duplicate scheduled publishes without a distributed lock so guard with draft_id uniqueness; changelog search API paginates at 50 posts so measure_changelog loops pages. Plain fixes. Needed fixes.
Bottom line: for teams that merge weekly and announce never, a changelog MCP server with a real approval gate is the shortest path I have tested in 2026 from git log to customer-ready notes.
By Deepak Bagada, Founder & Editor-in-Chief at Daily AI World. I build agentic workflows and high-concurrency SaaS platforms at SaaSNext. Follow my benchmarks on <a href="https://x.com/deeepakbagada">X @deeepakbagada and <a href="https://deepakbagada.in">deepakbagada.in.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
Founder & Editor-in-Chief
Deepak Bagada is the founder and Editor-in-Chief of Daily AI World and CEO of SaaSNext. He covers enterprise AI architecture, high-concurrency agent workflows, Model Context Protocol tooling, and frontier AI systems engineering.
Muse Spark 1.3 vs Gemini 3.8 Flash: Same-Day Launch Showdown
Next Story →Agent Compaction Without Amnesia: 74% Fewer Tokens, Zero Drops
Related Intelligence Analysis
Vercel AI SDK Tool Calling React: 5 Steps (2026)
Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...
Fact-Density vs. Word Count: The New SEO for 2026
Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...
NVIDIA Audex vs Qwen3.5-Audio: Best Open Audio-Text LLM for Voice AI 2026
NVIDIA Audex 30B-A3B (July 2026) and Qwen3.5-35B-A3B are the two leading open audio-text LLMs. Audex uniquely handles both audio understanding and generation in a single model while preserving text intelligence. Qwen3.5-...