{
  "ruleset": "2026-11-03.1",
  "count": 58,
  "rules": [
    {
      "id": "MNF-001",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "Manifest must be a JSON object",
      "rationale": "Every other check needs named fields; an array or scalar has none."
    },
    {
      "id": "MNF-002",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "Server name is required",
      "rationale": "The directory shows the server name and reviewers use it to identify the connector."
    },
    {
      "id": "MNF-003",
      "tool": "validate_manifest",
      "severity": "warning",
      "title": "Server name over 100 characters",
      "rationale": "The listing name field is capped at 100 characters; a longer server name will not fit it."
    },
    {
      "id": "MNF-004",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "Local transport cannot be listed",
      "rationale": "The directory requires a remote server; stdio or local-only transports are refused."
    },
    {
      "id": "MNF-005",
      "tool": "validate_manifest",
      "severity": "warning",
      "title": "SSE transport is legacy",
      "rationale": "Streamable HTTP is the current remote transport; SSE still works but is the older spec revision."
    },
    {
      "id": "MNF-006",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "Remote URL is required",
      "rationale": "The portal's first step asks for the connector URL; there is nothing to scan without it."
    },
    {
      "id": "MNF-007",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "URL must use HTTPS",
      "rationale": "Connector URLs must be remote HTTPS; plain HTTP and WebSocket URLs are refused."
    },
    {
      "id": "MNF-008",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "URL points at a loopback or private host",
      "rationale": "A localhost or RFC 1918 address is not reachable by the reviewer's machine or the portal scan."
    },
    {
      "id": "MNF-009",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "Static bearer token authentication blocks listing",
      "rationale": "Directory auth is OAuth 2.0 for account-scoped tools or none for public data; a static token is refused."
    },
    {
      "id": "MNF-010",
      "tool": "validate_manifest",
      "severity": "warning",
      "title": "Authentication mode is missing or unrecognised",
      "rationale": "The portal asks how users sign in, so the manifest should state none or OAuth 2.0 explicitly."
    },
    {
      "id": "MNF-011",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "Manifest must expose at least one tool",
      "rationale": "A connector with no tools has no review surface and no reason to be listed."
    },
    {
      "id": "MNF-012",
      "tool": "validate_manifest",
      "severity": "warning",
      "title": "First-party service domain is not declared",
      "rationale": "The directory requires the server to call its own first-party API; declaring the service domain makes that checkable."
    },
    {
      "id": "MNF-013",
      "tool": "validate_manifest",
      "severity": "error",
      "title": "Server URL does not match the declared service domain",
      "rationale": "The server domain must match the service it fronts; a mismatch is how proxy servers get refused."
    },
    {
      "id": "MNF-014",
      "tool": "validate_manifest",
      "severity": "warning",
      "title": "Manifest and listing names differ",
      "rationale": "Reviewers match the connector to its listing; two different names for one product read as two products."
    },
    {
      "id": "ANN-001",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Tool entry is not an object",
      "rationale": "tools/list returns objects; anything else cannot carry a name or annotations."
    },
    {
      "id": "ANN-002",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Tool name is required",
      "rationale": "An unnamed tool cannot be called or reviewed."
    },
    {
      "id": "ANN-003",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Tool name over 64 characters",
      "rationale": "The directory caps tool names at 64 characters, snake_case or kebab-case."
    },
    {
      "id": "ANN-004",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Tool name is not snake_case or kebab-case",
      "rationale": "The directory accepts snake_case or kebab-case names; spaces and mixed case are flagged in review."
    },
    {
      "id": "ANN-005",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Tool title is required",
      "rationale": "The portal flags tools whose title is missing; the title is the human label shown to users."
    },
    {
      "id": "ANN-006",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Tool title is long",
      "rationale": "Titles are labels; a long sentence is hard to scan in the connector's tool list."
    },
    {
      "id": "ANN-007",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Tool description is required",
      "rationale": "The description is what the model reads to decide when to call the tool."
    },
    {
      "id": "ANN-008",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Tool description is very short",
      "rationale": "A description under 20 characters cannot state what the tool returns."
    },
    {
      "id": "ANN-009",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Tool has no side-effect annotation",
      "rationale": "Every tool must set readOnlyHint true, or destructiveHint, so clients know what it does."
    },
    {
      "id": "ANN-010",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "readOnlyHint and destructiveHint are both true",
      "rationale": "The two hints contradict each other; clients use them to decide whether to ask for confirmation."
    },
    {
      "id": "ANN-011",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Tool character is ambiguous",
      "rationale": "readOnlyHint false with destructiveHint false says the tool acts on data but hides what kind of action it takes."
    },
    {
      "id": "ANN-012",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "inputSchema is missing or not an object schema",
      "rationale": "MCP expects an object JSON Schema so clients can validate arguments and render them."
    },
    {
      "id": "ANN-013",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Description contains instructions to the model",
      "rationale": "Tool descriptions describe the tool; hidden instructions or prompt-injection phrasing are refused in review."
    },
    {
      "id": "ANN-014",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Description contains a URL, contact or promotion",
      "rationale": "Cross-promotion and contact details belong in listing fields, not tool descriptions."
    },
    {
      "id": "ANN-015",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Write-verb name is marked read-only",
      "rationale": "A create/update/delete style name with readOnlyHint true is a contradiction a reviewer will test."
    },
    {
      "id": "ANN-016",
      "tool": "check_tool_annotations",
      "severity": "warning",
      "title": "Read-verb name is marked destructive",
      "rationale": "A get/list style name with destructiveHint true is a contradiction a reviewer will test."
    },
    {
      "id": "ANN-017",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Duplicate tool name",
      "rationale": "tools/list must be unambiguous; duplicate names make a call target undefined."
    },
    {
      "id": "ANN-018",
      "tool": "check_tool_annotations",
      "severity": "error",
      "title": "Destructive tool in a read-only connector",
      "rationale": "The caller declared the connector read-only, so a destructive tool is a contract mismatch."
    },
    {
      "id": "LST-001",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Listing name is required",
      "rationale": "The portal cannot save a listing without a name."
    },
    {
      "id": "LST-002",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Listing name over 100 characters",
      "rationale": "The directory caps the listing name at 100 characters."
    },
    {
      "id": "LST-003",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Listing name has stray whitespace",
      "rationale": "Leading, trailing or doubled spaces are invisible in review and look careless in the directory."
    },
    {
      "id": "LST-004",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "One-liner is required",
      "rationale": "The portal asks for a one-liner; it is the line that appears under the name."
    },
    {
      "id": "LST-005",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "One-liner over 200 characters",
      "rationale": "The directory caps the one-liner at 200 characters."
    },
    {
      "id": "LST-006",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "One-liner is very short",
      "rationale": "Under 20 characters the one-liner cannot say what task the connector completes."
    },
    {
      "id": "LST-007",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Description is required",
      "rationale": "The description is where the listing explains what each tool does and what it does not do."
    },
    {
      "id": "LST-008",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Description over 2,000 characters",
      "rationale": "The directory caps the description at 2,000 characters."
    },
    {
      "id": "LST-009",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Description is thin",
      "rationale": "Under 200 characters rarely covers what each tool does, who it is for and what it does not do."
    },
    {
      "id": "LST-010",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Categories must number 1 to 5",
      "rationale": "The listing accepts one to five categories."
    },
    {
      "id": "LST-011",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Categories contain duplicates or empty values",
      "rationale": "A repeated or blank category is a form error reviewers have to sort out."
    },
    {
      "id": "LST-012",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Documentation URL is required",
      "rationale": "The portal requires a documentation URL, and it must resolve."
    },
    {
      "id": "LST-013",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Documentation URL is not HTTPS",
      "rationale": "Documentation links are fetched by reviewers; plain HTTP or a malformed URL fails the check."
    },
    {
      "id": "LST-014",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Privacy-policy URL is required",
      "rationale": "The portal requires a privacy-policy URL for every connector."
    },
    {
      "id": "LST-015",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Privacy-policy URL is not HTTPS",
      "rationale": "The privacy link is clicked in review; plain HTTP or a malformed URL fails the check."
    },
    {
      "id": "LST-016",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Support contact is required",
      "rationale": "The listing must name a support route a user can actually reach."
    },
    {
      "id": "LST-017",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Support contact looks like a placeholder",
      "rationale": "example.com, todo and tbd addresses do not reach anyone."
    },
    {
      "id": "LST-018",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Unverifiable superlative in the copy",
      "rationale": "Superlatives such as best or world-class are not checkable and draw reviewer questions."
    },
    {
      "id": "LST-019",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Contact details inside listing text",
      "rationale": "The directory asks for contact details in their own fields, not inside the name, one-liner or description."
    },
    {
      "id": "LST-020",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Instructions to the model inside listing text",
      "rationale": "Listing copy is read by people; instruction or prompt-injection phrasing is refused in review."
    },
    {
      "id": "LST-021",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Slug is missing or invalid",
      "rationale": "The slug is permanent once published; it should be lowercase, hyphenated and short."
    },
    {
      "id": "LST-022",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Icon is missing",
      "rationale": "The listing asks for an icon; a connector without one ships an unfinished card."
    },
    {
      "id": "LST-023",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Icon URL is not HTTPS",
      "rationale": "Icons should be served over HTTPS; a malformed or insecure icon URL fails the asset check."
    },
    {
      "id": "LST-024",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Authless listing has no reviewer note",
      "rationale": "For authless connectors the portal asks for a note such as \"no account needed\"; leaving it blank stalls review."
    },
    {
      "id": "LST-026",
      "tool": "lint_listing_text",
      "severity": "error",
      "title": "Listing payload is not a JSON object",
      "rationale": "The listing fields are read by name; an array or scalar has none."
    },
    {
      "id": "LST-025",
      "tool": "lint_listing_text",
      "severity": "warning",
      "title": "Reviewer note mentions credentials for an authless connector",
      "rationale": "If the connector needs no account, a note containing a password or token contradicts it."
    }
  ]
}
