Lokal

io.github.lokal-id由 GitHub 帳號 lokal-id 發布v0.18.0更新於 Nov 5, 2026

Indonesia-ready agents: NIK, phone, Rupiah, regions, postal codes, holidays, and PII masking.

概覽

AI 產生的概覽

本機 MCP 伺服器,讓助理具備印尼在地化能力:NIK/NPWP/電話驗證、印尼盾金額、行政區、郵遞區號、假日,以及檔案個資遮蔽。

可以做什麼
  • 驗證並解析印尼識別資料:NIK、NPWP、電話號碼與 QRIS 付款碼,並可為 QRIS 設定金額
  • 格式化與解析印尼盾金額,包含簡寫與 terbilang 文字寫法
  • 依名稱搜尋並列出從省到村的 Kemendagri 行政區、查詢郵遞區號,並把自由文字地址解析為區碼
  • 列出國定假日與共同休假,並進行工作日計算,用於 SLA 與交期估算
  • 掃描檔案與目錄中的印尼個資,以穩定佔位符讀取檔案、寫出還原真實值的結果,並產生匿名副本
  • 清空工作階段保險庫,讓已保存的真實值被遺忘
適用情境
適合處理印尼資料或服務印尼使用者的情境:地址表單、發票、電話與證件驗證、交期或 SLA 日期,以及含個資、不宜直接貼進對話的正式環境匯出檔。
執行需求
以 stdio 在本機執行,通常透過 npx 使用 npm 套件,需要 Node.js。選用環境變數 LOKAL_ROOT 指定檔案工具可存取的資料夾,預設為工作目錄。未宣告需要帳號、API 金鑰或身分驗證。
安裝前請注意
檔案工具會在本機讀寫真實個資;write_file_unmasked 會把真實值寫回輸出檔,anonymize_file 會產生正式資料的副本。保險庫在呼叫 clear_vault 或達到設定的 TTL 前保留真實值。防護機制被說明為並非沙箱,無法掃描的檔案不應視為乾淨。原則設定可允許某類資料維持未遮蔽。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Lokal,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

@lokal-id/mcp

[npm] [License: MIT]

An MCP server that makes AI agents Indonesia-ready: accurate Kemendagri region and postal code data, holidays and working days, NIK/NPWP/phone validation, Rupiah, and Samaran, which lets an agent read production files (CSV, Excel, logs) with Indonesian personal data replaced by stable placeholders, then write results with the real values restored, locally.

Part of Lokal · Bahasa Indonesia

Install

Claude Code: install the Lokal plugin, which bundles this server with the skills and the Samaran Guard hook:

text
/plugin marketplace add lokal-id/lokal/plugin install lokal@lokal

Cursor, Claude Desktop, Windsurf (.cursor/mcp.json, claude_desktop_config.json, ~/.codeium/windsurf/mcp_config.json):

json
{  "mcpServers": {    "lokal": {      "command": "npx",      "args": ["-y", "@lokal-id/mcp"],      "env": { "LOKAL_ROOT": "/path/to/your/project" }    }  }}

VS Code (.vscode/mcp.json):

json
{ "servers": { "lokal": { "type": "stdio", "command": "npx", "args": ["-y", "@lokal-id/mcp"] } } }

Codex CLI (~/.codex/config.toml):

toml
[mcp_servers.lokal]command = "npx"args = ["-y", "@lokal-id/mcp"]

The file tools can only access LOKAL_ROOT, which defaults to CLAUDE_PROJECT_DIR and then to the working directory.

Tools

ToolWhat it does
parse_nikValidate and decode a NIK: region, date of birth, sex
validate_npwp15-digit NPWP, 16-digit NPWP, or NIK as NPWP
parse_phoneNormalize numbers, display format, WhatsApp link, carrier
parse_qrisRead and check a QRIS payload: merchant, NMID, criteria, amount, CRC
set_qris_amountMake a merchant's QRIS dynamic for an amount, with a new CRC
format_rupiah / parse_rupiahRp1.500.000, terbilang; "2jtan", "goceng" → numbers
search_wilayah / list_wilayahProvinces down to villages, with codes and postal codes
lookup_kode_posPostal code → villages, or all postal codes in a region
parse_alamatFree-text address → street, RT/RW, kode pos, and Kemendagri regions with their codes
list_holidays / working_daysNational holidays and cuti bersama (SKB 2026, 2027), SLA due dates
scan_piiScan files and folders for personal data by UU PDP category; reports files it could not scan
read_file_maskedRead a file (text, CSV, JSON, logs, .xlsx, .docx, text-based PDF) with personal data replaced by placeholders
write_file_unmaskedWrite results with placeholders restored, on your machine
anonymize_fileCopy dumps and exports with realistic stand-ins for every person (Kembaran), returning only counts
clear_vaultForget every real value behind this session's placeholders

