kustodyan-mcp
# kustodyan-mcp
A [Model Context Protocol](https://modelcontextprotocol.io) server for the
**[Kustodyan](https://kustodyan.com) data-protection API** (powered by the RegData
Protection Suite). It lets AI agents and applications **protect, unprotect and search**
sensitive data through Kustodyan's contextual transform engine — so data is tokenized,
encrypted, anonymized or masked according to *who* is asking (a role) and *what*
operation is requested, with every call auditable.
It speaks two transports from one binary:
- **stdio** (default) — for local use (`node dist/server.js` after a build)
- **streamable HTTP** — for hosting behind a reverse proxy (set `KUSTODYAN_MCP_TRANSPORT=http`)
## What it does
The Kustodyan Engine exposes a single `transform` operation; behaviour is selected by
**evidence** you send — a `Role` (who) and an `Action` (`Protect` / `Unprotect` / `Search`)
— plus the `(className, propertyName)` of each value. The transformation technique
(encryption, tokenization, anonymization, masking) is configured server-side, never chosen
by the caller. This server wraps that model in ergonomic tools.
### Tools
| Tool | Purpose |
|------|---------|
| `protect` | Protect field values for a role (encrypt / tokenize / anonymize per config). |
| `unprotect` | Reverse a protection to recover the original — **returns cleartext, treat as sensitive**. |
| `search` | Get a search token to match against a stored protected value. |
| `transform` | Low-level escape hatch: send a full Engine transform payload. |
| `validate_transform` | Statically validate a transform payload before sending. |
| `whoami` | Confirm credentials and the configured environment (decoded token claims). |
| `health` | Check Identity + Engine reachability. |
| `list_data_model` | Discover the configured classes, properties, roles and actions. |
### Resources & prompts
- `kustodyan://guide/contextualisation` — how Role/Action evidence drives transforms.
- `kustodyan://guide/best-practices` — safe, effective use of protect/unprotect.
- `kustodyan://data-model` — the configured data-model manifest.
- Prompt `protect_record` — guides an assistant to protect a record's PII safely.
## Configuration (environment)
| Variable | Required | Description |
|----------|----------|-------------|
| `KUSTODYAN_IDENTITY_URL` | yes | e.g. `https://<env>.kustodyan.io/api/identity` |
| `KUSTODYAN_ENGINE_URL` | yes | e.g. `https://<env>.kustodyan.io/api/engine` |
| `KUSTODYAN_CLIENT_ID` | yes | Engine API client id (from the CoreAdmin portal) |
| `KUSTODYAN_CLIENT_SECRET` | yes | Engine API client secret |
| `KUSTODYAN_DATA_MODEL` | no | Path to a data-model manifest JSON (classes/properties/roles) |
| `KUSTODYAN_MCP_TRANSPORT` | no | `stdio` (default) or `http` |
| `KUSTODYAN_HTTP_HOST` / `KUSTODYAN_HTTP_PORT` | no | HTTP bind (default `127.0.0.1` / `9090`) |
## Run locally (stdio)
```bash
npm install && npm run build
KUSTODYAN_IDENTITY_URL=https://<env>.kustodyan.io/api/identity \
KUSTODYAN_ENGINE_URL=https://<env>.kustodyan.io/api/engine \
KUSTODYAN_CLIENT_ID=... KUSTODYAN_CLIENT_SECRET=... \
node dist/server.js
```
MCP client config (stdio):
```json
{
"mcpServers": {
"kustodyan": {
"command": "node",
"args": ["/path/to/kustodyan-mcp/dist/server.js"],
"env": {
"KUSTODYAN_IDENTITY_URL": "https://<env>.kustodyan.io/api/identity",
"KUSTODYAN_ENGINE_URL": "https://<env>.kustodyan.io/api/engine",
"KUSTODYAN_CLIENT_ID": "...",
"KUSTODYAN_CLIENT_SECRET": "..."
}
}
}
}
```
## Run hosted (HTTP, token-gated image)
The container serves streamable HTTP behind an nginx bearer-token gate.
```bash
docker run -d -p 8080:8080 \
-e MCP_BEARER_TOKEN=<a long random secret> \
-e KUSTODYAN_IDENTITY_URL=https://<env>.kustodyan.io/api/identity \
-e KUSTODYAN_ENGINE_URL=https://<env>.kustodyan.io/api/engine \
-e KUSTODYAN_CLIENT_ID=... -e KUSTODYAN_CLIENT_SECRET=... \
<image>
```
Point your MCP client at `https://<host>/mcp`, sending `Authorization: Bearer <MCP_BEARER_TOKEN>`
(or `?token=<MCP_BEARER_TOKEN>`). `GET /healthz` is unauthenticated for probes.
## Build
```bash
npm install
npm run build # -> dist/
npm start # stdio
```
## Safety notes
- `unprotect` returns cleartext. Never log it, persist it, or call it for an unauthorised
role. Prefer a masking role when a partial value suffices.
- Discover the data model (`list_data_model`) instead of guessing `propertyName`s.
- Transform calls can succeed (HTTP 200) while individual fields carry a per-field `error` —
always inspect per-field results.
## Documentation
- [`docs/rps-api-contract.md`](docs/rps-api-contract.md) — the verified Identity + Engine API contract (auth, `/transform` shape, error model) this server is built on.
- [`docs/searchable-encryption.md`](docs/searchable-encryption.md) — the searchable-encryption guide: the scheme/leakage ladder, and the PROPE search-band contract (`dependencyContext {method,min,max}`, case-insensitive collation).
## License
MIT
TDQS
Scored across 8 tools
Each tool targets a distinct operation: health checks connectivity, whoami retrieves JWT claims, list_data_model discovers schema, protect/unprotect field values, search for tokens, transform for advanced payloads, validate_transform for payload validation. No overlapping purposes.
Tools use lowercase with underscores, but naming patterns vary: some are single verbs (health, protect, search), others verb_noun (list_data_model, validate_transform), and 'whoami' breaks the pattern. Mostly consistent but with minor deviations.
8 tools are a reasonable scope for a data protection API: discovery, protection, unprotection, search, transform, validation, health, and identity checks. Not too many or too few.
Core protection lifecycle (protect, unprotect, search) is covered, along with discovery and validation. Missing tools for updating or deleting protections, and no admin tools for role/policy management, but these may be out of scope for the client.