WEBMCP.COM

API for Agents

Query the WebMCP directory.

Read-only JSON API for agents. List WebMCP-enabled sites, fetch their tools and input schemas, or check whether a given URL exposes any.

TL;DR

Base URL https://webmcp.com. Directory endpoints return JSON, with no auth, CORS open (Access-Control-Allow-Origin: *). For a one-shot probe of an arbitrary URL, use /api/v1/lookup?url=….

Deprecation (2026-07-03). Tool kind values were renamed read/write/action → answer/act/transact — displayed on webmcp.com as Answer, Action, and Sensitive Action. The old values are no longer emitted or accepted by the kind filter. See the OpenAPI changelog and Tool categories.

Question

How can my agent find websites that support WebMCP?

Use this API. /api/v1/lookup?url=… tells you whether a page exposes WebMCP tools, /api/v1/sites lists every site in the directory, and /api/v1/sites/{host} returns one site's tools with their input schemas. Shopify stores have a separate index: read their shared tools at /api/v1/platforms/shopify and search stores at /api/v1/platforms/shopify/stores?q=…. The OpenAPI spec is at /api/openapi.json.

GET /api/v1/lookup

Probe an arbitrary URL

Given any URL, check the stored directory and Shopify index. A directory match returns supported: true and its site record. An index-only Shopify match returns platform: "shopify", platformUrl, and the shared toolCount, without a site record. Follow platformUrl for shared schemas; stores may add tools. This does not perform a live verification. The host is extracted, www. stripped, and path-scoped demos (e.g. googlechromelabs.github.io/webmcp-tools/demos/*) are matched by URL prefix.

ParamTypeDescription
urlstringAny URL on the page being probed. The host is extracted; path-scoped demos match by prefix.
hoststringAlternative to url. www. is stripped before matching.
Example — direct host match
GEThttps://webmcp.com/api/v1/lookup?url=https://store.nekuda.ai/checkout
{ "ok": true, "supported": true, "host": "store.nekuda.ai", "matchedHost": "store.nekuda.ai", "site": { "host": "store.nekuda.ai", "url": "https://store.nekuda.ai", "desc": "AI-native demo storefront", "type": "demo", "apiSurface": "spec", "toolCount": 13, "tools": [ /* 13 tools with inputSchema */ ] } }
Example — not supported
GEThttps://webmcp.com/api/v1/lookup?url=https://example.com
{ "ok": true, "supported": false, "host": "example.com", "message": "no WebMCP tools registered for this host in webmcp.com directory" }
GET /api/v1/sites

List & filter sites

Returns directory entries. All params are optional and combinable. Use fields to control response size: full includes every tool's inputSchema, summary drops schemas, minimal returns only name/kind/impl/description.

Curated Shopify entries remain in this directory. Search the additional store index with the Shopify store endpoint; it is excluded from bulk directory and tool listings.

ParamTypeDescription
typelive | demo | allFilter by site type. Defaults to all.
qstringSubstring search across host, description, URL, and every tool name/description.
toolstringReturn only sites that expose a tool whose name contains this substring.
kindanswer | act | transactFilter sites by tool category (Answer / Action / Sensitive Action). Repeat to OR (e.g. kind=answer&kind=act).
implimperative | declarativeFilter by tool implementation style.
apiSurfacespec | polyfill | mixedFilter by which API surface the site uses to register tools. spec = WICG registerTool or declarative DOM. polyfill = @mcp-b/webmcp-polyfill provideContext extension. mixed = both. Repeat to OR.
fieldsfull | summary | minimalResponse shape. Defaults to full.
limitintegerMax sites to return. Default 100, max 500.
offsetintegerPagination offset.
Example — live sites only, summary payload
GEThttps://webmcp.com/api/v1/sites?type=live&fields=summary
Example — sites that expose a checkout tool
GEThttps://webmcp.com/api/v1/sites?tool=checkout&fields=minimal
GET /api/v1/sites/{host}

Get a single site

Returns the full record for one directory entry, including every tool with its JSON Schema. host is the directory key — www. is stripped before matching.

GEThttps://webmcp.com/api/v1/sites/store.nekuda.ai
GET /api/v1/sites/{host}/tools

List tools for a site

Returns the tools array only — no surrounding site metadata.

GEThttps://webmcp.com/api/v1/sites/store.nekuda.ai/tools
GET /api/v1/sites/{host}/tools/{tool}

Get one tool definition

Returns a single tool's full record including its inputSchema.

GEThttps://webmcp.com/api/v1/sites/store.nekuda.ai/tools/add_to_cart
{ "ok": true, "host": "store.nekuda.ai", "url": "https://store.nekuda.ai", "tool": { "name": "add_to_cart", "kind": "act", "impl": "imperative", "page": "/cart", "description": "Add a product to the shopping cart…", "inputSchema": { "type": "object", "properties": { "product_id": { "type": "string", "description": "The product ID to add" }, "quantity": { "type": "number", "description": "Number of items (default 1)" } }, "required": ["product_id"] } } }
GET /api/v1/tools

