Skip to main content
Glama
fairquery-llm-data-guard

fairquery-llm-data-guard

Official

LLM Data Guard: PII & Secret Redactor

Finds and redacts PII and secrets in text, HTML and PDF before they reach an LLM, for $1.50 per 1,000 documents plus $0.20 per 1,000 findings. Error rows are free. Example: 1,000 support tickets with 2 findings each on average cost 1,000 × $0.0015 + 2,000 × $0.0002 = $1.90, plus Apify's $0.00005 Actor-start fee.

Fairquery Apify Actor that finds and redacts personal data (PII) and secrets in text, HTML and PDF documents before they are sent to an LLM, a vector store or a third-party API. Each document gets a risk level, a list of findings (type, offsets, masked preview) and, in redact mode, a clean output_text you can pass straight to your model. Placeholders are reversible, so you can put the original values back into the model's answer.

No data retention. The Actor never logs document content and writes nothing except the result rows in the run's default dataset and a counts-only RUN_SUMMARY. By default it also deletes the run's INPUT record when it finishes (purgeInputAfterRun). Raw values are not stored in findings unless you turn on includeFindingValues. You control the dataset; delete it (or the run) once you have read it.

When to use this Actor (LLM / tool selection)

Use it when an agent or pipeline needs to:

  • Strip emails, phone numbers, SSNs, card numbers, IBANs and IP addresses from prompts, chat logs, support tickets or CRM notes before LLM calls

  • Catch leaked credentials (AWS, GitHub, Slack, Stripe, OpenAI, Anthropic, Google, private keys, JWTs, password=...) in logs, code snippets and config files

  • Pseudonymise a dataset for RAG / fine-tuning with consistent placeholders ([EMAIL_1], [PERSON_2])

  • Restore real values in the LLM's answer (mode: "restore")

  • Audit a batch of documents for sensitive-data risk (mode: "detect")

Covers OWASP LLM Top 10 risk LLM02 Sensitive Information Disclosure at the input side. It is a pattern-and-validation engine, not a guarantee: always keep defense in depth.

Related MCP server: classifinder-mcp

What it detects

Category

Types

Validation

PII

EMAIL, PHONE, SSN, CREDIT_CARD, IBAN, UK_NINO, IP_ADDRESS

Luhn + issuer prefix for cards, mod-97 + country length for IBANs, libphonenumber for phones, SSN area/group rules

PII (opt-in)

PERSON, LOCATION, STREET_ADDRESS

Open-source NER (compromise, English) and street-address patterns

Secrets

PRIVATE_KEY, AWS_ACCESS_KEY, AWS_SECRET_KEY, GITHUB_TOKEN, GITLAB_TOKEN, SLACK_TOKEN, SLACK_WEBHOOK, STRIPE_KEY, GOOGLE_API_KEY, OPENAI_KEY, ANTHROPIC_KEY, HUGGINGFACE_TOKEN, NPM_TOKEN, SENDGRID_KEY, TWILIO_KEY, AZURE_STORAGE_KEY, JWT, URL_CREDENTIALS

Gitleaks-style rules

Secrets

GENERIC_SECRET (api_key=…, token: …, password=…)

Shannon-entropy threshold (minEntropy), placeholder words ignored

Secrets (opt-in)

HIGH_ENTROPY_STRING

Any 20+ char random-looking token

Overlapping matches are resolved (secrets > cards/IBAN > SSN > email > IP > phone > address > names), so a card number is never also reported as a phone.

Redaction styles

redactionStyle

Example

Reversible

placeholder (default)

Mail [EMAIL_1] or call [PHONE_1]

Yes, with returnPlaceholderMap and mode: "restore"

mask

j***@a***.com, **** **** **** 1111, AKIA****************

No

hash

[EMAIL:3f2a9c0d1b7e] (salted SHA-256 prefix)

No; stable across runs if you set hashSalt

Input

Field

Type

Default

Description

texts

string[]

– (sample texts are prefilled in the Console form)

One document per entry

documents

{id,text}[]

[]

Documents with your own IDs

documentUrls

string[]

[]

Public txt/md/json/csv/HTML/PDF URLs (max 10 MB each)

mode

detect | redact | restore

redact

redactionStyle

placeholder | mask | hash

placeholder

returnPlaceholderMap

bool

false

Adds placeholder_map per row (contains originals!)

consistentPlaceholders

bool

true

Same value → same placeholder across documents

placeholderMap

object

{}

For restore mode

hashSalt

secret string

random per run

For hash style

detectPii / detectSecrets

bool

true

detectNames / detectAddresses / entropyScan

bool

false

Opt-in detectors

minEntropy

number

3.5

Bits/char for generic secrets

defaultPhoneRegion

string

US

For numbers without + prefix

entityTypes

string[]

all

Only report these types

allowList

string[]

[]

Values never flagged

includeFindingValues

bool

false

Put raw values into findings

maxDocuments / maxCharsPerDocument

int

1000 / 1,000,000

Safety caps

purgeInputAfterRun

bool

true

Delete the INPUT record at the end

{
  "texts": ["Hi, I'm Jane (jane.doe@acme.com, +1 415-555-0132). Card 4111 1111 1111 1111.", "AWS key AKIAIOSFODNN7EXAMPLE"],
  "mode": "redact",
  "redactionStyle": "placeholder",
  "returnPlaceholderMap": true
}

Output

One row per document (type: "document") plus machine-readable type: "error" rows.

{
  "schema_version": "1.0.0", "type": "document", "id": "text-1", "source": "text", "chars": 78, "mode": "redact",
  "finding_count": 3, "risk_level": "high",
  "counts_by_type": { "EMAIL": 1, "PHONE": 1, "CREDIT_CARD": 1 },
  "findings": [
    { "type": "EMAIL", "category": "pii", "rule": "email", "confidence": "high", "start": 14, "end": 31, "length": 17, "preview": "j***@a***.com", "replacement": "[EMAIL_1]" }
  ],
  "output_text": "Hi, I'm Jane ([EMAIL_1], [PHONE_1]). Card [CREDIT_CARD_1].",
  "placeholder_map": { "[EMAIL_1]": "jane.doe@acme.com", "[PHONE_1]": "+1 415-555-0132", "[CREDIT_CARD_1]": "4111 1111 1111 1111" }
}

risk_level: high = any secret or card/SSN/IBAN/NI number; medium = other confident PII; low = only low-confidence (NER/entropy) hits; none.

Restore after the LLM answers:

{ "mode": "restore", "texts": ["Dear [EMAIL_1], your card ending [CREDIT_CARD_1]…"], "placeholderMap": { "[EMAIL_1]": "jane.doe@acme.com" } }

Error codes: INPUT_EMPTY, MAX_DOCUMENTS_EXCEEDED, PLACEHOLDER_MAP_MISSING, DOCUMENT_FETCH_FAILED (error rows are never charged).

RUN_SUMMARY (key-value store) holds counts per type, charged events and settings only, never text.

Pricing

Event

Price

When

document-processed

$0.0015

Each document scanned (or restored)

finding

$0.0002

Each PII or secret finding reported

apify-actor-start

$0.00005

Once per run, per GB of memory (Apify default event)

Error rows (INPUT_EMPTY, MAX_DOCUMENTS_EXCEEDED, PLACEHOLDER_MAP_MISSING, DOCUMENT_FETCH_FAILED) are never charged.

Worked example: 1,000 support tickets, 2,000 findings in total: 1,000 × $0.0015 + 2,000 × $0.0002 = $1.50 + $0.40 = $1.90, plus $0.00005. A clean document with no findings costs $0.0015.

To cap spend, set a maximum cost per run in the run options; the Actor stops cleanly at the limit. entityTypes limits which types are reported (and charged as findings); maxDocuments caps documents per run. Prices are from the Store pricing tab (October 2026).

Memory / runtime

Runs at a fixed 512 MB (default and maximum; enough for NER), no browser, no proxy. Regex detectors process roughly 1–5 MB of text per second; NER (detectNames) is slower, so split very big batches into several runs. PDFs are parsed with unpdf (text layer only, no OCR).

MCP / Apify tools

Call from agents via Apify MCP with Actor fairquery/llm-data-guard-pii-secrets:

{ "actor": "fairquery/llm-data-guard-pii-secrets", "input": { "texts": ["..."], "mode": "redact" } }

Local stdio MCP server

The mcp/ folder is a stdio MCP server (TypeScript, @modelcontextprotocol/sdk) with four tools, all with annotations and output schemas:

Tool

What it does

Cost

guard_text

Runs this Actor and returns the dataset rows. With waitForFinish: false it starts the run and returns the run ID at once

Needs APIFY_TOKEN, billed per event (same price either way)

get_run_results

Status and paged rows of a run started with waitForFinish: false

Free, needs APIFY_TOKEN (reads your own run)

list_detectors

Lists detectable types, redaction styles and events

Free, local, no token

restore_placeholders

Puts original values back into placeholder text (e.g. an LLM answer) from a placeholder_map; same output as guard_text mode="restore", without a run

Free, local, no token

Typical round trip: list_detectors (optional) -> guard_text (redact, returnPlaceholderMap: true) -> [get_run_results if you used waitForFinish: false] -> send the redacted text to your LLM -> restore_placeholders on the answer. Only guard_text is billed; list_detectors and restore_placeholders run entirely on your machine.

cd mcp && npm install && npm run build
node scripts/smoke-test.mjs                          # initialize + tools/list + free tool, no token
APIFY_TOKEN=... node scripts/smoke-test.mjs --call   # real call
{ "mcpServers": { "fairquery-llm-data-guard": { "command": "node", "args": ["/path/to/llm-data-guard-pii-secrets/mcp/dist/index.js"], "env": { "APIFY_TOKEN": "<your Apify token>" } } } }

Docker: the root Dockerfile builds the MCP server image (docker build -t fairquery-llm-data-guard-mcp .); it is also the Dockerfile for Glama. The Actor image is .actor/Dockerfile. glama.json lists fairquery as maintainer.

Works well after

  • Website Content Crawler: scrub crawled pages before you index them in a vector store or send them to an LLM.

  • Exports of support tickets, chat logs or CRM notes: run them through mode: "redact" first, call your model, then put the real values back with mode: "restore".

Troubleshooting

Symptom

Cause and fix

Run ends with only an INPUT_EMPTY row

No texts, documents or documentUrls. The sample texts in the Console form are prefill only and are not sent with API calls.

DOCUMENT_FETCH_FAILED

