mcpField
P
pseudonym-mcp

pseudonym-mcp

ローカル
github.com/woladiセキュリティ28 週間前に更新

pseudonym-mcp について

pseudonym-mcpは、テキストをクラウドLLMに送る前にローカルで機密情報を検出してトークン化し、後から元の値に戻せます。明示的なプライバシー処理を必要とするMCPクライアント向けで、コンプライアンス製品ではなくリスク低減策です。

主な機能

  1. 01ローカルでの個人情報検出とマスキング
  2. 02正規表現とローカルOllamaのNERエンジン
  3. 03セッション単位のトークン対応表
  4. 04unmask_textによる原文復元
  5. 05複数言語・地域のルール
  6. 06正規表現のみ、LLMのみ、ハイブリッドの各モード

活用シーン

  1. 01クラウドLLM処理前に識別情報をマスキング
  2. 02モデルの応答に含まれる名前や値を復元
  3. 03同じトークンを参照するメモの処理
  4. 04MCPワークフローに個人情報保護手順を追加

MCP 対応

mask_textとunmask_textのツール、およびプロンプトテンプレートをMCPクライアントに提供します。検出とトークン対応表の管理はローカルで行われ、クラウドLLMに送る前にクライアントが明示的にマスキングを呼び出す必要があります。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"woladi-pseudonym": {
"command": "npx",
"args": [
"-y",
"pseudonym-mcp"
]
}
}
}
起動コマンドnpx -y pseudonym-mcp

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npmpseudonym-mcp0.9.0

ツール

2

掲載時のツール一覧 · 2 週間前に更新

パラメーター型必須説明
textstringはい仮名化するテキスト
session_idstringいいえ任意:既存のセッションを再利用し、複数回の呼び出し間でトークン番号を維持します
wait_for_nerbooleanいいえtrue の場合、処理前に Ollama のモデル読み込み完了を最大 30 秒待ちます(デフォルト:false)
custom_literalsarrayいいえ常にマスキングする特定の文字列(氏名、ID、電話番号)

詳細

pseudonym-mcp

pseudonym-mcp — PII をローカルでマスキングし、クラウド LLM に安全に送信して、すぐに復元

LLM ワークフロー向けのローカル仮名化ツールです。テキストをクラウド LLM に渡す前に、検出した PII を不透明なトークンに置き換え、その後トークンを復元します。

npm version License: MIT Node 18+ GDPR-aligned Local detection Offline NER

クライアントやエージェントが明示的なプライバシー保護のステップとして呼び出せる MCP ツール(mask_text と unmask_text)を提供します。サーバーは PII をローカルで検出して不透明なトークンに置き換え、後で復元できるようトークンの対応関係をメモリ内に保持します。

これは多層防御の手段であり、コンプライアンスを一挙に解決するものではありません。この仕組みでできることを過信する前に、制限事項とGDPR と AI コンプライアンスのセクションをお読みください。

提供機能

  • 12 のロケールに対応する 46 種類の認識器を標準ですべて有効化:ポーランド(PESEL、NIP、REGON、dowód、paszport、księga wieczysta)、EU(mod-97 検証に対応した IBAN、27 加盟国すべての VAT に加え、DE、IT、ES、FR、NL、CZ/SK、SE、FI、UK の国民 ID)、米国(SSN、ITIN、EIN、ABA ルーティング番号、カード)。そのうち 24 種類は実際のチェックディジットを検証します。誤検出を減らしたい場合は、--lang で対象を絞り込めます。詳しくはロケール対応はフェイルクローズをご覧ください。ヒューリスティックによる言語検出(detectLanguage())はテキスト内容から言語を推定します。--lang は引き続き優先される上書き設定です。
  • ハイブリッド NER エンジン:構造化 PII(SSN、クレジットカード、IBAN、メールアドレス、電話番号)には正規表現を、非構造化エンティティ(氏名、組織名)にはローカルの Ollama LLM を使用します。
  • ローカル検出アーキテクチャ:MCP ツールの呼び出し時に、検出と置換をお使いのマシン上で行います。クラウド LLM の呼び出し自体は行われます(それが目的です)が、ワークフローでマスキング済みの出力を使用すれば、検出された PII の代わりにトークンを参照できます。
  • セッション単位のマッピングストア:[PERSON:1] のようなトークンは、リクエストごとに分離されたセッション内で元の値に対応付けられます。複数回の往復でもトークンの一貫性が保たれます。
  • マスキング解除ワークフローのサポート:mask_text は、この設定を尊重するクライアント向けに auto_unmask を返しますが、このサーバーが任意の LLM 応答を自動で傍受することはありません。
  • 柔軟なエンジン:regex のみ(Ollama 不要)、llm のみ、または hybrid(デフォルト)を実行できます。
  • 厳格な検証:SSN の地域番号検証、クレジットカードの Luhn チェックサム、PESEL チェックサムに対応し、いずれも設定可能です。
  • 適切な縮退動作:Ollama が利用できない場合も正規表現フェーズは実行され、例外は発生しません。
  • MCP ネイティブ:Claude Code、Claude Desktop、Cursor など、MCP 対応クライアントで動作します。

