# COMPONENTS.md
**Framework:** POWER (Purpose, Output, Work, Examples, Requirements)
**Version:** 1.0.0
**Date:** 2025-10-08
## Overview
This document inventories all reusable components in the docs-mcp server. Components include MCP tools, template loaders, and framework readers. Each component is documented with its interface, usage patterns, and copy-paste ready examples.
**Project Context:**
- **README Summary:** docs-mcp provides AI assistants with structured documentation generation through POWER framework templates
- **Architecture Summary:** File-based MCP server using stdio transport with async I/O and read-only template access
## Component Inventory
### 1. MCP Tools (3 components)
- `list_templates` - Template discovery tool
- `get_template` - Template retrieval tool
- `list_frameworks` - Framework documentation discovery tool
### 2. Internal Components
- Template Loader - File system template reader
- Framework Reader - Framework documentation reader
- Path Resolver - Directory path management
---
## MCP Tool Components
### Component: list_templates
**Purpose:** Discover and enumerate all available POWER framework documentation templates
**Type:** MCP Tool (no input parameters)
**Interface:**
```python
Tool(
name="list_templates",
description="Lists all available documentation templates (README, ARCHITECTURE, API, COMPONENTS, SCHEMA)",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
)
```
**Input Schema:**
- No parameters required
**Output Schema:**
```python
list[TextContent]
# Returns: List containing single TextContent with formatted template list
```
**Usage Pattern:**
```python
# Call from MCP client
response = await client.call_tool("list_templates", {})
# Expected response format:
"""
Available POWER Framework Templates:
1. api
2. architecture
3. components
4. readme
5. schema
Total: 5 templates
"""
```
**Implementation Details:**
```python
# Location: server.py:68-85
if name == "list_templates":
try:
templates = []
if TEMPLATES_DIR.exists():
for file in TEMPLATES_DIR.glob("*.txt"):
templates.append(file.stem)
if templates:
result = "Available POWER Framework Templates:\n\n"
for i, template in enumerate(sorted(templates), 1):
result += f"{i}. {template}\n"
result += f"\nTotal: {len(templates)} templates"
else:
result = "No templates found in templates/power/"
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error listing templates: {str(e)}")]
```
**Error Handling:**
- Returns error message as TextContent if exception occurs
- Gracefully handles missing templates directory
- Never crashes server
**State Management:**
- Stateless (no persistent state)
- Reads file system on each call
- No caching
---
### Component: get_template
**Purpose:** Retrieve the full content of a specific POWER framework documentation template
**Type:** MCP Tool (single parameter)
**Interface:**
```python
Tool(
name="get_template",
description="Retrieves the content of a specific documentation template",
inputSchema={
"type": "object",
"properties": {
"template_name": {
"type": "string",
"description": "Name of template: readme, architecture, api, components, or schema",
"enum": ["readme", "architecture", "api", "components", "schema"]
}
},
"required": ["template_name"]
}
)
```
**Input Schema:**
```python
{
"template_name": str # Required, must be one of: readme, architecture, api, components, schema
}
```
**Output Schema:**
```python
list[TextContent]
# Returns: List containing single TextContent with formatted template content
```
**Usage Pattern:**
```python
# Example 1: Get README template
response = await client.call_tool("get_template", {
"template_name": "readme"
})
# Example 2: Get Architecture template
response = await client.call_tool("get_template", {
"template_name": "architecture"
})
# Expected response format:
"""
=== README Template ===
framework: POWER
purpose: Generate README.md as the discovery entry document.
output: Must follow required header/footer format...
[Full template content]
"""
```
**Implementation Details:**
```python
# Location: server.py:87-104
elif name == "get_template":
template_name = arguments.get("template_name", "")
template_file = TEMPLATES_DIR / f"{template_name}.txt"
try:
if not template_file.exists():
return [TextContent(
type="text",
text=f"Template '{template_name}' not found. Available: readme, architecture, api, components, schema"
)]
with open(template_file, 'r', encoding='utf-8') as f:
content = f.read()
result = f"=== {template_name.upper()} Template ===\n\n{content}"
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error reading template: {str(e)}")]
```
**Validation:**
- Template name validated against enum in schema
- File existence checked before reading
- Returns helpful error message with available templates if not found
**Error Handling:**
- Invalid template name: Returns list of valid templates
- File read error: Returns error message
- Missing file: Returns "not found" message
**State Management:**
- Stateless (no persistent state)
- Reads file on each call
- No caching (templates are small)
---
### Component: list_frameworks
**Purpose:** Discover and enumerate all available prompt engineering framework documentation files
**Type:** MCP Tool (no input parameters)
**Interface:**
```python
Tool(
name="list_frameworks",
description="Lists available prompt framework documentation (POWER, COSTAR, Five S, CRISPE)",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
)
```
**Input Schema:**
- No parameters required
**Output Schema:**
```python
list[TextContent]
# Returns: List containing single TextContent with formatted framework list including file sizes
```
**Usage Pattern:**
```python
# Call from MCP client
response = await client.call_tool("list_frameworks", {})
# Expected response format:
"""
Available Framework Documentation:
1. COSTAR.md (2,156 bytes)
2. CRISPE.md (1,842 bytes)
3. FiveS.md (1,734 bytes)
4. POWER.md (2,401 bytes)
Total: 4 framework docs
"""
```
**Implementation Details:**
```python
# Location: server.py:106-125
elif name == "list_frameworks":
try:
frameworks = []
if FRAMEWORKS_DIR.exists():
for file in FRAMEWORKS_DIR.glob("*.md"):
frameworks.append(file.name)
if frameworks:
result = "Available Framework Documentation:\n\n"
for i, framework in enumerate(sorted(frameworks), 1):
file_path = FRAMEWORKS_DIR / framework
size = file_path.stat().st_size
result += f"{i}. {framework} ({size:,} bytes)\n"
result += f"\nTotal: {len(frameworks)} framework docs"
else:
result = "No framework documentation found"
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error listing frameworks: {str(e)}")]
```
**Error Handling:**
- Returns error message as TextContent if exception occurs
- Gracefully handles missing frameworks directory
- File stat errors are caught and reported
**State Management:**
- Stateless (no persistent state)
- Scans file system on each call
- No caching
---
## Internal Components
### Component: Template Loader
**Purpose:** Load template files from the file system
**Type:** Internal utility (embedded in get_template tool)
**Interface:**
```python
# Implicit interface
def load_template(template_name: str) -> str:
"""Load template content from templates/power/{template_name}.txt"""
template_file = TEMPLATES_DIR / f"{template_name}.txt"
if not template_file.exists():
raise FileNotFoundError(f"Template '{template_name}' not found")
with open(template_file, 'r', encoding='utf-8') as f:
return f.read()
```
**Usage Pattern:**
```python
# Used internally by get_template tool
template_file = TEMPLATES_DIR / f"{template_name}.txt"
with open(template_file, 'r', encoding='utf-8') as f:
content = f.read()
```
**Properties:**
- **Encoding:** UTF-8
- **Read mode:** Text mode
- **Buffering:** Default (system-dependent)
- **Error handling:** Returns TextContent with error message
---
### Component: Framework Reader
**Purpose:** Scan and list framework documentation files
**Type:** Internal utility (embedded in list_frameworks tool)
**Interface:**
```python
# Implicit interface
def list_framework_files() -> list[tuple[str, int]]:
"""List framework files with their sizes"""
frameworks = []
if FRAMEWORKS_DIR.exists():
for file in FRAMEWORKS_DIR.glob("*.md"):
size = file.stat().st_size
frameworks.append((file.name, size))
return sorted(frameworks)
```
**Usage Pattern:**
```python
# Used internally by list_frameworks tool
for file in FRAMEWORKS_DIR.glob("*.md"):
frameworks.append(file.name)
size = file_path.stat().st_size
```
**Properties:**
- **File pattern:** `*.md` (markdown files only)
- **Metadata:** File name and size in bytes
- **Sorting:** Alphabetical by filename
---
### Component: Path Resolver
**Purpose:** Manage and resolve directory paths for templates and frameworks
**Type:** Module-level configuration
**Interface:**
```python
# server.py:16-18
SERVER_DIR = Path(__file__).parent
TEMPLATES_DIR = SERVER_DIR / "templates" / "power"
FRAMEWORKS_DIR = SERVER_DIR / "frameworks"
```
**Usage Pattern:**
```python
# All components use these module-level constants
if TEMPLATES_DIR.exists():
for file in TEMPLATES_DIR.glob("*.txt"):
# Process template files
if FRAMEWORKS_DIR.exists():
for file in FRAMEWORKS_DIR.glob("*.md"):
# Process framework files
```
**Properties:**
- **SERVER_DIR:** Parent directory of server.py
- **TEMPLATES_DIR:** `{SERVER_DIR}/templates/power`
- **FRAMEWORKS_DIR:** `{SERVER_DIR}/frameworks`
- **Path type:** pathlib.Path objects
- **Resolution:** Relative to server.py location
---
## Component Architecture
### Dependency Graph
```
┌─────────────────────────────────────────────────────────┐
│ MCP Server Framework │
│ (mcp.server.Server) │
└────────────────────────┬────────────────────────────────┘
│
├─── Registers Tools ────┐
│ │
▼ ▼
┌──────────────────────────────────┐ ┌──────────────────────────────┐
│ list_templates Tool │ │ list_frameworks Tool │
│ - Scans templates/power/ │ │ - Scans frameworks/ │
│ - Returns template names │ │ - Returns framework files │
└──────────────────────────────────┘ └──────────────────────────────┘
│
▼
┌──────────────────────────────┐
│ get_template Tool │
│ - Loads template content │
│ - Validates template name │
└──────────────────────────────┘
│
├─── Uses ───┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Path Resolver │ │ Template Loader │
│ (Module-level) │ │ (File I/O) │
└──────────────────┘ └──────────────────┘
│
▼
┌──────────────────────────────┐
│ File System Storage │
│ - templates/power/*.txt │
│ - frameworks/*.md │
└──────────────────────────────┘
```
### Component Relationships
**Tool to Tool:**
- No direct dependencies between tools
- All tools are independent
- Can be called in any order
**Tool to Internal:**
- All tools depend on Path Resolver
- `get_template` depends on Template Loader
- `list_frameworks` depends on Framework Reader
**Internal to Internal:**
- Template Loader depends on Path Resolver
- Framework Reader depends on Path Resolver
- No circular dependencies
---
## Usage Examples
### Example 1: Discover and Use Templates
```python
# Step 1: List available templates
templates_response = await client.call_tool("list_templates", {})
print(templates_response[0].text)
# Output:
# Available POWER Framework Templates:
# 1. api
# 2. architecture
# 3. components
# 4. readme
# 5. schema
# Total: 5 templates
# Step 2: Get specific template
readme_response = await client.call_tool("get_template", {
"template_name": "readme"
})
print(readme_response[0].text)
# Output:
# === README Template ===
# framework: POWER
# purpose: Generate README.md as the discovery entry document.
# ...
```
### Example 2: Error Handling
```python
# Invalid template name
response = await client.call_tool("get_template", {
"template_name": "invalid"
})
print(response[0].text)
# Output: Template 'invalid' not found. Available: readme, architecture, api, components, schema
# Missing template file (if file deleted)
response = await client.call_tool("get_template", {
"template_name": "readme"
})
# Output: Template 'readme' not found. Available: readme, architecture, api, components, schema
```
### Example 3: Explore Framework Documentation
```python
# List all framework docs
frameworks_response = await client.call_tool("list_frameworks", {})
print(frameworks_response[0].text)
# Output:
# Available Framework Documentation:
# 1. COSTAR.md (2,156 bytes)
# 2. CRISPE.md (1,842 bytes)
# 3. FiveS.md (1,734 bytes)
# 4. POWER.md (2,401 bytes)
# Total: 4 framework docs
```
### Example 4: Generate Documentation Workflow
```python
# Workflow: Generate README for a project
# 1. Get README template
template_response = await client.call_tool("get_template", {
"template_name": "readme"
})
template_content = template_response[0].text
# 2. Parse template to understand structure
# Extract: framework, purpose, output, work, examples, requirements
# 3. Use template guidance to generate README
# Follow the "work" section to scan project
# Follow the "output" section for formatting
# Follow the "requirements" section for completeness
# 4. Validate against template requirements
# Ensure all required sections are present
```
---
## Component Patterns
### Pattern: Stateless Tool Execution
**Problem:** MCP tools need to be reliable and predictable
**Solution:** All tools are stateless - no persistent state between calls
```python
# Each call is independent
await client.call_tool("list_templates", {}) # Call 1
await client.call_tool("list_templates", {}) # Call 2 - identical behavior
# No state persists
await client.call_tool("get_template", {"template_name": "readme"}) # Call 1
await client.call_tool("get_template", {"template_name": "api"}) # Call 2 - no interference
```
### Pattern: Read-Only File Access
**Problem:** Ensure server safety and prevent data corruption
**Solution:** All file operations are read-only
```python
# Only read operations
with open(template_file, 'r', encoding='utf-8') as f: # Read mode only
content = f.read()
# No write operations
# No delete operations
# No modification operations
```
### Pattern: Graceful Error Handling
**Problem:** Prevent server crashes from file system errors
**Solution:** Catch exceptions and return error messages as TextContent
```python
try:
# Attempt operation
result = perform_operation()
return [TextContent(type="text", text=result)]
except Exception as e:
# Return error as TextContent, don't crash
return [TextContent(type="text", text=f"Error: {str(e)}")]
```
### Pattern: File System Discovery
**Problem:** Dynamically discover available templates without hardcoding
**Solution:** Use glob patterns to scan directories
```python
# Templates discovered automatically
templates = []
for file in TEMPLATES_DIR.glob("*.txt"):
templates.append(file.stem) # Filename without extension
# Frameworks discovered automatically
frameworks = []
for file in FRAMEWORKS_DIR.glob("*.md"):
frameworks.append(file.name) # Full filename with extension
```
---
## Extension Guidelines
### Adding a New MCP Tool
1. **Define Tool Schema:**
```python
# In list_tools() function
Tool(
name="new_tool_name",
description="Clear description of what the tool does",
inputSchema={
"type": "object",
"properties": {
"param_name": {
"type": "string",
"description": "Parameter description"
}
},
"required": ["param_name"]
}
)
```
2. **Implement Tool Handler:**
```python
# In call_tool() function
elif name == "new_tool_name":
try:
param_value = arguments.get("param_name", "")
# Implement tool logic
result = process_tool(param_value)
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error: {str(e)}")]
```
3. **Test Tool:**
```python
# Test from MCP client
response = await client.call_tool("new_tool_name", {
"param_name": "test_value"
})
```
### Modifying Existing Components
**Best Practices:**
- Maintain backward compatibility
- Keep input/output schemas consistent
- Preserve error handling patterns
- Update documentation
- Test all error paths
---
**AI Integration Notes**
These components are designed for AI assistant consumption. Key integration patterns:
- **Tool Discovery:** Use `list_tools()` to enumerate available tools before use
- **Schema Validation:** All inputs validated against JSON schemas
- **Error Messages:** Human-readable error messages guide correction
- **Idempotency:** Same inputs always produce same outputs
- **No Side Effects:** Safe for speculative execution and retry
For AI assistants: Use components by:
1. Calling `list_templates` to discover available templates
2. Calling `get_template` with specific template name to retrieve content
3. Parsing template structure to understand documentation requirements
4. Following template guidance to generate documentation
5. Using `list_frameworks` to discover prompt engineering frameworks
**🤖 This COMPONENTS document was generated using the docs-mcp components template**
---
**Maintained by:** willh
**Last updated:** 2025-10-08
**Related documents:** README.md, ARCHITECTURE.md, API.md (future)