The URL was unreachable, over 10 MB or not a supported type. These rows are free. Scanned PDFs have no text layer, and there is no OCR.

PLACEHOLDER_MAP_MISSING in restore mode

Pass the placeholderMap returned by the redact run (returnPlaceholderMap: true).

Too many findings (false positives)

Add known-safe values to allowList, raise minEntropy, keep entropyScan off, or limit entityTypes.

Names or addresses not detected

detectNames and detectAddresses are opt-in and English-focused. Turn them on (they are slower; split very big batches).

FAQ

Is document content stored?

Content is never logged. Results go only to your run's dataset, RUN_SUMMARY holds counts only, and the INPUT record is deleted at the end by default (purgeInputAfterRun). Delete the dataset when you no longer need it.

Can I get the original values back after the LLM answers?

Yes. Use redactionStyle: "placeholder" with returnPlaceholderMap: true, then run mode: "restore" with that map.

Does it catch everything?

No. It is a pattern-and-validation engine (checksums, issuer prefixes, Gitleaks-style rules). Unusual formats, non-English names and images can slip through. Use it as one layer of defence.

How much does it cost?

$0.0015 per document plus $0.0002 per finding, and a $0.00005 start fee. Error rows are free.

Can I scan text without sending it to Apify?

Yes. The Fairquery MCP server (npx -y @fairquery/agent-trust-mcp) has free pii_scan_text and pii_redact_text tools that run locally. This Actor adds names and addresses, URLs, PDFs and restore mode.

Fairquery family

Other Fairquery tools on the Apify Store. Each one returns structured rows with machine-readable error codes, and error rows are never charged:

All Fairquery Actors: https://apify.com/fairquery

Kits & templates

Optional guides from Fairquery for wiring this Actor into agents and automations (the Actor works fine without them):

Disclaimer

Pattern-based detection finds most but not all sensitive data: unusual formats, non-English names, images and scanned PDFs (no OCR) can slip through, and some matches are false positives. Use it as one layer of a data-protection program, not as a compliance certification. Only process data you are allowed to process.

Development

Local development, tests and deployment notes are in DEVELOPMENT.md. The Apify Store README is .actor/README.md.

Available Tools

4 tools
get_run_resultsPoll a background guard_text run (free read)A
Read-onlyIdempotent
Inspect

WHAT: Read-only follow-up to guard_text(waitForFinish=false): returns the run status and one page of its result rows. It scans nothing itself. WHEN TO USE: only after guard_text returned run.run_id (large batches, many documentUrls or PDFs); poll until run.finished is true and page with nextOffset. Rows already written are returned while the run is RUNNING. WHEN NOT TO USE: to scan or redact (guard_text; a synchronous guard_text call already returns its rows, so there is nothing to poll). To restore placeholders in the rows' output_text after the LLM step, restore_placeholders (free). Cannot read runs of other Actors. BEHAVIOR: GET requests to your Apify account only (actor, run, dataset); never starts, aborts or re-bills a run. Typically <1 s; poll every 10-30 s. To stop a run early, abort it in the Apify console (run.console_url); this server has no abort tool. COST: free; needs the APIFY_TOKEN that started the run. The run was billed once, per document and finding, when it ran. RETURNS: run.status is READY | RUNNING | SUCCEEDED | FAILED | ABORTED | TIMED-OUT; finished=true for the last four, after which total no longer changes (FAILED/TIMED-OUT runs still return the rows they wrote). Rows are exactly the ones guard_text returns, including placeholder_map with ORIGINAL values if requested. Errors: APIFY_TOKEN_MISSING, RUN_NOT_FOUND, RUN_NOT_FOR_THIS_ACTOR, APIFY_TOKEN_INVALID, APIFY_RUN_LOOKUP_FAILED / APIFY_DATASET_READ_FAILED (retryable). PARAMETERS: runId comes from guard_text run.run_id. Pages are in write order; total counts rows written so far, so keep calling with nextOffset until it is null and finished is true. EXAMPLE: {"runId":"a1B2c3D4e5F6g7H8i"} -> {"run":{"status":"SUCCEEDED","finished":true},"count":1,"total":1,"items":[{"type":"document","risk_level":"high",...}]}

NameRequiredDescriptionDefault
limitNoOptional page size, integer 1-1000, default 100. Example: 250
runIdYesRequired. Apify run ID (letters and digits) from guard_text run.run_id. Example: "a1B2c3D4e5F6g7H8i"
offsetNoOptional integer >= 0, default 0; use nextOffset from the previous page. Example: 100

Output Schema

