Simplify Athena operator architecture

This commit is contained in:
Mikei386
2026-08-25 21:53:12 +02:00
parent c25e57af57
commit 0a640e76ea
22 changed files with 577 additions and 1522 deletions
+7 -7
View File
@@ -10,7 +10,7 @@ Prompts heraus, verhindert den früher beobachteten Kontextverbrauch von über
| Container | Endpunkt im Netz `mike-ai-tools` | Zweck | Standard |
|---|---|---|---|
| `mcp-platform-context` | `http://mike-ai-mcp-platform-context:8000/mcp` | Athena-Wissen, begrenzter Snapshot und kontrollierte Docs-Pflege | an |
| `mcp-platform-context` | `http://mike-ai-mcp-platform-context:8000/mcp` | kurze read-only Athena-Auskunft und begrenzter Snapshot | an |
| `mcp-athena-operator` | `http://mike-ai-mcp-athena-operator:8000/mcp` | vollständiger Betrieb plus breites begrenztes Terminal | an |
| `tinysearch` | `http://tinysearch:8000/mcp` | allgemeine portable Websuche und Seitenabruf | an |
| `mcp-web` | `http://mike-ai-mcp-web:8000/mcp` | frühere spezialisierte Web-Fassade | nur Profil `legacy-web` |
@@ -35,15 +35,15 @@ gleichzeitig fehlschlagen.
Der Platform Context MCP hat keinen Docker-Socket, keine Shell, keinen Egress
und keine Secrets. Sein aktueller Zustand stammt aus einem fest programmierten
Host-Snapshot. Dokumentationspflege ist auf `docs/*.md` und einen zweistufigen
Preview/Approval-Ablauf begrenzt. Vollständige Beschreibung:
Host-Snapshot. Er liefert `ATHENA.md` sowie kleine, begrenzte Such- und
Leseausschnitte. Vollständige Beschreibung:
[`docs/PLATFORM_CONTEXT_MCP.md`](../../docs/PLATFORM_CONTEXT_MCP.md).
Der Athena Operator MCP ist die einzige Bedienebene für Arbeiten an der lokalen
KI-Plattform. Qwen kann damit Quellen lesen, strukturierte Änderungen
vorbereiten, MCPs und Docker-Dienste bauen/deployen, Modelle laden, Benchmarks
starten, Profile und OpenWebUI pflegen, Git veröffentlichen und Recovery
erzeugen. Zusätzlich bietet er ein breites, ausgabebegrenztes Terminal für
KI-Plattform. Seine sechs sichtbaren Werkzeuge sind Inspect, Suche, begrenztes
Lesen, Terminal, direkte Änderung und Jobstatus. Qwen kann damit MCPs und
Docker-Dienste bauen/deployen, Modelle laden, Benchmarks starten, Profile und
OpenWebUI pflegen, Git veröffentlichen und Recovery erzeugen. Zusätzlich bietet er ein breites, ausgabebegrenztes Terminal für
unvorhergesehene Docker-, Datei-, Git-, HTTP-, Modell- und Remote-SSH-Aufgaben.
Die MCP-Fassade sieht nur einen lokalen Unix-Socket; Root-Rechte verbleiben im
Executor. Strombefehle und Änderungen an Athenas SSH, LAN, WireGuard, Firewall,
+66 -84
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Single MCP facade for end-to-end Athena platform operation."""
"""Compact MCP facade for the Athena host operator."""
from __future__ import annotations
@@ -10,7 +10,7 @@ import sys
from typing import Any
VERSION = "2.2.0"
VERSION = "3.0.0"
SOCKET_PATH = os.environ.get("ATHENA_OPERATOR_SOCKET", "/operator/operator.sock")
if hasattr(sys.stdin, "reconfigure"):
@@ -22,50 +22,48 @@ if hasattr(sys.stdout, "reconfigure"):
TOOLS = [
{
"name": "athena_operator_inspect",
"description": (
"USE FIRST for Athena administration. Inspect live platform state without changing it. "
"Subjects: overview, git_status, containers, models, jobs. This is the authoritative "
"starting point before MCP, Docker, model, profile, benchmark or recovery work."
),
"description": "START HERE once for Athena work. Inspect the requested live area without changing it.",
"inputSchema": {
"type": "object",
"properties": {"subject": {"type": "string", "enum": ["overview", "git_status", "containers", "models", "jobs"], "default": "overview"}},
"properties": {
"subject": {"type": "string", "enum": ["overview", "git_status", "containers", "models", "jobs"], "default": "overview"},
},
"additionalProperties": False,
},
},
{
"name": "athena_operator_search_source",
"description": "Find the exact source path and line before reading or editing it. Returns at most 20 matches.",
"inputSchema": {
"type": "object",
"properties": {"query": {"type": "string", "minLength": 1, "maxLength": 120}},
"required": ["query"],
"additionalProperties": False,
},
},
{
"name": "athena_operator_read_source",
"description": (
"Read a versioned Athena repository file with SHA-256 and bounded line range. Use this "
"instead of guessing current Compose, MCP, router, model, OpenWebUI or recovery code."
"Read only the needed section of one known source file. Default 80 and maximum "
"200 lines. A not-found result must not be retried by guessing more paths."
),
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string", "pattern": "^[A-Za-z0-9_.+/-]{1,240}$"},
"start_line": {"type": "integer", "minimum": 1, "maximum": 1000000, "default": 1},
"line_count": {"type": "integer", "minimum": 1, "maximum": 1000, "default": 300},
"line_count": {"type": "integer", "minimum": 1, "maximum": 200, "default": 80},
},
"required": ["path"], "additionalProperties": False,
"required": ["path"],
"additionalProperties": False,
},
},
{
"name": "athena_operator_search_source",
"description": "Search the complete versioned Athena repository for exact text before designing or modifying a component.",
"inputSchema": {"type": "object", "properties": {"query": {"type": "string", "minLength": 1, "maxLength": 200}}, "required": ["query"], "additionalProperties": False},
},
{
"name": "athena_operator_terminal",
"description": (
"GENERAL ATHENA TERMINAL. Run one bounded shell command on the Athena host when the "
"structured operator tools are too narrow. This is the broad escape hatch for Docker, "
"Compose, Git, MCP development, model inspection, downloads, HTTP/API tests, files, logs "
"and SSH to configured remote systems. Prefer a single focused command and cap noisy output "
"with the command itself. The server blocks power control and changes to Athena's SSH, LAN, "
"WireGuard, firewall, boot, kernel, mounts and partitions so remote reachability cannot be "
"accidentally destroyed. Other commands execute immediately and must be verified afterwards. "
"Do not clone the canonical repository, request/copy an SSH key, or use Terminal as a substitute "
"for the durable file_update, git_publish and recovery workflow."
"General bounded terminal on Athena for focused Docker, Git, file, HTTP, model "
"and test work when no structured operation fits. Limit noisy output yourself. "
"Power control and Athena reachability changes are blocked."
),
"inputSchema": {
"type": "object",
@@ -73,69 +71,43 @@ TOOLS = [
"command": {"type": "string", "minLength": 1, "maxLength": 8000},
"cwd": {"type": "string", "maxLength": 500, "default": "/opt/mike-ai/stack"},
"timeout_seconds": {"type": "integer", "minimum": 1, "maximum": 3600, "default": 300},
"max_output_chars": {"type": "integer", "minimum": 1000, "maximum": 30000, "default": 12000},
"max_output_chars": {"type": "integer", "minimum": 1000, "maximum": 30000, "default": 8000},
},
"required": ["command"],
"additionalProperties": False,
},
},
{
"name": "athena_operator_prepare",
"name": "athena_operator_change",
"description": (
"PREPARE a state-changing Athena operation. This never changes state. It returns a "
"content-bound ticket, exact preview and confirmation phrase. Supported operations: "
"patch_update (preferred for small changes), file_update (only for new or fully replaced files), "
"mcp_release (preferred one-ticket end-to-end MCP delivery), run_checks, compose_deploy, "
"container_action, openwebui_sync, git_publish, model_download, benchmark, recovery. Show the complete "
"preview to the user and stop. Never execute in the same autonomous tool sequence. This is the "
"supported durable source and Git path: never clone the repository inside a sandbox and never "
"request or copy an SSH key. "
"patch_update payload: {files:[{path,patch,expected_sha256?}]} using standard unified diffs; "
"file_update payload: {files:[{path,content,expected_sha256?}]}; "
"mcp_release payload: {files?:[patch entries],imports?:[{source,path,expected_source_sha256,"
"expected_target_sha256?}],services,checks?,message,paths?,build?,openwebui_sync?,hermes_sync?,"
"create_recovery?,recovery_label?}. imports copies already reviewed UTF-8 staging files by exact SHA "
"from an approved staging root, so never reproduce a long staged source file in a payload. It applies "
"drift-protected patches/imports, runs checks, "
"deploys only named MCP services, syncs OpenWebUI, publishes only selected paths and creates recovery. "
"Use mcp_release instead of manually chaining all those operations. run_checks: "
"{checks:[operator-tests,openwebui-filter-tests,platform-verify,compose-main,compose-mcp]}; "
"compose_deploy: {compose_file,services,build}; container_action: {action,containers}; "
"openwebui_sync: {}; "
"git_publish: {message,paths:[exact changed repository paths]}; model_download: {url,destination,sha256?}; benchmark: "
"{script,arguments}; recovery: {label}."
"Apply one durable change that the user has requested. Operations: patch_update, "
"file_update, mcp_release, run_checks, compose_deploy, container_action, "
"openwebui_sync, git_publish, model_download, benchmark, recovery. Use a small "
"patch for edits and file_update only for a new or intentionally replaced file. "
"mcp_release can perform the complete MCP build/test/deploy/publish workflow."
),
"inputSchema": {
"type": "object",
"properties": {
"operation": {"type": "string", "enum": ["patch_update", "file_update", "mcp_release", "run_checks", "compose_deploy", "container_action", "openwebui_sync", "git_publish", "model_download", "benchmark", "recovery"]},
"operation": {
"type": "string",
"enum": ["patch_update", "file_update", "mcp_release", "run_checks", "compose_deploy", "container_action", "openwebui_sync", "git_publish", "model_download", "benchmark", "recovery"],
},
"payload": {"type": "object"},
},
"required": ["operation", "payload"], "additionalProperties": False,
},
},
{
"name": "athena_operator_execute",
"description": (
"EXECUTE exactly one previously prepared Athena operation. Call only after the user "
"approved the exact preview in a later message and supplied the exact confirmation. "
"Tickets expire, are content-bound and single-use. Long downloads, benchmarks and "
"recovery work return a job id. This tool cannot alter SSH, networking, WireGuard, "
"firewall, boot, kernel, drivers, partitions, mounts, shutdown or reboot."
),
"inputSchema": {
"type": "object",
"properties": {
"ticket": {"type": "string", "pattern": "^[0-9a-f]{32}$"},
"confirmation": {"type": "string", "pattern": "^EXECUTE [0-9a-f]{32}$"},
},
"required": ["ticket", "confirmation"], "additionalProperties": False,
"required": ["operation", "payload"],
"additionalProperties": False,
},
},
{
"name": "athena_operator_job",
"description": "Poll one asynchronous model download, benchmark or recovery job. Do not start duplicate jobs while it is running.",
"inputSchema": {"type": "object", "properties": {"job_id": {"type": "string", "pattern": "^[0-9a-f]{32}$"}}, "required": ["job_id"], "additionalProperties": False},
"description": "Poll one asynchronous change returned by athena_operator_change. Do not start a duplicate job.",
"inputSchema": {
"type": "object",
"properties": {"job_id": {"type": "string", "pattern": "^[0-9a-f]{32}$"}},
"required": ["job_id"],
"additionalProperties": False,
},
},
]
@@ -143,7 +115,7 @@ TOOLS = [
def request(action: str, arguments: dict[str, Any]) -> dict[str, Any]:
payload = json.dumps({"action": action, "arguments": arguments}, ensure_ascii=False, separators=(",", ":")).encode() + b"\n"
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
client.settimeout(20)
client.settimeout(3700 if action == "change" else 30)
client.connect(SOCKET_PATH)
client.sendall(payload)
chunks = bytearray()
@@ -163,16 +135,16 @@ def request(action: str, arguments: dict[str, Any]) -> dict[str, Any]:
def call(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
mapping = {
"athena_operator_inspect": "inspect",
"athena_operator_read_source": "read_source",
"athena_operator_search_source": "search_source",
"athena_operator_read_source": "read_source",
"athena_operator_terminal": "terminal",
"athena_operator_prepare": "prepare",
"athena_operator_execute": "execute",
"athena_operator_change": "change",
"athena_operator_job": "job",
}
if name not in mapping:
raise ValueError("unknown tool")
return request(mapping[name], arguments)
action = mapping.get(name)
if action is None:
return {"ok": False, "error": "unknown tool", "retry": False}
return request(action, arguments)
def emit(request_id: Any, result: Any = None, error: dict[str, Any] | None = None) -> None:
@@ -185,14 +157,22 @@ def emit(request_id: Any, result: Any = None, error: dict[str, Any] | None = Non
def handle(message: dict[str, Any]) -> None:
method, request_id = message.get("method"), message.get("id")
if method == "initialize":
emit(request_id, {"protocolVersion": message.get("params", {}).get("protocolVersion", "2024-11-05"), "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": "mike-ai-athena-operator", "version": VERSION}})
emit(request_id, {
"protocolVersion": message.get("params", {}).get("protocolVersion", "2024-11-05"),
"capabilities": {"tools": {"listChanged": False}},
"serverInfo": {"name": "mike-ai-athena-operator", "version": VERSION},
})
elif method == "tools/list":
emit(request_id, {"tools": TOOLS})
elif method == "tools/call":
params = message.get("params", {})
params = message.get("params") or {}
try:
value = call(str(params.get("name", "")), params.get("arguments") or {})
emit(request_id, {"content": [{"type": "text", "text": json.dumps(value, ensure_ascii=False, separators=(",", ":"))}], "structuredContent": value, "isError": False})
emit(request_id, {
"content": [{"type": "text", "text": json.dumps(value, ensure_ascii=False, separators=(",", ":"))}],
"structuredContent": value,
"isError": False,
})
except Exception as exc:
emit(request_id, {"content": [{"type": "text", "text": f"ERROR: {exc}"}], "isError": True})
elif request_id is not None:
@@ -202,9 +182,11 @@ def handle(message: dict[str, Any]) -> None:
def main() -> None:
for line in sys.stdin:
try:
if line.strip(): handle(json.loads(line))
if line.strip():
handle(json.loads(line))
except Exception as exc:
sys.stderr.write(f"MCP input error: {exc}\n"); sys.stderr.flush()
sys.stderr.write(f"MCP input error: {exc}\n")
sys.stderr.flush()
if __name__ == "__main__":
+5 -13
View File
@@ -180,25 +180,17 @@ services:
build:
context: .
dockerfile: Dockerfile.platform-context
image: mike-ai/mcp-platform-context:1.1.0
image: mike-ai/mcp-platform-context:2.0.0
container_name: mike-ai-mcp-platform-context
environment:
ATHENA_REPO_ROOT: /knowledge/repo
ATHENA_DOCS_ROOT: /workspace/docs
ATHENA_RUNTIME_FILE: /runtime/runtime.json
ATHENA_CONTEXT_STATE: /state
# Writes remain confined to docs/*.md and require a prepared proposal
# plus its exact confirmation string. The MCP cannot change code,
# containers, networking, secrets, Git or recovery bundles.
ATHENA_DOC_WRITE_MODE: enabled
volumes:
- ${PLATFORM_STACK_DIR:-/opt/mike-ai/stack}:/knowledge/repo:ro
- ${PLATFORM_DOCS_DIR:-/opt/mike-ai/stack/docs}:/workspace/docs:rw
- ${PLATFORM_CONTEXT_RUNTIME_DIR:-/var/lib/mike-ai-platform-context}:/runtime:ro
- ${PLATFORM_CONTEXT_STATE_DIR:-/data/mike-ai-platform-context}:/state:rw
# The context service has no arbitrary URL input. Its only egress use is a
# bounded reachability check of endpoints from config/service-catalog.json.
networks: [tools, egress]
# Documentation is strictly read-only. Runtime inspection and all changes
# belong to the Athena Operator instead of a second maintenance workflow.
networks: [tools]
healthcheck:
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
interval: 30s
@@ -211,7 +203,7 @@ services:
build:
context: .
dockerfile: Dockerfile.athena-operator
image: mike-ai/mcp-athena-operator:2.3.4
image: mike-ai/mcp-athena-operator:3.0.0
container_name: mike-ai-mcp-athena-operator
environment:
ATHENA_OPERATOR_SOCKET: /operator/operator.sock
+5 -9
View File
@@ -17,26 +17,22 @@ export SEARXNG_SETTINGS_FILE="${SEARXNG_SETTINGS_FILE:-$MCP_DIR/../web-search/se
exit 1
}
# The platform context MCP never receives the Docker socket. A root-owned
# timer writes a bounded metadata snapshot instead. Only documentation files
# and the dedicated state directory are writable by the unprivileged MCP uid.
# The read-only platform context MCP never receives the Docker socket. A
# root-owned timer writes a bounded metadata snapshot instead.
install -d -m 0755 /usr/local/libexec /var/lib/mike-ai-platform-context
install -d -o 10001 -g 10001 -m 0750 /data/mike-ai-platform-context
install -m 0755 "$MCP_DIR/platform-context-snapshot.py" \
/usr/local/libexec/mike-ai-platform-context-snapshot
install -m 0644 "$MCP_DIR/../systemd/mike-ai-platform-context-snapshot.service" \
/etc/systemd/system/mike-ai-platform-context-snapshot.service
install -m 0644 "$MCP_DIR/../systemd/mike-ai-platform-context-snapshot.timer" \
/etc/systemd/system/mike-ai-platform-context-snapshot.timer
find /opt/mike-ai/stack/docs -type d -exec chown root:10001 {} + -exec chmod 0775 {} +
find /opt/mike-ai/stack/docs -type f -name '*.md' -exec chown root:10001 {} + -exec chmod 0664 {} +
systemctl daemon-reload
systemctl enable --now mike-ai-platform-context-snapshot.timer
systemctl start mike-ai-platform-context-snapshot.service
# One user-facing Athena Operator MCP controls the complete local AI platform
# through a root-side structured executor. It is intentionally not a general
# shell and exposes no raw Docker socket or host paths to the MCP container.
# One user-facing Athena Operator MCP controls the local AI platform through a
# root-side executor. The facade exposes six bounded tools and no Docker socket
# or host paths to its unprivileged container.
"$MCP_DIR/../operator/install-operator.sh"
profiles=()
+2 -1
View File
@@ -78,6 +78,7 @@ def recovery_status() -> dict[str, object]:
def main() -> None:
generated = int(time.time())
active = [item["name"].removeprefix("mike-ai-llama-") for item in containers() if item["name"].startswith("mike-ai-llama-")]
git_commit = command("git", "-C", str(STACK), "rev-parse", "HEAD")
commit_file = STACK / ".mike-ai-source-commit"
data = {
"generated_unix": generated,
@@ -91,7 +92,7 @@ def main() -> None:
"gpus": gpus(),
"containers": containers(),
"active_inference_profiles": active,
"source_commit": commit_file.read_text().strip() if commit_file.is_file() else None,
"source_commit": git_commit or (commit_file.read_text().strip() if commit_file.is_file() else None),
"documentation_tree_sha256": docs_hash(),
"recovery_kit": recovery_status(),
"privacy_scope": "No logs, prompts, chats, container environment values, file contents outside versioned docs, or secrets are collected.",
+148 -568
View File
@@ -1,42 +1,28 @@
#!/usr/bin/env python3
"""Bounded platform knowledge and documentation-maintenance MCP for Athena.
"""Small read-only Athena knowledge MCP.
This service deliberately has no Docker socket, shell tool, network egress or
secret mounts. Live data is supplied by a root-owned, fixed-command snapshot
timer. Canonical documentation may only be changed through a preview/apply
workflow and only below docs/.
The normal entry point is ATHENA.md. Large historical documentation remains
available through bounded search/read tools but is never loaded automatically.
"""
from __future__ import annotations
import hashlib
import difflib
import calendar
import json
import os
import re
import socket
import sys
import tempfile
import time
import uuid
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any
SERVER_VERSION = "1.1.0"
REPO_ROOT = Path(os.environ.get("ATHENA_REPO_ROOT", "/knowledge/repo"))
DOCS_ROOT = Path(os.environ.get("ATHENA_DOCS_ROOT", "/workspace/docs"))
VERSION = "2.0.0"
REPO_ROOT = Path(os.environ.get("ATHENA_REPO_ROOT", "/knowledge/repo")).resolve()
RUNTIME_FILE = Path(os.environ.get("ATHENA_RUNTIME_FILE", "/runtime/runtime.json"))
STATE_ROOT = Path(os.environ.get("ATHENA_CONTEXT_STATE", "/state"))
WRITE_MODE = os.environ.get("ATHENA_DOC_WRITE_MODE", "proposal-only")
MAX_DOCUMENT_CHARS = 24000
MAX_OVERVIEW_CHARS = 14_000
MAX_READ_LINES = 160
MAX_SEARCH_RESULTS = 8
MAX_UPDATE_CHARS = 120000
ALLOWED_TEXT_SUFFIXES = {".md", ".txt", ".yaml", ".yml", ".json", ".py", ".sh", ".service", ".timer", ".conf", ".example"}
EXCLUDED_PARTS = {".git", "__pycache__", "xtts-test-audio", ".venv", "node_modules"}
ALLOWED_SUFFIXES = {".md", ".json", ".yaml", ".yml", ".txt"}
BLOCKED_PARTS = {".git", "secrets", "private", "credentials"}
if hasattr(sys.stdin, "reconfigure"):
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
@@ -48,630 +34,224 @@ TOOLS = [
{
"name": "athena_get_overview",
"description": (
"USE FIRST when a request concerns Athena, MikeAI, its models, profiles, GPUs, "
"OpenWebUI, router, MCPs, TTS/STT, Vision, networking or recovery. Returns the "
"short authoritative architecture overview plus snapshot freshness. This is "
"read-only and contains no secrets. Runtime claims still require "
"athena_get_current_state or the relevant specialist MCP."
"START HERE for Athena architecture or administration. Returns the compact, "
"authoritative ATHENA.md. Do not read additional platform documents unless a "
"specific unresolved question remains."
),
"inputSchema": {"type": "object", "properties": {}, "additionalProperties": False},
},
{
"name": "athena_get_current_state",
"description": (
"USE for the current bounded Athena runtime inventory: host, filesystems, GPUs, "
"active MikeAI containers, active inference profile, source commit and recovery "
"freshness. The snapshot contains no logs, prompts, chats, environment values or "
"secrets. If stale, state that explicitly. For detailed service diagnosis use the "
"specialist management tool instead of guessing."
),
"description": "Return the compact generated runtime snapshot: active profile, containers, GPUs, source commit and recovery status.",
"inputSchema": {"type": "object", "properties": {}, "additionalProperties": False},
},
{
"name": "athena_get_external_services",
"description": (
"USE before designing or installing an integration that may already run on Unraid "
"or elsewhere in the home network. Returns the versioned, secret-free service "
"catalog and performs only fixed bounded reachability checks for those catalogued "
"endpoints. It accepts no host, URL or port from the model and is not a scanner. "
"A failed check means unavailable or unverified; it never authorizes creating a "
"duplicate service. Use the listed specialist MCP for detailed current state."
),
"description": "List known services outside Athena so an existing Unraid or home-network backend is reused instead of duplicated.",
"inputSchema": {"type": "object", "properties": {}, "additionalProperties": False},
},
{
"name": "athena_search_knowledge",
"name": "athena_search_reference",
"description": (
"USE to find the relevant MikeAI documentation, Compose definition, installer, "
"profile, runbook or source file before planning a platform change. Returns bounded "
"matching excerpts and paths. Do not repeatedly rephrase the same search; follow up "
"with athena_read_source for the selected file."
"Search ATHENA.md and documentation for one concrete term. Returns at most eight "
"short excerpts. Use only when ATHENA.md did not answer the question."
),
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string", "minLength": 2, "maxLength": 300},
"max_results": {"type": "integer", "minimum": 1, "maximum": 8, "default": 5},
},
"properties": {"query": {"type": "string", "minLength": 2, "maxLength": 120}},
"required": ["query"],
"additionalProperties": False,
},
},
{
"name": "athena_read_source",
"name": "athena_read_reference",
"description": (
"USE after athena_search_knowledge to read a bounded section of one versioned "
"MikeAI source or documentation file. Secret files, .git and binary artifacts are "
"not accessible. Paths are relative to the repository, for example "
"docs/OPERATIONS.md or platform/mcp/compose.yaml."
"Read a bounded line range from one known documentation file. Missing paths are "
"reported as a normal not-found result and must not be retried by guessing."
),
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string", "minLength": 3, "maxLength": 240},
"start_line": {"type": "integer", "minimum": 1, "default": 1},
"max_lines": {"type": "integer", "minimum": 1, "maximum": 300, "default": 160},
"path": {"type": "string", "pattern": "^[A-Za-z0-9_.+/-]{1,200}$"},
"start_line": {"type": "integer", "minimum": 1, "maximum": 1000000, "default": 1},
"line_count": {"type": "integer", "minimum": 1, "maximum": MAX_READ_LINES, "default": 80},
},
"required": ["path"],
"additionalProperties": False,
},
},
{
"name": "athena_get_change_workflow",
"description": (
"USE before adding or replacing a model, MCP, TTS/STT, Vision/image service, "
"OpenWebUI integration, network component or recovery behavior. Returns the source "
"files, safety gates, validation steps, documentation duties, Git duties and "
"recovery duties for that change type. It performs no change."
),
"inputSchema": {
"type": "object",
"properties": {
"change_type": {
"type": "string",
"enum": ["mcp", "model", "profile", "tts", "stt", "vision", "image", "openwebui", "network", "recovery", "other"],
}
},
"required": ["change_type"],
"additionalProperties": False,
},
},
{
"name": "athena_prepare_documentation_update",
"description": (
"USE only after a real platform change or verified documentation drift. Creates a "
"reviewable proposal; it does not alter canonical documentation. Each update must "
"target an existing or new Markdown file below docs/. Include only verified facts, "
"never secrets, prompts, chats or private content. After preview, wait for explicit "
"user approval before calling athena_apply_documentation_update."
),
"inputSchema": {
"type": "object",
"properties": {
"summary": {"type": "string", "minLength": 5, "maxLength": 500},
"evidence": {"type": "string", "minLength": 5, "maxLength": 2000},
"updates": {
"type": "array",
"minItems": 1,
"maxItems": 6,
"items": {
"type": "object",
"properties": {
"path": {"type": "string", "pattern": "^docs/[A-Za-z0-9_.-]+\\.md$"},
"content": {"type": "string", "minLength": 1, "maxLength": 120000},
},
"required": ["path", "content"],
"additionalProperties": False,
},
},
},
"required": ["summary", "evidence", "updates"],
"additionalProperties": False,
},
},
{
"name": "athena_apply_documentation_update",
"description": (
"WRITE TOOL. Use only after the user explicitly approved the exact proposal in the "
"current conversation. Applies an already prepared proposal atomically below docs/, "
"backs up prior files and appends an audit record. It cannot change code, Compose, "
"services, secrets, Git or recovery bundles. The result always lists required Git "
"commit/push and recovery refresh work; never claim those are complete unless their "
"separate tools verify them."
),
"inputSchema": {
"type": "object",
"properties": {
"proposal_id": {"type": "string", "pattern": "^[a-f0-9]{32}$"},
"confirmation": {"type": "string", "minLength": 38, "maxLength": 64},
},
"required": ["proposal_id", "confirmation"],
"additionalProperties": False,
},
},
{
"name": "athena_get_maintenance_status",
"description": (
"USE after documentation or platform work. Reports pending documentation proposals, "
"applied documentation changes awaiting Git/recovery handling, source commit and "
"recovery-kit freshness. It never commits, pushes or rebuilds recovery automatically."
),
"inputSchema": {"type": "object", "properties": {}, "additionalProperties": False},
},
{
"name": "athena_close_maintenance_record",
"description": (
"WRITE TOOL for maintenance metadata only. Use after separate tools have verified "
"that the documentation change was committed/pushed, deployed to Athena and followed "
"by a newer recovery kit. The server checks source commit and recovery timestamp before "
"moving the record to resolved. It changes no documentation, Git or recovery data."
),
"inputSchema": {
"type": "object",
"properties": {
"proposal_id": {"type": "string", "pattern": "^[a-f0-9]{32}$"},
"git_commit": {"type": "string", "pattern": "^[a-f0-9]{40}$"},
"verification": {"type": "string", "minLength": 10, "maxLength": 1000},
"confirmation": {"type": "string", "minLength": 38, "maxLength": 64},
},
"required": ["proposal_id", "git_commit", "verification", "confirmation"],
"additionalProperties": False,
},
},
]
def now_iso() -> str:
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
def result_error(message: str, **details: Any) -> dict[str, Any]:
return {"ok": False, "error": message, "retry": False, **details}
def json_text(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
def allowed_text_file(path: Path) -> bool:
return (
path.suffix.lower() in ALLOWED_TEXT_SUFFIXES
or path.name.startswith("Dockerfile")
or path.name in {"LLAMA_CPP_COMMIT"}
)
def safe_repo_path(relative: str) -> Path:
if not relative or relative.startswith("/") or "\\" in relative:
raise ValueError("path must be repository-relative")
parts = Path(relative).parts
if ".." in parts or any(part in EXCLUDED_PARTS for part in parts):
raise ValueError("path is outside the allowed source tree")
lowered = relative.lower()
if any(token in lowered for token in ("secret", "authorized_keys", ".env", "agekey")):
raise ValueError("secret-bearing paths are not exposed")
path = (REPO_ROOT / relative).resolve()
root = REPO_ROOT.resolve()
if root not in path.parents and path != root:
raise ValueError("path escapes repository")
if not path.is_file() or not allowed_text_file(path):
raise ValueError("path is not an allowed text source")
return path
def safe_doc_path(relative: str) -> Path:
match = re.fullmatch(r"docs/([A-Za-z0-9_.-]+\.md)", relative)
if not match:
raise ValueError("documentation updates are limited to docs/*.md")
path = (DOCS_ROOT / match.group(1)).resolve()
root = DOCS_ROOT.resolve()
if root not in path.parents:
raise ValueError("documentation path escapes docs root")
if path.exists() and path.is_symlink():
raise ValueError("symbolic links are not writable")
return path
def read_runtime() -> dict[str, Any]:
def safe_path(relative: str) -> Path | None:
if not relative or relative.startswith("/"):
return None
candidate = Path(relative)
if ".." in candidate.parts or any(part.lower() in BLOCKED_PARTS for part in candidate.parts):
return None
target = (REPO_ROOT / candidate).resolve(strict=False)
try:
data = json.loads(RUNTIME_FILE.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
return {"available": False, "error": str(exc), "instruction": "Do not infer current runtime state."}
generated = int(data.get("generated_unix", 0))
age = max(0, int(time.time()) - generated) if generated else None
data["available"] = True
data["age_seconds"] = age
data["stale"] = age is None or age > 180
return data
target.relative_to(REPO_ROOT)
except ValueError:
return None
if candidate.name != "ATHENA.md" and (not candidate.parts or candidate.parts[0] != "docs"):
return None
if target.suffix.lower() not in ALLOWED_SUFFIXES:
return None
return target
def read_text(path: Path, limit: int | None = None) -> str:
text = path.read_text(encoding="utf-8", errors="replace")
return text if limit is None else text[:limit]
def overview() -> dict[str, Any]:
path = REPO_ROOT / "docs/PLATFORM_OVERVIEW.md"
text = path.read_text(encoding="utf-8")[:MAX_DOCUMENT_CHARS]
runtime = read_runtime()
return {
"source": "docs/PLATFORM_OVERVIEW.md",
"source_hierarchy": [
"current specialist-tool evidence",
"bounded Athena runtime snapshot",
"CURRENT_REFERENCE.md and STANDARD_PROFILE_MATRIX.md",
"versioned source and runbooks",
"chat memory only as an unverified hint",
],
"runtime_snapshot": {k: runtime.get(k) for k in ("available", "generated_at", "age_seconds", "stale", "source_commit")},
"content": text,
"instruction": "Search or read the relevant source before proposing a change; verify mutable claims with a current tool.",
}
path = REPO_ROOT / "ATHENA.md"
try:
content = read_text(path, MAX_OVERVIEW_CHARS)
except (OSError, PermissionError) as exc:
return result_error("ATHENA.md is unavailable", path="ATHENA.md", detail=str(exc))
return {"ok": True, "source": "ATHENA.md", "content": content, "truncated": path.stat().st_size > len(content.encode())}
def current_state() -> dict[str, Any]:
data = read_runtime()
data["scope"] = "bounded metadata only; no logs, prompts, chats, environment values or secrets"
if data.get("stale"):
data["instruction"] = "Snapshot is stale. Do not claim current service state until a specialist tool verifies it."
return data
def external_services() -> dict[str, Any]:
catalog_path = REPO_ROOT / "config/service-catalog.json"
try:
catalog = json.loads(catalog_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
return {
"available": False,
"error": str(exc),
"instruction": "The service inventory is unavailable. Do not infer that a replacement service is needed.",
}
results = []
for service in catalog.get("services", [])[:24]:
item = {key: service.get(key) for key in (
"id", "name", "location", "host", "address", "port", "protocol",
"specialist_tool", "purpose",
)}
address = str(service.get("address", ""))
port = int(service.get("port", 0))
started = time.monotonic()
reachable = False
http_status = None
error = None
try:
with socket.create_connection((address, port), timeout=2):
reachable = True
if service.get("probe") == "http-head":
path = str(service.get("probe_path", "/"))
url = f"{service.get('protocol', 'http')}://{address}:{port}{path}"
request = urllib.request.Request(url, method="HEAD", headers={"User-Agent": "MikeAI-Service-Catalog/1"})
try:
with urllib.request.urlopen(request, timeout=3) as response:
http_status = response.status
except urllib.error.HTTPError as exc:
http_status = exc.code
except (OSError, ValueError, urllib.error.URLError) as exc:
error = type(exc).__name__
item["check"] = {
"reachable": reachable,
"http_status": http_status,
"elapsed_ms": round((time.monotonic() - started) * 1000),
"error_class": error,
}
results.append(item)
value = json.loads(RUNTIME_FILE.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
return result_error("runtime snapshot is unavailable", detail=str(exc))
containers = value.get("containers") or []
return {
"available": True,
"source": "config/service-catalog.json",
"catalog_version": catalog.get("version"),
"updated": catalog.get("updated"),
"services": results,
"instruction": (
"Existing catalog entries are architecture constraints, not disposable suggestions. "
"If a check or specialist tool fails, report the gap and ask for direction; do not plan a duplicate backend."
),
"ok": True,
"generated_at": value.get("generated_at"),
"hostname": value.get("hostname"),
"active_inference_profiles": value.get("active_inference_profiles") or [],
"source_commit": value.get("source_commit"),
"gpus": value.get("gpus") or [],
"containers": containers,
"container_count": len(containers),
"recovery_kit": value.get("recovery_kit") or {"present": False},
}
def candidate_files() -> list[Path]:
files: list[Path] = []
for path in REPO_ROOT.rglob("*"):
try:
rel = path.relative_to(REPO_ROOT)
except ValueError:
continue
if not path.is_file() or any(part in EXCLUDED_PARTS for part in rel.parts):
continue
if not allowed_text_file(path):
continue
lowered = str(rel).lower()
if any(token in lowered for token in ("secret", "authorized_keys", ".env", "agekey")):
continue
files.append(path)
def external_services() -> dict[str, Any]:
path = REPO_ROOT / "config/service-catalog.json"
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
return result_error("service catalog is unavailable", detail=str(exc))
services = []
for item in value.get("services", []):
services.append({key: item.get(key) for key in ("id", "name", "host", "address", "port", "protocol", "purpose") if item.get(key) is not None})
return {"ok": True, "services": services, "count": len(services)}
def reference_files() -> list[Path]:
files = [REPO_ROOT / "ATHENA.md"]
docs = REPO_ROOT / "docs"
try:
files.extend(sorted(path for path in docs.glob("*.md") if path.is_file()))
except OSError:
pass
return files
def search_knowledge(arguments: dict[str, Any]) -> dict[str, Any]:
def search_reference(arguments: dict[str, Any]) -> dict[str, Any]:
query = str(arguments.get("query", "")).strip()
if len(query) < 2:
raise ValueError("query is too short")
limit = max(1, min(MAX_SEARCH_RESULTS, int(arguments.get("max_results", 5))))
terms = [term for term in re.findall(r"[a-zA-Z0-9_.-]{2,}", query.lower()) if term]
scored: list[tuple[int, str, int, str]] = []
for path in candidate_files():
return result_error("query must contain at least two characters")
pattern = re.compile(re.escape(query), re.IGNORECASE)
matches: list[dict[str, Any]] = []
for path in reference_files():
try:
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
except OSError:
continue
rel = str(path.relative_to(REPO_ROOT))
for index, line in enumerate(lines):
lower = line.lower()
score = sum(3 if term in rel.lower() else 1 for term in terms if term in lower or term in rel.lower())
if score:
excerpt = "\n".join(lines[max(0, index - 2): min(len(lines), index + 4)])[:1800]
scored.append((score, rel, index + 1, excerpt))
scored.sort(key=lambda item: (-item[0], item[1], item[2]))
seen: set[tuple[str, int]] = set()
results = []
for score, rel, line, excerpt in scored:
key = (rel, line // 20)
if key in seen:
continue
seen.add(key)
results.append({"path": rel, "line": line, "score": score, "excerpt": excerpt})
if len(results) >= limit:
break
return {"query": query, "count": len(results), "results": results, "instruction": "Read selected sources; do not treat search excerpts as current runtime proof."}
for number, line in enumerate(lines, 1):
if pattern.search(line):
matches.append({
"path": str(path.relative_to(REPO_ROOT)),
"line": number,
"excerpt": line.strip()[:280],
})
if len(matches) >= MAX_SEARCH_RESULTS:
return {"ok": True, "query": query, "matches": matches, "truncated": True}
return {"ok": True, "query": query, "matches": matches, "truncated": False}
def read_source(arguments: dict[str, Any]) -> dict[str, Any]:
def read_reference(arguments: dict[str, Any]) -> dict[str, Any]:
relative = str(arguments.get("path", ""))
path = safe_repo_path(relative)
path = safe_path(relative)
if path is None:
return result_error("path is not an allowed documentation path", path=relative)
if not path.is_file():
return result_error("documentation file not found", path=relative)
start = max(1, int(arguments.get("start_line", 1)))
max_lines = max(1, min(300, int(arguments.get("max_lines", 160))))
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
selected = lines[start - 1:start - 1 + max_lines]
content = "\n".join(f"{start + i}: {line}" for i, line in enumerate(selected))
return {"path": relative, "start_line": start, "end_line": start + len(selected) - 1, "total_lines": len(lines), "truncated": start - 1 + len(selected) < len(lines), "content": content[:MAX_DOCUMENT_CHARS]}
WORKFLOWS = {
"mcp": ["platform/mcp/compose.yaml", "platform/mcp/README.md", "platform/mcp/athena_operator_mcp.py", "platform/hermes/skills/athena-operator/SKILL.md", "compose.yaml", "docs/COMPONENTS.md", "docs/SECURITY.md", "docs/PLATFORM_CONTEXT_MCP.md", "docs/QWEN_OPERATOR_CONTEXT.md"],
"model": ["config/install.env.example", "platform/models/manifest.example.yaml", "platform/profiles/", "docs/STANDARD_PROFILE_MATRIX.md", "docs/QWEN_OPERATOR_CONTEXT.md"],
"profile": ["platform/profiles/", "router/router_profiles.json", "platform/openwebui/install-models.sh", "docs/STANDARD_PROFILE_MATRIX.md"],
"tts": ["compose.yaml", "router/xtts_worker.py", "platform/scripts/rollback-tts-production.sh", "docs/XTTS_EVALUATION_2026-08-23.md"],
"stt": ["compose.yaml", "router/stt_worker.py", "docs/COMPONENTS.md"],
"vision": ["compose.yaml", "router/ai_profile_router.py", "docs/STANDARD_PROFILE_MATRIX.md"],
"image": ["compose.yaml", "router/image_worker.py", "docs/OPERATIONS.md"],
"openwebui": ["compose.yaml", "platform/openwebui/", "docs/OPERATIONS.md", "docs/DISASTER_RECOVERY.md"],
"network": ["compose.yaml", "platform/host/", "docs/SECURITY.md", "docs/WIREGUARD_HOME_PEER.md", "docs/EMERGENCY_UNI_ACCESS.md"],
"recovery": ["platform/recovery/", "docs/BARE_METAL_RECOVERY.md", "docs/DISASTER_RECOVERY.md", "docs/RECOVERY_REQUIREMENTS.md"],
"other": ["docs/PLATFORM_OVERVIEW.md", "docs/QWEN_OPERATOR_CONTEXT.md", "docs/OPERATIONS.md"],
}
def change_workflow(arguments: dict[str, Any]) -> dict[str, Any]:
kind = str(arguments.get("change_type", "other"))
if kind not in WORKFLOWS:
raise ValueError("unsupported change_type")
count = min(MAX_READ_LINES, max(1, int(arguments.get("line_count", 80))))
try:
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
except OSError as exc:
return result_error("documentation file is unreadable", path=relative, detail=str(exc))
selected = lines[start - 1:start - 1 + count]
return {
"change_type": kind,
"read_first": WORKFLOWS[kind],
"mandatory_sequence": [
"Capture current state with the narrowest specialist tool.",
"Read relevant versioned sources and identify documentation drift.",
"Define rollback and protect SSH, LAN, WireGuard and the active inference path.",
"Prepare and apply source changes with Athena Operator operation file_update. Never clone the repository inside the sandbox and never request or copy an SSH key.",
"Validate syntax/configuration with Athena Operator operation run_checks and run a bounded synthetic test.",
"Deploy only named services with Athena Operator operation compose_deploy.",
"Verify service health and remote reachability without reading chats or private payloads.",
"Update PLATFORM_OVERVIEW/CURRENT_REFERENCE/QWEN_OPERATOR_CONTEXT and the affected runbook.",
"Commit and push only the explicitly selected changed paths with Athena Operator operation git_publish.",
"Create and verify a new encrypted recovery bundle and self-contained data-disk kit with Athena Operator operation recovery.",
],
"hard_boundaries": [
"This context MCP does not modify services, Docker, networking, models or secrets.",
"The sandbox needs neither a Git clone nor an SSH key; Athena Operator owns the canonical repository and deploy credentials.",
"No shutdown, reboot, kernel/driver, SSH, firewall or VPN change without exact user approval and rollback.",
"Never claim Git or recovery is current until separately verified.",
],
"ok": True,
"path": relative,
"start_line": start,
"end_line": start + len(selected) - 1 if selected else start - 1,
"total_lines": len(lines),
"content": "\n".join(selected),
"truncated": start - 1 + len(selected) < len(lines),
}
def prepare_update(arguments: dict[str, Any]) -> dict[str, Any]:
summary = str(arguments.get("summary", "")).strip()
evidence = str(arguments.get("evidence", "")).strip()
updates = arguments.get("updates")
if len(summary) < 5 or len(evidence) < 5 or not isinstance(updates, list) or not updates:
raise ValueError("summary, evidence and at least one update are required")
normalized = []
total = 0
for update in updates[:6]:
relative = str(update.get("path", ""))
safe_doc_path(relative)
content = str(update.get("content", ""))
if not content or len(content) > MAX_UPDATE_CHARS:
raise ValueError("invalid documentation content size")
if re.search(r"(?i)(BEGIN [A-Z ]*PRIVATE KEY|github_pat_[A-Za-z0-9_]+|GITHUB_PERSONAL_ACCESS_TOKEN\s*=\s*\S+)", content):
raise ValueError("probable secret material detected")
total += len(content)
if total > MAX_UPDATE_CHARS * 2:
raise ValueError("proposal is too large")
target = safe_doc_path(relative)
previous = target.read_text(encoding="utf-8") if target.exists() else ""
diff = "\n".join(difflib.unified_diff(previous.splitlines(), content.splitlines(), fromfile=f"a/{relative}", tofile=f"b/{relative}", lineterm=""))
normalized.append({"path": relative, "content": content, "before_sha256": hashlib.sha256(previous.encode()).hexdigest(), "after_sha256": hashlib.sha256(content.encode()).hexdigest(), "before_chars": len(previous), "after_chars": len(content), "diff_preview": diff[:12000]})
proposal_id = uuid.uuid4().hex
proposal = {"proposal_id": proposal_id, "created_at": now_iso(), "summary": summary, "evidence": evidence, "updates": normalized, "status": "pending"}
pending = STATE_ROOT / "pending"
pending.mkdir(parents=True, exist_ok=True)
(pending / f"{proposal_id}.json").write_text(json.dumps(proposal, ensure_ascii=False, indent=2), encoding="utf-8")
return {"proposal_id": proposal_id, "summary": summary, "files": [{k: item[k] for k in ("path", "before_sha256", "after_sha256", "before_chars", "after_chars", "diff_preview")} for item in normalized], "canonical_files_changed": False, "required_confirmation": f"APPLY {proposal_id}", "instruction": "Show this proposal to the user and wait for explicit approval. Do not call apply in the same autonomous tool sequence."}
def apply_update(arguments: dict[str, Any]) -> dict[str, Any]:
proposal_id = str(arguments.get("proposal_id", ""))
confirmation = str(arguments.get("confirmation", ""))
if not re.fullmatch(r"[a-f0-9]{32}", proposal_id):
raise ValueError("invalid proposal_id")
if confirmation != f"APPLY {proposal_id}":
raise ValueError("confirmation does not match the exact proposal")
if WRITE_MODE != "enabled":
raise PermissionError("documentation writes are in proposal-only mode")
proposal_path = STATE_ROOT / "pending" / f"{proposal_id}.json"
if not proposal_path.is_file():
raise ValueError("proposal not found or already applied")
proposal = json.loads(proposal_path.read_text(encoding="utf-8"))
backup_root = STATE_ROOT / "backups" / f"{int(time.time())}-{proposal_id}"
backup_root.mkdir(parents=True, exist_ok=False)
changed = []
for item in proposal["updates"]:
target = safe_doc_path(item["path"])
current = target.read_text(encoding="utf-8") if target.exists() else ""
current_hash = hashlib.sha256(current.encode()).hexdigest()
if current_hash != item["before_sha256"]:
raise RuntimeError(f"documentation drift after preview: {item['path']}")
if target.exists():
(backup_root / target.name).write_text(current, encoding="utf-8")
target.parent.mkdir(parents=True, exist_ok=True)
fd, temporary = tempfile.mkstemp(prefix=f".{target.name}.", dir=target.parent)
try:
with os.fdopen(fd, "w", encoding="utf-8") as handle:
handle.write(item["content"])
handle.flush()
os.fsync(handle.fileno())
os.chmod(temporary, 0o664)
os.replace(temporary, target)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
changed.append(item["path"])
applied = STATE_ROOT / "applied"
applied.mkdir(parents=True, exist_ok=True)
proposal["status"] = "applied_docs_only"
proposal["applied_at"] = now_iso()
proposal["backup_dir"] = str(backup_root)
destination = applied / proposal_path.name
destination.write_text(json.dumps(proposal, ensure_ascii=False, indent=2), encoding="utf-8")
proposal_path.unlink()
return {
"documentation_applied": True,
"changed_files": changed,
"backup_dir": str(backup_root),
"git_commit_complete": False,
"git_push_complete": False,
"recovery_refresh_complete": False,
"required_next_steps": [
"Use an authorized Git tool to apply the same documentation change to the private source repository, review diff, commit and push.",
"Deploy the committed source back to Athena so .mike-ai-source-commit matches.",
"Create and verify a new encrypted recovery bundle and self-contained /data recovery kit.",
"Run athena_get_maintenance_status and the platform verification checklist.",
],
"instruction": "Do not say the platform is fully documented or recoverable until all three false fields are separately verified.",
}
def maintenance_status() -> dict[str, Any]:
pending_dir = STATE_ROOT / "pending"
applied_dir = STATE_ROOT / "applied"
pending = sorted(path.stem for path in pending_dir.glob("*.json")) if pending_dir.exists() else []
applied = sorted(applied_dir.glob("*.json"), key=lambda path: path.stat().st_mtime, reverse=True) if applied_dir.exists() else []
runtime = read_runtime()
latest_applied = None
if applied:
data = json.loads(applied[0].read_text(encoding="utf-8"))
latest_applied = {"proposal_id": data.get("proposal_id"), "summary": data.get("summary"), "applied_at": data.get("applied_at"), "status": data.get("status")}
return {
"pending_proposals": pending,
"latest_applied_documentation_change": latest_applied,
"source_commit": runtime.get("source_commit"),
"documentation_tree_sha256": runtime.get("documentation_tree_sha256"),
"recovery_kit": runtime.get("recovery_kit"),
"attention_required": bool(pending or latest_applied),
"instruction": "Applied records mean Git and recovery may still be stale; verify them with their dedicated workflow before clearing the maintenance debt.",
}
def close_maintenance(arguments: dict[str, Any]) -> dict[str, Any]:
proposal_id = str(arguments.get("proposal_id", ""))
git_commit = str(arguments.get("git_commit", ""))
confirmation = str(arguments.get("confirmation", ""))
verification = str(arguments.get("verification", "")).strip()
if not re.fullmatch(r"[a-f0-9]{32}", proposal_id):
raise ValueError("invalid proposal_id")
if not re.fullmatch(r"[a-f0-9]{40}", git_commit):
raise ValueError("invalid git_commit")
if confirmation != f"CLOSE {proposal_id}":
raise ValueError("confirmation does not match the exact record")
if len(verification) < 10:
raise ValueError("verification summary is required")
record = STATE_ROOT / "applied" / f"{proposal_id}.json"
if not record.is_file():
raise ValueError("applied maintenance record not found")
data = json.loads(record.read_text(encoding="utf-8"))
runtime = read_runtime()
if runtime.get("stale"):
raise RuntimeError("runtime snapshot is stale")
if runtime.get("source_commit") != git_commit:
raise RuntimeError("deployed source commit does not match the verified Git commit")
applied_at = int(calendar.timegm(time.strptime(data["applied_at"], "%Y-%m-%dT%H:%M:%SZ")))
recovery = runtime.get("recovery_kit") or {}
if not recovery.get("present") or int(recovery.get("modified_unix", 0)) <= applied_at:
raise RuntimeError("recovery kit is absent or older than the documentation change")
data.update({"status": "resolved", "resolved_at": now_iso(), "git_commit": git_commit, "verification": verification, "recovery_kit": recovery})
resolved = STATE_ROOT / "resolved"
resolved.mkdir(parents=True, exist_ok=True)
destination = resolved / record.name
destination.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
record.unlink()
return {"resolved": True, "proposal_id": proposal_id, "git_commit": git_commit, "recovery_kit": recovery.get("target"), "instruction": "Maintenance debt is closed because deployed Git and a newer recovery kit were both verified."}
def call_tool(name: str, arguments: dict[str, Any]) -> str:
def call_tool(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
if name == "athena_get_overview":
result = overview()
elif name == "athena_get_current_state":
result = current_state()
elif name == "athena_get_external_services":
result = external_services()
elif name == "athena_search_knowledge":
result = search_knowledge(arguments)
elif name == "athena_read_source":
result = read_source(arguments)
elif name == "athena_get_change_workflow":
result = change_workflow(arguments)
elif name == "athena_prepare_documentation_update":
result = prepare_update(arguments)
elif name == "athena_apply_documentation_update":
result = apply_update(arguments)
elif name == "athena_get_maintenance_status":
result = maintenance_status()
elif name == "athena_close_maintenance_record":
result = close_maintenance(arguments)
else:
raise ValueError(f"unknown tool: {name}")
return json_text(result)
return overview()
if name == "athena_get_current_state":
return current_state()
if name == "athena_get_external_services":
return external_services()
if name == "athena_search_reference":
return search_reference(arguments)
if name == "athena_read_reference":
return read_reference(arguments)
return result_error("unknown tool", tool=name)
def response(request_id: Any, result: Any = None, error: dict[str, Any] | None = None) -> None:
payload: dict[str, Any] = {"jsonrpc": "2.0", "id": request_id}
payload["error" if error is not None else "result"] = error if error is not None else result
sys.stdout.write(json_text(payload) + "\n")
def emit(request_id: Any, result: Any = None, error: dict[str, Any] | None = None) -> None:
message = {"jsonrpc": "2.0", "id": request_id}
message["error" if error else "result"] = error or result
sys.stdout.write(json.dumps(message, ensure_ascii=False, separators=(",", ":")) + "\n")
sys.stdout.flush()
def handle(message: dict[str, Any]) -> None:
method = message.get("method")
request_id = message.get("id")
method, request_id = message.get("method"), message.get("id")
if method == "initialize":
response(request_id, {"protocolVersion": message.get("params", {}).get("protocolVersion", "2024-11-05"), "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": "mike-ai-platform-context", "version": SERVER_VERSION}})
emit(request_id, {
"protocolVersion": message.get("params", {}).get("protocolVersion", "2024-11-05"),
"capabilities": {"tools": {"listChanged": False}},
"serverInfo": {"name": "mike-ai-platform-context", "version": VERSION},
})
elif method == "tools/list":
response(request_id, {"tools": TOOLS})
emit(request_id, {"tools": TOOLS})
elif method == "tools/call":
params = message.get("params", {})
try:
text = call_tool(str(params.get("name", "")), params.get("arguments") or {})
response(request_id, {"content": [{"type": "text", "text": text}], "structuredContent": json.loads(text), "isError": False})
except Exception as exc:
response(request_id, {"content": [{"type": "text", "text": f"ERROR: {exc}"}], "isError": True})
params = message.get("params") or {}
value = call_tool(str(params.get("name", "")), params.get("arguments") or {})
emit(request_id, {
"content": [{"type": "text", "text": json.dumps(value, ensure_ascii=False, separators=(",", ":"))}],
"structuredContent": value,
"isError": False,
})
elif request_id is not None:
response(request_id, error={"code": -32601, "message": f"Method not found: {method}"})
emit(request_id, error={"code": -32601, "message": "method not found"})
def main() -> None:
STATE_ROOT.mkdir(parents=True, exist_ok=True)
for line in sys.stdin:
try:
if line.strip():