PG1

PG1 reference

Test your integration

Every MCP tool that can flag something has three fixed, made-up fixture inputs that always return the same answer: one FLAGGED, one CLEAN and one UNKNOWN. check_wallet_age also has a fourth, DELEGATED. Use them to test your integration's happy path, its "flagged" path and its skip/retry path without spending anything or depending on live data.

Fixture table

ToolFixtureArgumentsExpected result
check_wallet_sanctionsFLAGGED{"address":"0x7067312d746573742d6669787475726500000001"}listed: true, status "flagged", reason WALLET_SANCTIONED
check_wallet_sanctionsCLEAN{"address":"0x7067312d746573742d6669787475726500000002"}listed: false, status "no_flags"
check_wallet_sanctionsUNKNOWN{"address":"0x7067312d746573742d6669787475726500000003"}HTTP 503, JSON-RPC error -32003 "Sanctions data temporarily unavailable, please retry."
check_domain_ageFLAGGED{"domain":"pg1-test-flagged.invalid"}found: true, age_days 3, newly_registered: true, status "flagged", reason DOMAIN_NEWLY_REGISTERED_30D
check_domain_ageCLEAN{"domain":"pg1-test-clean.invalid"}found: true, registration_date 2000-01-01, status "no_flags"
check_domain_ageUNKNOWN{"domain":"pg1-test-unknown.invalid"}found: false, reason_code "timeout", status "unknown" (what a real registration-lookup timeout returns)
check_hostname_reputationFLAGGED{"hostname":"pg1-test-flagged.invalid"}verdict "listed" (match_type "exact"), status "flagged", reason HOSTNAME_PHISHING_LISTED
check_hostname_reputationCLEAN{"hostname":"pg1-test-clean.invalid"}verdict "not_listed", status "no_flags"
check_hostname_reputationUNKNOWN{"hostname":"pg1-test-unknown.invalid"}HTTP 503, JSON-RPC error -32003 "Phishing domain list temporarily unavailable, please retry."
check_wallet_ageFLAGGED{"address":"0x7067312d746573742d6669787475726500000001"}found: false (no transfer history), status "flagged", reason WALLET_NO_HISTORY
check_wallet_ageCLEAN{"address":"0x7067312d746573742d6669787475726500000002"}found: true, first_seen 2023-09-01T00:00:00.000Z, status "no_flags"
check_wallet_ageUNKNOWN{"address":"0x7067312d746573742d6669787475726500000003"}isError: true, code "upstream_unavailable", status "unknown" (never found: false)
check_wallet_ageDELEGATED{"address":"0x7067312d746573742d6669787475726500000004"}found: true, is_contract: true, delegated: true, delegate_address 0x7067312d746573742d66697874757265de1e9a7e, reason WALLET_DELEGATED (informational), status "no_flags"
get_ioc_contextFLAGGED{"value":"192.0.2.1"}found: true, status "flagged", reason IOC_FOUND_IN_THREAT_FEED (free, no payment)
get_ioc_contextCLEAN{"value":"198.51.100.1"}found: false, status "no_flags"
get_ioc_contextUNKNOWN{"value":"203.0.113.1"}HTTP 503, JSON-RPC error -32603 "Threat data temporarily unavailable, please retry."
get_ioc_batchFLAGGED{"values":["2001:db8::1"]}total_found 1, status "flagged", reason IOC_FOUND_IN_THREAT_FEED (free, no payment)
get_ioc_batchCLEAN{"values":["2001:db8::2"]}total_found 0, status "no_flags"
get_ioc_batchUNKNOWN{"values":["2001:db8::3"]}HTTP 503, JSON-RPC error -32003 "Threat data temporarily unavailable, please retry."
get_cve_detailsFLAGGED{"cve_id":"CVE-0000-0001"}cisa_kev.is_known_exploited: true, status "flagged", reason CVE_KNOWN_EXPLOITED_KEV (free, no payment)
get_cve_detailsCLEAN{"cve_id":"CVE-0000-0002"}cisa_kev.is_known_exploited: false, status "no_flags"
get_cve_detailsUNKNOWN{"cve_id":"CVE-0000-0003"}exploit-score and KEV checks "error", exploit score null, status "unknown"
get_cve_batchFLAGGED{"cve_ids":["CVE-0000-0001"]}one KEV-listed result, status "flagged", reason CVE_KNOWN_EXPLOITED_KEV (free, no payment)
get_cve_batchCLEAN{"cve_ids":["CVE-0000-0002"]}one non-KEV result, status "no_flags"
get_cve_batchUNKNOWN{"cve_ids":["CVE-0000-0003"]}exploit-score and KEV checks "error", status "unknown"
get_cve_by_productFLAGGED{"vendor":"pg1-test.invalid","product":"flagged"}one KEV-listed CVE, status "flagged", reason CVE_KNOWN_EXPLOITED_KEV (free, no payment)
get_cve_by_productCLEAN{"vendor":"pg1-test.invalid","product":"clean"}total_found 0, status "no_flags"
get_cve_by_productUNKNOWN{"vendor":"pg1-test.invalid","product":"unknown"}exploit-score and KEV checks "error", status "unknown"

No fixtures exist for get_threat_indicators (a bulk feed with no target input), get_threat_actor_profile and get_usage_status (they have no reason code to flag with), or subscribe_alerts and submit_indicator (write actions whose status is a lifecycle state, not a verdict).

A2A: the four fixture-bearing free skills (check_wallet_sanctions, check_domain_age, check_hostname_reputation, check_wallet_age) accept the same fixtures on /api/a2a. A result comes back as a completed Task. An isError-style UNKNOWN comes back as JSON-RPC error -32000 with the details in error.data. A 503-style UNKNOWN comes back as HTTP 503 with JSON-RPC error -32010. These are the same shapes a real failure produces on that endpoint.

Out of scope: the REST endpoints (/api/ioc and /api/ioc/context) have no fixtures. Fixture values sent there are looked up like any other value.

Examples

FLAGGED, over MCP, with no key:

bash
curl -X POST https://pg1-ai-agent.vercel.app/api/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_hostname_reputation","arguments":{"hostname":"pg1-test-flagged.invalid"}}}'

UNKNOWN, over MCP, to test your skip path (an upstream_unavailable isError result):

bash
curl -X POST https://pg1-ai-agent.vercel.app/api/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_wallet_age","arguments":{"address":"0x7067312d746573742d6669787475726500000003"}}}'

A paid tool's FLAGGED fixture, still free with no key:

bash
curl -X POST https://pg1-ai-agent.vercel.app/api/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_cve_details","arguments":{"cve_id":"CVE-0000-0001"}}}'

Invalid-input messages

Invalid input keeps its existing error code (JSON-RPC -32602 for most tools, -32004 for a malformed get_cve_details id, or the tool's own invalid_address / invalid_hostname / invalid_chain isError code). The message says what was wrong, what format is expected, gives one valid example, and ends with the request_id. Your raw input is never echoed back. For example:

text
address is not a valid EVM address. Expected: '0x' followed by exactly 40 hex characters (case-insensitive). Example: 0x7067312d746573742d6669787475726500000002. request_id: 9cf7345b-f8d6-46f7-8e45-0da5e25a002d