NameRequiredDescription
runYesrun_id, status, finished, dataset_id, started_at, finished_at, console_url
countYesRows in this page
itemsYesDataset rows: type=document or type=error
totalYesRows written by the run so far
nextOffsetYesOffset of the next page, or null

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnly/idempotent/openWorld annotations: discloses that it only issues GETs and 'never starts, aborts or re-bills a run', latency (<1 s) and poll cadence (10-30 s), that there is no abort tool and aborts happen in the Apify console, cost/auth requirements, terminal status semantics, and named error codes including retryability. This is exactly the extra context annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with labeled sections (WHAT/WHEN TO USE/WHEN NOT TO USE/BEHAVIOR/COST/RETURNS/PARAMETERS/EXAMPLE) that make it scannable, and every block earns its place. It is nonetheless long and verbose relative to a 3-parameter read tool, with some redundancy between WHAT, RETURNS and PARAMETERS.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite an output schema existing, the description still supplies the status enumeration, the finished-semantics for terminal states, partial-results-while-RUNNING behavior, the full error set, and a worked example. Nothing an agent needs to poll correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real semantics: runId's provenance ('comes from guard_text run.run_id'), page ordering ('in write order'), and the pagination contract ('keep calling with nextOffset until it is null and finished is true'). It doesn't restate limit/offset types, which the schema already covers.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Read-only follow-up to guard_text... returns the run status and one page of its result rows') and immediately scopes what it does not do ('It scans nothing itself'). An agent can distinguish it from guard_text, restore_placeholders and list_detectors without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit WHEN TO USE ('only after guard_text returned run.run_id... poll until run.finished is true') and WHEN NOT TO USE with named alternatives ('to scan or redact (guard_text...)... To restore placeholders... restore_placeholders'). It also states a hard boundary ('Cannot read runs of other Actors'), leaving nothing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

guard_textDetect / redact PII and secrets before LLM use (paid run)A
Read-onlyIdempotent
Inspect

WHAT: The only tool in this server that inspects content. Scans text documents (or public document URLs: txt/md/json/csv/HTML/PDF) for PII (emails, phones, US SSNs, Luhn-valid cards, IBANs, UK NINOs, IPs, optional person names/addresses) and secrets (AWS/GitHub/Slack/Stripe/OpenAI/... keys, JWTs, private keys, URL credentials, high-entropy key=value). mode="detect" reports findings; mode="redact" (default) also returns sanitized text and, on request, the placeholder map that restore_placeholders needs later. WHEN TO USE: before sending prompts, tickets, logs or files to an LLM or third party; to audit text for leaked credentials. Step 2 of the round trip (list_detectors -> guard_text -> [get_run_results] -> LLM -> restore_placeholders). WHEN NOT TO USE: to put originals back into an LLM answer, use restore_placeholders (free, local; mode="restore" here gives the same output but starts a paid run and is only kept for Actor pipelines outside MCP). To see entity types, list_detectors (free). To fetch rows of a run you already started, get_run_results (free; calling guard_text again starts and bills a new run). Not for images/scanned PDFs (text layer only, no OCR) or email deliverability. COST: paid. Runs Apify Actor fairquery/llm-data-guard-pii-secrets with your APIFY_TOKEN; pay-per-event: $0.0015 per document processed + $0.0002 per finding (error rows free). Example: 10 documents with 20 findings ~ $0.02. RETURNS: findings[] hold masked previews only, never raw values; placeholder_map (ORIGINAL values) only with returnPlaceholderMap=true. With waitForFinish=false: items is empty and run.run_id is the input for get_run_results. EXAMPLE: {"texts":["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"],"mode":"redact","redactionStyle":"placeholder"} -> output_text with the email replaced by [EMAIL_1] and the key by a placeholder; findings lists EMAIL and AWS_ACCESS_KEY. PARAMETERS: texts and/or documentUrls: at least one is required although the schema marks both optional (neither -> a free INPUT_EMPTY error row); both may be combined, and every text or URL is one billed document (max 1000 per run; extras get a free MAX_DOCUMENTS_EXCEEDED row). mode decides which other fields apply: "detect" ignores redactionStyle / returnPlaceholderMap; "redact" uses them; "restore" ignores all detector options and only replaces placeholders found in texts with values from placeholderMap (a placeholder missing from the map stays unchanged; restore rows have 0 findings, so they cost the per-document fee only). returnPlaceholderMap only works with mode="redact" + redactionStyle="placeholder" and is what restore_placeholders consumes; placeholders are consistent across all documents of one call ([EMAIL_1] is the same address everywhere) but numbering restarts on every call, so keep each call's map. detectNames / detectAddresses must be true before PERSON / LOCATION / STREET_ADDRESS can be found or used in entityTypes. entityTypes filters findings after detection (unknown names match nothing, no error) and is the main lever to cut per-finding cost. waitForFinish=false returns at once with run.run_id while the run continues in your Apify account (same price, billed once); use it for big batches or many URLs/PDFs that may not finish within this call's 280 s wait, then poll get_run_results with that run_id.

