"""File MCP tools - File operations with receipts.""" import json import os from datetime import datetime, timezone from pathlib import Path from typing import Any import blake3 # VaultMesh root from env or default VAULTMESH_ROOT = Path(os.environ.get("VAULTMESH_ROOT", Path(__file__).parents[3])).resolve() RECEIPTS_ROOT = VAULTMESH_ROOT / "receipts" FILE_RECEIPTS = RECEIPTS_ROOT / "file" / "file_operations.jsonl" def _vmhash_blake3(data: bytes) -> str: """VaultMesh hash: blake3:.""" return f"blake3:{blake3.blake3(data).hexdigest()}" def _emit_file_receipt(operation: str, file_path: str, details: dict, actor: str = "did:vm:mcp:file") -> str: """Emit a receipt for file operation.""" FILE_RECEIPTS.parent.mkdir(parents=True, exist_ok=True) body = { "operation": operation, "file_path": file_path, "details": details, "actor": actor, } root_hash = _vmhash_blake3(json.dumps(body, sort_keys=True).encode()) receipt = { "schema_version": "2.0.0", "type": "file_operation", "timestamp": datetime.now(timezone.utc).isoformat(), "scroll": "file", "tags": ["file", operation, "mcp"], "root_hash": root_hash, "body": body, } with open(FILE_RECEIPTS, "a") as f: f.write(json.dumps(receipt) + "\n") return root_hash def file_add( path: str, content: str, encoding: str = "utf-8", actor: str = "did:vm:mcp:file", ) -> dict[str, Any]: """ Add (create or overwrite) a file with content. Args: path: File path relative to VAULTMESH_ROOT or absolute content: File content to write encoding: File encoding (default: utf-8) actor: DID of actor performing operation Returns: Result with file hash and receipt hash """ try: file_path = Path(path) if not file_path.is_absolute(): file_path = VAULTMESH_ROOT / file_path # Create parent directories if needed file_path.parent.mkdir(parents=True, exist_ok=True) # Write file file_path.write_text(content, encoding=encoding) # Hash the content content_hash = _vmhash_blake3(content.encode(encoding)) details = { "content_hash": content_hash, "size_bytes": len(content.encode(encoding)), "encoding": encoding, "created": not file_path.exists(), } receipt_hash = _emit_file_receipt("add", str(file_path), details, actor) return { "success": True, "path": str(file_path), "content_hash": content_hash, "receipt_hash": receipt_hash, "size_bytes": details["size_bytes"], } except Exception as e: return {"success": False, "error": str(e)}