Skip to content

Add an embedded MCP Server in RetroArch#18757

Draft
drhelius wants to merge 3 commits intolibretro:masterfrom
drhelius:mcp
Draft

Add an embedded MCP Server in RetroArch#18757
drhelius wants to merge 3 commits intolibretro:masterfrom
drhelius:mcp

Conversation

@drhelius
Copy link
Copy Markdown
Contributor

@drhelius drhelius commented Feb 22, 2026

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 stdio and http being the most common ones.

I created a generic transport interface and implemented http for now. The http transport follows RA’s non-threaded polling model by exposing an HTTP server on host: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/h

Transport abstraction interface
network/mcp/mcp_transport.h

HTTP transport implementation, listener, request parsing, auth
network/mcp/mcp_http_transport.c/h

JSON-RPC dispatch (initialize, tools/list, tools/call)
network/mcp/mcp_adapter.c/h

Tool registry
network/mcp/mcp_adapter_tool_list.c/h

Tool implementations (get_content_info)
network/mcp/mcp_adapter_tools.h

Response format templates
network/mcp/mcp_json_templates.h

JSON builder helpers
network/mcp/mcp_adapter_utils.h

Constants
network/mcp/mcp_defines.h

This 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

image

@drhelius
Copy link
Copy Markdown
Contributor Author

Added some basic tools to test

@JoeOsborn
Copy link
Copy Markdown
Contributor

JoeOsborn commented Feb 22, 2026

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?

@drhelius
Copy link
Copy Markdown
Contributor Author

drhelius commented Feb 23, 2026

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.

@JoeOsborn
Copy link
Copy Markdown
Contributor

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?

@drhelius
Copy link
Copy Markdown
Contributor Author

drhelius commented Feb 24, 2026

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.

@drhelius
Copy link
Copy Markdown
Contributor Author

drhelius commented Feb 24, 2026

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.

@JoeOsborn
Copy link
Copy Markdown
Contributor

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?

@drhelius
Copy link
Copy Markdown
Contributor Author

drhelius commented Feb 26, 2026

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.

Users of AI agents don't care about tool descriptions. Tool descriptions including input parameter descriptions is a job for RA developers.

I think an external MCP server making use of the command interface could be very smooth to implement without any changes to retroarc

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.

One thing about the command API which is nice is that all of RA "speaks" it. [...] I think it would be fine if defining all commands were slightly heavier-weight with e.g. documentation strings or documented input/output types.

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.

This gets at a related question---are MCP tools required to be in English? Do these need to be localized ever?

No, but it's a good practice and also convenient to write descriptions in English.

@RobLoach
Copy link
Copy Markdown
Member

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.

@drhelius
Copy link
Copy Markdown
Contributor Author

Interesting idea. Another interface that's available to RetroArch is the Network Control Interface.

I'm confused. Is it not the same used by command.c?

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.

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?

@devinprater
Copy link
Copy Markdown

devinprater commented Feb 27, 2026 via email

@kivutar
Copy link
Copy Markdown
Member

kivutar commented Apr 9, 2026

This is interesting, it opens many doors, like debugging.

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.

5 participants