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:
- POST to
/mcpwith aninitializerequest - Capture the
Mcp-Session-Idheader from the response - 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.