NameRequiredDescriptionDefault
modeNoOptional, one of "detect" | "redact" | "restore", default "redact". "detect" = report findings only; "redact" = also return output_text with values replaced; "restore" = replace placeholders in texts with originals from placeholderMap (paid run; restore_placeholders does the same for free). Example: "detect"
textsNoOptional if documentUrls is given (at least one of the two is required). One string per document (prompt, chat message, ticket, log, file content), <= 1,000,000 chars each (longer is truncated); one billed document each. Example: ["Hi, I am Jane (jane.doe@acme.com)"]
allowListNoOptional, default []. Exact values that are never flagged (whole value, case-insensitive; not substrings). Example: ["support@yourcompany.com"]
detectNamesNoOptional boolean, default false. true detects person names (type PERSON) with English NER (lower precision, slightly slower; each finding billed).
entityTypesNoOptional, default omitted/[] = all enabled detectors. Entity type names (case-insensitive) from list_detectors, without the "(opt-in)" notes. Example: ["EMAIL","CREDIT_CARD","AWS_ACCESS_KEY"]
entropyScanNoOptional boolean, default false. true also flags unlabeled random-looking tokens of 20+ chars as HIGH_ENTROPY_STRING (catches unknown key formats; may flag hashes/IDs).
documentUrlsNoOptional if texts is given. Absolute public http(s) URLs of txt, md, json, csv, HTML or PDF files (max 10 MB each; PDFs text layer only, no OCR), fetched by the Actor; unreachable URLs give free DOCUMENT_FETCH_FAILED rows. Example: ["https://example.com/report.pdf"]
waitForFinishNoOptional boolean, default true. true waits up to 280 s and returns the rows; false starts the run and returns immediately with run.run_id (poll get_run_results). Same price either way. Example: false
placeholderMapNoOptional, default {}; used only with mode="restore". Object mapping placeholder -> original value, as returned in placeholder_map by a previous redact call. Example: {"[EMAIL_1]":"jane@acme.com"}
redactionStyleNoOptional, one of "placeholder" | "mask" | "hash", default "placeholder"; used in mode="redact" only. "placeholder" = [EMAIL_1], reversible via placeholder map; "mask" = j***@a***.com / **** 1111; "hash" = [EMAIL:3f2a9c0d1b7e] salted sha256 prefix (random salt per run).
detectAddressesNoOptional boolean, default false. true detects street addresses (STREET_ADDRESS, English formats) and NER place names (LOCATION).
defaultPhoneRegionNoOptional, default "US". ISO 3166-1 alpha-2 country code (exactly 2 letters, case-insensitive) used to parse phone numbers written without a +country prefix. Examples: "US", "DE", "GB"
returnPlaceholderMapNoOptional boolean, default false. true (mode="redact" + redactionStyle="placeholder" only) adds placeholder_map {"[EMAIL_1]":"jane@acme.com"} to each row; pass it to restore_placeholders after the LLM step. Contains the ORIGINAL sensitive values.

Output Schema

NameRequiredDescription
runNoOnly with waitForFinish=false: run_id, status, dataset_id, console_url
countYesNumber of rows in items
itemsYesDataset rows: type=document (findings, risk_level, output_text) or type=error

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/idempotentHint/destructiveHint=false, and the description adds the operational context that matters: it is a paid Apify Actor run with per-document and per-finding pricing, error rows are free, a 1000-document cap, a 280s wait, and exactly what each call bills. It also discloses that placeholder_map carries ORIGINAL sensitive values and that numbering restarts per call, which is the kind of safety-relevant behavior structured fields cannot express. (Minor tension: 'random salt per run' in hash mode means repeated calls differ, but this concerns return values rather than state mutation, so it does not amount to a contradiction of idempotentHint.)

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Headers (WHAT/WHEN TO USE/WHEN NOT TO USE/COST/RETURNS/EXAMPLE/PARAMETERS) make a long body navigable and front-load the identity of the tool. The length is largely justified by 13 parameters and a paid pipeline, but there is some repetition (the restore 'same output for free' caveat and billing behavior are restated more than once), which keeps it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 13-parameter, nested-object, paid tool with an async path, the description covers everything an agent needs: cost model, batching limits, the waitForFinish=false polling handoff to get_run_results, error-row semantics, and the round-trip contract with restore_placeholders. An output schema exists, yet the RETURNS note still flags the critical distinction between masked findings and the original-value placeholder map.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although schema description coverage is 100%, the description adds the cross-parameter logic the schema cannot: at least one of texts/documentUrls is required despite both being optional, mode gates which other fields apply, returnPlaceholderMap only functions with redact+placeholder, detectNames/detectAddresses must be true before PERSON/LOCATION/STREET_ADDRESS can appear in entityTypes, and entityTypes filters post-detection (unknown names silently match nothing). This meaningfully exceeds the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The WHAT section names a specific verb (scans/detects/redacts) and resource (text documents and public URLs) and enumerates the entity classes it covers. It explicitly positions itself against siblings ('the only tool in this server that inspects content') and routes to list_detectors, restore_placeholders, and get_run_results by name, so an agent can distinguish it without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Dedicated WHEN TO USE and WHEN NOT TO USE blocks name the exact scenarios (before sending prompts/tickets/logs to an LLM) and the precise alternatives for each edge case: restore_placeholders for putting originals back, list_detectors for entity types, get_run_results to avoid re-billing. It even calls out non-goals (no OCR, not for email deliverability).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_detectorsList detectable types and redaction styles (free, local)A
Read-onlyIdempotent
Inspect

