resources.py•6.74 kB
"""MCP resources for breadcrumb documents."""
from pathlib import Path
from typing import Any
from mcp.server import Server
from mcp.types import Resource, TextResourceContents
def register_resources(server: Server, breadcrumb_root: Path):
"""Register all breadcrumb resources."""
@server.list_resources()
async def list_resources() -> list[Resource]:
"""List available resources."""
resources = []
projects_dir = breadcrumb_root / "projects"
if not projects_dir.exists():
return resources
# List all projects
resources.append(
Resource(
uri="breadcrumb://projects",
name="All Projects",
mimeType="text/plain",
description="List of all breadcrumb projects",
)
)
# List resources for each project
for project_dir in sorted(projects_dir.iterdir()):
if not project_dir.is_dir() or project_dir.name.startswith("."):
continue
project_name = project_dir.name
# Project index
if (project_dir / "project-index.md").exists():
resources.append(
Resource(
uri=f"breadcrumb://project/{project_name}/index",
name=f"{project_name} - Project Index",
mimeType="text/markdown",
description=f"Project index for {project_name}",
)
)
# Sessions list
sessions_dir = project_dir / "sessions"
if sessions_dir.exists():
resources.append(
Resource(
uri=f"breadcrumb://project/{project_name}/sessions",
name=f"{project_name} - Sessions",
mimeType="text/plain",
description=f"List of session logs for {project_name}",
)
)
# Individual sessions
for session_file in sorted(sessions_dir.glob("*.md")):
resources.append(
Resource(
uri=f"breadcrumb://project/{project_name}/sessions/{session_file.name}",
name=f"{project_name} - {session_file.stem}",
mimeType="text/markdown",
description=f"Session log: {session_file.stem}",
)
)
# Components
components_dir = project_dir / "components"
if components_dir.exists():
for component_file in sorted(components_dir.glob("*.md")):
resources.append(
Resource(
uri=f"breadcrumb://project/{project_name}/components/{component_file.name}",
name=f"{project_name} - Component: {component_file.stem}",
mimeType="text/markdown",
description=f"Component documentation: {component_file.stem}",
)
)
# ADRs
adr_dir = project_dir / "adr"
if adr_dir.exists():
for adr_file in sorted(adr_dir.glob("*.md")):
resources.append(
Resource(
uri=f"breadcrumb://project/{project_name}/adr/{adr_file.name}",
name=f"{project_name} - ADR: {adr_file.stem}",
mimeType="text/markdown",
description=f"Architectural Decision Record: {adr_file.stem}",
)
)
# Patterns
patterns_dir = project_dir / "patterns"
if patterns_dir.exists():
for pattern_file in sorted(patterns_dir.glob("*.md")):
resources.append(
Resource(
uri=f"breadcrumb://project/{project_name}/patterns/{pattern_file.name}",
name=f"{project_name} - Pattern: {pattern_file.stem}",
mimeType="text/markdown",
description=f"Pattern documentation: {pattern_file.stem}",
)
)
return resources
@server.read_resource()
async def read_resource(uri: str) -> str:
"""Read a resource by URI."""
if not uri.startswith("breadcrumb://"):
raise ValueError(f"Invalid URI scheme: {uri}")
path = uri.replace("breadcrumb://", "")
parts = path.split("/")
if path == "projects":
# List all projects
projects_dir = breadcrumb_root / "projects"
if not projects_dir.exists():
return "No projects found"
projects = []
for project_dir in sorted(projects_dir.iterdir()):
if project_dir.is_dir() and not project_dir.name.startswith("."):
projects.append(f"- {project_dir.name}")
return "Projects:\n" + "\n".join(projects)
elif parts[0] == "project" and len(parts) >= 2:
project_name = parts[1]
project_dir = breadcrumb_root / "projects" / project_name
if not project_dir.exists():
raise ValueError(f"Project not found: {project_name}")
if len(parts) == 3 and parts[2] == "index":
# Read project index
index_file = project_dir / "project-index.md"
if not index_file.exists():
raise ValueError(f"Project index not found for: {project_name}")
return index_file.read_text()
elif len(parts) == 3 and parts[2] == "sessions":
# List sessions
sessions_dir = project_dir / "sessions"
if not sessions_dir.exists():
return f"No sessions found for project: {project_name}"
sessions = sorted(sessions_dir.glob("*.md"))
return "Sessions:\n" + "\n".join(f"- {s.name}" for s in sessions)
elif len(parts) == 4:
# Read specific document
doc_type = parts[2] # sessions, components, adr, patterns
filename = parts[3]
doc_file = project_dir / doc_type / filename
if not doc_file.exists():
raise ValueError(f"Document not found: {uri}")
return doc_file.read_text()
raise ValueError(f"Invalid resource URI: {uri}")