- .mcp.json: pass UE_ENGINE_ROOT and UE_DOCS_PATH via ${VAR} interpolation so the
MCP server subprocess inherits them without hardcoding paths
- search_source: search both Engine/Source/ and Engine/Plugins/ (path_hint applied
under both); switch from -m 40 (per-file cap) to streaming Popen with a true
40-line total cap; fix UE_ENGINE_ROOT to point at Engine/ not the repo root
- Update README and CLAUDE.md to reflect the above
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Purpose
UnrealDocGenerator is a tool for generating documentation from Unreal Engine C++ header files and exposing it to Claude via an MCP server for item-granularity lookups.
Current State
Implementation complete. Scripts live in docgen/:
docgen/ue_parser.py— Parses UE headers into dataclassesdocgen/ue_markdown.py— Renders parsed data as Markdown (ultra-compact format, documented items only)docgen/generate.py— CLI entry point; two-pass pipeline (parse-all → build type index → render-all)ue_mcp_server.py— MCP server exposing 5 tools for item-granularity doc lookups.mcp.json— Registers the MCP server with Claude Code (stdio transport)
Usage
python docgen/generate.py <input> [input2 ...] <output_dir>
python docgen/generate.py Runtime/Engine/ Runtime/AIModule/ docs/ # multiple directories
python docgen/generate.py Runtime/Engine/Classes/GameFramework/Actor.h docs/ # single file
Output: one .md per .h + docs/type-index.txt (compact TypeName: path/to/File.md lookup).
The last argument is always the output directory. All preceding arguments are inputs (files or directories, processed recursively).
MCP Server
ue_mcp_server.py exposes 5 tools Claude can call directly:
| Tool | Purpose |
|---|---|
search_types(pattern) |
Regex search over type-index.txt — locate a type before fetching details |
get_class_overview(class_name) |
Compact view: description + inherits + property/function name lists |
get_member(class_name, member_name) |
Full doc for one function or property (all overloads) |
get_file(relative_path) |
Full .md file — use when you need delegates, enums, or multiple classes |
search_source(pattern, path_hint) |
Grep UE .h files in Engine/Source/ and Engine/Plugins/; path_hint applied under both; returns up to 40 lines total |
Requires pip install mcp. Reads $UE_DOCS_PATH; search_source also requires $UE_ENGINE_ROOT (must point to the Engine/ directory, e.g. /path/to/UnrealEngine/Engine).
.mcp.json passes both vars via ${VAR} interpolation — no paths hardcoded.
Typical query flow: search_types → get_class_overview → get_member (~3 calls, ~15 lines vs ~100 lines for a full file read).
Architecture Notes
- Parser uses a position-based scanner, not line-by-line regex, to handle nested braces correctly.
find_matching_close()extracts balanced{}/()while skipping//,/* */, and string literals.- Only items with actual C++ doc comments (
/** */) are included in output (_has_doc()filter). - Two-pass pipeline in
generate.py: Pass 1 parses all files, Pass 2 renders with cross-reference links. - Multi-input: each
(header, base)pair tracks its own base dir for correct relative output paths. - Inline cross-references: base class names linked to their
.mdwhen in corpus (_make_type_link()). - Deprecated functions excluded from output (
not f.is_deprecatedin visibility filter). - Enums with no value descriptions use compact inline format (
Values: A, B, C) instead of a table. - Placeholder descriptions (
------//) filtered by_PLACEHOLDER_REin_fn_body(). - Headers with no documented content:
render_header()returns"",generate.pyskips writing the file and filters those types fromtype-index.txtto avoid dead entries.
Critical Gotchas
- Never use
\sinside a[...]character class with*?on C++ source — causes catastrophic regex backtracking. Use line-based scanning instead. - Identifier regex must be
r'\w+', notr'[\w:~]+'— the latter consumespublic:as one token, breaking access specifier detection (all members becomeprivate). - Module name casing: use
_caps_to_camel()(not.title()) —AIMODULE.title()→Aimoduleinstead ofAIModule.