Skip to content

docforge

docforge

Renderer-agnostic Python documentation compiler that converts Python docstrings into structured documentation for both humans (MkDocs) and machines (MCP / AI agents).

doc-forge statically analyzes source code, builds a semantic model of modules, classes, functions, and attributes, and renders that model into documentation outputs without executing user code.


Installation

Install using pip:

1
pip install doc-forge

Quick start

Generate a MkDocs site from a Python package:

1
doc-forge build --mkdocs --module my_package

Generate MCP JSON documentation:

1
doc-forge build --mcp --module my_package

Serve documentation locally:

1
doc-forge serve --mkdocs --module my_package

Core concepts

Loader

Extracts symbols, signatures, and docstrings using static analysis.

Semantic model

Structured, renderer-agnostic representation of the API.

Renderer

Converts the semantic model into output formats such as MkDocs or MCP JSON.

Symbol

Any documentable object:

1
2
3
4
5
6
- module
- class
- function
- method
- property
- attribute

Architecture

doc-forge follows a compiler architecture:

Front-end:

1
Static analysis of modules, classes, functions, type hints, and docstrings.

Middle-end:

1
Builds a semantic model describing symbols and relationships.

Back-end:

1
Renders documentation using interchangeable renderers.

This architecture ensures deterministic documentation generation.


Rendering pipeline

Typical flow:

1
2
3
4
5
6
7
8
9
Python package
    ↓
Loader (static analysis)
    ↓
Semantic model
    ↓
Renderer
    ↓
MkDocs site or MCP JSON

CLI usage

Build MkDocs documentation:

1
doc-forge build --mkdocs --module my_package

Build MCP documentation:

1
doc-forge build --mcp --module my_package

Serve MkDocs locally:

1
doc-forge serve --module my_package

Public API

Loaders:

1
2
GriffeLoader
discover_module_paths

Renderers:

1
2
MkDocsRenderer
MCPRenderer

CLI:

1
main

Models:

1
models

Google-Styled Doc-Forge Convention (GSDFC)

GSDFC defines how docstrings must be written so they render correctly in MkDocs and remain machine-parsable by doc-forge and AI tooling.

  • Docstrings are the single source of truth.
  • doc-forge compiles docstrings but does not generate documentation content.
  • Documentation follows the Python import hierarchy.
  • Every public symbol should have a complete and accurate docstring.

General rules

  • Use Markdown headings at package and module level.
  • Use Google-style structured sections at class, function, and method level.
  • Indent section contents using four spaces.
  • Use type hints in signatures instead of duplicating types in prose.
  • Write summaries in imperative form.
  • Sections are separated by ---

Package docstrings

  • Package docstrings act as the documentation home page.

Recommended sections:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
## Summary
## Installation
## Quick start
## Core concepts
## Architecture
## Rendering pipeline
## CLI usage
## Public API
## Examples
## Notes
Example

Package Doc String:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
'''
Foo-bar processing framework.

Provides tools for defining Foo objects and executing Bar pipelines.

---

# Installation

    pip install foo-bar

---

# Quick start

    from foobar import Foo, BarEngine

    foo = Foo("example")
    engine = BarEngine([foo])

    result = engine.run()

---

# Public API

    Foo
    Bar
    BarEngine

---
'''

Module docstrings

  • Module docstrings describe a subsystem.

Recommended sections:

1
2
3
4
## Summary
## Examples
## Notes
## Public API
Example

Module Doc String:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
'''
Foo execution subsystem.

Provides utilities for executing Foo objects through Bar stages.

---

Example:

    from foobar.engine import BarEngine
    from foobar.foo import Foo

    foo = Foo("example")

    engine = BarEngine([foo])
    engine.run()

---
'''

Class docstrings

  • Class docstrings define object responsibility, lifecycle, and attributes.

Recommended sections:

1
2
3
4
Attributes:
Notes:
Example:
Raises:
Example