parse_alamat, parse_nik, parse_phone, and validate_npwp also take placeholders from read_file_masked ([ADDRESS_3], [NIK_1]): the real value is read on your machine, and the street, RT/RW, and birth date come back as placeholders.

Samaran Guard

lokal-mcp guard is a hook for Claude Code (UserPromptSubmit, and PreToolUse for Read|Grep|Bash|PowerShell), Codex CLI, Cursor, and Gemini CLI. It holds prompts that contain NIK, bank account, or health data, blocks commands that would print a personal data file into the conversation, pointing the agent to read_file_masked instead, and asks before photos of documents and scanned PDFs are read. The Lokal plugin sets it up for Claude Code; see the Lokal README for the other agents.

SAMARAN_GUARD=strict makes it fail closed: what it cannot check is blocked, including a guard error, a file it cannot read, output from beyond the first 5 MB of a big file, a Grep over more than 300 data files, and a check that runs longer than 7 seconds. One-liners such as python -c and node -e that name a data file with personal data are blocked like cat. It is still not a sandbox; see Defense in depth.

Your own data types

.samaran.json at the project root adds rules for formats Samaran cannot know, and values it must never mask. The file tools, the guard, lokal-mcp scan, and the proxy read it; restart the server after changing it. A broken file makes the file tools fail rather than mask less.

json
{  "custom": [    { "label": "EMPLOYEE_ID", "pattern": "\\bEMP-\\d{6}\\b" },    { "label": "MEMBER", "fields": ["no_member"] }  ],  "allow": ["0800-1-500-500"]}

Policy and audit

.samaran.json can also set an action per type: "policy": { "HEALTH": "block", "FINANCIAL": "allow", "PASSPORT": "route" }. block makes read_file_masked refuse a file and the proxy refuse a request; allow leaves a type unmasked; route sends proxied requests to a local model ("route": { "openai": "http://127.0.0.1:11434/v1", "model": "llama3.2" }). "audit": true writes one JSON line per decision, with counts and never values, to .lokal/audit.jsonl; lokal-mcp audit summarizes it. "vaultTtlMinutes": 60 makes the server forget the real values after an hour without use. "guard": "strict" turns on the strict guard for everyone in the repository. "ner": true adds Lokal's NER model for names without a label or cue, such as "saya di chat sama rianto surya", to read_file_masked, scan_pii, and the proxy; install it next to the server with npx -y -p @lokal-id/mcp -p @lokal-id/ner lokal-mcp (see @lokal-id/ner). See the Lokal README.

lokal-mcp init --profile personal|team|strict [--dry-run] writes .samaran.json, .samaranignore, and for teams a GitHub workflow; lokal-mcp check validates the configuration and explains what it does.

Kembaran

lokal-mcp kembaran prod.sql export.csv -o staging/ [--secret <text>] [--keep FINANCIAL] [--keep-birth-date] [--dry-run] copies text files with every person replaced by a realistic stand-in, consistent across the files. See the Lokal README.

Lokal Scan

lokal-mcp scan --staged (pre-commit) or lokal-mcp scan --diff origin/main (CI) fails when a commit or pull request adds Indonesian personal data, and shows each value partially masked. lokal-mcp scan [path…] checks whole files. There is a GitHub Action too: uses: lokal-id/[email protected]. See the Lokal README.

Remote (HTTP) mode

lokal-mcp serve --http --port 3000 serves the data tools over Streamable HTTP at /mcp, for ChatGPT and Claude.ai connectors. The file tools are not available in this mode.

Samaran Proxy

