AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Ubag Protocol

mcp-mohameduk-ubag-protocol · by mohameduk

Open reference implementation: agent identity + routing at the web edge (Ed25519 + ES256/JWKS). Python & Node.

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add mcp-mohameduk-ubag-protocol

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-mohameduk-ubag-protocol)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Ubag Protocol? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

UBAG Web Layer

Universal Behavioral Authorization Gateway — Web Layer

[](https://github.com/mohameduk/Ubag_protocol/actions/workflows/ci.yml) [](https://pypi.org/project/ubag/) [](https://www.npmjs.com/package/ubag-web) [](LICENSE) [](#project-status)

Agent identity and routing at the web edge. The open reference implementation.

When an autonomous agent visits a website, UBAG verifies who it is and routes accordingly: humans to your normal site, credentialed agents to clean JSON-LD, unknown bots to a cryptographic challenge. MCP standardized how agents talk to tools — it left a gap at the web layer: agent identity when no human is in the loop. This is that layer.

> Where this fits. UBAG has two layers. The Gateway governs what an agent > is allowed to do against credentialed systems and MCP tools; this Web Layer > governs how an agent reaches a website. This repo is the open, MIT-licensed > reference implementation of the Web Layer — and of an open mechanism any site can > adopt (ubag.json discovery + a sign-the-nonce challenge + X-UBAG-Credential), > not a product you have to buy into.

> Status: early but real. Two published SDKs — pip install ubag (Python) and > npm install ubag-web (Node) — sharing a cross-verifiable wire format. > See [Project status](#project-status) for exactly what works today vs. what's planned.


The Problem

When an autonomous MCP agent visits a website today:

  • The website has no way to verify who it is
  • It scrapes raw HTML like any bot from 2005
  • Unknown agents get blocked by Cloudflare — including legitimate ones
  • No human is in the loop to click "Allow"

MCP's OAuth 2.1 spec is built for human-delegated auth (browser → redirect → user clicks Allow). It does not cover autonomous agent identity — the credential an agent carries when no human is in the loop.

UBAG fills that gap.


How It Works

Every request to a UBAG-enabled site is routed through a 3-branch matrix:

flowchart TD
    R[Incoming request] --> M{UBAG middleware}
    M -->|Valid X-UBAG-Credential| B["Branch B — Clean JSON-LDstructured data, no HTML parsing"]
    M -->|Looks human| A["Branch A — Transparent proxyorigin server, untouched"]
    M -->|Unknown agent| C["Branch C — Sandbox + challengesign a nonce with your Ed25519 key"]
    C -->|signature verified → credential issued| B

Branch B is the key insight. Instead of an agent crawling 50 pages to understand a business, UBAG serves one structured JSON-LD response — products, prices, policies, contacts. One request, no HTML parsing, far fewer extraction errors.


Security Model

UBAG is asymmetric — there are no shared secrets in the identity path:

  • Agent identity = an Ed25519 keypair. An agent's identity is the SHA-256 thumbprint of its public key (ubag:…). To get in, the agent signs the site's nonce with its private key; the site verifies with the public key. Only the holder of the key can pass — knowing a shared secret never establishes who an agent is.
  • Credentials = ES256 JWTs signed by an issuer's EC P-256 private key and verifiable by any site with the issuer's public key — auto-served as JWKS at /.well-known/jwks.json. No site needs a secret to validate a credential — the same model as OAuth / OIDC, which is what lets one credential work across independent sites.
  • Proof-of-possession ready. Each credential binds to the agent's key via the cnf claim, so a verifier can require the bearer to prove it still holds the matching private key.
  • One server-side HMAC, used only so the site can confirm it issued a given nonce without storing state (the server signing to itself). It is not part of the identity proof.

The Python and Node SDKs share identical wire formats (raw Ed25519 + ES256), so a signature or credential produced by one verifies byte-for-byte in the other. This interop is covered by tests in both packages.


Install

# Python (FastAPI / Starlette)
pip install "ubag[fastapi]"

# Node (Express)
npm install ubag-web

> The npm package is ubag-web (npm reserves the bare ubag); the Python > package is ubag. Same protocol, identical wire format.

Or from source: git clone the repo, then pip install -e ".[fastapi]" in ubag-python/ and npm install in ubag-node/.


Quick Start — Python (FastAPI)

from fastapi import FastAPI
from ubag import UBAGMiddleware, generate_issuer_keypair

# Your site is its own credential issuer. Generate once and persist these
# (or run verify-only by passing issuer_public_key alone).
ISSUER_PRIVATE, ISSUER_PUBLIC = generate_issuer_keypair()   # EC P-256 (ES256)

app = FastAPI()
app.add_middleware(
    UBAGMiddleware,
    origin="https://yoursite.com",   # Branch A proxy target
    issuer_key=ISSUER_PRIVATE,       # mints + verifies agent credentials
    server_secret="a-random-32+char-string-for-nonce-stamping",
    site_meta={"name": "My Store", "type": "Store", "description": "We sell widgets"},
)

issuer_key can also come from the UBAG_ISSUER_KEY env var; a verify-only deployment can pass issuer_public_key (or UBAG_ISSUER_PUBLIC) alone.

Your site now:

  • ✅ Serves clean JSON-LD to credentialed MCP agents (Branch B)
  • ✅ Proxies humans transparently to your origin (Branch A)
  • ✅ Sandboxes unknown agents with an Ed25519 nonce challenge (Branch C)
  • ✅ Serves yoursite.com/.well-known/ubag.json for agent discovery
  • ✅ Calls your optional audit_fn(branch, request, response) on every visit

Quick Start — Node (Express)

const express = require('express');
const { ubag, generateIssuerKeypair } = require('ubag-web');

const { privateKey: ISSUER_PRIVATE } = generateIssuerKeypair();  // EC P-256 (ES256)

const app = express();
app.use(ubag({
  origin: 'https://yoursite.com',
  issuerKey: ISSUER_PRIVATE,
  serverSecret: 'a-random-32+char-string-for-nonce-stamping',
  siteMeta: { name: 'My Store', type: 'Store', description: 'We sell widgets' },
}));

See it work (60 seconds)

Runnable end-to-end demos spin up a UBAG site in-process and walk one agent through the whole handshake — blocked → challenged → signs the nonce → credentialed → served JSON-LD — then print the JWKS another site would use to verify the credential:

# Python
cd ubag-python && pip install -e ".[fastapi]" && python ../examples/demo.py

# Node
npm install --prefix ubag-node && node examples/demo.js

For MCP Agent Developers

If you're building an MCP agent that visits websites, get a UBAG credential:

from ubag import AgentCredential

# Your agent's identity IS its Ed25519 keypair. Generate once; persist agent.export().
agent = AgentCredential.generate(owner="you@email.com")

# When a UBAG site challenges you (HTTP 429), sign the nonce and post it back:
#   challenge = resp.json()["ubag_challenge"]   # nested in the 429 body: nonce, timestamp, stamp
#   solution  = agent.solve_challenge(challenge)   # signs the nonce with your private key
#   r = httpx.post(f"{site}/ubag/verify", json=solution)
#   agent.set_credential(r.json()["credential"])

# Once credentialed, it travels with every request:
headers = agent.headers()
# {"X-UBAG-Credential": "eyJ..."}

UBAG-enabled sites recognize your agent and serve structured data instead of HTML. Your agent gets better data; the website owner gets visibility and control. The Node SDK exposes the same AgentCredential API.


Discovery: /.well-known/ubag.json

Every UBAG-enabled site serves a discovery document at /.well-known/ubag.json (with /agents.json kept as a legacy alias). It's deliberately not named agents.json — that filename is already claimed by unrelated specs (Wildcard's OpenAPI-style agents.json, Google/Microsoft/HF's ARD, etc.); ubag.json keeps UBAG's identity/routing document collision-free and unambiguous.

{
  "ubag_version": "1.0",
  "host": "yoursite.com",
  "credential_endpoint": "https://yoursite.com/ubag/verify",
  "branches": {
    "B-AGENT":   { "description": "Authorized MCP agents — clean JSON-LD",
                   "requires": "X-UBAG-Credential header with valid JWT",
                   "content_type": "application/ld+json" },
    "A-HUMAN":   { "description": "Human browsers — transparently proxied to origin",
                   "requires": "None" },
    "C-SANDBOX": { "description": "Unknown agents — Ed25519 nonce challenge",
                   "requires": "None — solve challenge to get credentialed",
                   "challenge_endpoint": "/ubag/verify" }
  },
  "discovery": {
    "ubag_json": "https://yoursite.com/.well-known/ubag.json",
    "verify_endpoint": "https://yoursite.com/ubag/verify",
    "jwks_endpoint": "https://yoursite.com/.well-known/jwks.json"
  }
}

Like robots.txt, but machine-actionable — agents fetch it before making requests.


Why Not Just Use Cloudflare?

| | Cloudflare | AWS WAF | UBAG | |---|---|---|---| | Blocks unknown bots | ✅ | ✅ | Challenges them instead | | Structured data for agents | Markdown (AI features) | ❌ | ✅ JSON-LD | | Cryptographic agent identity / credential | ❌ | ❌ | ✅ | | Autonomous agent support (no browser) | ❌ | ❌ | ✅ | | Open source | ❌ | ❌ | ✅ | | Vendor lock-in | Cloudflare | AWS | None |

Cloudflare and AWS block or filter bots. UBAG graduates them: an unknown agent can solve the challenge, get credentialed, and become authorized — no legitimate agent is permanently blocked. (Competitor columns reflect general capabilities at time of writing; verify against current vendor docs.)


MCP Integration

UBAG complements MCP, it doesn't replace it:

  • MCP OAuth 2.1 — human-delegated auth (user clicks Allow in a browser)
  • UBAG credential — autonomous agent identity (no human in the loop)
MCP Agent
    │
    ├── Talking to MCP servers?  ──► MCP OAuth 2.1
    │
    └── Visiting websites?  ────────► UBAG credential

A UBAG credential is issued once and verified in-process by the site's middleware — no redirect, no browser flow — and works on any UBAG-enabled site.


Repository Layout

ubag-python/    Python middleware — FastAPI / Starlette today (v0.2.0)
ubag-node/      Node middleware — Express today (v0.2.0)

Both packages implement the full protocol (routing, credentials, challenge, keys, ubag.json) and share a cross-verifiable wire format.


Run the tests

# Python
cd ubag-python && pip install -e ".[dev]" && pytest

# Node
cd ubag-node && npm install && npm test

Project status

Working today

  • [x] Branch A — human transparent proxy
  • [x] Branch B — agent JSON-LD structured data
  • [x] Branch C — sandbox + Ed25519 nonce challenge
  • [x] Asymmetric crypto — Ed25519 agent identity + ES256/JWKS credentials, no shared secrets
  • [x] ubag.json discovery — served on every UBAG site (alias: /agents.json)
  • [x] Audit hook — audit_fn callback on every request
  • [x] Python SDK (FastAPI/Starlette) + Node SDK (Express), cross-SDK verified
  • [x] Published — pip install ubag (PyPI) and npm install ubag-web (npm)

Planned / not yet built

  • [ ] Django / Flask / Next.js middleware adapters
  • [ ] Hosted credential registry / issuer at ubagprotocol.com/credential
  • [ ] WordPress plugin
  • [ ] Docker reference deployment (one-command self-host)
  • [ ] Formal spec docs (docs/spec/…)
  • [ ] Payment / revenue-share layer for site owners

Contributing

PRs welcome. The goal isn't to make everyone adopt "UBAG" — it's to make the mechanism easy to adopt: ubag.json discovery, a sign-the-nonce challenge, and a portable X-UBAG-Credential that any site can verify without a shared secret. Open, verifiable, and not owned by any cloud provider. UBAG is just the reference implementation.


Contact

Built by Mohamed Ben Hadj Hmida ubagprotocol.com · github.com/mohameduk/Ubagprotocol

MIT licensed.

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.