Skip to main content
Glama
fairquery-agent-trust-mcp

fairquery-agent-trust-mcp

Official

Fairquery Agent Trust & Data Safety (MCP server)

CI

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

pii_list_detectors

Free (local)

Catalog of detectors, redaction styles, and pricing for every paid tool

pii_scan_text

Free (local)

Finds PII and secrets in text. Findings are paged and show masked values only

pii_redact_text

Free (local)

Redacts with placeholder, mask or hash style. Can return a placeholder map

pii_restore_text

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

pii_guard_documents

$0.0015/doc + $0.0002/finding

Adds NER names/addresses, URLs and PDFs, and restore mode

email_check_syntax

Free (local)

Syntax, normalization, typo suggestions, disposable/role/free-provider flags

email_validate_deliverability

$0.00079/email

DNS/MX, null MX, SPF/DMARC, mail provider, verdict

sanctions_list_sources

Free (local)

Lists covered, issuing authorities, disclaimer

sanctions_screen_names

$0.003/name + $0.007/call

Fuzzy screening against OFAC SDN/Consolidated, EU, UK and UN

osv_query_packages

Free (api.osv.dev)

Vulnerable or not, plus advisory IDs, for up to 200 exact versions

osv_get_vulnerability

Free (api.osv.dev)

Full OSV advisory records

osv_audit_dependencies

$0.0005/package + $0.0015/vuln

Manifest/lockfile audit with severity, fixes and recommended version

trust_screen_lead

$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

pii_scan_text, then pii_redact_text

Free, local

Put the original values back into the LLM's answer after pii_redact_text

pii_restore_text

Free, local

Also catch names and addresses, or scan PDFs or URLs

pii_guard_documents

Paid

Catch typos, disposable or role addresses on a signup form

email_check_syntax

Free, local

Check that an email's domain can receive mail (MX, null MX, SPF/DMARC)

email_validate_deliverability

Paid

See which sanctions lists are covered and the disclaimer

sanctions_list_sources

Free, local

Screen people or companies against OFAC, EU, UK and UN sanctions lists

sanctions_screen_names

Paid

Get a pass/review/block decision for a new lead (email and name together)

trust_screen_lead

Paid

Find out whether exact package versions have known vulnerabilities

osv_query_packages, then osv_get_vulnerability for details

Free (api.osv.dev)

Audit a whole manifest or lockfile with severity, fixed and recommended versions

osv_audit_dependencies

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-mcp

The 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-mcp

MCPB 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_MISSING

build: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 tools
email_check_syntaxCheck email syntax, typos, disposable & role (free)A
Read-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".

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-1000 (default 100). Example: 50
emailsYesRequired. 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"]
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100

Output Schema

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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".

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-1000 (default 100). Example: 50
emailsYesRequired. 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"]
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
checkSpfDmarcNoOptional 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
allowDisposableNoOptional boolean, default false = disposable/temporary-inbox domains (mailinator.com, ...) get verdict "risky"; true stops penalising them
allowRoleAccountsNoOptional boolean, default true. false downgrades role mailboxes (info@, sales@, support@, ...) to verdict "risky"
allowFreeProvidersNoOptional boolean, default true. false downgrades free webmail (gmail.com, outlook.com, yahoo.com, ...) to "risky"; recommended for B2B lead lists

Output Schema

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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"}

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-1000 (default 100). Example: 50
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
packagesNoOptional 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"}]
minSeverityNoOptional, 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
manifestTextNoOptional 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"
manifestTypeNoOptional, 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
includeDevDependenciesNoOptional boolean, default true. false skips devDependencies (package.json) and dev packages (package-lock.json); no effect on requirements.txt / go.mod

Output Schema

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.8/5.0
Behavior5/5

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

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3. 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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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.

NameRequiredDescriptionDefault
idsYesRequired. 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"]
limitNoOptional page size: items to return, integer 1-25 (default 25). Example: 25
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100

