Skip to content

Fairquery Agent Trust & Data Safety

com.getfairquery/agent-trust-mcpgetfairquery.comstreamable-httpv1.1.10Scanned

PII/secret redaction, email checks, sanctions screening, OSV lookups and an MCP server pre-check.

20 tools: 1 do something that can't be undone, 9 change things and 10 only read.

Tools: 0 delete, 1 irreversible, 9 write, 10 read.
  • Reads private data · 2
  • Reads text others write · 15
  • Can send data out · 0
  • 13 findings

Anlyon alternative

10 of these tools change something. Run those as governed actions.

Declare what an action changes, and Anlyon puts it under a shared limit, binds every approval to the exact request, and grades the outcome. It works on any HTTPS API you can describe as an action.

Anlyon does not run, proxy or govern this MCP server. A tool your own code calls directly is not routed through Anlyon and is not governed by it.

Import an OpenAPI spec →

Tools

Grouped by what each tool does, most consequential first. Open one for its reasons and full description; descriptions are the publisher's own words.

20 tools

Can't be undone 1Claude Code: ask
  • pii_restore_textWHAT: 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_doc…1 finding

    Why it is in this group

    • The description says "cannot be restored".
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "pii_restore_text" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The description says "cannot be restored".

      What this check looks for
    Description, as the server wrote it
    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. 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: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. No further network call; the placeholder map is not forwarded. 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.

    Annotations the server declares

    • title: Restore placeholders (free, on this Worker)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
