{"openapi":"3.1.0","info":{"title":"webmcp.com Directory API","version":"1.0.0","description":"Read-only JSON API over the WebMCP Directory at webmcp.com. Use it to discover which websites expose WebMCP tools via navigator.modelContext, inspect each tool's JSON Schema, and probe an arbitrary URL to see whether it is WebMCP-enabled.\n\nChangelog\n- 2026-07-03: Tool `kind` values were renamed read/write/action → answer/act/transact (a clearer trust-tiered taxonomy). This is a breaking change to the enum: the old values (read, write, action) are no longer emitted or accepted by the `kind` filter. Update any client that pinned the old values. On webmcp.com these categories are displayed as Answer (answer), Action (act), and Sensitive Action (transact).\n\nPrivate implementation-code review is available through /api/code-reviews.","contact":{"name":"nekuda","url":"https://nekuda.ai"}},"servers":[{"url":"https://webmcp.com"}],"paths":{"/api/v1/directory":{"get":{"summary":"Paginated homepage row summaries without tool schemas","description":"15 rows per page. Preserves curated ordering, category facets, tool-text search, and Shopify exclusion. Full schemas remain at /api/v1/sites/{host}.","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"q","in":"query","schema":{"type":"string","maxLength":120}},{"name":"type","in":"query","schema":{"type":"string","enum":["all","live","demo"]}},{"name":"category","in":"query","schema":{"type":"string"},"description":"Exact name from categories; unknown names normalize to all."}],"responses":{"200":{"description":"rows (each with toolCount and toolKinds counts of answer, act and transact tools), total, page, pages, pageSize, stats and categories"},"304":{"description":"Unchanged representation"}}}},"/api/v1/sites":{"get":{"summary":"List WebMCP-enabled sites","description":"Returns directory entries, including curated Shopify records. The additional Shopify store index is not exported here; use /api/v1/platforms/shopify/stores to search it. All query parameters are optional; combine them to narrow the list.","parameters":[{"name":"type","in":"query","schema":{"type":"string","enum":["live","demo","all"]},"description":"Filter by site type."},{"name":"q","in":"query","schema":{"type":"string"},"description":"Substring search across host, description, URL, and tool name/description."},{"name":"tool","in":"query","schema":{"type":"string"},"description":"Return only sites that expose a tool whose name contains this substring."},{"name":"kind","in":"query","schema":{"type":"string","enum":["answer","act","transact"]},"description":"Filter by tool category. Repeat to OR (e.g. kind=answer&kind=act)."},{"name":"impl","in":"query","schema":{"type":"string","enum":["imperative","declarative"]},"description":"Filter by tool implementation style."},{"name":"apiSurface","in":"query","schema":{"type":"string","enum":["spec","polyfill","mixed"]},"description":"Filter by which API surface the site uses to register tools. spec = WICG registerTool / declarative DOM only. polyfill = @mcp-b/webmcp-polyfill provideContext only. mixed = both. Repeat to OR."},{"name":"fields","in":"query","schema":{"type":"string","enum":["full","summary","minimal"]},"description":"Response shape: full (default, with JSON schemas), summary (tools without inputSchema), minimal (tools as name/kind/impl/description only)."},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":500}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteListResponse"}}}}}}},"/api/v1/sites/{host}":{"get":{"summary":"Get a single site by host","parameters":[{"name":"host","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteResponse"}}}},"404":{"description":"Not found"}}}},"/api/v1/sites/{host}/tools":{"get":{"summary":"List tools exposed by a site","parameters":[{"name":"host","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/v1/sites/{host}/tools/{tool}":{"get":{"summary":"Get one tool definition including JSON schema","parameters":[{"name":"host","in":"path","required":true,"schema":{"type":"string"}},{"name":"tool","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/v1/tools":{"get":{"summary":"Flat search across every tool in the directory","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Substring search across tool name and description."},{"name":"kind","in":"query","schema":{"type":"string","enum":["answer","act","transact"]}},{"name":"impl","in":"query","schema":{"type":"string","enum":["imperative","declarative"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":500}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"OK"}}}},"/api/v1/lookup":{"get":{"summary":"Does this URL expose WebMCP tools?","description":"Pass either ?url=… (any page URL - host is extracted) or ?host=…. A directory match returns supported:true with site. An index-only Shopify match returns supported:true, platform, platformUrl and toolCount, without a site record. Follow platformUrl for shared tool schemas; individual stores may add tools. This is a stored index lookup, not a live verification. Non-directory lookups share Shopify search quotas and return 429 when exhausted or 503 when quota storage is unavailable. Otherwise returns supported:false. Path-scoped demos are matched by URL prefix.","parameters":[{"name":"url","in":"query","schema":{"type":"string","format":"uri"},"description":"Any URL on the page being probed."},{"name":"host","in":"query","schema":{"type":"string"},"description":"Alternative to ?url=. www. is stripped before matching."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResponse"}}}},"429":{"description":"Shopify quota exhausted","headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Shopify quota storage unavailable"}}}},"/api/v1/stats":{"get":{"summary":"Aggregate counts across the directory","description":"sites, tools and breakdowns cover directory records, including curated Shopify stores. platforms.shopify is the store index count, falling back to the imported metadata count. totalSites adds that count to sites and removes overlapping hosts; without the index, overlap assumes the importer included all curated Shopify hosts. Counts are index coverage, not a live verification.","responses":{"200":{"description":"OK"}}}},"/api/v1/platforms/shopify":{"get":{"summary":"Get the shared Shopify toolkit","description":"Shared baseline from the latest directory snapshot of sentinelHost, with imported tools as a fallback. Stores may add tools. updatedAt is the store index import date; toolsUpdatedAt is when the reference tools were checked, or null if unknown. storeCount is index coverage, not per-store verification. No store list is exported.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopifyPlatform"}}}},"404":{"description":"Platform metadata unavailable"}}}},"/api/v1/platforms/shopify/stores":{"get":{"summary":"Find a Shopify store","description":"Case-insensitive substring search of indexed hostnames. Returns at most 20 hosts, with no pagination or bulk export. matchCount is capped at 5000; matchCountTruncated indicates the cap. Index membership is not a live verification. Rate limit: 30 requests per IP per 10 minutes, plus 50 per IP per UTC day and 1000 globally per UTC day, shared with non-directory lookups. Invalid and cached queries count. Quotas persist across instances and restarts. Responses are not publicly cacheable.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":100},"description":"Store name or domain substring, trimmed before validation."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopifyStoreSearch"}}}},"400":{"description":"Query must be 3-100 characters"},"429":{"description":"Rate limited. JSON retryAfter and Retry-After header give seconds to wait.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until another request is allowed."}}},"503":{"description":"Store index or quota service unavailable on this instance"}}}},"/api/directory.json":{"get":{"summary":"Legacy: full directory dump (no filters)","deprecated":false,"responses":{"200":{"description":"OK"}}}},"/api/review-sessions":{"post":{"operationId":"createReviewSession","summary":"Create a private review session","description":"Rate-limited session creation. Save the credential privately and use it as a Bearer token for submissions and reports. expiresAt is authoritative; the default lifetime is seven days. Never create a new session to bypass a review limit.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"handoffId":{"type":"string","pattern":"^h_[a-z0-9]{12,32}$","description":"Optional anonymous reference from the copied webmcp.com prompt. It links the prompt copy to this session in analytics and carries nothing else."},"journey":{"type":"string","enum":["check-code","build-code"],"description":"Original WebMCP.com journey. Used only for request notifications."},"website":{"type":"string","format":"uri","maxLength":2048,"description":"Optional complete website URL for request notifications. Do not include credentials."},"email":{"type":"string","format":"email","maxLength":254,"description":"Optional requester email explicitly supplied for internal notifications."},"site":{"type":"string","maxLength":253,"pattern":"^(?=.{4,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$","description":"Optional website hostname from a prompt copied on that site's webmcp.com page. Not verified; it only labels this session's notifications and analytics."}}}}}},"responses":{"201":{"description":"Session created. The credential is returned only here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewSessionCreated"}}}},"400":{"description":"Send an empty object or the documented optional session metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"429":{"description":"Rate limited. Stop the agent loop; honor Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"503":{"description":"Service unavailable. A submission may have been accepted; recover its known report with GET. Never blindly repeat POST.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}}}}},"/api/code-reviews":{"post":{"operationId":"submitCodeReview","summary":"Review selected WebMCP definitions and implementation source","description":"Private static code review; no URL is required. Submit only the explicitly approved selection. Server-generated factualChecks run first; if they fail or supplied schemas cannot be fully validated, model advice is skipped and the report permits fixes within the loop limit. Otherwise code is sent to the model as untrusted text and is not executed. A session/project permits at most three accepted reviews, including factual-only results. Best-grade requires passing current factual checks and complete A+ model advice; provider failures stop the loop. Keep projectId stable and use the latest previousReviewId. No public directory grade is updated. Maximum request: 256 KiB; source: 20 files, 50 KiB per file, 100 KiB total (UTF-8 bytes).","security":[{"ReviewSession":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Use SHA-256 of the exact transmitted JSON bytes. Persist the key before POST. The known review ID is lowercase hex SHA-256(sessionId + NUL + key), UTF-8 encoded. Recover an uncertain submission with GET /api/reviews/{id}; do not blindly repeat POST. Reusing a key with different content conflicts. Recovering the same accepted submission does not consume another review.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{16,128}$","minLength":16,"maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeReviewRequest"}}}},"responses":{"202":{"description":"Accepted or recovered existing submission. Poll the private report.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeReviewAccepted"}}}},"400":{"description":"Invalid JSON, selection, path, size or idempotency key. No review accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"401":{"description":"Missing, invalid or expired private session credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"404":{"description":"Previous review not found in this session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"409":{"description":"Conflicting request key, pending review, wrong predecessor or stopped loop. Follow nextAction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"413":{"description":"Request exceeds 256 KiB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"429":{"description":"Rate limited. Stop the agent loop; honor Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"503":{"description":"Service unavailable. A submission may have been accepted; recover its known report with GET. Never blindly repeat POST.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}}}}},"/api/reviews/{id}":{"get":{"operationId":"getPrivateReview","summary":"Read a private review and its current loop state","description":"Authenticate with the same session credential. Read-only polling does not consume a review. Honor pollAfterSeconds, nextAction and loop, and recover latestReviewId before continuing from a stale report. factualChecks and advisory are separate siblings: factual repairs can finish with assessment absent or null and advisory not-requested. A checks-passed result earns no implementation grade. Only complete model assessments return one. Reports contain file hashes and grounded excerpts, not the submitted source bundle. A static review does not verify deployment or tool execution.","security":[{"ReviewSession":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":128}}],"responses":{"200":{"description":"Current review status. assessment is present when available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateReviewReport"}}}},"401":{"description":"Missing, invalid or expired private session credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"404":{"description":"Review not found in this session; stop rather than resubmit an uncertain request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}},"503":{"description":"Service unavailable. A submission may have been accepted; recover its known report with GET. Never blindly repeat POST.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewProblem"}}}}}}}},"components":{"schemas":{"Tool":{"type":"object","properties":{"name":{"type":"string","description":"Stable identifier the agent invokes."},"kind":{"type":"string","enum":["answer","act","transact"],"description":"answer (displayed as Answer) = read-only, returns info with no side effects. act (displayed as Action) = changes reversible state or drives the page (cart, filters, forms, navigation). transact (displayed as Sensitive Action) = money, commitment, or hard-to-reverse effects (checkout, order, book, subscribe, send, delete). Renamed 2026-07-03 from read/write/action — see the API description."},"impl":{"type":"string","enum":["imperative","declarative"],"description":"How the tool is registered — navigator.modelContext.registerTool (imperative) or a <tool> element (declarative)."},"description":{"type":"string"},"inputSchema":{"type":"object","description":"JSON Schema for the tool's input. Same shape as MCP/OpenAI function arguments."},"page":{"type":"string","description":"Optional page hint — relative path the tool typically runs on."}},"required":["name","kind","impl","description"]},"Site":{"type":"object","properties":{"host":{"type":"string","example":"store.nekuda.ai"},"url":{"type":"string","format":"uri"},"desc":{"type":"string"},"type":{"type":"string","enum":["live","demo"]},"apiSurface":{"type":"string","enum":["spec","polyfill","mixed"],"description":"Which API surface the site uses. spec = WICG registerTool / declarative DOM. polyfill = @mcp-b/webmcp-polyfill provideContext. mixed = both."},"platform":{"type":"string","description":"Shared platform when identified, e.g. shopify."},"toolCount":{"type":"integer"},"tools":{"type":"array","items":{"$ref":"#/components/schemas/Tool"}}},"required":["host","url","type"]},"SiteListResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"version":{"type":"integer","example":1},"count":{"type":"integer"},"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":"integer"},"sites":{"type":"array","items":{"$ref":"#/components/schemas/Site"}}}},"SiteResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"site":{"$ref":"#/components/schemas/Site"}}},"LookupResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"supported":{"type":"boolean","description":"true for a directory record or Shopify index match; this is not a live check."},"host":{"type":"string"},"matchedHost":{"type":"string"},"platform":{"type":"string","enum":["shopify"]},"verification":{"type":"string","const":"platform-index","description":"Index-only match from imported evidence, not a live store verification."},"platformUrl":{"type":"string","format":"uri-reference","description":"Shared platform tool metadata, e.g. /api/v1/platforms/shopify."},"toolCount":{"type":"integer","description":"Shared baseline tool count for an index-only Shopify match."},"message":{"type":"string"},"site":{"$ref":"#/components/schemas/Site"}}},"ShopifyPlatform":{"type":"object","properties":{"ok":{"type":"boolean"},"platform":{"type":"string","const":"shopify"},"name":{"type":"string"},"storeCount":{"type":"integer","description":"Indexed hosts, falling back to the imported count when the host index is unavailable."},"updatedAt":{"type":"string","format":"date-time","description":"Store index import date."},"source":{"type":["object","null"],"description":"Provenance recorded by the index import; separate from reference-tool freshness."},"indexSha256":{"type":["string","null"],"description":"SHA-256 of the compressed index artifact."},"indexReady":{"type":"boolean","description":"This instance has loaded and validated its store index."},"toolsUpdatedAt":{"type":["string","null"],"format":"date-time","description":"Reference tools last checked; null when unknown."},"sentinelHost":{"type":"string"},"apiSurface":{"type":"string"},"note":{"type":"string"},"tools":{"type":"array","items":{"$ref":"#/components/schemas/Tool"}}}},"ShopifyStoreSearch":{"type":"object","properties":{"ok":{"type":"boolean"},"platform":{"type":"string","const":"shopify"},"query":{"type":"string"},"results":{"type":"array","maxItems":20,"items":{"type":"string"}},"matchCount":{"type":"integer","maximum":5000},"matchCountTruncated":{"type":"boolean"},"limit":{"type":"integer","const":20}}},"ReviewSessionCreated":{"type":"object","required":["sessionId","credential","expiresAt"],"properties":{"sessionId":{"type":"string","format":"uuid"},"credential":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}}},"CodeReviewRequest":{"type":"object","additionalProperties":false,"required":["projectId","sharingApproved","protocolVersion","tools","files"],"properties":{"projectId":{"type":"string","pattern":"^[A-Za-z0-9._-]{1,80}$","minLength":1,"maxLength":80,"description":"Stable local project identifier. Keep it unchanged throughout the loop; never change it to evade limits."},"sharingApproved":{"type":"boolean","const":true,"description":"The user has approved this exact selection for sharing with the review service."},"protocolVersion":{"type":"integer","const":2},"tools":{"type":"array","minItems":1,"maxItems":100,"description":"Selected tools must have unique names. Review different page-specific definitions separately rather than renaming real tools.","items":{"$ref":"#/components/schemas/CodeReviewTool"}},"files":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/CodeReviewFile"}},"expectedTools":{"type":"array","minItems":1,"maxItems":100,"description":"Optional independent manifest of the approved selection. Build before assembling tools; declare fields to detect omitted definition fields. A match proves only agreement with this declaration, never full repository coverage.","items":{"$ref":"#/components/schemas/ExpectedCodeReviewTool"}},"expectedFiles":{"type":"array","minItems":1,"maxItems":20,"description":"Optional independent manifest built from complete selected files before assembling the payload. Hashes and byte counts must match the received UTF-8 content; ordering is irrelevant.","items":{"$ref":"#/components/schemas/ExpectedCodeReviewFile"}},"previousReviewId":{"type":"string","minLength":1,"maxLength":128,"description":"Omit only for the first review; then use the latest review in this session/project."},"withheldFiles":{"type":"array","minItems":1,"maxItems":20,"description":"Relevant files deliberately not sent, such as a file holding a secret, a file over the size limits, or a third-party package. The review treats their content as unknown, covers the rest and names the gap. Never include a secret value in a note.","items":{"type":"object","additionalProperties":false,"required":["path","reason"],"properties":{"path":{"type":"string","minLength":1,"maxLength":240,"description":"Relative POSIX path; must not also be in files."},"reason":{"type":"string","enum":["secret","too-large","third-party","other"]},"note":{"type":"string","minLength":1,"maxLength":300,"description":"Optional one-line context, such as a package name and version."}}}}}},"CodeReviewTool":{"type":"object","additionalProperties":false,"required":["name"],"description":"Complete selected tool definition. Handlers and registration source go in files, not in a tool field. Preserve schemas exactly as implemented; object-form schemas are limited to 64 levels. Optional output schemas may be boolean JSON Schemas. Explicit null is accepted by transport but yields a factual failure; omit an absent optional schema instead.","properties":{"name":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[^\\x00-\\x20\\x7f]+$"},"title":{"type":["string","null"],"maxLength":512},"description":{"type":["string","null"],"maxLength":8192},"inputSchema":{"type":["object","null"]},"outputSchema":{"type":["object","boolean","null"]},"annotations":{"type":["object","null"]},"availability":{"type":["object","null"]},"page":{"type":"string","minLength":1,"maxLength":2048},"kind":{"type":["string","null"],"maxLength":128},"impl":{"type":["string","null"],"maxLength":128},"apiSurface":{"type":["string","null"],"maxLength":128}}},"CodeReviewFile":{"type":"object","additionalProperties":false,"required":["path","content"],"properties":{"path":{"type":"string","minLength":1,"maxLength":240,"description":"Unique relative POSIX file path inside the approved project selection. No absolute paths, backslashes, traversal, .env, .git, node_modules, secret files or private keys."},"content":{"type":"string","minLength":1,"description":"Selected implementation source, at most 51,200 UTF-8 bytes. Exclude credentials, secrets, personal data and unrelated source. Total source is at most 102,400 UTF-8 bytes."}}},"ExpectedCodeReviewTool":{"type":"object","additionalProperties":false,"required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[^\\x00-\\x20\\x7f]+$"},"page":{"type":"string","minLength":1,"maxLength":2048,"description":"Selected registration page; omitted page means / for identity matching."},"fields":{"type":"array","minItems":1,"uniqueItems":true,"contains":{"const":"name"},"description":"Exact intended definition keys, including name. Compare with name plus receivedTools.fields, which excludes name for source receipts.","items":{"type":"string","enum":["name","title","description","inputSchema","outputSchema","annotations","availability","page","kind","impl","apiSurface"]}}}},"ExpectedCodeReviewFile":{"type":"object","additionalProperties":false,"required":["path","sha256","bytes"],"properties":{"path":{"type":"string","minLength":1,"maxLength":240,"description":"Unique relative POSIX path inside the explicitly approved selection; same restrictions as files.path."},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Lowercase SHA-256 of the complete selected file encoded as UTF-8."},"bytes":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Complete selected file size in UTF-8 bytes, not characters."}}},"ReviewLoop":{"type":"object","required":["attempt","maxAttempts","bestGrade","canContinue","stopReason"],"description":"At most three accepted reviews, including factual-only results; polling and recovery do not add attempts. best-grade requires stage done, current submitted factualChecks status checks-passed with zero failures and no unknown supplied schema validation, plus advisory complete and a complete A+ assessment. Intentional design/execution unknowns do not block it. Factual repairs can continue through attempt three. A review the service could not finish uses no attempt: the budget was reached or unavailable, a restart interrupted it, or the model returned no usable review (at most two retries per step). Its report says nextAction wait with retryAfterSeconds; then send the same step again with that review as previousReviewId and a new Idempotency-Key. Other incomplete or unavailable advice and legacy unverified results stop as review-failed.","properties":{"attempt":{"type":"integer","minimum":0,"maximum":3},"maxAttempts":{"type":"integer","const":3},"bestGrade":{"type":"string","const":"A+"},"canContinue":{"type":"boolean"},"stopReason":{"type":["string","null"],"enum":[null,"best-grade","attempt-limit","review-failed"]}}},"CodeReviewAccepted":{"type":"object","required":["reviewId","reviewType","stage","evidenceType","protocolVersion","reportUrl","submission","loop","nextAction"],"properties":{"reviewId":{"type":"string"},"reviewType":{"type":"string","const":"source"},"stage":{"type":"string"},"evidenceType":{"type":"string","const":"submitted"},"protocolVersion":{"type":"integer","const":2},"reportUrl":{"type":"string"},"submission":{"$ref":"#/components/schemas/CodeReviewSubmission"},"factualChecks":{"$ref":"#/components/schemas/SubmittedFactualChecks"},"advisory":{"$ref":"#/components/schemas/ReviewAdvisory"},"loop":{"$ref":"#/components/schemas/ReviewLoop"},"nextAction":{"type":"string","const":"get-report"},"pollAfterSeconds":{"type":"number"}}},"PrivateReviewReport":{"type":"object","required":["reviewId","stage","evidenceType","loop","nextAction"],"properties":{"reviewId":{"type":"string"},"reviewType":{"type":"string","const":"source"},"latestReviewId":{"type":"string"},"stage":{"type":"string"},"evidenceType":{"type":"string","const":"submitted"},"loop":{"$ref":"#/components/schemas/ReviewLoop"},"nextAction":{"type":"string"},"pollAfterSeconds":{"type":"number"},"retryAfterSeconds":{"type":"number"},"factualChecks":{"$ref":"#/components/schemas/SubmittedFactualChecks"},"advisory":{"$ref":"#/components/schemas/ReviewAdvisory"},"assessment":{"oneOf":[{"$ref":"#/components/schemas/SourceAssessment"},{"type":"null","description":"No model assessment, including a completed factual-only result."},{"type":"object","description":"Legacy definitions-only assessment for a review originally submitted to POST /api/reviews.","not":{"required":["reviewType"],"properties":{"reviewType":{"const":"source"}}}}]},"submission":{"type":"object","description":"Source reviews use CodeReviewSubmission; older definition reviews have their own receipt."},"comparison":{"type":"object"},"error":{"type":"string"},"code":{"type":"string"}}},"SubmittedFactualChecks":{"type":"object","required":["checkVersion","evidenceType","status","implementationGrade","execution","scope","checks","tools","counts","fields"],"description":"Server-generated checks of submitted definitions, separate from model advice. Schema definitions are validated locally using supported declared dialects (draft-07, 2019-09 or 2020-12); absent dialect assumes 2020-12. Unsupported dialects, unresolved external references and validation bounds remain unknown. No tool arguments, results, execution, deployment or design quality are verified.","properties":{"checkVersion":{"type":"string","const":"webmcp-captured-definitions-v3"},"evidenceType":{"type":"string","const":"submitted"},"status":{"type":"string","enum":["checks-passed","issues-found","insufficient-evidence"]},"implementationGrade":{"type":"null"},"execution":{"type":"string","const":"not-tested"},"summary":{"type":"string"},"scope":{"type":"object","required":["toolCount","schemaValidation","schemaDialectPolicy","designReview"],"properties":{"toolCount":{"type":"integer","minimum":0},"pages":{"type":"array","items":{"type":"string"}},"schemaValidation":{"type":"string","const":"json-schema-definitions"},"schemaDialectPolicy":{"type":"string"},"designReview":{"type":"string","const":"not-assessed"}}},"checks":{"type":"array","items":{"$ref":"#/components/schemas/FactualCheck"}},"tools":{"type":"array","items":{"type":"object","required":["index","name","checks"],"properties":{"index":{"type":"integer","minimum":0},"name":{"type":["string","null"]},"page":{"type":["string","null"]},"checks":{"type":"array","items":{"$ref":"#/components/schemas/FactualCheck"}}}}},"counts":{"type":"object","required":["pass","fail","notAssessed","notApplicable"],"properties":{"pass":{"type":"integer","minimum":0},"fail":{"type":"integer","minimum":0},"notAssessed":{"type":"integer","minimum":0},"notApplicable":{"type":"integer","minimum":0}}},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FactualRepairField"},"description":"Failed factual checks and unknown supplied schema validation, with paths into the submitted request. Read these for repairs; notAssessed also counts intentional execution/design unknowns and need not be zero."}}},"FactualCheck":{"type":"object","required":["id","status","message"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pass","fail","not-assessed","not-applicable"]},"message":{"type":"string"},"errors":{"type":"array","items":{"type":"object"}}}},"FactualRepairField":{"type":"object","required":["path","code","status","message"],"properties":{"path":{"type":"string","description":"JSON Pointer into the original request, for example /tools/0/inputSchema/properties/count/minimum; escaped property names use ~0 and ~1."},"code":{"type":"string"},"status":{"type":"string","enum":["fail","not-assessed"]},"message":{"type":"string"}}},"ReviewAdvisory":{"type":"object","required":["status"],"description":"Model advice state, independent of factual checks. not-requested with factual-checks-need-attention finishes with assessment absent or null and permits fix/resubmit within the same limit. incomplete or unavailable advice stops the autonomous loop; never infer a grade from factual checks.","properties":{"status":{"type":"string","enum":["pending","not-requested","complete","incomplete","unavailable"]},"reason":{"type":"string"}}},"SourceAssessment":{"type":"object","required":["reviewType","rubricVersion","projectId","targetUrl","status","scope","findings"],"properties":{"reviewType":{"type":"string","const":"source"},"rubricVersion":{"type":"string","enum":["webmcp-source-implementation-v1","webmcp-source-implementation-v2"]},"projectId":{"type":"string"},"targetUrl":{"type":"null"},"status":{"type":"string","enum":["complete","incomplete"]},"grade":{"type":["string","null"],"enum":[null,"A+","A","A-","B+","B","B-","C"],"description":"WebMCP.com technical implementation grade derived from grounded static findings. Complete assessments only; an incomplete review cannot issue a grade."},"summary":{"type":"string"},"scope":{"type":"object","required":["execution","files"],"properties":{"execution":{"type":"string","const":"not-tested"},"files":{"type":"array","items":{"$ref":"#/components/schemas/CodeReviewFileHash"}},"withheldFiles":{"type":"array","description":"Declared files that were not reviewed.","items":{"type":"object","required":["path","reason"],"properties":{"path":{"type":"string"},"reason":{"type":"string"}}}}}},"findings":{"type":"array","items":{"$ref":"#/components/schemas/SourceFinding"}}}},"SourceFinding":{"type":"object","required":["id","tool","check","severity","message","recommendation","evidence"],"properties":{"id":{"type":"string"},"tool":{"type":["string","null"]},"check":{"type":"string"},"severity":{"type":"string","enum":["issue","suggestion"]},"message":{"type":"string"},"recommendation":{"type":"string"},"evidence":{"type":"object","required":["path","startLine","endLine","quote"],"description":"Exact source span; 1-based inclusive line numbers, no more than 20 lines.","properties":{"path":{"type":"string"},"startLine":{"type":"integer","minimum":1},"endLine":{"type":"integer","minimum":1},"quote":{"type":"string","minLength":8,"maxLength":2000}}}}},"CodeReviewFileHash":{"type":"object","required":["path","sha256"],"properties":{"path":{"type":"string"},"sha256":{"type":"string","pattern":"^[a-f0-9]{64}$"}}},"CodeReviewSubmission":{"type":"object","required":["reviewType","receivedTools","receivedFiles","toolsCompleteness","filesCompleteness","repositoryCompleteness","sharingApproved"],"properties":{"reviewType":{"type":"string","const":"source"},"sharingApproved":{"type":"boolean","const":true},"repositoryCompleteness":{"type":"string","const":"not-verified"},"toolsCompleteness":{"type":"string","enum":["matches-declared-tools","not-declared"]},"filesCompleteness":{"type":"string","enum":["matches-declared-files","not-declared"]},"receivedTools":{"type":"array","items":{"type":"object","required":["name","fields"],"properties":{"name":{"type":"string"},"page":{"type":"string","description":"Present when supplied; otherwise use / for declaration identity matching."},"fields":{"type":"array","items":{"type":"string"},"description":"Supplied definition keys, excluding name. Compare name plus these keys with expectedTools.fields."}}}},"receivedFiles":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/CodeReviewFileHash"},{"type":"object","required":["bytes"],"properties":{"bytes":{"type":"integer","minimum":1,"maximum":51200}}}]}},"withheldFiles":{"type":"array","description":"Present when files were declared withheld.","items":{"type":"object","required":["path","reason"],"properties":{"path":{"type":"string"},"reason":{"type":"string"}}}}}},"ReviewProblem":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"nextAction":{"type":"string"},"reviewId":{"type":"string"},"loop":{"$ref":"#/components/schemas/ReviewLoop"},"retryAfterSeconds":{"type":"number"},"fields":{"type":"array","items":{"type":"object"}},"limits":{"type":"object"}}}},"securitySchemes":{"ReviewSession":{"type":"http","scheme":"bearer","description":"Private credential from POST /api/review-sessions. Never put it in a URL, copied public prompt or repository."}}}}