Output Schema

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds real semantics: 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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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",...].

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-200 (default 100). Example: 50
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
packagesYesRequired. 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

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.8/5.0
Behavior5/5

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

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds real 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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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}

NameRequiredDescriptionDefault
modeNoOptional, 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
limitNoOptional page size: items to return, integer 1-1000 (default 100). Example: 50
textsNoOptional 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"]
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
allowListNoOptional, default []. 0-1000 exact values never flagged (whole value, case-insensitive). Example: ["support@yourco.com"]
detectNamesNoOptional boolean, default false. true enables English person-name NER (type PERSON; lower precision, slower)
entityTypesNoOptional, 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
entropyScanNoOptional boolean, default false. true also flags unlabeled random-looking tokens (20+ chars) as HIGH_ENTROPY_STRING
documentUrlsNoOptional 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"]
placeholderMapNoOptional; used only with mode="restore". Object mapping placeholder -> original value, as returned by an earlier redact call. Example: {"[EMAIL_1]":"jane@acme.com"}
redactionStyleNoOptional, one of "placeholder" | "mask" | "hash", default "placeholder"; used in mode="redact" only. placeholder = [EMAIL_1] (reversible), mask = j***@a***.com, hash = [EMAIL:3f2a9c0d1b7e]placeholder
detectAddressesNoOptional boolean, default false. true enables street-address patterns (STREET_ADDRESS) and place-name NER (LOCATION)
returnPlaceholderMapNoOptional boolean, default false. true adds placeholder_map (ORIGINAL values) to each row; only with mode="redact" and redactionStyle="placeholder"

Output Schema

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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

For a 13-parameter, nested-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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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},...]}

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
detectorsYes
paid_pricingYes
redaction_stylesYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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]".

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-50 (default 10). Example: 10
textsYesRequired. 1-50 documents (prompt, chat message, log, file content), each <= 200,000 chars, total <= 500,000 chars. Example: ["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"]
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
allowListNoOptional, 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"]
entityTypesNoOptional, 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)
entropyScanNoOptional 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
redactionStyleNoOptional, 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
defaultPhoneRegionNoOptional, 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
returnPlaceholderMapNoOptional 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

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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.

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-50 (default 10). Example: 10
textsYesRequired. 1-50 documents (prompt, chat message, log, file content), each <= 200,000 chars, total <= 500,000 chars. Example: ["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"]
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
placeholderMapYesRequired. 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

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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".

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-1000 (default 100). Example: 50
textsYesRequired. 1-50 documents (prompt, chat message, log, file content), each <= 200,000 chars, total <= 500,000 chars. Example: ["Mail jane@acme.com, key AKIAIOSFODNN7EXAMPLE"]
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
allowListNoOptional, 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"]
entityTypesNoOptional, 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)
entropyScanNoOptional 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
defaultPhoneRegionNoOptional, 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

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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",...},...]}

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
pricingYes
sourcesYes
disclaimerYes
score_scaleYes

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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).

NameRequiredDescriptionDefault
limitNoOptional page size: items to return, integer 1-1000 (default 100). Example: 50
listsNoOptional, default all five. Any subset of "OFAC_SDN" | "OFAC_CONS" | "EU" | "UK" | "UN" (ids from sanctions_list_sources). Example: ["OFAC_SDN","EU"]
namesNoOptional 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"]
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
queriesNoOptional 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"}]
minScoreNoOptional integer 60-100, default 85. Minimum 0-100 fuzzy name-similarity score for a match; 90+ = fewer false positives, ~80 = more recall. Example: 90
maxMatchesPerNameNoOptional integer 1-100, default 10. Max entries in matches[] per name (best first); match_count still reports the total. Example: 3

Output Schema

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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)A
Read-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"}

