Skip to navigation

MCP Server

Connect Claude, Cursor, and your own agents to your document corpus

The Pyramid AI MCP server exposes your organization’s document corpus over the Model Context Protocol — an open standard for connecting AI assistants to external tools and data. Any MCP-aware client (Claude, Cursor, or a custom agent built on the MCP SDK) can search your documents and get back grounded answers with source links, without you writing a line of integration code.

Search documents

Run hybrid semantic + keyword search and get back ranked documents with their source URLs.

Answer questions

Get a finished, cited answer synthesized from the most relevant passages in your corpus.

Stay grounded

Every result carries a source link, so your agent can always cite where an answer came from.

MCP access is gated per organization. If your key returns 403 FEATURE_NOT_ENABLED, ask your Pyramid AI account manager to enable the MCP server for your org.

Endpoint

POST https://api.pyramid-ai.com/api/v2/mcp
EnvironmentBase URLKey prefix
Productionhttps://api.pyramid-ai.com/api/v2/mcppai_live_*
Staginghttps://api-staging.pyramid-ai.com/api/v2/mcppai_test_*
  • Transport: Streamable HTTP, stateless — one POST per JSON-RPC exchange, no session to manage.
  • Only POST is served; GET and DELETE return a JSON-RPC 405.

Connect in under 5 minutes

1

Get an API key

Create a key in the Pyramid AI platform, or use one you already have. Production keys (pai_live_*) work against api.pyramid-ai.com; staging keys (pai_test_*) against api-staging.pyramid-ai.com. See Authentication for key formats, environments, and rotation.

Store your API key securely — it is shown only once at creation time and cannot be retrieved later.

2

Add the server to your client

Point your MCP client at the endpoint and pass your key as a bearer token. See Connect your client below for the exact config for Claude, Cursor, and the SDK.

3

Ask a question

Once connected, your agent can call the search_documents and answer_question tools directly. Try: “Search the corpus for bamboo scaffolding safety requirements.”

Authentication

The MCP endpoint uses the same bearer API-key auth as every other Pyramid AI API route — no OAuth flow. Send your key in the Authorization header, and because the transport is Streamable HTTP, your client must advertise both content types in Accept:

Authorization: Bearer pai_live_YOUR_KEY_HERE
Content-Type: application/json
Accept: application/json, text/event-stream

Responses come back as a one-shot SSE frame (event: message / data: <json-rpc>). Most MCP clients and SDKs handle this framing for you.

Connect your client

Claude Code

claude mcp add pyramid-corpus --transport http \
https://api.pyramid-ai.com/api/v2/mcp \
--header "Authorization: Bearer pai_live_YOUR_KEY_HERE"

Claude Desktop / Cursor

Add the server to your mcpServers config (claude_desktop_config.json for Claude Desktop, .cursor/mcp.json for Cursor):

{
"mcpServers": {
"pyramid-document-corpus": {
"type": "http",
"url": "https://api.pyramid-ai.com/api/v2/mcp",
"headers": { "Authorization": "Bearer pai_live_YOUR_KEY_HERE" }
}
}
}

Custom agent (TypeScript SDK)

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
const transport = new StreamableHTTPClientTransport(
new URL('https://api.pyramid-ai.com/api/v2/mcp'),
{ requestInit: { headers: { Authorization: 'Bearer pai_live_YOUR_KEY_HERE' } } },
);
const client = new Client({ name: 'my-agent', version: '1.0' });
await client.connect(transport);
const result = await client.callTool({
name: 'answer_question',
arguments: { query: 'What are the requirements for road opening works?' },
});

MCP Inspector (visual debugging)

npx @modelcontextprotocol/inspector

Choose transport Streamable HTTP, set the URL to the endpoint, and add the Authorization: Bearer … header.

Tools

The server exposes two tools. Both accept the same arguments — the difference is whether you want raw hits to reason over yourself, or a finished, cited answer.

You wantCallTypical latency
Ranked documents your own model reasons oversearch_documents~5s
A finished answer with [n] citationsanswer_question~20–25s

Shared arguments

ArgumentTypeRequiredNotes
querystringyesNatural-language question or keywords.
scopeenumnoOne of technical_circulars, general_circulars, standards_and_codes, practice_notes, safety_health, publications, press_releases, everything. Omit it to search all technical documents and auto-exclude noise — the right default for almost every query. See Scope & broadening.
startDatestring (ISO)noInclusive lower bound, e.g. 2024-01-01T00:00:00Z. Only set for an explicit time window — date filters drop undated documents, which are common in this corpus.
endDatestring (ISO)noInclusive upper bound; set only alongside startDate.
versionPolicyenumnocurrent (the default) or all. Many documents exist in several editions — the Schedule of Rates, the General Specification, codes of practice. By default only the current edition of each document is searched; set all to include superseded editions, e.g. to compare editions or read an earlier one. See Editions.

search_documents

Hybrid (semantic vector + full-text BM25) search over the corpus, returning ranked documents with their source links. The tool result is a JSON payload:

{
"query": "bamboo scaffolding",
"scope": "technical_circulars",
"requestedScope": "technical_circulars",
"broadened": true,
"hasResults": true,
"count": 1,
"results": [
{
"title": "DEVB TC(W) No. 1/2025",
"url": "https://www.devb.gov.hk/tcw-1-2025.pdf",
"snippet": "Guidance on bamboo scaffolding...",
"score": 0.91,
"edition": null,
"versionStatus": "current",
"publishedAt": "2025-01-01T00:00:00.000Z"
}
],
"versionPolicy": "current"
}

Always surface results[].url to the user — it is the canonical source link. When hasResults is false, tell the user nothing relevant was found and ask them to refine; do not answer from prior knowledge.

Editions

Results come from the current edition of each document unless you pass versionPolicy: "all". Every result says which edition it is: edition (e.g. "2026 Edition", or null when the document has a single edition), versionStatus ("current" or "superseded") and, for a superseded edition, supersededBy (the title of the current one). When you show a result to a user, name its edition; never present a superseded result as current. A date range (startDate/endDate) returns documents published in that range and, for documents with editions, the edition that was in force then.

answer_question

Runs the same hybrid search, then synthesizes a complete written answer from the top passages, with bracketed [n] citation markers. The answer is atomic — it arrives whole after ~20–25s (MCP tool results cannot stream).

{
"query": "what are the requirements for road opening works?",
"answer": "Road opening works must follow the permit conditions [1]...",
"hasResults": true,
"scope": "default",
"requestedScope": "default",
"broadened": false,
"citations": [
{ "ref": 1, "title": "HyD TC No. 5/2024", "url": "https://..." }
],
"sources": [
{ "ref": 1, "title": "HyD TC No. 5/2024", "url": "https://...", "snippet": "...", "score": 0.88 }
]
}
  • citations is the subset of sources the answer actually references via its [n] markers.
  • sources lists every passage the search retrieved, whether or not it was cited.
  • The payload is also returned as structuredContent for SDK clients that prefer structured parsing.

Scope & broadening

A narrow scope is a relevance hint, not a hard filter. When you pass a narrow technical scope, the search runs over the broad technical pool so a wrong category guess can’t hide the right document — and the response sets broadened: true with your original choice preserved in requestedScope. Results may therefore include other document types.

  • answer_question will prefer the requested document type and add a caveat in the answer when it relies on another.
  • press_releases and everything are exact — they are never broadened.

A hasResults: false response means nothing ranked as relevant; it does not prove the document doesn’t exist. Refine the query rather than concluding the corpus lacks coverage.

Progress updates

MCP tool results don’t stream, but answer_question emits coarse notifications/progress while it works — if your call opts in with a progressToken in the request _meta:

"params": {
"name": "answer_question",
"arguments": { "query": "..." },
"_meta": { "progressToken": "t1" }
}

Stages: Searching the document corpus… (10/100) → Retrieved N documents — generating answer… (40/100) → Answer ready. (100/100). SDK clients receive these via the onprogress callback on callTool. Budget a 60-second client-side timeout for answer_question.

Raw JSON-RPC (curl)

For testing or non-SDK clients, call the JSON-RPC API directly. The Accept header must advertise both content types:

curl -sS https://api.pyramid-ai.com/api/v2/mcp \
-H "Authorization: Bearer pai_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_documents",
"arguments": { "query": "bamboo scaffolding safety requirements" }
}
}'

List the available tools with "method": "tools/list".

Troubleshooting

SymptomCause & fix
401 UNAUTHORIZEDMissing/invalid bearer key, or a pai_test_* key against production (and vice versa). Check the key matches the environment.
403 FEATURE_NOT_ENABLEDYour org doesn’t have MCP enabled. Contact your account manager.
406 / transport errorThe Accept header is missing text/event-stream. Add application/json, text/event-stream.
answer_question times outClient timeout is below ~30s. Raise it — generation takes ~20–25s.
Empty results with a scope setSet scope: "everything" to include all document types, or drop the date filters.

Next steps