Add an embedded MCP Server in RetroArch#18757
Add an embedded MCP Server in RetroArch#18757drhelius wants to merge 3 commits intolibretro:masterfrom
Conversation
|
Added some basic tools to test |
|
My first instinct here is to ask whether this could be implemented as a frontend to command.c, like the existing command drivers for TCP and stdin (https://github.com/libretro/RetroArch/blob/master/command.c#L181). I understand the input/output format is a bit different but if it could use all the existing commands that would be very powerful for mcp, and if new commands need to be added for mcp that will be very useful for emscripten or for existing users of the command feature. So, is there a way for the new http code on the MPC protocol to simply marshal/unmarshal for the existing command api? Then even cool stuff like auto discovery of new commands/tools is feasible. Reading more closely I see that the command_event calls are used, and you have some code to basically document the commands in json. Is there eg a way we could add command documentation or expected inputs to command.h/command.c and you can generate tools from that, which is also something we could e.g. implement like "help CMD" in the socket interface for? |
|
MCP tool descriptions in JSON, including input parameters, will need to be handcrafted. These descriptions are used by the AI assistants to understand when to use each tool. And you will like to hand tune these to minimize token usage and maximize effectiveness (prompt engineering). So we need both a description and an adapter to transform the information from and back to JSON-RPC. I'm already using command.c for tools that perform actions. But for tools that gather info I'm just calling the original source. Getting the info from command.c adds an additional layer of parsing as these commands output a stream of text and it would need to be parsed to be converted to JSON again. |
|
If it needs hand tuning, do you think users will recompile RA to suit or do you want the json descriptions to be loaded files instead of C string literals? |
|
I visualize the MCP server as an internal adapter into RA state, for both gathering the state (getting info) and modifying the state (performing actions). These set of functions in the adapter are called "tools" in MCP terminology. The MCP spec expects the MCP server to publish the "tool" list when a client connects. I never thought about users deciding or altering the "tools" in RA MCP server. Thus I wrote the JSON descriptions as string literals in code. If you think tool descriptions should live in separate files that are loaded to conform the "tool" list we can discuss it further. |
|
IMO the tool list, and its descriptions must be designed and tied to a specific RA version (specific RA state). In the other hand, external MCP servers do exist too, and use existing APIs to query the system under the MCP server. If RA has an external API we can implement the MCP server as external app. But this way is more complex for users. Embedding the server into RA is easier for users and (pontentially) avoids a layer of adaptation. In anyway, I can't see the tool definition as an user defined asset. |
|
I understand; when you said "you will like to hand tune these to minimize token usage and maximize effectiveness" I thought you were saying that the person trying to use the MCP server would need to do this hand tuning, and if that required recompilation there would be an argument for making the descriptions a resource file or something. But that doesn't seem to be the case. I think an external MCP server making use of the command interface (over a TCP socket or whatever) could be very smooth to implement without any changes to retroarch, but I also understand that if the data are available in RA it's convenient to use them without a JSON-to-command-API-and-back layer and another running process. One thing about the command API which is nice is that all of RA "speaks" it. With respect to the way this PR is structured, I hope it's possible to avoid creating a parallel API with a similarly broad surface that has to be updated and maintained separately from the existing API. For example, if I were to add new features for (say) replay recording, adding them as commands lets me get keybindings and stdin API and integration with the runloop "for free". I think it would be fine if defining all commands were slightly heavier-weight with e.g. documentation strings or documented input/output types or whatever (these seem useful in general and some of these are already defined in the help-strings for the keybinding menu), if it meant that we could still define new commands or queries in one spot and have that be used by many internal consumers (including MCP). This gets at a related question---are MCP tools required to be in English? Do these need to be localized ever? |
Users of AI agents don't care about tool descriptions. Tool descriptions including input parameter descriptions is a job for RA developers.
It's a tradeoff we can consider. A new process/app must be executed together with RA and managed and configured by users. I don't like it but I understand it's pros. We could also use a different programming language supported by the official MCP SDK (C is not supported). But this will requiere an enhanced command.c API as I will explain below.
I think the best solution it's probably this one. If command.c API has additional command descriptions with input parameter descriptions, all these commands are listed somehow and you can query this list with descriptions, an adaptation layer to JSON-RPC could be automatically created without any duplicated code. But this is out-of-scope for this PR and something I could not probably commit to implement.
No, but it's a good practice and also convenient to write descriptions in English. |
|
Interesting idea. Another interface that's available to RetroArch is the Network Control Interface. We could likely introduce a few of the MCP tooling you have here to that. It would save having to run an MCP Server, since you could hit the commands directly through the NCI port itself. |
I'm confused. Is it not the same used by command.c?
If your are referring to reuse the NCI port instead of creating a new HTTP server I don't think that's possible. NCI uses a UDP datagram socket and MCP requires and HTTP server with HTTP semantics (headers, Content-Length, status codes, JSON-RPC payloads, ...) So in order to have an embedded MCP server, avoid duplicating code and maintaining 2 different APIs we need a new feature in command.c so you can get a list of all the commands together with command and input parameter descriptions tailored to AI LLMs. What do you think about this? |
|
This could be really great for accessibility, either for having the AI help
during gameplay or map out memory for accessibility mods. Just some big
ideas. :)
Devin Prater
***@***.***
…On Fri, Feb 27, 2026 at 9:45 AM Rob Loach ***@***.***> wrote:
*RobLoach* left a comment (libretro/RetroArch#18757)
<#18757 (comment)>
Interesting idea. Another interface that's available to RetroArch is the Network
Control Interface
<https://docs.libretro.com/development/retroarch/network-control-interface/#retroarch-network-control-interface>
.
We could likely introduce a few of the MCP tooling you have here to that.
It would save having to run an MCP Server, since you could hit the commands
directly through the NCI port itself.
—
Reply to this email directly, view it on GitHub
<#18757 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ADUMTTVRR6PNPXIKMPJSN4L4OBRDHAVCNFSM6AAAAACV36RSPKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTSNZTGY2TANBWGU>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
|
This is interesting, it opens many doors, like debugging. |
Description
This PR adds a new embedded MCP server inside RA allowing AI assistants to query and control the frontend using the JSON-RPC 2.0 based MCP protocol.
MCP servers can be implemented using different transports, with
stdioandhttpbeing the most common ones.I created a generic transport interface and implemented
httpfor now. Thehttptransport follows RA’s non-threaded polling model by exposing an HTTP server onhost:port, configurable by the user in the settings. Basic auth can be optionally enabled.I also created an adapter that acts as a bridge between MCP messages and RA.
For now, I’ve started by exposing a single tool to query the content currently loaded in RA, just to keep things small until we agree on the overall approach and scope.
Server lifecycle (init/poll/deinit)
network/mcp/mcp_server.c/hTransport abstraction interface
network/mcp/mcp_transport.hHTTP transport implementation, listener, request parsing, auth
network/mcp/mcp_http_transport.c/hJSON-RPC dispatch (initialize, tools/list, tools/call)
network/mcp/mcp_adapter.c/hTool registry
network/mcp/mcp_adapter_tool_list.c/hTool implementations (get_content_info)
network/mcp/mcp_adapter_tools.hResponse format templates
network/mcp/mcp_json_templates.hJSON builder helpers
network/mcp/mcp_adapter_utils.hConstants
network/mcp/mcp_defines.hThis can be tested using the GitHub Copilot plugin in VS Code, Claude Code, OpenCode, OpenClaw, and similar AI tools.
Reviewers
@warmenhoven @JoeOsborn @sonninnos @hizzlekizzle
Example usage