WHAT: Reference data, not a scan: the PII and secret entity types guard_text can detect, its redaction styles and its billable events. Inspects no text. WHEN TO USE: optional step 1, before guard_text, to choose entityTypes / redactionStyle, to estimate guard_text cost, or to explain coverage to a user. WHEN NOT TO USE: to check whether a given text contains PII or secrets (guard_text; this catalog cannot tell you that). Not needed before every guard_text call: guard_text runs all default detectors when entityTypes is omitted. To undo a redaction, restore_placeholders. BEHAVIOR: the catalog ships with this server version and is not fetched from the Actor, so a newer Actor build can support types that an older server version does not list. COST: free; no APIFY_TOKEN. RETURNS: pii / secret entries are valid guard_text entityTypes once the parenthesised note is dropped ("CREDIT_CARD (Luhn)" -> "CREDIT_CARD"); "opt-in" entries need guard_text detectNames / detectAddresses / entropyScan; redaction_styles keys are the allowed redactionStyle values (only "placeholder" can be undone with restore_placeholders). PARAMETERS: none; call with {}. EXAMPLE: {} -> {"pii":["EMAIL","PHONE",...],"secret":["AWS_ACCESS_KEY",...],...}

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
piiYesPII entity types (opt-in ones marked)
secretYesSecret/credential entity types
pricing_eventsYesBillable Apify event -> unit
redaction_stylesYesRedaction style -> example output

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), yet the description adds substantial non-obvious context: the catalog is version-pinned to the server rather than fetched from the Actor, so newer Actor builds may support unlisted types; it is free and needs no APIFY_TOKEN. This is exactly the kind of behavioral detail annotations cannot express.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Heavy but front-loaded and labelled (WHAT / WHEN / WHEN NOT / BEHAVIOR / COST / RETURNS / PARAMETERS / EXAMPLE), so an agent can scan to the needed section. The RETURNS block is somewhat long for a tool that has an output schema, though its guidance on stripping parenthesised notes and the 'opt-in' flags is genuinely actionable rather than padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only catalog with a full output schema, the description supplies everything else an agent needs: cost, token requirements, version-skew caveat, and how to translate returned entries into valid guard_text arguments. Nothing required to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4; schema coverage is 100% and the description confirms 'none; call with {}'. It adds no per-parameter meaning because none exists, but the call convention is stated unambiguously.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource — the catalog of PII/secret entity types and redaction styles — and immediately disambiguates it from scanning ('Reference data, not a scan... Inspects no text'). The distinction from guard_text is explicit, so an agent can route correctly without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Dedicated WHEN TO USE and WHEN NOT TO USE sections name the concrete triggers (choose entityTypes/redactionStyle, estimate cost, explain coverage) and the anti-triggers (checking text → guard_text; undoing redaction → restore_placeholders; not needed before every guard_text call since defaults apply). Alternatives are named, not implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

restore_placeholdersRestore placeholders with original values (free, local)A
Read-onlyIdempotent
Inspect

WHAT: The last step of the round trip and the default way to restore: puts original values back into texts (typically an LLM answer) that contain placeholders such as [EMAIL_1], using the placeholder_map from a guard_text redact call. Does no detection. WHEN TO USE: after the LLM answered a prompt you redacted with guard_text (mode="redact", redactionStyle="placeholder", returnPlaceholderMap=true), before showing the answer to the user. Prefer it over guard_text mode="restore": same output_text, but free and local. WHEN NOT TO USE: to find or redact PII/secrets (guard_text) or list entity types (list_detectors). Masked and hashed values cannot be restored; only placeholder style is reversible. BEHAVIOR: no network call; the map never leaves your machine; milliseconds. Up to 1000 texts and 5,000,000 chars in total per call. COST: free; no APIFY_TOKEN. RETURNS: replacements counts substituted placeholders; unknown_placeholders lists [TYPE_N] tokens the map does not cover (left as is, e.g. invented by the LLM). Errors: INPUT_TOO_LARGE. The output holds the ORIGINAL sensitive values: show it to the user, do not send it back to the LLM. PARAMETERS: placeholderMap must be the map of the redact call that produced the placeholders (numbering restarts on every call); only [UPPERCASE_TYPE_N] keys are used (exact, case-sensitive, one pass). A placeholder the LLM rewrote (e.g. "EMAIL_1" without brackets) is not restored. EXAMPLE: {"texts":["I will email [EMAIL_1] today."],"placeholderMap":{"[EMAIL_1]":"jane@acme.com"}} -> output_text "I will email jane@acme.com today.", replacements 1.

NameRequiredDescriptionDefault
textsYesRequired. 1-1000 texts containing placeholders, 5,000,000 chars in total. Example: ["I will email [EMAIL_1] today."]
placeholderMapYesRequired. Object placeholder -> original value, exactly as returned in placeholder_map by a guard_text redact call. Example: {"[EMAIL_1]":"jane@acme.com"}

Output Schema