❌ なしの場合/✅ 使用した場合

❌ pseudonym-mcp を使用しない場合:

  • プロンプト:"John Smith, SSN 123-45-6789, card 4111 1111 1111 1111" → そのまま LLM プロバイダーに送信されます
  • プロンプト内のすべての氏名、ID 番号、クレジットカード番号が処理され、プロバイダーによって記録される可能性があります
  • プロバイダー側で情報漏えいが発生すると、これらの値が平文で露呈します
  • さらなる保護措置なしに個人データを EU 域外の LLM プロバイダーに送信すると、GDPR 第 44 条に関する説明責任が生じます

✅ クラウドへの呼び出し前に pseudonym-mcp を使用した場合:

  • 先に mask_text を呼び出すと、同じプロンプトを "[PERSON:1], SSN [SSN:1], card [CREDIT_CARD:1]" に置き換えられます
  • LLM は、検出された値を平文で見ることなく、構造と内容を推論します
  • ユーザーに届く前に、unmask_text を使ってレスポンスをローカルでトークン解除できます
  • 検出された直接識別子は送信されなくなります。ただし、構造、日付、間接的な参照、見逃された PII は引き続き送信されます

これにより、平文の PII が露呈するリスクを大幅に低減できます。ただし、「個人データが一切マシンの外に出ない」わけではありません。制限事項を参照してください。

GDPR と AI コンプライアンス

pseudonym-mcp はコンプライアンス対応に役立ちますが、コンプライアンス製品ではなく、技術的な管理策です。特定の規制に準拠しているかどうかは、スタック全体、役割(管理者/処理者)、契約、DPIA、管轄区域によって異なります。

重要な理由

EU の一般データ保護規則(GDPR)では、氏名、国民 ID 番号(SSN や PESEL など)、銀行口座番号(IBAN)、メールアドレス、クレジットカード番号、電話番号を第 4 条(1)における個人データと定めています。このデータをクラウド LLM プロバイダーに送信することは、第 4 条(2)における処理に該当します。仮名化は、第 4 条(5)でリスク低減策として明示的に認められています。ただし、重要なのは、仮名化されたデータも依然として個人データであるという点です(前文 26)。

GDPR 条項義務pseudonym-mcp が役立つ点対応できない点
第 5 条(1)(c)データの最小化送信前に、検出された直接識別子を除去しますコンテキスト、構造、未検出の PII は最小化されません
第 25 条データ保護の設計およびデフォルト化プライバシー・バイ・デザインのアーキテクチャに組み込める技術的なレイヤーを提供しますアーキテクチャやポリシーに関する判断は、引き続き利用者の責任です
第 32 条処理の安全性前文 83 で認められた技術的措置(仮名化)です多くの管理策のうちの一つであり、アクセス制御、ログ記録、暗号化の代わりにはなりません
第 44 条第三国への移転移転する平文の PII を減らします仮名化された個人データも依然として個人データであり、移転に関する規則は引き続き適用されます
第 4 条(5)仮名化の定義マッピングストアはクラウド LLM からは不透明で、再識別にはローカルセッションが必要です周辺知識を持つ人は、コンテキストから再識別できる可能性があります

率直に言うと: GDPR 第 4 条(5)における仮名化は、匿名化ではありません。データはシステム内で引き続き個人データとして扱われ、氏名フィールドをトークン化しただけで第 44 条の移転義務がなくなるわけではありません。

AI Act への準拠

EU の AI Act は、個人データを処理する高リスク AI システムに追加要件を課しています。中間レイヤーとして pseudonym-mcp を使用すると、次のことができます。

  • AI システムのデータフローにおけるデータ最小化をサポートします。
  • 透明性と人間による監督の要件に対する技術的管理策の文書化に役立ちます。
  • 明文の PII への露出を抑えることで、技術的堅牢性と安全性の原則(第 15 条)に沿うことができます。

これだけで AI Act におけるリスク分類が変わるわけではありません。分類はユースケースと導入コンテキストによって決まり、モデルの前段階で行うマスキングによって決まるものではありません。

米国および国際的な適用範囲

