.codex-plugin — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited .codex-plugin (MCP Server) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<div align="center"> <h1>@cyanheads/sanctions-screening-mcp-server</h1> <p><b>Screen names against the consolidated OFAC, EU, UK, and UN sanctions lists and resolve legal entities against GLEIF, fuzzy-matched offline over a local SQLite + FTS5 mirror. A screening aid, not a compliance determination.</b> <div>6 Tools • 3 Resources • 1 Prompt</div> </p> </div>
<div align="center">
</div>
<div align="center">
Public Hosted Server: https://sanctions-screening.caseyjhand.com/mcp
</div>
[!IMPORTANT] This is a screening aid, not legal or compliance certification. Every tool returns potential matches with a transparent score and source provenance — never a verdict. A hit means "review this candidate against the official source"; an empty result never means "cleared." Real sanctions compliance is a legal process — it requires human review and a qualified compliance determination. This server feeds that process; it does not perform it, and its output is not a compliance record.
sanctions-screening-mcp-server turns the world's open sanctions data plus the global legal-entity registry into one screening-and-resolution workflow, answered offline and fuzzy-matched. It screens a name against the consolidated US (OFAC), EU, UK, and UN sanctions lists at once, and resolves legal entities against the GLEIF Legal Entity Identifier (LEI) database with corporate-ownership tracing.
All sources are bulk-downloadable, keyless, and clear for redistribution. The server mirrors them to a local SQLite + FTS5 index and serves matches from that mirror — no live API key, no per-request rate limit on the hot path. The agent sees screening verbs (screen_name, resolve_entity, trace_ownership); which list answered a query surfaces only as provenance on each hit.
The matching model is transparent by design: strict token matching first (exact-normalized, then all-tokens-present via FTS5), with a scored Jaro-Winkler + phonetic fuzzy fallback. Approximate hits carry the raw Jaro-Winkler similarity (0–1) — a real measurement, never a fabricated "confidence percentage."
Six tools organized around two workflows — screen a name against the watchlists, and resolve a legal entity to its global identifier and ownership graph:
| Tool | Description |
|---|---|
sanctions_screen_name | Screen a name (person, company, vessel, aircraft) against all loaded watchlists at once — OFAC SDN + Consolidated, EU, UK, UN — alias- and fuzzy-aware. Returns scored potential matches with source list, program, designation date, and the matched alias. |
sanctions_get_designation | Fetch the full record for one sanctions designation by source list + entry ID: all aliases, identifiers, addresses, dates/places of birth, nationalities, program, legal basis, and designation date. |
sanctions_resolve_entity | Resolve a company / organization name (+ optional jurisdiction) to ranked candidate GLEIF LEIs. Turns a free-text counterparty name into a stable global identifier. |
sanctions_get_entity | Fetch the full GLEIF Level 1 record for one LEI — legal name, trading names, addresses, registration status, jurisdiction — plus a sanctions cross-reference screened on the legal name. |
sanctions_trace_ownership | Trace the GLEIF Level 2 corporate-ownership graph for an LEI (parents and/or children, BFS to a bounded depth), optionally screening every node for beneficial-ownership screening. |
sanctions_list_sources | List the loaded watchlists and GLEIF datasets with record counts, source URLs, licenses, and the mirror's readiness and as-of timestamps. |
sanctions_screen_nameThe 80% entry point — "is this entity on a watchlist?"
exact / strong / approximate; approximate hits carry the raw Jaro-Winkler score (0–1)min_score), and result limitsanctions_get_designationThe drill-in after sanctions_screen_name surfaces a candidate.
source + entry_id (the sourceEntryId from a screen hit)sanctions_resolve_entityThe bridge from a free-text counterparty name to a stable LEI that the entity tools key off.
issued default, lapsed, or any)sanctions_get_entityWho is this legal entity — plus a watchlist cross-reference in the same call.
sanctions_trace_ownershipBeneficial-ownership screening — the cross-source workflow that single-list tools can't do.
direction: walk parents (who owns it), children (what it owns), or bothscreenNodes: true screens every entity in the graph against all watchlists — "is anyone in this ownership chain sanctioned?"screenedNodeCount / flaggedNodeCount so a caller can see coverage at a glance| Type | Name | Description |
|---|---|---|
| Resource | sanctions://designation/{source}/{entryId} | One sanctions designation by source + entry ID (URI mirror of sanctions_get_designation). |
| Resource | sanctions://entity/{lei} | One GLEIF Level 1 entity by LEI (URI mirror of sanctions_get_entity's entity payload, without the screening cross-reference). |
| Resource | sanctions://sources | Loaded lists + GLEIF datasets with counts and refresh timestamps (URI mirror of sanctions_list_sources). |
| Prompt | sanctions_vet_counterparty | Sequences the tools into a full counterparty due-diligence pass: resolve → trace ownership → screen the entity and every beneficial owner → summarize with provenance and the decision-support caveat. |
All resource data is also reachable via the tools, which are the primary path for tool-only MCP clients. The resources are a convenience for resource-capable clients only.
The server aggregates five upstream sources behind the screening surface. All are bulk, keyless, and clear for redistribution.
| Source | Role | License |
|---|---|---|
| OFAC SDN + Consolidated (US Treasury) | Primary US sanctions/watchlist — individuals, entities, vessels, aircraft, with a.k.a. aliases | US Government public domain |
| EU Consolidated Financial Sanctions List | EU-designated persons and entities | Freely redistributable |
| UK Sanctions List (UKSL, FCDO) | UK sanctions targets — persons, entities, ships | Open Government Licence v3.0 |
| UN Security Council Consolidated List | UN-designated individuals and entities across all regimes | Freely redistributable |
| GLEIF LEI (Level 1 + Level 2) | Who-is-who (entity reference) and who-owns-whom (corporate ownership) | CC0 1.0 Universal |
The UK source is the UK Sanctions List (UKSL), the single authoritative UK source since the OFSI Consolidated List closed on 28 January 2026.
The mirror is not bundled — the sanctions lists and the GLEIF golden copy are downloaded and normalized on first run. Run the init lifecycle script out-of-band before screening:
bun run mirror:initThis harvests all four sanctions lists in full, rebuilds the per-alias name index, then streams the GLEIF golden copy (Level 1 entities + Level 2 ownership relationships). It is resumable and intended to run once, off the request path.
| Script | Purpose |
|---|---|
bun run mirror:init | Full initial load of all sources (sanctions lists + GLEIF golden copy). |
bun run mirror:refresh | Re-harvest the sanctions lists and apply GLEIF deltas. Also runs on a cron under HTTP transport. |
bun run mirror:verify | Report mirror readiness and per-source record counts. |
bun run mirror:seed | Load a small synthetic fixture for local smoke tests (no downloads). |
Set SANCTIONS_INIT_SKIP_GLEIF=1 on mirror:init to load only the (small) sanctions lists and skip GLEIF.
GLEIF memory note: the GLEIF Level 1 ingest is the memory-heavy leg — the full golden copy is roughly 3.3M LEI records (~490 MB compressed) and ~890 MB of resident memory during the streaming ingest. The four sanctions lists and the GLEIF deltas / Level 2 ownership data are light by comparison. Size the host accordingly formirror:init, or skip GLEIF withSANCTIONS_INIT_SKIP_GLEIF=1if you only need watchlist screening.
Built on @cyanheads/mcp-ts-core:
mirror_not_ready, designation_not_found, lei_not_found)none, jwt, oauth (defaults to none — all data is public)Sanctions-specific:
MirrorService — offline, no live API key, no per-request rate limitAgent-friendly output:
match_type (exact / strong), never a fabricated percentageprimary / aka / fka / low-quality-aka)sanctions_list_sources — each source's record count and the mirror's as-of timestamp, so an agent can judge stalenessA public instance is available at https://sanctions-screening.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP, with this client config:
{
"mcpServers": {
"sanctions-screening-mcp-server": {
"type": "streamable-http",
"url": "https://sanctions-screening.caseyjhand.com/mcp"
}
}
}Add the following to your MCP client configuration file. The server is offline-first — populate the mirror with bun run mirror:init before screening (see Source lists).
{
"mcpServers": {
"sanctions-screening-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/sanctions-screening-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"sanctions-screening-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/sanctions-screening-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcpgit clone https://github.com/cyanheads/sanctions-screening-mcp-server.gitcd sanctions-screening-mcp-serverbun installcp .env.example .env
# edit .env if you need to override defaults (all optional)bun run mirror:initAll sources are keyless — there is no required API key. Every variable below is optional with a sensible default.
| Variable | Description | Default |
|---|---|---|
SANCTIONS_MIRROR_PATH | Filesystem path for the SQLite mirror; a persistent volume on a hosted deployment. | ./data/sanctions.db |
SANCTIONS_REFRESH_CRON | Cron for the scheduled refresh of sanctions lists + GLEIF deltas (HTTP transport only). | 0 4 * * * |
SANCTIONS_FUZZY_MIN_SCORE | Default Jaro-Winkler similarity floor for fuzzy matches when min_score is omitted. | 0.85 |
SANCTIONS_FUZZY_MAX_RESULTS | Hard cap on fuzzy candidates scored per query, to bound work on short queries. | 50 |
OFAC_SDN_URL | Override for the OFAC SDN advanced-XML file. | official SLS URL |
OFAC_CONSOLIDATED_URL | Override for the OFAC Consolidated advanced-XML file. | official SLS URL |
EU_FSF_URL | Override for the EU consolidated XML file (includes the static public token path component). | official EU URL |
UK_SANCTIONS_URL | Override for the UK Sanctions List (UKSL) XML file. | official FCDO URL |
UN_SC_URL | Override for the UN Security Council consolidated XML file. | official UN URL |
GLEIF_GOLDEN_COPY_BASE_URL | Override for the GLEIF golden-copy / delta download API. | https://goldencopy.gleif.org |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for the HTTP server. | 3010 |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
Source URLs default to the verified official endpoints; overrides exist for testing and for pinning a mirror in restricted environments. The EU "token" is a static public path component, not a credential.
See .env.example for the full list of optional overrides.
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http bun run devcheck # Lint, format, typecheck, security, changelog sync
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against specdocker build -t sanctions-screening-mcp-server .
docker run --rm -p 3010:3010 -v sanctions-data:/usr/src/app/data sanctions-screening-mcp-serverThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/sanctions-screening-mcp-server. The image runs under Bun, so the mirror uses bun:sqlite (no native build). Mount a volume at the mirror path (/usr/src/app/data by default) so the populated mirror survives container restarts, and run bun run mirror:init inside the container (docker exec) to populate it. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools/resources/prompts, inits the screening service, schedules the HTTP refresh. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts) — the six screening/resolution tools. |
src/mcp-server/resources | Resource definitions (*.resource.ts) — the three URI mirrors. |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts) — the counterparty vetting prompt. |
src/services/screening | The screening service — local mirror, normalized schema, source ingesters (OFAC/EU/UK/UN/GLEIF), and the strict/fuzzy matching engine. |
scripts/mirror-*.ts | Mirror lifecycle CLI — init, refresh, verify, seed. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/mcp-server/*/definitions/index.tsThis server redistributes open data from the following sources, cited here per their terms:
Issues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testApache-2.0 — see LICENSE for details.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.