Skip to main content

MCP and AI agents

MAGPIE makes an operational service directly consumable by AI tools. McpSchema exposes registered methods through the Model Context Protocol (MCP), while a client adapter carries MCP messages through any MAGPIE RPC requester.

The result is one service contract for ordinary applications and agents:

Why this matters for edge systems​

A robot behind NAT can make an outbound MQTT connection. An agent in the cloud connects to the same broker, discovers the robot’s tools, and calls them without opening an inbound port or running a separate MCP gateway on the robot.

MCP does not replace authentication or authorization. It standardizes discovery and calls; transport security and tool policy remain application responsibilities.

Define tools on the service​

from luxai.magpie.schema import McpSchema

schema = McpSchema(name="robot-service", version="1.0.0")

@schema.method()
def get_battery() -> dict:
"""Return the robot battery level and charging state."""
return {"level": 0.82, "charging": False}

@schema.method()
def look_at(yaw: float, pitch: float) -> dict:
"""Move the robot head to the requested angles."""
return {"accepted": True, "yaw": yaw, "pitch": pitch}

Good descriptions and schemas matter: they are the information an agent uses to decide whether and how to call a tool.

Attach the schema to a responder​

The schema is transport-independent. Attach the same schema to the responder appropriate for the deployment:

# Direct connection
server = ZMQRpcResponder("tcp://*:5556", schema=schema)

# Brokered connection; convenient behind NAT
connection = MqttConnection("mqtts://broker.example.com:8883")
connection.connect()
server = MqttRpcResponder(connection, service_name="robot-01", schema=schema)

# Peer-to-peer connection
peer = WebRTCConnection.with_mqtt(
"mqtts://broker.example.com:8883", session_id="robot-01"
)
peer.connect()
server = WebRTCRpcResponder(peer, service_name="robot-01", schema=schema)

Connect an agent client​

Install the client integration:

pip install "luxai-magpie[mqtt,mcp]"
import asyncio
from fastmcp import Client
from luxai.magpie.adapters.mcp import McpTransport
from luxai.magpie.transport import MqttConnection, MqttRpcRequester

async def main():
connection = MqttConnection("mqtts://broker.example.com:8883")
connection.connect()
requester = MqttRpcRequester(connection, service_name="robot-01")

try:
async with Client(McpTransport(requester)) as client:
tools = await client.list_tools()
print([tool.name for tool in tools])

result = await client.call_tool("get_battery", {})
print(result.content[0].text)
finally:
requester.close()
connection.disconnect()

asyncio.run(main())

The MCP transport borrows the requester. Close the MCP client first, then the requester, then the shared connection.

Tool design for physical systems​

  • Expose narrow operations with explicit units, ranges, and consequences.
  • Validate every argument server-side even when an MCP client validates first.
  • Separate read-only observation tools from actions that change physical state.
  • Require confirmation or policy checks for dangerous or irreversible actions.
  • Apply authorization per tool and per robot—not only per broker connection.
  • Return structured, actionable errors rather than ambiguous text.
  • Log tool identity, caller identity, request ID, outcome, and duration.
An agent is not a safety controller

Keep collision avoidance, limits, emergency stops, and other safety mechanisms below the agent/tool layer. A successful MCP call must still pass the robot’s normal safety checks.