server.py•9.19 kB
#!/usr/bin/env python3
"""
Documentation Generation MCP Server
Provides tools for generating project documentation using POWER framework templates.
"""
import asyncio
from pathlib import Path
from mcp.server import Server
from mcp.types import Tool, TextContent
from mcp.server.stdio import stdio_server
# Import generators
from generators import FoundationGenerator, BaseGenerator
# Get server directory
SERVER_DIR = Path(__file__).parent
TEMPLATES_DIR = SERVER_DIR / "templates" / "power"
FRAMEWORKS_DIR = SERVER_DIR / "frameworks"
# Create MCP server
app = Server("docs-mcp")
@app.list_tools()
async def list_tools() -> list[Tool]:
"""List available documentation tools."""
return [
Tool(
name="list_templates",
description="Lists all available documentation templates (README, ARCHITECTURE, API, COMPONENTS, SCHEMA)",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
),
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, schema, or user-guide",
"enum": ["readme", "architecture", "api", "components", "schema", "user-guide"]
}
},
"required": ["template_name"]
}
),
Tool(
name="list_frameworks",
description="Lists available prompt framework documentation (POWER, COSTAR, Five S, CRISPE)",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
),
Tool(
name="generate_foundation_docs",
description="Generate foundation documentation (README, ARCHITECTURE, API, COMPONENTS, SCHEMA) for a project. Returns templates and generation plan - Claude will generate and save the actual documents.",
inputSchema={
"type": "object",
"properties": {
"project_path": {
"type": "string",
"description": "Absolute path to the project directory"
}
},
"required": ["project_path"]
}
),
Tool(
name="generate_individual_doc",
description="Generate a single individual documentation file for a project. Returns the template - Claude will generate and save the document.",
inputSchema={
"type": "object",
"properties": {
"project_path": {
"type": "string",
"description": "Absolute path to the project directory"
},
"template_name": {
"type": "string",
"description": "Name of template to generate",
"enum": ["readme", "architecture", "api", "components", "schema", "user-guide"]
}
},
"required": ["project_path", "template_name"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""Handle tool calls."""
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)}")]
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, user-guide"
)]
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)}")]
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)}")]
elif name == "generate_foundation_docs":
project_path = arguments.get("project_path", "")
try:
# Initialize foundation generator
generator = FoundationGenerator(TEMPLATES_DIR)
# Get generation plan
plan = generator.get_generation_plan(project_path)
# Get all templates
templates = generator.get_templates_for_generation()
# Build response
result = plan + "\n\n" + "=" * 50 + "\n\n"
result += "TEMPLATES FOR GENERATION:\n\n"
for template in templates:
if 'error' in template:
result += f"ERROR - {template['template_name']}: {template['error']}\n\n"
else:
result += f"=== {template['template_name'].upper()} ===\n\n"
result += f"{template['template_content']}\n\n"
result += "-" * 50 + "\n\n"
result += "\nINSTRUCTIONS:\n"
result += "Generate each document in order using the templates above.\n"
result += f"Save all documents to: {project_path}/docs/\n"
result += "Each document should reference previous documents as indicated in the templates.\n"
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error generating foundation docs: {str(e)}")]
elif name == "generate_individual_doc":
project_path = arguments.get("project_path", "")
template_name = arguments.get("template_name", "")
try:
generator = BaseGenerator(TEMPLATES_DIR)
paths = generator.prepare_generation(project_path)
template_content = generator.read_template(template_name)
template_info = generator.get_template_info(template_name)
result = f"=== Generating {template_name.upper()} ===\n\n"
result += f"Project: {paths['project_path']}\n"
result += f"Output: {paths['output_dir']}/{template_info.get('save_as', f'{template_name.upper()}.md')}\n\n"
result += "=" * 50 + "\n\n"
result += f"TEMPLATE:\n\n{template_content}\n\n"
result += "=" * 50 + "\n\n"
result += "INSTRUCTIONS:\n"
result += f"Generate {template_info.get('save_as', f'{template_name.upper()}.md')} using the template above.\n"
result += f"Save the document to: {paths['output_dir']}/{template_info.get('save_as', f'{template_name.upper()}.md')}\n"
return [TextContent(type="text", text=result)]
except Exception as e:
return [TextContent(type="text", text=f"Error generating individual doc: {str(e)}")]
else:
raise ValueError(f"Unknown tool: {name}")
async def main():
"""Run the server using stdio transport."""
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())