NameRequiredDescription
countYesNumber of rows in items
itemsYestype=restored_document rows (doc_index, chars, replacements, unknown_placeholders, output_text)

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond annotations: discloses no network call, the map never leaves the machine, millisecond latency, size limits (1000 texts / 5,000,000 chars), cost (free, no APIFY_TOKEN), what cannot be restored (masked/hashed), and a critical safety caveat that output contains original sensitive values and must not be sent back to the LLM. Annotations already say readOnly/idempotent/non-destructive; description adds local-only execution and data-handling warnings.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded WHAT/WHEN TO USE/WHEN NOT TO USE/BEHAVIOR/COST/RETURNS/PARAMETERS/EXAMPLE headings make it scannable. Despite length, each section carries distinct, actionable content with no filler; the example is compact and illustrative.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers everything an agent needs: correct sequencing, alternatives, local-only guarantee, limits, cost, return fields (replacements, unknown_placeholders), the INPUT_TOO_LARGE error, and the sensitive-output handling rule. With an output schema present, the description still adds the 'do not send back to LLM' caveat that the schema cannot express.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the description still adds meaning: placeholderMap must come from the exact redact call that produced the placeholders, numbering restarts per call, only [UPPERCASE_TYPE_N] keys within brackets are used, matching is exact, case-sensitive, and single-pass, and a rewritten placeholder without brackets is not restored. These are behavioral semantics not captured by the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The WHAT section states a specific verb (restore/put back), resource (original values into texts with placeholders), and positions it as 'the last step of the round trip and the default way to restore'. It explicitly names the counterpart guard_text and its mode, so an agent can distinguish it from siblings without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit WHEN TO USE (after guard_text redact with placeholder style, before showing user), WHEN NOT TO USE (finding/redacting PII -> guard_text, listing types -> list_detectors), and a preference rule (prefer over guard_text mode=restore because free and local). This is textbook when/when-not/alternatives guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev1.1.1
    • Changedguard_text2 fields changed
      • changedInput schema / properties / mode / description
        Previous value: -"Optional, one of \"detect\" | \"redact\" | \"restore\", default \"redact\". \"detect\" = report findings only; \"redact\" = also return output_text with values replaced; \"restore\" = replace placeholders in texts with originals from placeholderMap. Example: \"detect\""New value: +"Optional, one of \"detect\" | \"redact\" | \"restore\", default \"redact\". \"detect\" = report findings only; \"redact\" = also return output_text with values replaced; \"restore\" = replace placeholders in texts with originals from placeholderMap (paid run; restore_placeholders does the same for free). Example: \"detect\""
      • changedInput schema / properties / returnPlaceholderMap / description
        Previous value: -"Optional boolean, default false. true (mode=\"redact\" + redactionStyle=\"placeholder\" only) adds placeholder_map {\"[EMAIL_1]\":\"jane@acme.com\"} to each row, needed for restore mode. Contains the ORIGINAL sensitive values."New value: +"Optional boolean, default false. true (mode=\"redact\" + redactionStyle=\"placeholder\" only) adds placeholder_map {\"[EMAIL_1]\":\"jane@acme.com\"} to each row; pass it to restore_placeholders after the LLM step. Contains the ORIGINAL sensitive values."
  2. 3 tool updatesv1.1.0
    • Addedget_run_results
    • Changedguard_text2 fields changed
      • addedInput schema / properties / waitForFinish
        Added value: +{
        +  "description": "Optional boolean, default true. true waits up to 280 s and returns the rows; false starts the run and returns immediately with run.run_id (poll get_run_results). Same price either way. Example: false",
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / run
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Only with waitForFinish=false: run_id, status, dataset_id, console_url",
        +  "type": "object"
        +}
    • Addedrestore_placeholders
  3. 1 tool update
    • Changedguard_text12 fields changed
      • changedInput schema / properties / allowList / description
        Previous value: -"Exact values that are never flagged, e.g. [\"support@yourcompany.com\"]. Default []."New value: +"Optional, default []. Exact values that are never flagged (whole value, case-insensitive; not substrings). Example: [\"support@yourcompany.com\"]"
      • changedInput schema / properties / defaultPhoneRegion / description
        Previous value: -"ISO 3166-1 alpha-2 country code used to parse phone numbers written without +country prefix, e.g. \"US\" (default), \"DE\", \"GB\"."New value: +"Optional, default \"US\". ISO 3166-1 alpha-2 country code (exactly 2 letters, case-insensitive) used to parse phone numbers written without a +country prefix. Examples: \"US\", \"DE\", \"GB\""
      • changedInput schema / properties / detectAddresses / description
        Previous value: -"Default false. Detect street addresses (English formats) and NER place names."New value: +"Optional boolean, default false. true detects street addresses (STREET_ADDRESS, English formats) and NER place names (LOCATION)."
      • changedInput schema / properties / detectNames / description
        Previous value: -"Default false. Detect person names with English NER (lower precision, slightly slower)."New value: +"Optional boolean, default false. true detects person names (type PERSON) with English NER (lower precision, slightly slower; each finding billed)."
      • changedInput schema / properties / documentUrls / description
        Previous value: -"Public http(s) URLs of txt, md, json, csv, HTML or PDF files (max 10 MB each; PDFs text layer only). Example: [\"https://example.com/report.pdf\"]. Optional if texts is given."New value: +"Optional if texts is given. Absolute public http(s) URLs of txt, md, json, csv, HTML or PDF files (max 10 MB each; PDFs text layer only, no OCR), fetched by the Actor; unreachable URLs give free DOCUMENT_FETCH_FAILED rows. Example: [\"https://example.com/report.pdf\"]"
      • changedInput schema / properties / entityTypes / description
        Previous value: -"Only report these entity types, e.g. [\"EMAIL\",\"CREDIT_CARD\",\"AWS_ACCESS_KEY\"]. Omit/empty = all enabled detectors. Valid names: see list_detectors."New value: +"Optional, default omitted/[] = all enabled detectors. Entity type names (case-insensitive) from list_detectors, without the \"(opt-in)\" notes. Example: [\"EMAIL\",\"CREDIT_CARD\",\"AWS_ACCESS_KEY\"]"
      • changedInput schema / properties / entropyScan / description
        Previous value: -"Default false. Also flag unlabeled random-looking tokens of 20+ chars (catches unknown key formats; may flag hashes/IDs)."New value: +"Optional boolean, default false. true also flags unlabeled random-looking tokens of 20+ chars as HIGH_ENTROPY_STRING (catches unknown key formats; may flag hashes/IDs)."
      • changedInput schema / properties / mode / description
        Previous value: -"\"detect\" = report findings only; \"redact\" (default) = also return output_text with values replaced; \"restore\" = replace placeholders in texts with originals from placeholderMap."New value: +"Optional, one of \"detect\" | \"redact\" | \"restore\", default \"redact\". \"detect\" = report findings only; \"redact\" = also return output_text with values replaced; \"restore\" = replace placeholders in texts with originals from placeholderMap. Example: \"detect\""
      • changedInput schema / properties / placeholderMap / description
        Previous value: -"Only for mode=\"restore\": map of placeholder -> original value returned by a previous redact call, e.g. {\"[EMAIL_1]\":\"jane@acme.com\"}. Default {}."New value: +"Optional, default {}; used only with mode=\"restore\". Object mapping placeholder -> original value, as returned in placeholder_map by a previous redact call. Example: {\"[EMAIL_1]\":\"jane@acme.com\"}"
      • changedInput schema / properties / redactionStyle / description
        Previous value: -"Used in redact mode. \"placeholder\" (default) = [EMAIL_1], reversible via placeholder map; \"mask\" = j***@a***.com / **** 1111; \"hash\" = [EMAIL:3f2a9c0d1b7e] salted sha256 prefix."New value: +"Optional, one of \"placeholder\" | \"mask\" | \"hash\", default \"placeholder\"; used in mode=\"redact\" only. \"placeholder\" = [EMAIL_1], reversible via placeholder map; \"mask\" = j***@a***.com / **** 1111; \"hash\" = [EMAIL:3f2a9c0d1b7e] salted sha256 prefix (random salt per run)."
      • changedInput schema / properties / returnPlaceholderMap / description
        Previous value: -"Default false. If true, each row includes placeholder_map {\"[EMAIL_1]\":\"jane@acme.com\"} needed for restore mode. Contains the original sensitive values."New value: +"Optional boolean, default false. true (mode=\"redact\" + redactionStyle=\"placeholder\" only) adds placeholder_map {\"[EMAIL_1]\":\"jane@acme.com\"} to each row, needed for restore mode. Contains the ORIGINAL sensitive values."
      • changedInput schema / properties / texts / description
        Previous value: -"Documents to scan, one string per document (prompt, chat message, ticket, log line, file content). Example: [\"Hi, I am Jane (jane.doe@acme.com)\"]. Optional if documentUrls is given."New value: +"Optional if documentUrls is given (at least one of the two is required). One string per document (prompt, chat message, ticket, log, file content), <= 1,000,000 chars each (longer is truncated); one billed document each. Example: [\"Hi, I am Jane (jane.doe@acme.com)\"]"
  4. 2 tool updatesv0.1.0
    • First observedguard_text
    • First observedlist_detectors

