Skip to content

Conversation

@shikokuchuo
Copy link
Member

This is the first PR to support multiple client / server combinations.

  • mcp_server() may be called in different R sessions and their URLs will automatically increment.
  • Each mcp_server() instance supports multiple proxies connected to it, i.e. different apps at the same time.

The client - proxy - server relationship is still 1-1-1 as the client e.g. Claude Desktop assumes it is talking to a single server. So, for now, we also have:

  • mcp_proxy() gains an i instance argument to connect to a specific server.

Copy link
Collaborator

@simonpcouch simonpcouch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is brilliant. Hell yeah.

The client - proxy - server relationship is still 1-1-1 as the client e.g. Claude Desktop assumes it is talking to a single server.

This makes sense. Reflecting on how I’d imagine actually using this, I feel like it’s reasonable, and maybe even preferable for UX, that a client can only talk to one server at a time.

mcp_proxy() gains an i instance argument to connect to a specific server.

Super neat how this is implemented.

Notes-to-self, for my own understanding. Please do poke hole if there are any:

  1. With this PR, if a user wants to make use of more than one client, they'd set mcp_proxy(i) to a constant value for a given client. e.g. Claude Code could always be i = 2 by setting that in the config
  2. mcp_serve() increments i = 1, 2, 3 until it successfully connects to a client. So, if Claude Desktop is i = 1 but the app isn't open, and Claude Code is 2 and it is open, the server will connect to Claude Code.
  3. Several clients can connect to one server at once.
  4. Several servers cannot connect to one client at the same time. I think this makes sense and can probably stay this way.
    • My interpretation of "Dialers and Listeners are always associated with a single socket" is that the first server that successfully connects to a client stays connected to the proxy even when new servers are booted up and attempt to connect?

How do we imagine the interface to configure a server to a specific client? e.g. if in the above example from 2) both Claude Code and Claude Desktop were open, is there any way to tell the server to "choose" 2? Or is there a "priority" ordering to the is, where users should set clients that they want to prefer to be connected to with lower is?

Will let Winston have a look here as well before merging. Will plan on putting together some documentation on this after merge.

Also, feel free to add yourself as aut or I can do so in a bit!

@shikokuchuo
Copy link
Member Author

shikokuchuo commented May 1, 2025

  • mcp_serve() increments i = 1, 2, 3 until it successfully connects to a client. So, if Claude Desktop is i = 1 but the app isn't open, and Claude Code is 2 and it is open, the server will connect to Claude Code.

The server listens at a socket address. It will accept connections from any proxy that dials into it, hence several clients can connect at once.

  • My interpretation of "Dialers and Listeners are always associated with a single socket" is that the first server that successfully connects to a client stays connected to the proxy even when new servers are booted up and attempt to connect?

The client (proxy) dials into a server (so there's no danger it connects to more than one). At the moment, the instance numbers just reflect the order in which the servers started up. This makes it not so ergonomic to say always connect Claude Code to my Positron session and not my other Rstudio sessions. We could enable the 'I' argument on the server side as well?

@wch
Copy link
Collaborator

wch commented May 1, 2025

I have a few questions:

  • In practice, how is i passed to the mcp_proxy()? Does it have to be hard-coded into the application's config file?
  • I was thinking of a workflow like this: in the Claude desktop app, you would be able to ask, "What R sessions do I have running?" and then it would talk to the proxy, and the proxy would go and discover all the running R sessions which are running the acquaint server. Then you could tell it which one you want to query. From my understanding of this PR, that's not how things would work here.

It think we want to be able to have the MCP client app and the R sessions to start and stop independently, in no particular order, and for the MCP client app to be able to discover the running sessions at any time. Or in other words, a proxy should be able to discover the available servers. It may also be useful for a server to be able to discover all proxies, so that when the server starts up, it can tell all the proxies that it is available to talk to.

Side note: the word "server" is a little confusing, acquaint has a proxy and a server, but the proxy is an MCP server. Maybe we should use different name for the acquaint server?

@simonpcouch
Copy link
Collaborator

I think I'm on board for this generally.

in the Claude desktop app, you would be able to ask, "What R sessions do I have running?" and then it would talk to the proxy, and the proxy would go and discover all the running R sessions which are running the acquaint server.

Do you imagine that this would be implemented as another ellmer-based tool that the model can invoke, where in that case the proxy makes a call to a fresh Rscript to call a function implemented in R?


I do think that there should be some sort of auto-discovery/dispatch to an active mcp_serve() that the currently available tools will reference without the model needing to invoke that tool that requests which sessions are running and picking one in subsequent calls. I think the three of us are used to having many R sessions running at once, but it'd be nice to cull out that additional tool call in the case where there's just one R session active that we could reasonably infer is the intended one.

@shikokuchuo
Copy link
Member Author

Moving this forward in commit e0368c8 after the latest comments:

  1. Removed the 'i' argument to mcp_proxy() as it's rather unpractical to set this in the client config files.
  2. Instead, the proxy will dial into the first mcp server by default (for most users with one R session, it'll just work out of the box).
  3. Discovery can be performed at any time by acquaint:::mcp_discover() - this can be performed within the proxy session or in a fresh R session - and returns metadata sent by each server (the result of commandArgs() for now, which lets us tell the difference between an R terminal / Rstudio / Positron session).
  4. Server selection can then be made by acquaint:::select_server() within the proxy session.

I'm assuming 3 and 4 can ultimately be effected through the chat interface.

Side note: the word "server" is a little confusing, acquaint has a proxy and a server, but the proxy is an MCP server. Maybe we should use different name for the acquaint server?

I had a similar issue with the use of 'server' within mirai some time ago, and settled on 'host' to refer to the user process. I think it would work here i.e. mcp_host().

Copy link
Collaborator

@simonpcouch simonpcouch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pushing this forward, Charlie!

Thumbs up from me. Just spun off a couple issues related to conversations here; I think my path forward here is merging this as-is and then I can take a moment to "plug in" mcp_discover() and select_server() as tools that the model can call in the proxy, figuring out the ergonomics of allowing the user to direct the model on which session to "choose" then.

Will give Winston a chance to holler here before merging this in tomorrow afternoon.

@wch
Copy link
Collaborator

wch commented May 6, 2025

This looks great. Thanks for making this work!

@simonpcouch simonpcouch merged commit ccc18bc into posit-dev:main May 6, 2025
6 checks passed
@shikokuchuo shikokuchuo deleted the dev branch May 8, 2025 08:55
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.

3 participants