Skip to content

Conversation

gpeal
Copy link
Collaborator

@gpeal gpeal commented Sep 26, 2025

This PR adds support for streamable HTTP MCP servers when the experimental_use_rmcp_client is enabled.

To set one up, simply add a new mcp server config with the url:

[mcp_servers.figma]
url = "http://127.0.0.1:3845/mcp"

It also supports an optional bearer_token which will be provided in an authorization header. The full oauth flow is not supported yet.

The config parsing will throw if it detects that the user mixed and matched config fields (like command + bearer token or url + env).

The best way to review it is to review core/src and then rmcp-client/src/rmcp_client.rs first. The rest is tests and propagating the Transport struct around the codebase.

Example with the Figma MCP:
CleanShot 2025-09-26 at 13 35 40

@gpeal gpeal force-pushed the gpeal/rmcp-client-http branch from 734d384 to 50fcc9a Compare September 26, 2025 22:01
@gpeal gpeal marked this pull request as ready for review September 26, 2025 22:46
@gpeal gpeal requested a review from bolinfest September 26, 2025 22:46
Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

tracing::error!("new mcp_servers: {mcp_servers:?} use_rmcp_client: {use_rmcp_client}");

[P1] Stop logging full MCP configuration at error level

The new call to tracing::error!("new mcp_servers: {mcp_servers:?} …) emits the entire MCP server configuration – including bearer_token values – every time the connection manager starts. Because error logs are typically collected in production, this effectively dumps secrets to log files and may trigger noisy alerting even when everything succeeds. Consider removing the log or downgrading it to a non‑sensitive, lower‑level message that redacts credentials.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

@gpeal
Copy link
Collaborator Author

gpeal commented Sep 26, 2025

@codex review it again

@gpeal gpeal changed the title [Draft][MCP] Add support for streamable HTTP MCP servers [MCP] Add support for streamable HTTP MCP servers Sep 26, 2025
Copy link
Contributor

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

env: Option<Option<HashMap<String, String>>>,

url: Option<String>,
bearer_token: Option<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't love that we encourage storing this in plaintext, but I admit we don't have a great alternative for people just yet.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

And also https://crates.io/crates/keyring

I don't love that this is plain text either and we'll be able to provide better options over time. I just want to get the simplest thing in now and we can build upon it.

});
let env = match &cfg.transport {
McpServerTransportConfig::Stdio { env, .. } => env.as_ref().map(|env| {
env.iter()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this not a straight clone? Does it have to be a BTreeMap? Isn't there a .from() or into() you can use instead here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is just indented from the original impl (view the PR without whitespace). I didn't want to make other modifications here

}),
McpServerTransportConfig::StreamableHttp { .. } => None,
};
let transport = match &cfg.transport {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why match &cfg.transport twice? You only use the env in the Stdio case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh this whole env block can be removed because it's embedded in transport

let mut entry = TomlTable::new();
entry.set_implicit(false);
entry["command"] = toml_edit::value(config.command.clone());
match &config.transport {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We really need to move all this TOML editing stuff to a separate file.

Comment on lines +211 to +215
let listener = TcpListener::bind("127.0.0.1:0")?;
let port = listener.local_addr()?.port();
drop(listener);
let bind_addr = format!("127.0.0.1:{port}");
let server_url = format!("http://{bind_addr}/mcp");
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's going on here? What happens when we drop(listener)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Binding 0 us bind to a port that we know will work so but then we release it so the mcp http server can bind to it

Comment on lines +219 to +220
.env("MCP_STREAMABLE_HTTP_BIND_ADDR", &bind_addr)
.env("MCP_TEST_VALUE", expected_env_value)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pull out these constants somewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

MCP_STREAMABLE_HTTP_BIND_ADDR is shared between this test and the test server in another crate and they intentionally don't depend on each other. The other is internal to this test so I think it's fine.

"schemars",
"server",
"transport-child-process",
"transport-streamable-http-client-reqwest",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note the other Cargo.toml files have been updated to use { workspace = true }

env_var: Option<String>,
}

impl ServerHandler for TestToolServer {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this basically copy/pasted from the previous file? What's the difference?

@gpeal gpeal force-pushed the gpeal/rmcp-client-http branch from da25ce7 to 268ba77 Compare September 27, 2025 00:56
@gpeal gpeal merged commit 3a1be08 into main Sep 27, 2025
19 checks passed
@gpeal gpeal deleted the gpeal/rmcp-client-http branch September 27, 2025 01:24
@github-actions github-actions bot locked and limited conversation to collaborators Sep 27, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants