Skip to content

Conversation

@cliffhall
Copy link
Member

@cliffhall cliffhall commented Sep 16, 2025

Description

  • In App.tsx

    • Add state for connectionType
    • Fetch connectionType from localstorage if present
  • In Sidebar.tsx

    • Add dropdown for connectionType, only visible if transport type is not stdio
  • In Sidebar.test.tsx

    • set connectionType to "proxy"
  • In useConnection.ts

    • add connectionType param, defaulting to "proxy"
    • add state for mcpSessionId
    • Only check proxy health if the connection type is proxy
    • create a serverUrl variable which will either be the proxy url OR the server url depending upon the connection type
    • added captureResponseHeaders function to extract the protocol-related headers from a response.
    • when creating transport options and connectionType is "direct" and transportType is not "stdio",
      • add the appropriate Accept and Content-Type headers
      • capture the protocol related response headers and store them in the custom fetch handler
      • set serverUrl to be the server url, not the proxy url
    • if connection type is proxy set serverUrl to the mcpProxyUrl
    • use serverUrl instead of mcpProxyUrl for the transport instantiation
  • In useConnection.test.tsx

    • replaced test "sends X-MCP-Proxy-Auth header when proxy auth token is configured" with two tests:
      • "sends X-MCP-Proxy-Auth header when proxy auth token is configured for 'proxy' connectionType"
      • "does NOT send X-MCP-Proxy-Auth header when proxy auth token is configured for 'direct' connectionType"
    • added Connection URL Verification section with 2 tests
      • uses server URL directly when connectionType is 'direct'"
      • "uses proxy server URL when connectionType is 'proxy'"

Motivation and Context

Multiple issues have been raised where the proxy has obfuscated what is actually going on with the server.

Checking the browser's network inspector tab doesn't help because it only shows the connection to the proxy, not what was actually sent to or returned from the server.

The Proxy is only actually necessary for STDIO connections, because it runs the server in a process. For SSE and StreamableHttp servers, the only hurdle for connecting directly from the browser is that the server must configure CORS to allow it.

So for developers who would like better insight into the traffic and don't mind configuring CORS on their server, this dropdown (which only appears for SSE and StreamableHttp Transport types) is a great way to cut out the middleman.

How Has This Been Tested?

I added CORS configuration to the Everything Reference server for its SSE and StreamableHttp transports.

Direct SSE Connection From Inspector

Screenshot 2025-09-16 at 4 13 27 PM

Direct StreamableHttp Connection From Inspector

Screenshot 2025-09-16 at 4 11 00 PM

Proxied SSE Connection From Inspector

proxy-sse

Proxied StreamableHttp Connection From Inspector

proxy-shttp

Breaking Changes

Nope.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Test against this PR on the Servers repo: modelcontextprotocol/servers#2725

  - Add state for connectionType
  - Fetch connectionType from localstorage if present

* In Sidebar.tsx
  - Add dropdown for connectionType, only visible if transport type is not stdio

* In Sidebar.test.tsx
  - set connectionType to "proxy"

* In useConnection.ts
  - add connectionType param, defaulting to "proxy"
  - add state for mcpSessionId
  - Only check proxy health if the connection type is proxy
  - create a serverUrl variable which will either be the proxy url OR the server url depending upon the connection type
  - added captureResponseHeaders function to extract the protocol-related headers from a response.
  - when creating transport options and connectionType is "direct" and transportType is not "stdio",
    - add the appropriate `Accept` and `Content-Type` headers
    - capture the protocol related response headers and store them in the custom fetch handler
    - set serverUrl to be the server url, not the proxy url
  - if connection type is proxy set serverUrl to the mcpProxyUrl
  - use serverUrl instead of mcpProxyUrl for the  transport instantiation
* In useConnection.test.tsx
  - replaced test "sends X-MCP-Proxy-Auth header when proxy auth token is configured" with two tests:
    - "sends X-MCP-Proxy-Auth header when proxy auth token is configured for 'proxy' connectionType"
    - "does NOT send X-MCP-Proxy-Auth header when proxy auth token is configured for 'direct' connectionType"
   - added Connection URL Verification section with 2 tests
     - uses server URL directly when connectionType is 'direct'"
     - "uses proxy server URL when connectionType is 'proxy'"
@felixweinberger
Copy link
Contributor

felixweinberger commented Sep 18, 2025

I definitely see the motivation for more visibility in Browser DevTools and general ergonomics. I'm curious though, if the proxy makes the DevEx strictly worse, why keep it around at all? Is there actually a reason we need it even in the StreamableHTTP / SSE case?

If my understanding is correct, the Proxy has a function as a CORS bypass even in the StreamableHTTP/SSE case - this seems important for the inspector to be able to connect to servers without needing to change CORS policy to be able to debug.

The proxy is an internal implementation detail of the inspector, are we confident users would understand the choice between these two options how they're presented here? Ideally when using a debugging tool I wouldn't need to think about how the tool itself works.

proxy direct
CleanShot 2025-09-18 at 21 06 33@2x CleanShot 2025-09-18 at 21 06 57@2x

If the proxy makes things harder to debug, I feel like we should try to find a way to resolve that rather than give people the option to disable it? For example we could pass through logs so the devtools see them or provide a raw JSONRPC message view in the inspector somewhere.

