03e3df058f
보호 컨테이너(home-caddy, home-fail2ban, nanoclaude) 재시작 차단. MCP 11개 도구 + NanoClaude wrapper. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
148 lines
4.2 KiB
Python
148 lines
4.2 KiB
Python
"""MCP Infra Server — thin wrapper over core/ functions.
|
|
|
|
This file ONLY does:
|
|
1. MCP tool registration (decorators)
|
|
2. Parameter validation
|
|
3. Call core/ functions
|
|
4. Return results as JSON text
|
|
|
|
All actual logic lives in src/core/.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from mcp.server.fastmcp import FastMCP
|
|
|
|
from .core.docker import docker_status, docker_logs, docker_restart
|
|
from .core.health import service_health, VALID_SERVICES
|
|
from .core.system import disk_usage
|
|
from .core.network import tailscale_status
|
|
from .core.models import ollama_models, mlx_models
|
|
from .core.docserver import scheduler_status as _scheduler_status, queue_status as _queue_status
|
|
from .core.verify import run_verify as _run_verify, VERIFY_COMMANDS
|
|
|
|
mcp = FastMCP(
|
|
"infra",
|
|
instructions=(
|
|
"인프라 모니터링 도구. GPU 서버, Mac mini, 회사 NAS의 "
|
|
"Docker 상태, 서비스 헬스체크, 디스크 사용량, 네트워크, 모델 목록을 확인합니다."
|
|
),
|
|
)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_docker_status(host: str) -> str:
|
|
"""Docker 컨테이너 상태 확인.
|
|
|
|
Args:
|
|
host: 대상 호스트 (gpu | nas-company)
|
|
"""
|
|
result = await docker_status(host)
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_docker_logs(host: str, container: str, lines: int = 50) -> str:
|
|
"""Docker 컨테이너 최근 로그 조회.
|
|
|
|
Args:
|
|
host: 대상 호스트 (gpu | nas-company)
|
|
container: 컨테이너 이름
|
|
lines: 조회할 줄 수 (기본 50)
|
|
"""
|
|
result = await docker_logs(host, container, lines)
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def restart_docker_container(host: str, container: str) -> str:
|
|
"""Docker 컨테이너 재시작. 보호된 컨테이너(home-caddy, home-fail2ban, nanoclaude)는 거부.
|
|
|
|
Args:
|
|
host: 대상 호스트 (gpu | nas-company)
|
|
container: 재시작할 컨테이너 이름
|
|
"""
|
|
result = await docker_restart(host, container)
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_service_health(service: str) -> str:
|
|
"""서비스 헬스체크. 서비스별 정상 판정 기준이 다름.
|
|
|
|
Args:
|
|
service: 서비스 이름 (document-server | mlx | mlx-proxy | nanoclaude | ollama-gpu | ollama-macmini)
|
|
"""
|
|
result = await service_health(service)
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_disk_usage(host: str) -> str:
|
|
"""디스크 사용량 확인. 85% 초과 시 경고.
|
|
|
|
Args:
|
|
host: 대상 호스트 (gpu | macmini | nas-company)
|
|
"""
|
|
result = await disk_usage(host)
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_tailscale() -> str:
|
|
"""Tailscale 네트워크 상태 확인. 모든 피어 연결 상태를 반환."""
|
|
result = await tailscale_status()
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_ollama_models(host: str) -> str:
|
|
"""Ollama 설치 모델 목록 조회.
|
|
|
|
Args:
|
|
host: 대상 호스트 (gpu | macmini)
|
|
"""
|
|
result = await ollama_models(host)
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_mlx_models() -> str:
|
|
"""Mac mini MLX 서버에 로드된 모델 목록 조회."""
|
|
result = await mlx_models()
|
|
return result.model_dump_json(indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_scheduler_status() -> str:
|
|
"""Document Server APScheduler 잡 상태. 최근 스케줄러 로그에서 추출."""
|
|
result = await _scheduler_status()
|
|
return json.dumps(result, ensure_ascii=False, indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_queue_status() -> str:
|
|
"""Document Server 문서 처리 큐 현황. 최근 큐 로그에서 추출."""
|
|
result = await _queue_status()
|
|
return json.dumps(result, ensure_ascii=False, indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
async def check_verify(check_name: str) -> str:
|
|
"""인프라 검증 명령 실행 (infra_inventory.md 기반).
|
|
|
|
Args:
|
|
check_name: 체크 이름 (gpu-snapshot | macmini-snapshot | docserver-health | config-model-match)
|
|
"""
|
|
result = await _run_verify(check_name)
|
|
return json.dumps(result, ensure_ascii=False, indent=2)
|
|
|
|
|
|
def main():
|
|
mcp.run(transport="stdio")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|