同じ注意点はありますが、このツールは EU 以外でも役立ちます。

  • CCPA / CPRA(カリフォルニア州) — サードパーティの処理業者に送信される個人情報を減らしますが、管理者や事業者の義務、または消費者の権利は変わりません。
  • HIPAA(米国の医療分野) — 仮名化された PHI も HIPAA の下では PHI です。対象事業体または業務提携者である場合、このツールを使用しても、クラウド LLM プロバイダーとの BAA が不要になるわけではありません。適切な安全対策の一環にはなり得ますが、その代わりにはなりません。
  • PCI DSS(決済業界) — Luhn 検証を用いた検出により、カード番号が明文のまま LLM に送られる可能性を減らします。これは管理策の一つです。PCI の適用範囲、セグメンテーション、保管ルールは別途考慮する必要があります。
  • SOC 2 — PII への露出を制限する技術的管理策の有用な証拠になります。監査人はこのレイヤーだけでなく、全体を確認します。
  • PIPEDA(カナダ)、LGPD(ブラジル)、POPIA(南アフリカ) — いずれも、国境を越える個人データの移転に適切な安全対策を求めています。このツールは関連する安全対策の一つですが、移転の法的根拠に代わるものではありません。
セクターごとの適用範囲
セクター関連する規制一般的に取り扱う PII の種類
医療・ヘルスケアGDPR + HIPAA + 国内の医療データ法患者の氏名、SSN、診断情報
銀行・金融GDPR + PCI DSS + PSD2 + DORAクレジットカード、IBAN、SSN、PESEL
人事・採用GDPR 第 9 条(特別カテゴリー)氏名、国民識別番号、連絡先情報
法務GDPR + 弁護士・依頼者間の秘匿特権氏名、事件番号、個人情報
保険GDPR + Solvency II個人識別情報、健康データ
公共部門(米国)CCPA + 州のプライバシー法SSN、運転免許証番号
公共部門(PL)GDPR + UODO + KRIPESEL、NIP、REGON

この表のどの行においても、pseudonym-mcp は有用な構成要素です。これらの規制は、マスキングツールだけで遵守できるものではありません。

仕組み

Your App / Claude Desktop
        │
        │  explicit mask_text tool call with PII
        ▼
┌─────────────────────────┐
│      pseudonym-mcp      │
│                         │
│  Phase 1: Pattern NER   │  ← 46 rules: national IDs, tax numbers,
│                         │    IBAN, VAT, cards, wallets, devices
│                         │    scored, checksum-verified, context-aware
│  Phase 2: Ollama NER    │  ← PERSON, ORG  (local LLM)
│  MappingStore (session) │  ← [TAG:N] ↔ original value
└────────────┬────────────┘
             │  masked text returned to the client/agent
             ▼
      Your workflow sends the masked text
             ▼
      Cloud LLM API
      (Claude / GPT-4 / Gemini)
             │
             │  response with [TAG:N] tokens
             ▼
┌─────────────────────────┐
│      pseudonym-mcp      │
│   unmask_text / revert  │  ← tokens → originals
└────────────┬────────────┘
             │  restored response
             ▼
        Your App / User
トークン形式
US / English identifiers (en pack):
[PERSON:1]       John Smith
[SSN:1]          123-45-6789
[CREDIT_CARD:1]  4111 1111 1111 1111
[ORG:1]          Acme Corp
[EMAIL:1]        [email protected]
[PHONE:1]        (555) 123-4567

Polish identifiers (pl pack):
[PERSON:1]       Jan Kowalski
[PESEL:1]        90010112318
[ORG:1]          Auto-Lux
[NIP:1]          526-000-00-05
[REGON:1]        123456785
[IBAN:1]         PL27114020040000300201355387
[EMAIL:1]        [email protected]
[PHONE:1]        +48 123 456 789

対応付けは、セッション単位のインメモリストアに保存されます。各 mask_text 呼び出しは session_id を返します。元のテキストに復元するには、その値を unmask_text に渡してください。

実例

Claude Code / Obsidian での会議メモ

次のようなメモがあります。

Meeting with Jan Kowalski (PESEL: 90010112318) from Acme sp. z o.o.
We discussed a contract for 45 000 zł. Contact: [email protected]

Claude Code で次のように入力します。

Use mask_text on this note, then summarise the key points of the meeting.

まず mask_text を呼び出すと、pseudonym-mcp が検出した PII をローカルで置き換えます。

Meeting with [PERSON:1] ([PESEL:1]) from [ORG:1].
We discussed a contract for 45 000 zł. Contact: [EMAIL:1]

次に、マスキング済みテキストを使って作業するよう Claude に依頼します。Claude はトークンを使って応答します。

Meeting with [PERSON:1] from [ORG:1] covered a contract
for 45 000 zł. Follow up via [EMAIL:1].

pseudonym-mcp が元の情報をローカルで復元します。

Meeting with Jan Kowalski from Acme sp. z o.o. covered
a contract for 45 000 zł. Follow up via [email protected]

マスキング済みテキストを上流に送信すれば、クラウドプロバイダーに見えるのは会議の構成と金額であり、検出された氏名、PESEL、組織名、メールアドレスは明文では見えません。置き換えはお使いのマシン上で行われます。

session_idを使用したObsidianボルト
# mask the entire vault once — save the session_id
Use mask_text on my notes — remember the session_id

# ask Claude anything across multiple prompts
Summarise all meetings from Q1

# Claude replies with tokens; restore originals
Use unmask_text with session_id abc123 on the response

