fairquery-agent-trust-mcp
OfficialOne MCP server for AI agents to check untrusted data for PII/secrets, email quality, sanctions exposure, and dependency vulnerabilities, with free local tools plus optional paid Apify-backed tools.
Scan, redact, and restore PII/secrets in text locally for free, with detector catalog, paged findings, placeholder/mask/hash styles, and placeholder maps.
Run deep PII/secret guarding on documents and public URLs (paid): adds NER names/locations, street addresses, PDF text layers, and detect/redact/restore modes.
Check emails for free: syntax, normalization, typo suggestions, disposable/role/free-provider flags; paid deliverability adds MX/null-MX, SPF/DMARC, and verdicts.
Screen names against sanctions lists: free source catalog; paid fuzzy screening for OFAC SDN/Consolidated, EU, UK, and UN with optional country/DOB filters and score thresholds.
Check dependencies against OSV.dev: free exact-version vulnerability queries and advisory details; paid manifest/lockfile audits with severity, fixed versions, and recommended upgrades.
Screen leads in one paid call: parallel email deliverability and sanctions checks merged into pass/review/block decisions.
Use paged outputs and named error codes; paid tools require your own APIFY_TOKEN and bill to your Apify account.
Fairquery Agent Trust & Data Safety (MCP server)
One MCP server for AI agents that handle untrusted data. It covers four jobs:
PII and secret redaction: check text before it reaches an LLM.
Email checks: syntax, typos, disposable and role addresses, plus MX/SPF/DMARC in the paid tool.
Sanctions screening: OFAC, EU, UK and UN lists.
Dependency vulnerabilities: lookups against OSV.dev.
Free tools need no token or account and run on your machine. Paid tools are optional. They call public Fairquery Apify Actors with your own APIFY_TOKEN, and runs are billed to your Apify account.
Tools
Tool | Cost | What it does |
| Free (local) | Catalog of detectors, redaction styles, and pricing for every paid tool |
| Free (local) | Finds PII and secrets in text. Findings are paged and show masked values only |
| Free (local) | Redacts with placeholder, mask or hash style. Can return a placeholder map |
| Free (local) | Puts the original values back into placeholder text (e.g. an LLM answer) using that map; lists placeholders the map does not cover |
| $0.0015/doc + $0.0002/finding | Adds NER names/addresses, URLs and PDFs, and restore mode |
| Free (local) | Syntax, normalization, typo suggestions, disposable/role/free-provider flags |
| $0.00079/email | DNS/MX, null MX, SPF/DMARC, mail provider, verdict |
| Free (local) | Lists covered, issuing authorities, disclaimer |
| $0.003/name + $0.007/call | Fuzzy screening against OFAC SDN/Consolidated, EU, UK and UN |
| Free (api.osv.dev) | Vulnerable or not, plus advisory IDs, for up to 200 exact versions |
| Free (api.osv.dev) | Full OSV advisory records |
| $0.0005/package + $0.0015/vuln | Manifest/lockfile audit with severity, fixes and recommended version |
| $0.00079/email + $0.003/name + $0.007/call | Email check and sanctions screening in parallel, merged into a pass/review/block decision per lead |
Every tool description has WHAT / WHEN / WHEN NOT / COST / RETURNS / EXAMPLE sections. Outputs are paged (limit, offset, nextOffset, hasMore). Errors use named codes: APIFY_TOKEN_MISSING, APIFY_TOKEN_INVALID, APIFY_PAYMENT_REQUIRED, APIFY_ACTOR_RUN_FAILED, APIFY_TIMEOUT, INPUT_EMPTY, INPUT_TOO_LARGE, INVALID_ECOSYSTEM, OSV_FETCH_FAILED and VULN_NOT_FOUND.
Paid tools use these Apify Actors: fairquery/llm-data-guard-pii-secrets, fairquery/email-validator-mx-disposable, fairquery/sanctions-watchlist-screener and fairquery/osv-dependency-vulnerability-check.
Related MCP server: vibecheck
When to use which tool
You want to… | Use | Cost |
Check a prompt, ticket or log for PII and secrets before it reaches an LLM |
| Free, local |
Put the original values back into the LLM's answer after |
| Free, local |
Also catch names and addresses, or scan PDFs or URLs |
| Paid |
Catch typos, disposable or role addresses on a signup form |
| Free, local |
Check that an email's domain can receive mail (MX, null MX, SPF/DMARC) |
| Paid |
See which sanctions lists are covered and the disclaimer |
| Free, local |
Screen people or companies against OFAC, EU, UK and UN sanctions lists |
| Paid |
Get a pass/review/block decision for a new lead (email and name together) |
| Paid |
Find out whether exact package versions have known vulnerabilities |
| Free (api.osv.dev) |
Audit a whole manifest or lockfile with severity, fixed and recommended versions |
| Paid |
Start with the free tools. Use a paid tool only when you need what it adds; each paid call runs an Apify Actor billed to your own token.
Install
Requires Node.js 20 or later. APIFY_TOKEN is optional and only needed for paid tools. Get one at https://console.apify.com/settings/integrations.
Quick start (stdio):
npx -y @fairquery/agent-trust-mcpThe configs below run the npm package with npx -y: no clone or build step needed.
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"fairquery-trust": {
"command": "npx",
"args": ["-y", "@fairquery/agent-trust-mcp"],
"env": { "APIFY_TOKEN": "<optional-your-apify-token>" }
}
}
}Cursor (~/.cursor/mcp.json or .cursor/mcp.json)
{
"mcpServers": {
"fairquery-trust": {
"command": "npx",
"args": ["-y", "@fairquery/agent-trust-mcp"],
"env": { "APIFY_TOKEN": "<optional-your-apify-token>" }
}
}
}VS Code (.vscode/mcp.json)
{
"inputs": [{ "type": "promptString", "id": "apify-token", "description": "Apify token (optional, paid tools only)", "password": true }],
"servers": {
"fairquery-trust": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@fairquery/agent-trust-mcp"],
"env": { "APIFY_TOKEN": "${input:apify-token}" }
}
}
}From source / Docker
git clone https://github.com/fairquery/fairquery-agent-trust-mcp && cd fairquery-agent-trust-mcp
npm ci && npm run build && node dist/index.js # stdio
node dist/index.js --http # Streamable HTTP on http://127.0.0.1:3000/mcp (PORT, HOST env)
docker build -t fairquery-trust-mcp . && docker run -i --rm -e APIFY_TOKEN fairquery-trust-mcpMCPB bundle (Smithery local install, Claude Desktop)
manifest.json describes an MCPB bundle that runs this server locally over stdio. The bundle asks for one optional, sensitive setting: your own Apify API token. Leave it empty to use only the free tools.
npm run build:mcpb # -> build/fairquery-agent-trust-mcp-<version>.mcpb (dist + production node_modules)
npm run test:mcpb # unpack, initialize, tools/list, free call, paid call without token -> APIFY_TOKEN_MISSINGbuild:mcpb also writes <name>-smithery.mcpb, the file to upload to Smithery. It is the same bundle with only manifest.json changed: each tool also carries its inputSchema, outputSchema and annotations, taken from the server's own tools/list. Smithery's publish API needs these for its server card, but the MCPB 0.3 manifest schema allows only name/description per tool, so the canonical .mcpb keeps the spec-valid manifest. test:mcpb tests both bundles and checks that they differ only in those tool schemas.
Tests
npm run smoke # initialize + tools/list + guide/annotation checks, no token
npm run test:live # every free tool live, ReDoS probes, paid tools without token -> APIFY_TOKEN_MISSING
npm run test:paid # also ONE paid call per family using APIFY_TOKEN from env (costs < $0.02)Security
See SECURITY.md. Free tools never contact Apify or Fairquery, and the code contains no secrets.
Disclaimer
Sanctions results come from automated fuzzy matching and are not a certified screening service. Review every potential match yourself. Email checks do not probe SMTP mailboxes.
License
MIT © Fairquery
Available Tools
13 toolsemail_check_syntaxCheck email syntax, typos, disposable & role (free)ARead-onlyIdempotent
WHAT: Offline email checks: RFC-style syntax + normalization, common domain typo "did you mean" (gmial.com -> gmail.com), disposable/temporary domain (bundled CC0 list, ~thousands of domains), role account (info@, sales@), free webmail provider. WHEN: Fast pre-filtering of signups or lead lists, before paying for email_validate_deliverability. WHEN NOT: Does NOT check DNS/MX - it cannot tell whether the domain receives mail (verdict is "invalid", "risky" or "unknown", never "valid"). Use email_validate_deliverability (paid) for MX/SPF/DMARC. COST: Free; local, no token. RETURNS: Paged "email_check" rows; verdict is invalid | risky | unknown, never valid (dns_checked is always false). BEHAVIOR: No network calls (no DNS/SMTP); synchronous and CPU-bound, no rate limits: 10,000 addresses (the per-call maximum) return in well under a second. PARAMETERS: emails: each element is parsed on its own (one address per element; a comma-separated list in one element is INVALID_SYNTAX, not split). Before checking, "Name " is reduced to addr, a leading "mailto:" is dropped, the domain is lowercased (local part keeps its case), a trailing dot is removed and IDN domains are converted to punycode; the normalized value is returned as email, the original as input. Duplicates are not merged: every element yields one row, in input order. Role detection ignores +tags (sales+eu@ counts as sales@) and disposable matching also covers subdomains of listed domains. limit/offset page those rows; meta.summary always counts verdicts over ALL rows, not just the current page. EXAMPLE: {"emails":["someone@gmial.com"]} -> verdict "risky", reasons ["POSSIBLE_TYPO","DNS_NOT_CHECKED"], did_you_mean "someone@gmail.com".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-1000 (default 100). Example: 50 | |
| emails | Yes | Required. 1-10000 strings, 1-320 chars each, one address per element; bare "jane@acme.com", "Jane <jane@acme.com>" and "mailto:jane@acme.com" forms accepted. Example: ["jane@acme.com","bob@gmial.com"] | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/no-open-world, yet the description adds substantial non-structural behavior: no DNS/SMTP network calls, synchronous CPU-bound execution, no rate limits, sub-second throughput at the 10,000-address cap, and the crucial limitation that dns_checked is always false. It also flags duplicate non-merging and the fact that meta.summary counts all rows rather than the page.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The labeled WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE structure is well front-loaded and scannable, and each block carries real information. It is long, with mild restatement ('never valid' and dns_checked appear in both WHEN NOT and RETURNS), so it stops just short of maximal conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter, high-throughput batch tool with an output schema, the description supplies everything the schema and annotations cannot: verdict vocabulary, normalization semantics, paging interplay with meta.summary, and cost/latency expectations. Nothing needed to call it correctly is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description goes well beyond it with normalization rules (Name <addr> reduction, mailto: stripping, domain lowercasing with local-part case preserved, trailing-dot removal, punycode), the fact that a comma-separated list inside one element is INVALID_SYNTAX rather than split, +tag-insensitive role detection, and disposable matching on subdomains. This is exactly the kind of input-shape detail the schema cannot express.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The WHAT line names a specific verb (check) and enumerates the four concrete checks performed (RFC syntax/normalization, domain typo, disposable, role/free-webmail), and the WHEN NOT line names the sibling email_validate_deliverability as the differing case. An agent can distinguish this tool from every sibling without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN (fast pre-filtering of signups/lead lists before paying for deliverability) and WHEN NOT (cannot verify MX/DNS; verdict is never "valid"), plus a named paid alternative for that case. Routing is fully determined by the text.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
email_validate_deliverabilityValidate email deliverability: MX, SPF, DMARC (paid)ARead-onlyIdempotent
WHAT: Runs the Fairquery Apify Actor email-validator-mx-disposable: syntax, domain existence, MX/null-MX, disposable, role, free provider, typo suggestion, SPF/DMARC policy, mail provider; final verdict valid | risky | invalid | unknown. WHEN: Only when MX/DNS matters (can the domain receive mail?), e.g. cleaning lists before a send or verifying a lead's domain; for format, typo, disposable or role checks use email_check_syntax (free, local) and pre-filter with it to avoid paying for obviously bad rows. WHEN NOT: Offline format/typo checks (free email_check_syntax). Leads that also need sanctions screening: trust_screen_lead (one merged decision). It does not SMTP-probe mailboxes. COST: Paid, billed to YOUR Apify account via your APIFY_TOKEN: $0.00079 per email (+ $0.00005/run). 100 emails ~ $0.08. RETURNS: Paged "email" rows (verdict valid | risky | invalid | unknown with reasons[]) or "error" rows. Tool errors: APIFY_TOKEN_MISSING, APIFY_TOKEN_INVALID, APIFY_PAYMENT_REQUIRED, APIFY_ACTOR_RUN_FAILED, APIFY_TIMEOUT. BEHAVIOR: No email is sent; results change only with live DNS. Makes network calls: runs an Apify Actor (DNS/MX lookups) in your account; latency seconds to minutes (300 s timeout). PARAMETERS: emails: elements may also hold comma/semicolon/newline-separated lists or "Name " forms; the Actor splits them, trims, and skips case-insensitive duplicates, so you pay once per unique address. The allow* flags only decide whether a condition downgrades the verdict to "risky": the matching reason code (ROLE_ACCOUNT, DISPOSABLE_DOMAIN, FREE_PROVIDER) is reported in reasons[] either way, and they can never turn an "invalid" address (bad syntax, no domain, null MX, no mail server) into "valid". POSSIBLE_TYPO and IMPLICIT_MX_ONLY always make it "risky" regardless of flags. Typical presets: B2B lead list = allowFreeProviders false + allowRoleAccounts false; consumer signups = defaults. checkSpfDmarc only adds has_spf / has_dmarc / dmarc_policy fields; it never changes the verdict or the price. limit/offset only page the returned rows: every call validates and bills the whole list again (also when only offset changes), so set limit (max 1000) to cover all rows instead of paging. EXAMPLE: {"emails":["john@stripe.com"]} -> verdict "valid", has_mx true, mail_provider "Google Workspace / Gmail".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-1000 (default 100). Example: 50 | |
| emails | Yes | Required. 1-10000 strings, 1-320 chars each, one address per element; bare "jane@acme.com", "Jane <jane@acme.com>" and "mailto:jane@acme.com" forms accepted. Example: ["jane@acme.com","bob@gmial.com"] | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| checkSpfDmarc | No | Optional boolean, default true. true adds has_spf, has_dmarc and dmarc_policy (none|quarantine|reject) per domain; false skips those DNS lookups. Verdict and price are unaffected | |
| allowDisposable | No | Optional boolean, default false = disposable/temporary-inbox domains (mailinator.com, ...) get verdict "risky"; true stops penalising them | |
| allowRoleAccounts | No | Optional boolean, default true. false downgrades role mailboxes (info@, sales@, support@, ...) to verdict "risky" | |
| allowFreeProviders | No | Optional boolean, default true. false downgrades free webmail (gmail.com, outlook.com, yahoo.com, ...) to "risky"; recommended for B2B lead lists |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare read-only/open-world/idempotent; the description adds materially: no email is sent, live-DNS dependence, network calls via an Apify Actor in the caller's account, seconds-to-minutes latency with a 300s timeout, consolidated error codes (APIFY_TOKEN_MISSING, PAYMENT_REQUIRED, etc.), and repeated billing on every call including offset-only changes. Nothing here contradicts readOnlyHint/idempotentHint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE layout is strongly front-loaded and scannable. It is long, but the length is justified by seven parameters, paid billing, and error semantics; only minor repetition (a few restated 'free'/'paid' points) could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be spelled out, yet the RETURNS line still orients the agent on paged 'email' rows versus 'error' rows. Combined with cost, latency, timeout, and paging/billing caveats, an agent has everything needed to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3, but the description goes well beyond it: it explains that list/"Name <addr>" forms are split and deduplicated so you pay once per unique address, that allow* flags only downgrade to 'risky' and can never rescue a truly invalid address, that reason codes appear in reasons[] regardless of flags, gives typical B2B/consumer presets, and clarifies checkSpfDmarc affects fields but not verdict or price.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The WHAT line names the exact Actor (email-validator-mx-disposable), enumerates every check performed, and states the output verdict set. It is immediately distinguishable from siblings like email_check_syntax and trust_screen_lead, which it explicitly names and contrasts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
WHEN gives the selecting condition (only when MX/DNS matters, e.g. list cleaning or lead-domain verification) and WHEN NOT names the alternatives, email_check_syntax for offline format/typo work and trust_screen_lead for leads needing sanctions screening. It also prescribes pre-filtering to avoid paying for obviously bad rows.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
osv_audit_dependenciesAudit dependencies/manifests with fix recommendations (paid)ARead-onlyIdempotent
WHAT: Runs the Fairquery Apify Actor osv-dependency-vulnerability-check: parses package.json, package-lock.json (v1-v3), requirements.txt or go.mod (or a package list), checks OSV, and returns per-package max severity/CVSS, fixed versions, a recommended upgrade version and one row per vulnerability (deduped aliases). WHEN: Only for manifests, severity filtering or fix recommendations: full project audits from a pasted manifest, CI-style reports; for a few exact versions use osv_query_packages (free). WHEN NOT: Quick checks of a few exact versions (osv_query_packages, free) or one advisory (osv_get_vulnerability, free). COST: Paid, billed to YOUR Apify account via your APIFY_TOKEN: $0.0005 per package + $0.0015 per vulnerability found (+ $0.00005/run). Use minSeverity="HIGH" to cut vuln rows. lodash+django (2 pkgs, 41 vulns) ~ $0.06. RETURNS: Paged rows type="package" (vulnerable, vuln_count, max_severity, fixed_versions, recommended_version), type="vulnerability" (id, cve_ids, severity, cvss_score, fixed_versions, osv_url) or type="error". Tool errors: INPUT_EMPTY, APIFY_TOKEN_MISSING, APIFY_TOKEN_INVALID, APIFY_PAYMENT_REQUIRED, APIFY_ACTOR_RUN_FAILED, APIFY_TIMEOUT. BEHAVIOR: Makes network calls: runs an Apify Actor (querying OSV.dev) in your account; latency seconds to minutes (300 s timeout). Up to 1000 packages or a 2,000,000-char manifest. PARAMETERS: packages or manifestText = input (one required); manifestType = format, default auto-detect; minSeverity = only report at/above ALL|LOW|MEDIUM|HIGH|CRITICAL (default ALL); includeDevDependencies (default true); limit = page size (default 100, max 1000); offset = items to skip for paging (default 0). EXAMPLE: {"manifestText":"django==3.2.0\nrequests==2.19.0","minSeverity":"HIGH"}
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-1000 (default 100). Example: 50 | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| packages | No | Optional if manifestText is given (at least one is required). 0-1000 {ecosystem, name, version} objects with exact versions; billed per package. Example: [{"ecosystem":"npm","name":"lodash","version":"4.17.20"}] | |
| minSeverity | No | Optional, one of "ALL" | "LOW" | "MEDIUM" | "HIGH" | "CRITICAL", default "ALL". Only emit (and bill) vulnerability rows at or above this severity; package rows are always emitted. Example: "HIGH" | ALL |
| manifestText | No | Optional if packages is given. Raw file content (<= 2,000,000 chars) of package.json, package-lock.json (v1-v3), requirements.txt (== pins) or go.mod; parsed packages are added to packages. Example: "django==3.2.0\nrequests==2.19.0" | |
| manifestType | No | Optional, one of "auto" | "package.json" | "package-lock.json" | "requirements.txt" | "go.mod", default "auto" (detect from content). Set it when detection fails (MANIFEST_UNDETECTED) | auto |
| includeDevDependencies | No | Optional boolean, default true. false skips devDependencies (package.json) and dev packages (package-lock.json); no effect on requirements.txt / go.mod |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnly/idempotent/openWorld, and the description adds substantial context beyond them: full pricing model ($0.0005/pkg + $0.0015/vuln + $0.00005/run) billed to the caller's Apify account, network calls to an Apify Actor, seconds-to-minutes latency with a 300s timeout, input caps (1000 packages / 2M chars), and an enumerated error-code list. This is exactly the extra burden a cost-incurring tool requires.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Heavily structured with WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE labels and the key purpose front-loaded. It is long, but for a paid multi-mode tool nearly every line carries decision-relevant information; minor trimming is possible but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still summarizes the row types and error codes, covers cost, latency, and limits, and clarifies mode selection. Nothing an agent needs to invoke this correctly and safely is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description nonetheless adds billing-relevant semantics not obvious from the schema (e.g. 'Use minSeverity=HIGH to cut vuln rows' and that only matching rows are billed), plus a worked EXAMPLE and the packages-or-manifestText mutual-requirement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('parses package.json/requirements.txt/go.mod... checks OSV... returns per-package max severity/CVSS, fixed versions, recommended upgrade'), and explicitly distinguishes the manifest-based audit from the single/lookup siblings. An agent can tell exactly what this does versus osv_query_packages.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Has explicit WHEN ('full project audits from a pasted manifest, CI-style reports') and WHEN NOT ('quick checks of a few exact versions or one advisory') sections, each naming the specific free alternative (osv_query_packages, osv_get_vulnerability). Routing is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
osv_get_vulnerabilityGet vulnerability advisory details (free, OSV.dev)ARead-onlyIdempotent
WHAT: Fetch full OSV records (summary, details, aliases, severity vectors, affected ranges, fixed versions, references) for advisory IDs directly from api.osv.dev. WHEN: You have IDs (from osv_query_packages or a report) and need details or the fixed version. WHEN NOT: To find which vulns affect a package version (osv_query_packages, free) or a whole manifest (osv_audit_dependencies, paid). COST: Free; no token. RETURNS: Paged "vulnerability" rows with the raw OSV record fields, or "error" rows (VULN_NOT_FOUND | OSV_FETCH_FAILED, see PARAMETERS). BEHAVIOR: One HTTPS call per ID to api.osv.dev, in parallel; typically <2 s, 20 s timeout each. Max 25 IDs per call. PARAMETERS: ids: each ID is fetched separately (in parallel) and yields exactly one row in input order, so one bad ID never fails the others: an unknown ID returns a VULN_NOT_FOUND row (not retryable), a network/5xx problem an OSV_FETCH_FAILED row (retryable - retry just that ID). Prefer the ecosystem/GHSA IDs returned by osv_query_packages (vuln_ids); a CVE-... ID only resolves when OSV indexes that CVE as its own record, otherwise look up the GHSA alias. IDs are matched as given (no trimming of URLs or prefixes), and duplicates are fetched twice. limit/offset only page the returned rows; every call re-fetches all given IDs, so for a next page resend the same ids with offset = nextOffset. EXAMPLE: {"ids":["GHSA-35jh-r3h4-6jhm"]} -> lodash command injection, fixed in 4.17.21.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | Required. 1-25 OSV advisory IDs, 3-100 chars each, pattern ^[A-Za-z0-9][A-Za-z0-9._:-]{2,99}$ (letters, digits, . _ : -). Formats: GHSA-xxxx-xxxx-xxxx, PYSEC-YYYY-N, GO-YYYY-NNNN, RUSTSEC-YYYY-NNNN, CVE-YYYY-NNNNN. Example: ["GHSA-35jh-r3h4-6jhm","PYSEC-2021-9","GO-2022-0969"] | |
| limit | No | Optional page size: items to return, integer 1-25 (default 25). Example: 25 | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover read-only/idempotent/open-world/destructive, but the description goes well beyond them: one parallel HTTPS call per ID, <2s typical and 20s timeout, 25-ID cap, two distinct error classes with different retryability (VULN_NOT_FOUND non-retryable vs OSV_FETCH_FAILED retryable per-ID), and the fact that pagination re-fetches every ID.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Labelled sections (WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE) make a long description scannable and front-loaded. It is slightly verbose, with minor repetition (the free/no-token cost is stated in the title, COST line and WHEN NOT).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-param, single-resource fetch tool with rich annotations and an output schema, the description covers everything an agent needs: scope, cost, timing, error taxonomy, ID format guidance, and pagination mechanics. No material gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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: IDs are fetched individually and yield one row in input order, matching is literal with no URL/prefix trimming, duplicates are fetched twice, ecosystem/GHSA IDs are preferred over CVE IDs, and next-page calls must resend the same ids with offset=nextOffset.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The WHAT line names a specific verb (Fetch) and resource (full OSV records for advisory IDs from api.osv.dev), and even enumerates the fields returned. It explicitly distinguishes itself from osv_query_packages and osv_audit_dependencies, so an agent can route 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
WHEN states the precondition ('you have IDs... and need details or the fixed version') and WHEN NOT names both alternatives with their selection condition (query by package version vs whole manifest) plus their cost model. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
osv_query_packagesCheck package versions for known vulnerabilities (free, OSV.dev)ARead-onlyIdempotent
WHAT: Query the public OSV.dev database (api.osv.dev querybatch) directly from your machine for exact package versions; returns which are vulnerable and the advisory IDs. WHEN: Quick "is lodash 4.17.20 vulnerable?" checks for up to 200 packages; then osv_get_vulnerability for details of specific IDs. WHEN NOT: Parsing manifests/lockfiles, severity filtering, fixed/recommended versions in one shot -> osv_audit_dependencies (paid). COST: Free; no token, nothing sent to Fairquery or Apify. RETURNS: Paged "package" rows (vulnerable, vuln_ids) or per-entry INVALID_ECOSYSTEM "error" rows. Tool errors: OSV_FETCH_FAILED (retryable). BEHAVIOR: One HTTPS request to api.osv.dev (Google-run public API) for all packages; typically <2 s, 20 s timeout. Results change only when OSV publishes advisories. PARAMETERS: packages: each entry is checked independently and returns exactly one row, in input order. ecosystem is case-insensitive and accepts aliases (pip -> PyPI, cargo -> crates.io, gem -> RubyGems, golang -> Go); an unknown ecosystem yields a per-entry INVALID_ECOSYSTEM error row while the other entries still run. version must be one exact released version: a range such as "^4.17.0" or ">=2" is not resolved and normally returns no vulnerabilities (a false "safe"), so pin versions from your lockfile first. Duplicate entries are queried and returned twice. limit/offset page the rows (all packages are sent to OSV in a single request); meta.vulnerable_count counts all packages, not only the page. EXAMPLE: {"packages":[{"ecosystem":"npm","name":"lodash","version":"4.17.20"}]} -> vulnerable true, vuln_ids ["GHSA-35jh-r3h4-6jhm",...].
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-200 (default 100). Example: 50 | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| packages | Yes | Required. 1-200 {ecosystem, name, version} objects, one exact version each. Example: [{"ecosystem":"npm","name":"lodash","version":"4.17.20"},{"ecosystem":"PyPI","name":"django","version":"3.2.0"}] |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnly, idempotent, openWorld, non-destructive), and the description adds substantial context beyond them: one HTTPS request to api.osv.dev, free/no token, typical latency and 20s timeout, retryable OSV_FETCH_FAILED, per-entry INVALID_ECOSYSTEM rows, and that results only change when OSV publishes advisories. This is a rich disclosure of cost, network behavior, and failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Labeled sections (WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE) make it easy to scan and the most important routing information is front-loaded. It is longer than necessary, with the PARAMETERS section partly restating schema descriptions, but every section still carries useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a 3-parameter tool with 100% schema coverage, rich annotations, and an output schema, the description covers the remaining gaps an agent needs: cost, endpoint behavior, error semantics, paging quirks, and a concrete example. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 meaning: duplicate entries are queried twice, each entry produces exactly one row in input order, limit/offset page rows while all packages still go to OSV in one request, and meta.vulnerable_count counts all packages. Some content (ecosystem aliases, exact-version requirement) duplicates the schema, keeping it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The WHAT line names a specific verb (query), resource (OSV.dev package versions), and output (vulnerable flags and advisory IDs). It explicitly distinguishes itself from osv_get_vulnerability (detail lookup) and osv_audit_dependencies (manifest parsing/severity), so an agent can select 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
WHEN and WHEN NOT are both stated with named alternatives and the exact conditions that select them: quick version checks up to 200 packages here, manifest/lockfile auditing or severity filtering in osv_audit_dependencies. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pii_guard_documentsDeep PII/secret guard for documents and URLs (paid)ARead-onlyIdempotent
WHAT: Runs the Fairquery Apify Actor llm-data-guard-pii-secrets: everything pii_scan_text does PLUS person-name/location NER, street addresses, public document URLs (txt/md/json/csv/HTML/PDF text layer, <= 10 MB) and restore mode (put originals back into LLM output using a placeholder map). WHEN: Only when you need NER names/addresses, files at URLs/PDFs, or the hosted pipeline; for plain text with regex-detectable PII/secrets use pii_scan_text / pii_redact_text (free, local). WHEN NOT: Plain text scans (pii_scan_text / pii_redact_text, free), restoring placeholders in an LLM answer (pii_restore_text, free, same result as restore mode), images (no OCR), email deliverability. COST: Paid, billed to YOUR Apify account via your APIFY_TOKEN: $0.0015 per document + $0.0002 per finding. Example: 10 docs with 20 findings ~ $0.02. RETURNS: Paged "document" rows (findings masked; output_text in redact/restore modes) or "error" rows. Tool errors: APIFY_TOKEN_MISSING, APIFY_TOKEN_INVALID, APIFY_PAYMENT_REQUIRED, APIFY_ACTOR_RUN_FAILED, APIFY_TIMEOUT, INPUT_EMPTY. BEHAVIOR: Makes network calls: runs the Actor in your Apify account, which may fetch documentUrls. Latency seconds to a few minutes (300 s actor timeout). Up to 1000 texts / 100 URLs (10 MB each). PARAMETERS: texts and/or documentUrls: at least one non-empty (the schema marks both optional; neither -> INPUT_EMPTY, not billed); both may be combined and each text or URL becomes one billed document row. mode picks which other fields matter: "detect" ignores redactionStyle/returnPlaceholderMap; "redact" uses them; "restore" ignores all detector flags and only replaces placeholders found in texts using placeholderMap (pass the map from an earlier redact call; a placeholder missing from the map stays as is). returnPlaceholderMap works only with mode="redact" + redactionStyle="placeholder". detectNames / detectAddresses enable English NER (PERSON, LOCATION, STREET_ADDRESS; lower precision, slower) and are required before those names can be used in entityTypes. entityTypes / allowList: same semantics as pii_scan_text (case-insensitive, whole-value allow-list) but up to 1000 allow-list values. Cost scales with documents and findings, so narrow entityTypes to cut finding charges. limit/offset only page the returned rows: every call processes and bills the whole input again (also when only offset changes), so pick a limit (max 1000) that covers all rows instead of paging. EXAMPLE: {"documentUrls":["https://example.com/report.pdf"],"mode":"redact","detectNames":true}
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Optional, one of "detect" | "redact" | "restore", default "redact". "detect" = findings only; "redact" = findings + output_text; "restore" = replace placeholders in texts with originals from placeholderMap. Example: "detect" | redact |
| limit | No | Optional page size: items to return, integer 1-1000 (default 100). Example: 50 | |
| texts | No | Optional if documentUrls is given (at least one of the two is required). 0-1000 documents as plain strings, each <= 1,000,000 chars; one billed document row each. Example: ["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"] | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| allowList | No | Optional, default []. 0-1000 exact values never flagged (whole value, case-insensitive). Example: ["support@yourco.com"] | |
| detectNames | No | Optional boolean, default false. true enables English person-name NER (type PERSON; lower precision, slower) | |
| entityTypes | No | Optional, default omitted = all enabled detectors. 0-40 type names from pii_list_detectors (case-insensitive), e.g. ["EMAIL","PERSON"]; PERSON/LOCATION/STREET_ADDRESS also need detectNames/detectAddresses | |
| entropyScan | No | Optional boolean, default false. true also flags unlabeled random-looking tokens (20+ chars) as HIGH_ENTROPY_STRING | |
| documentUrls | No | Optional if texts is given. 0-100 public http(s) URLs (<= 2000 chars) of txt/md/json/csv/HTML/PDF files (<= 10 MB each, PDF text layer only), fetched by the Actor in Apify cloud. Example: ["https://example.com/a.pdf"] | |
| placeholderMap | No | Optional; used only with mode="restore". Object mapping placeholder -> original value, as returned by an earlier redact call. Example: {"[EMAIL_1]":"jane@acme.com"} | |
| redactionStyle | No | Optional, one of "placeholder" | "mask" | "hash", default "placeholder"; used in mode="redact" only. placeholder = [EMAIL_1] (reversible), mask = j***@a***.com, hash = [EMAIL:3f2a9c0d1b7e] | placeholder |
| detectAddresses | No | Optional boolean, default false. true enables street-address patterns (STREET_ADDRESS) and place-name NER (LOCATION) | |
| returnPlaceholderMap | No | Optional boolean, default false. true adds placeholder_map (ORIGINAL values) to each row; only with mode="redact" and redactionStyle="placeholder" |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the safety profile (readOnly/openWorld/idempotent/non-destructive) and no auth or cost information; the description adds the pricing model and billing target ($0.0015/doc + $0.0002/finding, billed to your Apify account), network/Actor-run behavior, latency and 300s timeout, input caps, and a full error-code list. It also discloses the non-obvious billing trap that limit/offset only page results while every call re-processes and re-bills the whole input.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded with WHAT/WHEN/WHEN NOT before the dense detail, and every block is relevant to a 13-parameter paid tool. It is nevertheless long and repeats some material already in the schema (e.g., mode semantics, allowList semantics), which costs it the top score.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter, nested-schema, network-calling paid tool with an output schema, the description covers cost, latency, caps, error codes, paging/billing behavior, and mode-field interactions. An agent has everything needed to call it correctly and to avoid unexpected charges.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline would be 3, but the description adds interaction semantics the schema does not: which fields each mode consumes or ignores, that detectNames/detectAddresses must be enabled before PERSON/LOCATION/STREET_ADDRESS are usable in entityTypes, that returnPlaceholderMap only applies to redact+placeholder, that a missing placeholder is left untouched in restore mode, and that narrowing entityTypes reduces finding charges.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The WHAT line names a specific verb/resource (runs the llm-data-guard-pii-secrets Actor for PII/secret scanning of text and documents/URLs) and immediately scopes it as a superset of pii_scan_text. It explicitly distinguishes the tool from named siblings (pii_scan_text, pii_redact_text, pii_restore_text), so an agent can pick 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Dedicated WHEN and WHEN NOT sections give the selecting conditions and the free alternatives for each excluded case (plain text -> pii_scan_text/pii_redact_text, placeholder restoration -> pii_restore_text, no OCR for images). This is textbook routing guidance with explicit exclusions, not just implied usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pii_list_detectorsList PII/secret detectors and pricing (free)ARead-onlyIdempotent
WHAT: Catalog of every PII and secret entity type, which ones run locally for free (pii_scan_text / pii_redact_text) vs only in the paid Actor (NER names/locations, street addresses, URL/PDF documents), redaction styles, and the price of every paid tool in this server. WHEN: Before pii_scan_text / pii_redact_text to pick entityTypes, or to explain coverage and costs to a user. WHEN NOT: To actually scan text (pii_scan_text) or redact it (pii_redact_text). COST: Free; no token. RETURNS: One catalog object; local:true marks the detectors the free tools run (see PARAMETERS). BEHAVIOR: No network calls; local static catalog, instant (<10 ms). No rate limits. PARAMETERS: None: call with an empty object {}; the catalog is always complete and cannot be filtered. Its output feeds other tools' parameters: copy detector "type" values verbatim into entityTypes of pii_scan_text / pii_redact_text (only rows with local:true can match there) or pii_guard_documents (all rows, incl. PERSON / LOCATION / STREET_ADDRESS); redaction_styles keys are the allowed redactionStyle values. EXAMPLE: {} -> {"detectors":[{"type":"AWS_ACCESS_KEY","category":"secret","local":true},...]}
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| detectors | Yes | |
| paid_pricing | Yes | |
| redaction_styles | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/openWorld, and the description adds genuinely new behavioral context: no network calls, static local catalog, instant (<10 ms), no rate limits, and free with no token. This exceeds what the annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Labeled blocks (WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE) front-load the routing decision, and each line carries distinct information. Length is justified by the cross-tool dependency it documents.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-param, free, read-only catalog with an output schema, the description covers purpose, routing, cost, latency, and how its output maps into sibling parameters. An agent has everything needed to call it and use the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the baseline is 4. The description still adds value by stating the call must use an empty object {} and, importantly, how the returned 'type' and 'redaction_styles' keys feed other tools' parameters — but that is output semantics more than input semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource: a catalog of every PII/secret entity type, plus which run locally for free vs only in the paid Actor, redaction styles, and pricing. It clearly distinguishes itself from the scan/redact siblings it names.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN ('before pii_scan_text / pii_redact_text to pick entityTypes') and WHEN NOT ('To actually scan text... or redact it'), naming the alternatives by tool name. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pii_redact_textRedact PII and secrets locally (free)ARead-onlyIdempotent
WHAT: Return sanitized copies of texts with every local PII/secret finding replaced (placeholder [EMAIL_1], mask, or salted hash). Placeholders are consistent across documents in one call. Runs 100% on your machine. WHEN: Right before sending user data to an LLM/third party. Use returnPlaceholderMap=true if you need to restore originals in the LLM answer later. WHEN NOT: For a findings report only use pii_scan_text. For names/addresses NER, URLs or PDFs use pii_guard_documents (paid). COST: Free; no token. RETURNS: One "redacted_document" row per text with output_text; meta.placeholder_map only when requested. Errors: INPUT_TOO_LARGE. BEHAVIOR: Same input gives the same output except redactionStyle "hash" (random per-call salt). No network calls; local, milliseconds. Max 50 texts, 200,000 chars each, 500,000 total. PARAMETERS: texts: one element per document, 500,000 chars in total; the same value gets the same placeholder in every document of one call ([EMAIL_1] everywhere), but numbering restarts on each call. redactionStyle: "placeholder" is the only reversible style; "mask" keeps a few characters for human review; "hash" output differs between calls (random salt), so do not compare hashes across calls. returnPlaceholderMap: only has an effect with redactionStyle="placeholder" (ignored for mask/hash); the map in meta.placeholder_map holds the ORIGINAL values, so keep it out of the LLM prompt and restore answers with pii_restore_text (free, local). entityTypes / allowList / entropyScan / defaultPhoneRegion: identical semantics to pii_scan_text (filter after detection, whole-value case-insensitive allow-list). limit/offset page whole documents (one item per text, default 10 per page), not findings. EXAMPLE: {"texts":["Call +1 415 555 0100 or mail jane@acme.com"]} -> output_text "Call [PHONE_1] or mail [EMAIL_1]".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-50 (default 10). Example: 10 | |
| texts | Yes | Required. 1-50 documents (prompt, chat message, log, file content), each <= 200,000 chars, total <= 500,000 chars. Example: ["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"] | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| allowList | No | Optional, default [] (nothing exempt). 0-500 exact values (<= 500 chars each) that are never flagged; whole-value, case-insensitive match, not substring. Example: ["support@yourco.com","192.0.2.1"] | |
| entityTypes | No | Optional, default omitted = every local detector. 0-40 detector type names (<= 40 chars, case-insensitive), e.g. ["EMAIL","CREDIT_CARD","AWS_ACCESS_KEY"]. Valid values: the "type" field of pii_list_detectors rows with local:true; unknown names match nothing (no error) | |
| entropyScan | No | Optional boolean, default false. true also flags unlabeled random-looking tokens of 20-200 chars as HIGH_ENTROPY_STRING (catches unknown key formats; may flag hashes/UUIDs). Example: true | |
| redactionStyle | No | Optional, one of "placeholder" | "mask" | "hash", default "placeholder". "placeholder" = [EMAIL_1] (reversible); "mask" = j***@a***.com, **** **** **** 1111; "hash" = [EMAIL:3f2a9c0d1b7e] (salted sha256 prefix, random per-call salt). Example: "mask" | placeholder |
| defaultPhoneRegion | No | Optional, default "US". ISO 3166-1 alpha-2 code (2 letters, pattern ^[A-Za-z]{2}$, case-insensitive) used only for phone numbers written without a +country prefix. Examples: "US", "DE", "GB" | US |
| returnPlaceholderMap | No | Optional boolean, default false. true (placeholder style only) adds meta.placeholder_map {"[EMAIL_1]":"jane@acme.com"} containing the ORIGINAL sensitive values. Example: true |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/non-destructive/local, yet the description adds real operational facts: no network calls, millisecond latency, deterministic output except the hash style whose salt is random per call, the INPUT_TOO_LARGE error, and the 50-doc / 200k-per-doc / 500k-total limits. It also warns that the placeholder map contains originals and must be kept out of the LLM prompt, which is exactly the kind of risk an annotation 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The labeled sections (WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE) make it skimmable and front-loaded with the decision-relevant parts. It is long and mildly repetitive — the size limits appear in both BEHAVIOR and PARAMETERS, and 'free/local' is restated from the title — but nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 9 parameters, a mutation-adjacent redaction result, and an output schema, this is the level of detail needed: limits, error code, determinism guarantees, restoration path via pii_restore_text, and a concrete input-to-output example. An agent has everything required to call it correctly and to interpret the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (baseline 3), but the PARAMETERS block adds semantics the schema does not: placeholder consistency across documents in one call with numbering restarting per call, 'hash' being non-comparable across calls, returnPlaceholderMap being silently ignored for mask/hash, and limit/offset paging whole documents rather than findings. These are non-obvious interaction rules between parameters, not restatements.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The WHAT line states a specific verb and resource ('Return sanitized copies of texts with every local PII/secret finding replaced') plus the three replacement styles, and the WHEN NOT line names the two siblings it is not (pii_scan_text for reports, pii_guard_documents for NER/URLs/PDFs). An agent can distinguish the three text-PII tools without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
WHEN gives the trigger ('right before sending user data to an LLM/third party') and adds a conditional hint for returnPlaceholderMap when restoration is needed. WHEN NOT explicitly routes findings-only work to pii_scan_text and NER/URL/PDF cases to pii_guard_documents, so both the use case and the exclusions are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pii_restore_textRestore placeholders with original values (free, local)ARead-onlyIdempotent
WHAT: Put the original values back into texts (typically an LLM answer) that contain placeholders such as [EMAIL_1], using the placeholder map from pii_redact_text or pii_guard_documents. Runs 100% on your machine. WHEN: After an LLM or third party answered a prompt you sanitized with pii_redact_text (redactionStyle "placeholder", returnPlaceholderMap=true), before showing the answer to the user. WHEN NOT: To redact (pii_redact_text) or find PII (pii_scan_text). Masked and hashed values cannot be restored; only placeholder style is reversible. COST: Free; no token. RETURNS: One "restored_document" row per text with output_text, replacements and unknown_placeholders. Errors: INPUT_TOO_LARGE. BEHAVIOR: No network calls; the map never leaves your machine; milliseconds. Max 50 texts, 200,000 chars each, 500,000 total; map up to 10,000 entries. PARAMETERS: placeholderMap: pass meta.placeholder_map unchanged; every key is replaced wherever it occurs exactly (case-sensitive, longest key first, one pass, so a restored value is never replaced again). unknown_placeholders lists [TYPE_N]-shaped tokens left in the text that the map does not contain (the LLM invented or altered them); they stay as they are. A placeholder the LLM rewrote (e.g. "EMAIL_1" without brackets) is not restored. The output contains the ORIGINAL sensitive values: show it to the user, do not send it back to the LLM. limit/offset page whole documents (default 10 per page). 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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-50 (default 10). Example: 10 | |
| texts | Yes | Required. 1-50 documents (prompt, chat message, log, file content), each <= 200,000 chars, total <= 500,000 chars. Example: ["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"] | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| placeholderMap | Yes | Required. Object placeholder -> original value, as returned in meta.placeholder_map by pii_redact_text (or placeholder_map by pii_guard_documents); keys 1-200 chars, values <= 10,000 chars, <= 10,000 entries. Example: {"[EMAIL_1]":"jane@acme.com","[PHONE_1]":"+14155550100"} |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/non-destructive/no-open-world, but the description adds substantial context beyond them: it runs 100% locally with no network and the map never leaves the machine, states hard input limits (50 texts, 200k chars each, 500k total, 10k map entries), and warns that the output contains ORIGINAL sensitive values so it must be shown to the user and not sent back to the LLM.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although long, it is densely labeled (WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE) and front-loaded, so an agent can skim to the needed section. A few limits are restated from the schema, but every sentence carries actionable information and the concrete JSON example seals the call format.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation-of-text tool with a nested object parameter, the definition covers purpose, prerequisites, cost, return shape, error code (INPUT_TOO_LARGE), safety constraints, parameter edge cases and limits. An output schema exists, yet the RETURNS line still usefully names the fields (output_text, replacements, unknown_placeholders).
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is already 100%, the description adds semantics the schema cannot convey: placeholderMap keys are replaced exactly, case-sensitively, longest key first, in one pass so a restored value is never re-replaced; and unknown_placeholders lists [TYPE_N]-shaped tokens the map lacks and that stay untouched. It also clarifies limit/offset page whole documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The WHAT line states a specific verb (restore) and resource (original values / placeholders in texts), and explicitly says it consumes the placeholder map from pii_redact_text or pii_guard_documents. It is immediately distinguishable from siblings like pii_redact_text and pii_scan_text, which are called out in the WHEN NOT line.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The WHEN line gives the exact prerequisite workflow (after a sanitized prompt was answered, before showing to the user) and WHEN NOT names the two alternative tools plus the key exclusion that masked/hashed values are not reversible (only placeholder style is). Nothing about tool selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pii_scan_textScan text for PII and secrets locally (free)ARead-onlyIdempotent
WHAT: Detect PII (emails, phones, US SSNs, Luhn-valid cards, mod-97 IBANs, UK NINOs, IPv4/IPv6) and secrets (AWS/GitHub/GitLab/Slack/Stripe/Google/OpenAI/Anthropic/HF/npm/SendGrid/Twilio/Azure keys, JWTs, private keys, URL credentials, key=value secrets) in text. Runs 100% on your machine. WHEN: Before sending prompts, tickets, logs or files to an LLM or third party; to audit text for leaked credentials. Findings are paged across all documents. WHEN NOT: To get sanitized text use pii_redact_text. For person names/addresses (NER), public URLs or PDFs use pii_guard_documents (paid). Not for images (no OCR). COST: Free. Local regex/validators only; no APIFY_TOKEN. RETURNS: Paged findings across all documents, each with a masked preview; raw values are never returned. Errors: INPUT_TOO_LARGE. BEHAVIOR: No network calls; text never leaves your machine; milliseconds. Max 50 texts, 200,000 chars each, 500,000 total. PARAMETERS: texts: one array element per document; each finding carries doc_index = the element's 0-based position, and the 500,000-char cap applies to the SUM of all elements (over it -> INPUT_TOO_LARGE; split into several calls). entityTypes: filters findings after detection; names are case-insensitive, unknown names silently match nothing, and NER-only types (PERSON, LOCATION, STREET_ADDRESS) never match here. allowList: compared with the whole matched value, case-insensitively (allow "support@yourco.com", not "yourco.com"). entropyScan: only adds HIGH_ENTROPY_STRING findings; it never removes others. defaultPhoneRegion: affects only national-format numbers such as "030 1234567" (needs "DE"); "+49 ..." numbers parse with any region. limit/offset page the flat findings list across all documents, while meta.documents always summarises every document; fetch the next page with offset = nextOffset until hasMore is false. EXAMPLE: {"texts":["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"]} -> 2 findings: EMAIL (masked "j***@a***.com"), AWS_ACCESS_KEY; risk_level "critical".
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-1000 (default 100). Example: 50 | |
| texts | Yes | Required. 1-50 documents (prompt, chat message, log, file content), each <= 200,000 chars, total <= 500,000 chars. Example: ["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"] | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| allowList | No | Optional, default [] (nothing exempt). 0-500 exact values (<= 500 chars each) that are never flagged; whole-value, case-insensitive match, not substring. Example: ["support@yourco.com","192.0.2.1"] | |
| entityTypes | No | Optional, default omitted = every local detector. 0-40 detector type names (<= 40 chars, case-insensitive), e.g. ["EMAIL","CREDIT_CARD","AWS_ACCESS_KEY"]. Valid values: the "type" field of pii_list_detectors rows with local:true; unknown names match nothing (no error) | |
| entropyScan | No | Optional boolean, default false. true also flags unlabeled random-looking tokens of 20-200 chars as HIGH_ENTROPY_STRING (catches unknown key formats; may flag hashes/UUIDs). Example: true | |
| defaultPhoneRegion | No | Optional, default "US". ISO 3166-1 alpha-2 code (2 letters, pattern ^[A-Za-z]{2}$, case-insensitive) used only for phone numbers written without a +country prefix. Examples: "US", "DE", "GB" | US |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: no network calls, data never leaves the machine, millisecond latency, raw values never returned (masked previews only), and the INPUT_TOO_LARGE failure mode. Annotations already cover readOnly/idempotent/closed-world, so this extra operational context is a genuine addition; it is not quite 5 only because latency/limits are asserted without any guidance on batching strategy beyond the split hint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Uppercase section labels make it front-loaded and scannable, and most sentences carry unique information. There is some duplication — "Runs 100% on your machine" is restated as "text never leaves your machine", and the 50/200k/500k limits repeat the schema — but the paging and sum-cap nuances justify most of the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter scanner with a complex paging contract, the description covers input sizing, filtering semantics, error handling, cost/auth, and paging continuation. An output schema exists, yet the RETURNS/EXAMPLE lines still give the agent enough to interpret and iterate on results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds real semantics the schema lacks: entityTypes filters after detection and applies case-insensitively with silent no-match on unknown names, enttropyScan is purely additive, allowList matches whole values not substrings, defaultPhoneRegion only affects national-format numbers, and limit/offset page a flat cross-document findings list while meta.documents summarises all documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with an explicit verb+resource ("Detect PII ... and secrets ... in text") and enumerates the concrete detector families covered. It also names the sibling tools it is not (pii_redact_text, pii_guard_documents), so an agent can disambiguate without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
A dedicated WHEN section gives the triggering scenarios (before sending prompts/logs to an LLM, auditing for leaked credentials), and WHEN NOT routes to specific alternatives with the reason (redaction, NER names/addresses, URLs/PDFs, images). No inference is left to the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sanctions_list_sourcesList covered sanctions lists (free)ARead-onlyIdempotent
WHAT: Static list of the official sanctions lists sanctions_screen_names checks (id, name, authority, download URL), the score scale and the legal disclaimer. WHEN: To choose the lists parameter, cite sources in a report, or explain coverage before paying. WHEN NOT: To screen a name (sanctions_screen_names, paid). It does not download lists or report freshness. COST: Free; no token. RETURNS: One static object, including the disclaimer to show with screening results. BEHAVIOR: No network calls; static local catalog, instant. No rate limits. PARAMETERS: None: call with an empty object {}; the result is static and cannot be filtered. Its output feeds sanctions_screen_names: each sources[].id (OFAC_SDN, OFAC_CONS, EU, UK, UN) is a valid value for its lists parameter, and score_scale explains how its minScore threshold is applied. EXAMPLE: {} -> {"sources":[{"id":"OFAC_SDN","authority":"US Treasury OFAC",...},...]}
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| pricing | Yes | |
| sources | Yes | |
| disclaimer | Yes | |
| score_scale | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/no-open-world, and the description still adds non-obvious behavior: no network calls, static local catalog, instant, no rate limits, and explicitly what it does NOT do (no list downloads, no freshness reporting). It also flags the operational obligation to show the disclaimer alongside screening results, which no structured field conveys.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Labeled sections (WHAT/WHEN/WHEN NOT/COST/RETURNS/BEHAVIOR/PARAMETERS/EXAMPLE) front-load the routing decision and cost before details. Every line carries distinct information; there is no filler despite the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be enumerated, and the sample response plus the note that output feeds sanctions_screen_names completes the picture. Cost, offline behavior, and unsupported capabilities are all covered for this trivial zero-parameter tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the schema carries nothing to clarify and the baseline is 4. The description usefully states the call convention (empty object {}, static, unfilterable) and even ties sources[].id values to the sibling's lists parameter, which is bonus semantic linkage rather than input documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific resource (the static catalog of official sanctions lists filtered by sanctions_screen_names) and its fields (id, name, authority, download URL), plus the score scale and disclaimer. It is clearly distinguishable from the sibling sanctions_screen_names, which is the paid screener this list serves.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN (choose the lists parameter, cite sources, explain coverage before paying) and WHEN NOT (to screen a name, use sanctions_screen_names, paid), naming the alternative and the condition that selects it. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sanctions_screen_namesScreen names against OFAC/EU/UK/UN sanctions (paid)ARead-onlyIdempotent
WHAT: Runs the Fairquery Apify Actor sanctions-watchlist-screener: fuzzy-match people/organisations against OFAC SDN, OFAC Consolidated, EU, UK and UN lists from the official sources (re-downloaded when the cached copy in your Apify account is older than 6 h; every row reports each list's publication date); optional country/DOB filters. WHEN: KYC/KYB onboarding, vendor/counterparty checks, bulk re-screening. Put all names in ONE call (each call is one run with its own start fee; list loading takes ~2-5 s from the cache, or 10-40 s, up to ~60 s, when lists are re-downloaded). WHEN NOT: PEP/adverse media, certified compliance decisions. For coverage info use free sanctions_list_sources. Leads that also have an email to validate: trust_screen_lead (one merged decision). COST: Paid, billed to YOUR Apify account via your APIFY_TOKEN: $0.003 per name + $0.007 per run. 200 names in one call ~ $0.61. RETURNS: Paged "screening" rows (status potential_match | no_match, matches[] best first, lists_unavailable) or "error" rows. Tool errors: INPUT_EMPTY, APIFY_TOKEN_MISSING, APIFY_TOKEN_INVALID, APIFY_PAYMENT_REQUIRED, APIFY_ACTOR_RUN_FAILED, APIFY_TIMEOUT. Not a certified screening service. Automated fuzzy matching against public lists; false positives and misses are possible. Review every potential match manually and consult official sources/legal counsel before acting. BEHAVIOR: Results are stable for a given list snapshot. Makes network calls: runs an Apify Actor in your account; latency seconds to minutes (300 s timeout). Up to 10,000 names. PARAMETERS: names and/or queries: at least one non-empty (the schema marks both optional; neither -> INPUT_EMPTY, not billed). Both may be combined; every entry is billed once, so use names for bare strings and queries when you have country/dateOfBirth/entityType or want your own id echoed. In queries, dateOfBirth removes listed individuals born more than 1 year apart, while country never removes matches (it only sets country_match); entityType "entity" covers organisations, vessels and aircraft. lists: omit to screen all five; a list that cannot be downloaded is reported in lists_unavailable instead of failing the run. minScore: a match is returned only if its 0-100 similarity is >= minScore; 85 balances recall and noise, 90-95 for large batches, 75-80 for transliterated names; it does not change the price. maxMatchesPerName caps matches[] only; match_count still reports all matches. limit/offset only page the returned rows: every call screens and bills all names again (also when only offset changes), so set limit (max 1000) to cover all rows instead of paging. EXAMPLE: {"queries":[{"name":"Banco Nacional de Cuba","entityType":"entity"}],"minScore":90} -> status potential_match (OFAC_SDN).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional page size: items to return, integer 1-1000 (default 100). Example: 50 | |
| lists | No | Optional, default all five. Any subset of "OFAC_SDN" | "OFAC_CONS" | "EU" | "UK" | "UN" (ids from sanctions_list_sources). Example: ["OFAC_SDN","EU"] | |
| names | No | Optional if queries is given (at least one is required). 0-10000 plain person/organisation names, 1-300 chars each. Example: ["Acme Trading LLC","Banco Nacional de Cuba"] | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| queries | No | Optional if names is given. 0-10000 {name, id?, country?, dateOfBirth?, entityType?} objects. Example: [{"name":"Ali Khamenei","id":"cust-42","country":"IR","dateOfBirth":"1939","entityType":"individual"}] | |
| minScore | No | Optional integer 60-100, default 85. Minimum 0-100 fuzzy name-similarity score for a match; 90+ = fewer false positives, ~80 = more recall. Example: 90 | |
| maxMatchesPerName | No | Optional integer 1-100, default 10. Max entries in matches[] per name (best first); match_count still reports the total. Example: 3 |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/openWorld/idempotent, and the description layers on material context they cannot carry: exact cost model ($0.003/name + $0.007/run), cache vs re-download latency bands, the 300 s timeout, that offset-only paging re-screens and re-bills every name, and enumerated INPUT_EMPTY/APIFY_* error codes. It also discloses the reliability caveat (false positives/misses, not a certified service), which is critical for a screening decision.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded WHAT/WHEN/WHEN NOT and labeled sections make a long definition navigable, and most content is decision-relevant. It is verbose at the edges (exhaustive error-code list, precise dollar examples) where slightly tighter prose would preserve the meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter paid, networked, non-certified tool, every dimension an agent needs is present: cost, latency, billing pitfalls, filtering semantics, and failure modes. An output schema exists, yet the description still summarizes the returned row shapes (screening/error, status, matches[], lists_unavailable), leaving no material gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds genuine semantics beyond schema: names vs queries selection logic (bare strings vs country/DOB/entityType or id echo), that dateOfBirth drops individuals >1 year apart while country only sets country_match and never filters, entityType 'entity' covering vessels/aircraft, minScore tuning bands, and that maxMatchesPerName caps matches[] only while match_count reports all.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('fuzzy-match people/organisations against OFAC SDN, OFAC Consolidated, EU, UK and UN lists') and names the batch scope (up to 10,000 names). It explicitly distinguishes itself from siblings by routing to sanctions_list_sources for coverage and trust_screen_lead for email-bearing leads, so an agent can tell it apart 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The WHEN clause names concrete scenarios (KYC/KYB onboarding, vendor/counterparty checks, bulk re-screening) and the WHEN NOT clause names exclusions (PEP/adverse media, certified compliance decisions) plus the specific alternative tools for those cases. Nothing about tool selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
trust_screen_leadScreen leads: email deliverability + sanctions (paid)ARead-onlyIdempotent
WHAT: One call per batch of leads/counterparties: runs email_validate_deliverability on their emails and sanctions_screen_names on their person/company names IN PARALLEL, then merges results per lead with an overall decision: block (invalid email or sanctions potential_match), review (risky email), pass. WHEN: Onboarding customers/vendors, qualifying inbound leads, CRM hygiene where both email quality and sanctions exposure matter. WHEN NOT: Email only -> email_validate_deliverability; names only -> sanctions_screen_names (same per-item prices, no merge step). Free pre-filter first: email_check_syntax. Use this tool only when a lead needs both checks and one decision. COST: Paid, billed to YOUR Apify account via your APIFY_TOKEN: $0.00079 per lead with email + $0.003 per lead with name (= $0.00379 per full lead) + $0.007 per call for the sanctions run. 100 full leads ~ $0.39. RETURNS: Paged "lead" rows (decision pass | review | block with reasons[]; sanctions_matches holds the top 3); meta.summary counts by decision. Partial failure: the affected check shows "unavailable" and meta.errors lists codes. Tool errors: INPUT_EMPTY, APIFY_TOKEN_MISSING. BEHAVIOR: No email is sent. Runs the email and sanctions Apify Actors in parallel (2 runs per call) in your account; ~5-45 s (sanctions lists come from a cache at most 6 h old, or are downloaded), 300 s timeout. PARAMETERS: leads: each record needs email and/or name (if no lead has either -> INPUT_EMPTY, nothing billed); a lead with only an email skips sanctions (sanctions_status "not_checked") and one with only a name skips email (email_verdict "not_checked"), so a lead is billed for each check it gets (prices under COST), plus the per-call sanctions run fee when any lead has a name. Identical emails across leads are validated and billed once. ref joins sanctions results back to the lead, so keep refs unique (default = the lead's 0-based index as a string). country and entityType apply to that lead's sanctions check only: entityType filters candidates, while country only annotates matches (never removes them). minScore: sanctions similarity threshold; default 88 here (stricter than the 85 of sanctions_screen_names) to keep block decisions low-noise; each name returns at most its top 3 matches. allowFreeProviders=false turns gmail.com-type addresses into email_verdict "risky", i.e. decision "review" (never "block"). Decision precedence: any sanctions potential_match or invalid email -> block; else risky email or an incomplete check -> review; else pass. limit/offset page the merged lead rows, but every call re-runs and re-bills both checks for all leads (also when only offset changes), so set limit (max 1000) to cover all leads; meta.summary counts all leads. EXAMPLE: {"leads":[{"ref":"L1","email":"cfo@acme.com","name":"Acme Trading LLC","country":"US"}]} -> {decision:"pass",email_verdict:"valid",sanctions_status:"no_match"}
| Name | Required | Description | Default |
|---|---|---|---|
| leads | Yes | Required. 1-1000 lead objects {ref?, email?, name?, country?, entityType?}; each needs email and/or name. Example: [{"ref":"L1","email":"cfo@acme.com","name":"Acme Trading LLC","country":"US","entityType":"entity"}] | |
| limit | No | Optional page size: items to return, integer 1-1000 (default 100). Example: 50 | |
| offset | No | Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100 | |
| minScore | No | Optional integer 60-100, default 88. Minimum 0-100 fuzzy name-similarity for a sanctions potential_match (which blocks the lead). Example: 92 | |
| allowFreeProviders | No | Optional boolean, default true. false makes free webmail (gmail.com, outlook.com, ...) email_verdict "risky" -> decision "review"; recommended for B2B |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | Tool-specific context (summary counts, cost notes, ...) |
| count | Yes | Number of items in this page |
| items | Yes | Result rows for this page |
| total | Yes | Total items before paging |
| offset | Yes | Offset of the first item in this page |
| hasMore | Yes | True if more items exist after this page |
| nextOffset | Yes | Offset to pass for the next page, or null |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint, openWorldHint, idempotentHint, destructiveHint=false), the description discloses critical behavioral traits: cost model ($0.00079/lead email + $0.003/name + $0.007/call sanctions fee, example pricing), runtime (~5-45s, 300s timeout), parallel Actor execution (2 runs per call in user's account), partial failure semantics ('unavailable' + meta.errors), error codes (INPUT_EMPTY, APIFY_TOKEN_MISSING), billing behavior (identical emails billed once, each check billed separately), caching (sanctions lists at most 6h old), and decision precedence. It also explicitly states 'No email is sent', which is important given the 'email' parameter could suggest sending.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with labeled sections (WHAT, WHEN, WHEN NOT, COST, RETURNS, BEHAVIOR, PARAMETERS, EXAMPLE) that scan quickly. It is dense but every sentence carries actionable information; the main detractor is that the PARAMETERS and COST sections are somewhat terse and interleaved, requiring the reader to parse a dense block to extract billing edge cases. It's not bloated, but the heavy density prevents a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (paid, multi-actor, paged, partial-failure semantics) and the presence of an output schema, the description covers all critical operational details an agent needs: cost awareness, paging and re-billing warnings, partial failure semantics, error codes, decision precedence, and a concrete example. Nothing essential is missing from the call's perspective.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, but the description adds substantial value beyond it: it explains the interaction between email and name (if only email given, sanctions_status='not_checked'; if only name, email_verdict='not_checked'), the billing linkage to which checks run, ref uniqueness defaulting to the 0-based index string, country annotation-not-filtering behavior, entityType filtering vs country annotating, minScore default of 88 (stricter than the sibling's 85) with rationale, allowFreeProviders=false mapping to 'review' (never 'block'), and decision precedence rules. This goes well beyond the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb (screen) and resource (leads) and precisely states the composite operation: email_validate_deliverability + sanctions_screen_names in parallel, merged into a per-lead decision. It explicitly distinguishes itself from the two building-block siblings and clarifies it's for when both checks and one decision are needed.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit WHEN and WHEN NOT sections: 'Onboarding customers/vendors...' for use, and for non-use it routes to email_validate_deliverability for email-only, sanctions_screen_names for names-only, and email_check_syntax as a free pre-filter. It states the condition under which this tool is appropriate ('only when a lead needs both checks and one decision').
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 tool update
v1.1.0- Added
pii_restore_text
10 tool updates
- Changed
email_check_syntax3 fields changed- changed
Input schema / properties / emails / descriptionPrevious value: -"Required. 1-10000 addresses (<= 320 chars each); \"Name <addr>\" and \"mailto:\" forms accepted. Example: [\"jane@acme.com\",\"bob@gmial.com\"]"New value: +"Required. 1-10000 strings, 1-320 chars each, one address per element; bare \"jane@acme.com\", \"Jane <jane@acme.com>\" and \"mailto:jane@acme.com\" forms accepted. Example: [\"jane@acme.com\",\"bob@gmial.com\"]" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-1000 (default 100)"New value: +"Optional page size: items to return, integer 1-1000 (default 100). Example: 50" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100"
- Changed
email_validate_deliverability7 fields changed- changed
Input schema / properties / allowDisposable / descriptionPrevious value: -"Default false (disposable = \"risky\")"New value: +"Optional boolean, default false = disposable/temporary-inbox domains (mailinator.com, ...) get verdict \"risky\"; true stops penalising them" - changed
Input schema / properties / allowFreeProviders / descriptionPrevious value: -"Default true; false makes gmail.com etc. \"risky\" (B2B lists)"New value: +"Optional boolean, default true. false downgrades free webmail (gmail.com, outlook.com, yahoo.com, ...) to \"risky\"; recommended for B2B lead lists" - changed
Input schema / properties / allowRoleAccounts / descriptionPrevious value: -"Default true; false makes role addresses \"risky\""New value: +"Optional boolean, default true. false downgrades role mailboxes (info@, sales@, support@, ...) to verdict \"risky\"" - changed
Input schema / properties / checkSpfDmarc / descriptionPrevious value: -"Also report SPF/DMARC (default true)"New value: +"Optional boolean, default true. true adds has_spf, has_dmarc and dmarc_policy (none|quarantine|reject) per domain; false skips those DNS lookups. Verdict and price are unaffected" - changed
Input schema / properties / emails / descriptionPrevious value: -"Required. 1-10000 addresses (<= 320 chars each); \"Name <addr>\" and \"mailto:\" forms accepted. Example: [\"jane@acme.com\",\"bob@gmial.com\"]"New value: +"Required. 1-10000 strings, 1-320 chars each, one address per element; bare \"jane@acme.com\", \"Jane <jane@acme.com>\" and \"mailto:jane@acme.com\" forms accepted. Example: [\"jane@acme.com\",\"bob@gmial.com\"]" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-1000 (default 100)"New value: +"Optional page size: items to return, integer 1-1000 (default 100). Example: 50" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100"
- Changed
osv_audit_dependencies10 fields changed- changed
Input schema / properties / includeDevDependencies / descriptionPrevious value: -"Include dev dependencies (default true)"New value: +"Optional boolean, default true. false skips devDependencies (package.json) and dev packages (package-lock.json); no effect on requirements.txt / go.mod" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-1000 (default 100)"New value: +"Optional page size: items to return, integer 1-1000 (default 100). Example: 50" - changed
Input schema / properties / manifestText / descriptionPrevious value: -"Raw package.json / package-lock.json / requirements.txt / go.mod content (<= 2,000,000 chars)"New value: +"Optional if packages is given. Raw file content (<= 2,000,000 chars) of package.json, package-lock.json (v1-v3), requirements.txt (== pins) or go.mod; parsed packages are added to packages. Example: \"django==3.2.0\\nrequests==2.19.0\"" - changed
Input schema / properties / manifestType / descriptionPrevious value: -"Manifest format (default auto-detect)"New value: +"Optional, one of \"auto\" | \"package.json\" | \"package-lock.json\" | \"requirements.txt\" | \"go.mod\", default \"auto\" (detect from content). Set it when detection fails (MANIFEST_UNDETECTED)" - changed
Input schema / properties / minSeverity / descriptionPrevious value: -"Only emit vulns at/above this severity (default ALL)"New value: +"Optional, one of \"ALL\" | \"LOW\" | \"MEDIUM\" | \"HIGH\" | \"CRITICAL\", default \"ALL\". Only emit (and bill) vulnerability rows at or above this severity; package rows are always emitted. Example: \"HIGH\"" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100" - changed
Input schema / properties / packages / descriptionPrevious value: -"Up to 1000 exact versions. Optional if manifestText given"New value: +"Optional if manifestText is given (at least one is required). 0-1000 {ecosystem, name, version} objects with exact versions; billed per package. Example: [{\"ecosystem\":\"npm\",\"name\":\"lodash\",\"version\":\"4.17.20\"}]" - changed
Input schema / properties / packages / items / properties / ecosystem / descriptionPrevious value: -"npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist, Pub, Hex (aliases pip/cargo/gem accepted)"New value: +"Required, 1-40 chars, case-insensitive. One of npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist, Pub, Hex; aliases node, pip, python, golang, java, cargo, rust, gem, ruby, composer accepted. Example: \"npm\"" - changed
Input schema / properties / packages / items / properties / name / descriptionPrevious value: -"Package name, e.g. \"lodash\", \"django\", \"org.apache.logging.log4j:log4j-core\""New value: +"Required, 1-300 chars. Package name exactly as published: npm \"lodash\" or \"@scope/pkg\", PyPI \"django\", Go module path \"github.com/gin-gonic/gin\", Maven \"groupId:artifactId\" e.g. \"org.apache.logging.log4j:log4j-core\"" - changed
Input schema / properties / packages / items / properties / version / descriptionPrevious value: -"Exact version, e.g. \"4.17.20\" (no ranges)"New value: +"Required, 1-100 chars. One exact version without range operators or \"v\" prefix, e.g. \"4.17.20\" (not \"^4.17.0\"); Go uses \"v1.9.0\""
- Changed
osv_get_vulnerability3 fields changed- changed
Input schema / properties / ids / descriptionPrevious value: -"Required. 1-25 IDs (3-100 chars, letters/digits/.-_:), e.g. [\"GHSA-35jh-r3h4-6jhm\",\"PYSEC-2021-9\",\"GO-2022-0969\"]"New value: +"Required. 1-25 OSV advisory IDs, 3-100 chars each, pattern ^[A-Za-z0-9][A-Za-z0-9._:-]{2,99}$ (letters, digits, . _ : -). Formats: GHSA-xxxx-xxxx-xxxx, PYSEC-YYYY-N, GO-YYYY-NNNN, RUSTSEC-YYYY-NNNN, CVE-YYYY-NNNNN. Example: [\"GHSA-35jh-r3h4-6jhm\",\"PYSEC-2021-9\",\"GO-2022-0969\"]" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-25 (default 25)"New value: +"Optional page size: items to return, integer 1-25 (default 25). Example: 25" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100"
- Changed
osv_query_packages6 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-200 (default 100)"New value: +"Optional page size: items to return, integer 1-200 (default 100). Example: 50" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100" - changed
Input schema / properties / packages / descriptionPrevious value: -"Required. 1-200 exact package versions. Example: [{\"ecosystem\":\"npm\",\"name\":\"lodash\",\"version\":\"4.17.20\"}]"New value: +"Required. 1-200 {ecosystem, name, version} objects, one exact version each. Example: [{\"ecosystem\":\"npm\",\"name\":\"lodash\",\"version\":\"4.17.20\"},{\"ecosystem\":\"PyPI\",\"name\":\"django\",\"version\":\"3.2.0\"}]" - changed
Input schema / properties / packages / items / properties / ecosystem / descriptionPrevious value: -"npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist, Pub, Hex (aliases pip/cargo/gem accepted)"New value: +"Required, 1-40 chars, case-insensitive. One of npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist, Pub, Hex; aliases node, pip, python, golang, java, cargo, rust, gem, ruby, composer accepted. Example: \"npm\"" - changed
Input schema / properties / packages / items / properties / name / descriptionPrevious value: -"Package name, e.g. \"lodash\", \"django\", \"org.apache.logging.log4j:log4j-core\""New value: +"Required, 1-300 chars. Package name exactly as published: npm \"lodash\" or \"@scope/pkg\", PyPI \"django\", Go module path \"github.com/gin-gonic/gin\", Maven \"groupId:artifactId\" e.g. \"org.apache.logging.log4j:log4j-core\"" - changed
Input schema / properties / packages / items / properties / version / descriptionPrevious value: -"Exact version, e.g. \"4.17.20\" (no ranges)"New value: +"Required, 1-100 chars. One exact version without range operators or \"v\" prefix, e.g. \"4.17.20\" (not \"^4.17.0\"); Go uses \"v1.9.0\""
- Changed
pii_guard_documents13 fields changed- changed
Input schema / properties / allowList / descriptionPrevious value: -"Values never flagged"New value: +"Optional, default []. 0-1000 exact values never flagged (whole value, case-insensitive). Example: [\"support@yourco.com\"]" - changed
Input schema / properties / detectAddresses / descriptionPrevious value: -"Street addresses + place-name NER (default false)"New value: +"Optional boolean, default false. true enables street-address patterns (STREET_ADDRESS) and place-name NER (LOCATION)" - changed
Input schema / properties / detectNames / descriptionPrevious value: -"Person-name NER (default false)"New value: +"Optional boolean, default false. true enables English person-name NER (type PERSON; lower precision, slower)" - changed
Input schema / properties / documentUrls / descriptionPrevious value: -"Public http(s) URLs of txt/md/json/csv/HTML/PDF files (<= 10 MB each), fetched by the Actor in Apify cloud, max 100. Example: [\"https://example.com/a.pdf\"]"New value: +"Optional if texts is given. 0-100 public http(s) URLs (<= 2000 chars) of txt/md/json/csv/HTML/PDF files (<= 10 MB each, PDF text layer only), fetched by the Actor in Apify cloud. Example: [\"https://example.com/a.pdf\"]" - changed
Input schema / properties / entityTypes / descriptionPrevious value: -"Restrict types, e.g. [\"EMAIL\",\"PERSON\"]"New value: +"Optional, default omitted = all enabled detectors. 0-40 type names from pii_list_detectors (case-insensitive), e.g. [\"EMAIL\",\"PERSON\"]; PERSON/LOCATION/STREET_ADDRESS also need detectNames/detectAddresses" - changed
Input schema / properties / entropyScan / descriptionPrevious value: -"Flag high-entropy tokens (default false)"New value: +"Optional boolean, default false. true also flags unlabeled random-looking tokens (20+ chars) as HIGH_ENTROPY_STRING" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-1000 (default 100)"New value: +"Optional page size: items to return, integer 1-1000 (default 100). Example: 50" - changed
Input schema / properties / mode / descriptionPrevious value: -"\"detect\" findings only; \"redact\" (default) also output_text; \"restore\" replaces placeholders in texts using placeholderMap"New value: +"Optional, one of \"detect\" | \"redact\" | \"restore\", default \"redact\". \"detect\" = findings only; \"redact\" = findings + output_text; \"restore\" = replace placeholders in texts with originals from placeholderMap. Example: \"detect\"" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100" - changed
Input schema / properties / placeholderMap / descriptionPrevious value: -"For mode=\"restore\": {\"[EMAIL_1]\":\"jane@acme.com\"}"New value: +"Optional; used only with mode=\"restore\". Object mapping placeholder -> original value, as returned by an earlier redact call. Example: {\"[EMAIL_1]\":\"jane@acme.com\"}" - changed
Input schema / properties / redactionStyle / descriptionPrevious value: -"Redact style (default placeholder)"New value: +"Optional, one of \"placeholder\" | \"mask\" | \"hash\", default \"placeholder\"; used in mode=\"redact\" only. placeholder = [EMAIL_1] (reversible), mask = j***@a***.com, hash = [EMAIL:3f2a9c0d1b7e]" - changed
Input schema / properties / returnPlaceholderMap / descriptionPrevious value: -"Include placeholder_map with original values (default false)"New value: +"Optional boolean, default false. true adds placeholder_map (ORIGINAL values) to each row; only with mode=\"redact\" and redactionStyle=\"placeholder\"" - changed
Input schema / properties / texts / descriptionPrevious value: -"Documents as strings (each <= 1,000,000 chars, max 1000). Optional if documentUrls given"New value: +"Optional if documentUrls is given (at least one of the two is required). 0-1000 documents as plain strings, each <= 1,000,000 chars; one billed document row each. Example: [\"Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE\"]"
- Changed
pii_redact_text8 fields changed- changed
Input schema / properties / allowList / descriptionPrevious value: -"Exact values never flagged (case-insensitive), e.g. [\"support@yourco.com\"]. Default []"New value: +"Optional, default [] (nothing exempt). 0-500 exact values (<= 500 chars each) that are never flagged; whole-value, case-insensitive match, not substring. Example: [\"support@yourco.com\",\"192.0.2.1\"]" - changed
Input schema / properties / defaultPhoneRegion / descriptionPrevious value: -"ISO 3166-1 alpha-2 region for phones without +prefix, e.g. \"US\" (default), \"DE\", \"GB\""New value: +"Optional, default \"US\". ISO 3166-1 alpha-2 code (2 letters, pattern ^[A-Za-z]{2}$, case-insensitive) used only for phone numbers written without a +country prefix. Examples: \"US\", \"DE\", \"GB\"" - changed
Input schema / properties / entityTypes / descriptionPrevious value: -"Only report these types, e.g. [\"EMAIL\",\"CREDIT_CARD\",\"AWS_ACCESS_KEY\"]. Omit = all local detectors. Valid names: pii_list_detectors"New value: +"Optional, default omitted = every local detector. 0-40 detector type names (<= 40 chars, case-insensitive), e.g. [\"EMAIL\",\"CREDIT_CARD\",\"AWS_ACCESS_KEY\"]. Valid values: the \"type\" field of pii_list_detectors rows with local:true; unknown names match nothing (no error)" - changed
Input schema / properties / entropyScan / descriptionPrevious value: -"Also flag unlabeled random-looking 20-200 char tokens as HIGH_ENTROPY_STRING (default false; may flag hashes/IDs)"New value: +"Optional boolean, default false. true also flags unlabeled random-looking tokens of 20-200 chars as HIGH_ENTROPY_STRING (catches unknown key formats; may flag hashes/UUIDs). Example: true" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-50 (default 10)"New value: +"Optional page size: items to return, integer 1-50 (default 10). Example: 10" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100" - changed
Input schema / properties / redactionStyle / descriptionPrevious value: -"\"placeholder\" (default) = [EMAIL_1]; \"mask\" = j***@a***.com; \"hash\" = [EMAIL:3f2a9c0d1b7e] (random per-call salt)"New value: +"Optional, one of \"placeholder\" | \"mask\" | \"hash\", default \"placeholder\". \"placeholder\" = [EMAIL_1] (reversible); \"mask\" = j***@a***.com, **** **** **** 1111; \"hash\" = [EMAIL:3f2a9c0d1b7e] (salted sha256 prefix, random per-call salt). Example: \"mask\"" - changed
Input schema / properties / returnPlaceholderMap / descriptionPrevious value: -"Default false. If true (placeholder style), meta.placeholder_map maps \"[EMAIL_1]\" -> original value"New value: +"Optional boolean, default false. true (placeholder style only) adds meta.placeholder_map {\"[EMAIL_1]\":\"jane@acme.com\"} containing the ORIGINAL sensitive values. Example: true"
- Changed
pii_scan_text6 fields changed- changed
Input schema / properties / allowList / descriptionPrevious value: -"Exact values never flagged (case-insensitive), e.g. [\"support@yourco.com\"]. Default []"New value: +"Optional, default [] (nothing exempt). 0-500 exact values (<= 500 chars each) that are never flagged; whole-value, case-insensitive match, not substring. Example: [\"support@yourco.com\",\"192.0.2.1\"]" - changed
Input schema / properties / defaultPhoneRegion / descriptionPrevious value: -"ISO 3166-1 alpha-2 region for phones without +prefix, e.g. \"US\" (default), \"DE\", \"GB\""New value: +"Optional, default \"US\". ISO 3166-1 alpha-2 code (2 letters, pattern ^[A-Za-z]{2}$, case-insensitive) used only for phone numbers written without a +country prefix. Examples: \"US\", \"DE\", \"GB\"" - changed
Input schema / properties / entityTypes / descriptionPrevious value: -"Only report these types, e.g. [\"EMAIL\",\"CREDIT_CARD\",\"AWS_ACCESS_KEY\"]. Omit = all local detectors. Valid names: pii_list_detectors"New value: +"Optional, default omitted = every local detector. 0-40 detector type names (<= 40 chars, case-insensitive), e.g. [\"EMAIL\",\"CREDIT_CARD\",\"AWS_ACCESS_KEY\"]. Valid values: the \"type\" field of pii_list_detectors rows with local:true; unknown names match nothing (no error)" - changed
Input schema / properties / entropyScan / descriptionPrevious value: -"Also flag unlabeled random-looking 20-200 char tokens as HIGH_ENTROPY_STRING (default false; may flag hashes/IDs)"New value: +"Optional boolean, default false. true also flags unlabeled random-looking tokens of 20-200 chars as HIGH_ENTROPY_STRING (catches unknown key formats; may flag hashes/UUIDs). Example: true" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-1000 (default 100)"New value: +"Optional page size: items to return, integer 1-1000 (default 100). Example: 50" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100"
- Changed
sanctions_screen_names12 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-1000 (default 100)"New value: +"Optional page size: items to return, integer 1-1000 (default 100). Example: 50" - changed
Input schema / properties / lists / descriptionPrevious value: -"Subset of OFAC_SDN, OFAC_CONS, EU, UK, UN (default all five)"New value: +"Optional, default all five. Any subset of \"OFAC_SDN\" | \"OFAC_CONS\" | \"EU\" | \"UK\" | \"UN\" (ids from sanctions_list_sources). Example: [\"OFAC_SDN\",\"EU\"]" - changed
Input schema / properties / maxMatchesPerName / descriptionPrevious value: -"Top matches per name 1-100 (default 10)"New value: +"Optional integer 1-100, default 10. Max entries in matches[] per name (best first); match_count still reports the total. Example: 3" - changed
Input schema / properties / minScore / descriptionPrevious value: -"Fuzzy threshold 60-100 (default 85; 90+ fewer false positives)"New value: +"Optional integer 60-100, default 85. Minimum 0-100 fuzzy name-similarity score for a match; 90+ = fewer false positives, ~80 = more recall. Example: 90" - changed
Input schema / properties / names / descriptionPrevious value: -"Plain names, e.g. [\"Acme Trading LLC\"]. Optional if queries given"New value: +"Optional if queries is given (at least one is required). 0-10000 plain person/organisation names, 1-300 chars each. Example: [\"Acme Trading LLC\",\"Banco Nacional de Cuba\"]" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100" - changed
Input schema / properties / queries / descriptionPrevious value: -"Names with per-name filters. Optional if names given"New value: +"Optional if names is given. 0-10000 {name, id?, country?, dateOfBirth?, entityType?} objects. Example: [{\"name\":\"Ali Khamenei\",\"id\":\"cust-42\",\"country\":\"IR\",\"dateOfBirth\":\"1939\",\"entityType\":\"individual\"}]" - changed
Input schema / properties / queries / items / properties / country / descriptionPrevious value: -"ISO-2 (\"IR\") or country name; adds country_match"New value: +"Optional, <= 60 chars. ISO 3166-1 alpha-2 code (\"IR\") or English country name (\"Iran\"); adds country_match true/false to each match, does not filter" - changed
Input schema / properties / queries / items / properties / dateOfBirth / descriptionPrevious value: -"YYYY or YYYY-MM-DD, e.g. \"1939-04-19\""New value: +"Optional. Format YYYY or YYYY-MM-DD (pattern ^\\d{4}(-\\d{2}-\\d{2})?$), e.g. \"1939\" or \"1939-04-19\"; listed individuals whose birth year differs by more than 1 year are dropped" - changed
Input schema / properties / queries / items / properties / entityType / descriptionPrevious value: -"Restrict to individuals or entities"New value: +"Optional, one of \"individual\" | \"entity\" | \"any\", default \"any\". \"entity\" = organisations, vessels, aircraft" - changed
Input schema / properties / queries / items / properties / id / descriptionPrevious value: -"Your reference echoed back, e.g. \"cust-42\""New value: +"Optional, <= 200 chars. Your own reference echoed back in the row, e.g. \"cust-42\"" - changed
Input schema / properties / queries / items / properties / name / descriptionPrevious value: -"Person or organisation name, e.g. \"Banco Nacional de Cuba\""New value: +"Required. Person or organisation name, 1-300 chars, any order/case/diacritics, e.g. \"Banco Nacional de Cuba\""
- Changed
trust_screen_lead10 fields changed- changed
Input schema / properties / allowFreeProviders / descriptionPrevious value: -"Default true; false marks gmail.com etc. as risky (B2B)"New value: +"Optional boolean, default true. false makes free webmail (gmail.com, outlook.com, ...) email_verdict \"risky\" -> decision \"review\"; recommended for B2B" - changed
Input schema / properties / leads / descriptionPrevious value: -"Required. 1-1000 leads; each needs email and/or name"New value: +"Required. 1-1000 lead objects {ref?, email?, name?, country?, entityType?}; each needs email and/or name. Example: [{\"ref\":\"L1\",\"email\":\"cfo@acme.com\",\"name\":\"Acme Trading LLC\",\"country\":\"US\",\"entityType\":\"entity\"}]" - changed
Input schema / properties / leads / items / properties / country / descriptionPrevious value: -"ISO-2 or country name to refine sanctions matches"New value: +"Optional, <= 60 chars. ISO 3166-1 alpha-2 code (\"US\") or English country name; sets country_match on sanctions matches, does not filter" - changed
Input schema / properties / leads / items / properties / email / descriptionPrevious value: -"Lead email, e.g. \"cfo@acme.com\""New value: +"Optional (email and/or name required), <= 320 chars. One email address, e.g. \"cfo@acme.com\"; \"Name <addr>\" accepted" - changed
Input schema / properties / leads / items / properties / entityType / descriptionPrevious value: -"Name type (default any)"New value: +"Optional, one of \"individual\" | \"entity\" | \"any\", default \"any\". Restricts sanctions candidates for this lead's name" - changed
Input schema / properties / leads / items / properties / name / descriptionPrevious value: -"Person or company name to sanctions-screen, e.g. \"Acme Trading LLC\""New value: +"Optional (email and/or name required), <= 300 chars. Person or company name to sanctions-screen, e.g. \"Acme Trading LLC\"" - changed
Input schema / properties / leads / items / properties / ref / descriptionPrevious value: -"Your ID echoed back, e.g. \"crm-123\" (default = index)"New value: +"Optional, <= 200 chars, must be unique per call. Your ID echoed back as ref, e.g. \"crm-123\"; default = the lead's 0-based index as a string (\"0\", \"1\", ...)" - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size: items to return, integer 1-1000 (default 100)"New value: +"Optional page size: items to return, integer 1-1000 (default 100). Example: 50" - changed
Input schema / properties / minScore / descriptionPrevious value: -"Sanctions fuzzy threshold 60-100 (default 88)"New value: +"Optional integer 60-100, default 88. Minimum 0-100 fuzzy name-similarity for a sanctions potential_match (which blocks the lead). Example: 92" - changed
Input schema / properties / offset / descriptionPrevious value: -"Items to skip for paging, integer >= 0 (default 0); pass nextOffset from the previous response"New value: +"Optional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100"
12 tool updates
v0.1.0- First observed
email_check_syntax - First observed
email_validate_deliverability - First observed
osv_audit_dependencies - First observed
osv_get_vulnerability - First observed
osv_query_packages - First observed
pii_guard_documents - First observed
pii_list_detectors - First observed
pii_redact_text - First observed
pii_scan_text - First observed
sanctions_list_sources - First observed
sanctions_screen_names - First observed
trust_screen_lead
TDQS
Scored across 13 tools
Each tool occupies a clearly distinct slot: free-local vs paid-hosted PII handling, detect vs redact vs restore, syntax-only vs MX email checks, manifest audit vs exact-version query vs advisory lookup. The few apparent overlaps (pii_guard_documents restore mode vs pii_restore_text, trust_screen_lead vs its two component tools) are explicitly called out in WHEN/WHEN NOT sections with guidance on which to pick. An agent should have no trouble selecting the right tool.
All 13 names are snake_case with a stable domain_prefix_verb_noun pattern (pii_*, email_*, sanctions_*, osv_*, trust_screen_lead). The domain prefixes make grouping obvious and the verb choice (scan/redact/restore/list/guard/check/validate/screen/query/get/audit) is used meaningfully and consistently.
13 tools is a well-scoped size for a multi-domain trust server covering PII, email, sanctions, and dependency vulnerabilities, with roughly 3-5 tools per sub-domain. Every tool has a distinct role (a free local variant, a paid hosted variant, a catalogue, or a merged convenience call) rather than being filler.
The surface covers the full lifecycle in each area: PII detect/redact/restore/guard plus a detector catalogue, email syntax + deliverability, sanctions screening + source listing, and OSV query/get/audit, with trust_screen_lead tying email and sanctions together. Minor gaps remain, such as no way to check APIFY_TOKEN credit/payment status before invoking paid tools (only error codes) and no image OCR, but these are stated limitations agents can work around.
Maintenance
Related MCP Connectors
Security firewall for AI agents — scans MCP calls for injection, secrets, and risks.
AgentGuard — 20-tool AI safety MCP: policy preflight, risk scoring, audit logging, rate limits.
Trust layer for AI agents. Privi Guard checks web pages, emails and tool results for prompt injection and outgoing text for leaked API keys and card numbers, with signed on-chain receipts (1,000 free checks a month). Privi Scan grades any MCP server for tool poisoning and rug pulls. Plus tamper-evident audit logs, agent passports, spending mandates and a live kill switch.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceRuntime safety guardrails for AI coding agents. Checks file access, validates shell commands, and scores your repo's AI safety — all via MCP.8 npm8MIT
- AlicenseAqualityDmaintenanceAgent-native "safe to ship?" security gate for AI-generated code. Uses real parsers and inter-rocedural taint analysis (JS/TS, Python, Go) to flag the classes AI coding agents get wrong — secrets, SQL injection, SS, SSRF, path traversal, command injection, weak JWT/CORS — and ranks findings by confidence. Exposes a scan tool over MCP.15 npm2MIT
- FlicenseNot gradedqualityDmaintenanceRuntime agent firewall for PII redaction, rate limits, and policy enforcement, enabling autonomous agent security via MCP integration.-
- AlicenseAqualityDmaintenanceProvides prompt injection detection, PII/secrets redaction, and an audit trail for AI agents via MCP tools.4MIT