Changes things 9Claude Code: ask
  • email_check_syntaxWHAT: Offline email checks: RFC-style syntax + normalization, common domain typo "did you mean" (gmial.com -> gmail.com), disposable/temporary domain (bundled CC0 list, ~thousands …No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    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, so it cannot tell whether the domain receives mail; use email_validate_deliverability (paid) for MX/SPF/DMARC. COST: Free; no token. RETURNS: Paged "email_check" rows; verdict is invalid | risky | unknown, never valid (dns_checked is always false). BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. No further network call (no DNS/SMTP); addresses are not forwarded. 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 <addr>" 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".

    Annotations the server declares

    • title: Check email syntax (free, on this Worker)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • jobs_check_openWHAT: Re-check ONE job on the employer's ATS right now and report open or closed.Reads text others write2 findings

    Why it is in this group

    • The name "jobs_check_open" says "open": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 2,117 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...ey":"greenhouse:stripe:8172503"} -> {"status":"open","method":"detail_endpoint"}."

      What this check looks for
    • Warn

      The server marks "jobs_check_open" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}; The name "jobs_check_open" says "open": a word for a change.

      What this check looks for
    Description, as the server wrote it
    WHAT: Re-check ONE job on the employer's ATS right now and report open or closed. WHEN: Before applying, alerting or contacting about a specific job you got earlier (job_key or ats + slug + jobId), when you only need open or closed. WHEN NOT: One job's full detail (jobs_get_job). Listing a board (jobs_get_board). Checking many jobs at once (jobs_search_companies with confirmOpen, paid). BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs or results. The job id and board slug are sent to the public ATS host in the requests below. Greenhouse, Lever: one GET to the documented per-job endpoint; HTTP 404 = closed. Ashby has no per-job endpoint, so the whole board is fetched once and searched for the id (method "board_listing"). Personio has no per-job endpoint either: robots.txt, then the documented /xml feed only, and the position id is looked up (method "board_listing"). No apply URL is requested. "open" means currently published by the employer; it does not prove the role is still being filled. COST: No APIFY_TOKEN, no billing. RETURNS: One "open_check" row (job = the full "job" row when includeDescription=true and the job is open, else null), or a tool error: INVALID_INPUT, BOARD_NOT_FOUND, HOST_BLOCKED or NETWORK_ERROR (the last two retryable). PARAMETERS: A malformed jobKey, or neither jobKey nor a complete ats + slug + jobId, returns INVALID_INPUT without a request. An Ashby or Personio id costs one full board download (Personio is robots.txt plus /xml); on Greenhouse and Lever a 404 triggers one extra board request so a closed job (status closed) is told apart from a missing board (BOARD_NOT_FOUND). includeDescription adds no request on Greenhouse and Lever (the per-job response already holds the text) and makes the Ashby or Personio board download include descriptions; description_text is plain text, <= 20,000 chars, e-mails/phones removed, exactly as in jobs_get_board. EXAMPLE: {"jobKey":"greenhouse:stripe:8172503"} -> {"status":"open","method":"detail_endpoint"}.

    Annotations the server declares

    • title: Re-check whether one job is still published (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • mcp_server_precheckWHAT: Pre-install check for one MCP server.Reads text others write1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 3,002 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...tions false. A poisoned description returns finding rows and a higher risk_level."

      What this check looks for
    Description, as the server wrote it
    WHAT: Pre-install check for one MCP server. Scans tool names, descriptions and parameter text you pass for injection phrasing, concealed characters, hidden markup and cross-server shadowing. WHEN: Before installing or enabling an MCP server, once you have a tools/list result to pass as tools or toolsListJson. WHEN NOT: For a dependency audit with no tool list, call osv_audit_dependencies. For a few exact versions, call osv_query_packages. This tool does not install, start or execute the package. COST: The scan is free and runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; tool names and descriptions you send leave the client. This Worker's code is designed not to store them and does not forward them to Apify. path and packageName are refused (PATH_NOT_ALLOWED) because this Worker has no disk, so auditDependencies=true cannot read a manifest and does not start the OSV Actor. To audit a manifest, call osv_audit_dependencies (paid: $0.50 per 1,000 packages + $1.50 per 1,000 vulnerabilities found). RETURNS: Paged "finding" rows (rule, severity, tool, field, evidence). If auditDependencies is true, one "dependency_audit" row with status no_manifest on this Worker (no disk). meta.risk_level is none, low, medium, high, critical or not_assessed. meta.shared_externally.tool_names_and_descriptions is always false, which means the scan does not forward that text to npm or Apify; the text is still received by this Worker. Errors: INPUT_EMPTY, INVALID_JSON, INVALID_PACKAGE_NAME, PATH_NOT_ALLOWED, INPUT_TOO_LARGE. BEHAVIOR: Default auditDependencies=false: no further network call and no process spawn. Scans only tools or toolsListJson you pass. path and packageName return PATH_NOT_ALLOWED and nothing is read. This Worker has no disk, so it does not read package.json, lockfiles, manifest.json, or server.json, and it does not follow symlinks. auditDependencies=true does not call registry.npmjs.org or the OSV Actor from this Worker, because a manifest is never loaded. PARAMETERS: Pass tools or toolsListJson (neither, and no path or packageName -> INPUT_EMPTY). tools: structured tools/list entries. toolsListJson: raw JSON of a tools/list result, an MCPB manifest or a tools array; invalid JSON -> INVALID_JSON. path and packageName always fail on this Worker (no disk): an invalid packageName is INVALID_PACKAGE_NAME, and any other path or packageName is PATH_NOT_ALLOWED. Nothing is read or downloaded. Use the npm server @fairquery/agent-trust-mcp for a path or packageName scan. Each field is scanned up to 20,000 characters and the raw total must be <= 500,000. auditDependencies defaults to false and does not start a paid run here. minSeverity and includeDevDependencies are unused on this Worker. limit/offset page the combined rows. EXAMPLE: {"tools":[{"name":"sum","description":"Add two numbers."}]} -> risk_level "none", shared_externally.tool_names_and_descriptions false. A poisoned description returns finding rows and a higher risk_level.

    Annotations the server declares

    • title: Pre-check an MCP server
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • osv_audit_dependenciesWHAT: 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,…Reads text others writeNo findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
    Description, as the server wrote it
    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.50 per 1,000 packages + $1.50 per 1,000 vulnerabilities found (+ $0.05 per 1,000 runs). 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: Called from this Fairquery-hosted Worker, which forwards your input and Apify token to Apify and waits 20 s (APIFY_TIMEOUT if the run has not finished). This Worker's code is designed not to store them. The Actor in your account queries OSV.dev. 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"}

    Annotations the server declares

    • title: Audit dependencies (paid)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • pii_guard_documentsWHAT: 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/cs…Reads text others writeNo findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
    Description, as the server wrote it
    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. 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: $1.50 per 1,000 documents + $0.20 per 1,000 findings. 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: Called from this Fairquery-hosted Worker, which forwards your input and Apify token to Apify and waits 20 s (APIFY_TIMEOUT if the run has not finished). This Worker's code is designed not to store them. The Actor runs in your Apify account and may fetch documentUrls. 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="restore" ignores all detector flags; a placeholder missing from placeholderMap stays as is. 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}

    Annotations the server declares

    • title: Deep PII guard (paid)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • pii_redact_textWHAT: Return sanitized copies of texts with every local PII/secret finding replaced (placeholder [EMAIL_1], mask, or salted hash).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    WHAT: Return sanitized copies of texts with every local PII/secret finding replaced (placeholder [EMAIL_1], mask, or salted hash). 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). Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. No further network call; texts are not forwarded. Milliseconds. 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, on this Worker). 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]".

    Annotations the server declares

    • title: Redact PII/secrets (free, on this Worker)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • pii_scan_textWHAT: 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/…2 findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Info

      The description mentions "credentials".

      Matched text

      description: "...lio/Azure keys, JWTs, private keys, URL credentials, key=value secrets) in text. WHEN: Befo..."

      What this check looks for
    • Info

      The description is 2,147 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...findings: EMAIL (masked "j***@a***.com"), AWS_ACCESS_KEY; risk_level "critical"."

      What this check looks for
    Description, as the server wrote it
    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. 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. Regex and validators on this Worker; no APIFY_TOKEN. RETURNS: Paged findings across all documents, each with a masked preview; raw values are never returned. Errors: INPUT_TOO_LARGE. BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. No further network call; the text is not forwarded to another service. Milliseconds. 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".

    Annotations the server declares

    • title: Scan text for PII/secrets (free, on this Worker)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • sanctions_screen_namesWHAT: 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 sourc…Reads text others write1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 3,041 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...uba","entityType":"entity"}],"minScore":90} -> status potential_match (OFAC_SDN)."

      What this check looks for
    Description, as the server wrote it
    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). This Worker waits 20 s, so a re-download that runs longer returns APIFY_TIMEOUT. 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: $3.00 per 1,000 names + $7.00 per 1,000 runs. 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. Called from this Fairquery-hosted Worker, which forwards your input and Apify token to Apify and waits 20 s (APIFY_TIMEOUT if the run has not finished). This Worker's code is designed not to store them. The Actor runs in your account. Up to 10,000 names. A list re-download can outlast the 20 s wait. 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).

    Annotations the server declares

    • title: Screen sanctions (paid)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • trust_screen_leadWHAT: 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 merg…Reads text others write1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 3,155 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...y":"US"}]} -> {decision:"pass",email_verdict:"valid",sanctions_status:"no_match"}"

      What this check looks for
    Description, as the server wrote it
    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.79 per 1,000 leads with email + $3.00 per 1,000 leads with name (= $3.79 per 1,000 full leads) + $7.00 per 1,000 calls 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. Called from this Fairquery-hosted Worker, which forwards your input and Apify token to Apify and waits 20 s (APIFY_TIMEOUT if the run has not finished). This Worker's code is designed not to store them. Runs the email and sanctions Apify Actors in parallel (2 runs per call) in your account. Sanctions lists come from a cache at most 6 h old, or are downloaded; a download can outlast the 20 s wait. 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"}

    Annotations the server declares

    • title: Screen leads (paid)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