session_idによって、セッション中はトークンマップが保持されます。同じ[PERSON:1]は、ノートをまたいでも常に同じ人物を指します。この一貫性があるからこそ、ノート間の推論が可能になります。また、業務に関する周辺知識を持つ人にとって、マスク済みのコーパスから個人を再特定できる可能性が生じます。長時間継続するセッションは、意図を持って使用してください。

MCP プロンプトテンプレート

pseudonym-mcpには、マスク → タスク → マスク解除のワークフローを説明する、組み込みのプロンプトテンプレートが2つ用意されています。

重要: MCP プロンプトテンプレートは便利なヘルパーであり、プライバシーを保護する境界ではありません。インラインのプロンプト引数は、ツールによるマスキングが行われる前に、ホストクライアントやモデルから見える場合があります。プライバシーを最大限に確保するには、まずmask_textを直接呼び出し、返されたmasked_textをLLMのプロンプトで使用してください。

pseudonymize_task — インラインテキスト
/pseudonymize_task text="Meeting with Jan Kowalski (PESEL: 90010112318). Contract: 45 000 zł." task="Extract action items"

想定されるワークフロー:

  1. pseudonym-mcpが検出したPIIをローカルでマスクします → [PERSON:1]、[PESEL:1]
  2. Claudeがマスク済みのテキストを処理します
  3. pseudonym-mcpが応答内の元の値を復元します

省略可能なlang引数:enまたはpl。生成されるプロンプトテキストに注釈を付けるだけです。サーバーが実際に実行するパックは、サーバー自身の--langによって決まります。

privacy_scan_file — ファイル/PDF(macOSのみ)

macos-vision-mcpが必要です — AppleのVisionフレームワークを使用して、PDFや画像からデバイス上でテキストを抽出する、別のMCPサーバーです。macOSのみで利用できます。

/privacy_scan_file filePath="/Users/me/contracts/nda.pdf" task="Summarise obligations and deadlines"

想定されるワークフロー:

  1. macos-vision-mcpがデバイス上でファイルからテキストを抽出します
  2. pseudonym-mcpが検出したPIIをローカルでマスクします
  3. Claudeがマスク済みのコンテンツを処理します
  4. pseudonym-mcpが応答を表示する前に元の値を復元します

省略可能な引数:task(デフォルト:要点を要約する)、lang(enまたはpl。プロンプトテキストに注釈を付けるだけです)。

クイックスタート

ステップ1 — MCPクライアントに追加します(Claude Codeの例 — インストールは不要です):

claude mcp add pseudonym-mcp -- npx -y pseudonym-mcp --engines hybrid

ステップ2 —(任意)ハイブリッドNERを完全に利用するため、Ollamaモデルを取得します:

ollama pull llama3

正規表現ベースのマスキング(--engines regex)のみが必要な場合は、この手順を省略してください。Ollamaがない場合、構造化された識別子(SSN、IBAN、カード番号、メールアドレス、電話番号、PESEL)は検出できますが、自由形式の人名や組織名は検出できません。

グローバルインストール — npm install -g pseudonym-mcpを使用する場合は、以下のすべてのスニペットでnpx -y pseudonym-mcpをpseudonym-mcpに置き換えてください。

クライアントを再起動してください。mask_textとunmask_textのツールが自動的に表示されます。

利用可能なツール

ツール機能プロンプトの例
mask_textテキスト内で検出したPIIを仮名化します。masked_textとsession_idを返します。「この顧客からの手紙を要約する前に、mask_textを使用してください」
unmask_textセッションから元の値を復元します。mask_textが返したsession_idを渡してください。「session_id Xを指定してunmask_textを使用し、応答を復元してください」
mask_textの入力
{
  "text": "John Smith (SSN: 123-45-6789) works at Acme Corp.",
  "session_id": "optional — omit to create a new session",
  "custom_literals": ["John Smith", "Acme Corp"]
}
mask_textの出力
{
  "session_id": "3f2a1b...",
  "masked_text": "[PERSON:1] (SSN: [SSN:1]) works at [ORG:1].",
  "auto_unmask": false,
  "ner_status": "ready",
  "active_locales": ["pl", "en", "de", "it", "es", "fr", "nl", "cz", "sk", "se", "fi", "uk"]
}

active_localesには、実行されたパックが列挙されます。サーバーの対象が--langで限定されている場合、応答には、検出できない識別子を示すdisabled_localesとlocale_warningも含まれます:

{
  "active_locales": ["en"],
  "disabled_locales": ["pl", "de", "it", "es", "fr", "nl", "cz", "sk", "se", "fi", "uk"],
  "locale_warning": "Locale packs disabled: pl (PESEL, NIP, REGON, …); de (Steuer-IdNr, PLZ); … Identifiers from those countries are NOT detected and will pass through unmasked. Start the server without --lang, or with --lang all, to load every pack."
}
unmask_textの入力
{
  "text": "The case concerns [PERSON:1] at [ORG:1].",
  "session_id": "3f2a1b..."
}

