Model Context Protocol
Outpost ships an MCP server so AI clients (Claude Code, ChatGPT MCP,
custom agents) can list, create, update, and delete pages on a user's
behalf. Two transports are supported: SSE at
GET /mcp and Streamable HTTP at
POST /mcp.
Authorization: Bearer header. Either an API token
(op_…) or a JWT works, but API tokens are strongly
recommended for non-interactive clients.
Quickest setup (Claude Code)
Add this to your ~/.claude.json under
mcpServers:
"outpost": {
"type": "http",
"url": "https://outpost.click/mcp",
"headers": {
"Authorization": "Bearer op_YOUR_TOKEN"
}
}
See Claude Code integration for the full walkthrough.
POST /mcp (Streamable HTTP)
A single round-trip JSON-RPC endpoint — best for clients that don't want to maintain an SSE connection. Send a JSON-RPC request body, receive a JSON-RPC response body.
Initialize
curl -X POST https://outpost.click/mcp \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": { "tools": {} },
"serverInfo": { "name": "outpost", "version": "1.0.0" }
}
}
List tools
curl -X POST https://outpost.click/mcp \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
Call a tool
curl -X POST https://outpost.click/mcp \
-H "Authorization: Bearer op_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"outpost_list","arguments":{}}}'
Tool results are wrapped in MCP's standard content array,
with the JSON encoded as text:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"pages\": [...],\n \"count\": 3\n}"
}
]
}
}
Errors
401 Missing or invalid Authorization header401 Invalid token400 Invalid JSON- JSON-RPC
-32601 Method not foundfor unknown methods.
GET /mcp + POST /mcp/message (SSE)
The Streaming HTTP transport defined by MCP spec 2024-11-05. The client
opens a long-lived SSE connection on GET /mcp; the server
immediately emits an endpoint event whose data is the URL the
client should POST messages to. The server publishes responses on the SSE
channel.
1. Open the SSE channel
curl -N https://outpost.click/mcp \
-H "Authorization: Bearer op_YOUR_TOKEN"
# event: endpoint
# data: https://outpost.click/mcp/message?session_id=<uuid>
#
# event: ping
# data:
# ...
2. POST messages to the endpoint URL
curl -X POST "https://outpost.click/mcp/message?session_id=<uuid>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'
# 200 OK
# {"status":"accepted"}
# Response arrives on the open SSE stream:
# event: message
# data: {"jsonrpc":"2.0","id":1,"result":{...}}
Session lifecycle
- Each
GET /mcpcreates a new session keyed by a UUID. - The session holds an
asyncio.Queuefor outbound messages and the authenticated user record. - If no message is queued for 30 seconds, the server emits an
event: pingkeepalive. - When the SSE connection closes, the session is removed from the in-memory map (
_mcp_sessions).
Errors
401 Missing or invalid Authorization headeronGET /mcp.401 Invalid token404 Session not foundonPOST /mcp/messageif the SSE connection has dropped.400 Invalid JSONon a malformed POST body.
Tool reference
Eight tools are exposed. outpost_create and
outpost_update_from_url create/replace page content directly
(URL source only). outpost_upload_url and
outpost_update_files return curl recipes for
when you need to upload a local file (MCP can't ship binary uploads
through a tool call).
| Tool | What it does | Touches files? |
|---|---|---|
outpost_list | List your pages. | No |
outpost_get | Get one page (incl. passcodes & allowed emails). | No |
outpost_create | Create a new page from a source_url, with passcodes/allowed_emails. | Yes (URL fetch) |
outpost_update | Update name, visibility, passcodes, allowed_emails. | No |
outpost_update_from_url | Replace files on an existing page from a source_url. | Yes (URL fetch) |
outpost_delete | Delete a page and its files. | Yes |
outpost_upload_url | Return curl recipes for creating from a local file. Includes a follow-up PUT recipe if you ask for passcodes/allowed_emails. | No (recipe only) |
outpost_update_files | Return curl recipes for replacing files on an existing page from a local file. | No (recipe only) |
outpost_list
List all pages owned by the authenticated user, newest first.
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "outpost_list", "arguments": {} } }
Returns:
{
"pages": [
{
"id": "Ab12Cd34",
"name": "My Site",
"visibility": "public",
"default_file": "index.html",
"url": "https://outpost.click/p/Ab12Cd34",
"has_passcode": false,
"allowed_emails": [],
"created_at": "2026-05-03T17:42:11",
"updated_at": "2026-05-03T17:42:11"
}
],
"count": 1
}
outpost_get
Get a single page including its passcodes (decrypted) and allowed emails.
{
"name": "outpost_get",
"arguments": { "page_id": "Ab12Cd34" }
}
outpost_create
Create a new page directly from a URL. Outpost fetches the page and
its same-domain assets, rewrites references to local copies, and stores
the result. Supports passcodes and allowed_emails
at create time — no follow-up call needed.
{
"name": "outpost_create",
"arguments": {
"name": "Vendor docs",
"source_url": "https://vendor.example.com/onboarding",
"visibility": "shared",
"passcodes": ["pilot-2026"],
"allowed_emails": ["alice@acme.com", "bob@acme.com"]
}
}
Returns:
{
"id": "Mn7Pq2Rs",
"name": "Vendor docs",
"visibility": "shared",
"passcodes": ["pilot-2026"],
"allowed_emails": ["alice@acme.com", "bob@acme.com"],
"default_file": "index.html",
"url": "https://outpost.click/p/Mn7Pq2Rs",
"created_at": "2026-05-03T22:44:38",
"message": "Page created from source_url"
}
outpost_upload_url —
MCP can't carry binary content through a tool call, so the upload has
to shell out to curl.
outpost_update
Update any subset of name, visibility,
passcodes, and allowed_emails. Files are
not touched — use outpost_update_from_url
or outpost_update_files for content.
{
"name": "outpost_update",
"arguments": {
"page_id": "Mn7Pq2Rs",
"name": "Renamed",
"visibility": "shared",
"passcodes": ["one", "two"],
"allowed_emails": ["alice@acme.com", "carol@acme.com"]
}
}
Pass an empty array to clear a list:
{
"name": "outpost_update",
"arguments": { "page_id": "Mn7Pq2Rs", "allowed_emails": [] }
}
outpost_update_from_url
Replace the content of an existing page by re-fetching from a URL. The page id and URL stay the same.
{
"name": "outpost_update_from_url",
"arguments": {
"page_id": "Mn7Pq2Rs",
"source_url": "https://vendor.example.com/onboarding"
}
}
Returns:
{
"id": "Mn7Pq2Rs",
"name": "Vendor docs",
"visibility": "shared",
"default_file": "index.html",
"url": "https://outpost.click/p/Mn7Pq2Rs",
"updated_at": "2026-05-03T22:44:52",
"message": "Page files replaced from source_url"
}
outpost_delete
{
"name": "outpost_delete",
"arguments": { "page_id": "Ab12Cd34" }
}
outpost_upload_url
Returns curl recipes for creating a new page from a
local file (ZIP or HTML). MCP can't carry binary content, so the upload
itself has to shell out. If you pass passcodes or
allowed_emails, the recipe also includes the follow-up
PUT to set them after the page exists.
{
"name": "outpost_upload_url",
"arguments": {
"name": "Team wiki",
"visibility": "shared",
"passcodes": ["letmein"],
"allowed_emails": ["alice@acme.com", "bob@acme.com"]
}
}
Result (abbreviated):
{
"note": "Creates a NEW page from a local file. For URL-based creation use outpost_create instead.",
"upload_file": {
"endpoint": "POST https://outpost.click/api/pages",
"example": "curl -X POST \"https://outpost.click/api/pages\" -H \"Authorization: Bearer op_YOUR_TOKEN\" -F \"name=Team wiki\" -F \"visibility=shared\" -F \"file=@./my_file.zip\""
},
"after_upload": [
{
"endpoint": "PUT https://outpost.click/pages/<NEW_PAGE_ID>",
"example": "curl -X PUT \"https://outpost.click/pages/<NEW_PAGE_ID>\" -H \"Authorization: Bearer op_YOUR_TOKEN\" -F \"passcodes=letmein\" -F \"allowed_emails=alice@acme.com,bob@acme.com\""
}
]
}
outpost_update_files
Returns curl recipes for replacing files on an existing page from a
local file. The page URL stays the same. For URL-based updates prefer
outpost_update_from_url — no curl needed.
{
"name": "outpost_update_files",
"arguments": { "page_id": "Ab12Cd34" }
}
Result includes
POST https://outpost.click/api/pages/Ab12Cd34/files recipes
for both file=@… and source_url=… forms, plus a
hint to use outpost_update for metadata changes.
Error shape
Recoverable tool errors are returned inside the tool result (so the AI client sees them) rather than as JSON-RPC errors. Examples:
{ "error": "page_id is required" }
{ "error": "Invalid visibility" }
{ "error": "Page not found. Use outpost_list to see your pages." }
Transport-level problems (auth, malformed JSON, unknown methods) come back as proper HTTP / JSON-RPC errors.
Quick Python client
import json, requests
URL = "https://outpost.click/mcp"
TOKEN = "op_YOUR_TOKEN"
def rpc(method, params=None, id_=1):
r = requests.post(
URL,
headers={"Authorization": f"Bearer {TOKEN}"},
json={"jsonrpc": "2.0", "id": id_, "method": method, "params": params or {}},
timeout=30,
)
r.raise_for_status()
return r.json()
print(rpc("initialize"))
print(rpc("tools/list", id_=2))
resp = rpc("tools/call",
{"name": "outpost_list", "arguments": {}}, id_=3)
payload = json.loads(resp["result"]["content"][0]["text"])
print(payload["count"], "pages")