Skip to main content
Glama

Scribe MCP Server

by paxocial
manage_docs.py8.78 kB
"""Tool for managing project documentation with structured updates.""" from __future__ import annotations import asyncio from typing import Any, Dict, Optional from scribe_mcp import server as server_module, reminders from scribe_mcp.server import app from scribe_mcp.tools.project_utils import load_active_project from scribe_mcp.doc_management.manager import apply_doc_change from scribe_mcp.tools.append_entry import append_entry @app.tool() async def manage_docs( action: str, doc: str, section: Optional[str] = None, content: Optional[str] = None, template: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, dry_run: bool = False, ) -> Dict[str, Any]: """Apply structured updates to architecture/phase/checklist documents.""" state_snapshot = await server_module.state_manager.record_tool("manage_docs") project, _, recent = await load_active_project(server_module.state_manager) reminders_payload: list[Dict[str, Any]] = [] if not project: return { "ok": False, "error": "No project configured.", "suggestion": "Invoke set_project before managing docs.", "recent_projects": list(recent), "reminders": reminders_payload, } agent_identity = server_module.get_agent_identity() agent_id = "Scribe" if agent_identity: agent_id = await agent_identity.get_or_create_agent_id() try: change = await apply_doc_change( project, doc=doc, action=action, section=section, content=content, template=template, metadata=metadata, dry_run=dry_run, ) except Exception as exc: return { "ok": False, "error": str(exc), "recent_projects": list(recent), } backend = server_module.storage_backend storage_record = None if backend and not dry_run: try: timeout = server_module.settings.storage_timeout_seconds async with asyncio.timeout(timeout): storage_record = await backend.fetch_project(project["name"]) if not storage_record: async with asyncio.timeout(timeout): storage_record = await backend.upsert_project( name=project["name"], repo_root=project["root"], progress_log_path=project["progress_log"], ) await backend.record_doc_change( storage_record, doc=doc, section=section, action=action, agent=agent_id, metadata=metadata, sha_before=change.before_hash, sha_after=change.after_hash, ) except Exception as exc: print(f"⚠️ Failed to record doc change in storage: {exc}") log_error = None if not dry_run: log_meta = metadata.copy() if metadata else {} log_meta.update( { "doc": doc, "section": section or "", "action": action, "sha_after": change.after_hash, } ) try: await append_entry( message=f"Doc update [{doc}] {section or 'full'} via {action}", status="info", meta=log_meta, agent=agent_id, log_type="doc_updates", ) except Exception as exc: log_error = str(exc) reminders_payload = await reminders.get_reminders( project, tool_name="manage_docs", state=state_snapshot, ) response: Dict[str, Any] = { "ok": change.success, "doc": doc, "section": section, "action": action, "path": str(change.path) if change.success else "", "dry_run": dry_run, "diff": change.diff_preview, "recent_projects": list(recent), "reminders": reminders_payload, } # Add error information if operation failed if not change.success and change.error_message: response["error"] = change.error_message # Add verification information if not dry_run: response["verification_passed"] = change.verification_passed response["file_size_before"] = change.file_size_before response["file_size_after"] = change.file_size_after if log_error: response["log_warning"] = log_error if dry_run: response["preview"] = change.content_written return response def manage_docs_main(): """CLI entry point for manage_docs functionality.""" import argparse import asyncio import sys from pathlib import Path async def _run_manage_docs(args): """Run manage_docs with provided CLI arguments.""" try: result = await manage_docs( action=args.action, doc=args.doc, section=args.section, content=args.content, template=args.template, metadata=args.metadata, dry_run=args.dry_run, ) if result.get("ok"): if "error_message" in result and result["error_message"]: print(f"⚠️ Operation completed with warnings: {result['error_message']}") else: print(f"✅ {result.get('message', 'Documentation updated successfully')}") if args.dry_run: print("🔍 Dry run - no changes made") if "preview" in result: print("\nPreview:") print(result["preview"]) # Show verification status if "verification_passed" in result: if result["verification_passed"]: print("✅ File write verification passed") else: print("❌ File write verification failed") return 0 else: print(f"❌ Error: {result.get('error', 'Unknown error')}") return 1 except Exception as e: print(f"❌ Unexpected error: {e}") return 1 parser = argparse.ArgumentParser( description="Manage project documentation with structured updates", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Replace a section in architecture guide manage_docs replace_section architecture directory_structure --template directory_structure # Update checklist status manage_docs status_update checklist phase_0 --metadata status=done proof=commit_123 # Append content to document manage_docs append phase_plan --content "New phase details here" """ ) parser.add_argument( "action", choices=["replace_section", "append", "status_update"], help="Action to perform on the document" ) parser.add_argument( "doc", choices=["architecture", "phase_plan", "checklist", "progress_log", "doc_log", "security_log", "bug_log"], help="Document to modify" ) parser.add_argument( "--section", help="Section ID (required for replace_section and status_update)" ) parser.add_argument( "--content", help="Content to add/replace" ) parser.add_argument( "--template", help="Template fragment to use (from templates/fragments/)" ) parser.add_argument( "--metadata", type=str, help="Metadata as JSON string (e.g., '{\"status\": \"done\", \"proof\": \"commit_123\"}')" ) parser.add_argument( "--dry-run", action="store_true", help="Preview changes without applying them" ) args = parser.parse_args() # Validate arguments if args.action in ["replace_section", "status_update"] and not args.section: print("❌ Error: --section is required for replace_section and status_update actions") return 1 if not args.content and not args.template: print("❌ Error: Either --content or --template must be provided") return 1 # Parse metadata if provided metadata = None if args.metadata: try: import json metadata = json.loads(args.metadata) except json.JSONDecodeError: print(f"❌ Error: Invalid JSON in metadata: {args.metadata}") return 1 # Run the operation return asyncio.run(_run_manage_docs(args)) if __name__ == "__main__": sys.exit(manage_docs_main())

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/paxocial/scribe_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server