jackfield

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.

MITTypeScript, Node ≥ 223 runtime dependenciesno database
$ npx @jackfield/cli init
$ npx @jackfield/cli serve
Quickstart Source

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

Full reference →

03Protocol behaviour

The cases that decide whether a gateway works in practice, and what Jackfield does with each.

SituationWhat happens
elicitation/create from a serverForwarded to the agent that made the call. If the agent declared no elicitation capability, the server is told so — it does not wait.
sampling/createMessageSame path. Refused clearly when the agent cannot answer.
roots/listForwarded, or an empty list when the agent has none.
Progress on a long callForwarded with relatedRequestId, so it arrives on the call's own stream rather than the standalone one most clients never open.
Idle SSE streamKeep-alive every 15 s by default. Some clients cut a stream that goes quiet for five minutes.
Unknown Mcp-Session-Id404, 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 namePrefixed by source. With prefix: never a collision gets a short hash suffix — a tool is never silently dropped.
Tool name over 128 charactersTrimmed with a hash, so two long names cannot collapse into one.
An upstream is downLeft 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 limitTrimmed largest-block-first, with a note saying how much went and to ask for less.
tools/list_changed upstreamCatalogue invalidated and the agent notified.
A tool the catalogue has not seenRefreshed once before failing, and the error names the closest matches.
An upstream needs OAuthStored 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 watchingA 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.

TargetEntry pointNotes
Processjackfield serveEverything works. The default.
systemddeploy/systemdHardened unit, one read-only secrets directory.
Dockerdeploy/dockerMulti-stage image, compose file, health check.
NixOSservices.jackfieldModule with options; secrets stay out of the world-readable store.
ServerlesscreateWebHandlerFive lines of adapter. Needs stateless: true: no sessions, no forms, no child processes.
Your machinejackfield-executorServes 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.

ProjectBuilt aroundPick it over Jackfield when
JackfieldA 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.—
agentgatewayA 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.
ToolHiveRunning 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.
ContextForgeA full gateway with a web admin UI, virtual servers, upstream OAuth and federation.You want to add and wire servers by clicking.
MetaMCPNamespaces of servers with their own URLs, configured entirely from its UI.A UI is the interface you want, and Postgres is fine.
ObotTeams: a catalogue, central OAuth, auditing, Kubernetes.You are several people with compliance needs.
Docker MCP GatewayServer profiles and adding them mid-session, inside Docker Desktop.Docker Desktop is already your world.
MCPHub · Bifrost · MCPJungleHub-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.