TDQS

A4.7/5.0

Scored across 4 tools

Disambiguation4/5

Each tool has a distinct role (scan, poll, list reference types, restore), and descriptions explicitly tell agents when to use one over another. The only real overlap is guard_text mode="restore" versus restore_placeholders, but the description flags this and steers agents to the free tool.

Naming Consistency5/5

All four names follow a clean verb_noun snake_case pattern (guard_text, get_run_results, list_detectors, restore_placeholders). No casing or verb-style deviations.

Tool Count4/5

Four tools is on the lean side but each maps to a distinct stage of the redact/restore round trip, so nothing feels redundant. It is well-scoped for the domain, though there is little slack.

Completeness4/5

The lifecycle (discover types -> detect/redact -> poll async run -> restore) is fully covered, including an async path and a free local restore. Minor gaps exist: no abort/run-management tool (explicitly deferred to the Apify console) and no OCR/image handling.

Maintenance

ActivityNo data
ResponsivenessSyncing

Related MCP Connectors

  • Detect and redact PII and secrets before text reaches an LLM, with reversible placeholders.

  • Find and redact leaked secrets, wallet keys and PII before an AI agent sends text.

  • Detects and redacts PII (emails, phones, SSNs, names, addresses) from text. $0.02/call via x402.

  • Redact personal data from text before your agent sends it to a model or stores it: names, contact details, ID numbers, financial and health identifiers, card numbers, plus any exact terms you add. Nothing stored, no third-party AI. Tools - redact_text: returns the redacted text and a count of what was removed, by type. Never the removed values. Options: restrict to particular data types, add exact terms to remove, choose the replacement style (a type label such as <PERSON> by default) and set a confidence threshold. - list_entity_types: lists the detectable data types. Free. Data handling - Nothing you send is stored. Text is processed in memory and discarded when the call returns. - No third-party AI. Text is never sent to OpenAI, Anthropic, Google or any other outside model. - Tool arguments are never logged. Only counts are kept: credits used, a timestamp and how many of each type were found. PDFs, including scanned pages, are handled by the REST API with the same key and credits. They aren't offered as an MCP tool because a file doesn't fit in a tool call. Pricing: 1 credit per call plus 1 per 1,000 characters. 10 free credits on sign-up, no card. Credit packs from $5; purchased credits never expire. Failed calls and repeated identical calls are free. Setup and API key: https://eleyed.com/mcp?utm_source=glama&utm_medium=directory&utm_campaign=listing

    https://eleyed.com/api/mcp

Related MCP Servers