Simple Foo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Foo:
    '''
    Represents a unit of work.

    Attributes:
        name (str):
            Identifier of the foo instance.

        value (int):
            Numeric value associated with foo.

    Notes:
        Guarantees:

            - instances are immutable after creation

        Lifecycle:

            - create instance
            - pass to processing engine

    Example:
        Create and inspect a Foo:

            foo = Foo("example", value=42)
            print(foo.name)
    '''

Complex Bar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BarEngine:
    '''
    Executes Foo objects through Bar stages.

    Attributes:
        foos (tuple[Foo, ...]):
            Foo instances managed by the engine.

    Notes:
        Guarantees:

            - deterministic execution order

    Example:
        Run engine:

            foo1 = Foo("a")
            foo2 = Foo("b")

            engine = BarEngine([foo1, foo2])
            engine.run()
    '''

Function and method docstrings

  • Function docstrings define API contracts.

Recommended sections:

1
2
3
4
5
6
Args:
Returns:
Raises:
Yields:
Notes:
Example:
Example

Simple process method:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def process(foo: Foo, multiplier: int) -> int:
    '''
    Process a Foo instance.

    Args:
        foo (Foo):
            Foo instance to process.

        multiplier (int):
            Value used to scale foo.

    Returns:
        int:
            Processed result.

    Raises:
        ValueError:
            If multiplier is negative.

    Notes:
        Guarantees:

            - foo is not modified

    Example:
        Process foo:

            foo = Foo("example", value=10)

            result = process(foo, multiplier=2)
            print(result)
    '''

Multiple Examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def combine(foo_a: Foo, foo_b: Foo) -> Foo:
    '''
    Combine two Foo instances.

    Args:
        foo_a (Foo):
            First foo.

        foo_b (Foo):
            Second foo.

    Returns:
        Foo:
            Combined foo.

    Example:
        Basic usage:

            foo1 = Foo("a")
            foo2 = Foo("b")

            combined = combine(foo1, foo2)

        Pipeline usage:

            engine = BarEngine([foo1, foo2])
            engine.run()
    '''

Property docstrings

  • Properties must document return values.
Example

Property Doc String:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@property
def foos(self) -> tuple[Foo, ...]:
    '''
    Return contained Foo instances.

    Returns:
        tuple[Foo, ...]:
            Stored foo objects.

    Example:
        container = FooContainer()

        foos = container.foos
    '''

Attribute documentation

  • Document attributes in class docstrings using Attributes:.
Example

Attribute Doc String:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
'''
Represents a processing stage.

Attributes:
    id (str):
        Unique identifier.

    enabled (bool):
        Whether the stage is active.
'''

Notes subsection grouping

  • Group related information using labeled subsections.
Example

Notes Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Notes:
    **Guarantees:**

        - deterministic behavior

    **Lifecycle:**

        - created during initialization
        - reused across executions

    **Thread safety:**

        - safe for concurrent reads

Example formatting

  • Use indentation for examples.
Example

Single example:

1
2
3
4
Example:

    foo = Foo("example")
    process(foo, multiplier=2)

Multiple examples:

1
2
3
4
5
6
7
8
9
Example:
    Create foo:

        foo = Foo("example")

    Run engine:

        engine = BarEngine([foo])
        engine.run()

Avoid fenced code blocks inside structured sections.


Separator rules

Use horizontal separators only at docstring root level to separate sections:

1
---

Allowed locations:

  • package docstrings
  • module docstrings
  • major documentation sections

Do not use separators inside code sections.


Parsing guarantees

GSDFC ensures doc-forge can deterministically extract:

  • symbol kind (module, class, function, property, attribute)
  • symbol name
  • parameters
  • return values
  • attributes
  • examples
  • structured Notes subsections

This enables:

  • reliable MkDocs rendering
  • deterministic MCP export
  • accurate AI semantic interpretation

