This commit is contained in:
John Lancaster
2026-06-18 20:01:47 -05:00
parent 485b93b3c9
commit c915c1846d
3 changed files with 279 additions and 360 deletions
+172
View File
@@ -0,0 +1,172 @@
---
icon: lucide/library
---
# Resource-First Pattern Module Architecture
## Overview
The platform is implemented as a resource-first MCP system with an integrated static documentation surface. The same methodology content powers both MCP resources and the published docs site.
The system is complete in four layers:
1. Pattern modules expose methodology as MCP resources.
2. Catalog resources provide normalized discovery.
3. A docs export step materializes resource content into markdown.
4. Zensical builds a static site that is served by the FastAPI app in the FastMCP runtime process.
This architecture keeps authored content human-friendly while preserving machine-stable contracts.
## Intent
The architecture is designed to satisfy three long-term requirements:
1. Methodology must be editable as markdown by humans.
2. Agents must consume stable, discoverable resource contracts.
3. Public documentation must be pre-built static output served from the application runtime without a separate docs service.
## System Model
### Pattern Modules
Each module encapsulates one methodology domain and publishes resource families:
1. overview
2. rules
3. checklist
4. references
Tools are intentionally narrow and optional. They exist only for deterministic export-like behavior.
### Catalog Module
The catalog is the canonical discovery layer and publishes normalized records for all modules.
Typical catalog resources:
1. resource://catalog/patterns
2. resource://catalog/patterns_by_id
3. resource://catalog/skills_index
4. resource://catalog/skills_details
### Content Sources
Content is authored in markdown and managed as long-form reference material. Resource handlers load authored content and expose it through stable resource URIs.
### Static Docs Surface
Static docs are generated from two merged content streams:
1. Project-authored docs pages
2. Generated pages derived from MCP resources
The merged docs tree is built by Zensical into static files and served by the FastAPI app.
## Data Flow
```mermaid
flowchart TD
A[Authored Markdown] --> C[Resource Handlers]
B[Pattern Metadata] --> D[Catalog Resources]
C --> E[Docs Export Step]
D --> E
E --> F[Generated Docs Markdown]
F --> G[Zensical Static Build]
G --> H[FastAPI Static Mount]
H --> I[Served Docs Site]
```
## Contracts
### Metadata Contract
Each pattern module declares:
1. id
2. name
3. version
4. description
5. tags
6. capabilities
7. depends_on
### URI Contract
Module resource URIs are stable and follow:
1. resource://skills/<skill_id>/overview
2. resource://skills/<skill_id>/rules
3. resource://skills/<skill_id>/checklist
4. resource://skills/<skill_id>/references
Catalog resource URIs are stable and discovery-focused.
### Versioning Rule
Published URIs are immutable. Behavioral or schema changes are versioned in metadata and documented through additive migration notes.
## Static Hosting Pattern
The docs site is pre-built and served by the same FastAPI runtime process used by the MCP app.
Runtime behavior:
1. App starts.
2. FastAPI mounts the static docs output directory.
3. Requests to docs paths are served as static assets.
This provides a single deployment artifact with no runtime markdown rendering dependency.
## Advantages
### Single Source of Truth
Methodology is authored once and reused in both MCP resources and docs pages.
### High-Fidelity Agent Context
Resources expose explicit, attachable context for accurate implementation behavior.
### Operational Simplicity
A single app process serves MCP and docs surfaces.
### Long-Term Maintainability
Markdown remains easy to review, while contracts remain stable for clients.
### Client Independence
Clients can use Ask, Edit, or Agent modes without requiring server-owned prompt orchestration.
## Authoring and Publishing Lifecycle
1. Update markdown reference content.
2. Update metadata if capability surface changes.
3. Run resource export to generated docs markdown.
4. Build static docs with Zensical.
5. Serve built output through FastAPI static mount.
## Scope and Non-Goals
In-scope:
1. Resource-first methodology delivery
2. Catalog-based discovery
3. Pre-built static docs hosting in app runtime
Out-of-scope:
1. Prompt-first orchestration as the primary interface
2. Large tool inventories duplicating static guidance
3. Separate dynamic docs service at runtime
## Example Content Inputs
Existing markdown reference sets are valid examples of authored source material for this architecture:
1. ../skills/pytest-scaffolding/references/pytest-docs.md
2. ../skills/python-logging-dictconfig/references/python-logging-docs.md
3. ../skills/fastapi-uv-docker/references/fastapi-best-practices.md
These inputs are treated as content sources, while resource URIs and catalog payloads remain the machine-facing contracts.
+5
View File
@@ -3,3 +3,8 @@ icon: lucide/rocket
--- ---
# Get started # Get started
## Architecture
- [Resource-First Pattern Module Architecture](./architecture.md)
- [Static Docs Hosting Pattern](./mcp_layout.md)
+102 -360
View File
@@ -1,408 +1,150 @@
For a modern Python project in 2026, I would treat the MCP server exactly like any other deployable service: # Static Docs Hosting Pattern
* `src/` layout ## Purpose
* `uv` for dependency management
* skill modules as importable Python packages
* FastMCP composition via `mount()`
* Docker image built from the root package
* resources/prompts kept close to the skill that owns them
* avoid giant decorator files with hundreds of tools
FastMCP's mounting/composition model is specifically intended for this sort of modular organization. ([FastMCP][1]) This document describes the completed layout and runtime pattern used to host a pre-built static documentation site from the same FastAPI app process that runs the FastMCP server.
I'd structure it something like: This design intentionally avoids runtime docs rendering and avoids a separate docs hosting service.
## Completed-State Layout
```text ```text
personal-mcp/ project-root/
|
├── pyproject.toml |- pyproject.toml
├── uv.lock |- uv.lock
├── README.md |- zensical.toml
|
├── Dockerfile |- docs/
├── .dockerignore | |- index.md
| |- architecture.md
├── src/ | |- mcp_layout.md
│ └── personal_mcp/ | |- generated/
| |- patterns/
│ ├── __init__.py | |- index.md
│ │ | |- pytest-scaffolding.md
│ ├── main.py | |- python-logging-dictconfig.md
│ │ # Creates the root MCP server | |- fastapi-uv-docker.md
│ │ # Mounts all skills |
│ │ |- site/
│ ├── settings.py | |- ... static files built by zensical ...
│ │ # Pydantic settings |
│ │ |- src/
├── graph/ |- personal_mcp/
│ ├── __init__.py |- main.py
│ │ └── capability_graph.py |- web/
│ │ | |- app.py
│ ├── catalog/ | |- docs_mount.py
│ │ ├── __init__.py |- catalog/
├── resources.py | |- server.py
│ │ └── tools.py |- skills/
│ │ |- pytest_scaffolding/
└── skills/ |- python_logging_dictconfig/
|- fastapi_uv_docker/
│ ├── __init__.py
│ │
│ ├── nixos/
│ │
│ │ ├── __init__.py
│ │ ├── server.py
│ │ │
│ │ ├── tools/
│ │ │ └── rebuild.py
│ │ │
│ │ ├── prompts/
│ │ │ └── expert.py
│ │ │
│ │ ├── resources/
│ │ │ ├── overview.py
│ │ │ └── troubleshooting.py
│ │ │
│ │ └── metadata.yaml
│ │
│ ├── homelab/
│ │
│ │ ├── __init__.py
│ │ ├── server.py
│ │ ├── tools/
│ │ ├── prompts/
│ │ ├── resources/
│ │ └── metadata.yaml
│ │
│ └── knowledge_base/
│ ├── __init__.py
│ ├── server.py
│ ├── tools/
│ ├── prompts/
│ ├── resources/
│ └── metadata.yaml
└── tests/
├── test_catalog.py
├── test_nixos.py
└── test_homelab.py
``` ```
--- Notes:
## main.py 1. docs contains authored pages and generated markdown pages.
2. site contains static build output only.
3. MCP resources and docs generation share the same content contract.
This is intentionally boring. ## Runtime Composition
```python The runtime process serves two surfaces:
from fastmcp import FastMCP
from personal_mcp.catalog.server import catalog_server 1. MCP protocol surface from FastMCP
from personal_mcp.skills.nixos.server import nixos_server 2. Static docs surface from FastAPI static mount
from personal_mcp.skills.homelab.server import homelab_server
mcp = FastMCP("Personal MCP") ```mermaid
flowchart TD
mcp.mount(catalog_server, namespace="catalog") A[FastMCP Root Server] --> B[MCP Transport]
mcp.mount(nixos_server, namespace="nixos") A --> C[FastAPI Application]
mcp.mount(homelab_server, namespace="homelab") C --> D[Static Mount /docs]
D --> E[Zensical site output directory]
if __name__ == "__main__":
mcp.run()
``` ```
FastMCP namespaces resources, prompts, and tools automatically when mounted. ([FastMCP][2]) ## Build and Publish Flow
--- The docs flow is pre-build only.
## Skill Metadata 1. Read module resources and catalog metadata.
2. Generate docs markdown pages into docs/generated.
3. Build static site with Zensical into site.
4. Start app and serve site directory as static files.
Every skill gets metadata. No runtime markdown conversion is required.
```yaml ## Content Merge Pattern
# skills/nixos/metadata.yaml
id: nixos The published docs site always contains both:
name: NixOS Administration 1. Project-authored docs pages
2. Resource-derived pattern pages
description: | This ensures the public docs reflect both architectural guidance and live pattern knowledge.
Tools and guidance for managing NixOS systems.
tags: ## Resource-to-Docs Mapping
- nix
- linux
- systemd
capabilities: Pattern resources map directly to docs sections.
- package_management
- service_debugging
- flakes
depends_on: Example mapping model:
- certificates
```
The catalog layer reads these files. 1. resource://skills/<id>/overview -> docs/generated/patterns/<id>.md section Overview
2. resource://skills/<id>/rules -> docs/generated/patterns/<id>.md section Rules
3. resource://skills/<id>/checklist -> docs/generated/patterns/<id>.md section Checklist
4. resource://skills/<id>/references -> docs/generated/patterns/<id>.md section References
--- Catalog resources generate index pages and navigation pages.
## Skill Server ## Why This Pattern
Each skill owns its own MCP instance. ### Operational Simplicity
```python One application process serves both protocol and static docs surfaces.
# skills/nixos/server.py
from fastmcp import FastMCP ### Deterministic Docs
nixos_server = FastMCP( Published docs are immutable static assets for a given build.
"NixOS"
)
from .tools.rebuild import * ### Documentation Fidelity
from .resources.overview import *
from .prompts.expert import *
```
The pattern is: Resource-derived pages align docs with the exact methodology contracts exposed to agents.
```text ### Maintainer Experience
One skill
=
One FastMCP server
```
This keeps ownership obvious. Authors continue to work in markdown while resource contracts remain machine-consumable.
--- ## FastAPI Static Mount Expectations
## Skill Resource The FastAPI app is expected to:
Resources are your discovery layer. 1. Mount static directory containing Zensical output.
2. Serve index and asset files from that directory.
3. Keep docs route stable across releases.
```python Recommended route conventions:
# resources/overview.py
from .server import nixos_server 1. /docs for static site root
2. /docs/* for static assets and page routes
## Update Lifecycle
@nixos_server.resource( For each documentation update:
"resource://skills/nixos"
)
def nixos_skill_description():
return """
NixOS administration skill.
Provides: 1. Edit authored markdown and resource content.
- flake troubleshooting 2. Regenerate docs markdown from resources.
- package search 3. Rebuild static site.
- service diagnostics 4. Restart runtime if needed.
- generation management
"""
```
Resources are exactly what MCP intends for exposing contextual information and data to clients. ([fastmcp.mintlify.app][3]) This keeps docs publication explicit and predictable.
--- ## Example Source Material
## Skill Prompt Existing reference docs remain valid content inputs in this pattern:
```python 1. ../skills/pytest-scaffolding/references/pytest-docs.md
# prompts/expert.py 2. ../skills/python-logging-dictconfig/references/python-logging-docs.md
3. ../skills/fastapi-uv-docker/references/fastapi-best-practices.md
from .server import nixos_server These are source documents, not deployment artifacts.
@nixos_server.prompt
def nixos_expert():
return """
Act as an expert NixOS administrator.
Prefer:
- flakes
- declarative configuration
- modern systemd patterns
Avoid:
- imperative package management
"""
```
---
## Skill Tool
```python
# tools/rebuild.py
from .server import nixos_server
@nixos_server.tool
def explain_rebuild():
"""
Explain a nixos-rebuild failure.
"""
return {
"workflow": [
"Inspect journal",
"Check evaluation errors",
"Verify inputs",
"Retry build"
]
}
```
---
## Catalog Layer
The catalog should be its own server.
Not a skill.
Its job is discovery.
```python
catalog/
server.py
tools.py
resources.py
```
Example:
```python
# catalog/tools.py
@catalog_server.tool
def list_skills():
return [
"nixos",
"homelab",
"knowledge_base"
]
```
```python
@catalog_server.tool
def describe_skill(
name: str
):
...
```
---
## Capability Graph
This is where NetworkX belongs.
```python
# graph/capability_graph.py
import networkx as nx
graph = nx.DiGraph()
graph.add_edge(
"homelab",
"certificates",
relation="depends_on"
)
graph.add_edge(
"nixos",
"homelab",
relation="manages"
)
```
Expose it as:
```python
resource://skills/graph
resource://skills/nixos/dependencies
```
Now agents can discover related capabilities without hardcoding them.
---
## pyproject.toml
Minimal UV setup:
```toml
[project]
name = "personal-mcp"
version = "0.1.0"
dependencies = [
"fastmcp",
"networkx",
"pydantic-settings",
"pyyaml",
]
[project.scripts]
personal-mcp = "personal_mcp.main:mcp"
```
---
## Docker
```dockerfile
FROM python:3.13-slim
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN pip install uv
RUN uv sync --frozen
COPY src ./src
CMD ["uv", "run", "python", "-m", "personal_mcp.main"]
```
---
If I were building this for long-term growth, I'd make one additional change: treat every skill as a Python package with a common interface:
```python
class Skill:
id: str
metadata: SkillMetadata
def create_server(self) -> FastMCP:
...
```
Then `main.py` simply discovers skills dynamically:
```python
for skill in discover_skills():
mcp.mount(
skill.create_server(),
namespace=skill.id,
)
```
At that point adding a new skill becomes:
```text
mkdir skills/new_skill
drop in metadata.yaml
implement Skill
```
and the server automatically exposes it. That's the closest thing to a plugin architecture while still remaining very Pythonic and Docker-friendly.
[1]: https://fastmcp.wiki/en/servers/composition?utm_source=chatgpt.com "Server Composition - FastMCP"
[2]: https://gofastmcp.com/servers/composition?utm_source=chatgpt.com "Composing Servers - FastMCP"
[3]: https://fastmcp.mintlify.app/servers/resources?utm_source=chatgpt.com "Resources & Templates - FastMCP"