{
  "openapi": "3.1.0",
  "info": {
    "title": "Traveler.md MCP server",
    "summary": "Memory that travels with you. Scoped OAuth 2.1 access to a traveler's Traveler.md and Trip.md.",
    "description": "Traveler.md is travel memory the traveler owns, kept in two markdown files. `Traveler.md` is who they are: their travel style and pace, the budget they actually spend, the seat and the room they want, their loyalty programs, the food and the neighborhoods they gravitate to, their dealbreakers, and the trips they are still dreaming about. `Trip.md` is what they are planning, one file per trip. A trip file opens vague, such as \"Mediterranean in July or August\", tightens into dates, a shortlist, and a neighborhood, and ends as the record of what they booked and what they would do again.\n\nThe traveler authorizes an agent once. From then on that agent reads the memory before it answers, and writes approved updates back as it learns. Its first answer already fits this traveler, and the next trip starts from what the last one taught it. The files outlive any single conversation and any single assistant, so the same memory works in whatever the traveler uses next. That portability is why they keep it current, and the files stay theirs to export or revoke at any time.\n\nThere is no inventory behind the memory: no flights, no prices, no availability, and no shortlists. The agent still does the recommending. The memory decides whose taste it is built on.\n\n## Why call this\n\n- Helpful from the first message. You already know the traveler's pace, taste, budget, and constraints, so the first answer is a real recommendation instead of a round of questions.\n- Aware of the active trip. Reading Trip.md picks planning up where it left off rather than restarting it.\n- Better than a search bar can hold. How fast does this traveler like to move? Boutique hotel or full-service resort? Walkability, design, food, or luxury? What did they love or avoid last time? A destination and a date range do not carry any of that.\n- Smarter every conversation. What you learn can flow back to the profile with the traveler's permission, so the next session starts further along.\n- Portable, so it is worth the traveler filling in. The same memory works in your product and in every other assistant they use, which is why they maintain it.\n\n## What this is not\n\n- Searching, pricing, or booking travel. There is no inventory, no prices, and no availability behind this server.\n- Returning shortlists of flights, hotels, or restaurants. Your agent does the recommending. The memory supplies whose taste it is built on.\n- General-purpose storage. It holds travel preferences and trips, and nothing else belongs in it.\n- Deleting a trip. Nothing on this surface deletes. Archiving is the strongest write, and the traveler can reverse it.\n\n## Transport and authorization\n\nThe server implements the Model Context Protocol over Streamable HTTP.\nAuthorization is OAuth 2.1 with PKCE (`S256`) and dynamic client registration\n(RFC 7591), so no pre-registration is needed. A client should discover the\nauthorization server from the protected resource metadata at\nhttps://mcp.traveler.md/.well-known/oauth-protected-resource rather than hard-coding the endpoints\nbelow.\n\nEach tool needs exactly one scope. Request the minimum set:\n\n| Tool | Required scope |\n| --- | --- |\n| `read_profile` | `profile.read` |\n| `create_profile` | `profile.create` |\n| `update_profile` | `profile.update` |\n| `list_trips` | `trip.list` |\n| `read_trip` | `trip.read` |\n| `create_trip` | `trip.create` |\n| `update_trip` | `trip.update` |\n| `archive_trip` | `trip.update` |\n\nA read-only assistant needs only `profile.read`, `trip.read`, `trip.list`.\nThere is no all-scopes grant, and the traveler sees every scope on the consent\nscreen before approving it.\n\n## Product stage\n\nTraveler.md is a public beta.\n- Traveler app: Live and free for individual travelers. Account creation is self-serve. Sign up at https://app.traveler.md.\n- MCP server (this document): Live. Requires OAuth 2.1 authorization.\n- Standalone REST API: Pre-launch. Access is granted to early partners on request.\n- Sandbox: No sandbox or test environment yet. Authorize a real account to develop against.",
    "version": "2026-08-24",
    "contact": {
      "name": "Traveler.md support",
      "email": "support@traveler.md",
      "url": "https://docs.traveler.md/mcp/support"
    },
    "termsOfService": "https://traveler.md/terms",
    "license": {
      "name": "Traveler.md terms of service",
      "url": "https://traveler.md/terms"
    }
  },
  "externalDocs": {
    "description": "Traveler.md developer documentation",
    "url": "https://docs.traveler.md/mcp"
  },
  "servers": [
    {
      "url": "https://mcp.traveler.md",
      "description": "Traveler.md MCP server"
    }
  ],
  "security": [
    {
      "travelerOAuth": [
        "profile.read",
        "profile.create",
        "profile.update",
        "trip.read",
        "trip.create",
        "trip.update",
        "trip.list"
      ]
    }
  ],
  "paths": {
    "/mcp": {
      "post": {
        "operationId": "callMcp",
        "summary": "Send a Model Context Protocol JSON-RPC request",
        "description": "Single JSON-RPC 2.0 endpoint for the Streamable HTTP transport. `initialize`,\n`tools/list`, and `tools/call` all arrive here. The scope a request needs is\nthe scope of the tool it names, so the security requirement lists every scope;\nthe server rejects a `tools/call` whose tool is outside the granted set.\n\n| Tool | Required scope |\n| --- | --- |\n| `read_profile` | `profile.read` |\n| `create_profile` | `profile.create` |\n| `update_profile` | `profile.update` |\n| `list_trips` | `trip.list` |\n| `read_trip` | `trip.read` |\n| `create_trip` | `trip.create` |\n| `update_trip` | `trip.update` |\n| `archive_trip` | `trip.update` |",
        "security": [
          {
            "travelerOAuth": [
              "profile.read",
              "profile.create",
              "profile.update",
              "trip.read",
              "trip.create",
              "trip.update",
              "trip.list"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response, or an SSE stream when the client accepts one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InsufficientScope"
          }
        }
      },
      "get": {
        "operationId": "openMcpStream",
        "summary": "Open the server-to-client event stream",
        "description": "Server-Sent Events channel of the Streamable HTTP transport, used for server-initiated messages. Same authorization as the POST endpoint.",
        "security": [
          {
            "travelerOAuth": [
              "profile.read",
              "trip.read",
              "trip.list"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "An open SSE stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "operationId": "getProtectedResourceMetadata",
        "summary": "RFC 9728 protected-resource metadata",
        "description": "Authoritative discovery document: names the authorization server and the scopes this resource supports. Unauthenticated by design.",
        "security": [],
        "responses": {
          "200": {
            "description": "Protected-resource metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resource": {
                      "type": "string",
                      "format": "uri"
                    },
                    "authorization_servers": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uri"
                      }
                    },
                    "bearer_methods_supported": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "scopes_supported": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "resource",
                    "authorization_servers",
                    "scopes_supported"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "travelerOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.1 with PKCE (`S256`) against the Traveler.md authorization server.\nClients may self-register through RFC 7591 dynamic client registration at\nhttps://connect.traveler.md/v1/oauth/register; no pre-registration is required.\nAccess tokens are JWTs valid for 15 minutes. Refresh tokens are opaque, valid\nfor 30 days of inactivity, and rotated on every exchange.\n\nRevocation and scope narrowing take effect on the next call, not when the\naccess token expires. Do not build a client that assumes a grace window.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://connect.traveler.md/v1/oauth/authorize",
            "tokenUrl": "https://connect.traveler.md/v1/oauth/token",
            "refreshUrl": "https://connect.traveler.md/v1/oauth/token",
            "scopes": {
              "profile.read": "Read your Traveler.md",
              "profile.create": "Create your Traveler.md",
              "profile.update": "Update your Traveler.md",
              "trip.read": "Read your trips",
              "trip.create": "Create new trips",
              "trip.update": "Update and archive your trips",
              "trip.list": "List your trips"
            }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, expired, or revoked token. The `WWW-Authenticate` header points at the protected-resource metadata, per RFC 9728.",
        "headers": {
          "WWW-Authenticate": {
            "description": "Bearer challenge including `resource_metadata`.",
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "InsufficientScope": {
        "description": "The token is valid but the granted scopes do not cover the tool that was called. Re-run the authorization flow requesting the missing scope."
      }
    },
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "description": "A JSON-RPC 2.0 request as defined by the Model Context Protocol.",
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer"
            ]
          },
          "method": {
            "type": "string",
            "examples": [
              "initialize",
              "tools/list",
              "tools/call"
            ]
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "required": [
          "jsonrpc",
          "method"
        ]
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer"
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "jsonrpc"
        ]
      }
    }
  }
}
