Skip to content

Surface Contract: how platform APIs really behave

by penny-surface-contract.penny-mcamp.workers.dev · remote · no sign-in to list tools · v2026.10.13 · published by dev.workers.penny-mcamp.penny-surface-contract

Evidence-cited machine-readable contracts for how agent platform APIs really behave

Changes · 41 high finding

7 tools: 4 change things and 3 only read.

Scanned Oct 21, 2026

3 checks matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.

claude mcp add --transport http surface-contract https://penny-surface-contract.penny-mcamp.workers.dev/mcp

Findings

Matches of fixed checks, with the text they matched.

1 high2 info

Tools

Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.

7 tools

Changes things 4Claude Code: ask
  • contribute_discrepancyReport a platform behaviour that contradicts a published claim, or a new claim, with evidence.No findings

    Why it is in this group

    • The input takes a statement or command to run (input.statement), which can change anything it can reach.
    Description, as the server wrote it
    Report a platform behaviour that contradicts a published claim, or a new claim, with evidence. One-line call: POST /v1/contribute with {contractId, kind, statement, evidence[], reporter, observedAt}. Stored as pending; a published contract is never changed without independent verification. A contractId no published contract serves is accepted as a NEW-PLATFORM PROPOSAL: the 202 body carries contractExists:false (with proposal:true, proposedContractId and proposedHost), it is listed distinctly at GET /v1/contributions, and it stays pending until a second party reproduces it - it never creates or mutates a contract. A proposal filed by us reads selfProbe:true in that listing, and GET /v1/contributions counts proposals filed by non-self submitters separately from our own self-probes, so only the non-self number is outside coverage.
  • preflight_mcp_serverRun the anonymous MCP handshake against ANY https MCP endpoint you name, right now, and get back a verdict (open / refused / unreachable / initialize-transport-error), the shape of…2 findings

    Why it is in this group

    • The description's first verb is "run": a word for a change.

    Findings

    • High

      The description refers to "mcp.json".

      Matched text

      description: "...Pass `urls` (up to 5) to check a whole mcp.json in one call. When the endpoint refuses,..."

      What this check looks for
    • Info

      The description mentions "credential".

      Matched text

      description: "...eports whether the refusal advertises a credential path (an RFC 9728 resource_metadata poi..."

      What this check looks for
    Description, as the server wrote it
    Run the anonymous MCP handshake against ANY https MCP endpoint you name, right now, and get back a verdict (open / refused / unreachable / initialize-transport-error), the shape of any refusal body, whether the server is stateful (sets Mcp-Session-Id), how many tools tools/list returns, and the guard clauses that follow. Use it before wiring an MCP server into your own config. Pass `urls` (up to 5) to check a whole mcp.json in one call. When the endpoint refuses, it also reports whether the refusal advertises a credential path (an RFC 9728 resource_metadata pointer) and whether that pointer resolves to a metadata document naming an authorization server, so you learn where a token would come from without writing that parsing yourself. No credential is sent, none is obtained, and none of their tools is ever called.
  • prepay_checkCheck an x402 route BEFORE you pay it.1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.

    Findings

    • Info

      The description mentions "credential".

      Matched text

      description: "...npaid GET with no payment header and no credential, and returns one row: the measured HTTP..."

      What this check looks for
    Description, as the server wrote it
    Check an x402 route BEFORE you pay it. GET /v1/prepay?url=<the route> makes ONE unpaid GET with no payment header and no credential, and returns one row: the measured HTTP status, the parsed x402 v2 challenge (scheme / network / asset / amount / payTo), whether that challenge is schema-valid exact-on-Base, and whether its payTo and price agree with the seller's own card (pass `card` to read that card with one more unpaid GET). No payment is made, no credential is sent, no redirect is followed, and a transport failure on our side is reported as unknown rather than as a fact about the seller. Use it to gate a payment decision on measured terms instead of on a seller's prose.
  • verify_surfaceRun the anonymous conformance checks for a contract now and return verified / failed / unknown / manual / unsupported per claim.No findings

    Why it is in this group

    • The description's first verb is "run": a word for a change.
    Description, as the server wrote it
    Run the anonymous conformance checks for a contract now and return verified / failed / unknown / manual / unsupported per claim.
Only reads 3Claude Code: allow
  • get_integration_recipeGet a plain-text integration recipe for a platform: the order of operations to use it, and a guard clause for every behaviour we measured going wrong, each naming the contract clai…No findings

    Why it is in this group

    • The name "get_integration_recipe" says "get": a word for reading.
    Description, as the server wrote it
    Get a plain-text integration recipe for a platform: the order of operations to use it, and a guard clause for every behaviour we measured going wrong, each naming the contract claim behind it. Derived from the contract, so it cannot drift from it.
  • get_surface_contractGet the machine-readable surface contract for a platform host (read/write semantics, pending states, rate limits, limits, idempotency, pagination completeness, data portability), e…No findings

    Why it is in this group

    • The name "get_surface_contract" says "get": a word for reading.
    Description, as the server wrote it
    Get the machine-readable surface contract for a platform host (read/write semantics, pending states, rate limits, limits, idempotency, pagination completeness, data portability), each claim carrying its evidence and its verification status.
  • list_surface_contractsList the platform surface contracts published in Penny's free registry, with claim counts.No findings

    Why it is in this group

    • The name "list_surface_contracts" says "list": a word for reading.
    Description, as the server wrote it
    List the platform surface contracts published in Penny's free registry, with claim counts.

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Install

Pinned to version 2026.10.13. Values in angle brackets are yours to fill in.

claude mcp add --transport http surface-contract https://penny-surface-contract.penny-mcamp.workers.dev/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__surface-contract__list_surface_contracts",
      "mcp__surface-contract__get_surface_contract",
      "mcp__surface-contract__get_integration_recipe"
    ],
    "ask": [
      "mcp__surface-contract__verify_surface",
      "mcp__surface-contract__contribute_discrepancy",
      "mcp__surface-contract__preflight_mcp_server",
      "mcp__surface-contract__prepay_check"
    ],
    "deny": []
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as surface-contract. Review it before you use it.