lokal-mcp proxy is an OpenAI- and Anthropic-compatible endpoint for apps: it masks Indonesian personal data in every request and restores it in the response, streaming and tool calls included.

sh
npx @lokal-id/mcp proxy --port 8787 [--openai <url>] [--anthropic <url>] [--exclude FINANCIAL]

Then use OpenAI(base_url="http://127.0.0.1:8787/v1") or Anthropic(base_url="http://127.0.0.1:8787"). Requests with images or files inside are forwarded with x-samaran-status: partial, since they cannot be checked; --strict refuses them. See the Lokal README.

License

MIT. The library is @lokal-id/core.

來源:packages/mcp/README.md,提交 b83e92a

工具

18

parse_nik

Parse NIK

Validate an Indonesian NIK (16-digit national ID) by structure and decode it: province, regency, district (Kemendagri), birth date, gender. Cannot prove the NIK is registered at Dukcapil. Also takes a [NIK_n] placeholder from read_file_masked; then the birth date and gender come back as placeholders.

輸入參數nik必填

validate_npwp

Validate NPWP

Validate the format of an Indonesian tax ID (NPWP): legacy 15-digit (XX.XXX.XXX.X-XXX.XXX), 16-digit, or NIK used as NPWP for individuals. Returns the 16-digit form DJP uses today. Format only: it does not check the DJP check digit or registration. Also takes an [NPWP_n] or [NIK_n] placeholder from read_file_masked; then only validity and kind come back.

輸入參數npwp必填

parse_qris

Read a QRIS payload

Read and check a QRIS payload (the text inside an Indonesian QRIS payment code): static or dynamic, merchant name and city, NMID, merchant criteria (UMI, UKE, …), amount, tip, merchant accounts, and whether the CRC checksum is intact. Errors and warnings are in Indonesian.

輸入參數payload必填

set_qris_amount

Make a dynamic QRIS with an amount

Turn a QRIS payload into a dynamic one for an amount in Rupiah, optionally with a fixed or percentage fee, and recompute the CRC. The merchant account is unchanged. Returns the new payload, ready to render as a QR code.

輸入參數amount必填payload必填fee_fixedfee_percent

parse_phone

Parse Indonesian phone number

Normalize an Indonesian phone number (08xx, +62, 62, landline with area code) to E.164, display format, WhatsApp form, and original operator (Telkomsel, Indosat, Tri, XL, Axis, Smartfren). Also takes a [PHONE_n] placeholder from read_file_masked; then only validity, type, and operator come back.

輸入參數phone必填

format_rupiah

Format Rupiah

Format an amount as Rupiah the Indonesian way (Rp1.500.000), short form (Rp1,5 jt), and spelled out in words (terbilang) for receipts and invoices.

輸入參數amount必填

parse_rupiah

Parse Rupiah from text

Turn how Indonesians write money into a number: "Rp 25.000", "1,5jt", "2jtan", "50rb-an", "goceng", "2 ceban", "sejuta setengah", "dua ratus ribu". "m" means miliar.

輸入參數text必填

search_wilayah

Search Indonesian regions

Find official Kemendagri regions by name (provinsi, kabupaten/kota, kecamatan, kelurahan/desa) with their codes. Use for address forms, normalizing user-typed locations, and seed data instead of inventing region names.

輸入參數levellimitquery必填within

list_wilayah

List child regions

List the direct children of a region code — the data behind cascading address dropdowns. No code returns the 38 provinces; "32" returns the kabupaten/kota in Jawa Barat, and so on down to kelurahan/desa (which include their kode pos).

輸入參數parent_code

lookup_kode_pos

Look up kode pos

Kode pos (postal codes) for Indonesian regions. Give kode_pos to find the kelurahan/desa that use it, or wilayah_code to list every kode pos inside a region (e.g. "32.76" Kota Depok).

輸入參數kode_poswilayah_code

parse_alamat

Parse an Indonesian address

