Skip to content

io.github.cyanheads/sanctions-screening-mcp-server

by cyanheads · remote · local (npm) · no sign-in to list tools · v0.5.1 · published by GitHub user cyanheads

Screen names against OFAC, EU, UK, UN sanctions lists; resolve entities via GLEIF. Screening aid.

Changes · 3

7 tools: 3 change things and 4 only read.

Scanned Oct 21, 2026

1 check matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.

claude mcp add --transport http sanctions-screening https://sanctions-screening.caseyjhand.com/mcp

Findings

Matches of fixed checks, with the text they matched.

1 info

Tools

Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.

7 tools

Changes things 3Claude Code: ask
  • sanctions_screen_identifierLook up an identifier — a vessel IMO number, a SWIFT/BIC code, a digital-currency wallet address, a passport or national ID number, or any other identifier a list publishes — again…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Look up an identifier — a vessel IMO number, a SWIFT/BIC code, a digital-currency wallet address, a passport or national ID number, or any other identifier a list publishes — against all loaded sanctions watchlists at once: OFAC SDN + Consolidated, EU, UK, and UN. Exact match after normalization, with no fuzzy or partial matching and no score: spacing, letter case, and the separators - . / are ignored, an IMO number matches with or without its IMO prefix, a SWIFT/BIC code compares on its first eight characters so a branch code matches its institution, and a wallet address folds case only where its encoding is case-insensitive (hex, bech32, cashaddr — never base58). Returns every designation that publishes a matching identifier, one per designation — an OFAC party both OFAC lists publish under one entry ID is one hit, its sources naming both lists — with the identifiers that matched as published; sanctions_get_designation pulls the full record. This is a screening AID for a human/compliance review, NOT a compliance determination: a hit means "review this candidate against the official source," and an empty result never means "cleared" — an identifier a list prints only in free-text remarks, or bundled with other numbers in one field, does not match.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • sanctions_screen_nameScreen a name (person, company, vessel, aircraft) against all loaded sanctions watchlists at once — OFAC SDN + Consolidated, EU, UK, and UN — alias- and fuzzy-aware.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Screen a name (person, company, vessel, aircraft) against all loaded sanctions watchlists at once — OFAC SDN + Consolidated, EU, UK, and UN — alias- and fuzzy-aware. Returns scored potential matches with the source list, sanctioning program, designation date, and the matched alias; an OFAC party both OFAC lists publish under one entry ID is one hit, its sources naming both lists. Strict mode (default) matches exact-normalized then all-tokens-present, then runs a fuzzy pass over each selected list strict finds nothing on: a full pass when strict finds nothing on any list, otherwise one that adds only candidates covering every word of the name other than legal forms, articles and other function words, and the jurisdiction codes uk, usa, uae, and rf, ranked after the strict hits. Fuzzy mode runs the fuzzy pass over every selected list. It adds Jaro-Winkler and phonetic matching and labels hits approximate with a raw 0–1 similarity score plus the count of query tokens the candidate covers, which orders candidates that tie on score; fuzzySources names the lists it searched. Results are paged: totalAvailable and hasMore report matches beyond the returned page, and nextOffset retrieves them. This is a screening AID for a human/compliance review, NOT a compliance determination: a hit means "review this candidate against the official source," and an empty result never means "cleared."

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • sanctions_trace_ownershipTrace the GLEIF Level 2 corporate-ownership graph for an LEI: direct and ultimate parents and/or children, traversed breadth-first to a bounded depth, with relationship type for ea…1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Info

      The description is 2,047 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: ".... Requires a valid 20-character LEI (use sanctions_resolve_entity to obtain one)."

      What this check looks for
    Description, as the server wrote it
    Trace the GLEIF Level 2 corporate-ownership graph for an LEI: direct and ultimate parents and/or children, traversed breadth-first to a bounded depth, with relationship type for each edge. Direction both walks up to the parents and down to the children from the root, never sideways into siblings or co-parents. An ultimate-parent edge is a shortcut to the top of the group, not a hop: a node only it reaches within the depth is returned as a leaf flagged reachedVia: ultimate. Set screenNodes to also cross-reference every entity in the graph against all loaded watchlists — resolving "is anyone in this ownership chain sanctioned." Each node is cross-referenced as sanctions_get_entity does it: its legal name and every other and transliterated name screened strict (exact, then all tokens present — never fuzzy), and its LEI and registration number looked up as exact non-document identifiers, the registration number matching only an identifier published for the country of its legal jurisdiction; hits merge to one per designation, an OFAC party both OFAC lists publish to one hit whose sources names both, matchedOn naming every input that produced each. A node with no Level 1 record (missingEntityLeis) has no names, so it is looked up by its LEI alone. Each per-node screen is a screening AID: hits are candidates to verify, and an empty result for a node is not a clearance of that node. Each node whose parents were walked carries parentStatus for its direct and ultimate parent: a published relationship, a reporting exception with the reasons the entity gave (such as NATURAL_PERSONS or NON_CONSOLIDATING), none, or unknown when reporting exceptions are not loaded. The response says what it could not do: complete/truncated/missingEntityLeis report whether the loaded relationship graph within the depth is fully shown, screeningStatus reports whether the cross-reference actually ran, and each screened node reports whether its own hit list was capped. Requires a valid 20-character LEI (use sanctions_resolve_entity to obtain one).

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
Only reads 4Claude Code: allow
  • sanctions_get_designationFetch the full record for one sanctions designation by source list + entry ID or the list's published reference number — the drill-in after sanctions_screen_name or sanctions_scree…No findings

    Why it is in this group

    • The name "sanctions_get_designation" says "get": a word for reading.
    Description, as the server wrote it
    Fetch the full record for one sanctions designation by source list + entry ID or the list's published reference number — the drill-in after sanctions_screen_name or sanctions_screen_identifier surfaces a candidate, or the lookup for a reference a notice cites (UN QDe.004, EU EU.27.28, UK OFSI Group ID). Returns all published aliases, identifiers (passport, national ID, tax and registration numbers, SWIFT/BIC codes, digital-currency addresses, vessel call signs, aircraft tail and serial numbers, phone numbers, email addresses, websites), addresses, dates and places of birth at the precision the source published, nationalities, descriptive features (a vessel's flag, type, and tonnage; an aircraft's model and operator; a title or gender; sanctions notes), sanctioning program, legal basis, and designation date. The record reflects exactly what the source published; missing fields mean the source omitted them. This is a screening aid — the designation record supports a compliance review, it is not itself a determination.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • sanctions_get_entityFetch the full GLEIF Level 1 record for one LEI: legal name, other/trading names, legal and headquarters addresses, registration status, jurisdiction, registration authority and ID…No findings

    Why it is in this group

    • The name "sanctions_get_entity" says "get": a word for reading.
    Description, as the server wrote it
    Fetch the full GLEIF Level 1 record for one LEI: legal name, other/trading names, legal and headquarters addresses, registration status, jurisdiction, registration authority and ID, and last-update date — plus a sanctions cross-reference against all loaded watchlists. The cross-reference screens the legal name and every other and transliterated name strict (exact, then all tokens present — never fuzzy, unlike sanctions_screen_name), and looks up the LEI and the registration number as exact non-document identifiers, the registration number matching only an identifier published for the country of the entity's legal jurisdiction. Hits merge to one per designation, an OFAC party both OFAC lists publish to one hit whose sources names both: matchedOn names every input that produced each, and a hit only an identifier produced carries matchedIdentifiers and no matchedName. The screening cross-reference is a screening AID: a hit is a candidate to verify against the official source, and no hit is not a clearance. screeningStatus says whether that cross-reference actually ran — an empty sanctionsHits under not_ready means the sanctions mirror was unavailable, not that nothing matched. sanctionsScreen says whether the hit list is the whole set: it reports how many potential matches existed before the cap, so a capped cross-reference is distinguishable from a complete one. LEI must be a 20-character GLEIF identifier (18 alphanumerics + 2 check digits).

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • sanctions_list_sourcesList the sanctions watchlists (OFAC SDN + Consolidated, EU, UK, UN) and GLEIF datasets currently loaded in the local mirror, each with its record count, source URL, license, and th…No findings

    Why it is in this group

    • The name "sanctions_list_sources" says "list": a word for reading.
    Description, as the server wrote it
    List the sanctions watchlists (OFAC SDN + Consolidated, EU, UK, UN) and GLEIF datasets currently loaded in the local mirror, each with its record count, source URL, license, and the mirror's readiness and as-of timestamp — for GLEIF, also its Level 2 ownership relationship count, whether its reporting exceptions are loaded and how many, and whether its alternate-name index is built. Use this for provenance and freshness on any result — results are only as current as the last mirror refresh, and a not-ready mirror means screening cannot run yet. Attribution: UK data is under the Open Government Licence v3.0; all sources are cited here.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false
  • sanctions_resolve_entityResolve a company or organization name (with an optional jurisdiction: a country code, which includes its subdivisions, or an ISO 3166-2 subdivision code) to candidate GLEIF Legal …No findings

    Why it is in this group

    • The name "sanctions_resolve_entity" says "resolve": a word for reading.
    Description, as the server wrote it
    Resolve a company or organization name (with an optional jurisdiction: a country code, which includes its subdivisions, or an ISO 3166-2 subdivision code) to candidate GLEIF Legal Entity Identifiers (LEIs), ranked. This turns a free-text counterparty name into a stable global identifier that sanctions_get_entity and sanctions_trace_ownership key off. Every name GLEIF publishes takes part: the legal name, previous legal names, trading names, alternative-language legal names, and ASCII transliterations of a legal name in another script — each candidate reports the name it matched on and that name's type, one candidate per LEI. Strict mode (default) matches exact-normalized then all-tokens-present; fuzzy mode (or auto when strict is empty) adds Jaro-Winkler scoring labeled approximate with a raw 0–1 score plus the count of query tokens the matched name covers, which orders candidates that tie on score. Results are paged: totalAvailable and hasMore report candidates beyond the returned page, and nextOffset retrieves them. Returns potential matches to confirm against the GLEIF record — name resolution is a candidate ranking, not an authoritative identification.

    Annotations the server declares

    • readOnlyHint: true
    • idempotentHint: true
    • openWorldHint: false

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Install

Pinned to version 0.5.1. Values in angle brackets are yours to fill in.

claude mcp add --transport http sanctions-screening https://sanctions-screening.caseyjhand.com/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__sanctions-screening__sanctions_get_designation",
      "mcp__sanctions-screening__sanctions_list_sources",
      "mcp__sanctions-screening__sanctions_resolve_entity",
      "mcp__sanctions-screening__sanctions_get_entity"
    ],
    "ask": [
      "mcp__sanctions-screening__sanctions_screen_name",
      "mcp__sanctions-screening__sanctions_screen_identifier",
      "mcp__sanctions-screening__sanctions_trace_ownership"
    ],
    "deny": []
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as sanctions-screening. Review it before you use it.