Build Your First MCP Server in X07
The Model Context Protocol is now the standard way to connect coding agents to tools and data sources.
X07 ships an MCP kit through x07-mcp, and the core x07 CLI delegates template scaffolding and conformance tooling to it.
This tutorial uses the current HTTP template because it is the easiest one to probe with curl. If you want stdio transport instead, use mcp-server-stdio. If you need long-running task APIs, use mcp-server-http-tasks.
Prerequisites
You need:
- macOS or Linux, or Windows through WSL2
- a working C compiler such as
clangorgcc - about five minutes
1. Install X07
# Install the stable X07 toolchain through x07up.
curl -fsSL https://x07lang.org/install.sh | sh -s -- --yes --channel stable
# Make the X07 shims visible in the current shell session.
export PATH="$HOME/.x07/bin:$PATH"
# Confirm that the CLI is available.
x07 --help
If you want a machine-readable environment check after install, run x07 doctor.
2. Scaffold the HTTP MCP template
# Generate a starter MCP server project from the current HTTP template.
x07 init --template mcp-server-http --dir ./my-mcp-http
# Enter the new project directory.
cd ./my-mcp-http
# Resolve and lock the template's package graph.
x07 pkg lock
The generated project includes the pieces you need to get moving:
config/mcp.server.jsonfor the production-shaped server configconfig/mcp.server.dev.jsonfor no-auth local developmentconfig/mcp.tools.jsonfor tool declarationssrc/main.x07.jsonfor the router entrypointsrc/worker_main.x07.jsonfor the worker entrypointsrc/mcp/user.x07.jsonfor your tool logictests/tests.jsonplus replay fixtures for deterministic checks
3. Look at one tool implementation
The scaffold already includes a simple echo-style tool. The real template has extra validation and error handling, but this trimmed excerpt shows the shape.
This snippet is based on the X07 language itself, using the canonical x07AST JSON form from the scaffolded MCP template. Every line is commented because many readers will be new to X07.
{
"kind": "defn", // Define one X07 function that implements a tool.
"name": "mcp.user._tool_echo_v1", // Give the tool a stable module-qualified symbol name.
"params": [
{"name": "args_json", "ty": "bytes_view"} // Tool arguments arrive as UTF-8 JSON bytes.
],
"result": "bytes", // MCP tool responses are returned as bytes.
"body": [
"begin", // Evaluate the tool body as a sequence of expressions.
["let", "doc_b", ["ext.json.data_model.parse", "args_json"]], // Parse the incoming JSON argument object.
["let", "doc", ["bytes.view", "doc_b"]], // Borrow the parsed document without copying it.
["let", "root", ["ext.data_model.root_offset", "doc"]], // Find the root object inside the parsed JSON document.
["let", "k_text", ["bytes.lit", "text"]], // Build the key name for the argument lookup.
["let", "text_off", ["ext.data_model.map_find", "doc", "root", ["bytes.view", "k_text"]]], // Locate the "text" field in the argument object.
["let", "text", ["ext.data_model.string_get", "doc", "text_off"]], // Read the string value for that field.
["std.mcp.tool.result.ok_text_v1", ["bytes.view", "text"]] // Return a successful MCP text result.
]
}
The important part is the contract: your tool code stays in src/mcp/user.x07.json, and the router/worker infrastructure is already scaffolded around it.
4. Bundle the router and the sandboxed worker
The current MCP docs use two bundles: one router process and one worker process.
# Build the main router binary with real OS access.
x07 bundle --profile os --out out/mcp-router
# Build the worker binary from the worker entrypoint under the sandbox profile.
x07 bundle --profile sandbox --program src/worker_main.x07.json --out out/mcp-worker
The template's config/mcp.server.json already expects the worker binary at out/mcp-worker, so you do not need to change the config if you keep that output path.
5. Run the local no-auth dev server
The HTTP template defaults to OAuth for production-shaped behavior. For local development, use the shipped dev config.
# Start the router with the no-auth development configuration.
X07_MCP_CFG_PATH=config/mcp.server.dev.json ./out/mcp-router
The template defaults are:
- bind address:
127.0.0.1:8314 - MCP path:
/mcp - SSE enabled for streamable HTTP
6. Initialize it with curl
# Send a minimal MCP initialize request to the local server.
curl -i \
-H 'Content-Type: application/json' \
-H 'Accept: application/json,text/event-stream' \
-H 'Origin: http://localhost:3000' \
-H 'MCP-Protocol-Version: 2025-11-25' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}' \
http://127.0.0.1:8314/mcp
At that point you have a running MCP server built from the current X07 template.
7. Run the built-in checks
# Run the template's deterministic test manifest.
x07 test --manifest tests/tests.json
# Check protocol behavior against the MCP conformance harness.
x07 mcp conformance --url http://127.0.0.1:8314/mcp
Where to go next
If you want to keep building:
- use
mcp-server-stdiofor direct stdio transport - use
mcp-server-http-taskswhen you needtasks/*, progress, or resumable task flows - edit
src/mcp/user.x07.jsonto add real tools - keep
x07 test --manifest tests/tests.jsonin your loop so replay fixtures stay deterministic
The main point is that X07 gives you a scaffolded MCP architecture, a sandboxed worker split, and conformance tooling from the start instead of expecting you to assemble those pieces later.