One MCP endpoint for every agent.
Jackfield puts your MCP servers behind a single URL per agent. One file decides who reaches what, and the whole protocol goes through — elicitation forms, sampling, progress on long calls, sessions that survive a restart.
What it does that a plain proxy does not
servers can talk back
Elicitation, sampling and roots are forwarded to the agent that made the call — and refused with a clear message when the agent cannot answer, instead of hanging.
long calls stay alive
Upstream progress is forwarded on the originating call's own stream, which is the detail that makes clients actually see it.
restarts are invisible
An unknown session gets 404, the protocol's signal to open a new one, so clients recover without being touched.
per-agent surface
Profiles decide which servers and tools each key sees, enforced on tools/list and again on every call.
approvals that mean something
Per-tool allow / ask / deny. ask goes to the user, in the client or out of band — never to the agent itself.
the token bill, itemised
jackfield tools --by-source prints definitions and rough token cost per server, so you can see what a turn actually costs.
credentials stay here
OAuth upstream with discovery, PKCE and refresh. Tokens live in one file per server at mode 0600; no agent ever sees one.
no port to open
An executor can dial the gateway instead of listening, so the machine doing the real work needs no inbound rule at all.
01Quickstart
The repository ships a working example: two MCP servers, three clients, nothing to install and no network needed.
git clone https://github.com/PabloPC05/jackfield && cd jackfield npm install && npm run build # does everything connect? node packages/cli/dist/bin.js check -c examples/jackfield.yaml # what does each client load, and what does it cost? node packages/cli/dist/bin.js tools -c examples/jackfield.yaml --by-source # what to paste into each agent node packages/cli/dist/bin.js routes -c examples/jackfield.yaml node packages/cli/dist/bin.js serve -c examples/jackfield.yaml
Point an agent at it
# any client that can send a header https://gateway.example.com/mcp Authorization: Bearer <key> # clients that cannot send headers (the key is then part of the secret) https://gateway.example.com/k/<key>/mcp
See the cost before you pay it
$ jackfield tools --by-source
source tools definitions tokens (rough)
-------- ----- ----------- --------------
porkbun 120 191 KB ~48,900
github 49 137 KB ~35,100
coolify 77 71 KB ~18,200
memoria 15 14 KB ~3,600
505 tools, 713 KB of definitions, about 182,000 tokens
for "claude-code" if the client loads them all.
That is the number expose: search and transport: cli exist to bring down.
02jackfield.yaml
The whole gateway is one file. No database, no admin panel writing state behind
your back. Secrets are referenced, not embedded, and anything resolved from
${env:…} or ${file:…} is registered for redaction so it
cannot reach a log line.
upstreams:
github:
transport: http
url: https://api.example.com/mcp
token: ${file:secrets/github.token}
notes:
transport: stdio # a child process
command: npx
args: ["-y", "some-mcp-server"]
porkbun:
transport: cli # 120 definitions → 1 tool
commands:
porkbun:
description: "Porkbun CLI. Ask it for --help first."
executable: porkbun
allowedSubcommands: ["domain", "dns"]
vault:
transport: http
url: https://vault.example.com/mcp
oauth: # jackfield login vault, once
scope: "read write"
browser:
transport: executor # on a machine of yours
executor: home
executors:
home:
attach: true # it dials us; no inbound port
token: ${file:secrets/executor.token}
permissions: # a floor every client inherits
- tools: ["*.delete_*"]
action: ask # asks the user, not the agent
via: link # on a page, even if nobody is watching
profiles:
core: { upstreams: ["tag:personal"] }
full: { extends: [core], upstreams: ["*"] }
clients:
laptop:
key: ${file:secrets/key-laptop}
profile: full
web-agent:
key: ${file:secrets/key-web}
keyInPath: true # /k/<key>/mcp
profile: core
thrifty:
key: ${file:secrets/key-thrifty}
profile: full
expose: search # find_tool + call_tool
03Protocol behaviour
The cases that decide whether a gateway works in practice, and what Jackfield does with each.
| Situation | What happens |
|---|---|
elicitation/create from a server | Forwarded to the agent that made the call. If the agent declared no elicitation capability, the server is told so — it does not wait. |
sampling/createMessage | Same path. Refused clearly when the agent cannot answer. |
roots/list | Forwarded, or an empty list when the agent has none. |
| Progress on a long call | Forwarded with relatedRequestId, so it arrives on the call's own stream rather than the standalone one most clients never open. |
| Idle SSE stream | Keep-alive every 15 s by default. Some clients cut a stream that goes quiet for five minutes. |
Unknown Mcp-Session-Id | 404, not 400. Clients then open a new session on their own; answered 400 they retry the dead one forever. |
| Two servers with the same tool name | Prefixed by source. With prefix: never a collision gets a short hash suffix — a tool is never silently dropped. |
| Tool name over 128 characters | Trimmed with a hash, so two long names cannot collapse into one. |
| An upstream is down | Left out of the catalogue and named in the gateway's instructions, so the agent is told what is missing. failOpen: false to fail loudly instead. |
| A reply larger than the limit | Trimmed largest-block-first, with a note saying how much went and to ask for less. |
tools/list_changed upstream | Catalogue invalidated and the agent notified. |
| A tool the catalogue has not seen | Refreshed once before failing, and the error names the closest matches. |
| An upstream needs OAuth | Stored tokens are used and refreshed. A login is never started by the gateway on its own: that is an operator's command. |
A rule says ask, nobody is watching | A one-time link goes to your notifier and the call waits, with progress, until a person decides. The agent never sees the link. |
04Features
Everything the gateway does. Filter or search; open a row for the detail.
05Deploy
The HTTP layer is written against Web Request/Response, so
the gateway runs wherever you want it. What a given host cannot do — child processes,
a browser, a shell — a Jackfield executor on a machine you own does, and the core
reaches it with transport: executor.
| Target | Entry point | Notes |
|---|---|---|
| Process | jackfield serve | Everything works. The default. |
| systemd | deploy/systemd | Hardened unit, one read-only secrets directory. |
| Docker | deploy/docker | Multi-stage image, compose file, health check. |
| NixOS | services.jackfield | Module with options; secrets stay out of the world-readable store. |
| Serverless | createWebHandler | Five lines of adapter. Needs stateless: true: no sessions, no forms, no child processes. |
| Your machine | jackfield-executor | Serves the upstreams a stateless core cannot host, behind one token. |
| …behind NAT | --core <url> | The executor dials the gateway and keeps the stream open. Nothing has to reach it. |
06Compared
What each project in this space is built around, from its own documentation (November 2026). Pick by what you need, not by a feature count.
| Project | Built around | Pick it over Jackfield when |
|---|---|---|
| Jackfield | A declarative file, per-agent surfaces, and forwarding the whole protocol — forms, sampling, progress, sessions — plus a split core/executor so hosting never decides what your tools are. | — |
| agentgateway | A general-purpose agent proxy under a foundation: MCP alongside A2A and LLM routing, configured by file, with CEL rules per client. | You want one proxy for everything and a larger project behind it. |
| ToolHive | Running each MCP server in its own container, from a registry, with a tool-search optimiser on top. | Per-server isolation matters more than protocol fidelity. |
| ContextForge | A full gateway with a web admin UI, virtual servers, upstream OAuth and federation. | You want to add and wire servers by clicking. |
| MetaMCP | Namespaces of servers with their own URLs, configured entirely from its UI. | A UI is the interface you want, and Postgres is fine. |
| Obot | Teams: a catalogue, central OAuth, auditing, Kubernetes. | You are several people with compliance needs. |
| Docker MCP Gateway | Server profiles and adding them mid-session, inside Docker Desktop. | Docker Desktop is already your world. |
| MCPHub · Bifrost · MCPJungle | Hub-style discovery, tool groups per key, high-throughput proxying. | One of those emphases is yours. |
Status
Jackfield is new: written in November 2026, covered by its test suite, not yet
battle-tested over months. Interfaces may change before 1.0. It deliberately has no
container isolation, and its web view (/status) only reads — the file is
the state, so a change stays a diff you can review.