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 分