Read a free-text Indonesian address ("Jl. Margonda Raya No. 100 RT 3/5 Kemiri Muka, Beji, Depok 16423") into street, RT/RW, kode pos, and the Kemendagri kelurahan/desa, kecamatan, kabupaten/kota, and provinsi with their codes. Handles missing commas, capitals, abbreviations (Kel., Kec., Kab., Jaksel, Jabar), and left-out levels. Check confidence and issues: a kode pos that belongs to another kelurahan, a village that is not in the data, or an address that fits more than one region (see alternatives). Use it to clean address columns, fill region dropdowns from typed addresses, or check a shipping address. Also takes an [ADDRESS_n] placeholder from read_file_masked: the regions come back as they are, the street and RT/RW as placeholders.

輸入參數address必填

list_holidays

Indonesian public holidays

Libur nasional and cuti bersama for a year, from the official SKB 3 Menteri. Only years with a published decree are available; other years return an error rather than a guess.

輸入參數year必填

working_days

Indonesian working days

Working-day math with Indonesian holidays: is a date a working day, the date N working days after a date (SLA, delivery estimates), or the number of working days between two dates (inclusive). Cuti bersama counts as a day off unless cuti_bersama is false (private companies may work on them).

輸入參數toaddfrom必填cuti_bersama

scan_pii

Scan files for Indonesian personal data

Scan a file or directory (text, CSV, JSON, logs, SQL, .xlsx, .docx, text-based PDF; up to 100 MB each) for personal data — identifiers (NIK, KK, NPWP, phone, email, bank account, BPJS, passport, SIM, NIP, card, plate, IP, coordinates), names, addresses, birth dates, and UU PDP categories (religion, marital status, gender, nationality, health, finances, criminal records, biometrics) — without revealing it: findings show only partially masked values. Also lists files and pages it could not scan (scanned or encrypted PDFs, .xls, .doc, images, archives). Use before reading data files and before committing fixtures or seeders.

輸入參數pathtypesmin_confidence

read_file_masked

Read a file with personal data masked

Read a text, CSV, JSON, log, .xlsx, .docx, or text-based PDF file with Indonesian personal data replaced by stable placeholders ([NIK_1], [PHONE_2], [RELIGION_1] …). The same value always gets the same placeholder in this session, so joins, counts, grouping and deduplication still work. Real values never leave the user's machine. Spreadsheets come back as CSV, one block per sheet; Word documents as their text plus each table as CSV; PDFs one block per page.

輸入參數path必填max_linesstart_line

write_file_unmasked

Write a file, restoring masked values

Write content to a file after replacing Samaran placeholders ([NIK_1], [PHONE_2] …) from this session with the real values, locally. Use this to produce output (cleaned CSV, reports, migrations) from data you read with read_file_masked.

輸入參數path必填content必填overwrite

anonymize_file

Make a copy of data with realistic stand-ins (Kembaran)

Copy SQL dumps, CSV, JSON, or log files with every piece of Indonesian personal data replaced by a realistic stand-in, for staging, seeders, tests, or sharing with a vendor: valid NIKs in the same district with the same gender and birth year, phone numbers on the same operator, names with the same casing, addresses in the same area, e-mail at example.com. The same value gets the same stand-in in every file of one call, so joins still work. Runs on the user's machine and returns only counts: you never see the data. Use it instead of reading production data, and when asked to copy production data to staging or build seeders from it.

輸入參數keeppaths必填output必填secretdry_runoverwritekeep_birth_date

clear_vault

Forget the masked values

Forget every real value behind the placeholders this session gave out. Afterwards write_file_unmasked can no longer restore them, and reading a file again gives new placeholders. Use it when a task with real data is done, or when the user asks to forget the data.

無需輸入參數。

版本歷史

14
  1. v0.18.0最新Nov 5, 2026
  2. v0.17.0Oct 31, 2026
  3. v0.15.0Oct 31, 2026
  4. v0.14.0Oct 30, 2026
  5. v0.12.0Oct 30, 2026
  6. v0.9.0Oct 29, 2026
  7. v0.7.2Oct 26, 2026
  8. v0.7.0Oct 26, 2026
  9. v0.6.0Oct 23, 2026
  10. v0.5.0Oct 22, 2026
  11. v0.4.1Oct 21, 2026
  12. v0.4.0Oct 17, 2026
  13. v0.3.0Oct 17, 2026
  14. v0.2.2Oct 16, 2026

評分

已滿足適用檢查項的 90%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分
  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分