A Ryze RoboMaster TT flown by AI agents — telemetry, flight, and camera as plain English, routed through agentgateway as MCP. Then the real question: fly the same mission on gpt-5.5, Claude, and grok — which model saves money?
The bird is a Ryze RoboMaster TT (Tello) in station mode — it joins the lab Wi-Fi at a reserved address and speaks the Tello SDK over three UDP channels. Everything the agents do bottoms out in those datagrams.
Flight & query commands in, ACKs back.
takeoff · move · battery?Telemetry broadcast ~10 Hz while in SDK mode.
bat · height · attitudeH.264 stream for photos & recording.
take_photo · recordA request crosses four boundaries — model, gateway, MCP server, radio — and the telemetry comes back the same way. The gateway is the one control point: it prices, traces, and shapes every tool call.
Plain English in the kagent UI.
"do a flip"Reasons, picks a tool.
kagent · declarativeTool-mode proxy, tracing, budgets.
/drone · :31606Owns the UDP link · hostNetwork pod · 5s keepalive.
drone-system nsExecutes, streams telemetry back up the chain.
Tello SDKA real trip through the stack — "what's the battery?" — plus the heartbeat that runs the whole time in the background so the answer is never stale.
You type it (or tap an example). The agent, on gpt-5.5, decides get_battery is the right tool.
The call hits agentgateway at /drone, which routes it to the drone MCP server — and traces it to Langfuse.
The server sends battery? as a UDP datagram to the drone on port 8889 and waits for the reply.
The drone replies 76; it flows back up the same chain and the agent says it in plain English.
Every 5s the server re-sends command, keeping the drone in SDK mode so telemetry never stops.
get_state reports connected / stale / state_age_s — a dead link reads false, never a frozen value.
agentgateway can present the same 28 tools four different ways — from the full list to a single code sandbox — trading model context for directness. Each mode is its own agent and endpoint, all flying the same aircraft.
All 28 tools, exposed directly. The simplest path — the model sees everything.
:31606/droneget_state · takeoff · orbit · panorama · celebrate …Two meta-tools. The model looks a tool up, then invokes it — keeps context small.
:31606/drone-searchget_tool · invoke_toolOne tool: the model writes JavaScript that calls the drone in a 15s sandbox.
:31606/drone-coderun_codeDiscover typed signatures, then write code against them. Smallest footprint of all.
:31606/drone-codesearchget_tool · run_codeEvery MCP turn re-injects the 28-tool catalog into the prompt — pure token overhead. agentgateway's CodeSearch tool mode replaces it with two meta-tools (get_tool + run_code), so the model discovers only what it needs and batches calls in code. To prove it, the same flight ran on all four modes (Standard, Search, Code, CodeSearch) on gpt-5.5 — measured in tokens, straight from the kagent trace store. Money lives in §06; this is about the token tax.
| # | Prompt | Tools exercised |
|---|---|---|
| 1 | “Take off and hover.” | takeoff · get_state |
| 2 | “Do a flip.” | flip |
| 3 | “Take a photo and describe what you see.” | take_photo (vision) |
| 4 | “Spin around 360 degrees.” | rotate |
| 5 | “Land and give me a status report.” | land · status_report |
| Agent · mode | Model calls | Total tokens | Turn latency | Tokens vs Standard |
|---|---|---|---|---|
| drone-agentStandard | 35 | 98.3k | 47s | baseline |
| drone-agent-searchSearch | 43 | 123.0k | 68s | +25% |
| drone-agent-codeCode | 22 | 80.5k | 46s | -18% |
| drone-agent-codesearchCodeSearch | 21 | 53.1k | 37s | -46% |
CodeSearch — the agentgateway tool mode that swaps the 28-tool catalog for two meta-tools (get_tool + run_code) — is the focal point: same standard 5-prompt flight on 46% fewer tokens than Standard, the leanest of all four modes. Token counts pulled per call from the kagent Langfuse project. Per-prompt detail below.
Two forces set each mode's token count. Context tax — Standard puts all 28 tool schemas in front of the model on every call, so each prompt it sends is large. Search and CodeSearch keep context tiny by discovering tools on demand, but that buys extra round-trips (a get_tool/invoke_tool hop before the real call) — which is why Search racks up the most model calls. Code collapses a whole task into one run_code call, cutting round-trips but paying for the model to write JavaScript. CodeSearch gets both wins — typed signatures on demand plus a single code call — and lands leanest. One pattern holds across all four modes: the Land + status report prompt dominates, because the agent polls get_state repeatedly to compose the report (see the per-call detail — it's the biggest row in every mode).
| Agent | # | Prompt | Calls | In | Out |
|---|---|---|---|---|---|
| Standard | 1 | Take off and hover. | 3 | 7.4k | 243 |
| Standard | 2 | Do a flip. | 4 | 10.8k | 150 |
| Standard | 3 | Take a photo and describe what you see. | 6 | 17.7k | 182 |
| Standard | 4 | Spin around 360 degrees. | 5 | 15.9k | 116 |
| Standard | 5 | Land and give me a status report. | 17 | 45.1k | 667 |
| Search | 1 | Take off and hover. | 9 | 17.5k | 341 |
| Search | 2 | Do a flip. | 7 | 16.5k | 260 |
| Search | 3 | Take a photo and describe what you see. | 7 | 19.1k | 262 |
| Search | 4 | Spin around 360 degrees. | 7 | 22.0k | 170 |
| Search | 5 | Land and give me a status report. | 13 | 45.8k | 1.1k |
| Code | 1 | Take off and hover. | 3 | 9.5k | 690 |
| Code | 2 | Do a flip. | 3 | 10.6k | 1.4k |
| Code | 3 | Take a photo and describe what you see. | 4 | 15.8k | 498 |
| Code | 4 | Spin around 360 degrees. | 3 | 12.7k | 1.1k |
| Code | 5 | Land and give me a status report. | 9 | 27.0k | 1.2k |
| CodeSearch | 1 | Take off and hover. | 4 | 8.6k | 205 |
| CodeSearch | 2 | Do a flip. | 4 | 9.2k | 378 |
| CodeSearch | 3 | Take a photo and describe what you see. | 3 | 7.2k | 336 |
| CodeSearch | 4 | Spin around 360 degrees. | 5 | 12.7k | 397 |
| CodeSearch | 5 | Land and give me a status report. | 5 | 13.3k | 864 |
The whole point. The identical flight, flown on three models — gpt-5.5, Claude (claude-fable-5), and xAI (grok-4.3) — each swapped in with a one-line agentgateway ModelConfig, every token metered in Langfuse. Verdict up front: grok-4.3 is cheapest and fastest, gpt-5.5 is the token-leanest and CodeSearch cuts it further, and claude-fable-5 is the most thorough but runs ~5× the cost. Same agent, same drone, same gateway — the model is the biggest lever on the bill.
How to read it. Cost = tokens × each model's price — gpt-5.5 and Claude priced by Langfuse, grok-4.3 from models.dev (grok-4 rate, $3/M in · $15/M out). The CodeSearch saves column compares each model's CodeSearch run to its own Standard run, so it isolates the tool mode from the model choice.
| Model | Mode | Calls | Tokens | Latency | Cost | CodeSearch saves |
|---|---|---|---|---|---|---|
| gpt-5.5 | Standard | 35 | 98.3k | 47s | $0.5252 | baseline |
| gpt-5.5CodeSearch helps most | CodeSearch | 21 | 53.1k | 37s | $0.3201 | −39% |
| claude-fable-5dearest | Standard | 26 | 159.5k | 79s | $1.6676 | baseline |
| claude-fable-5 | CodeSearch | 33 | 165.6k | 101s | $1.5167 | −9% |
| grok-4.3fastest | Standard | 34 | 113.0k | 25s | $0.3441 | baseline |
| grok-4.3cheapest | CodeSearch | 33 | 99.5k | 31s | $0.3050 | −11% |
Takeaways — two levers, one lesson. Tool mode: CodeSearch is a big win for gpt-5.5 (−46% tokens, −39% cost) but a wash for claude-fable-5 — it doesn't shrink Claude's verbose runs. Model: grok-4.3 is cheapest and fastest (CodeSearch $0.3050, ~25–31s); gpt-5.5 is the token-leanest; Claude is the most thorough but roughly 5× the cost of grok. The winning tool mode is model-dependent — benchmark the mode with the model you'll actually ship.
These numbers aren't estimated — they're read straight from kagent's built-in tracing (Solo Enterprise for kagent), the same spans that flow to Langfuse. Every prompt is a trace with its input, output, duration, and token count; drill in and you see the agent's execution flow and each tool call.
| Take off and hover | 1.11s | 2,815 |
| Do a flip | 0.74s | 2,925 |
| Take a photo | 1.51s | 3,104 |
| Spin 360° | 1.03s | 3,229 |
| Land + status report | 1.46s | 3,614 |
Every model flown on every tool mode, 1 pass(es) each (mean shown; range in small text). Tokens per flight — lower is leaner.
| Model | Standard | Search | Code | CodeSearch |
|---|---|---|---|---|
| gpt-5.5 | 91.1k | 37.0k | — | 140.3k |
| claude-fable-5 | 192.0k | 116.0k | 122.3k | 172.3k |
| grok-4.3 | 103.8k | 60.7k | 87.9k | 106.8k |
| Qwen (local) | 60.7k | 77.4k | 96.8k | 130.3k |
Every capability the drone MCP server exposes — 16 primitives that map straight to Tello SDK commands, plus 12 composite maneuvers that orchestrate them into higher-level moves. The agent calls these by name (Standard mode) or discovers them at runtime (Search / Code). Two canned prompts round it out — one-tap procedures. Flight maneuvers assume the drone is already airborne.
An agent isn't just a model with tools — it carries a skill (an operating runbook) and a brief that shape how it flies. Both live in Git, so every agent behaves the same across restarts.
A runbook the agent pulls from this repo (skills/drone) at runtime — not baked into the model. It carries the facts a pilot needs: the drone's IP and UDP ports, that takeoff is refused under 15% and <30% means "land soon", how to read stale telemetry, and to describe a photo rather than dump JSON. Edit the runbook, and every agent's behavior updates on the next pull.
Each of the four agents is the same recipe with one dial changed — the tool mode. The model, skill, and safety rules stay constant; only how the tools are presented differs. That's what makes them a clean side-by-side comparison.
The drone drops SDK mode after ~15s of silence and its Wi-Fi is unreliable. Four systems keep the picture honest and the aircraft safe.
A background thread re-sends command every 5 seconds, so telemetry streams non-stop — and a drone you just charged reconnects on its own within ~5s. No pod restart.
get_state stamps every packet. connected means "heard from it in the last 5s" — otherwise stale:true, so the agent never quotes a frozen number.
get_battery actively asks the drone (battery?) instead of trusting the cache — the reading you get is the reading right now.
Idempotent commands (battery, land, emergency, keepalive) retry through Wi-Fi blips. Movement never does — a timed-out move may have already run, and a retry would double it.
Everything is GitOps — edit, push, ArgoCD syncs. Here's the shape: the server on the drone LAN, a gateway endpoint per mode, and an agent that speaks to it.
# owns the drone's UDP link directly env: - name: TELLO_IP value: "172.16.10.168" # DHCP-reserved hostNetwork: true nodeSelector: kubernetes.io/hostname: talos-9kw-b68
spec: entMcp: toolMode: Code # Standard|Search|Code|CodeSearch codeMode: { timeout: "15s" } targets: - static: host: drone-mcp-server.drone-system… port: 8090 path: /mcp/
curl -s http://172.16.10.155:31606/drone \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize", "params":{"protocolVersion":"2025-06-18", "capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}'
| Endpoint | URL | Exposes |
|---|---|---|
| Standard | 172.16.10.155:31606/drone | all 28 tools |
| Search | …/drone-search | get_tool · invoke_tool |
| Code | …/drone-code | run_code |
| CodeSearch | …/drone-codesearch | get_tool · run_code |