Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020
This commit is contained in:
commit
cb5be898ea
663 files changed
+82674
No files matched your search
@@ -0,0 +1,136 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from mcp.server.mcpserver import MCPServer
|
||||
from mcp.server.mcpserver.context import Context
|
||||
|
||||
from connectors.unraid import RegisteredContainer, UnraidConnector
|
||||
from connectors.unraid.client import FileBrokerTokenProvider, HttpBrokerClient
|
||||
from packages.connector_kit import ConnectorContext, PageRequest
|
||||
|
||||
from .common import (
|
||||
connector_context,
|
||||
deterministic_id,
|
||||
load_connector_identity_key,
|
||||
read_regular_json,
|
||||
request_headers,
|
||||
)
|
||||
from .mcp_server import add_operational_routes, add_workflow_prompts, run_mcp
|
||||
|
||||
|
||||
def build_connector() -> tuple[UnraidConnector, set[str]]:
|
||||
path = Path(os.environ.get("UNRAID_REGISTRATIONS_FILE", "/etc/itworx/registered-targets.json"))
|
||||
document = read_regular_json(path)
|
||||
registrations: list[RegisteredContainer] = []
|
||||
projects: set[str] = set()
|
||||
for item in document.get("containers", []):
|
||||
if not isinstance(item, dict):
|
||||
raise ValueError("Unraid registration contains an invalid entry")
|
||||
project = str(item.get("project", "")).strip().lower()
|
||||
environment = str(item.get("environment", "")).strip().lower()
|
||||
registered_id = str(item.get("registeredId", "")).strip()
|
||||
registrations.append(
|
||||
RegisteredContainer(
|
||||
registered_id,
|
||||
deterministic_id("project", project),
|
||||
environment,
|
||||
)
|
||||
)
|
||||
projects.add(project)
|
||||
if not projects:
|
||||
raise ValueError("at least one Unraid project registration is required")
|
||||
client = HttpBrokerClient(
|
||||
os.environ.get("BROKER_URL", "http://docker-read-broker:8080"),
|
||||
FileBrokerTokenProvider(
|
||||
os.environ.get("BROKER_TOKEN_FILE", "/run/secrets/docker_broker_service_token")
|
||||
),
|
||||
)
|
||||
return UnraidConnector(client, tuple(registrations)), projects
|
||||
|
||||
|
||||
def build_server() -> MCPServer[Any]:
|
||||
connector, projects = build_connector()
|
||||
signing_key = load_connector_identity_key()
|
||||
server: MCPServer[Any] = MCPServer(
|
||||
"itworx-unraid",
|
||||
title="ITWorx Unraid read-only connector",
|
||||
version="1.0.0",
|
||||
instructions=(
|
||||
"Only broker-registered, bounded read operations are available. Inspect health "
|
||||
"before requesting logs, keep log windows small, distinguish observed facts from "
|
||||
"hypotheses, and never claim or request a container mutation."
|
||||
),
|
||||
)
|
||||
add_operational_routes(server, "unraid-connector")
|
||||
add_workflow_prompts(server, "unraid")
|
||||
|
||||
def current(context: Context[Any, Any]) -> ConnectorContext:
|
||||
return connector_context(request_headers(context), projects, signing_key=signing_key)
|
||||
|
||||
@server.tool(name="unraid.host.status", structured_output=True)
|
||||
async def host_status(context: Context[Any, Any]) -> dict[str, Any]:
|
||||
return connector.host_status(current(context))
|
||||
|
||||
@server.tool(name="unraid.project.summary", structured_output=True)
|
||||
async def project_summary(context: Context[Any, Any]) -> dict[str, Any]:
|
||||
return connector.project_summary(current(context))
|
||||
|
||||
@server.tool(name="unraid.storage.list", structured_output=True)
|
||||
async def storage_list(
|
||||
context: Context[Any, Any], limit: int = 25, cursor: str | None = None
|
||||
) -> dict[str, Any]:
|
||||
return connector.storage_list(
|
||||
current(context), page=PageRequest(limit=limit, cursor=cursor)
|
||||
)
|
||||
|
||||
@server.tool(name="unraid.container.list", structured_output=True)
|
||||
async def container_list(
|
||||
context: Context[Any, Any],
|
||||
state: str | None = None,
|
||||
limit: int = 25,
|
||||
cursor: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
return connector.container_list(
|
||||
current(context), state=state, page=PageRequest(limit=limit, cursor=cursor)
|
||||
)
|
||||
|
||||
@server.tool(name="unraid.container.inspect", structured_output=True)
|
||||
async def container_inspect(containerRef: str, context: Context[Any, Any]) -> dict[str, Any]:
|
||||
return connector.container_inspect(current(context), containerRef)
|
||||
|
||||
@server.tool(name="unraid.container.health", structured_output=True)
|
||||
async def container_health(containerRef: str, context: Context[Any, Any]) -> dict[str, Any]:
|
||||
return connector.container_health(current(context), containerRef)
|
||||
|
||||
@server.tool(name="unraid.container.logs", structured_output=True)
|
||||
async def container_logs(
|
||||
containerRef: str,
|
||||
context: Context[Any, Any],
|
||||
tail: int = 200,
|
||||
sinceSeconds: int | None = None,
|
||||
contains: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
return connector.container_logs(
|
||||
current(context),
|
||||
containerRef,
|
||||
tail=tail,
|
||||
since_seconds=sinceSeconds,
|
||||
contains=contains,
|
||||
)
|
||||
|
||||
@server.tool(name="unraid.network.list", structured_output=True)
|
||||
async def network_list(
|
||||
context: Context[Any, Any], limit: int = 25, cursor: str | None = None
|
||||
) -> dict[str, Any]:
|
||||
return connector.network_list(
|
||||
current(context), page=PageRequest(limit=limit, cursor=cursor)
|
||||
)
|
||||
|
||||
return server
|
||||
|
||||
|
||||
def run() -> None:
|
||||
run_mcp(build_server())
|
||||
Reference in new issue
Block a user