Search every tool across every site

Flat search across all tools in the directory. Each result carries the host and URL it belongs to.

ParamTypeDescription
qstringSubstring match against tool name and description.
kindanswer | act | transactFilter by category (Answer / Action / Sensitive Action). Repeat to OR.
implimperative | declarativeFilter by implementation style.
limit / offsetintegerPagination — default 100, max 500.
GEThttps://webmcp.com/api/v1/tools?q=cart&kind=act
GET /api/v1/stats

Aggregate directory stats

Useful for status dashboards. Returns totals plus a breakdown by tool kind and implementation, and the top 10 sites by tool count. sites, tools, and breakdowns cover directory records. platforms.shopify counts indexed stores; totalSites adds both sets and removes overlapping hosts. When the index is unavailable, the imported count is used and overlap assumes the importer included all curated Shopify stores. These counts describe index coverage.

GEThttps://webmcp.com/api/v1/stats
{ "ok": true, "version": 1, "sites": 18, "liveSites": 3, "demoSites": 15, "tools": 66, "byKind": { "act": 33, "answer": 26, "transact": 7 }, "byImpl": { "imperative": 58, "declarative": 8 }, "topSitesByToolCount": [ /* 10 entries */ ] }
GET /api/v1/platforms/shopify

One shared Shopify toolkit

Shopify stores share a core WebMCP implementation and may add their own tools. This endpoint returns the shared tools with input schemas, apiSurface, sentinelHost, and storeCount. Tools follow the latest directory snapshot of the reference store, falling back to the imported tool snapshot.

updatedAt is the store index import date. toolsUpdatedAt is when the reference tools were captured, or null if unknown. The count describes indexed hosts, not individual live verification. Returns 404 if platform metadata is unavailable.

source records the import provenance separately; indexSha256 identifies the artifact and indexReady reports whether it is available. The September 30, 2026 snapshot contains 2,375,833 hosts, combining historical and recent positive WebMCP findings. The shared Alo Yoga toolkit was captured again on September 30; this does not mean every indexed store was rechecked then.

GEThttps://webmcp.com/api/v1/platforms/shopify
GET /api/v1/platforms/shopify/stores

Find a Shopify store

Pass q with 3-100 characters to search indexed hostnames by substring, ignoring case. Queries are trimmed. Returns up to 20 hosts in results, plus query, matchCount, matchCountTruncated, and limit. The match count stops at 5,000; matchCountTruncated: true means 5,000 or more.

There is no pagination or bulk export. Curated Shopify records remain in the regular directory APIs. Index membership is not a live verification; use /api/v1/lookup?host=… for a stored record and the shared toolkit link. Non-directory lookups share the search quotas below.

Invalid queries return 400; an unavailable index returns 503. Search and non-directory lookups share limits: 30 requests per IP per 10 minutes, 50 per IP per UTC day, and 1,000 globally per UTC day. Operators can lower these ceilings. Invalid and cached searches count. Limits persist across restarts and instances; if quota storage is unavailable, requests return 503. A 429 response includes Retry-After and JSON retryAfter in seconds. The OpenAPI spec reports the configured limit.

GEThttps://webmcp.com/api/v1/platforms/shopify/stores?q=alo
POST /api/review-sessions

Start a private implementation review

Your coding agent can send selected WebMCP source code, read the findings, fix the implementation, and submit an updated version. This preview is enabled separately from the directory API. Before sending code, check that POST /api/code-reviews appears in this deployment's live OpenAPI contract.

Availability has not been confirmed. Check the live OpenAPI contract before submitting code.

Create a session by sending an empty JSON object, or only the anonymous handoffId your copied prompt includes (it links the prompt to this session in our analytics and carries nothing else) and, when the prompt was copied on a site's page, that site's hostname as site (it labels this session's notifications and is not verified). Save the returned sessionId, credential, and expiresAt privately. Use Authorization: Bearer <credential> to submit code and read reports. Session creation is rate-limited; it does not submit a review.

POST /api/review-sessions
Content-Type: application/json

{}

A successful request returns 201 with {sessionId, credential, expiresAt}. The default session lifetime is seven days; use the returned expiry. Source is retained privately until the session expires. When factual checks pass, it is sent to the review model for static analysis. The service does not execute the submitted code. Keep credentials out of URLs, shared prompts, logs, and version control.

POST /api/code-reviews

Submit the approved implementation

Include the selected tool definitions and the source that registers and implements them. A public URL is optional context for local work and is not part of this request. Show the user exactly which files and content will be shared, then set sharingApproved: true after approval. Include only relevant source from that project, without secrets, credentials, personal data, or unrelated files.

FieldTypeDescription
protocolVersionintegerRequired: 2.
projectIdstringStable identifier for the local project: 1-80 letters, digits, periods, underscores or hyphens. Keep it the same for the whole loop.
sharingApprovedbooleanRequired: true, following the user's approval of the shared selection.
toolsarray1-100 complete tool definitions with unique names. Supported fields: name, title, description, inputSchema, outputSchema, annotations, availability, page, kind, impl, apiSurface. Source belongs in files.
filesarray1-20 objects containing path and content. Unique relative POSIX paths of at most 240 characters, inside the approved selection. No traversal, absolute paths, .env, .git, node_modules, secret files, or private keys.
expectedToolsarrayOptional: 1-100 declarations of name, optional page (default /), and optional fields. Declare the exact intended fields, including name, to detect missing definition fields.
expectedFilesarrayOptional: 1-20 declarations of exactly path, sha256, and bytes. Hash complete selected files as UTF-8 using lowercase SHA-256; count bytes, not characters. Unique paths; ordering does not matter.
withheldFilesarrayOptional: 1-20 relevant files you deliberately did not send, each with path (not also in files), reason (secret, too-large, third-party or other) and an optional one-line note of up to 300 characters, such as a package name and version. Never put a secret value in a note. The review treats their content as unknown, covers the rest, and names the gap; the receipt and assessment.scope.withheldFiles list them.
previousReviewIdstringOmit for the first review. For every updated submission, send the latest review ID from this session and project.

Limits are 50 KiB per file, 100 KiB of source total, and 256 KiB for the complete JSON request, measured in UTF-8 bytes. Object-form schemas support at most 64 levels. Optional output schemas may also be boolean JSON Schemas. Explicit null is accepted by transport but fails factual validation; omit an absent optional schema instead. Preserve complete definitions and code; explicitly revise the selection if it does not fit, rather than silently dropping files or cutting schemas.

Build the optional manifests from the approved selection before assembling the payload. Otherwise the manifest could repeat the same omission. Compare the received receipt against this independent selection. A match proves only that the declared selection arrived; it never establishes full repository coverage. If a request does not fit, revise the approved scope explicitly and rebuild the manifests.

Authored example only - not a request to upload your repository
{
  "protocolVersion": 2,
  "projectId": "page-title-demo",
  "sharingApproved": true,
  "tools": [{
    "name": "get_page_title",
    "description": "Return the current page title as text.",
    "inputSchema": { "type": "object", "properties": {} }
  }],
  "expectedTools": [{ "name": "get_page_title", "fields": ["name", "description", "inputSchema"] }],
  "files": [{
    "path": "src/tools.js",
    "content": "navigator.modelContext.registerTool({\n  name: 'get_page_title',\n  description: 'Return the current page title as text.',\n  inputSchema: { type: 'object', properties: {} },\n  execute: async () => ({ content: [{ type: 'text', text: document.title }] })\n});\n"
  }],
  "expectedFiles": [{ "path": "src/tools.js", "sha256": "12a2edc5c8ebf3392c0be202f4906e88d43b1fc4954aacb19d792ac879bb0ac1", "bytes": 255 }]
}

Send Content-Type: application/json, the session's Authorization header, and an Idempotency-Key of 16-128 letters, digits, hyphens or underscores. Use the SHA-256 hex digest of the exact JSON bytes you will transmit, and save those bytes and the key privately before submitting. Changed JSON gets a new key.

The review ID is the lowercase hex SHA-256 of the UTF-8 string sessionId + NUL + idempotencyKey, where NUL is byte 0. Compute and save it before POST. If the response is lost, recover the known report with authenticated GET /api/reviews/{id}. Never blindly repeat a submission or create a new session to recover an uncertain POST. If GET cannot recover the report, stop and report the uncertainty.

202 response shape
{ "reviewId": "<computed-review-id>", "reviewType": "source", "stage": "queued", "evidenceType": "submitted", "protocolVersion": 2, "reportUrl": "/api/reviews/<computed-review-id>", "submission": { "reviewType": "source", "sharingApproved": true, "receivedTools": [{ "name": "get_page_title", "fields": ["description", "inputSchema"] }], "receivedFiles": [{ "path": "src/tools.js", "sha256": "12a2edc5c8ebf3392c0be202f4906e88d43b1fc4954aacb19d792ac879bb0ac1", "bytes": 255 }], "toolsCompleteness": "matches-declared-tools", "filesCompleteness": "matches-declared-files", "repositoryCompleteness": "not-verified" }, "advisory": { "status": "pending" }, "loop": { "attempt": 1, "maxAttempts": 3, "bestGrade": "A+", "canContinue": false, "stopReason": null }, "nextAction": "get-report", "pollAfterSeconds": 2 }

The response also includes server-generated factualChecks (omitted from the shape above for brevity). Compare receivedTools and receivedFiles with the manifests. Source receipt fields exclude name, so compare ["name", ...receivedTools[i].fields] with the declared fields; use page || "/" for tool identity. Compare every file path, hash and byte count. toolsCompleteness and filesCompleteness are matches-declared-tools and matches-declared-files when declared, otherwise not-declared. repositoryCompleteness is always not-verified. Recovering the same accepted submission does not add a review call. Reusing its key for different content returns 409.

Manifest mismatches reject the request before accepting a review. Read fields and nextAction: "fix-request": review_scope_mismatch identifies missing or unexpected tools, review_fields_mismatch identifies changed definition fields, source_selection_mismatch returns missingFiles/unexpectedFiles, and source_content_mismatch returns mismatchedFiles with expected and received hashes/bytes. Restore the complete approved selection; do not silently shrink it to make the request pass.

GET /api/reviews/{id}

Read the findings and continue the loop

factualChecks contains server-generated definition checks; advisory describes model advice separately. Factual checks run first. If they find issues or cannot fully validate supplied schemas, the service skips the model and finishes with no assessment and advisory: {status: "not-requested", reason: "factual-checks-need-attention"}. This can be a fixable result: follow loop.canContinue and nextAction.

Factual statuses are checks-passed, issues-found, or insufficient-evidence. Read checkVersion, scope, checks, tools, and counts. Repair entries in factualChecks.fields have path, code, status (fail or not-assessed), and message. The path is a JSON Pointer into your request, for example /tools/0/inputSchema/properties/count/minimum; property names escape ~ as ~0 and / as ~1. Supplied schemas use their supported declared dialect (draft-07, 2019-09 or 2020-12), with 2020-12 assumed when absent. Unsupported dialects and unresolved external references remain unknown. These checks validate definitions, not actual arguments, results, design quality or execution; implementationGrade remains null.

Read the report with the same session credential. Honor pollAfterSeconds while it is running. When model advice completes, advisory.status is complete. A completed source assessment has assessment.reviewType: "source" and rubricVersion: "webmcp-source-implementation-v2". It includes the project ID, file hashes, and findings with path, startLine, endLine, and an exact source quote in each finding's evidence.

WebMCP.com derives the technical implementation grade from findings grounded in the submitted source. Use grade only when the assessment is complete. Read nextAction and loop before another submission; a stale report may point to latestReviewId.

The loop stops after three accepted reviews, including factual-only results, or when current factual checks pass and a complete review earns A+, whichever comes first. Best-grade requires zero factual failures, fully validated supplied schemas, and complete model advice. Intentional unknowns about design or execution do not block it. Read-only report polling does not count, and neither does a review the service could not finish: the daily budget was reached or could not be checked, a restart interrupted it, or the model returned no usable review (at most two retries per step). Its answer says nextAction: "wait" with retryAfterSeconds; after that, send the same step again with that review as previousReviewId and a new Idempotency-Key. Within the limit, the agent fixes useful findings, runs focused tests, and submits the updated approved code with the latest previousReviewId. Keep the same session and projectId; never replace either to bypass the limit. Get approval before adding files to the shared selection. Stop earlier if the API says to stop, a review fails, tests cannot run, access is blocked, no useful findings remain, or the last pass made no progress. Otherwise an incomplete or unavailable advisory stops the loop; preserve factual findings and do not infer a grade. Legacy reports without current factual checks cannot establish the best grade.

This is a private static review of the submitted implementation. targetUrl is null and scope.execution is not-tested. It does not change your directory listing or verify deployment, discoverability, site coverage, or agent task success. After the user confirms deployment, a live rescan is a separate, rate-limited action. Connecting tools to AgentLane is an optional next step.

StatusMeaningNext action
400 / 413Invalid requestRead field and size errors. A rejected request is not an accepted review; never silently shrink the approved selection.
401Session unavailableStop if the credential is missing, invalid or expired.
404Review unavailableStop if the known report cannot be recovered in this session.
409Conflict or stopped loopFollow the returned nextAction, reviewId and loop. A pending review must finish before another submission.
429Rate limitedStop the autonomous loop. Honor Retry-After and retryAfterSeconds before any later user-requested attempt.
503Service unavailableAn uncertain POST may have been accepted. Read its known report, without repeating POST. Stop if recovery is unavailable.

The older POST /api/reviews accepts tool definitions and a site URL only. It rejects source files. Use the separately enabled code-review route above for implementation source.

GET /api/openapi.json

OpenAPI 3.1 spec

The full machine-readable contract for this API. Works with Swagger UI, OpenAPI codegen, or a ChatGPT action.

GEThttps://webmcp.com/api/openapi.json

Built and maintained by nekuda. Missing a site? Submit it on the directory.