{
  "openapi": "3.1.0",
  "info": {
    "title": "MasterDB hosted MCP endpoints",
    "version": "1.0.0",
    "summary": "The Model Context Protocol endpoints MasterDB hosts — where hosting cannot cost anything.",
    "description": "MasterDB hosts three MCP servers over the Streamable HTTP transport. Each is one endpoint that takes MCP's JSON-RPC messages; the tools, resources\nand prompts are discovered through MCP itself (`tools/list`), not described here.\nWhat this document fixes is the HTTP surface: the endpoint, how it authenticates,\nand how it refuses.\n\n- **Public** (`mcp.masterdb.ai`): the public verification reads only — verify a\n  record you hold, a certificate, the key set, a projection, the log, the\n  vocabularies. No credential and nothing billable; no search and no fetch.\n- **Business** (`mcp.business.masterdb.ai`): a business's own MCP client, signed in\n  as a person of the business. Drafts are validated and prepared; **publishing is\n  never done here** — the `publish` tool returns a link to the Business Portal's\n  Save screen, where the person reviews the change and seals it with their passkey.\n\n**MasterDB never hosts a billable MCP server**: production retrieval is only ever a\nrequest signed by the AI company's own key, from its own `masterdb-mcp`.\n\nSessions: the `initialize` answer carries an `Mcp-Session-Id`; send it on every later\nrequest of the conversation. A session is bound to the credential that opened it and\nends after 30 minutes idle. There is no server-initiated stream: `GET` answers 405,\nand `DELETE` with the session id ends a session. Responses are JSON (`enableJsonResponse`).\n",
    "contact": {
      "name": "MasterDB developer documentation",
      "url": "https://docs.masterdb.ai"
    },
    "license": {
      "name": "Proprietary",
      "identifier": "LicenseRef-MasterDB"
    }
  },
  "servers": [
    {
      "url": "https://mcp.masterdb.ai",
      "description": "Production — the public surface; the business surface at mcp.business.masterdb.ai."
    },
    {
      "url": "https://mcp.sandbox.masterdb.ai",
      "description": "Sandbox — the sandbox surface (and the public surface over the sandbox's roots)."
    }
  ],
  "tags": [
    {
      "name": "MCP",
      "description": "The hosted Model Context Protocol endpoints."
    }
  ],
  "paths": {
    "/v1/mcp/public": {
      "post": {
        "operationId": "mcpPublic",
        "tags": [
          "MCP"
        ],
        "summary": "The public MCP server",
        "security": [],
        "description": "MCP over Streamable HTTP for the public verification reads: tools `verify`,\n`certificate`, `keys`, `projection`, `spec`, `log_checkpoint`, `log_proof` and\n`vocabularies`. No credential. Rate-limited per client address generously, so as\nnever to block a checker. The client must accept both\n`application/json` and `text/event-stream` (406 otherwise).\n",
        "requestBody": {
          "$ref": "#/components/requestBodies/JsonRpc"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/JsonRpcResult"
          },
          "202": {
            "description": "A notification or response from the client, accepted."
          },
          "400": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "404": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "406": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "413": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "415": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/v1/mcp/business": {
      "post": {
        "operationId": "mcpBusiness",
        "tags": [
          "MCP"
        ],
        "summary": "The business-side MCP server",
        "security": [
          {
            "identityPlatformBearer": [
              "email"
            ]
          }
        ],
        "description": "MCP over Streamable HTTP for a business's own MCP client, signed in as a person of\nthe business (the portal's own sign-in session). Tools:\n`draft` (create or edit a draft and validate it with every reason), `publish`\n(validate, show the change, and return the link to the Business Portal's Save\nscreen, where the person seals with their passkey — the seal is never made here),\n`status` (drafts, publication state, where a record is live) and\n`receipts_for_my_records` (publish receipts and who is asking). Every call runs\nunder the person's own grants in the portal API; this server can never seal,\nspend, change people, place or lift a block, or change keys. Rate-limited per\nperson.\n",
        "requestBody": {
          "$ref": "#/components/requestBodies/JsonRpc"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/JsonRpcResult"
          },
          "202": {
            "description": "A notification or response from the client, accepted."
          },
          "400": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "404": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "406": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "413": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "415": {
            "$ref": "#/components/responses/JsonRpcError"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "identityPlatformBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "An ID token for a person signed in to the Business Portal or the\nAI Portal. The token carries `mdb_pid` and `mdb_amr`; grants are never in the\ntoken and are resolved on every call, and every mutating call is gated by\n`(actor, party, action)` and refused with a reason code. The value in a security\nrequirement is the `mdb_amr` factor the operation requires: `email` (any session),\n`passkey` (publish, spend, block, manage people or hold keys; `auth_time` within 12\nhours, `step_up_required` otherwise) or `totp` (accepted in place of `passkey` for a\nperson who never seals).\n"
      }
    },
    "requestBodies": {
      "JsonRpc": {
        "required": true,
        "description": "One JSON-RPC 2.0 message of the Model Context Protocol, or a batch of them.",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/JsonRpcMessage"
                },
                {
                  "type": "array",
                  "minItems": 1,
                  "maxItems": 100,
                  "items": {
                    "$ref": "#/components/schemas/JsonRpcMessage"
                  }
                }
              ]
            },
            "example": {
              "jsonrpc": "2.0",
              "id": 1,
              "method": "initialize",
              "params": {
                "protocolVersion": "2025-06-18",
                "capabilities": {},
                "clientInfo": {
                  "name": "example-client",
                  "version": "1.0.0"
                }
              }
            }
          }
        }
      }
    },
    "responses": {
      "JsonRpcResult": {
        "description": "The JSON-RPC answer to the request (or requests), with `Mcp-Session-Id` on an `initialize`.",
        "headers": {
          "Mcp-Session-Id": {
            "description": "The session this conversation continues on.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/JsonRpcMessage"
                },
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/JsonRpcMessage"
                  }
                }
              ]
            },
            "example": {
              "jsonrpc": "2.0",
              "id": 1,
              "result": {
                "protocolVersion": "2025-06-18",
                "capabilities": {
                  "tools": {}
                },
                "serverInfo": {
                  "name": "masterdb-mcp-public",
                  "version": "0.1.0"
                }
              }
            }
          }
        }
      },
      "JsonRpcError": {
        "description": "The transport refused the message (not JSON-RPC, a wrong media type, a session that does not exist, too large).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/JsonRpcMessage"
            },
            "example": {
              "jsonrpc": "2.0",
              "error": {
                "code": -32000,
                "message": "Not Acceptable: Client must accept both application/json and text/event-stream"
              },
              "id": null
            }
          }
        }
      },
      "Problem": {
        "description": "A refusal or failure, as RFC 9457 problem details with a stable `code`.",
        "headers": {
          "x-request-id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://docs.masterdb.ai/errors/sort_required",
              "title": "A sort is required",
              "status": 400,
              "code": "sort_required",
              "detail": "sort_by is required; for relevance order write \"_text_match:desc\"",
              "errors": [
                {
                  "code": "sort_required",
                  "pointer": "/sort_by",
                  "detail": "sort_by is required; for relevance order write \"_text_match:desc\""
                }
              ]
            }
          }
        }
      }
    },
    "schemas": {
      "JsonRpcMessage": {
        "type": "object",
        "description": "A JSON-RPC 2.0 request, notification, result or error (the MCP messages themselves are MCP's).",
        "required": [
          "jsonrpc"
        ],
        "additionalProperties": true,
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "method": {
            "type": "string"
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          },
          "result": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": true,
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "ErrorCode": {
        "type": "string",
        "description": "The stable, machine-readable reason. A code, once published, is never renamed or reused; callers branch on it. `x-masterdb-status` gives the HTTP status each code is returned with when it is a refusal.",
        "enum": [
          "sort_required",
          "filter_required",
          "country_required",
          "unknown_field",
          "operator_not_allowed",
          "sort_not_allowed",
          "value_invalid",
          "limit_exceeded",
          "collection_unknown",
          "request_invalid",
          "signature_missing",
          "signature_invalid",
          "signature_expired",
          "key_unknown",
          "nonce_reused",
          "digest_mismatch",
          "no_grant",
          "permission_denied",
          "grant_expired",
          "grant_deactivated",
          "party_not_verified",
          "party_suspended",
          "party_held",
          "party_not_funded",
          "admin_hold",
          "passkey_required",
          "device_bound_required",
          "invitation_invalid",
          "invitation_email_mismatch",
          "domain_claimed",
          "assembly_pending",
          "agreement_required",
          "challenge_invalid",
          "registration_invalid",
          "assertion_invalid",
          "passkey_test_failed",
          "credential_unknown",
          "credential_exists",
          "mint_assertion_missing",
          "session_invalid",
          "record_invalid",
          "json_invalid",
          "record_too_large",
          "duplicate_key",
          "depth_exceeded",
          "string_too_long",
          "too_many_keys",
          "control_character",
          "zero_width_character",
          "bidi_override",
          "not_nfc",
          "invalid_utf8",
          "bom_present",
          "number_invalid",
          "money_not_string",
          "money_invalid",
          "schema_missing",
          "schema_invalid",
          "not_canonical",
          "seal_invalid",
          "seal_key_unknown",
          "seal_payload_type",
          "seal_hash_mismatch",
          "seal_time_skew",
          "seq_not_increasing",
          "price_country_not_published",
          "unsafe_url",
          "field_required",
          "plain_text_required",
          "role_address_required",
          "personal_data",
          "country_invalid",
          "currency_invalid",
          "language_invalid",
          "vocabulary_invalid",
          "seal_required",
          "mandate_required",
          "mandate_scope",
          "draft_revision_conflict",
          "scope_violation",
          "url_flagged",
          "display_domain_mismatch",
          "image_type_refused",
          "image_invalid",
          "source_not_allowed",
          "domain_unproven",
          "verification_level_insufficient",
          "screening_not_passed",
          "verification_locked",
          "feature_not_enabled",
          "prf_unsupported",
          "budget_exhausted",
          "window_expired",
          "token_invalid",
          "token_reused",
          "allowance_exhausted",
          "idempotency_key_missing",
          "idempotency_key_invalid",
          "idempotency_key_reused",
          "idempotency_in_progress",
          "unauthenticated",
          "step_up_required",
          "forbidden",
          "not_found",
          "conflict",
          "rate_limited",
          "internal",
          "not_implemented",
          "unavailable"
        ],
        "x-enumDescriptions": {
          "sort_required": "A sort is required",
          "filter_required": "A filter naming exactly one country is required",
          "country_required": "A country is required",
          "unknown_field": "Field not allowed for this collection",
          "operator_not_allowed": "Operator not allowed for this field",
          "sort_not_allowed": "Sort key not allowed for this collection",
          "value_invalid": "Value is not valid for this field",
          "limit_exceeded": "Limit exceeds the maximum",
          "collection_unknown": "Unknown collection",
          "request_invalid": "Request is not valid",
          "signature_missing": "Request is not signed",
          "signature_invalid": "Request signature is not valid",
          "signature_expired": "Request signature is outside its validity window",
          "key_unknown": "Signing key is not registered or not live",
          "nonce_reused": "Nonce has already been used",
          "digest_mismatch": "Content-Digest does not match the body",
          "no_grant": "You hold no grant on this party",
          "permission_denied": "Your roles on this party do not include this action",
          "grant_expired": "Your access to this party has expired",
          "grant_deactivated": "Your access to this party is deactivated",
          "party_not_verified": "The party must be verified for this action",
          "party_suspended": "The party is suspended",
          "party_held": "The party is on hold: its records stay live, new publishing is paused",
          "party_not_funded": "The party has no funds for this action",
          "admin_hold": "A new admin cannot change grants, keys or mandates for 24 hours",
          "passkey_required": "This action requires a passkey",
          "device_bound_required": "This party requires a device-bound passkey for sealing",
          "invitation_invalid": "Invitation is not valid",
          "invitation_email_mismatch": "Sign in with the email address the invitation was sent to",
          "domain_claimed": "This email domain belongs to an existing party",
          "assembly_pending": "This AI company's setup is not complete",
          "agreement_required": "The AI-company Terms in force must be accepted first",
          "challenge_invalid": "Challenge is unknown, expired or already used",
          "registration_invalid": "Passkey registration is not valid",
          "assertion_invalid": "Passkey assertion is not valid",
          "passkey_test_failed": "The new passkey failed its test signature and was not saved",
          "credential_unknown": "Passkey is not registered or has been revoked",
          "credential_exists": "Passkey is already registered",
          "mint_assertion_missing": "Sign-in token has no recent mint assertion",
          "session_invalid": "The session is not bound to a live MasterDB sign-in; sign in again",
          "record_invalid": "Record is not valid",
          "json_invalid": "Body is not one strict JSON value",
          "record_too_large": "Record is too large",
          "duplicate_key": "Object has a duplicate key",
          "depth_exceeded": "Nesting is too deep",
          "string_too_long": "String is too long",
          "too_many_keys": "Object has too many keys",
          "control_character": "String contains a control character",
          "zero_width_character": "String contains a zero-width space or U+FEFF",
          "bidi_override": "String contains a bidirectional override",
          "not_nfc": "String is not in Unicode Normalization Form C",
          "invalid_utf8": "Body is not valid UTF-8",
          "bom_present": "Body starts with a byte-order mark",
          "number_invalid": "Number is outside the range every parser agrees on",
          "money_not_string": "Money must be a decimal string",
          "money_invalid": "Money string is not a valid decimal",
          "schema_missing": "Record has no top-level schema field",
          "schema_invalid": "Record schema field is not valid",
          "not_canonical": "Bytes are not in the canonical form",
          "seal_invalid": "Seal does not verify",
          "seal_key_unknown": "Seal names a key that is not registered",
          "seal_payload_type": "Envelope carries the wrong payload type",
          "seal_hash_mismatch": "Seal hash does not match the record bytes",
          "seal_time_skew": "sealed_at is too far from the time of receipt",
          "seq_not_increasing": "Batch sequence number is not greater than the last accepted",
          "price_country_not_published": "A price names a country the record is not published in",
          "unsafe_url": "URL points at a private or unsafe address",
          "field_required": "A required field is missing",
          "plain_text_required": "Text must be plain text",
          "role_address_required": "A published contact must be a role address, not a person",
          "personal_data": "Freeform text must not carry personal data",
          "country_invalid": "Not a country code in the platform vocabulary",
          "currency_invalid": "Not a currency code in the platform vocabulary",
          "language_invalid": "Not a language in the platform vocabulary",
          "vocabulary_invalid": "Value is not in the controlled vocabulary",
          "seal_required": "A seal is required",
          "mandate_required": "The signing key has no live publishing mandate",
          "mandate_scope": "The mandate does not cover this record type or country",
          "draft_revision_conflict": "The draft has changed since you read it",
          "scope_violation": "Text names another company or brand, or directs how other sources are treated",
          "url_flagged": "URL is flagged as unsafe",
          "display_domain_mismatch": "display_domain is not the destination host",
          "image_type_refused": "Only JPEG, PNG and WebP images are accepted",
          "image_invalid": "Image could not be decoded within the limits",
          "source_not_allowed": "The request comes from outside the mandate’s source allow-list",
          "domain_unproven": "An endpoint domain has no live proof of control",
          "verification_level_insufficient": "This action is not available to this party until its verification is complete: finish it, then try again",
          "screening_not_passed": "A check this action needs has not passed: try again later, and if it still has not passed, get in touch with us",
          "verification_locked": "Locked while verification is submitted or decided",
          "feature_not_enabled": "This feature is not enabled",
          "prf_unsupported": "The passkey does not support the PRF extension",
          "budget_exhausted": "Budget exhausted",
          "window_expired": "Confirmation window has expired",
          "token_invalid": "Token is not valid",
          "token_reused": "Token has already been confirmed",
          "allowance_exhausted": "Query allowance exhausted",
          "idempotency_key_missing": "Idempotency-Key header is required",
          "idempotency_key_invalid": "Idempotency-Key header is not valid",
          "idempotency_key_reused": "Idempotency-Key was used with a different request",
          "idempotency_in_progress": "A request with this Idempotency-Key is in progress",
          "unauthenticated": "Not signed in",
          "step_up_required": "A stronger sign-in is required for this action",
          "forbidden": "Not permitted",
          "not_found": "Not found",
          "conflict": "Conflict",
          "rate_limited": "Too many requests",
          "internal": "Internal error",
          "not_implemented": "Not implemented",
          "unavailable": "Temporarily unavailable"
        }
      },
      "FieldError": {
        "type": "object",
        "description": "One reason among several: a request or record that fails several checks is answered with every reason at once.",
        "additionalProperties": false,
        "required": [
          "code",
          "pointer",
          "detail"
        ],
        "properties": {
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "pointer": {
            "type": "string",
            "description": "JSON Pointer (RFC 6901) into the request or record; \"\" is the whole document."
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details, the one error shape on every MasterDB API. `type` is the documentation page of `code`. Extension members may appear; they never shadow the standard ones.",
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "`https://docs.masterdb.ai/errors/{code}`"
          },
          "title": {
            "type": "string",
            "description": "The code's human title; may be reworded, never branch on it."
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string",
            "description": "The request path, or the request id."
          },
          "errors": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/FieldError"
            }
          }
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "The request's id in MasterDB's logs (the `retrieval_id` on the retrieval API). Quote it to support.",
        "schema": {
          "type": "string"
        }
      }
    }
  }
}