cd /news/developer-tools/show-hn-mcp-proxy-add-mcp-support-to… · home topics developer-tools article
[ARTICLE · art-101157] src=github.com ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

Show HN: MCP-Proxy Add MCP support to any REST API – no code changes required

Mcp-proxy, an open-source tool by crhuber, lets developers add MCP support to any REST API without code changes by loading a YAML config, dynamically registering MCP tools, and translating tool calls into HTTP requests. The tool can be deployed as a sidecar in Kubernetes or in front of any API service lacking a native MCP server, and it includes features like bearer token authentication and DNS-rebinding protection.

read7 min views2 publishedAug 18, 2026
Show HN: MCP-Proxy Add MCP support to any REST API – no code changes required
Image: Michielbdejong (auto-discovered)

Add MCP support to any REST API - no code changes required.

We've spent decades building REST APIs. Adding MCP support shouldn't mean reimplementing your business logic. mcp-proxy

exposes your existing REST endpoints as MCP tools automatically, with zero changes to your API.

mcp-proxy

loads a YAML config describing one or more upstream REST APIs, dynamically registers MCP tools for them, and translates each tool call into an HTTP request against the real upstream API and back.

flowchart LR
    A[MCP Client] -- MCP tool call --> B[mcp-proxy]
    B -- HTTP request --> C[REST API]
    C -- HTTP response --> B
    B -- MCP tool result --> A

mcp-proxy

can be deployed in a sidecar pattern in Kubernetes or infront of any API service that doesnt provide a native MCP server.

Say for example, you already have an REST API server at https://api.gold-api.com but you want to be able to query it via MCP.

To achieve this, create a config.yaml

endpoints:
  - name: gold-api
    upstream:
      base_url: "https://api.gold-api.com"
      timeout: 10s
      auth:
        type: "none"
    tools:
      - name: getPrice
        description: "Fetch gold price"
        http:
          method: GET
          path: "/price/XAU"

Run the server.

mcp-proxy --config config.yaml

What happens here is a new MCP tool called getPrice

is registered on the server. When this MCP tool is called the server makes a HTTP GET

request to https://api.gold-api.com/price/XAU

and returns the result to the MCP Client.

Verify its working by making MCP tool call using curl

curl -X POST http://localhost:8080/mcp \
        -H "Content-Type: application/json" \
        -H "Accept: application/json, text/event-stream" \
        -d '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "tools/call",
      "params": {
        "name": "gold-api_getPrice",
        "arguments": {}
      }
    }'

Response:

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"currency\":\"USD\",\"price\":4409.299805}"}],"structuredContent":{"currency":"USD","price":4409.299805}}}

Congrats! We have now adapted our API to be MCP ready with no code changes to the API!

go install github.com/crhuber/mcp-proxy/cmd/mcp-proxy@latest

Or build from source:

go build -o mcp-proxy ./cmd/mcp-proxy

Or Use kelp

kelp add crhuber/mcp-proxy --install
mcp-proxy --config config.yaml
Flag Env var Default Description
--config
MCP_PROXY_CONFIG
(required)
Path to the proxy config YAML file
--listen
MCP_PROXY_LISTEN_ADDR
:8080
Address to listen on
--auth-mode
MCP_PROXY_AUTH_MODE
none
none bearer — whether the proxy's own /mcp endpoint requires a bearer token
--log-level
MCP_PROXY_LOG_LEVEL
info
debug info warn error
--shutdown-grace
MCP_PROXY_SHUTDOWN_GRACE
15s
How long to wait for in-flight requests to finish on shutdown
--disable-localhost-protection
MCP_PROXY_DISABLE_LOCALHOST_PROTECTION
false
Disable the MCP SDK's DNS-rebinding Host header check

When --auth-mode=bearer

, set MCP_PROXY_BEARER_TOKEN

in the environment (it is intentionally not a flag so it never shows up in --help

output or process argv).

The MCP SDK auto-enables DNS-rebinding protection: if the TCP connection it accepted came in on a loopback address, it requires the request's Host

header to also look like loopback (localhost

, 127.0.0.0/8

, ::1

), and otherwise rejects it with 403 Forbidden: invalid Host header

. This guards local companion services against a browser being tricked into hitting them, and doesn't apply to mcp-proxy's deployment model.

If a sidecar connects to mcp-proxy over 127.0.0.1

but forwards the original external Host

header (e.g. mcp.domain.com

) — the accepted connection's local address is loopback regardless of what address mcp-proxy itself binds to, so changing --listen

/MCP_PROXY_LISTEN_ADDR

alone won't fix it. Set --disable-localhost-protection

(or MCP_PROXY_DISABLE_LOCALHOST_PROTECTION=1

) in that topology.

The config file describes one or more upstream endpoints and the tools that should be generated for each. See config.yaml.example for a full annotated example, including path/query parameters, request body templating, and response field selection.