Only reads 10Show 10 read-only tools
  • email_validate_deliverabilityWHAT: 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 p…Reads text others write1 finding

    Why it is in this group

    • The name "email_validate_deliverability" says "validate": a word for reading.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 2,433 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...com"]} -> verdict "valid", has_mx true, mail_provider "Google Workspace / Gmail"."

      What this check looks for
    Description, as the server wrote it
    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; pre-filter with email_check_syntax to avoid paying for obviously bad rows. WHEN NOT: Offline format, typo, disposable or role 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.79 per 1,000 emails (+ $0.05 per 1,000 runs). 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. Called from this Fairquery-hosted Worker, which forwards your input and Apify token to Apify and waits 20 s (APIFY_TIMEOUT if the run has not finished). This Worker's code is designed not to store them. The Actor in your account does the DNS/MX lookups. PARAMETERS: emails: elements may also hold comma/semicolon/newline-separated lists or "Name <addr>" 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".

    Annotations the server declares

    • title: Validate email deliverability (paid)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • jobs_find_boardWHAT: Resolve a company name, domain or careers/board URL to its public job board(s) on Greenhouse, Lever or Ashby.Reads text others write1 finding

    Why it is in this group

    • The name "jobs_find_board" says "find": a word for reading.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
    • untrusted-inputThe description's first sentence says "url": it reads web pages, which anyone can write.

    Findings

    • Info

      The description is 3,144 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...p.com"} -> [{"ats":"ashby","slug":"ramp","via":"seed_list","confidence":"high"}]."

      What this check looks for
    Description, as the server wrote it
    WHAT: Resolve a company name, domain or careers/board URL to its public job board(s) on Greenhouse, Lever or Ashby. A Personio URL or personio:account you already know is parsed on this Worker and not looked up. WHEN: Before jobs_get_board when you only know the company ("Ramp", "sentry.io", "https://stripe.com/jobs") or you have a Personio career URL. WHEN NOT: You already have ats + slug (call jobs_get_board). For many companies plus their jobs in one step use jobs_search_companies (paid). It cannot find Workday, Workable, Recruitee, Rippling or LinkedIn pages, and it does not discover Personio accounts. BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs or results. Parts of the input are sent to public ATS hosts or the company site, as the steps below describe. Order: board-URL parsing (including personio:{account} and https://{account}.jobs.personio.de/..., no network) -> verified starter list -> the company's own website (only for a domain/URL input; robots.txt respected, <= 3 pages, private/internal IPs refused; Personio hosts are not crawled) -> at most 6 GETs against the Greenhouse, Lever and Ashby APIs with slug guesses. Personio is never probed and a careers-page link to Personio is ignored. Stops at the first hit. Nothing is cached or stored. Rate limits: max 2 parallel requests per host, Lever spaced 1 s apart, 15 s timeout per GET; ATS API network errors and HTTP 5xx are retried at most twice with backoff (0.5-1.6 s). Any HTTP 403/429 (or an HTML block page from an ATS API) stops that host for the session (kill switch, never retried, also for the other free tools): the call does not fail but adds a HOST_BLOCKED note to meta.notes, stops the careers-page step when the company site is blocked, and skips later slug probes on a stopped ATS host without sending them. COST: No APIFY_TOKEN and no billing; worst case about 4 website requests (robots.txt + 3 pages) and 6 ATS API GETs per call. RETURNS: "board" rows, or one NO_BOARD_FOUND error row. via (board_url | seed_list | careers_page | slug_probe) says how the board was found; confidence is "high" except for slug_probe hits ("medium": the slug exists but may belong to a different company with the same name, so check the jobs or pass the URL from the careers page). PARAMETERS: company accepts three forms with different behavior: a board URL (including a Personio account URL or personio:account) is parsed with no network call; a domain or https URL enables the careers-page step; a plain name only uses the starter list and slug probes. Slug guesses for a name drop legal suffixes and "the" (Inc, LLC, Ltd, GmbH, Corp, Co, PLC, AG, SA, BV) and try the joined words, the hyphenated words and the first word; probes run Greenhouse, Lever, Ashby in that order (never Personio) and the first hit wins, so an ambiguous name returns at most one medium-confidence board. A starter-list match can return several boards (all high). EXAMPLE: {"company":"ramp.com"} -> [{"ats":"ashby","slug":"ramp","via":"seed_list","confidence":"high"}].

    Annotations the server declares

    • title: Find which ATS board a company uses (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • jobs_get_boardWHAT: Fetch every job currently published on ONE company board from its official public ATS API (Greenhouse, Lever or Ashby) or from Personio's documented XML feed for an account y…Reads text others write1 finding

    Why it is in this group

    • The name "jobs_get_board" says "get": a word for reading.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 2,476 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...gineer"],"remote":"only"} -> remote engineering roles at Ramp with salary ranges."

      What this check looks for
    Description, as the server wrote it
    WHAT: Fetch every job currently published on ONE company board from its official public ATS API (Greenhouse, Lever or Ashby) or from Personio's documented XML feed for an account you name, normalize it and filter it on this Worker. WHEN: You know the board (ats + slug), e.g. from jobs_find_board or a careers-page link, and want its live openings now. For Personio you already know the company subdomain. WHEN NOT: One known job's full detail (jobs_get_job). Many companies at once, history (new/closed/reposted since last run), or careers-page discovery at scale: use jobs_search_companies (paid). Unknown Greenhouse/Lever/Ashby board: use jobs_find_board first. Personio accounts are never discovered. SmartRecruiters, LinkedIn, Workday, Workable, Recruitee and Rippling are not supported and never contacted. BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs or results. The slug is sent to the public ATS host in the requests below. Greenhouse, Lever and Ashby: one GET to the documented API host only (very large Greenhouse boards: a second, lighter GET). Personio: robots.txt, then GET https://{account}.jobs.personio.de/xml only (no other path, query or apply URL); all Personio accounts share one lane, one request at a time, at least 1.5 s apart. Nothing stored, no history: first_seen_at = now and change = null. Any HTTP 403/429 stops that host for the session (kill switch, HOST_BLOCKED error). A blocked Personio account does not block other accounts. Lever requests are spaced 1 s apart (its robots.txt Crawl-delay). COST: No APIFY_TOKEN, no billing. RETURNS: Paged "job" rows. compensation only when the ATS publishes it; posted_at is the ATS date or null (never guessed); age_basis says how age_days was derived. PARAMETERS: All filters are AND-ed; values inside one filter are OR-ed. titleIncludes/titleExcludes match whole words ("engineer" does not match "Engineering"). countries uses the ATS's structured country where present (Lever, Ashby) and a place-name list otherwise; rows without any country are dropped when countries is set. postedWithinDays drops rows with posted_at = null. Paging re-fetches the board, so pages from different calls can shift if the employer edits the board in between. EXAMPLE: {"ats":"ashby","slug":"ramp","titleIncludes":["engineer"],"remote":"only"} -> remote engineering roles at Ramp with salary ranges.

    Annotations the server declares

    • title: Get live jobs from one ATS board (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • jobs_get_jobWHAT: Fetch the full detail of ONE job that is currently published on its official public ATS API (Greenhouse, Lever or Ashby) or on a Personio XML feed for an account you name, no…Reads text others write1 finding

    Why it is in this group

    • The name "jobs_get_job" says "get": a word for reading.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 2,658 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...emoved. EXAMPLE: {"jobKey":"greenhouse:stripe:8172503"} -> that Stripe job's row."

      What this check looks for
    Description, as the server wrote it
    WHAT: Fetch the full detail of ONE job that is currently published on its official public ATS API (Greenhouse, Lever or Ashby) or on a Personio XML feed for an account you name, normalized the same way as a board listing, including its description. WHEN: You already have a job_key, or ats + slug + jobId, from jobs_get_board or a board link and need that single job's text, location, compensation and dates. WHEN NOT: Open or closed only (jobs_check_open). Every opening on a board (jobs_get_board). Many companies, history or repost signals (jobs_search_companies, paid). SmartRecruiters, LinkedIn, Workday, Workable, Recruitee and Rippling are not supported and never contacted. BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs or results. The job id and board slug are sent to the public ATS host in the requests below. Greenhouse and Lever: one GET to the documented per-job endpoint (the description is in that response). Ashby has no per-job endpoint, so the board is fetched once and the id is looked up. Personio has no per-job endpoint: robots.txt, then GET https://{account}.jobs.personio.de/xml only, and the position id is looked up. No apply URL is requested. HTTP 404/410 on Greenhouse or Lever, when the board itself exists, or an id missing from an Ashby or Personio board, is JOB_CLOSED (closed or unknown id) with no partial row. Nothing is stored. Any HTTP 403/429 stops that host for the session (kill switch, HOST_BLOCKED). Lever requests are spaced 1 s apart. Personio accounts share one lane, at least 1.5 s apart. COST: No APIFY_TOKEN, no billing. RETURNS: One "job" row, or a tool error: INVALID_INPUT, BOARD_NOT_FOUND, JOB_CLOSED, HOST_BLOCKED or NETWORK_ERROR (the last two retryable). A non-JSON body or other HTTP failure uses the ATS client code (ATS_BAD_RESPONSE, ATS_HTTP_ERROR). PARAMETERS: A malformed jobKey, or neither jobKey nor a complete ats + slug + jobId, returns INVALID_INPUT with no request. An Ashby id downloads the whole board with descriptions (one request, the same board GET as jobs_get_board with descriptions). A Personio id downloads the documented /xml feed (robots.txt plus /xml) and looks up the position id. Greenhouse and Lever use the per-job endpoint; a 404 or 410 triggers one extra board request so JOB_CLOSED is told apart from a missing board (BOARD_NOT_FOUND). description_text is always included when the job is open: plain text, at most 20,000 characters, e-mail addresses and phone numbers removed. EXAMPLE: {"jobKey":"greenhouse:stripe:8172503"} -> that Stripe job's row.

    Annotations the server declares

    • title: Get one published job's full detail (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • jobs_list_seed_boardsWHAT: List the bundled starter list of company job boards that Fairquery verified on their official ATS APIs (company, domain, ats, slug, verified_at, jobs_at_verification).No findings

    Why it is in this group

    • The name "jobs_list_seed_boards" says "list": a word for reading.
    Description, as the server wrote it
    WHAT: List the bundled starter list of company job boards that Fairquery verified on their official ATS APIs (company, domain, ats, slug, verified_at, jobs_at_verification). WHEN: To pick companies to scan, or to know which names jobs_find_board resolves instantly. WHEN NOT: Live job data (jobs_get_board) or companies not in the list (jobs_find_board). The list is a starting point, not a global index of employers. BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs or results. No further network call: it reads a static list compiled into this Worker. jobs_at_verification is a snapshot from verified_at and goes stale. COST: No APIFY_TOKEN, no billing. RETURNS: Paged "seed_board" rows in the fixed bundled order (grouped by ATS, so offsets are stable). PARAMETERS: ats narrows the list; omit it for the whole starter list (Greenhouse, Lever, Ashby). Personio is not in the starter list (named accounts only). meta.by_ats always counts the whole list per ATS and ignores ats, so take paging totals from total. Feed ats + slug of any item straight into jobs_get_board. EXAMPLE: {"ats":"lever"} -> Palantir, Spotify, Zoox, Ro.

    Annotations the server declares

    • title: List the verified starter boards (free, offline)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • jobs_search_companiesWHAT: Run the Fairquery Apify Actor fairquery/company-jobs-feed over many companies or boards: board discovery for Greenhouse, Lever and Ashby, a Personio XML feed when you name th…Reads text others write1 finding

    Why it is in this group

    • The name "jobs_search_companies" says "search": a word for reading.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.

    Findings

    • Info

      The description is 2,453 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...com","ramp.com"],"titleIncludes":["engineer"],"countries":["US"],"onlyNew":true}."

      What this check looks for
    Description, as the server wrote it
    WHAT: Run the Fairquery Apify Actor fairquery/company-jobs-feed over many companies or boards: board discovery for Greenhouse, Lever and Ashby, a Personio XML feed when you name the account, search filters, per-board history (first_seen/last_seen, new/updated/closed, repost detection), optional per-job re-checks and descriptions. WHEN: Monitoring a company list ("what is new since yesterday?"), searching the verified starter list, or anything that needs history, closed/repost signals or more than a few boards. WHEN NOT: One board right now (jobs_get_board), one job's full detail (jobs_get_job), or one open/closed re-check (jobs_check_open). BEHAVIOR: Read-only on job sites; requires APIFY_TOKEN. Called from this Fairquery-hosted Worker, which forwards your input and token to Apify and waits 20 s (APIFY_TIMEOUT if the run has not finished). The Actor runs in YOUR Apify account; history is kept in a named key-value store there. This Worker's code is designed not to store the input or token. Same politeness rules as the free jobs tools: documented ATS APIs only, Personio only at https://{account}.jobs.personio.de/xml after robots.txt, 403/429 kill switch, robots.txt on careers pages. Personio accounts are not discovered. Output is that run's rows, not a public jobs directory. COST: Paid, pay-per-event, billed to your Apify account: $0.99 per 1,000 jobs + $0.50 per 1,000 boards, plus optional $0.29 per 1,000 re-checks (confirmOpen) and $1.90 per 1,000 discovered companies; no start fee. Error, closed and merged rows are free. Example: 3 boards, 300 jobs = about $0.30. RETURNS: Paged dataset rows: "job" rows (as jobs_get_board plus first_seen_at, last_seen_at, change, reposted, repost_of, stale_signal, duplicate_of), one "board" row per board (jobs_total, new, updated, closed), "company" rows for discovery, "error" rows (code, message, retryable). PARAMETERS: companies, boards and useSeedList: at least one is required although the schema marks all optional (none -> INPUT_EMPTY, not billed). onlyNew implies history and compares with the last run that used the same stateStoreName, so use one name per saved search; the first run returns everything as new. limit/offset page the returned rows on this Worker; every call starts a new billed run, so fetch the pages you need from one result instead of re-calling. EXAMPLE: {"companies":["stripe.com","ramp.com"],"titleIncludes":["engineer"],"countries":["US"],"onlyNew":true}.

    Annotations the server declares

    • title: Search and monitor jobs across many companies (paid Apify Actor)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • osv_get_vulnerabilityWHAT: Fetch full OSV records (summary, details, aliases, severity vectors, affected ranges, fixed versions, references) for advisory IDs from api.osv.dev.Reads text others writeNo findings

    Why it is in this group

    • The name "osv_get_vulnerability" says "get": a word for reading.

    What it can reach

    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
    Description, as the server wrote it
    WHAT: Fetch full OSV records (summary, details, aliases, severity vectors, affected ranges, fixed versions, references) for advisory IDs 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) or a whole manifest (osv_audit_dependencies, paid). COST: Free; no token. Advisory IDs leave the client, are received by this Worker, and are forwarded to api.osv.dev. Nothing is sent to Apify. This Worker's code is designed not to store inputs or results. RETURNS: Paged "vulnerability" rows with the raw OSV record fields, or "error" rows (VULN_NOT_FOUND | OSV_FETCH_FAILED, see PARAMETERS). BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. One HTTPS call per ID to api.osv.dev, in parallel; typically <2 s, 20 s timeout each. PARAMETERS: ids: each ID is fetched separately 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.

    Annotations the server declares

    • title: Get OSV advisory (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • osv_query_packagesWHAT: Query the public OSV.dev database (api.osv.dev querybatch) for exact package versions; returns which are vulnerable and the advisory IDs.Reads private dataReads text others writeNo findings

    Why it is in this group

    • The name "osv_query_packages" says "query": a word for reading.

    What it can reach

    • private-dataThe description's first sentence says "database": it reads databases.
    • untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
    Description, as the server wrote it
    WHAT: Query the public OSV.dev database (api.osv.dev querybatch) 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. Package names and versions leave the client, are received by this Worker, and are forwarded to api.osv.dev. Nothing is sent to Apify. This Worker's code is designed not to store inputs or results. RETURNS: Paged "package" rows (vulnerable, vuln_ids) or per-entry INVALID_ECOSYSTEM "error" rows. Tool errors: OSV_FETCH_FAILED (retryable). BEHAVIOR: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. 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. An unknown ecosystem yields a per-entry INVALID_ECOSYSTEM error row while the other entries still run. A version 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",...].

    Annotations the server declares

    • title: Query OSV.dev (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • pii_list_detectorsWHAT: Catalog of every PII and secret entity type, which ones the free tools run on this Worker (pii_scan_text / pii_redact_text) vs only in the paid Actor (NER names/locations, st…Reads private dataReads text others writeNo findings

    Why it is in this group

    • The name "pii_list_detectors" says "list": a word for reading.

    What it can reach

    • private-dataThe description's first sentence says "secret": it reads secrets.
    • untrusted-inputThe description's first sentence says "url": it reads web pages, which anyone can write.
    Description, as the server wrote it
    WHAT: Catalog of every PII and secret entity type, which ones the free tools run on this Worker (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: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. No further network call; the catalog is compiled into this Worker, 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},...]}

    Annotations the server declares

    • title: List PII/secret detectors (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • sanctions_list_sourcesWHAT: Static list of the official sanctions lists sanctions_screen_names checks (id, name, authority, download URL), the score scale and the legal disclaimer.Reads text others writeNo findings

    Why it is in this group

    • The name "sanctions_list_sources" says "list": a word for reading.

    What it can reach

    • untrusted-inputThe description's first sentence says "url": it reads web pages, which anyone can write.
    Description, as the server wrote it
    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: Runs on this Fairquery-hosted Worker (Cloudflare), not on the caller's machine; inputs leave the client. This Worker's code is designed not to store inputs, results, or an Apify token. No further network call; static catalog compiled into this Worker, 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",...},...]}

    Annotations the server declares

    • title: List sanctions sources (free)
    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Exfil surface

Which tools read private data, which read text someone else can write, and which can send data out. One of each is a path a prompt injection can use.

No private-data, untrusted-input, outbound path in this server alone: no tool is tagged outbound.

Reads private data2
Reads files, mail, messages, databases, secrets, calendars or repositories.
Can send data out0
Can carry data to people or systems: it sends, posts or publishes, or takes a recipient, channel or URL.

Other servers in the same agent add their own tools to these paths. Audit your whole setup to see them together.

Findings

Matches of fixed checks, with the text they matched.

2 warn11 info
  • The server marks "pii_restore_text" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The description says "cannot be restored".

    What this check looks for
  • The server marks "jobs_check_open" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}; The name "jobs_check_open" says "open": a word for a change.

    What this check looks for
  • The description mentions "credentials".

    Matched text

    description: "...lio/Azure keys, JWTs, private keys, URL credentials, key=value secrets) in text. WHEN: Befo..."

    What this check looks for
  • The description is 2,147 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...findings: EMAIL (masked "j***@a***.com"), AWS_ACCESS_KEY; risk_level "critical"."

    What this check looks for
  • The description is 2,433 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...com"]} -> verdict "valid", has_mx true, mail_provider "Google Workspace / Gmail"."

    What this check looks for
  • The description is 3,041 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...uba","entityType":"entity"}],"minScore":90} -> status potential_match (OFAC_SDN)."

    What this check looks for
Show 7 more findings
  • The description is 3,155 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...y":"US"}]} -> {decision:"pass",email_verdict:"valid",sanctions_status:"no_match"}"

    What this check looks for
  • The description is 3,002 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...tions false. A poisoned description returns finding rows and a higher risk_level."

    What this check looks for
  • The description is 2,476 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...gineer"],"remote":"only"} -> remote engineering roles at Ramp with salary ranges."

    What this check looks for
  • The description is 3,144 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...p.com"} -> [{"ats":"ashby","slug":"ramp","via":"seed_list","confidence":"high"}]."

    What this check looks for
  • The description is 2,117 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...ey":"greenhouse:stripe:8172503"} -> {"status":"open","method":"detail_endpoint"}."

    What this check looks for
  • The description is 2,658 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...emoved. EXAMPLE: {"jobKey":"greenhouse:stripe:8172503"} -> that Stripe job's row."

    What this check looks for
  • The description is 2,453 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...com","ramp.com"],"titleIncludes":["engineer"],"countries":["US"],"onlyNew":true}."

    What this check looks for

History

Changes to its tool definitions and to its remote endpoint, newest first.

No change seen yet. Its tools are rescanned every day, and a remote endpoint is looked at once a week.

Install

Pinned to version 1.1.10. Values in angle brackets are yours to fill in.

claude mcp add --transport http agent-trust https://mcp.getfairquery.com/mcp
claude mcp add --transport http agent-trust https://mcp.getfairquery.com/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change or a way out, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__agent-trust__pii_list_detectors",
      "mcp__agent-trust__email_validate_deliverability",
      "mcp__agent-trust__sanctions_list_sources",
      "mcp__agent-trust__osv_query_packages",
      "mcp__agent-trust__osv_get_vulnerability",
      "mcp__agent-trust__jobs_get_board",
      "mcp__agent-trust__jobs_find_board",
      "mcp__agent-trust__jobs_get_job",
      "mcp__agent-trust__jobs_list_seed_boards",
      "mcp__agent-trust__jobs_search_companies"
    ],
    "ask": [
      "mcp__agent-trust__pii_scan_text",
      "mcp__agent-trust__pii_redact_text",
      "mcp__agent-trust__pii_restore_text",
      "mcp__agent-trust__pii_guard_documents",
      "mcp__agent-trust__email_check_syntax",
      "mcp__agent-trust__sanctions_screen_names",
      "mcp__agent-trust__osv_audit_dependencies",
      "mcp__agent-trust__trust_screen_lead",
      "mcp__agent-trust__mcp_server_precheck",
      "mcp__agent-trust__jobs_check_open"
    ],
    "deny": []
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. A read that can send data out (it takes a URL, say) asks first too. It assumes the server is installed as agent-trust. Review it before you use it.

Capability tags behind 15 rules
  • mcp__agent-trust__pii_list_detectors private-data, untrusted-input
  • mcp__agent-trust__pii_guard_documents untrusted-input
  • mcp__agent-trust__email_validate_deliverability untrusted-input
  • mcp__agent-trust__sanctions_list_sources untrusted-input
  • mcp__agent-trust__sanctions_screen_names untrusted-input
  • mcp__agent-trust__osv_query_packages private-data, untrusted-input
  • mcp__agent-trust__osv_get_vulnerability untrusted-input
  • mcp__agent-trust__osv_audit_dependencies untrusted-input
  • mcp__agent-trust__trust_screen_lead untrusted-input
  • mcp__agent-trust__mcp_server_precheck untrusted-input
  • mcp__agent-trust__jobs_get_board untrusted-input
  • mcp__agent-trust__jobs_find_board untrusted-input
  • mcp__agent-trust__jobs_check_open untrusted-input
  • mcp__agent-trust__jobs_get_job untrusted-input
  • mcp__agent-trust__jobs_search_companies untrusted-input

Pin

Keep the decision: fail CI when the tools change, follow every change, or show a badge.

Pin in CI

Fail a build when the tools you reviewed are not the tools the server serves today.

curl -s "https://anlyon.dev/api/v1/servers/com.getfairquery/agent-trust-mcp/verify?hash=3f3e56ad35ccaf9a77504618c68e82370a9a860560914b26883916f62a5bd327" | jq -e '.changed == false'

Pinned hash 3f3e56ad35cc, from Oct 27, 2026, 00:17 UTC.

Watch

Get told when its tools change: a new tool, a new description, a new class.

https://anlyon.dev/servers/com.getfairquery/agent-trust-mcp/changes.xml

An Atom feed. Works in any feed reader or Slack's RSS app.

Badge

anlyon.dev badge for com.getfairquery/agent-trust-mcp
[![anlyon.dev](https://anlyon.dev/badge/com.getfairquery/agent-trust-mcp.svg)](https://anlyon.dev/servers/com.getfairquery/agent-trust-mcp)

Source: MCP Registry. Synced Oct 27, 2026, 00:17 UTC.