Skip to content

Parse port numbers from MCP server URLs in CORS proxy#20208

Merged
ngxson merged 4 commits intoggml-org:masterfrom
eapache:parse-port-from-mcp-urls
Mar 9, 2026
Merged

Parse port numbers from MCP server URLs in CORS proxy#20208
ngxson merged 4 commits intoggml-org:masterfrom
eapache:parse-port-from-mcp-urls

Conversation

@eapache
Copy link
Copy Markdown
Contributor

@eapache eapache commented Mar 7, 2026

Fixes #20205.

When specifying an MCP server on a custom port (e.g. http://127.0.0.1:8123/mcp), the CORS proxy was not parsing the port number, leading to a malformed host (with the trailing port still attached) and falling back to port 80/443.

This is my first PR here and it's been a while since I touched C/C++ so likely I've done something wrong with the formatting or data types or something 😅

The only AI used in making this PR was Copilot's autocomplete, which I trusted to pick the right stdlib methods for stoi and throwing a runtime error as my C++ is so rusty.

Copy link
Copy Markdown
Contributor

@ngxson ngxson left a comment

Choose a reason for hiding this comment

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

Technically this won't support something like https://myserver.com:8443

@eapache
Copy link
Copy Markdown
Contributor Author

eapache commented Mar 7, 2026

Why not? Because it doesn't recognize 8443 as a tls port?

@ngxson
Copy link
Copy Markdown
Contributor

ngxson commented Mar 7, 2026

There is a check port == 443

server_http_proxy::server_http_proxy(
const std::string & method,
const std::string & host,
int port,
const std::string & path,
const std::map<std::string, std::string> & headers,
const std::string & body,
const std::function<bool()> should_stop,
int32_t timeout_read,
int32_t timeout_write
) {
// shared between reader and writer threads
auto cli = std::make_shared<httplib::ClientImpl>(host, port);
auto pipe = std::make_shared<pipe_t<msg_t>>();
if (port == 443) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
cli.reset(new httplib::SSLClient(host, port));
#else
throw std::runtime_error("HTTPS requested but CPPHTTPLIB_OPENSSL_SUPPORT is not defined");
#endif
}

@eapache
Copy link
Copy Markdown
Contributor Author

eapache commented Mar 8, 2026

Ah ok I just need to pass the scheme in explicitly there. I will refresh the patch tomorrow morning when I'm back at my desk.

@eapache eapache force-pushed the parse-port-from-mcp-urls branch from a9f3981 to a9daf8a Compare March 8, 2026 13:03
}
auto proxy = std::make_unique<server_http_proxy>(
method,
"http",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

While adding scheme to be passed around we also now need a scheme here (for the model router). I don't see an existing scheme here, but AFAICT the child processes always just use http so I can hard-code it (model routing still works in a trivial local test with it hard-coded like this). Wanted to call this out in case there's a flag I'm not familiar with for sending the communications to the child inference processes via https or something.

}

SRV_INF("proxying %s request to %s://%s%s\n", method.c_str(), parsed_url.scheme.c_str(), parsed_url.host.c_str(), parsed_url.path.c_str());
SRV_INF("proxying %s request to %s://%s:%i%s\n", method.c_str(), parsed_url.scheme.c_str(), parsed_url.host.c_str(), parsed_url.port, parsed_url.path.c_str());
Copy link
Copy Markdown
Contributor Author

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 now always log the port explicitly, but parsed_url no longer indicates whether the port was explicit or implicit.

Comment thread common/http.h
#endif

httplib::Client cli(parts.scheme + "://" + parts.host);
httplib::Client cli(parts.scheme + "://" + parts.host + ":" + std::to_string(parts.port));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I ran it through ChatGPT Codex and it pointed out that I'd broken http downloads on non-standard ports because that had been relying on the port being smushed into the host still. Not quite sure how to test this, but it seems right.

Comment thread common/http.h
parts.path = "/";
}

auto colon_pos = parts.host.find(':');
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

How do we feel about IPv6 addresses? They can have colons in their literals which will break here. But parsing those properly feels way outside the scope of a little internal helper method, and starts to feel like we should be pulling in a library which can do proper URL parsing...

@eapache
Copy link
Copy Markdown
Contributor Author

eapache commented Mar 8, 2026

@ngxson I went down a bit of a rabbit-hole trying to support IPv6 literals and it turned into a substantial refactor which I didn't manage to get to the bottom of before giving up. In particular, it adds more logic anywhere we convert from parts to full-URL or back (because a host of ::1 needs to be printed and parsed as [::1] in the context of a URL).

I haven't tried but I'm pretty sure those are already broken for MCP in master, because we're not stripping the [ ] when passing the host to server_http_proxy, so I'd rather not consider that blocking this fix. And as I mentioned in my previous comment, this level of complexity is getting to the point where I'd consider just using a library to do the URL manipulation, it's not as bad as email addresses but there are still a lot of edge cases.

Putting all that aside, I've addressed the tls-inferred-from-port issue you pointed out, and I think otherwise this PR is a reasonable improvement over the current state of master.

Copy link
Copy Markdown
Contributor

@ngxson ngxson left a comment

Choose a reason for hiding this comment

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

I think it's fine as a quick fix, we can refactor the parser to support ipv6 in a dedicated PR, but I think not many people will actually use it

@github-actions github-actions Bot added the python python script changes label Mar 9, 2026
@ngxson ngxson merged commit 23fbfcb into ggml-org:master Mar 9, 2026
75 of 78 checks passed
bartowski1182 pushed a commit to bartowski1182/llama.cpp that referenced this pull request Mar 10, 2026
…rg#20208)

* Parse port numbers from MCP server URLs

* Pass scheme to http proxy for determining whether to use SSL

* Fix download on non-standard port and re-add port to logging

* add test

---------

Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
@eapache eapache deleted the parse-port-from-mcp-urls branch March 11, 2026 02:29
Ethan-a2 pushed a commit to Ethan-a2/llama.cpp that referenced this pull request Mar 20, 2026
…rg#20208)

* Parse port numbers from MCP server URLs

* Pass scheme to http proxy for determining whether to use SSL

* Fix download on non-standard port and re-add port to logging

* add test

---------

Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
@kurnevsky
Copy link
Copy Markdown
Contributor

kurnevsky commented Mar 21, 2026

There is one problem still left - server sets Host header with host only, but it should include port as well. There are some strict mcp servers that reject requests that have invalid Host header. Created MR: #20843

Seunghhon pushed a commit to Seunghhon/llama.cpp that referenced this pull request Apr 26, 2026
…rg#20208)

* Parse port numbers from MCP server URLs

* Pass scheme to http proxy for determining whether to use SSL

* Fix download on non-standard port and re-add port to logging

* add test

---------

Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
rsenthilkumar6 pushed a commit to rsenthilkumar6/llama.cpp that referenced this pull request May 1, 2026
…rg#20208)

* Parse port numbers from MCP server URLs

* Pass scheme to http proxy for determining whether to use SSL

* Fix download on non-standard port and re-add port to logging

* add test

---------

Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

examples python python script changes server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Misc. bug: MCP CORS proxy can't establish connection to servers on custom ports

3 participants