Summary
@player-devtools/mcp connects to Player through a shared flipper-server
daemon (FlipperServerTransport in devtools/client/flipper/src/transport.ts),
but none of that connection's state is exposed to the agent as a tool today.
All existing tools (list_players, get_player_status, etc.) operate one
level up, on Player instances that are already reachable through a working
transport.
When the daemon isn't running, isn't owned by this process, or a required
Flipper plugin isn't installed, an agent currently just sees every
player-scoped tool call fail generically — it has no way to distinguish "no
Player instance is connected" from "the Flipper transport itself is down" or
self-diagnose/self-heal.
Proposal
Add a new devtools/mcp/src/tools/flipper.ts with a small set of
daemon-scoped diagnostic tools, following the existing ToolDef pattern
(devtools/mcp/src/tools/index.ts). Tool names stay flat and un-prefixed,
consistent with existing naming (list_players, get_player_status) — they
register into the same TOOL_DEFS list as the core Player tools. To keep
them visually distinct from the core Player tools (the actual value the MCP
server provides) without touching the wire protocol, the README documents
them under a separate "Diagnostics" table beneath the existing "Player"
tools table, rather than under one combined list.
get_flipper_status — is the transport currently connected to a
flipper-server daemon; if so, host/port, and whether this MCP process
started (owns) the daemon vs. attached to one another process started
(surfacing the existing refcount/ownership state).
get_flipper_consumers — when this process owns the daemon, how many
clients/consumers are currently attached (e.g. current refcount, and/or the
connected device/app client IDs already tracked internally).
restart_flipper_server — restart the daemon, scoped to the existing
refcount/ownership model:
- If the calling process owns the daemon (per
FlipperRefcount), it may
restart it directly — tear down and respawn the child process, since it's
already responsible for that daemon's lifecycle.
- If the calling process does not own the daemon (it only attached to
one another process started), the tool returns a soft error explaining
that only the owning process can restart it, rather than silently killing
a daemon other consumers depend on. This mirrors the existing
acquire/release discipline in FlipperRefcount — no new coordination
primitive is needed, just surfacing the ownership check that already
exists before performing a destructive action.
get_flipper_plugin_install_status — report whether the
player-ui-devtools Flipper plugin specifically is installed/enabled on the
daemon. (Plugin installation itself is already handled elsewhere — this is
status-only, not a general-purpose plugin manager.)
Tool annotations
ToolDef (devtools/mcp/src/tools/index.ts) doesn't carry MCP's
annotations field today, and registerTools() in server.ts only passes
description/inputSchema to registerTool() — annotations is silently
dropped even though the SDK supports it. This issue proposes:
- Adding an optional
annotations field to ToolDef, forwarded through in
registerTools().
- Setting it on the new tools:
{ readOnlyHint: true, destructiveHint: false }
on the three get_* tools, and { readOnlyHint: false, destructiveHint: true }
on restart_flipper_server.
Per the MCP spec, annotations are hints the client may use, and clients
"MUST consider tool annotations untrusted unless they come from trusted
servers" — they're not guaranteed to reach the calling model, and aren't a
substitute for the ownership check above, which remains the actual safety
mechanism preventing a non-owning process from tearing down a daemon other
consumers depend on. Adding annotations here is about standards-compliant
correctness and giving well-behaved MCP clients an honest signal, not a claim
that it alone makes restart "safe."
Retrofitting annotations onto existing tools (e.g. invoke_action) is a
reasonable follow-up but out of scope for this issue.
Motivation
Better observability and self-service recovery for agents driving Player
devtools over MCP — surfacing existing internal state
(FlipperRefcount, activeClientIds, waitForPort) that the transport
already tracks, rather than requiring a human to intervene when the shared
daemon needs attention.
Summary
@player-devtools/mcpconnects to Player through a sharedflipper-serverdaemon (
FlipperServerTransportindevtools/client/flipper/src/transport.ts),but none of that connection's state is exposed to the agent as a tool today.
All existing tools (
list_players,get_player_status, etc.) operate onelevel up, on Player instances that are already reachable through a working
transport.
When the daemon isn't running, isn't owned by this process, or a required
Flipper plugin isn't installed, an agent currently just sees every
player-scoped tool call fail generically — it has no way to distinguish "no
Player instance is connected" from "the Flipper transport itself is down" or
self-diagnose/self-heal.
Proposal
Add a new
devtools/mcp/src/tools/flipper.tswith a small set ofdaemon-scoped diagnostic tools, following the existing
ToolDefpattern(
devtools/mcp/src/tools/index.ts). Tool names stay flat and un-prefixed,consistent with existing naming (
list_players,get_player_status) — theyregister into the same
TOOL_DEFSlist as the core Player tools. To keepthem visually distinct from the core Player tools (the actual value the MCP
server provides) without touching the wire protocol, the README documents
them under a separate "Diagnostics" table beneath the existing "Player"
tools table, rather than under one combined list.
get_flipper_status— is the transport currently connected to aflipper-serverdaemon; if so, host/port, and whether this MCP processstarted (owns) the daemon vs. attached to one another process started
(surfacing the existing refcount/ownership state).
get_flipper_consumers— when this process owns the daemon, how manyclients/consumers are currently attached (e.g. current refcount, and/or the
connected device/app client IDs already tracked internally).
restart_flipper_server— restart the daemon, scoped to the existingrefcount/ownership model:
FlipperRefcount), it mayrestart it directly — tear down and respawn the child process, since it's
already responsible for that daemon's lifecycle.
one another process started), the tool returns a soft error explaining
that only the owning process can restart it, rather than silently killing
a daemon other consumers depend on. This mirrors the existing
acquire/release discipline in
FlipperRefcount— no new coordinationprimitive is needed, just surfacing the ownership check that already
exists before performing a destructive action.
get_flipper_plugin_install_status— report whether theplayer-ui-devtoolsFlipper plugin specifically is installed/enabled on thedaemon. (Plugin installation itself is already handled elsewhere — this is
status-only, not a general-purpose plugin manager.)
Tool annotations
ToolDef(devtools/mcp/src/tools/index.ts) doesn't carry MCP'sannotationsfield today, andregisterTools()inserver.tsonly passesdescription/inputSchematoregisterTool()—annotationsis silentlydropped even though the SDK supports it. This issue proposes:
annotationsfield toToolDef, forwarded through inregisterTools().{ readOnlyHint: true, destructiveHint: false }on the three
get_*tools, and{ readOnlyHint: false, destructiveHint: true }on
restart_flipper_server.Per the MCP spec, annotations are hints the client may use, and clients
"MUST consider tool annotations untrusted unless they come from trusted
servers" — they're not guaranteed to reach the calling model, and aren't a
substitute for the ownership check above, which remains the actual safety
mechanism preventing a non-owning process from tearing down a daemon other
consumers depend on. Adding annotations here is about standards-compliant
correctness and giving well-behaved MCP clients an honest signal, not a claim
that it alone makes restart "safe."
Retrofitting annotations onto existing tools (e.g.
invoke_action) is areasonable follow-up but out of scope for this issue.
Motivation
Better observability and self-service recovery for agents driving Player
devtools over MCP — surfacing existing internal state
(
FlipperRefcount,activeClientIds,waitForPort) that the transportalready tracks, rather than requiring a human to intervene when the shared
daemon needs attention.