Skip to content

[REF] livekit-agent: derive tool schemas from signatures - #194

Merged
beorngb merged 2 commits into
mainfrom
refactor/livekit-fastmcp
Aug 28, 2026
Merged

[REF] livekit-agent: derive tool schemas from signatures#194
beorngb merged 2 commits into
mainfrom
refactor/livekit-fastmcp

Conversation

@beorngb

@beorngb beorngb commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Pilot for #190, on the smallest of the three servers that still use the
decorator API mcp 2.0 dropped.

Why this is worth doing on its own

The four tools were declared as hand-written JSON schemas beside a
if name == ... dispatch table. The declared contract and the code reading
args.get(...) were two independent sources of truth, and nothing reconciled
them — rename an argument in one and everything still compiles, every test
still passes, and the agent gets an incomprehensible failure at runtime.

Deriving the schema from the signature makes that drift unrepresentable.

Why it does not need the 2.0 move

FastMCP ships in the pinned mcp 1.x and mirrors the MCPServer API that
replaces it in 2.0, so this lands today and shortens the eventual migration
instead of competing with it.

Verified against the live server, not by inspection

Same four names, same required arguments (start_voice_calluser_id,
end_voice_callroom_name, get_call_linkuser_id,
list_active_calls → none), and every per-argument description preserved.
structured_output=False keeps the wire response exactly what it was.

61 lines shorter.

Tests

Five, spoken over stdio through the gateway the way production does — they were
run against the real server, not mocked. One of them asserts every tool is named
in the provider allowlist, since a tool renamed on the server side otherwise
drops out of an agent's reach silently. They skip where the livekit extra is
absent; CI installs it.

The server declared four tools as hand-written JSON next to a name-dispatch
table, so the contract and the code reading the arguments were two independent
sources of truth with nothing reconciling them. Renaming an argument in one
place left everything compiling and every test green.

FastMCP builds the schema from the function signature, which makes that drift
unrepresentable. It ships in the pinned mcp 1.x, so this needs no dependency
move, and it is the same shape as the MCPServer API that replaces it in 2.0 —
groundwork for #190 rather than a detour.

Surface verified unchanged against the live server: same four names, same
required arguments, every per-argument description preserved. structured_output
is off so the wire response stays what it was. 61 lines shorter.
The ergonomic server API is FastMCP on mcp 1.x and MCPServer on 2.x. They are
identical for what this file uses — same @tool parameters, same
run_stdio_async, same emitted schema, verified by running one file under both.

Importing whichever is present means the major can be switched from pyproject
alone, so trying 2.0 costs one line and rolls back in one line, instead of
riding on a rewrite that would have to be undone.
@beorngb
beorngb merged commit 33a0291 into main Aug 28, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant