Documentation
The MCP server and what it will not do
Two addresses on one handler, why authentication runs before tools/list, the three read-only tools, and the protocol revisions it answers.
Last reviewed against the product on .
This site runs a Model Context Protocol server, so an assistant can read your market signals, supplier totals and provider health directly rather than being told about them second-hand. It is the only authenticated MCP server in the Novus estate, because it is the only one with a database behind it: the answer here depends on who is asking.
The address, and a ready-made client configuration block, are on the public MCP server page. This page explains how the endpoint behaves.
Two addresses, one server
The endpoint answers at /mcp and at /api/mcp. The second came first and is the address already
published to clients configured earlier; the first is the path every Novus site uses, and the one
the sibling sites point at. Rather than move a published URL, both paths call the same handler.
They also share one rate-limit budget. Two doors with a limit on only one of them is not a limit, and a caller alternating between them gets one allowance rather than two.
POST only
| Method | Answer | Why |
|---|---|---|
| POST | JSON-RPC over HTTP | The whole transport. Every method rides here. |
| GET | 405, with an Allow header naming POST | Protocol revision 2026-07-28 removed the server-sent stream. A client that opens one has to be told definitively, or it waits for an event that will never arrive. |
| DELETE | 405, with an Allow header naming POST | The same revision removed protocol sessions. There is no session to tear down. |
Measured from app/mcp/route.ts, app/api/mcp/route.ts and createMcpHandler in src/lib/mcp/core.ts.
Opening the endpoint in a browser is therefore expected to fail. That is why the human-readable MCP server page exists separately.
Authentication comes first
The server requires a bearer token, and the check runs before any method dispatches, including
tools/list and server/discover. That ordering is the point: merely listing the tools of a
private server tells an unauthenticated caller what data it holds. A caller without a valid token
does not learn the tool names.
A failed check answers 401 with a WWW-Authenticate header naming the bearer scheme and an
invalid_token error, which is what lets a client tell "your credential is wrong" apart from "this
endpoint is not here". The credential is a personal access token; see
personal access tokens for how to make one and what it is limited to.
The tools
| Tool | What it returns | Scoped to |
|---|---|---|
| market_summary | Comparable market signals: item, data level, percent change, confidence, priority, when it was observed, and the source URL it came from | Your organization |
| supplier_summary | Purchasing totals per supplier, split by currency, with the number of purchase rows behind each | Your organization |
| source_health | The latest health check for every public data provider the site ingests from, newest first | The whole site |
Measured from mcpServerDefinition in src/lib/mcp/server.ts.
source_health is deliberately not scoped to an organization, because it describes the ingestion
pipeline rather than anyone's data. It stays behind authentication anyway, since it describes
internal infrastructure.
- Tools
- 3
- all read-only
- Market signals per call
- 25
- a fixed cap; the tool does not page
- Rate limit
- 120 per minute
- per caller address, shared across both addresses
- Server version
- 2.0.0
- reported in the server info every client receives
Measured from src/lib/mcp/server.ts and RATE_POLICIES.mcp in src/lib/shared/rate-limit.ts.
The limit is the loosest of the three policies in this repo, because one assistant conversation legitimately makes many small tool calls in a row and throttling that makes the site look broken to exactly the audience the endpoint exists for. The real ceiling on abuse is credential issuance rather than this number, which is why the token endpoint is the one carrying a durable quota.
Protocol revisions
The server is dual-era: it answers the current stateless revision and the older handshake-based ones on the same endpoint, because clients in the wild speak both.
| Revision | Era | How a client selects it |
|---|---|---|
| 2026-07-28 | Modern, stateless | Version, client identity and capabilities ride in the request's own metadata. No handshake, no session. |
| 2025-11-25, 2025-06-18, 2025-03-26 | Legacy | An initialize request, answered with the newest of these the client asked for. |
Measured from MODERN_PROTOCOL_VERSION and LEGACY_PROTOCOL_VERSIONS in src/lib/mcp/core.ts.
A modern request may repeat its protocol version, method name or tool name in headers. If a header disagrees with the body, the request is refused with a 400 rather than resolved by guessing which one the client meant.
Origin
A request carrying an Origin header is accepted only when that origin is this site's own or one
explicitly allowed; anything else is refused with 403. A request with no Origin header at all is
accepted, because that is every non-browser client, which is what an MCP client normally is.
What this server will not do
The server also asks its callers to report provenance. Every market observation carries a source URL
and an observation time, and an assistant reporting a figure should carry both, because the point of
this dataset is that a number can be traced to the official source it came from. Checking
source_health first matters for the same reason: a provider that is currently failing still
returns its last successful observation, which looks exactly like a current one.
When the endpoint is switched off
MCP delivery sits behind an environment flag. When it is off, the endpoint answers 404, not 403, so a disabled server is indistinguishable from one that was never deployed. If a client reports the address as missing rather than unauthorised, that is the state to suspect before you start checking tokens.