設定

mcp-config.json(プロジェクトのルート)
{
  "lang": "all",
  "engines": "hybrid",
  "ollamaModel": "llama3",
  "ollamaBaseUrl": "http://localhost:11434",
  "autoUnmask": false,
  "strictValidation": true,
  "sensitivity": "balanced",
  "extraLocales": [],
  "customLiterals": ["Jan Kowalski", "78091512345", "+48 123 456 789"]
}
キー値デフォルト説明
langall, en, pl, de, it, es, fr, nl, cz, sk, se, fi, ukall正規表現ルールに使用するロケールパックです。all の場合はすべてのパックを実行します。以下を参照してください
enginesregex | llm | hybridhybrid実行する NER エンジン
ollamaModel任意の Ollama モデル名llama3エンティティ検出に使用するローカル LLM
ollamaBaseUrlURLhttp://localhost:11434Ollama API エンドポイント
autoUnmasktrue | falsefalse推奨するマスク解除の動作をクライアントに通知します。このサーバーはレスポンスを傍受しません
strictValidationtrue | falsetrueチェックサム/形式の検証を有効にします(SSN の地域コード確認、カード番号の Luhn 検証、PESEL チェックサム)
sensitivitybalanced | strict | paranoidbalancedマスクする前にマッチに必要な確信度の高さ
extraLocalesstring[][]lang と併せて実行する追加のロケールパック。例:["de", "it"]。lang が all の場合は重複します
customLiteralsstring[][]エンジンにかかわらず、常に編集する特定の文字列(名前、ID、電話番号)
ロケールの適用範囲はフェイルクローズです

ロケールを指定しない限り、すべてのロケールパックが実行されます。 lang のデフォルト値は all です。そのため、新しいサーバーはポーランド、米国、ドイツ、イタリア、スペイン、フランス、オランダ、チェコ/スロバキア、スウェーデン、フィンランド、英国の識別子を一度に認識します。

これは意図的な仕様です。1つのパックに絞ったサーバーも、呼び出し側から見るとすべてのパックが有効なサーバーと区別できません。見た目には問題のないテキストを返し、成功を報告する一方で、ほかのすべての国の識別子はそのまま通過します。注文番号に反応するパックは不要なトークンを消費し、PESELを検出できないパックはインシデントを招きます。

実在するパックを指定していない場合(入力ミス、空文字列、all)は、すべてのパックが有効になります。適用範囲が狭まるのは、明示的に指定した場合に限られます。その場合、サーバーは起動時の stderr および 各 mask_text レスポンス(disabled_locales、locale_warning)でその旨を通知します。

コストについて。 12個のパックを同時に実行すると、すべての数値に対して12か国分の仮説を検証することになります。そのため、ある国では珍しくない形式が、別の国ではマスキングされます。

入力マスキング結果理由
123456789PHONEポーランドの有効な9桁の電話番号体系
045678/2024CZ_SK_BIRTH_NUMBERrodné číslo の形式
AB123456CUK_NINONational Insurance の形式
123456789012345FR_NIR1または2で始まる15桁

文書が単一の管轄区域のもので、これらのトークンがノイズになる場合は、サーバーの適用範囲を意図的に絞ってください(--lang en、--lang pl --extra-locales de,it)。絞り込んだパックの識別子は検出されなくなることを受け入れてください。

設定ファイルの読み込み場所

mcp-config.json は、サーバーの作業ディレクトリから読み込まれます。MCP が起動したプロセスの場合、これはインストールされたパッケージのディレクトリではなく、クライアントの作業ディレクトリです。npm パッケージに同梱されているファイルは例であり、読み込まれることはありません。特定のファイルを使う場合は、--config /path/to/mcp-config.json を渡してください。

設定の優先順位は高い順に、CLI フラグ → mcp-config.json → 組み込みのデフォルト値です。CLI 入力として扱われるのは実際に入力したフラグだけなので、コマンドラインで省略したすべての設定は、引き続き設定ファイルによって決まります。

CLI フラグ

すべての設定キーは起動時に上書きできます(優先度が最も高くなります)。

pseudonym-mcp --lang pl --extra-locales de,it --sensitivity strict --engines regex
フラグ説明
--lang正規表現ルール用のロケールパック:all、en、pl、de、it、es、fr、nl、cz、sk、se、fi、uk(デフォルト:all)
--enginesregex、llm、または hybrid(デフォルト:hybrid)
--ollama-modelNER に使用する Ollama モデル
--ollama-base-urlOllama のベース URL
--sensitivitybalanced、strict、または paranoid(デフォルト:balanced)
--extra-locales--lang に加えて認識するロケールをカンマ区切りで指定します。例:de,it
--configカスタム JSON 設定ファイルへのパス
--auto-unmaskこれに対応するクライアント向けに、mask_text の出力で auto_unmask: true を設定します
--custom-literals常にマスキングする文字列をカンマ区切りで指定します。例:"Jan Kowalski,78091512345"
Claude Code
claude mcp add pseudonym-mcp -- npx -y pseudonym-mcp --engines hybrid
Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json を編集します。

