Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Id Jag

FreeNot checked

Go MCP server implementing Athenz ID-JAG least-privilege token exchange (port of id-jag-the-hard-way's api_server/mcp)

GitHubEmbed

About

Go MCP server implementing Athenz ID-JAG least-privilege token exchange (port of id-jag-the-hard-way's api_server/mcp)

README

Go CI

中文版說明

A Go MCP (Model Context Protocol) server that implements the ID-JAG token-exchange pattern from athenz-community/id-jag-the-hard-way.

It is a Go reimplementation of that tutorial's api_server/mcp/ (originally TypeScript), built with the official modelcontextprotocol/go-sdk. It exposes 3 tools that let an AI agent read/write documents through an upstream API, while enforcing least-privilege access via Athenz: every tool call exchanges the caller's Access Token for a new one narrowly scoped to just that tool's Athenz role, over mTLS, before forwarding the request upstream.

How it works

AI client (already holds an Athenz Access Token)
   │  Authorization: Bearer <AT>
   ▼
id-jag-mcp  ── tools/call ──▶  exchange <AT> for a scope-narrowed AT via Athenz ZTS (mTLS)
   │
   ▼
upstream API server  (called with the narrowed AT)

Each tool declares the exact Athenz role scope it needs. id-jag-mcp never forwards the token it received from the client — it always exchanges it first, so the upstream server only ever sees a token that can do the one thing that tool is allowed to do.

For the full protocol background (ID-JAG, RFC 8693 token exchange, Athenz ZTS/ZMS), see the design doc and the upstream tutorial.

Tools

Tool Method Upstream endpoint Required Athenz scope
get_k8s_docs GET /api/docs api:role.docs-getter
delete_k8s_doc DELETE /api/docs/{doc_id} api:role.docs-deleter
post_k8s_doc POST /api/docs api:role.docs-poster

delete_k8s_doc takes a doc_id integer argument. post_k8s_doc takes name and content string arguments.

Each tool is also reachable as a plain REST route (same auth/exchange logic, no MCP client needed) — see REST shortcuts below.

Requirements

  • Go 1.25+
  • An Athenz ZTS endpoint reachable over mTLS
  • An mTLS client certificate/key for this service's own Athenz identity, and the CA that signs ZTS's server certificate
  • An upstream REST API server (the "docs API") that accepts Athenz Access Tokens

This server does not run Athenz, Keycloak, or the upstream API itself — it only bridges between an MCP client and that infrastructure. Follow the id-jag-the-hard-way tutorial to stand up ZTS, ZMS, and the upstream API if you don't already have them.

Installation

Clone the repo and build the binary (or run it directly with go run, see Running it below):

git clone https://github.com/kkdai/id-jag-mcp.git
cd id-jag-mcp
go build -o id-jag-mcp ./cmd/id-jag-mcp

This only builds the binary — it does not install/configure Athenz, Keycloak, or the upstream API server (see Requirements above).

Configuration

Configuration is environment variables only — no config file, no flags.

Env var Default Purpose
PORT 8101 Port to listen on
UPSTREAM_BASE_URL http://localhost:14443 Base URL of the upstream API server
PUBLIC_BASE_URL http://localhost:{PORT} Display-only, shown in startup logs
AUTHORIZATION_SERVER_URL https://athenz-zts-server.athenz:4443/zts/v1 Athenz ZTS base URL
MCP_CERT_DIR ./certs Base directory for the default cert paths below
ATHENZ_CERT_PATH {MCP_CERT_DIR}/api-mcp.crt This service's mTLS client certificate
ATHENZ_KEY_PATH {MCP_CERT_DIR}/api-mcp.key This service's mTLS client key
ATHENZ_CA_PATH {MCP_CERT_DIR}/ca.crt CA to trust when connecting to ZTS
LOGGER_ENABLE_HEADERS false Log request headers (Authorization is always redacted)
LOGGER_ENABLE_BODY true Log request bodies
DANGEROUSLY_SHOW_RAW_ACCESS_TOKEN false Log full tokens instead of truncated — local debugging only

Certificates are read once at startup. A missing or invalid certificate is a fatal startup error — there is no hot-reload; restart the process after rotating certs.

mTLS note: the ZTS connection is made with InsecureSkipVerify: true (it does not validate ZTS's server certificate against the configured CA), matching the original tutorial's local-development setup. This is intentional for a local/tutorial ZTS with a self-signed certificate — do not point this at a production ZTS without changing that.

Running it

# put your mTLS cert/key and the ZTS CA here (or point MCP_CERT_DIR elsewhere)
mkdir -p certs
cp /path/to/api-mcp.crt /path/to/api-mcp.key /path/to/ca.crt certs/

export UPSTREAM_BASE_URL=http://localhost:14443
export AUTHORIZATION_SERVER_URL=https://athenz-zts-server.athenz:4443/zts/v1

go run ./cmd/id-jag-mcp

On startup it logs the port, upstream URL, and public base URL, then serves:

  • GET /health — plain health check, returns {"status":"ok"}
  • /mcp — the MCP endpoint (Streamable HTTP transport). Point any MCP client (Claude Code, Claude Desktop, VS Code, etc.) at http://localhost:{PORT}/mcp, with the caller's Athenz Access Token as Authorization: Bearer <token>.

Build a standalone binary instead:

go build -o id-jag-mcp ./cmd/id-jag-mcp
./id-jag-mcp

REST shortcuts

For testing without an MCP client, the same 3 operations are also plain REST routes on the same port, sharing the same token-exchange logic:

curl -H "Authorization: Bearer $AT" http://localhost:8101/api/docs

curl -X DELETE -H "Authorization: Bearer $AT" http://localhost:8101/api/docs/5

curl -X POST -H "Authorization: Bearer $AT" -H "Content-Type: application/json" \
  -d '{"name":"doc1","content":"hello"}' \
  http://localhost:8101/api/docs

A missing/malformed Authorization header returns 500 with a JSON {"error": "..."} body (matching the original tutorial's behavior).

Testing

go build ./...
go vet ./...
go test ./...

Run a single package's tests with -v for verbose output, e.g. go test ./internal/athenz/... -v.

Tests use httptest throughout — they don't require a live Athenz/Keycloak stack or real mTLS certificates.

Project layout

cmd/id-jag-mcp/       entrypoint: loads config, wires everything, starts the HTTP server
internal/config/      environment-variable configuration loading
internal/athenz/      mTLS client + Athenz ZTS RFC 8693 token exchange
internal/tools/       tool input types + shared upstream-forwarding logic
internal/server/      MCP tool registration (official SDK) + REST shortcuts + logging

See docs/superpowers/specs/ for the full design rationale and docs/superpowers/plans/ for the implementation plan this was built from.

Not included (yet)

  • Docker packaging / Kubernetes manifests
  • mTLS certificate hot-reload
  • A generic/pluggable tool registry (the 3 tools above are the only ones implemented)
  • Retry logic on Athenz ZTS token-exchange failures — failures are surfaced immediately

Acknowledgments

Ports the ID-JAG token-exchange architecture from athenz-community/id-jag-the-hard-way, which is itself inspired by kelseyhightower/kubernetes-the-hard-way.

License

Apache License 2.0

from github.com/kkdai/id-jag-mcp

Installing Id Jag

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/kkdai/id-jag-mcp

FAQ

Is Id Jag MCP free?

Yes, Id Jag MCP is free — one-click install via Unyly at no cost.

Does Id Jag need an API key?

No, Id Jag runs without API keys or environment variables.

Is Id Jag hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Id Jag in Claude Desktop, Claude Code or Cursor?

Open Id Jag on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Id Jag with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs