# Plainport > Deterministic remote MCP and REST utilities for public-web intake: fetch readable Markdown, inspect URLs, extract metadata and links, parse feeds and sitemaps, inspect robots rules, convert HTML, diff text, and validate JSON Schema. Stateless Streamable HTTP; no login or model calls. - MCP endpoint: https://plainport.exportitnow.workers.dev/mcp (Streamable HTTP, protocol 2026-07-28) - REST documentation: https://plainport.exportitnow.workers.dev/docs - OpenAPI: https://plainport.exportitnow.workers.dev/openapi.json - Full agent documentation: https://plainport.exportitnow.workers.dev/llms-full.txt - MCP Registry manifest: https://plainport.exportitnow.workers.dev/server.json - MCP Server Card: https://plainport.exportitnow.workers.dev/.well-known/mcp/server-card.json - Tool catalog: https://plainport.exportitnow.workers.dev/api/v1/tools Use Plainport for deterministic public-web intake, parsing, text conversion, diffs, and JSON Schema validation. It does not execute JavaScript, authenticate to sites, crawl recursively, run code, or call an LLM. # Response contract REST success: {"ok":true,"data":{},"meta":{"request_id":"...","duration_ms":1}}. REST failure: {"ok":false,"error":{"code":"...","message":"...","retryable":false},"meta":{...}}. MCP structuredContent uses the same success or error envelope. # Tools ## fetch_markdown Fetches one public HTTP/HTTPS page and returns its primary readable content as Markdown. Use this for article or documentation body text without page chrome. It does not run JavaScript; use a browser tool for client-rendered pages. Native text/markdown responses are preserved. Limit: 8 s, 5 redirects, 768 KiB remote body, 150k output characters. REST: POST https://plainport.exportitnow.workers.dev/api/v1/fetch-markdown Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "minLength": 8, "maxLength": 2048, "description": "Absolute public HTTP or HTTPS URL without embedded credentials." }, "max_chars": { "default": 100000, "type": "integer", "minimum": 1000, "maximum": 150000 } }, "required": [ "url", "max_chars" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "requested_url": { "type": "string" }, "final_url": { "type": "string" }, "status": { "type": "integer" }, "title": { "type": [ "string", "null" ] }, "markdown": { "type": "string" }, "content_type": { "type": "string" }, "source_format": { "enum": [ "html", "markdown", "plain_text" ] }, "redirects": { "type": "array", "items": { "type": "object" } }, "bytes_read": { "type": "integer" }, "truncated": { "type": "boolean" } }, "required": [ "requested_url", "final_url", "status", "title", "markdown", "content_type", "source_format", "redirects", "bytes_read", "truncated" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## inspect_url Resolves a public URL through bounded redirects and returns status plus a safe allowlist of HTTP headers. Use HEAD for cheap reachability/type checks and GET when a server mishandles HEAD. Use extract_metadata for HTML semantic metadata. Limit: 8 s, 5 redirects, 768 KiB only for GET. REST: POST https://plainport.exportitnow.workers.dev/api/v1/inspect-url Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "minLength": 8, "maxLength": 2048, "description": "Absolute public HTTP or HTTPS URL without embedded credentials." }, "method": { "default": "HEAD", "type": "string", "enum": [ "GET", "HEAD" ] } }, "required": [ "url", "method" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "requested_url": { "type": "string" }, "final_url": { "type": "string" }, "method": { "enum": [ "GET", "HEAD" ] }, "status": { "type": "integer" }, "status_text": { "type": "string" }, "content_type": { "type": "string" }, "headers": { "type": "object", "additionalProperties": { "type": "string" } }, "redirects": { "type": "array" }, "bytes_read": { "type": "integer" } }, "required": [ "requested_url", "final_url", "method", "status", "headers", "redirects", "bytes_read" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## extract_metadata Fetches one public HTML page and extracts title, description, canonical URL, robots directives, Open Graph, Twitter Card, advertised feeds, and parsed JSON-LD. Use this when an agent needs page identity or structured data rather than body text. Limit: 8 s, 5 redirects, 768 KiB, 50 JSON-LD blocks. REST: POST https://plainport.exportitnow.workers.dev/api/v1/extract-metadata Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "minLength": 8, "maxLength": 2048, "description": "Absolute public HTTP or HTTPS URL without embedded credentials." } }, "required": [ "url" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "requested_url": { "type": "string" }, "final_url": { "type": "string" }, "title": { "type": [ "string", "null" ] }, "description": { "type": [ "string", "null" ] }, "canonical_url": { "type": [ "string", "null" ] }, "language": { "type": [ "string", "null" ] }, "robots": { "type": [ "string", "null" ] }, "author": { "type": [ "string", "null" ] }, "open_graph": { "type": "object" }, "twitter": { "type": "object" }, "feeds": { "type": "array" }, "json_ld": { "type": "array" }, "invalid_json_ld_count": { "type": "integer" } }, "required": [ "requested_url", "final_url", "title", "description", "canonical_url", "open_graph", "twitter", "feeds", "json_ld", "invalid_json_ld_count" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## extract_links Fetches one public HTML page and returns unique normalized HTTP/HTTPS links with anchor text, rel values, and same-origin classification. Use this for bounded navigation discovery; it does not crawl the returned links. Limit: 8 s, 5 redirects, 768 KiB, 500 links. REST: POST https://plainport.exportitnow.workers.dev/api/v1/extract-links Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "minLength": 8, "maxLength": 2048, "description": "Absolute public HTTP or HTTPS URL without embedded credentials." }, "include_external": { "default": true, "type": "boolean" }, "max_links": { "default": 100, "type": "integer", "minimum": 1, "maximum": 500 } }, "required": [ "url", "include_external", "max_links" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "requested_url": { "type": "string" }, "final_url": { "type": "string" }, "links": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string" }, "text": { "type": [ "string", "null" ] }, "rel": { "type": "array", "items": { "type": "string" } }, "external": { "type": "boolean" } }, "required": [ "url", "text", "rel", "external" ] } }, "truncated": { "type": "boolean" }, "max_links": { "type": "integer" } }, "required": [ "requested_url", "final_url", "links", "truncated", "max_links" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## parse_feed Fetches and normalizes RSS 2.0, Atom 1.0, or JSON Feed into a stable item list. Use this when consuming update feeds without format-specific parsing. It does not fetch linked articles. Limit: 8 s, 5 redirects, 768 KiB, 100 items. REST: POST https://plainport.exportitnow.workers.dev/api/v1/parse-feed Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "minLength": 8, "maxLength": 2048, "description": "Absolute public HTTP or HTTPS URL without embedded credentials." }, "max_items": { "default": 50, "type": "integer", "minimum": 1, "maximum": 100 } }, "required": [ "url", "max_items" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "format": { "enum": [ "rss", "atom", "json_feed" ] }, "version": { "type": [ "string", "null" ] }, "title": { "type": [ "string", "null" ] }, "home_page_url": { "type": [ "string", "null" ] }, "feed_url": { "type": "string" }, "items": { "type": "array", "items": { "type": "object" } }, "truncated": { "type": "boolean" } }, "required": [ "format", "version", "title", "home_page_url", "feed_url", "items", "truncated" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## parse_sitemap Fetches one uncompressed XML sitemap and returns either URL entries or child sitemap references. Use this for site structure discovery. Sitemap indexes are not recursively expanded; call this tool on each child explicitly. Limit: 8 s, 5 redirects, 768 KiB, 1,000 entries. REST: POST https://plainport.exportitnow.workers.dev/api/v1/parse-sitemap Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "minLength": 8, "maxLength": 2048, "description": "Absolute public HTTP or HTTPS URL without embedded credentials." }, "max_entries": { "default": 250, "type": "integer", "minimum": 1, "maximum": 1000 } }, "required": [ "url", "max_entries" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "kind": { "enum": [ "urlset", "index" ] }, "sitemap_url": { "type": "string" }, "entries": { "type": "array", "items": { "type": "object" } }, "truncated": { "type": "boolean" } }, "required": [ "kind", "sitemap_url", "entries", "truncated" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## inspect_robots Fetches an origin's robots.txt and evaluates one path for a named user agent using longest-match allow/disallow precedence. Also returns crawl delay and declared sitemaps. Use this before automated retrieval; the result is policy guidance, not authorization. Limit: 8 s, 5 redirects, 768 KiB, 50 sitemap declarations. REST: POST https://plainport.exportitnow.workers.dev/api/v1/inspect-robots Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "minLength": 8, "maxLength": 2048, "description": "Absolute public HTTP or HTTPS URL without embedded credentials." }, "user_agent": { "default": "*", "type": "string", "minLength": 1, "maxLength": 200 }, "path": { "default": "/", "type": "string", "maxLength": 2048, "pattern": "^\\/.*" } }, "required": [ "url", "user_agent", "path" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "robots_url": { "type": "string" }, "status": { "type": "integer" }, "allowed": { "type": "boolean" }, "matched_rule": { "type": [ "object", "null" ] }, "crawl_delay": { "type": [ "number", "null" ] }, "sitemaps": { "type": "array", "items": { "type": "string" } }, "groups": { "type": "integer" } }, "required": [ "robots_url", "status", "allowed", "matched_rule", "crawl_delay", "sitemaps", "groups" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## html_to_markdown Converts supplied HTML into readable Markdown without fetching a URL. Use this when the agent already has HTML. Provide base_url only to resolve relative links; use fetch_markdown when Plainport should retrieve the page. Limit: 200k input characters, 150k output characters. REST: POST https://plainport.exportitnow.workers.dev/api/v1/html-to-markdown Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "html": { "type": "string", "minLength": 1, "maxLength": 200000 }, "base_url": { "type": "string", "maxLength": 2048 }, "max_chars": { "default": 100000, "type": "integer", "minimum": 1000, "maximum": 150000 } }, "required": [ "html", "max_chars" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "title": { "type": [ "string", "null" ] }, "markdown": { "type": "string" }, "truncated": { "type": "boolean" }, "input_chars": { "type": "integer" }, "max_chars": { "type": "integer" } }, "required": [ "title", "markdown", "truncated", "input_chars", "max_chars" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## diff_text Computes a deterministic line- or word-level diff and returns typed add/remove/equal parts. Use this when an agent needs structured change data. Split highly divergent large documents if the computation limit is reached. Limit: 200k combined characters, 2,000 result parts, 8 ms diff budget. REST: POST https://plainport.exportitnow.workers.dev/api/v1/diff-text Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "before": { "type": "string", "maxLength": 200000 }, "after": { "type": "string", "maxLength": 200000 }, "granularity": { "default": "lines", "type": "string", "enum": [ "lines", "words" ] }, "ignore_whitespace": { "default": false, "type": "boolean" } }, "required": [ "before", "after", "granularity", "ignore_whitespace" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "equal": { "type": "boolean" }, "granularity": { "enum": [ "lines", "words" ] }, "parts": { "type": "array", "items": { "type": "object", "properties": { "operation": { "enum": [ "add", "remove", "equal" ] }, "value": { "type": "string" }, "count": { "type": "integer" } }, "required": [ "operation", "value", "count" ] } }, "truncated": { "type": "boolean" }, "before_chars": { "type": "integer" }, "after_chars": { "type": "integer" } }, "required": [ "equal", "granularity", "parts", "truncated", "before_chars", "after_chars" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ``` ## validate_json_schema Validates a JSON value against a supplied JSON Schema using draft 4, 7, 2019-09, or 2020-12. Use this to verify generated or extracted structured data. It performs validation only and does not mutate the instance. Limit: 512 KiB request, 40 nesting levels, 100 returned errors. REST: POST https://plainport.exportitnow.workers.dev/api/v1/validate-json-schema Input schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "schema": { "anyOf": [ { "type": "object", "propertyNames": { "type": "string" }, "additionalProperties": {} }, { "type": "boolean" } ] }, "instance": {}, "draft": { "default": "2020-12", "type": "string", "enum": [ "4", "7", "2019-09", "2020-12" ] } }, "required": [ "schema", "instance", "draft" ], "additionalProperties": false } ``` Output schema: ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "ok": { "const": true }, "data": { "type": "object", "properties": { "valid": { "type": "boolean" }, "draft": { "enum": [ "4", "7", "2019-09", "2020-12" ] }, "errors": { "type": "array", "items": { "type": "object" } }, "errors_truncated": { "type": "boolean" } }, "required": [ "valid", "draft", "errors", "errors_truncated" ] }, "meta": { "type": "object", "properties": { "request_id": { "type": "string" }, "duration_ms": { "type": "integer" }, "limits": { "type": "object" } }, "required": [ "request_id", "duration_ms" ] } }, "required": [ "ok", "data", "meta" ], "additionalProperties": false } ```