Add birdcage-mcp FastMCP server for satellite dish control
34 tools (connection, movement, signal, system, satellite, console), 5 resources, 3 prompts. Backed by DemoDevice for offline testing. 46 tests passing against the demo backend via run_server_async.
This commit is contained in:
parent
16ca4892b3
commit
8a6b99bd8c
21 changed files with 3233 additions and 0 deletions
74
mcp/tests/conftest.py
Normal file
74
mcp/tests/conftest.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
"""Shared fixtures for birdcage-mcp tests.
|
||||
|
||||
Uses FastMCP's run_server_async to spin up a real MCP server backed by
|
||||
DemoDevice + DemoCraftClient. No serial hardware, no subprocesses.
|
||||
"""
|
||||
|
||||
import json
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
import pytest
|
||||
from birdcage.demo import DemoCraftClient, DemoDevice
|
||||
from fastmcp import Client, FastMCP
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
from fastmcp.utilities.tests import run_server_async
|
||||
|
||||
from birdcage_mcp.state import BirdcageState
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def _test_lifespan(server: FastMCP):
|
||||
"""Test lifespan that pre-connects a DemoDevice."""
|
||||
device = DemoDevice()
|
||||
device.connect()
|
||||
device.initialize()
|
||||
|
||||
state = BirdcageState(
|
||||
device=device,
|
||||
craft_client=DemoCraftClient(),
|
||||
demo_mode=True,
|
||||
serial_port="/dev/demo",
|
||||
firmware_name="g2",
|
||||
connected=True,
|
||||
)
|
||||
yield state
|
||||
|
||||
|
||||
def _build_server() -> FastMCP:
|
||||
"""Build a FastMCP server with all tools registered."""
|
||||
from birdcage_mcp import prompts, resources
|
||||
from birdcage_mcp.tools import (
|
||||
connection,
|
||||
console,
|
||||
movement,
|
||||
satellite,
|
||||
signal,
|
||||
system,
|
||||
)
|
||||
|
||||
mcp = FastMCP("birdcage-test", lifespan=_test_lifespan)
|
||||
connection.register(mcp)
|
||||
movement.register(mcp)
|
||||
signal.register(mcp)
|
||||
system.register(mcp)
|
||||
satellite.register(mcp)
|
||||
console.register(mcp)
|
||||
resources.register(mcp)
|
||||
prompts.register(mcp)
|
||||
return mcp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def mcp_client():
|
||||
"""Yield a connected MCP client backed by DemoDevice."""
|
||||
server = _build_server()
|
||||
async with (
|
||||
run_server_async(server) as url,
|
||||
Client(StreamableHttpTransport(url)) as client,
|
||||
):
|
||||
yield client
|
||||
|
||||
|
||||
def parse_result(result) -> dict:
|
||||
"""Extract the dict from a tool call result."""
|
||||
return json.loads(result.content[0].text)
|
||||
Loading…
Add table
Add a link
Reference in a new issue