{
  "mcpServers": {
    "pseudonym-mcp": {
      "command": "npx",
      "args": ["-y", "pseudonym-mcp", "--engines", "hybrid"]
    }
  }
}
Cursor

~/.cursor/mcp.json に追加します。

{
  "mcpServers": {
    "pseudonym-mcp": {
      "command": "npx",
      "args": ["-y", "pseudonym-mcp", "--engines", "regex"]
    }
  }
}

サポート対象の PII タイプ

ポーランド、ヨーロッパ、米国を対象とする46個の認識器。そのうち24個はチェックサム検証済み

検出はベストエフォートです。以下のパターンは、このツールが検出対象とするものであり、常に検出できることを保証するものではありません。既知の制限については、制限事項を参照してください。

以下のすべての表は、デフォルト設定で有効です。--lang を指定するとパックが無効になります。ロケールのカバー範囲はフェイルクローズを参照してください。

カスタムリテラル
タグ検出方法一致
CUSTOMcustomLiterals 設定または custom_literals ツールパラメーターとの完全一致(大文字と小文字を区別しない)文字列全体

カスタムリテラルは、エンジンモードにかかわらず、正規表現フェーズの後、LLM NER の前に適用されます。部分置換を防ぐため、長いリテラルから先に照合されます。

マッチの判定方法

信頼度メーター:数字だけの場合はスコアが0.15、前後に文脈語があると0.50に上がり、チェックサムが検証されると0.85に上がります。慎重、厳格、バランスの各しきい値と比較しています

すべてのパターンには信頼度スコアが設定されています。チェックサムが有効だとスコアが上がり、マッチの近くに文脈語(「PESEL:」、「NIP」、「Steuer-ID」、「date of birth」など)がある場合もスコアが上がります。文脈語だけでも、デフォルトのしきい値に達するほどです。マスクされるのは、機密度の基準を満たすものです。

--sensitivityしきい値効果
balanced0.50デフォルトです。特徴的な形式、検証済みのチェックサム、ラベル付きのものを検出します。
strict0.35より判別しにくい形式を追加します。IBANに似た数字のみの文字列、日付、一般的な識別子などです。
paranoid0.10ルールで検出できるものすべてを対象とします。誤検出も含みます。

チェックサムの失敗が「このエンティティではない」ことを意味する場合(Luhn検証に失敗したカード番号など)、候補は破棄されます。一方、入力ミスを意味する場合(PESELなど)、候補は信頼度を下げて残ります。入力ミスの識別子がクラウドに送られるより、関係のない番号がマスクされるほうが安全です。

以下で✓の付いたルールは、実際のチェックディジットを検証します。

グローバル — すべての言語で有効
タグ検出内容チェックサム
EMAILRFC 5321互換のアドレス
PHONE国際形式
IBAN76か国、ISO 13616の桁数✓ mod-97
VAT_IDEU加盟27か国すべて、およびXI✓ PL、IT、NL、SI、LU
CRYPTO_WALLETBitcoin(Base58Check、bech32)、Ethereum✓ BTC
IMEIモバイルデバイス識別子✓ Luhn
VIN車両識別番号✓ ISO 3779
MACハードウェアアドレス
UUIDUUID / GUID
IPIPv4およびIPv6
URLウェブアドレス
DATE暦日 — 生年月日と解釈される場合にマスク
ポーランド語(pl パック)
タグ検出内容チェックサム
PESEL国民識別番号、11桁✓ Mod-10
NIP税務識別番号 — 請求書上のハイフン区切り、空白区切り、または数字のみの形式✓ Mod-11
REGON事業者登録番号、9桁または14桁✓ Mod-11
ID_CARDDowód osobisty、ABC123456✓
PASSPORT2文字と7桁の数字✓
KWKsięga wieczysta(土地登記簿)✓
KRS裁判所登録番号 — ラベルが必要
IBANPL接頭辞、または数字のみの26桁のNRB✓ mod-97
PHONE+48 / 0048、携帯電話、固定電話
POSTAL_CODEXX-XXX
米国(en パック)
タグ検出チェックサム
SSNハイフン付き形式。ドット区切り、スペース区切り、区切りなしの形式にはラベルが必要です領域/グループ範囲
CREDIT_CARD13~19 桁✓ Luhn
ITIN納税者識別番号
EIN雇用者識別番号
ABA_ROUTING銀行ルーティング番号✓ 3-7-1
PASSPORT9 桁 — ラベルが必要です
DRIVER_LICENSE州ごとの形式 — ラベルが必要です
ZIP_CODEXXXXX / XXXXX-XXXX
その他のヨーロッパのロケール

