New MCP tools: - collect_coverage(name) - combine parallel files, return summary - generate_coverage_report(name, format) - HTML/XML/JSON reports - combine_coverage(names) - aggregate across test runs - delete_coverage(name?, older_than_days?) - cleanup Modified: - launch_flowgraph() now accepts enable_coverage parameter - stop() uses 30s timeout for graceful shutdown (coverage needs atexit) Docker: - Dockerfile.gnuradio-coverage extends runtime with python3-coverage - entrypoint-coverage.sh wraps execution with coverage run - .coveragerc configured for GNU Radio source paths Tests: 125 unit tests (21 new), 80% coverage
77 lines
2.5 KiB
Python
77 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastmcp import FastMCP
|
|
|
|
from gnuradio_mcp.middlewares.docker import DockerMiddleware
|
|
from gnuradio_mcp.providers.runtime import RuntimeProvider
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class McpRuntimeProvider:
|
|
"""Registers runtime control tools with FastMCP.
|
|
|
|
Docker is optional: if unavailable, container lifecycle and visual
|
|
feedback tools are skipped, but XML-RPC connection/control tools
|
|
are still registered (for connecting to externally-managed flowgraphs).
|
|
"""
|
|
|
|
def __init__(self, mcp_instance: FastMCP, runtime_provider: RuntimeProvider):
|
|
self._mcp = mcp_instance
|
|
self._provider = runtime_provider
|
|
self.__init_tools()
|
|
|
|
def __init_tools(self):
|
|
p = self._provider
|
|
|
|
# Connection management (always available)
|
|
self._mcp.tool(p.connect)
|
|
self._mcp.tool(p.disconnect)
|
|
self._mcp.tool(p.get_status)
|
|
|
|
# Variable control (always available)
|
|
self._mcp.tool(p.list_variables)
|
|
self._mcp.tool(p.get_variable)
|
|
self._mcp.tool(p.set_variable)
|
|
|
|
# Flowgraph execution (always available)
|
|
self._mcp.tool(p.start)
|
|
self._mcp.tool(p.stop)
|
|
self._mcp.tool(p.lock)
|
|
self._mcp.tool(p.unlock)
|
|
|
|
# Docker-dependent tools
|
|
if p._has_docker:
|
|
# Container lifecycle
|
|
self._mcp.tool(p.launch_flowgraph)
|
|
self._mcp.tool(p.list_containers)
|
|
self._mcp.tool(p.stop_flowgraph)
|
|
self._mcp.tool(p.remove_flowgraph)
|
|
self._mcp.tool(p.connect_to_container)
|
|
|
|
# Visual feedback
|
|
self._mcp.tool(p.capture_screenshot)
|
|
self._mcp.tool(p.get_container_logs)
|
|
|
|
# Coverage collection
|
|
self._mcp.tool(p.collect_coverage)
|
|
self._mcp.tool(p.generate_coverage_report)
|
|
self._mcp.tool(p.combine_coverage)
|
|
self._mcp.tool(p.delete_coverage)
|
|
|
|
logger.info("Registered 21 runtime tools (Docker available)")
|
|
else:
|
|
logger.info(
|
|
"Registered 10 runtime tools (Docker unavailable, "
|
|
"container tools skipped)"
|
|
)
|
|
|
|
@classmethod
|
|
def create(cls, mcp_instance: FastMCP) -> McpRuntimeProvider:
|
|
"""Factory: create RuntimeProvider with optional Docker support."""
|
|
docker_mw = DockerMiddleware.create()
|
|
provider = RuntimeProvider(docker_mw=docker_mw)
|
|
return cls(mcp_instance, provider)
|