uta-shield

io.github.eddyflores100-langPublished by GitHub account eddyflores100-langv0.3.0Updated Nov 5, 2026

Provider-side MCP firewall: identity, threat-intel, barter, rate limits, DLP, audit.

Overview

AI-generated overview

A provider-side firewall and threat-intel toolkit that wraps your own MCP server with agent identity, ACLs, rate limits, DLP and audit, plus a PhishGuard IOC…

What you can do
  • Screen indicators and text offline against a bundled phishing campaign feed, with scriptable exit codes
  • Run a PhishGuard MCP server exposing IOC lookup, URL checks, text screening, campaign listing and feed info
  • Wrap an existing MCP server behind a proxy that verifies HMAC-signed agent identity and enforces per-tool ACLs
  • Apply two-tier rate limits, inbound threat-intel blocking, outbound redact/block/log policy and DLP masking of secrets
  • Meter calls and write a JSONL audit trail of decisions, readable through an admin-authenticated endpoint
When to use it
Use it if you operate an MCP server or SaaS endpoint exposed to anonymous agents and want identity checks, per-tool authorization, rate limits, secret masking and an audit trail without changing your server code. Also useful when an assistant needs to check domains, URLs or text against a known phishing campaign.
Requirements
Runs locally as a Node.js 18 or newer package (npm uta-shield) over stdio; the firewall mode needs an upstream MCP server URL, a keys file of agent secrets, and optionally an admin token and ACL file. No accounts or API keys are declared for the bundled feed.
Before you install
The proxy handles agent shared secrets and an admin token, so keep the keys file and admin token out of version control. It writes audit logs and can block or redact traffic, so misconfiguration affects live clients; outbound policy log passes indicators through. It does no TLS termination, so run it behind your own reverse proxy. The bundled feed covers one campaign and some DLP patterns are heuristic.

Installation

In SourceWeft

  1. Open uta-shield in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

uta-shield

Provider-side firewall for MCP servers + PhishGuard threat-intel MCP server — in one zero-dependency package.

Landing / docs: https://wall.utalabs.tech

Every gateway in the MCP security category (Lasso, Mintive, Obot, agentgateway, Cequence) protects the consumer — the company whose employees connect agents to third-party servers. Nothing protects the operator — the SaaS that publishes its MCP server and exposes its database to every anonymous agent on the internet.

uta-shield is the mirror side: you wrap YOUR MCP server and gain verified identity, per-tool authorization, threat-intel blocking (in AND out), two-tier rate limits, DLP masking, proxy hardening, per-call metering and a JSONL audit trail — without touching your server code.

Backed by a real, documented campaign: the taikotalent phishing case (fake-recruiter malware against open-source developers, domain taken down in ~12h, C2 infra tracked). The bundled feed ships its 23 verified IOCs.

Quickstart

bash
# 1. Screen any indicator offline (exit code 1 on match → scriptable)npx uta-shield check gaganata.ink          # → MATCH (C2, campaign taikotalent-2026-10)npx uta-shield check example.com           # → NO_MATCH
# 2. Screen text (email body, web content, tool output)echo "verify at https://taikotalent.xyz/verify_m" | npx uta-shield screen
# 3. Run the PhishGuard MCP server (agents get lookup_ioc / check_url / screen_text)npx uta-shield mcp
# 4. THE FIREWALL: wrap your MCP server (fail-closed: keys or explicit opt-out)cat > keys.json <<'EOF'{ "recruiter-agent": "generate-a-long-random-secret", "auditor-agent": "another-secret" }EOFnpx uta-shield wrap --upstream http://localhost:3001 --port 8787 --keys-file keys.json --admin-token "$(openssl rand -hex 24)"#    → agents now talk to :8787; your server stays at :3001#    GET /health · GET /pricing (x402-ready) · GET /audit (admin-only: x-uta-admin header)
# 5. See it all livenode demo/run-demo.mjs

Verified identity (v0.2.0, ACL strict mode v0.2.2)

Identity is no longer declared-only. Agents present three headers:

