{
  "openapi": "3.1.0",
  "info": {
    "title": "tokyo.how Public HTTP API",
    "version": "1.0.0",
    "description": "Public access to practical Tokyo guides through MCP, editorial article requests, and application liveness. No registration, API key, or OAuth token is required. MCP tool input schemas are discovered with tools/list; the language values currently supported by those tools are en and ja. Article submissions have side effects and should be sent only when the user asks. HTTP API version 1: additive fields are backward compatible. Breaking changes use a new major URL path, with at least 90 days of overlap after announcement. See /developers#api-policy for the deprecation and sunset policy. Public APIs share a best effort limit of 120 requests per minute per client IP per Worker isolate; counters are not global. Optional anonymous client enrollment and OAuth device consent protect the separate /api/agent/guides resource; /mcp and /api/a2a remain public. Client credentials last 24 hours and read-only access tokens last at most one hour. Secrets are not returned again after issuance.",
    "contact": {
      "name": "tokyo.how",
      "url": "https://tokyo.how/contact"
    },
    "termsOfService": "https://tokyo.how/ja/terms"
  },
  "servers": [
    {
      "url": "https://tokyo.how"
    }
  ],
  "security": [],
  "externalDocs": {
    "description": "Quickstart, tool documentation, and access model",
    "url": "https://tokyo.how/developers#api-policy"
  },
  "paths": {
    "/mcp": {
      "post": {
        "operationId": "sendMcpMessage",
        "summary": "Send an MCP JSON-RPC message",
        "description": "Use an MCP client with the Streamable HTTP transport. Send Content-Type: application/json and Accept: application/json, text/event-stream. Initialize the connection and use tools/list to discover the current tools and their input schemas. Send the negotiated MCP-Protocol-Version header on subsequent requests. The endpoint is stateless and does not issue an MCP session ID. Read-only tools are search_guides, get_guide, list_guides, list_categories, get_related_guides, and get_latest_guides. The request_article tool submits an editorial request and can notify the operator; call it only when the user has asked to submit one. GET is unsupported and returns a JSON-RPC error with HTTP 405 and Allow: POST, OPTIONS.",
        "parameters": [
          {
            "name": "MCP-Protocol-Version",
            "in": "header",
            "required": false,
            "description": "The protocol version negotiated during MCP initialization; send on subsequent requests.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/McpMessage"
              },
              "examples": {
                "listTools": {
                  "summary": "Discover current tools without an editorial submission",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/list",
                    "params": {}
                  }
                },
                "searchGuides": {
                  "summary": "Search guides in English",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "search_guides",
                      "arguments": {
                        "query": "suica card",
                        "lang": "en",
                        "limit": 5
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC result or error. Tool results can report isError even when the HTTP response is 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "202": {
            "description": "Notification accepted; no response body.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "description": "Invalid JSON or invalid MCP message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "406": {
            "description": "Accept must support application/json and text/event-stream.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "415": {
            "description": "Content-Type must be application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "500": {
            "description": "Application runtime or data unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "429": {
            "description": "Client request limit exceeded. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/article-request": {
      "post": {
        "operationId": "submitArticleRequest",
        "summary": "Submit an editorial article request or report",
        "description": "This creates an editorial submission and can send a notification to the site operator. Call only when the user asks to submit a request or report. Supply an email address only if the user provided it for a reply. A 200 response acknowledges receipt; it does not guarantee email delivery, publication, or a response.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArticleRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The request was accepted. Editorial notification delivery is not confirmed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "400": {
            "description": "Invalid JSON, or a missing type or blank description.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "500": {
            "description": "Unexpected application error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            }
          },
          "429": {
            "description": "Client request limit exceeded. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "checkApplicationLiveness",
        "summary": "Check application liveness",
        "description": "Reports that the application can answer this request. This does not exercise MCP tool execution, search data, editorial email delivery, or external dependencies and is not a complete readiness check. HEAD is also supported and returns the same status and headers without a body.",
        "responses": {
          "200": {
            "description": "Application is responding.",
            "headers": {
              "Cache-Control": {
                "description": "The liveness response is not cached.",
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "429": {
            "description": "Client request limit exceeded. Wait for Retry-After seconds before retrying.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/register": {
      "post": {
        "operationId": "registerAnonymousGuideAgent",
        "summary": "Provision a one-day anonymous agent client",
        "description": "Provision a one-day anonymous agent client Use this operation only when the user asks to connect a registered guide agent. No personal user account or verified identity is created.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_name": {
                    "type": "string"
                  },
                  "identity_type": {
                    "type": "string",
                    "enum": [
                      "anonymous"
                    ]
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "guides:read"
                    ]
                  }
                },
                "additionalProperties": false,
                "required": []
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_name": {
                    "type": "string"
                  },
                  "identity_type": {
                    "type": "string",
                    "enum": [
                      "anonymous"
                    ]
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "guides:read"
                    ]
                  }
                },
                "additionalProperties": false,
                "required": []
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful response. Secrets must be stored outside prompts and logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_id": {
                      "type": "string"
                    },
                    "client_secret": {
                      "type": "string"
                    },
                    "client_name": {
                      "type": "string"
                    },
                    "client_id_issued_at": {
                      "type": "integer"
                    },
                    "client_secret_expires_at": {
                      "type": "integer"
                    },
                    "scope": {
                      "type": "string"
                    },
                    "grant_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "token_endpoint_auth_method": {
                      "type": "string"
                    },
                    "response_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "401": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "429": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "503": {
            "$ref": "#/components/responses/AgentOAuthError"
          }
        }
      }
    },
    "/api/oauth/device": {
      "post": {
        "operationId": "startGuideAgentDeviceConsent",
        "summary": "Start a ten-minute public-guide device consent ceremony",
        "description": "Start a ten-minute public-guide device consent ceremony Use this operation only when the user asks to connect a registered guide agent. No personal user account or verified identity is created.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  },
                  "scope": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "client_id",
                  "client_secret"
                ]
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  },
                  "scope": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "client_id",
                  "client_secret"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response. Secrets must be stored outside prompts and logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "device_code": {
                      "type": "string"
                    },
                    "user_code": {
                      "type": "string"
                    },
                    "verification_uri": {
                      "type": "string"
                    },
                    "verification_uri_complete": {
                      "type": "string"
                    },
                    "expires_in": {
                      "type": "integer"
                    },
                    "interval": {
                      "type": "integer"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "401": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "429": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "503": {
            "$ref": "#/components/responses/AgentOAuthError"
          }
        }
      }
    },
    "/api/oauth/token": {
      "post": {
        "operationId": "exchangeGuideAgentDeviceCode",
        "summary": "Exchange an approved device code exactly once for a read-only access token",
        "description": "Exchange an approved device code exactly once for a read-only access token Use this operation only when the user asks to connect a registered guide agent. No personal user account or verified identity is created.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  },
                  "device_code": {
                    "type": "string"
                  },
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "urn:ietf:params:oauth:grant-type:device_code"
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "client_id",
                  "client_secret",
                  "grant_type",
                  "device_code"
                ]
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  },
                  "device_code": {
                    "type": "string"
                  },
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "urn:ietf:params:oauth:grant-type:device_code"
                    ]
                  }
                },
                "additionalProperties": false,
                "required": [
                  "client_id",
                  "client_secret",
                  "grant_type",
                  "device_code"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response. Secrets must be stored outside prompts and logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string"
                    },
                    "expires_in": {
                      "type": "integer"
                    },
                    "scope": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "401": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "429": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "503": {
            "$ref": "#/components/responses/AgentOAuthError"
          }
        }
      }
    },
    "/api/oauth/revoke": {
      "post": {
        "operationId": "revokeGuideAgentToken",
        "summary": "Idempotently revoke a token owned by this client",
        "description": "Idempotently revoke a token owned by this client Use this operation only when the user asks to connect a registered guide agent. No personal user account or verified identity is created.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  },
                  "token": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "client_id",
                  "client_secret",
                  "token"
                ]
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  },
                  "token": {
                    "type": "string"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "client_id",
                  "client_secret",
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response. Secrets must be stored outside prompts and logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {},
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "401": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "429": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "503": {
            "$ref": "#/components/responses/AgentOAuthError"
          }
        }
      }
    },
    "/api/agent/guides": {
      "get": {
        "operationId": "readRegisteredAgentGuides",
        "summary": "Search or retrieve public guides using a registered agent token",
        "description": "Use exactly one of q or slug. Returns public content and the anonymous client ID. This token grants no personal-data or editorial-submission access.",
        "security": [
          {
            "guideBearer": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "slug",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ja"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response. Secrets must be stored outside prompts and logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent_id": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "slug": {
                      "type": "string"
                    },
                    "body": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "401": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "404": {
            "$ref": "#/components/responses/AgentOAuthError"
          },
          "503": {
            "$ref": "#/components/responses/AgentOAuthError"
          }
        }
      }
    },
    "/api/a2a": {
      "post": {
        "operationId": "sendGuideAgentA2AMessage",
        "summary": "Send a stateless read-only A2A 1.0 JSON-RPC message",
        "description": "A2A 1.0 JSON-RPC using A2A-Version: 1.0. Public anonymous synchronous guide search/retrieval through SendMessage. No streaming, background tasks, history, URL fetching, or editorial tools. Protocol errors use JSON-RPC envelopes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "id": {
                    "type": [
                      "string",
                      "integer"
                    ]
                  },
                  "method": {
                    "type": "string"
                  },
                  "params": {
                    "type": "object"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "jsonrpc",
                  "id",
                  "method",
                  "params"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response. Secrets must be stored outside prompts and logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jsonrpc": {
                      "type": "string"
                    },
                    "id": {
                      "type": [
                        "string",
                        "integer",
                        "null"
                      ]
                    },
                    "result": {
                      "type": "object"
                    },
                    "error": {
                      "type": "object"
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, expired credential, or unsupported scope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid request, expired credential, or unsupported scope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Invalid request, expired credential, or unsupported scope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Invalid request, expired credential, or unsupported scope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "McpMessage": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Omit only for a JSON-RPC notification."
          },
          "method": {
            "type": "string",
            "description": "An MCP method, such as initialize, notifications/initialized, tools/list, or tools/call."
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "McpResponse": {
        "type": "object",
        "required": [
          "jsonrpc",
          "id"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "data": {}
            }
          }
        },
        "oneOf": [
          {
            "required": [
              "result"
            ],
            "not": {
              "required": [
                "error"
              ]
            }
          },
          {
            "required": [
              "error"
            ],
            "not": {
              "required": [
                "result"
              ]
            }
          }
        ]
      },
      "ArticleRequest": {
        "type": "object",
        "required": [
          "type",
          "description"
        ],
        "properties": {
          "type": {
            "type": "string",
            "minLength": 1,
            "description": "A nonempty string, such as new_article, missing_info, or other. MCP accepts only those three values.",
            "examples": [
              "new_article",
              "missing_info",
              "other"
            ]
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "pattern": "\\S",
            "description": "Describe the requested article or missing information. Whitespace-only descriptions are rejected. The MCP request_article tool requires at least 10 characters."
          },
          "url": {
            "type": "string",
            "description": "Existing article URL, if relevant."
          },
          "email": {
            "type": "string",
            "description": "Optional reply email supplied by the user for this submission. It is not an authentication credential."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code",
          "detail"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "description": "Stable application error code."
          },
          "detail": {
            "type": "string",
            "description": "How to correct the request or retry."
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "status",
          "service",
          "discovery",
          "mcp"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          },
          "service": {
            "type": "string",
            "const": "tokyo.how"
          },
          "discovery": {
            "type": "string",
            "const": "public"
          },
          "mcp": {
            "type": "string",
            "const": "/mcp"
          }
        }
      },
      "Problem": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "code",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "error": {
            "type": "string",
            "description": "Human readable error for existing clients."
          }
        }
      }
    },
    "headers": {
      "RateLimit-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Best effort per-isolate quota: 120 requests per 60 seconds."
      },
      "RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests remaining in this isolate window."
      },
      "RateLimit-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Seconds until this isolate window resets."
      },
      "RateLimit-Policy": {
        "schema": {
          "type": "string"
        },
        "description": "Quota policy, 120;w=60."
      }
    },
    "securitySchemes": {
      "guideBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Optional registered guide API token, acquired using the OAuth device flow at the issuer advertised by /.well-known/oauth-authorization-server."
      }
    },
    "responses": {
      "AgentOAuthError": {
        "description": "Invalid request, expired credential, or unsupported scope.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "error"
              ],
              "properties": {
                "error": {
                  "type": "string"
                },
                "error_description": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  }
}
