{
  "$schema": "https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",
  "version": "1.0",
  "protocolVersion": "2025-11-25",
  "serverInfo": {
    "name": "adam-hale-tools",
    "title": "Adam Hale Tools: web research and open data",
    "version": "1.1.0",
    "websiteUrl": "https://tools.adamhaleworks.workers.dev/x402/"
  },
  "description": "Web research and open-data tools for AI agents: website tech stack, SEO meta tags, URL status, sitemap URLs, RSS/Atom to JSON, EU TED tenders (free single items), plus page to Markdown, domain WHOIS/DNS, robots.txt checks, PDF/document to Markdown, short audio transcription and website screenshots paid per call in USDC via x402.",
  "iconUrl": "https://tools.adamhaleworks.workers.dev/favicon.svg",
  "documentationUrl": "https://tools.adamhaleworks.workers.dev/x402/#mcp",
  "transport": {
    "type": "streamable-http",
    "endpoint": "/mcp"
  },
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "authentication": {
    "required": false,
    "schemes": []
  },
  "instructions": "Adam Hale Tools: web research and open-data lookups for agents. Single-item calls of tech_stack_lookup, rss_to_json, sitemap_urls and ted_tender_search are free (10 a minute per IP). Bulk calls and page_to_markdown, domain_intel, robots_txt_check, document_to_markdown, transcribe_short_audio and website_screenshot are paid per call in USDC on Base via x402 v2 ($0.001-$0.01): without payment the tool returns the x402 PaymentRequired object; an x402-capable client signs it and retries with _meta[\"x402/payment\"]. The same endpoints are plain HTTP at https://tools.adamhaleworks.workers.dev/x402/ (docs) and https://tools.adamhaleworks.workers.dev/openapi.json. This server never asks for or accepts private keys.",
  "tools": [
    {
      "name": "tech_stack_lookup",
      "title": "Website tech stack lookup",
      "description": "Detect what a website is built with: CMS, frameworks, analytics, CDN, hosting, email and DNS providers (MX, SPF, DMARC) and a security-header grade, from one fetch of its homepage. Free for 1 domain per call (10 free calls a minute per IP); 2-3 domains in one call cost $0.005 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "domains": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048,
              "description": "A public domain or homepage URL."
            },
            "minItems": 1,
            "maxItems": 3,
            "description": "1-3 domains or homepage URLs, e.g. [\"wordpress.org\"]. One domain is free; two or three are one paid call."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "domains"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "Website tech stack lookup",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "seo_meta_tags_check",
      "title": "SEO meta tag and schema.org check",
      "description": "On-page SEO audit of web pages: title and meta description lengths, canonical, robots/noindex and X-Robots-Tag, H1/H2, hreflang, Open Graph, Twitter card and JSON-LD rich-result checks, with machine-readable issue codes. Free for 1 URL when the free web tool is enabled; up to 3 URLs per call cost $0.004 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048,
              "description": "A public http(s) URL or bare domain (bare domain = its https:// homepage). No IP addresses, internal names or ports other than 80/443."
            },
            "minItems": 1,
            "maxItems": 3,
            "description": "1-3 page URLs."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "urls"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "SEO meta tag and schema.org check",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "url_status_check",
      "title": "URL status and redirect checker",
      "description": "Check whether links are alive: HTTP status, every redirect hop, final URL and a classification (ok, redirect, broken, restricted, blocked_by_site, disallowed_by_robots_txt, timeout). HEAD first, GET to confirm errors. Free for 1 URL when the free web tool is enabled; up to 10 URLs per call cost $0.003 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048,
              "description": "A public http(s) URL or bare domain (bare domain = its https:// homepage). No IP addresses, internal names or ports other than 80/443."
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "1-10 URLs to check."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "urls"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "URL status and redirect checker",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "sitemap_urls",
      "title": "Sitemap URL extractor",
      "description": "List the pages of a website from its sitemaps: finds them via robots.txt or common paths, follows sitemap indexes, reads gzip, text and feed sitemaps, returns URLs with lastmod. Free: up to 2,000 URLs from 12 sitemap files. With bulk=true: up to 5,000 URLs from 20 files for $0.004 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "A domain (sitemaps are discovered) or the URL of one sitemap file, e.g. \"github.blog\"."
          },
          "bulk": {
            "type": "boolean",
            "default": false,
            "description": "true = paid tier (5,000 URLs, 20 files)."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "Sitemap URL extractor",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "rss_to_json",
      "title": "RSS, Atom and JSON Feed to JSON",
      "description": "Read news feeds or blogs as clean JSON: accepts feed URLs or plain site URLs (finds the feed via <link rel=alternate>), normalises RSS 0.9x/2.0, RDF, Atom and JSON Feed to one item schema (title, url, publishedAt, author, categories, summary), up to 100 items per feed. Free for 1 feed; up to 5 feeds per call cost $0.003 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048,
              "description": "A public http(s) URL or bare domain (bare domain = its https:// homepage). No IP addresses, internal names or ports other than 80/443."
            },
            "minItems": 1,
            "maxItems": 5,
            "description": "1-5 feed or site URLs."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "urls"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "RSS, Atom and JSON Feed to JSON",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "ted_tender_search",
      "title": "EU public tender search (TED)",
      "description": "Search EU public procurement notices (tenders, contract notices, awards) on TED through the official API v3: keywords, optional buyer country and open-deadline filter, newest first. Free: the 25 newest matches. With bulk=true or page: 100 notices per page, pages 1-50, for $0.002 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "q": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300,
            "description": "Keywords; a comma separates alternatives (OR), up to 5, e.g. \"cloud hosting,data centre\"."
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$",
            "description": "Optional buyer country, ISO 3166-1 alpha-2 (EU/EEA + CH), e.g. \"FR\"."
          },
          "open_only": {
            "type": "boolean",
            "default": false,
            "description": "true = only notices whose tender deadline has not passed."
          },
          "page": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "description": "Paid tier: result page of 100 notices."
          },
          "bulk": {
            "type": "boolean",
            "default": false,
            "description": "true = paid tier (100 notices per page)."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "q"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "EU public tender search (TED)",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "page_to_markdown",
      "title": "Web page to clean Markdown",
      "description": "Read web pages: returns each page's main content as clean Markdown without navigation, scripts or ads (headings, lists, absolute links, tables, code blocks), plus title, description, language, canonical URL and word count; up to 100,000 characters per page. Does not get past bot walls. Paid only: up to 3 URLs per call for $0.002 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048,
              "description": "A public http(s) URL or bare domain (bare domain = its https:// homepage). No IP addresses, internal names or ports other than 80/443."
            },
            "minItems": 1,
            "maxItems": 3,
            "description": "1-3 page URLs."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "urls"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "Web page to clean Markdown",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "domain_intel",
      "title": "Domain WHOIS (RDAP), DNS and email security",
      "description": "Who a domain is registered with and since when (RDAP: registrar, created, expires, age, status, nameservers, DNSSEC), its DNS records (A, AAAA, MX, NS, TXT, CAA) and email setup (provider, SPF and DMARC policy). No registrant personal data. Paid only: up to 3 domains per call for $0.003 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "domains": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048,
              "description": "A public domain name."
            },
            "minItems": 1,
            "maxItems": 3,
            "description": "1-3 domains (a URL or email address is reduced to its domain)."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "domains"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "Domain WHOIS (RDAP), DNS and email security",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "robots_txt_check",
      "title": "robots.txt crawl permission check",
      "description": "Crawl preflight: whether each user agent may fetch each URL under the site's robots.txt (RFC 9309, longest match, '*' fallback), with crawl-delay, the sitemaps robots.txt lists and whether the site publishes /llms.txt. Default agents: GPTBot, ChatGPT-User, OAI-SearchBot, ClaudeBot, Claude-User, PerplexityBot, Google-Extended, Googlebot, Bingbot, CCBot, Applebot-Extended, Bytespider and *. Paid only: up to 5 URLs per call for $0.001 USDC. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "urls": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048,
              "description": "A public http(s) URL or bare domain (bare domain = its https:// homepage). No IP addresses, internal names or ports other than 80/443."
            },
            "minItems": 1,
            "maxItems": 5,
            "description": "1-5 URLs to check."
          },
          "agents": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^(\\*|[A-Za-z0-9][A-Za-z0-9._-]{0,39})$"
            },
            "minItems": 1,
            "maxItems": 15,
            "description": "Optional user-agent product tokens, e.g. [\"GPTBot\", \"*\"]."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "urls"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "robots.txt crawl permission check",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "document_to_markdown",
      "title": "PDF and document to Markdown",
      "description": "Convert one document at a public URL to Markdown from its text layer: PDF, Word DOCX, Excel XLSX/XLSM/XLSB/XLS, OpenDocument ODS/ODT, CSV, HTML, XML or Apple Numbers, up to 10 MB, Markdown up to 250,000 characters. Not OCR: scanned PDFs, images, PPTX and encrypted files are refused and not charged. Paid only: $0.004 USDC per file. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Public http(s) URL of the document, e.g. \"https://bitcoin.org/bitcoin.pdf\"."
          },
          "filename": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[\\w .()\\[\\]-]{1,120}$",
            "description": "Optional file name, used as a format hint."
          },
          "include_metadata": {
            "type": "boolean",
            "default": true,
            "description": "false drops the PDF metadata block from the Markdown."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "PDF and document to Markdown",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "transcribe_short_audio",
      "title": "Short audio transcription (Whisper)",
      "description": "Transcribe one short audio file (direct public URL to MP3, WAV, M4A, OGG, FLAC or WebM; one file up to 2 MB and 20 minutes) with Whisper large-v3-turbo, or translate it to English: text, timed segments, WebVTT and detected language. No YouTube or social media pages. Paid only: $0.01 USDC per file. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Direct public URL of the audio file."
          },
          "task": {
            "type": "string",
            "enum": [
              "transcribe",
              "translate"
            ],
            "default": "transcribe",
            "description": "translate = English output."
          },
          "language": {
            "type": "string",
            "pattern": "^[A-Za-z]{2,3}$",
            "description": "Optional spoken language, ISO 639-1 (en, de, es…)."
          },
          "vad_filter": {
            "type": "boolean",
            "default": true,
            "description": "false turns off silence filtering."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "Short audio transcription (Whisper)",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    },
    {
      "name": "website_screenshot",
      "title": "Website screenshot",
      "description": "Screenshot one public web page in a headless browser: desktop (1440x900) or mobile (390x844) viewport, PNG or JPEG, returned as an image. Viewport only, no logins or scripts; follows robots.txt and does not get past bot walls; at most one screenshot every 10 seconds across all callers. Paid only: $0.01 USDC per page. Paid calls made without a payment return isError with the x402 v2 PaymentRequired object (amount in USDC atomic units, network, payTo, HTTP endpoint); retry with the signed PaymentPayload in params._meta[\"x402/payment\"]. Invalid input and calls where every item fails are never charged. Fetches follow robots.txt, use the User-Agent AdamHaleTools/1.0 and refuse private or internal addresses.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Public http(s) URL of the page."
          },
          "device": {
            "type": "string",
            "enum": [
              "desktop",
              "mobile"
            ],
            "default": "desktop",
            "description": "Viewport: desktop 1440x900 or mobile 390x844."
          },
          "format": {
            "type": "string",
            "enum": [
              "png",
              "jpeg"
            ],
            "default": "png",
            "description": "Image format."
          },
          "x402_payment": {
            "type": "string",
            "maxLength": 16384,
            "description": "Optional, paid tier only: a signed x402 v2 PaymentPayload, base64 (the value an HTTP client sends as the PAYMENT-SIGNATURE header). Prefer params._meta[\"x402/payment\"]. Never a private key or seed phrase."
          }
        },
        "required": [
          "url"
        ],
        "additionalProperties": false
      },
      "annotations": {
        "title": "Website screenshot",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": true
      }
    }
  ],
  "resources": [],
  "prompts": []
}