Improve external service discovery and tool routing

This commit is contained in:
Mikei386 committed 2026-08-23 21:44:33 +02:00
1 parent b5118099ed
commit feb99293b5
10 files changed
+198 -10

No files matched your search

+8 -2
View File
@@ -142,7 +142,7 @@ services:
build:
context: .
dockerfile: Dockerfile.platform-context
image: mike-ai/mcp-platform-context:1.0.0
image: mike-ai/mcp-platform-context:1.1.0
container_name: mike-ai-mcp-platform-context
environment:
ATHENA_REPO_ROOT: /knowledge/repo
@@ -158,7 +158,9 @@ services:
- ${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
networks: [tools]
# 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]
healthcheck:
test: ["CMD", "python", "-c", "import socket; s=socket.create_connection(('127.0.0.1',8000),2); s.close()"]
interval: 30s
@@ -224,6 +226,10 @@ services:
UNRAID_RMCP_DISABLE_HTTP_AUTH: "true"
UNRAID_NOAUTH: "true"
UNRAID_RMCP_ALLOWED_HOSTS: "mike-ai-mcp-unraid-official:8000,mike-ai-mcp-unraid-official,localhost:8000,127.0.0.1:8000"
# Public DNS cannot resolve the Fritzbox-only name. Preserve the hostname
# used by the TLS endpoint while binding it to the verified home-LAN IP.
extra_hosts:
- "homeserver.fritz.box:192.168.1.2"
volumes:
- ${RUNRAID_BINARY:-/usr/local/bin/runraid}:/usr/local/bin/unraid:ro
entrypoint: ["/usr/local/bin/unraid"]
+75 -1
View File
@@ -15,15 +15,18 @@ 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.0.0"
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"))
RUNTIME_FILE = Path(os.environ.get("ATHENA_RUNTIME_FILE", "/runtime/runtime.json"))
@@ -64,6 +67,18 @@ TOOLS = [
),
"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."
),
"inputSchema": {"type": "object", "properties": {}, "additionalProperties": False},
},
{
"name": "athena_search_knowledge",
"description": (
@@ -293,6 +308,63 @@ def current_state() -> dict[str, Any]:
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)
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."
),
}
def candidate_files() -> list[Path]:
files: list[Path] = []
for path in REPO_ROOT.rglob("*"):
@@ -550,6 +622,8 @@ def call_tool(name: str, arguments: dict[str, Any]) -> str:
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":
@@ -1,7 +1,7 @@
"""
title: MikeAI Auto Tool Selector
author: MikeAI
version: 2.0.0
version: 2.1.0
description: Selects a small, relevant set of MCP servers for each user request.
"""
@@ -80,7 +80,10 @@ class Filter:
"unless the user's current message explicitly requests that exact change "
"and every required preview, confirmation, backup, and validation rule "
"of the tool is satisfied. Do not call unrelated tools merely because "
"they are available. If the selected tool cannot verify the claim, say so."
"they are available. If the selected tool cannot verify the claim, say so. "
"A missing, disabled or failed inventory tool is not evidence that a service "
"does not exist. Never compensate by planning or installing a duplicate backend; "
"stop and request clarification."
)
messages = body.setdefault("messages", [])
for message in messages:
@@ -165,9 +168,13 @@ class Filter:
),
)
# A specialist source is more precise than public web search. Platform
# wins over a generic Docker mention when Athena is explicitly named.
if operator:
# Existing infrastructure must be inspected before a new integration
# is designed. For a GitHub-backed MCP that targets a service already
# running on Unraid, source plus Unraid inventory are the two most
# useful bounded capabilities; the Operator follows at implementation.
if github and unraid:
selected.extend(("github", "unraid"))
elif operator:
selected.append("operator")
elif platform:
selected.append("platform")