To connect to an upstream REST API, you need to define your upstream base_url

, timeout

, and auth

type.

endpoints:
  - name: gold-api
    upstream:
      base_url: "https://api.gold-api.com"
      timeout: 10s
      auth:
        type: "none"

The base_url should be defined without a trailing slash /

.

Auth types supported are:

bearer

      auth:
        type: bearer                      # bearer | header | query | none
        env: ORDERS_API_KEY               # secret ALWAYS comes from this env var, never literal in file

header

      auth:
        type: header
        header: X-API-Key
        type: "none"
        env: ORDERS_API_KEY               # secret ALWAYS comes from this env var, never literal in file

none

      auth:
        type: "none"

For any key defined in env

it must be set in environment variables before starting the server. In the preceeding examples ORDERS_API_KEY

must be set

Tools are the MCP tools to create on the MCP server. Each tool has name

, description

, and parameters

    tools:
      - name: createInvoice
        description: "Create a invoice for a customer."
        parameters:
          type: object
          required: [customerId]
          properties:
            customerId:
              type: string
              description: "Customer to bill."

Whenever a toolcall is made to the attached tool, a coresponding http request is made to the upstream.

The http

object controls what how the HTTP request upstream is made. The usual parameters for a HTTP request including method

, path

and body

    tools:
      - name: getPrice
        description: "Fetch gold price"
        http:
          method: GET
          path: "/price/XAU"

A JSON request body can also be sent on any HTTP method by adding the body

        http:
          method: POST
          path: "/v1/invoices"
          body: # literal JSON template of exactly what to POST
            customerId: "123"

Suppose your tool has properties customerId

and currency

but you want to reference those when you make a http POST method upstream. You can reference them using {parameter}

syntax.

    tools:
      - name: createInvoice
        description: "Create a new draft invoice for a customer."
        parameters:
          type: object
          required: [customerId]
          properties:
            customerId:
              type: string
              description: "Customer to bill."
            currency:
              type: string
              description: "ISO-4217 currency code."
              default: "USD"
        http:
          method: POST
          path: "/v1/invoices"
          body:                            # literal JSON template of exactly what to POST
            customerId: "{customerId}"      # "{name}" -> substituted with that parameter's runtime value
            currency: "{currency}"

Sometimes you want to want to use parameters as query strings on the upstream request. To do this use in

on the properties. For example the following config would result in a HTTP GET

to /v1/invoices?customerId={customerId}¤cy={currency}

          properties:
            customerId:
              type: string
              in: query
              description: "Customer to bill."
            currency:
              type: string
              in: query
              description: "ISO-4217 currency code."
        http:
          method: POST
          path: "/v1/invoices"

This also works in paths

        http:
          method: POST
          path: "/v1/invoices/{customerId}"

Suppose our API call to https://api.gold-api.com/price/XAU

returns JSON response

{
    "currency": "USD",
    "currencySymbol": "$",
    "exchangeRate": 1.0,
    "name": "Gold",
    "price": 4394.299805,
    "symbol": "XAU",
    "updatedAt": "2026-08-17T12:23:02Z",
    "updatedAtReadable": "a few seconds ago"
}

In many MCP cases it is preferred to use structured but compact text over raw JSON dumps. For something like this, a lightly formatted markdown table or bullet list is often easier for the model to reason over than raw JSON and cheaper in tokens.

To achieve this we can use the http.response.select

object to JQ

style select only the fields we need

http:
  method: GET
  path: "/price/XAU"
  response:
    select:
      currency: "{currency}"
      price: "{price}"

Here we use "{currency}"

variables to return fields from the JSON response which will return

currency: USD
price: 4394.299805

A tool call result in MCP follows this structure:

{
  "content": [
    {
      "type": "text",
      "text": "
        currency: USD
        price: 4394.299805
      "
    }
  ],
  "isError": false
}

Other available response formatting functions:

{path.to.field}

— plain field extraction{arrayField[].path}

— map a sub-path over every element of an array{{literal}}

— escape hatch for a literal string that looks like a path (e.g. {{id}} → the string "{id}"){truncate(path, maxLen)}

— truncate a string (or every string in an array-valued path) to maxLen

mcp-proxy

can be protected with an API key and setting MCP_PROXY_AUTH_MODE

and MCP_PROXY_BEARER_TOKEN

export MCP_PROXY_AUTH_MODE=bearer
openssl rand -base64 32
export MCP_PROXY_BEARER_TOKEN=****

To connect to mcp-proxy

you will need to pass header

Authorization: Bearer ***
go build ./...
go vet ./...
go test ./...
── more in #developer-tools 4 stories · sorted by recency
── more on @mcp-proxy 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-mcp-proxy-ad…] indexed:0 read:7min 2026-08-18 ·