Vendor-neutral UAV capability layer + MCP server: connect AI agents to MAVLink drones (PX4/ArduPilot) via MAVSDK
Strong domain-specific tool design with excellent descriptions (avg 180 chars, well above baseline 34 chars minimum). All 12 tools have clear verb-noun naming and comprehensive parameter documentation. Schemas are well-structured with proper types and constraints. However, output schemas are documented only in descriptions, not as formal JSON Schema objects. Error handling is present but lacks recovery guidance. Security considerations (confirm flags, guardrails) are well-integrated. Missing tool annotations (readOnlyHint, destructiveHint, idempotentHint) and formal output schema declarations.
Arm the vehicle (spin up motors). DANGEROUS on real hardware. Rejected in readonly mode. Additionally requires confirm=true: ask the human operator for approval first — never set confirm=true on your own initiative. Preflight: check get_telemetry (GPS fix, battery) before arming. Returns {"armed": true} or {"error": ...}.
Disarm the vehicle (stop motors). Autopilots reject this in flight. Rejected in readonly mode. Returns {"disarmed": true} or {"error": ...}.
Upload a waypoint mission and start flying it. The vehicle must be armed and flying (or the autopilot will take off per its mission config). Args: waypoints: List of {"latitude_deg", "longitude_deg", "relative_altitude_m", "speed_m_s"?} objects, flown in order. Every waypoint is validated against the altitude ceiling and the soft geofence before anything is uploaded. Rejected in readonly mode. Returns {"mission_started": true, "waypoint_count": N}. Use hold to pause or return_to_launch to abort; poll get_flight_mode — mode leaves MISSION when the mission completes.
Discover what this vehicle can do and which guardrails are active. Takes no arguments; does not require a live vehicle. Call this first. Returns {"adapter", "capabilities": ["telemetry", "arm_disarm", ...], "guardrails": {"readonly", "max_altitude_m", "geofence_radius_m"}, "connected"}. Plan only with listed capabilities; if "readonly" is true, every tool that can move the vehicle will be rejected.
Output schemas documented only in descriptions, not as formal JSON Schema. LLMs cannot parse unstructured text to extract field types and structure for downstream tool chaining.
Missing tool annotations (readOnlyHint, destructiveHint, idempotentHint). Tools like 'arm', 'takeoff', 'goto_location' are destructive/irreversible but lack explicit annotations to guide agent planning.
Error responses lack recovery guidance. When a tool fails (e.g., 'readonly mode'), the response includes error text but no actionable next steps for the agent.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 66 | 2026-07-28+ | v2 |
Report the MAVLink endpoint, connection state, and guardrail config. Takes no arguments. The connection is opened lazily, so "connected" is false until another tool has been used. Check this first when other tools report errors.
Report the current flight mode (e.g. HOLD, TAKEOFF, MISSION, RTL). Takes no arguments. Read-only. Returns {"flight_mode": "HOLD"}.
Read one snapshot of the vehicle's telemetry. Read-only. Takes no arguments. Returns {"position": {latitude_deg, longitude_deg, absolute_altitude_m, relative_altitude_m}, "attitude": {roll_deg, pitch_deg, yaw_deg}, "battery": {voltage_v, remaining_percent}, "gps": {num_satellites, fix_type}, "flight_mode", "armed", "in_air"}. Fields the vehicle did not report within a few seconds are null. Always check telemetry before commanding motion.
Fly to a single waypoint and loiter there. The vehicle must be flying already (armed + taken off). Args: latitude_deg: Target latitude (WGS84). longitude_deg: Target longitude (WGS84). relative_altitude_m: Target altitude above the launch point, in meters. Checked against the altitude ceiling. yaw_deg: Heading at the target, 0 = north (default 0). Rejected in readonly mode. The target is also checked against the soft geofence around home (MAVLINK_MCP_GEOFENCE_RADIUS_M, default 200 m). Returns {"goto_started": true, "target": {...}} — travel is asynchronous; poll get_telemetry to track progress.
Pause: stop and loiter at the current position (HOLD mode). Rejected in readonly mode. Use this to interrupt a goto or mission safely. Returns {"holding": true}.
Land at the current position. Rejected in readonly mode (no confirmation needed — landing makes the vehicle safer). Returns {"landing_started": true}; poll get_telemetry until "in_air" is false.
Fly back to the launch point and land there (RTL). Rejected in readonly mode (no confirmation needed — RTL is a recovery action). Returns {"rtl_started": true}; poll get_telemetry until "in_air" is false.
Take off vertically to `altitude_m` meters above the launch point. Rejected in readonly mode. Additionally requires confirm=true after human operator approval — never set it on your own initiative. The vehicle must be armed first (call arm). Altitude is validated against the configured ceiling (MAVLINK_MCP_MAX_ALTITUDE_M, default 50 m). Returns {"takeoff_started": true, "target_altitude_m": ...} — takeoff is asynchronous; poll get_telemetry to watch the climb.
Numeric parameters (altitude_m, latitude_deg, longitude_deg, speed_m_s) lack explicit min/max constraints in schema. Descriptions mention validation but schema does not enforce bounds.
Confirmation pattern (confirm=true) is manual and requires agent to explicitly ask user. No formal MRTR (Multi Round-Trip Request) pattern for structured user input elicitation.