x-agent-name:      recruiter-agentx-agent-timestamp: 1761196800000            (ms; ±5 min window)x-agent-signature: <hex>                    HMAC_SHA256(secret, `${agent}|${ts}|${sha256(rawBody)}`)
  • Valid signature → ALLOW_VERIFIED (per-agent rate buckets, ACL enforcement, metering).
  • Presented-but-invalid credentials → REJECT_AUTH (401). Fail-closed, always.
  • No credentials → REJECT_ANON (401) unless the operator passes --allow-anonymous.
  • Declared names (no signature) are labels only, verified:false — they earn no per-agent rate bucket.
  • Per-tool ACL (--acl-file): by default, agents absent from the map are unrestricted. For allow-list semantics, add --acl-deny-unlisted — unlisted agents get 403 REJECT_FORBIDDEN even with a valid signature. The CLI refuses --acl-deny-unlisted without an ACL file (with no map, deny-unlisted would reject every agent). /health discloses identity.acl_deny_unlisted.

Signing a request (client side, any language):

js
import { createHmac, createHash } from "node:crypto";const ts = Date.now();const bodyHash = createHash("sha256").update(rawBody).digest("hex");const sig = createHmac("sha256", secret).update(`${agent}|${ts}|${bodyHash}`).digest("hex");

The 8 layers

#LayerStatus in v0.2.2
1Agent identity: HMAC-SHA256 verified (--keys-file, anti-replay window, fail-closed) + per-tool ACL (--acl-file) + declared/anonymous fallbackSHIPPED
2Reputation score per agenthook (identity verdicts recorded; scoring = roadmap)
3Threat-intel in/out: inbound args blocked; outbound responses under explicit policy (--outbound-policy block|redact|log, default redact)SHIPPED
4Rate limiting: two-tier — per-IP bucket (always) + per-agent bucket (verified only); free/paid tiersSHIPPED
5Billing: per-call metering + x402-ready pricing manifest (settlement delegated to operator payment layer)SHIPPED (metering)
6DLP: emails (plain + obfuscated), JWTs, AWS/GCP keys, GitHub PATs, Stripe keys, bearer tokens, PEM blocks, hex secrets, credit cards (Luhn-validated), optional base64 blobsSHIPPED
7Reverse drift alerts (notify clients if schema changes)roadmap
8Audit: JSONL per decision (indicator + campaign for triage, never raw args or secrets)SHIPPED

Honesty policy: this README marks exactly what ships. Layers 2 and 7 are design hooks — no inflated claims.

Proxy hardening (v0.2.1)

