View on GitHub

Turn your Git diffs intodocumentation

01 — What it does

An MCP server for Claude Code that turns your Git changes into a structured Markdown skeleton — ready for Claude to fill in the analysis.

Get started

Output preview

A real documentation.md: diffs in, placeholders for Claude to fill.

From one sentence to a skeleton

The server does the mechanical part — Git, filters, template, file. Claude Code does the understanding and the writing.

  1. 01

    You ask in plain language

    Inside any Git repository, tell Claude Code what to document.

    "Document the changes on this branch, ignoring CSS files, in Portuguese."
  2. 02

    Claude Code picks the parameters

    It translates the request into arguments for the generate_documentation tool.

    {"exclude": ["*.css"], "language": "pt"}
  3. 03

    The server writes the skeleton

    It reads the Git diffs, applies the include/exclude filters, renders a Jinja2 template and writes documentation.md.

    {
      "success": true,
      "output_file": "/path/to/documentation.md",
      "files_processed": 12,
      "files_ignored": 3
    }
  4. 04

    Claude Code fills in the analysis

    It edits the file in place: “Changes made”, “Testing process” and the AI Usage fields, using only real session data.

    - Changes made: [To be filled by AI]

Design principles

No AI API calls

All language understanding (“ignore CSS files”, “in Portuguese”) happens in Claude Code. The server only executes structured parameters: it reads diffs, filters, renders and writes.

core/ owns all logic

cli/ and mcp_server/ are thin adapters that call core.generator.generate_documentation() and format its result. All Git commands live in core/git_diff.py.

Skeleton, not final prose

Diffs are embedded next to [To be filled by AI] placeholders. Claude Code then edits the output file directly to write the actual analysis.

Three ways to pick the diff

Only mode decides which Git command runs. Filtering, rendering and writing are identical for every mode.

working_treedefault

Uncommitted changes

Staged + unstaged changes. When a file has both, the staged diff takes priority.

git diff --cached · git diff

“Document the changes on this branch, ignoring CSS files, in Portuguese.”
tool arguments
{"exclude": ["*.css"], "language": "pt"}

mode omitted — defaults to "working_tree".

commitsingle commit

One commit

Everything a single commit introduced. Without commit_hash it documents HEAD.

git diff-tree -p --root <commit>

“Document what changed in commit a1b2c3d.”
tool arguments
{"mode": "commit", "commit_hash": "a1b2c3d"}

“Document the last commit.” → {"mode": "commit"}

branchwhole branch

A whole branch

The current branch against the merge-base of a base branch (three-dot diff).

git diff <base_branch>...HEAD

“Document everything this branch adds compared to origin/develop.”
tool arguments
{"mode": "branch", "base_branch": "origin/develop"}

base_branch omitted — defaults to "origin/main".

Run it from GitHub or locally

Two ways to plug it into Claude Code. Online needs nothing but uv; local gives you the source to read and change.

  1. 1

    Install uv

    uvx runs Python tools straight from a Git URL. uv can also provide Python 3.10+ if you don't have it.

    terminal
    curl -LsSf https://astral.sh/uv/install.sh | sh
  2. 2

    Register the server with Claude Code

    At user scope, so it's available in every project. On first launch uvx downloads the package from GitHub and caches it.

    terminal
    claude mcp add --scope user --transport stdio gendoc -- \
      uvx --from git+https://github.com/Paulosouzx/mcp-gen-documentation.git gendoc-mcp-server
  3. 3

    Check it's connected

    Or run /mcp inside a Claude Code session.

    terminal
    claude mcp list

Then, inside any Git repository, ask Claude Code something like “Document the last commit.” The server writes documentation.md and Claude Code fills it in.

The generate_documentation tool

Calling it with nothing but project_path documents the uncommitted changes.

