MCP Server

braised mcp serve is a long-running Streamable HTTP server that exposes built site artifacts as structured MCP tools. Agents (Claude, Cursor, LLM pipelines) connect to it and query the site's nav tree, page index, and page content without crawling HTML.

Requires a prior braised build. The server loads artifacts into memory at startup — it cannot serve what hasn't been built.

Starting the server

braised mcp serve

By default the server looks for braised.yaml in the current directory. Pass a path if you're running from elsewhere:

braised mcp serve --site /path/to/site

Default bind address is 127.0.0.1:8081. Configure host and port in braised.yaml:

mcp:
  serve:
    host: 127.0.0.1
    port: 8081

See CLI Reference for all flags.

Reading the startup log

The startup line tells you immediately whether the server is healthy:

{"time":"...","level":"INFO","msg":"startup","tools":3,"host":"127.0.0.1","port":8081}
{"time":"...","level":"INFO","msg":"artifacts_loaded","frontmatter-index_kb":514.5,"nav-structure_kb":204.1}

"tools":3 is the number of registered MCP tools. If this is lower than expected, an artifact is missing — check that braised build completed cleanly and that the artifacts/ directory contains the expected files.

A warning at startup indicates the build marker shows a problem:

{"level":"WARN","msg":"build_state","artifacts":"failed","detail":"artifacts may be stale — run braised build"}

Tool catalog

Tool Source Available when
get_nav_structure artifacts/nav-structure.json Always (after build)
get_page_index artifacts/frontmatter-index.json Always (after build)
get_page dist/*/index.md companion files Always (after build)
get_theme_variables artifacts/theme-variables.json When artifact is present

get_nav_structure and get_page_index serve from the in-memory artifact map — they are fast map lookups. get_page reads companion Markdown files from disk at request time, so it reflects incremental rebuilds without a server restart.

get_theme_variables only registers if artifacts/theme-variables.json exists. If it is absent, the tool does not appear in tools/list.

Session protocol

braised mcp serve uses the MCP Streamable HTTP transport, which requires session management. Every request after initialize must include the session ID returned by the server — requests without it are rejected with 404 Invalid session ID.

The flow:

  1. POST to /mcp with an initialize request
  2. Capture the Mcp-Session-Id header from the response
  3. Include that header on every subsequent request
# Initialize and capture the session ID
SESSION=$(curl -si -X POST http://127.0.0.1:8081/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {"name": "my-client", "version": "1.0"}
    }
  }' | grep -i "Mcp-Session-Id:" | awk '{print $2}' | tr -d '\r')

# List available tools
curl -s -X POST http://127.0.0.1:8081/mcp \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

MCP client libraries (in Python, TypeScript, Go) handle session management automatically. The manual session dance above only matters when calling the server directly or building your own client.

Tool usage

get_nav_structure

Returns the full navigation tree. No arguments.

curl -s -X POST http://127.0.0.1:8081/mcp \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_nav_structure","arguments":{}}}'

The response content[0].text is a JSON string. Parse it to get the nav tree — an object with a roots array of nodes, each with label, url, and children.

get_page_index

Returns all pages with title, URL, layout, description, and in_nav status. No arguments. Use this to discover valid page URLs before calling get_page.

curl -s -X POST http://127.0.0.1:8081/mcp \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION" \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_page_index","arguments":{}}}'

Pages excluded via llm_exclude: true are not present in the index.

get_page

Returns the raw Markdown source for a specific page URL.

curl -s -X POST http://127.0.0.1:8081/mcp \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION" \
  -d '{
    "jsonrpc": "2.0",
    "id": 5,
    "method": "tools/call",
    "params": {
      "name": "get_page",
      "arguments": {"path": "/i-tutorial/advanced-features/transactions/"}
    }
  }'

URL format: Use the url field from get_page_index directly. Leading and trailing slashes are normalized internally, so /guide/install, /guide/install/, and guide/install all resolve to the same page. Path matching is case-sensitive.

Stub pages: Section index pages that exist only as organizational containers — a file with frontmatter and no body content — return just the frontmatter. This is correct behavior. get_page_index shows these pages normally; get_page returns what's there.

Missing pages: Returns isError: true with "page not found: /path/". Pages excluded via llm_exclude: true return the same not-found error — their existence is not disclosed.

Freshness

Tool Freshness
get_page Live — reads from disk, reflects incremental rebuilds
get_nav_structure Frozen at last braised build (server startup)
get_page_index Frozen at last braised build (server startup)
get_theme_variables Frozen at last braised build (server startup)

After a full braised build, restart braised mcp serve to pick up updated artifacts. get_page does not require a restart — it reads companion files at request time.