ControlDefaultFlag
Startup fail-closedrefuses to run without --keys-file or --allow-anonymous—
/audit authenticateddisabled (404) without a token; with one, requires x-uta-admin (timing-safe compare) — the trail holds IOCs and agent names, it is operator telemetry, never public--admin-token SECRET / UTA_SHIELD_ADMIN (≥16 chars)
Request body cap1 MiB (declared and streamed — header spoofing doesn't help)--max-body-bytes N
Upstream timeout30 s → 504, fixed message--upstream-timeout-ms N
MCP method allow-listinitialize, notifications/initialized, ping, tools/list, tools/call → everything else 403 METHOD_DENIED--allow-methods "a,b" | "*"
Safe errorsupstream failures return fixed messages; detail goes to the audit log, never the client—
Exact outbound verdictthe x-uta-shield response flag is the core verdict (OUT_OK/OUT_MASKED/OUT_REDACTED/OUTBOUND_INTEL) — no recomputation, policy=log reports OUTBOUND_INTEL, never a clean-looking OUT_OK—
Raw-body forwardingexact bytes upstream (signature integrity)—
Per-IP rate ceiling120 req/min, burst 60-- config via API
Header timeoutsheaders 10 s, request timeout = upstream + 5 sautomatic

Known limitations (honest scope)

  • Identity verification is HMAC-shared-secret (operator-issued keys). It authenticates agents to the operator; it is not a federated PKI. /.well-known/agent.json origin verification remains a supplementary hook.
  • policy=log records outbound IOCs but passes them through — it exists for operators who explicitly opt in; redact (default) or block are the safe choices.
  • Obfuscated-email and hex-secret DLP patterns are heuristic; false positives are possible (tune via dlp options).
  • No TLS termination — run behind your existing reverse proxy (Caddy/nginx/certbot).
  • The feed ships ONE documented campaign (23 IOCs). Feed data is CC-BY-4.0; extend it with your own campaigns.

Why zero dependencies?

A security product with 47 transitive dependencies is an attack surface, not a defense. uta-shield is ~1,600 lines of auditable Node.js ≥18 (ESM), including a hand-rolled MCP stdio implementation (newline-delimited JSON-RPC 2.0). npm pack ≈ 108 kB unpacked / 54 kB tarball.

The bundled feed

feed/iocs.json (schema pg/1) ships campaign taikotalent-2026-10: 23 IOCs across 9 types — phishing domain (down via registrar clientHold), live C2 domains behind Cloudflare, sender IP, fake recruiter email, stager URLs, sha256 of payloads, wildcard second-stage path (/var/tmp/.kdm.*), mutex name — with MITRE ATT&CK mapping (T1566.002, T1656, T1204.002, T1059.004, T1027, T1071.001, T1070.004, T1583.001) and per-IOC context. Every verdict carries this evidence so downstream agents can reason about WHY they were blocked.

Feed data CC-BY-4.0 from the public case repo. Update/extend it by adding a campaign object — schema validated by uta-shield verify-feed.

As an MCP server (for agents, not just operators)

Agents get the intel as tools: lookup_ioc(value), check_url(url) (feed + advisory heuristics), screen_text(text), list_campaigns(), feed_info(). Every response is DATA — the server never fetches or executes anything.

Commercial

OfferWhatPrice
Self-host (this package, license for use)all shipped layers, bundled feed — run on your own infra, commercial use allowedfree
S7 — managed feed subscriptionupdated campaign feed + webhook alerts + YARA rules$50/mes (free for first 100 ATC-holding agents)
S6 — Shield assessment + pilotoaudit of your MCP server + deployed piloto (identity+intel+audit) + report$1,000 setup + $100/mes/server

Contact: AliceLabs LLC — [email protected]

Test (reproducible)

bash
node --test test/*.test.mjs   # 85 tests: identity/HMAC, strict-ACL, outbound policies, DLP formats,                             # authenticated /audit, exact verdict propagation, streamed overflow,                             # MCP client lifecycle e2e, proxy limits,                              # two-tier rate limits, hardened proxy e2e (413/502/504/403/429),                              # feed matching, MCP protocolnpm run verify-feed           # feed schema validationnpm run demo                  # end-to-end with signed traffic

Independent execution: .github/workflows/tests.yml runs the full suite on GitHub's own runners (Node 20/22/24) on every push — the results are public and don't depend on this file's claims.

Test results for v0.2.2: see EVIDENCE.md (command, Node version, full runner summary).

Migration v0.1.0 → v0.2.0 (breaking)

  • wrap without --keys-file/UTA_SHIELD_KEYS/--allow-anonymous refuses to start (fail-closed default).
  • Rate limits for unverified callers are keyed by IP; declared agent names no longer create per-agent buckets.
  • gateOut default behavior changed from pass-through to redact. Set outboundPolicy:"log" for v0.1.0 behavior.
  • New verdicts in the audit stream: ALLOW_VERIFIED, REJECT_AUTH, REJECT_FORBIDDEN, OUT_REDACTED, OUT_BLOCKED, UPSTREAM_ERROR.

Migration v0.2.0 → v0.2.1 (breaking)

  • GET /audit no longer works without credentials. Without --admin-token (or UTA_SHIELD_ADMIN) it answers 404; with a token it requires the x-uta-admin header. Dashboards that scraped /audit anonymously must present the operator token. The response flag x-uta-shield is now the exact core verdict — policy=log users will see OUTBOUND_INTEL instead of the previously mislabeled flag.

Migration v0.2.1 → v0.2.2

  • Additive. --acl-deny-unlisted enables opt-in strict ACL (allow-list semantics); the CLI refuses to start with that flag but no --acl-file (fail-closed). New audit verdicts: BODY_TOO_LARGE (both 413 paths — v0.2.1 added AUDIT_DISABLED/AUDIT_DENIED). Public CI added — nothing else changes for existing operators.

Licensed for use — see LICENSE (license for use / licencia de uso). © 2026 AliceLabs LLC. Commercial exceptions (white label, OEM, redistribution, managed service): [email protected]

Source: README.md at commit e31cbd8

Tools

0
SourceWeft has not started this package in its sandbox yet, so its tools are not known.

Version history

2
  1. v0.3.0LatestOct 29, 2026
  2. v0.2.4Oct 23, 2026

Score

40% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts

    Declare a license in the repository or the registry entry.