NameTypeDefaultMeaning
project_pathstringserver cwdGit repository to analyze
includestring[]none (= all)Glob patterns; only matching changed files are kept
excludestring[]noneGlob patterns to drop, applied after include
output_filestringdocumentation.mdOutput path, relative to project_path unless absolute
language"en" | "pt""en"Skeleton language
titlestringlanguage defaultDocument title
templatestring"default"Template name, resolved as templates/{template}_{language}.md.j2
mode"working_tree" | "commit" | "branch""working_tree"Diff source
commit_hashstring | nullnull (= HEAD)Commit to document, used when mode="commit"
base_branchstring"origin/main"Base branch to diff against, used when mode="branch" (base_branch...HEAD)

Returns

result
{
  "success": true,
  "output_file": "/path/to/documentation.md",
  "files_processed": 12,
  "files_ignored": 3
}

On failure (success: false) an error field explains what went wrong: not a Git repo, unknown template, unwritable path.

Output

documentation.md
Documentation of changes
Branch: feat/landing-page
Commit: [describe in English]

AI Instructions:
- Fill in only the "Changes made", "Testing process" and "AI Usage" fields below.
- Keep exactly the structure of this document.
- Do not include the diff blocks in the final response.
- For "AI Usage", fill in fields only with real data from the current session, if available. Never invent values — leave a field empty if the information isn't available.

- cli/main.py:
    - Changes made: [To be filled by AI]

    Diff analysed:
    ```diff
    diff --git a/cli/main.py b/cli/main.py
    index 41b5d22..36e3cfe 100644
    --- a/cli/main.py
    +++ b/cli/main.py
    @@ -20,6 +20,14 @@ def build_parser() -> argparse.ArgumentParser:
         parser.add_argument("--language", default="en", choices=["en", "pt"], help="Skeleton language")
         parser.add_argument("--title", default=None, help="Document title")
         parser.add_argument("--template", default="default", help="Template name (see templates/)")
    +    parser.add_argument(
    +        "--mode",
    +        default="working_tree",
    +        choices=["working_tree", "commit", "branch"],
    +        help="What to diff: uncommitted changes, a single commit, or a branch vs a base branch",
    +    )
    +    parser.add_argument("--commit", dest="commit_hash", default=None, help="Commit hash for --mode commit (default: HEAD)")
    +    parser.add_argument("--base", dest="base_branch", default="origin/main", help="Base branch for --mode branch")
         return parser
     
     
    @@ -34,6 +42,9 @@ def main(argv: list[str] | None = None) -> int:
             language=args.language,
             title=args.title,
             template=args.template,
    +        mode=args.mode,
    +        commit_hash=args.commit_hash,
    +        base_branch=args.base_branch,
         )
     
         print(json.dumps(result.to_dict(), indent=2, ensure_ascii=False))

    ```

Testing process:
- [To be filled by AI]

## AI Usage

- Model:
- Input tokens:
- Output tokens:

Real output for one commit of this repo, filtered to cli/*. Every skeleton ends with an empty AI Usage section; the server never fills it in and no cost is calculated.

No Claude Code? Use the CLI

gendoc-mcp wraps the same core and prints the result as JSON. With no LLM in the loop, the placeholders and the AI Usage section stay empty.

terminal
# Uncommitted changes (default mode)
gendoc-mcp
gendoc-mcp --mode working_tree --exclude "*.css" --language pt

# A specific commit (defaults to HEAD if --commit omitted)
gendoc-mcp --mode commit --commit a1b2c3d

# Current branch vs a base branch (defaults to origin/main)
gendoc-mcp --mode branch --base origin/develop
without installing · uvx
uvx --from git+https://github.com/Paulosouzx/mcp-gen-documentation.git gendoc-mcp --mode commit
--project-path
Path to the Git repository (default: cwd)
--include / --exclude
Glob patterns, e.g. *.py src/* or *.css
--output-file
Output Markdown file path
--language
en or pt
--title / --template
Document title · template name (see templates/)
--mode
working_tree, commit or branch
--commit
Commit hash for --mode commit (default: HEAD)
--base
Base branch for --mode branch (default: origin/main)