Xpec is the home for product, feature, and architectural specs. The Xpec MCP server gives local AI coding agents (Claude Code, Cursor, VS Code, Zed, Windsurf, β¦) read and write access to those specs, so agents can plan, implement, and update features against the spec β not against stale docs/, hallucinated APIs, or whatever the model remembers from training.
Coding agents drift from your product's actual contracts. You get:
- β Code that ships ahead of the spec, then quietly diverges
- β Implementations that contradict ADRs nobody re-read
- β Duplicate "RFC-2025-β¦" markdown files in the repo, none authoritative
- β Specs updated only after the code lands, when nobody can challenge them
The agent reads the current spec before writing code, and proposes spec changes through the same workflow a human reviewer approves.
Implement the password-reset flow per the "auth/password-reset" spec.
Use the contracts and error codes from Β§4. If the spec is incomplete,
open a draft, fill it in, and request review before writing code.
What ADRs apply to background jobs in this product? Read them, then
critique my proposed worker change against them.
The agent calls read_specification, list_open_questions, start_new_version, update_specification_section, request_review β and you stay in control: a human still marks the draft Reviewed in the Xpec UI.
- Workspace β top-level container. Contains member Products plus its own Workspace-scoped specs (e.g., cross-product ADRs).
- Product β a single product or service. Holds the feature, UX, and architecture specs that govern its codebase.
- Specification β Markdown document with status (
DraftβNeeds ReviewβReviewed), open questions, and a version history. - Binding β a
.xpec.jsonat the repo root binds the local checkout to a Workspace and/or Product, so agents don't have to pass ids on every call.
See xpec.app for the dashboard and to mint a token.
- Node.js β₯ 20.11
- An MCP-compatible client (Claude Code, Cursor, VS Code, Windsurf, Zed, Claude Desktop, β¦)
- An Xpec Personal Access Token β generate one at
https://xpec.app/settings/developer - A repo with a
.xpec.jsonfile (orXPEC_WORKSPACE_ID/XPEC_PRODUCT_IDenv vars). See Binding the workspace below.
Run this command. See the Claude Code MCP docs for more info.
claude mcp add --scope user \
-e XPEC_API_TOKEN=YOUR_TOKEN \
xpec -- npx -y @nextfreelatech/xpec-mcpDrop --scope user to install only for the current project.
To add a rule so the agent always reads the spec first, append the snippet from Add a rule below to your CLAUDE.md.
Add this to ~/.cursor/mcp.json (global) or .cursor/mcp.json (project). See the Cursor MCP docs.
{
"mcpServers": {
"xpec": {
"command": "npx",
"args": ["-y", "@nextfreelatech/xpec-mcp"],
"env": {
"XPEC_API_TOKEN": "YOUR_TOKEN"
}
}
}
}See the VS Code MCP docs.
"mcp": {
"servers": {
"xpec": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@nextfreelatech/xpec-mcp"],
"env": {
"XPEC_API_TOKEN": "YOUR_TOKEN"
}
}
}
}Add this to your Windsurf MCP config. See the Windsurf MCP docs.
{
"mcpServers": {
"xpec": {
"command": "npx",
"args": ["-y", "@nextfreelatech/xpec-mcp"],
"env": {
"XPEC_API_TOKEN": "YOUR_TOKEN"
}
}
}
}Add this to your Zed settings.json. See the Zed Context Server docs.
{
"context_servers": {
"Xpec": {
"source": "custom",
"command": "npx",
"args": ["-y", "@nextfreelatech/xpec-mcp"],
"env": {
"XPEC_API_TOKEN": "YOUR_TOKEN"
}
}
}
}Edit your claude_desktop_config.json. See the Claude Desktop MCP docs.
{
"mcpServers": {
"xpec": {
"command": "npx",
"args": ["-y", "@nextfreelatech/xpec-mcp"],
"env": {
"XPEC_API_TOKEN": "YOUR_TOKEN"
}
}
}
}See the OpenAI Codex repo for more on the MCP configuration format. Codex reads ~/.codex/config.toml.
[mcp_servers.xpec]
command = "npx"
args = ["-y", "@nextfreelatech/xpec-mcp"]
env = { XPEC_API_TOKEN = "YOUR_TOKEN" }
startup_timeout_ms = 20_000First, run the server (see Running over HTTP). Then point Codex at it:
[mcp_servers.xpec]
url = "http://127.0.0.1:3030/mcp"
http_headers = { "Authorization" = "Bearer YOUR_TOKEN" }Optional troubleshooting β only if Codex reports startup "request timed out" or "program not found". Most users can ignore this.
First try: bump
startup_timeout_msto40_000.Windows quick fix (absolute
npxpath + explicit env):[mcp_servers.xpec] command = "C:\\Users\\yourname\\AppData\\Roaming\\npm\\npx.cmd" args = ["-y", "@nextfreelatech/xpec-mcp"] env = { XPEC_API_TOKEN = "YOUR_TOKEN", SystemRoot = "C:\\Windows", APPDATA = "C:\\Users\\yourname\\AppData\\Roaming" } startup_timeout_ms = 40_000macOS quick fix (call Node directly with the installed package's entry point):
[mcp_servers.xpec] command = "/Users/yourname/.nvm/versions/node/v22.14.0/bin/node" args = [ "/Users/yourname/.nvm/versions/node/v22.14.0/lib/node_modules/@nextfreelatech/xpec-mcp/dist/cli.js", "--stdio" ] env = { XPEC_API_TOKEN = "YOUR_TOKEN" }Replace
yournamewith your OS username. On Windows, settingAPPDATAandSystemRootis essential becausenpxrequires them but some Codex builds don't pass them through.
Any client that launches an MCP server via command + args can swap npx for an alternative runtime.
{
"mcpServers": {
"xpec": {
"command": "bunx",
"args": ["-y", "@nextfreelatech/xpec-mcp"],
"env": { "XPEC_API_TOKEN": "YOUR_TOKEN" }
}
}
}{
"mcpServers": {
"xpec": {
"command": "deno",
"args": [
"run",
"--allow-env",
"--allow-net",
"--allow-read",
"npm:@nextfreelatech/xpec-mcp"
],
"env": { "XPEC_API_TOKEN": "YOUR_TOKEN" }
}
}
}npx on Windows usually needs to be invoked via cmd /c:
{
"mcpServers": {
"xpec": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@nextfreelatech/xpec-mcp"],
"env": { "XPEC_API_TOKEN": "YOUR_TOKEN" }
}
}
}For agents that consume MCP over HTTP/SSE rather than stdio, run the server explicitly:
XPEC_API_TOKEN=YOUR_TOKEN \
npx -y @nextfreelatech/xpec-mcp --http --port 3030 --cors-origin https://your-agent.example.comThen point your hosted agent at http://<host>:3030/mcp.
The MCP server is bound to a Workspace and/or a Product so tools like list_specifications work without passing ids every call.
Drop a .xpec.json at your repo root:
{
"workspaceId": "ws_β¦",
"productId": "prd_β¦"
}Either field is optional:
| Configuration | Effective mode | What works |
|---|---|---|
workspaceId + productId |
workspace+product |
Everything; defaults to the Product's specs |
workspaceId only |
workspace |
Workspace-scoped specs + cross-Product search |
productId only |
product |
Product-scoped specs (orphan / pre-aggregation Products work this way) |
| Neither | discovery |
Only list_workspaces / list_products β bind first to do anything else |
You can also use environment variables: XPEC_WORKSPACE_ID, XPEC_PRODUCT_ID. The file wins over env vars when both are present.
XPEC_API_TOKEN=YOUR_TOKEN npx -y @nextfreelatech/xpec-mcp --checkPrints OK: https://xpec.app reachable, N product(s) visible. on success, or a structured error code (AUTH_REQUIRED, PRODUCT_NOT_BOUND, β¦) and remediation when something is off. Add --json for machine-readable output.
All tools take ids as strings. Bound Workspace/Product ids are inferred from .xpec.json unless overridden in the call.
| Tool | Purpose |
|---|---|
list_workspaces |
List Workspaces visible to the token. Use when you don't yet know which Workspace to bind. |
list_products |
List Products. With a Workspace binding, returns its member Products; without, returns orphan Products. |
read_workspace |
Workspace metadata: name, description, type, archived state. |
read_product |
Product metadata: name, description, specificationManagementType, member-of Workspace. |
list_specifications |
List specs in scope. Filters: type (BUSINESS / UX / DESIGN_SYSTEM / DOCUMENT), status (DRAFT / GENERATING / NEEDS_REVIEW / REVIEWED), folder, tags, query. Cursor-paginated. |
search_specifications |
Lexical full-text search across spec titles and content. Workspace bindings search the Workspace plus every member Product; results carry a scope discriminator. |
read_specification |
Current Markdown body of a spec, plus status and OCC version. Use format="rendered" to strip open-question and assumption markers. |
list_specification_versions |
Reviewed snapshots of a spec, newest first. |
read_specification_version |
Full Markdown of a specific approved revision. Pair with list_specification_versions to diff history against current. |
list_open_questions |
Questions and assumptions attached to a spec. Resolved/dismissed items are excluded unless includeResolved=true. |
| Tool | Purpose |
|---|---|
start_new_version |
Open a new Draft of a Reviewed spec. Required before any write tool on a published spec. No-op when the spec is already a Draft (returns hint="already_draft"). |
update_specification_content |
Replace the full Markdown body of a Draft. OCC-guarded β pass the version from your most recent read_specification. Returns STALE_VERSION (409) if another writer landed first; re-read and retry. |
update_specification_section |
Replace one heading-bound section (sectionPath="## Pricing"). OCC-guarded. Records a before-image revision. |
request_review |
Move a Draft to Needs Review for a human to approve. Rejected with OPEN_QUESTIONS_PRESENT if questions remain β surface them to the user first. |
discard_draft |
Roll a Draft (or Needs Review) back to its last approved version. Rejected on specs that have never been approved. |
create_free_specification |
Create a new Markdown spec in the bound Free product. Path uniqueness is enforced. Rejected with PRODUCT_TYPE_MISMATCH on Web Application Products β use start_new_version on a structured spec. |
Note β the agent never calls "mark reviewed". Approval stays a human action in the Xpec UI. The MCP can only nudge a draft to
Needs Review.
Once installed, tell your agent to consult Xpec before writing code. Drop this into CLAUDE.md, .cursorrules, .windsurfrules, or your client's equivalent:
Before writing or updating code, planning a feature, or making an architectural
choice, search and read the relevant Xpec specs via the xpec
MCP. Treat them as the source of truth. If a spec is wrong or incomplete, open
a draft (start_new_version), update it (update_specification_section), and
request review (request_review) before implementing. Never duplicate spec
content into the repo.
The repo's CLAUDE.md is a good place for project-specific guidance.
If you already know the spec id, pass it directly to skip the search:
Read the "auth/password-reset" spec and reconcile Β§4 with the current
src/server/auth/reset.ts implementation. specId=spec_01Hβ¦
Override the API base URL at the binding:
{
"apiUrl": "http://localhost:3000",
"workspaceId": "ws_local_dev",
"productId": "prd_local_dev"
}http://localhost is allowed without --allow-insecure. For any other non-HTTPS host, pass --allow-insecure (intended for self-hosted dev only).
Standard https_proxy / HTTPS_PROXY env vars are honoured.
# From the monorepo root
npm install
npm run buildRun the built server:
XPEC_API_TOKEN=YOUR_TOKEN node dist/cli.jsxpec-mcp accepts:
serve(default) β run the MCP server. Stdio unless--httpis set.--checkβ verify token + API URL and exit0/1. Pair with--jsonfor scripting.--help,-hβ usage.--stdioβ run over stdio (default; for desktop agents).--httpβ run as an HTTP/SSE server (for hosted agents).--port <n>β port for--http(default3030).--host <addr>β host for--http(default127.0.0.1).--cors-origin <o>β origin to allow (repeatable). Without this, cross-origin browser requests are rejected.--api-url <url>β override the Xpec API base URL.--allow-insecureβ permit a non-HTTPSapiUrl(self-hosted dev only).
| Variable | Purpose |
|---|---|
XPEC_API_TOKEN |
Required. Personal Access Token from /settings/developer. |
XPEC_API_URL |
Override the API base URL. Default https://xpec.app. |
XPEC_WORKSPACE_ID |
Default Workspace binding when no .xpec.json is present. |
XPEC_PRODUCT_ID |
Default Product binding when no .xpec.json is present. |
XPEC_TELEMETRY |
Set to 0 to disable anonymous telemetry. |
XPEC_LOG_LEVEL |
debug | info | warn | error (default info). |
The --api-url CLI flag takes precedence over XPEC_API_URL. The .xpec.json apiUrl field falls between the two.
XPEC_API_TOKEN=YOUR_TOKEN \
npx -y @modelcontextprotocol/inspector npx @nextfreelatech/xpec-mcpAUTH_REQUIRED / 401 from every tool β token is missing, expired, or revoked. Mint a new one at /settings/developer and update your client's env.
PRODUCT_NOT_BOUND / WORKSPACE_NOT_BOUND β the tool needs a binding the session doesn't have. Either pass productId / workspaceId explicitly, or add it to .xpec.json (see Binding the workspace).
STALE_VERSION from update_specification_* β another writer landed between your read and your write. Re-call read_specification to get the current version, then retry.
OPEN_QUESTIONS_PRESENT from request_review β call list_open_questions first, resolve them in the spec, then retry.
Legacy .xpec.json shape rejected at startup β the binding format changed; the CLI prints a remediation pointing to the new shape. Update the file.
ERR_MODULE_NOT_FOUND under npx β try bunx instead. It often resolves stale npm caches.
Plain-HTTP apiUrl β only localhost / 127.0.0.1 / ::1 are allowed by default. For any other host, pass --allow-insecure (self-hosted dev only).
Apache License 2.0 Β© Nextfreela Tech.