Notes
  • doc-forge never executes analyzed modules.
  • Documentation is generated entirely through static analysis.

Classes

GriffeLoader

GriffeLoader()

Load Python modules using Griffe and convert them into doc-forge models.

This loader uses the Griffe introspection engine to analyze Python source code and transform the extracted information into Project, Module, and DocObject instances used by doc-forge.

Initialize the Griffe-backed loader.

Creates an internal Griffe loader instance with dedicated collections for modules and source lines.

Functions
load_module
load_module(path: str) -> Module

Load and convert a single Python module.

The module is introspected using Griffe and then transformed into a doc-forge Module model.

Parameters:

Name Type Description Default
path str

Dotted import path of the module.

required

Returns:

Type Description
Module

A populated Module instance.

load_project
load_project(module_paths: List[str], project_name: Optional[str] = None, skip_import_errors: bool = None) -> Project

Load multiple modules and assemble them into a Project model.

Each module path is introspected and converted into a Module instance. All modules are then aggregated into a single Project object.

Parameters:

Name Type Description Default
module_paths List[str]

List of dotted module import paths to load.

required
project_name Optional[str]

Optional override for the project name. Defaults to the top-level name of the first module.

None
skip_import_errors bool

If True, modules that fail to load will be skipped instead of raising an error.

None

Returns:

Type Description
Project

A populated Project instance containing the loaded modules.

Raises:

Type Description
ValueError

If no module paths are provided.

ImportError

If a module fails to load and skip_import_errors is False.

MCPRenderer

Renderer that generates MCP-compatible documentation resources.

This renderer converts doc-forge project models into structured JSON resources suitable for consumption by systems implementing the Model Context Protocol (MCP).

Functions
generate_sources
generate_sources(project: Project, out_dir: Path) -> None

Generate MCP documentation resources for a project.

The renderer serializes each module into a JSON resource and produces supporting metadata files such as nav.json and index.json.

Parameters:

Name Type Description Default
project Project

Documentation project model to render.

required
out_dir Path

Directory where MCP resources will be written.

required

MkDocsRenderer

Renderer that produces Markdown documentation for MkDocs.

Generated pages use mkdocstrings directives to reference Python modules, allowing MkDocs to render API documentation dynamically.

Functions
generate_readme
generate_readme(project: Project, docs_dir: Path, module_is_source: bool | None = None) -> None

Generate a README.md file from the root module docstring.

Behavior:

  • If module_is_source is True, README.md is written to the project root directory.
  • If False, README generation is currently not implemented.

Parameters:

Name Type Description Default
project Project

Project model containing documentation metadata.

required
docs_dir Path

Directory containing generated documentation sources.

required
module_is_source bool | None

Whether the module is treated as the project source root.

None
generate_sources
generate_sources(project: Project, out_dir: Path, module_is_source: bool | None = None) -> None

Generate Markdown documentation files for a project.

This method renders a documentation structure from the provided project model and writes the resulting Markdown files to the specified output directory.

Parameters:

Name Type Description Default
project Project

Project model containing modules to document.

required
out_dir Path

Directory where generated Markdown files will be written.

required
module_is_source bool | None

If True, treat the specified module as the documentation root rather than nesting it inside a folder.

None

Functions

discover_module_paths

discover_module_paths(module_name: str, project_root: Path | None = None) -> List[str]

Discover Python modules within a package directory.

The function scans the filesystem for .py files inside the specified package and converts them into dotted module import paths.

Discovery rules:

  • Directories containing __init__.py are treated as packages.
  • Each .py file is treated as a module.
  • Results are returned as dotted import paths.

Parameters:

Name Type Description Default
module_name str

Top-level package name to discover modules from.

required
project_root Path | None

Root directory used to resolve module paths. If not provided, the current working directory is used.

None

Returns:

Type Description
List[str]

A sorted list of unique dotted module import paths.

Raises:

Type Description
FileNotFoundError

If the specified package directory does not exist.