init: vaultmesh mcp server
Some checks are pending
Governance CI / Constitution Hash Gate (push) Waiting to run
Governance CI / Governance Tests (push) Blocked by required conditions
Governance CI / Golden Drill Mini (push) Blocked by required conditions

This commit is contained in:
Vault Sovereign
2025-12-26 23:23:08 +00:00
commit e4871c2a29
35 changed files with 6511 additions and 0 deletions

View File

@@ -0,0 +1,610 @@
#!/usr/bin/env python3
"""
VaultMesh MCP Server
Model Context Protocol server exposing VaultMesh Guardian, Treasury,
Cognitive, and Auth tools. This enables Claude to operate as the
7th Organ of VaultMesh - the Cognitive Ψ-Layer.
"""
import asyncio
import json
import logging
import os
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import blake3
# Try to import mcp, fallback gracefully if not available
try:
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
MCP_AVAILABLE = True
except ImportError:
MCP_AVAILABLE = False
from .tools import (
# Guardian
guardian_anchor_now,
guardian_verify_receipt,
guardian_status,
# Treasury
treasury_balance,
treasury_debit,
treasury_credit,
treasury_create_budget,
# Cognitive
cognitive_context,
cognitive_decide,
cognitive_invoke_tem,
cognitive_memory_get,
cognitive_memory_set,
cognitive_attest,
cognitive_audit_trail,
cognitive_oracle_chain,
# Auth
auth_challenge,
auth_verify,
auth_validate_token,
auth_check_permission,
check_profile_permission,
get_profile_for_scope,
auth_revoke,
auth_list_sessions,
auth_create_dev_session,
auth_revoke,
auth_list_sessions,
)
# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("vaultmesh-mcp")
# VaultMesh root
VAULTMESH_ROOT = Path(os.environ.get("VAULTMESH_ROOT", Path(__file__).parents[2])).resolve()
MCP_RECEIPTS = VAULTMESH_ROOT / "receipts/mcp/mcp_calls.jsonl"
# Tools that must remain callable without an authenticated session token.
# These are the bootstrap endpoints required to obtain/check a session.
OPEN_TOOLS = {
"auth_challenge",
"auth_verify",
"auth_create_dev_session",
"auth_check_permission",
}
def _vmhash_blake3(data: bytes) -> str:
"""VaultMesh hash: blake3:<hex>."""
return f"blake3:{blake3.blake3(data).hexdigest()}"
def _redact_call_arguments(arguments: dict) -> dict:
# Never persist session tokens in receipts.
if not arguments:
return {}
redacted = dict(arguments)
redacted.pop("session_token", None)
return redacted
def _emit_mcp_receipt(tool_name: str, arguments: dict, result: dict, caller: str = "did:vm:mcp:client") -> None:
"""Emit a receipt for every MCP tool call."""
MCP_RECEIPTS.parent.mkdir(parents=True, exist_ok=True)
body = {
"tool": tool_name,
"arguments": _redact_call_arguments(arguments),
"result_hash": _vmhash_blake3(json.dumps(result, sort_keys=True).encode()),
"caller": caller,
"success": "error" not in result,
}
receipt = {
"schema_version": "2.0.0",
"type": "mcp_tool_call",
"timestamp": datetime.now(timezone.utc).isoformat(),
"scroll": "mcp",
"tags": ["mcp", "tool-call", tool_name],
"root_hash": _vmhash_blake3(json.dumps(body, sort_keys=True).encode()),
"body": body,
}
with open(MCP_RECEIPTS, "a") as f:
f.write(json.dumps(receipt) + "\n")
def require_session_and_permission(name: str, arguments: dict) -> tuple[bool, dict, str, dict | None]:
"""Fail-closed session + profile enforcement ahead of tool handlers.
Returns (allowed, safe_args, caller, denial_result).
- safe_args strips session_token so downstream handlers never see it.
- caller is derived from the validated session (operator_did) when available.
- denial_result is a structured error payload when denied.
"""
safe_args = dict(arguments or {})
caller = "did:vm:mcp:client"
if name in OPEN_TOOLS:
return True, safe_args, caller, None
session_token = safe_args.pop("session_token", None)
if not session_token:
return False, safe_args, caller, {
"error": "Missing session_token",
"allowed": False,
"reason": "Session required for non-auth tools",
}
validation = auth_validate_token(session_token)
if not validation.get("valid"):
return False, safe_args, caller, {
"error": "Invalid session",
"allowed": False,
"reason": validation.get("error", "invalid_session"),
}
caller = validation.get("operator_did") or caller
profile = get_profile_for_scope(str(validation.get("scope", "read")))
perm = check_profile_permission(profile, name)
if not perm.get("allowed"):
return False, safe_args, caller, {
"error": "Permission denied",
"allowed": False,
"profile": perm.get("profile"),
"reason": perm.get("reason", "denied"),
}
return True, safe_args, caller, None
# =============================================================================
# TOOL DEFINITIONS
# =============================================================================
TOOLS = [
# -------------------------------------------------------------------------
# GUARDIAN TOOLS
# -------------------------------------------------------------------------
{
"name": "guardian_anchor_now",
"description": "Anchor all or specified scrolls to compute a Merkle root snapshot. Emits a guardian receipt.",
"inputSchema": {
"type": "object",
"properties": {
"scrolls": {
"type": "array",
"items": {"type": "string"},
"description": "List of scroll names to anchor. Omit for all scrolls.",
},
"guardian_did": {
"type": "string",
"default": "did:vm:guardian:mcp",
},
"backend": {
"type": "string",
"default": "local",
"enum": ["local", "ethereum", "stellar"],
},
},
},
},
{
"name": "guardian_verify_receipt",
"description": "Verify a receipt exists in a scroll's JSONL by its hash.",
"inputSchema": {
"type": "object",
"properties": {
"receipt_hash": {"type": "string"},
"scroll": {"type": "string", "default": "guardian"},
},
"required": ["receipt_hash"],
},
},
{
"name": "guardian_status",
"description": "Get current status of all scrolls including Merkle roots and leaf counts.",
"inputSchema": {"type": "object", "properties": {}},
},
# -------------------------------------------------------------------------
# TREASURY TOOLS
# -------------------------------------------------------------------------
{
"name": "treasury_create_budget",
"description": "Create a new budget for tracking expenditures.",
"inputSchema": {
"type": "object",
"properties": {
"budget_id": {"type": "string"},
"name": {"type": "string"},
"allocated": {"type": "integer"},
"currency": {"type": "string", "default": "EUR"},
"created_by": {"type": "string", "default": "did:vm:mcp:treasury"},
},
"required": ["budget_id", "name", "allocated"],
},
},
{
"name": "treasury_balance",
"description": "Get balance for a specific budget or all budgets.",
"inputSchema": {
"type": "object",
"properties": {
"budget_id": {"type": "string"},
},
},
},
{
"name": "treasury_debit",
"description": "Debit (spend) from a budget. Fails if insufficient funds.",
"inputSchema": {
"type": "object",
"properties": {
"budget_id": {"type": "string"},
"amount": {"type": "integer"},
"description": {"type": "string"},
"debited_by": {"type": "string", "default": "did:vm:mcp:treasury"},
},
"required": ["budget_id", "amount", "description"],
},
},
{
"name": "treasury_credit",
"description": "Credit (add funds) to a budget.",
"inputSchema": {
"type": "object",
"properties": {
"budget_id": {"type": "string"},
"amount": {"type": "integer"},
"description": {"type": "string"},
"credited_by": {"type": "string", "default": "did:vm:mcp:treasury"},
},
"required": ["budget_id", "amount", "description"],
},
},
# -------------------------------------------------------------------------
# COGNITIVE TOOLS (Claude as 7th Organ)
# -------------------------------------------------------------------------
{
"name": "cognitive_context",
"description": "Read current VaultMesh context for AI reasoning. Aggregates alerts, health, receipts, threats, treasury, governance, and memory.",
"inputSchema": {
"type": "object",
"properties": {
"include": {
"type": "array",
"items": {"type": "string"},
"description": "Context types: alerts, health, receipts, threats, treasury, governance, memory",
},
"session_id": {"type": "string"},
},
},
},
{
"name": "cognitive_decide",
"description": "Submit a reasoned decision with cryptographic attestation. Every decision is signed and anchored to ProofChain.",
"inputSchema": {
"type": "object",
"properties": {
"reasoning_chain": {
"type": "array",
"items": {"type": "string"},
"description": "List of reasoning steps leading to decision",
},
"decision": {
"type": "string",
"description": "Decision type: invoke_tem, alert, remediate, approve, etc.",
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1,
},
"evidence": {
"type": "array",
"items": {"type": "string"},
},
"operator_did": {"type": "string", "default": "did:vm:cognitive:claude"},
"auto_action_threshold": {"type": "number", "default": 0.95},
},
"required": ["reasoning_chain", "decision", "confidence"],
},
},
{
"name": "cognitive_invoke_tem",
"description": "Invoke Tem (Guardian) with AI-detected threat pattern. Transmutes threats into defensive capabilities.",
"inputSchema": {
"type": "object",
"properties": {
"threat_type": {
"type": "string",
"description": "Category: replay_attack, intrusion, anomaly, credential_stuffing, etc.",
},
"threat_id": {"type": "string"},
"target": {"type": "string"},
"evidence": {
"type": "array",
"items": {"type": "string"},
},
"recommended_transmutation": {"type": "string"},
"operator_did": {"type": "string", "default": "did:vm:cognitive:claude"},
},
"required": ["threat_type", "threat_id", "target", "evidence"],
},
},
{
"name": "cognitive_memory_get",
"description": "Query conversation/reasoning memory from CRDT realm.",
"inputSchema": {
"type": "object",
"properties": {
"key": {"type": "string"},
"session_id": {"type": "string"},
"realm": {"type": "string", "default": "memory"},
},
"required": ["key"],
},
},
{
"name": "cognitive_memory_set",
"description": "Store reasoning artifacts for future sessions. Uses CRDT-style merge.",
"inputSchema": {
"type": "object",
"properties": {
"key": {"type": "string"},
"value": {"type": "object"},
"session_id": {"type": "string"},
"realm": {"type": "string", "default": "memory"},
"merge": {"type": "boolean", "default": True},
},
"required": ["key", "value"],
},
},
{
"name": "cognitive_attest",
"description": "Create cryptographic attestation of Claude's reasoning state. Anchors to external chains.",
"inputSchema": {
"type": "object",
"properties": {
"attestation_type": {"type": "string"},
"content": {"type": "object"},
"anchor_to": {
"type": "array",
"items": {"type": "string"},
"description": "Anchor backends: local, rfc3161, eth, btc",
},
"operator_did": {"type": "string", "default": "did:vm:cognitive:claude"},
},
"required": ["attestation_type", "content"],
},
},
{
"name": "cognitive_audit_trail",
"description": "Query historical AI decisions for audit with full provenance.",
"inputSchema": {
"type": "object",
"properties": {
"filter_type": {"type": "string"},
"time_range": {
"type": "object",
"properties": {
"start": {"type": "string"},
"end": {"type": "string"},
},
},
"confidence_min": {"type": "number"},
"limit": {"type": "integer", "default": 100},
},
},
},
{
"name": "cognitive_oracle_chain",
"description": "Execute oracle chain with cognitive enhancement. Adds memory context and Tem awareness.",
"inputSchema": {
"type": "object",
"properties": {
"question": {"type": "string"},
"frameworks": {
"type": "array",
"items": {"type": "string"},
"description": "Compliance frameworks: GDPR, AI_ACT, NIS2, etc.",
},
"max_docs": {"type": "integer", "default": 10},
"include_memory": {"type": "boolean", "default": True},
"session_id": {"type": "string"},
},
"required": ["question"],
},
},
# -------------------------------------------------------------------------
# AUTH TOOLS
# -------------------------------------------------------------------------
{
"name": "auth_challenge",
"description": "Generate an authentication challenge for an operator.",
"inputSchema": {
"type": "object",
"properties": {
"operator_pubkey_b64": {"type": "string"},
"scope": {
"type": "string",
"enum": ["read", "admin", "vault", "anchor", "cognitive"],
"default": "read",
},
"ttl_seconds": {"type": "integer", "default": 300},
},
"required": ["operator_pubkey_b64"],
},
},
{
"name": "auth_verify",
"description": "Verify a signed challenge and issue session token.",
"inputSchema": {
"type": "object",
"properties": {
"challenge_id": {"type": "string"},
"signature_b64": {"type": "string"},
"ip_hint": {"type": "string"},
},
"required": ["challenge_id", "signature_b64"],
},
},
{
"name": "auth_check_permission",
"description": "Check if a session has permission to call a tool.",
"inputSchema": {
"type": "object",
"properties": {
"token": {"type": "string"},
"tool_name": {"type": "string"},
},
"required": ["token", "tool_name"],
},
},
{
"name": "auth_create_dev_session",
"description": "Create a development session for testing (DEV ONLY).",
"inputSchema": {
"type": "object",
"properties": {
"scope": {"type": "string", "default": "cognitive"},
"operator_did": {"type": "string", "default": "did:vm:cognitive:claude-dev"},
},
},
},
{
"name": "auth_revoke",
"description": "Revoke a session token.",
"inputSchema": {
"type": "object",
"properties": {
"token": {"type": "string"},
},
"required": ["token"],
},
},
{
"name": "auth_list_sessions",
"description": "List all active sessions (admin only).",
"inputSchema": {"type": "object", "properties": {}},
},
]
def handle_tool_call(name: str, arguments: dict) -> dict[str, Any]:
"""Dispatch tool call to appropriate handler."""
handlers = {
# Guardian
"guardian_anchor_now": guardian_anchor_now,
"guardian_verify_receipt": guardian_verify_receipt,
"guardian_status": guardian_status,
# Treasury
"treasury_create_budget": treasury_create_budget,
"treasury_balance": treasury_balance,
"treasury_debit": treasury_debit,
"treasury_credit": treasury_credit,
# Cognitive
"cognitive_context": cognitive_context,
"cognitive_decide": cognitive_decide,
"cognitive_invoke_tem": cognitive_invoke_tem,
"cognitive_memory_get": cognitive_memory_get,
"cognitive_memory_set": cognitive_memory_set,
"cognitive_attest": cognitive_attest,
"cognitive_audit_trail": cognitive_audit_trail,
"cognitive_oracle_chain": cognitive_oracle_chain,
# Auth
"auth_challenge": auth_challenge,
"auth_verify": auth_verify,
"auth_check_permission": auth_check_permission,
"auth_create_dev_session": auth_create_dev_session,
"auth_revoke": auth_revoke,
"auth_list_sessions": auth_list_sessions,
}
if name not in handlers:
return {"error": f"Unknown tool: {name}"}
allowed, safe_args, caller, denial = require_session_and_permission(name, arguments)
if not allowed:
_emit_mcp_receipt(name, safe_args, denial, caller=caller)
return denial
result = handlers[name](**safe_args)
# Emit receipt for the tool call
_emit_mcp_receipt(name, safe_args, result, caller=caller)
return result
if MCP_AVAILABLE:
# Create MCP server
app = Server("vaultmesh-mcp")
@app.list_tools()
async def list_tools() -> list[Tool]:
"""List available VaultMesh tools."""
return [Tool(**t) for t in TOOLS]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""Handle tool invocation."""
logger.info(f"Tool call: {name} with {arguments}")
result = handle_tool_call(name, arguments)
return [TextContent(type="text", text=json.dumps(result, indent=2))]
async def main():
"""Run the MCP server."""
if not MCP_AVAILABLE:
print("MCP library not available. Install with: pip install mcp")
return
logger.info(f"Starting VaultMesh MCP Server (root: {VAULTMESH_ROOT})")
logger.info(f"Tools registered: {len(TOOLS)}")
async with stdio_server() as (read_stream, write_stream):
await app.run(read_stream, write_stream, app.create_initialization_options())
def run_standalone():
"""Run as standalone CLI for testing without MCP."""
import sys
if len(sys.argv) < 2:
print("VaultMesh MCP Server - Standalone Mode")
print(f"\nVaultMesh Root: {VAULTMESH_ROOT}")
print(f"\nRegistered Tools ({len(TOOLS)}):")
print("-" * 60)
for tool in TOOLS:
print(f" {tool['name']}")
print(f" {tool['description'][:70]}...")
print("-" * 60)
print("\nUsage: python -m vaultmesh_mcp.server <tool> [json_args]")
print("\nExample:")
print(' python -m vaultmesh_mcp.server cognitive_context \'{"include": ["health"]}\'')
return
tool_name = sys.argv[1]
args_str = sys.argv[2] if len(sys.argv) > 2 else "{}"
try:
arguments = json.loads(args_str)
except json.JSONDecodeError:
print(f"Invalid JSON arguments: {args_str}")
return
result = handle_tool_call(tool_name, arguments)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
import sys
# If any CLI arguments provided (other than module name), run standalone
if len(sys.argv) > 1 or not MCP_AVAILABLE:
run_standalone()
else:
asyncio.run(main())