他のすべてのパックと同様、これらはデフォルトで実行されます。--lang で対象を絞るか、 --extra-locales de,it で選択した言語に隣接するロケールを組み合わせます — ポーランド語の請求書には、ドイツ語とイタリア語の識別子も含まれます。

ロケールタグ検出チェックサム
deDE_TAX_ID納税者識別番号✓ ISO 7064
dePOSTAL_CODE郵便番号
itIT_FISCAL_CODE納税者番号✓
esES_NIF / ES_NIEDNI/NIF および外国人 ID✓ mod-23
frFR_NIR社会保障番号✓ mod-97
nlNL_BSN市民サービス番号✓ elfproef
cz skCZ_SK_BIRTH_NUMBER出生番号✓ mod-11
seSE_PERSONNUMMER個人番号✓ Luhn
fiFI_HETU個人識別コード✓ mod-31
ukUK_NHSNHS 番号✓ mod-11
ukUK_NINO国民保険番号
パターンではなく LLM が検出
タグ検出必要なもの
PERSONフルネームOllama NER(hybrid / llm エンジン)
ORG会社名/組織名Ollama NER(hybrid / llm エンジン)

言語検出

pseudonym-mcp には、franc に基づく軽量なヒューリスティック言語検出器が含まれています。 テキストの内容から言語を推定し、構造化された結果を返します。

detectLanguage('Umowa zostaje zawarta na czas nieokreślony')
// → { detected: 'pl', source: 'text', raw: 'pol', confidence: 0.94 }

detectLanguage('Hello')
// → { detected: 'unknown', source: 'fallback', raw: null, confidence: null }
フィールド説明
detected'pl'、'en'、または 'unknown'
source'text' — franc が実行され、正常にマッピングされた場合。'fallback' — 短すぎる、または判定不能の場合
rawfranc の ISO 639-3 コード(例:'pol')、または null
confidencefranc のスコア(0~1)、または franc が呼び出されなかった場合は null

20 文字未満のテキスト、または信頼度が低いテキストでは、detected: 'unknown' が返されます。 検出器は現在の仮名化パイプラインには影響せず、--lang 設定が引き続き優先されます。 これは、将来の多言語モードや自動選択モードの基盤となるものです。

エンジンモード

モードOllama が必要構造化 PII を検出氏名/組織を検出
regexいいえはいいいえ
llmはいいいえはい
hybrid(デフォルト)はい(正常にフォールバック)はいはい

hybrid モードでは、正規表現による処理の後に Ollama が実行されるため、ローカル NER モデルには、すでにトークン化された構造化識別子が渡されます。Ollama に接続できない場合、サーバーは stderr に警告を記録し、正規表現のみでマスクしたテキストを返します。クラッシュやハングは発生しません。

プライバシーとセキュリティに関する注意事項

誇張を避けた説明:

  • ツール自体からテレメトリーは送信されません。 pseudonym-mcp は、ローカルの Ollama インスタンスおよび(任意で)MCP stdio トランスポートを除き、ネットワークリクエストを行いません。
  • デフォルトではマッピングをメモリ内に保持します。 マッピングストアはディスクに書き込まれません。セッションのスコープはサーバープロセスの実行期間です。
  • セッション中のトークンは一貫しています。 同じ元の値は常に同じトークンにマッピングされます(同じ名前が再度出現しても、[PERSON:1] が [PERSON:2] に変わることはありません)。これにより、LLM の推論における意味の一貫性が保たれます。
  • モデルのトレーニングは行いません。 ローカルの Ollama モデルはオフラインで動作します。このツールによって、データがモデルのトレーニングに使われることはありません。
  • デフォルトでは厳密な検証を行います。 無効な SSN(地域コードが 000/666/900 以上)、Luhn 検査に失敗したクレジットカード番号、チェックサムが無効な PESEL はマスクされません。これにより、OCR エラーや無作為な数字列による誤検出を防ぎます。

以下については保証されません:

  • 入力内のすべての PII が検出されること。
  • トークン化されたテキストから実在の人物を特定できないこと。文脈から再識別できる可能性があります。
  • クラウドプロバイダーが、構造、タイミング、内容から機密情報を知り得ないこと。
  • 特定の規制への準拠。これはツール単体ではなく、システム全体に関わるものです。

制限事項

