{
  "openapi": "3.1.0",
  "info": {
    "title": "Anahana agent-facing API",
    "version": "1.3.0",
    "summary": "The MCP endpoint, its OAuth 2.1 tier, and the static discovery surfaces on www.anahana.com.",
    "description": "Anahana (anahana.com) publishes wellness and spiritual-growth content in 24 languages. This document describes every machine-facing endpoint the site exposes. Everything is read-only and everything it returns is already public on the website — there is no private data, no write action, and no credential required to read anything. The MCP server covers all 24 languages, backed by a public build-time index under /content-index/ that the site regenerates on every deploy. The single dynamic endpoint is `POST /mcp`, a Model Context Protocol server over Streamable HTTP; the rest are static discovery documents. Human-readable companion: https://www.anahana.com/en/ai-agents/resources-for-ai-agents/",
    "termsOfService": "https://www.anahana.com/en/terms/",
    "contact": {
      "name": "Anahana",
      "email": "team@anahana.com",
      "url": "https://www.anahana.com/en/contact/"
    },
    "license": {
      "name": "Quote and summarize with attribution: yes. Full republication: no. AI training: yes.",
      "url": "https://www.anahana.com/robots.txt"
    }
  },
  "externalDocs": {
    "description": "Resources for AI Agents — every agent-facing surface with example calls",
    "url": "https://www.anahana.com/en/ai-agents/resources-for-ai-agents/"
  },
  "servers": [
    {
      "url": "https://www.anahana.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "mcp",
      "description": "Model Context Protocol server (Streamable HTTP, JSON-RPC 2.0)."
    },
    {
      "name": "oauth",
      "description": "OAuth 2.1 registered tier. Optional — anonymous access to /mcp is fully supported."
    },
    {
      "name": "discovery",
      "description": "Static agent-discovery documents."
    }
  ],
  "paths": {
    "/mcp": {
      "post": {
        "tags": ["mcp"],
        "operationId": "mcpJsonRpc",
        "summary": "MCP JSON-RPC 2.0 endpoint (Streamable HTTP)",
        "description": "A single JSON-RPC 2.0 endpoint implementing MCP over Streamable HTTP. Two-step usage: (1) call `initialize` and read the `mcp-session-id` **response header**; (2) send that value back as an `mcp-session-id` request header on every later call. Responses are returned as a `text/event-stream` of `event: message` lines carrying JSON-RPC payloads. Negotiated protocol versions: 2025-11-25 (latest), 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07. Five tools. The three content tools (search_content, get_article, lookup_angel_number) take an optional `language`; omit it and you get English. It accepts one code, a comma-separated list, or \"all\". Supported codes: en, de, it, es, nl, fr, sv, pl, cs, fi, hu, ro, ru, da, no, pt, tr, el, id, uk, ja, ko, zh, th. Search is keyword/substring matching in every one of them, never semantic. The two product tools, list_products (optional `language`; omit it for every product on sale) and get_product (`sku`), return each product's page url, list price, currency and language, never a payment link; checkout happens on the product page. Anonymous calls are rate limited to 30 requests/minute per IP; calls bearing an OAuth 2.1 access token get 120 requests/minute per client.",
        "externalDocs": {
          "description": "Model Context Protocol specification",
          "url": "https://modelcontextprotocol.io/"
        },
        "security": [{}, { "mcpOAuth": ["read:content"] }],
        "parameters": [
          {
            "name": "mcp-session-id",
            "in": "header",
            "required": false,
            "description": "Session id returned in the `mcp-session-id` response header of `initialize`. Required on every request after `initialize`.",
            "schema": { "type": "string" }
          },
          {
            "name": "mcp-protocol-version",
            "in": "header",
            "required": false,
            "description": "Negotiated protocol version, echoed back on subsequent requests by spec-compliant clients.",
            "schema": { "type": "string", "examples": ["2025-11-25"] }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/JsonRpcRequest" },
              "examples": {
                "initialize": {
                  "summary": "Step 1 — initialize (read mcp-session-id from the response headers)",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {
                      "protocolVersion": "2025-11-25",
                      "capabilities": {},
                      "clientInfo": { "name": "your-agent", "version": "1.0.0" }
                    }
                  }
                },
                "toolsList": {
                  "summary": "List the five tools",
                  "value": { "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
                },
                "searchContent": {
                  "summary": "tools/call — search_content",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "tools/call",
                    "params": {
                      "name": "search_content",
                      "arguments": { "query": "box breathing" }
                    }
                  }
                },
                "getArticle": {
                  "summary": "tools/call — get_article",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 4,
                    "method": "tools/call",
                    "params": {
                      "name": "get_article",
                      "arguments": { "url_or_slug": "111-angel-number" }
                    }
                  }
                },
                "lookupAngelNumber": {
                  "summary": "tools/call — lookup_angel_number",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 5,
                    "method": "tools/call",
                    "params": {
                      "name": "lookup_angel_number",
                      "arguments": { "number": "1111" }
                    }
                  }
                },
                "searchContentOneLanguage": {
                  "summary": "tools/call — search_content in one language (omit `language` for English)",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 6,
                    "method": "tools/call",
                    "params": {
                      "name": "search_content",
                      "arguments": { "query": "呼吸", "language": "zh" }
                    }
                  }
                },
                "searchContentManyLanguages": {
                  "summary": "tools/call — search_content across a list, or across every indexed language with \"all\"",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 7,
                    "method": "tools/call",
                    "params": {
                      "name": "search_content",
                      "arguments": { "query": "box breathing", "language": "de,fr,ja" }
                    }
                  }
                },
                "getArticleLanguage": {
                  "summary": "tools/call — get_article, bare slug resolved in another language",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 8,
                    "method": "tools/call",
                    "params": {
                      "name": "get_article",
                      "arguments": { "url_or_slug": "111-angel-number", "language": "ru" }
                    }
                  }
                },
                "lookupAngelNumberLanguage": {
                  "summary": "tools/call — lookup_angel_number in another language (coverage differs per language)",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 9,
                    "method": "tools/call",
                    "params": {
                      "name": "lookup_angel_number",
                      "arguments": { "number": "1008", "language": "ja" }
                    }
                  }
                },
                "listProducts": {
                  "summary": "tools/call — list_products (every product on sale: page url, list price, currency, language; never a payment link)",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 10,
                    "method": "tools/call",
                    "params": {
                      "name": "list_products",
                      "arguments": {}
                    }
                  }
                },
                "getProduct": {
                  "summary": "tools/call — get_product by sku",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 11,
                    "method": "tools/call",
                    "params": {
                      "name": "get_product",
                      "arguments": { "sku": "complete-guide-en" }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response, delivered as a server-sent-events stream. The `initialize` response additionally carries the `mcp-session-id` header.",
            "headers": {
              "mcp-session-id": {
                "description": "Session identifier. Returned by `initialize`; send it back on every subsequent request.",
                "schema": { "type": "string" }
              }
            },
            "content": {
              "text/event-stream": {
                "schema": { "type": "string" },
                "examples": {
                  "initializeResult": {
                    "summary": "initialize result",
                    "value": "event: message\ndata: {\"result\":{\"protocolVersion\":\"2025-11-25\",\"capabilities\":{\"tools\":{\"listChanged\":true}},\"serverInfo\":{\"name\":\"Anahana Content MCP\",\"version\":\"1.1.0\"}},\"jsonrpc\":\"2.0\",\"id\":1}\n"
                  }
                }
              },
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JsonRpcResponse" }
              }
            }
          },
          "400": {
            "description": "Malformed JSON-RPC, or a call made without the `mcp-session-id` header.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JsonRpcResponse" }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Anonymous: 30/min per IP. Registered: 120/min per client_id.",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the current one-minute window resets.",
                "schema": { "type": "integer" }
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/RateLimitError" }
              }
            }
          }
        }
      },
      "options": {
        "tags": ["mcp"],
        "operationId": "mcpPreflight",
        "summary": "CORS preflight (does not consume rate-limit budget)",
        "responses": {
          "200": { "description": "CORS headers. `Access-Control-Allow-Origin: *`; `mcp-session-id` is exposed." }
        }
      }
    },
    "/mcp/register": {
      "post": {
        "tags": ["oauth"],
        "operationId": "registerClient",
        "summary": "Dynamic client registration (RFC 7591)",
        "description": "Open registration — no human approval, no pre-existing credential, no waiting. Registering raises the rate limit to 120/min and gives a stable, revocable client identity. It does not change what the three currently published tools return.",
        "security": [{}],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ClientMetadata" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered. Returns `client_id`, and `client_secret` for confidential clients.",
            "content": { "application/json": { "schema": { "type": "object" } } }
          },
          "400": { "description": "Invalid client metadata." }
        }
      }
    },
    "/mcp/authorize": {
      "get": {
        "tags": ["oauth"],
        "operationId": "authorize",
        "summary": "Authorization endpoint (OAuth 2.1 authorization code + PKCE)",
        "description": "There is no consent screen and no login — nothing user-owned is being delegated, so every valid request from a registered client is auto-approved and answered with a 302 to the client's `redirect_uri` carrying `code` and `state`. Headless agents can read the `Location` header instead of following it.",
        "security": [{}],
        "parameters": [
          { "name": "response_type", "in": "query", "required": true, "schema": { "type": "string", "const": "code" } },
          { "name": "client_id", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "redirect_uri", "in": "query", "required": true, "schema": { "type": "string", "format": "uri" } },
          { "name": "code_challenge", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "code_challenge_method", "in": "query", "required": true, "schema": { "type": "string", "const": "S256" } },
          { "name": "state", "in": "query", "required": false, "schema": { "type": "string" } },
          { "name": "scope", "in": "query", "required": false, "description": "Normalized to `read:content`, the only scope.", "schema": { "type": "string" } }
        ],
        "responses": {
          "302": {
            "description": "Redirect to `redirect_uri` with `code` and `state`.",
            "headers": { "Location": { "schema": { "type": "string", "format": "uri" } } }
          },
          "400": { "description": "Unknown client_id or invalid request." }
        }
      }
    },
    "/mcp/token": {
      "post": {
        "tags": ["oauth"],
        "operationId": "token",
        "summary": "Token endpoint (also handles RFC 7009 revocation)",
        "security": [{}],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "grant_type": { "type": "string", "enum": ["authorization_code", "refresh_token"] },
                  "code": { "type": "string" },
                  "redirect_uri": { "type": "string", "format": "uri" },
                  "code_verifier": { "type": "string" },
                  "refresh_token": { "type": "string" },
                  "client_id": { "type": "string" },
                  "client_secret": { "type": "string" }
                },
                "required": ["grant_type"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bearer `access_token` (expires in 1 hour) plus a `refresh_token`.",
            "content": { "application/json": { "schema": { "type": "object" } } }
          },
          "400": { "description": "Invalid grant." }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "tags": ["oauth", "discovery"],
        "operationId": "authorizationServerMetadata",
        "summary": "Authorization server metadata (RFC 8414)",
        "description": "Augmented with an `agent_auth` block describing the self-registration surface.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "tags": ["oauth", "discovery"],
        "operationId": "protectedResourceMetadata",
        "summary": "Protected resource metadata (RFC 9728)",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/.well-known/mcp/server-card.json": {
      "get": {
        "tags": ["discovery"],
        "operationId": "mcpServerCard",
        "summary": "MCP server card",
        "description": "Endpoint, protocol versions, capabilities, tools, auth, and rate limits for the MCP server.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/.well-known/agents.md": {
      "get": {
        "tags": ["discovery"],
        "operationId": "agentsMd",
        "summary": "Agent quickstart",
        "description": "One page written for agents: what the site offers, the MCP handshake, markdown negotiation, auth tiers, licence terms, and how to cite.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "text/markdown": { "schema": { "type": "string" } } } }
        }
      }
    },
    "/auth.md": {
      "get": {
        "tags": ["discovery"],
        "operationId": "authMd",
        "summary": "Access and registration policy for agents",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "text/markdown": { "schema": { "type": "string" } } } }
        }
      }
    },
    "/.well-known/agent-skills/index.json": {
      "get": {
        "tags": ["discovery"],
        "operationId": "agentSkillsIndex",
        "summary": "Agent skills index",
        "description": "Three skills with SHA-256 digests, per Cloudflare's Agent Skills Discovery RFC v0.2.0: two skill-md files teaching an agent how to find content and resolve angel numbers, and one archive (ai-agent-wellbeing v0.1.0) of operational protocols for the agent's own working state.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/.well-known/agent-skills/ai-agent-wellbeing.tar.gz": {
      "get": {
        "tags": ["discovery"],
        "operationId": "agentSkillWellbeingArchive",
        "summary": "ai-agent-wellbeing skill archive",
        "description": "Archive-type Agent Skill, v0.1.0. SKILL.md at the archive root plus one reference file per protocol under references/en/. Verify the body against the digest published in the agent-skills index before unpacking. Contains no scripts.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/gzip": { "schema": { "type": "string", "format": "binary" } } } }
        }
      }
    },
    "/.well-known/api-catalog": {
      "get": {
        "tags": ["discovery"],
        "operationId": "apiCatalog",
        "summary": "API catalog (RFC 9727 linkset)",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/linkset+json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": ["discovery"],
        "operationId": "openapiDocument",
        "summary": "This document",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": ["discovery"],
        "operationId": "llmsTxt",
        "summary": "One-page site map for language models",
        "description": "Sections, free calculators, language list, editorial standards. Generated at build time from the real site structure.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "text/plain": { "schema": { "type": "string" } } } }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "tags": ["discovery"],
        "operationId": "llmsFullTxt",
        "summary": "Curated full-text corpus",
        "description": "Full markdown text of the AI-agents section plus a curated set of flagship English articles. A sample, not the whole site — use /mcp or the per-article `.md` mirrors for everything else.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "text/plain": { "schema": { "type": "string" } } } }
        }
      }
    },
    "/content-index/manifest.json": {
      "get": {
        "tags": ["discovery"],
        "operationId": "contentIndexManifest",
        "summary": "Manifest for the machine-readable content index",
        "description": "Every indexed language with its live document count, the path to its index file, and that file's size in bytes; plus the total document count, the schema version, and the generation timestamp. Regenerated by the site build on every deploy, so it cannot drift from the site. This is the same index the MCP server at /mcp reads at runtime — published rather than hidden, so you can use it directly.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/content-index/{lang}.json": {
      "get": {
        "tags": ["discovery"],
        "operationId": "contentIndexLanguage",
        "summary": "Content index for one language",
        "description": "One record per published page in that language: title, path (site-relative and already language-prefixed), section, slug, description, date, lastmod. Article BODIES are not included — concatenate base_url with a record's path and fetch that with `Accept: text/markdown`, or append `index.md`. Roughly 0.5-1.1 MB per language. Sufficient for keyword/substring retrieval; nothing here supports semantic search.",
        "security": [{}],
        "parameters": [
          {
            "name": "lang",
            "in": "path",
            "required": true,
            "description": "Language code from the manifest.",
            "schema": {
              "type": "string",
              "enum": ["en", "de", "it", "es", "nl", "fr", "sv", "pl", "cs", "fi", "hu", "ro", "ru", "da", "no", "pt", "tr", "el", "id", "uk", "ja", "ko", "zh", "th"]
            }
          }
        ],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/content-index/angel-numbers.json": {
      "get": {
        "tags": ["discovery"],
        "operationId": "contentIndexAngelNumbers",
        "summary": "Angel-number coverage map",
        "description": "Every angel number with a published article anywhere on the site, its slug, and the exact list of languages that publish it. Slugs are English-stable across all 24 languages, and two patterns cover the whole set: `<number>-angel-number` for the majority, `angel-number-<number>` for the single digits 1-9, the master numbers 11/22/33/44/55/66/77/88/99, and the outlier 828. Coverage is NOT uniform — en, de, es and ru publish 568 distinct numbers; the other 20 languages publish 519, all missing the same 49. Derived from the content tree at build time.",
        "security": [{}],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }
        }
      }
    },
    "/{path}": {
      "get": {
        "tags": ["discovery"],
        "operationId": "getPageAsMarkdown",
        "summary": "Any article page, as HTML or as markdown",
        "description": "Content negotiation is live site-wide: send `Accept: text/markdown` to any page URL and the origin returns clean markdown (200, `Content-Type: text/markdown`) instead of HTML. The same markdown is also published at `<page-url>index.md` and declared in each page's `<head>` as a `text/markdown` alternate. Every page lives under a language prefix — `/en/`, `/de/`, `/fr/`, and 21 more. There is no unprefixed content.",
        "security": [{}],
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "Language-prefixed page path, e.g. `en/breathing-exercise/box-breathing/`.",
            "schema": { "type": "string" },
            "example": "en/breathing-exercise/box-breathing/"
          },
          {
            "name": "Accept",
            "in": "header",
            "required": false,
            "description": "`text/markdown` returns markdown; anything else returns HTML.",
            "schema": { "type": "string", "examples": ["text/markdown"] }
          }
        ],
        "responses": {
          "200": {
            "description": "The page.",
            "content": {
              "text/html": { "schema": { "type": "string" } },
              "text/markdown": { "schema": { "type": "string" } }
            }
          },
          "404": { "description": "No such page. Check https://www.anahana.com/sitemap.xml — it is the authoritative list of live URLs." }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "mcpOAuth": {
        "type": "oauth2",
        "description": "Optional. Anonymous access to /mcp is fully supported: the five tools published today — search_content, get_article, lookup_angel_number, list_products, get_product — are available on both tiers and return identical data on each. A token raises the rate limit from 30/min per IP to 120/min per client and gives a stable, revocable client identity. Registration is open (RFC 7591) at /mcp/register.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://www.anahana.com/mcp/authorize",
            "tokenUrl": "https://www.anahana.com/mcp/token",
            "refreshUrl": "https://www.anahana.com/mcp/token",
            "scopes": {
              "read:content": "Read the public articles and calculators — the same data anonymous callers receive from the same tools."
            }
          }
        }
      }
    },
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "type": ["string", "integer"] },
          "method": {
            "type": "string",
            "description": "MCP method. Supported: `initialize`, `notifications/initialized`, `tools/list`, `tools/call`, `ping`.",
            "examples": ["initialize", "tools/list", "tools/call"]
          },
          "params": { "type": "object" }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": ["jsonrpc"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "type": ["string", "integer", "null"] },
          "result": { "type": "object" },
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "integer" },
              "message": { "type": "string" },
              "data": {}
            }
          }
        }
      },
      "RateLimitError": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "const": "rate_limited" },
          "message": { "type": "string" },
          "retry_after_seconds": { "type": "integer" }
        }
      },
      "ClientMetadata": {
        "type": "object",
        "description": "RFC 7591 client metadata.",
        "properties": {
          "client_name": { "type": "string" },
          "redirect_uris": { "type": "array", "items": { "type": "string", "format": "uri" } },
          "grant_types": {
            "type": "array",
            "items": { "type": "string", "enum": ["authorization_code", "refresh_token"] }
          },
          "response_types": { "type": "array", "items": { "type": "string", "const": "code" } },
          "token_endpoint_auth_method": {
            "type": "string",
            "enum": ["client_secret_post", "client_secret_basic", "none"],
            "description": "`none` for public clients using PKCE only."
          },
          "scope": { "type": "string", "const": "read:content" }
        },
        "required": ["client_name", "redirect_uris"]
      }
    }
  }
}