NameRequiredDescriptionDefault
leadsYesRequired. 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"}]
limitNoOptional page size: items to return, integer 1-1000 (default 100). Example: 50
offsetNoOptional zero-based number of items to skip, integer 0-1000000 (default 0); pass nextOffset from the previous response, e.g. 100
minScoreNoOptional integer 60-100, default 88. Minimum 0-100 fuzzy name-similarity for a sanctions potential_match (which blocks the lead). Example: 92
allowFreeProvidersNoOptional boolean, default true. false makes free webmail (gmail.com, outlook.com, ...) email_verdict "risky" -> decision "review"; recommended for B2B

Output Schema

NameRequiredDescription
metaNoTool-specific context (summary counts, cost notes, ...)
countYesNumber of items in this page
itemsYesResult rows for this page
totalYesTotal items before paging
offsetYesOffset of the first item in this page
hasMoreYesTrue if more items exist after this page
nextOffsetYesOffset to pass for the next page, or null

TDQS

A4.9/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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. 1 tool updatev1.1.0
    • Addedpii_restore_text
  2. 10 tool updates
    • Changedemail_check_syntax3 fields changed
      • changedInput schema / properties / emails / description
        Previous 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\"]"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / offset / description
        Previous 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"
    • Changedemail_validate_deliverability7 fields changed
      • changedInput schema / properties / allowDisposable / description
        Previous value: -"Default false (disposable = \"risky\")"New value: +"Optional boolean, default false = disposable/temporary-inbox domains (mailinator.com, ...) get verdict \"risky\"; true stops penalising them"
      • changedInput schema / properties / allowFreeProviders / description
        Previous 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"
      • changedInput schema / properties / allowRoleAccounts / description
        Previous value: -"Default true; false makes role addresses \"risky\""New value: +"Optional boolean, default true. false downgrades role mailboxes (info@, sales@, support@, ...) to verdict \"risky\""
      • changedInput schema / properties / checkSpfDmarc / description
        Previous 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"
      • changedInput schema / properties / emails / description
        Previous 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\"]"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / offset / description
        Previous 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"
    • Changedosv_audit_dependencies10 fields changed
      • changedInput schema / properties / includeDevDependencies / description
        Previous 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"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / manifestText / description
        Previous 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\""
      • changedInput schema / properties / manifestType / description
        Previous 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)"
      • changedInput schema / properties / minSeverity / description
        Previous 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\""
      • changedInput schema / properties / offset / description
        Previous 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"
      • changedInput schema / properties / packages / description
        Previous 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\"}]"
      • changedInput schema / properties / packages / items / properties / ecosystem / description
        Previous 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\""
      • changedInput schema / properties / packages / items / properties / name / description
        Previous 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\""
      • changedInput schema / properties / packages / items / properties / version / description
        Previous 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\""
    • Changedosv_get_vulnerability3 fields changed
      • changedInput schema / properties / ids / description
        Previous 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\"]"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / offset / description
        Previous 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"
    • Changedosv_query_packages6 fields changed
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / offset / description
        Previous 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"
      • changedInput schema / properties / packages / description
        Previous 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\"}]"
      • changedInput schema / properties / packages / items / properties / ecosystem / description
        Previous 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\""
      • changedInput schema / properties / packages / items / properties / name / description
        Previous 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\""
      • changedInput schema / properties / packages / items / properties / version / description
        Previous 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\""
    • Changedpii_guard_documents13 fields changed
      • changedInput schema / properties / allowList / description
        Previous value: -"Values never flagged"New value: +"Optional, default []. 0-1000 exact values never flagged (whole value, case-insensitive). Example: [\"support@yourco.com\"]"
      • changedInput schema / properties / detectAddresses / description
        Previous 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)"
      • changedInput schema / properties / detectNames / description
        Previous value: -"Person-name NER (default false)"New value: +"Optional boolean, default false. true enables English person-name NER (type PERSON; lower precision, slower)"
      • changedInput schema / properties / documentUrls / description
        Previous 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\"]"
      • changedInput schema / properties / entityTypes / description
        Previous 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"
      • changedInput schema / properties / entropyScan / description
        Previous 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"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / mode / description
        Previous 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\""
      • changedInput schema / properties / offset / description
        Previous 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"
      • changedInput schema / properties / placeholderMap / description
        Previous 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\"}"
      • changedInput schema / properties / redactionStyle / description
        Previous 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]"
      • changedInput schema / properties / returnPlaceholderMap / description
        Previous 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\""
      • changedInput schema / properties / texts / description
        Previous 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\"]"
    • Changedpii_redact_text8 fields changed
      • changedInput schema / properties / allowList / description
        Previous 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\"]"
      • changedInput schema / properties / defaultPhoneRegion / description
        Previous 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\""
      • changedInput schema / properties / entityTypes / description
        Previous 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)"
      • changedInput schema / properties / entropyScan / description
        Previous 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"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / offset / description
        Previous 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"
      • changedInput schema / properties / redactionStyle / description
        Previous 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\""
      • changedInput schema / properties / returnPlaceholderMap / description
        Previous 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"
    • Changedpii_scan_text6 fields changed
      • changedInput schema / properties / allowList / description
        Previous 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\"]"
      • changedInput schema / properties / defaultPhoneRegion / description
        Previous 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\""
      • changedInput schema / properties / entityTypes / description
        Previous 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)"
      • changedInput schema / properties / entropyScan / description
        Previous 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"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / offset / description
        Previous 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"
    • Changedsanctions_screen_names12 fields changed
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / lists / description
        Previous 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\"]"
      • changedInput schema / properties / maxMatchesPerName / description
        Previous 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"
      • changedInput schema / properties / minScore / description
        Previous 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"
      • changedInput schema / properties / names / description
        Previous 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\"]"
      • changedInput schema / properties / offset / description
        Previous 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"
      • changedInput schema / properties / queries / description
        Previous 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\"}]"
      • changedInput schema / properties / queries / items / properties / country / description
        Previous 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"
      • changedInput schema / properties / queries / items / properties / dateOfBirth / description
        Previous 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"
      • changedInput schema / properties / queries / items / properties / entityType / description
        Previous value: -"Restrict to individuals or entities"New value: +"Optional, one of \"individual\" | \"entity\" | \"any\", default \"any\". \"entity\" = organisations, vessels, aircraft"
      • changedInput schema / properties / queries / items / properties / id / description
        Previous 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\""
      • changedInput schema / properties / queries / items / properties / name / description
        Previous 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\""
    • Changedtrust_screen_lead10 fields changed
      • changedInput schema / properties / allowFreeProviders / description
        Previous 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"
      • changedInput schema / properties / leads / description
        Previous 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\"}]"
      • changedInput schema / properties / leads / items / properties / country / description
        Previous 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"
      • changedInput schema / properties / leads / items / properties / email / description
        Previous 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"
      • changedInput schema / properties / leads / items / properties / entityType / description
        Previous value: -"Name type (default any)"New value: +"Optional, one of \"individual\" | \"entity\" | \"any\", default \"any\". Restricts sanctions candidates for this lead's name"
      • changedInput schema / properties / leads / items / properties / name / description
        Previous 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\""
      • changedInput schema / properties / leads / items / properties / ref / description
        Previous 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\", ...)"
      • changedInput schema / properties / limit / description
        Previous 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"
      • changedInput schema / properties / minScore / description
        Previous 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"
      • changedInput schema / properties / offset / description
        Previous 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"
  3. 12 tool updatesv0.1.0
    • First observedemail_check_syntax
    • First observedemail_validate_deliverability
    • First observedosv_audit_dependencies
    • First observedosv_get_vulnerability
    • First observedosv_query_packages
    • First observedpii_guard_documents
    • First observedpii_list_detectors
    • First observedpii_redact_text
    • First observedpii_scan_text
    • First observedsanctions_list_sources
    • First observedsanctions_screen_names
    • First observedtrust_screen_lead

TDQS

A4.8/5.0

Scored across 13 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityNo data
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Agent-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.
    1
    5 npm
    2
    MIT