Skip to main content
Glama
brasspage

gpt-action-mcp-demo

by brasspage
README.md
# A custom GPT action, rebuilt as a remote MCP server

The worked example for the DEV tutorial "Rebuild a custom GPT action as a remote MCP server". Code: https://github.com/brasspage/gpt-action-to-mcp (MIT).

`openapi.json` is a custom GPT action schema for the free [Frankfurter](https://frankfurter.dev/) exchange rate API (no key, commercial use allowed). The Worker turns each operation into one MCP tool and serves them over stateless Streamable HTTP on Cloudflare Workers, with the official MCP TypeScript SDK.

Live demo: `https://gpt-action-mcp-demo.brasspage.workers.dev/mcp` (60 requests a minute for each IP address).

| Action operation  | MCP tool               |
| ----------------- | ---------------------- |
| `GET /currencies` | `list_currencies`      |
| `GET /latest`     | `get_latest_rates`     |
| `GET /{date}`     | `get_historical_rates` |

## Files

- `openapi.json`: the action schema, as you would paste it into the GPT editor.
- `src/tools.js`: schema to tools (one per operation), argument checks, and the call to the API.
- `src/index.js`: the Worker. A fresh SDK server and transport for each POST to `/mcp`.
- `wrangler.toml`: Worker name, workers.dev URL, and the rate limit.
- `tools.test.js`: the mapping and the argument checks, with a fake fetch (no network).
- `check.mjs`: an SDK client that lists the tools and calls each one.
- `output.txt`: the real output of `check.mjs` and the MCP Inspector against the live demo.

## Run it

```sh
npm install
node --test --test-concurrency=1 tools.test.js
npx wrangler dev                         # local, on http://localhost:8787
node check.mjs http://localhost:8787/mcp
npx wrangler login && npx wrangler deploy
npx @modelcontextprotocol/inspector --cli https://<your worker>/mcp --method tools/list
```

## Limits

- The mapper handles GET operations with path and query parameters. A POST body or a header parameter throws when the Worker loads; map those by hand.
- No sign-in. For an API key, keep it as a Worker secret (`npx wrangler secret put API_KEY`) and add it to the request in `callTool`, never to tool arguments. For per-user data, use OAuth.
- Checked with the SDK client and the MCP Inspector, not inside a plugin.