pseudonym-mcp は技術的なプライバシー制御であり、法令への準拠を保証するものではありません。

  • 検出はベストエフォートです。 偽陰性と偽陽性のどちらも発生する可能性があります(例:「経理の背の高い人」、「大家」、「橋の近くの場所」 のような間接的な表現は検出されません)。あだ名、イニシャル、名前の一部は通常、検出されません。
  • 構造は引き続き送信されます。 金額、トークン間の関係、物語の内容、および検出されなかった PII は、すべてクラウド LLM に送信されます。日付は、生年月日として解釈される場合、または --sensitivity の値を高くした場合にのみマスクされ、それ以外の暦上の日付はそのまま送信されます。トークン化で隠されるのは「誰か」であり、「どのような状況か」ではありません。
  • --lang を絞ると、検出範囲も狭くなります。しかも、情報源では通知されません。 ロケールパックを無効にすると、その識別子は一切検索されません。出力は問題がないように見え、呼び出しも成功として報告されます。サーバーは、stderr と各 mask_text の応答で無効になっているパックを示しますが、後続の処理でそれを必ず確認させる仕組みはありません。
  • マスク前のログ記録は、お使いのアプリケーション側の責任です。 アプリケーションが mask_text に渡す前に平文をログに記録した場合、このツールでは対処できません。
  • マッピングはプロセス内に保持されます。 サーバーを再起動するとセッションは終了し、マッピングは破棄されます。これは意図した動作です。
  • ローカルのマッピングストアにアクセスできる人は、再識別できる可能性があります。また、周辺知識を持つ人は、文脈だけから再識別できる可能性があります。 これは GDPR 第 4 条第 5 項における仮名化であり、匿名化ではありません。
  • 法律上の助言ではありません。 この README の内容は、法律上の助言を構成するものではありません。準拠はシステム全体に関わるものです。具体的な導入については、DPO、コンプライアンスチーム、弁護士にご相談ください。

GDPR 第 4 条第 5 項および前文 26 に基づき、仮名化されたデータも引き続き個人データです。pseudonym-mcp は、PII が平文で露出するリスクを大幅に低減しますが、法的義務をなくすものではありません。

開発

git clone https://github.com/woladi/pseudonym-mcp
cd pseudonym-mcp
npm install
npm run build    # tsc compile
npm test         # vitest (no Ollama required)

テストスイートは完全にオフラインで実行できます。Ollama の呼び出しはコンストラクター経由で注入され、すべてのテストでモックされます。稼働中の LLM は必要ありません。

認識器の追加
  1. src/patterns/locale/<lang>/(言語に依存しない場合は src/patterns/global/)にファイルを追加し、PatternRule をエクスポートします。
export const regonRule: PatternRule = {
  id: 'pl.regon',
  entityType: 'REGON',
  patterns: [
    { name: 'REGON (14 digits)', regex: /\b\d{14}\b/g, score: 0.25 },
    { name: 'REGON (9 digits)', regex: /\b\d{9}\b/g, score: 0.15 },
  ],
  locales: ['pl'],
  context: ['regon', 'nr regon', 'gus'],
  description: 'Polish business register number',
  validate: regonChecksum,
  checksumMode: 'boost',
}
  1. src/patterns/index.ts の allPatterns 配列に登録します。配線が必要なのはここだけです。ルールセットはロケールごとにレジストリーから導出されるため、ほかにルールの存在を知らせる必要はありません。
  2. 新しい言語を追加する場合は、src/patterns/types.ts の SupportedLocale と SUPPORTED_LOCALES の両方にロケールを追加します。デフォルトの選択ではこのリストが読み込まれるため、リストに含まれていないパックは実行されません。また、src/language/language-map.ts に ISO 639-3 から短縮コードへのマッピングを追加します。tests/default-coverage.test.ts は両者に不整合があると失敗し、各パックに標本が 1 つあることを求めます。

スコアの選び方:まずは低めに設定し、判定は証拠に委ねます。このエンティティにしか現れない形式(ABC123456、codice fiscale など)なら、0.5 付近から始められます。何にでもなり得る数字の並びなら、0.15〜0.25 から始め、validate または context で基準を満たすようにします。

checksumMode の選び方:チェックに失敗した場合、そのエンティティではあり得ないものとして扱う場合(カードの Luhn)は filter、チェック失敗がタイプミスを意味し、見逃すとデータが漏えいする場合(PESEL、IBAN)は boost、チェックでは不可能な値を除外できるだけで何も確認できない場合(米国の SSN の範囲)は gate を選びます。

3 つすべての例については、src/patterns/locale/pl/ をご覧ください。

コントリビューション

コントリビューションを歓迎します。コミットメッセージには Conventional Commits に従ってください。

リリースは changesets で実施します。変更がユーザーに見える場合は、プルリクエストに変更セットを追加してください。

npm run changeset   # pick patch / minor / major, describe the change

master へのマージ後、「version packages」プルリクエストが作成され、バージョン、server.json、src/version.ts、変更履歴が更新されます。そのプルリクエストをマージすると、npm への公開(プロベナンス付きの Trusted Publishing)、リリースのタグ付け、MCP レジストリーエントリーの更新が行われます。保留中の変更セットがない状態でマージしても、何も公開されません。

言語パックのコントリビューションを特に歓迎します。ドイツ語(Personalausweis、Steuer-ID)、フランス語(NIR、SIRET)、スペイン語(DNI/NIE)などが加われば、このツールの有用性が大きく広がります。

ライセンス

MIT — Adrian Wolczuk

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。