Alternatively - could we make the choice clearer? It seems like in the StreamableHTTP/SSE case the proxy is purely about bypassing CORS. Maybe that information could be added as a tooltip underneath the dropdown? Something like:

  • "HTTP Proxy: by default inspector uses a proxy internally to connect to HTTP servers, allowing it to bypass CORS restrictions"
  • "Direct: you can connect to your MCP server directly if you want to see logs in your browser devtools, but you'll need to configure CORS on your server to connect."

@cliffhall
Copy link
Member Author

cliffhall commented Sep 18, 2025

I definitely see the motivation for more visibility in Browser DevTools and general ergonomics. I'm curious though, if the proxy makes the DevEx strictly worse, why keep it around at all? Is there actually a reason we need it even in the StreamableHTTP / SSE case?

My initial take when I raised this in discord a few months back was "can we do away with it for all but stdio servers." The answer, of course, is CORS. Unless servers are built with an open door CORS policy, it won't work. Right now that's 99.999% of all servers because this is the first time we're trying to hit a server directly with the browser.

If my understanding is correct, the Proxy has a function as a CORS bypass even in the StreamableHTTP/SSE case - this seems important for the inspector to be able to connect to servers without needing to change CORS policy to be able to debug.

The problem with the proxy is that it has two transports, Client <-> Proxy & Proxy <-> Server. Bridging headers and responses is not as automatic as you might think. I recently had to add a bunch of code to fish headers out of the responses and shuttle them back to the client because headers that weren't part of the initial connection but that were introduced later in the conversation weren't making it back and were therefore being dropped from later requests, i.e, mcp-protocol-version.

A number of issues that have come up have shown that the proxy makes communications more opaque and difficult to troubleshoot. This is why an option to connect directly is desirable. You can be certain that the headers and responses arriving in the browser (or failing to) are not the result of the proxy acting as an unintentional filter.

If the proxy makes things harder to debug, I feel like we should try to find a way to resolve that rather than give people the option to disable it? For example we could pass through logs so the devtools see them or provide a raw JSONRPC message view in the inspector somewhere.

The problem is that it is never going to be as optimal as a direct connection. You can never be certain that some flaw in the proxy itself isn't causing the problem. If you are having a particularly difficult time troubleshooting the problem, even with the source of the proxy running locally and adding in more log statements, if you bypass the proxy, you can rule it out completely.

Alternatively - could we make the choice clearer? It seems like in the StreamableHTTP/SSE case the proxy is purely about bypassing CORS. Maybe that information could be added as a tooltip underneath the dropdown?

Sure we can do tooltips - I can do that right away. And entries in the readme as well as the documentation in on the website. And a blog post about this way of debugging things. An educational outreach step can come after the feature is available. People can see that with a minor config change to their server, they can make the communications much clearer and easier to debug.

@cliffhall
Copy link
Member Author

cliffhall commented Sep 18, 2025

Maybe that information could be added as a tooltip underneath the dropdown? Something like:

"HTTP Proxy: by default inspector uses a proxy internally to connect to HTTP servers, allowing it to bypass CORS restrictions"
"Direct: you can connect to your MCP server directly if you want to see logs in your browser devtools, but you'll need to configure CORS on your server to connect."

@felixweinberger That's a lot of text to go in a tooltip or to take up valuable space in the sidebar. Here's a crack at a compromise:

Screenshot 2025-09-18 at 5 41 34 PM

  - Add tooltip over the Connection Type field
@domdomegg
Copy link
Member

A drive-by proposal on an alternative (I might be missing something here, sorry if so!): always default to direct, and if that doesn't work try proxy. If no proxy is available or it still doesn't work, throw an error. This removes people having to think about / understand this at all, which is probably better UX.

(If we want to generally encourage allowing direct connections, we could have a green tick + 'connected directly' OR orange warning sign 'connected via proxy' with info bubble with tooltip linking to some docs about CORS)

@cliffhall
Copy link
Member Author

Woohoo! We have the server side of this effort merged:
modelcontextprotocol/servers#2725

@cliffhall
Copy link
Member Author

cliffhall commented Sep 19, 2025

A drive-by proposal on an alternative (I might be missing something here, sorry if so!): always default to direct, and if that doesn't work try proxy. If no proxy is available or it still doesn't work, throw an error. This removes people having to think about / understand this at all, which is probably better UX.

(If we want to generally encourage allowing direct connections, we could have a green tick + 'connected directly' OR orange warning sign 'connected via proxy' with info bubble with tooltip linking to some docs about CORS)

@domdomegg That would be an interesting approach I do like it. But maybe a 2.0? It will be a bit more involved to manage the state for that. The useConnection hook could use a bit of refactoring first.

I think we need to do some educational outreach, documentation, blog posts, etc on opening up CORS first and how this can give you a better troubleshooting experience first.

Copy link
Member

@domdomegg domdomegg left a comment

Choose a reason for hiding this comment

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

LGTM

@cliffhall cliffhall merged commit cf9acf6 into modelcontextprotocol:main Sep 20, 2025
5 checks passed
@cliffhall cliffhall deleted the no-proxy-for-http-transports branch September 22, 2025 18:10
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