MCP Integration
Purple8 Hyper Graph v0.72.0 ships a built-in MCP server (HTTP + SSE + Streamable HTTP) exposing 125 MCP tools across 16 namespaces, with Focused MCP Tool Profiles (?profile=starter|builder|ops|full) so AI agents only load the exact tools they need. Any MCP-compatible AI agent — Claude Desktop, Cursor, GitHub Copilot, ChatGPT Desktop, or a custom agent loop — connects directly to the running Purple8 server and gets typed, role-filtered tools.
Focused MCP Tool Profiles (?profile=)
Pass ?profile= on /mcp or /mcp/sse (or send the X-MCP-Profile header) to scope tools/list:
| Profile | Endpoint | Tools | Best For |
|---|---|---|---|
starter | /mcp?profile=starter | 22 | Non-technical operators, Claude Desktop, and rapid prototyping (rag.*, core graph.* CRUD/search/traverse, data.ingest_*, journey.*). |
builder | /mcp?profile=builder | 72 | Cursor, Claude Code, and Copilot app builders (starter + schema.*, memory.*, models.*, egress.*, scheduler.*, feedback.*, registry.*). |
ops | /mcp?profile=ops | 33 | Operations, compliance & governance (journey.*, sentinel.*, guardrails.*, feedback.*, admin.*). |
full | /mcp (or ?profile=full) | 125 | All 16 namespaces including quantum.* and evolution.*. |
Architecture
MCP Client (Claude Desktop / Cursor / ChatGPT Desktop / custom agent)
│ MCP over Streamable HTTP (POST /mcp) ← MCP 1.x clients
│ MCP over HTTP + SSE (GET /mcp/sse) ← legacy clients
▼
http://localhost:8000/mcp ← built into the Purple8 server, no separate process
│ internal call
▼
GraphEngine + JourneyEngine + RAG pipeline + Quantum engine (same process)The MCP server runs as part of purple8_graph.secure_server — no separate process, no separate install. Start Purple8, both MCP endpoints are live.
Hosted on AWS / Fly / Kubernetes? The endpoints are identical — replace
http://localhost:8000with your instance'shttps://URL. Jump to Connecting to a hosted instance.
Quick start
Step 1 — Start the server
export P8G_ADMIN_EMAIL=admin@example.com
export P8G_ADMIN_PASSWORD=changeme
export SECRET_KEY=your-secret-key
export LLM_PROVIDER=openai
export OPENAI_API_KEY=sk-...
python -m purple8_graph.secure_server
# Streamable HTTP: http://localhost:8000/mcp
# SSE (legacy): http://localhost:8000/mcp/sseStep 2 — Create an API key
TOKEN=$(curl -s -X POST http://localhost:8000/auth/token \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"changeme"}' | jq -r .access_token)
curl -X POST http://localhost:8000/auth/api-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"claude-agent","role":"editor"}'
# Returns: { "key": "p8g_...", ... } — store this, shown onceStep 3 — Connect via Claude CLI (recommended)
# Streamable HTTP — MCP 1.x (Claude Desktop 2026+, ChatGPT Desktop)
claude mcp add purple8 --transport http http://localhost:8000/mcp \
--header "X-API-Key: p8g_your-key"
# SSE — legacy clients
claude mcp add purple8 --transport sse http://localhost:8000/mcp/sse \
--header "X-API-Key: p8g_your-key"Step 4 (alternative) — Connect Claude Desktop
claude_desktop_config.json only launches local (stdio) servers — it ignores url. For a local instance use the stdio bridge:
{
"mcpServers": {
"purple8-graph": {
"command": "purple8-hyper-graph",
"args": ["mcp-server", "--url", "http://localhost:8000", "--api-key", "p8g_your-api-key-here"]
}
}
}For a hosted instance, see Connecting to a hosted instance below.
Restart Claude Desktop — Purple8 tools permitted by your role and API key appear in the tool chooser.
Step 4 (alternative) — Connect Cursor
In Cursor settings → MCP → Add server:
{
"purple8-graph": {
"url": "http://localhost:8000/mcp",
"headers": {
"X-API-Key": "p8g_your-api-key-here"
}
}
}Connecting to a hosted instance
The MCP endpoints are part of the Purple8 HTTP server, so they work the same way on AWS, Fly, or Kubernetes as they do on localhost. You don't need to install anything locally. Change the URL to your instance's public HTTPS address and send an API key issued by that instance.
| What | Value |
|---|---|
| Streamable HTTP (preferred, MCP 1.x) | https://<your-host>/mcp |
| HTTP + SSE (legacy clients) | https://<your-host>/mcp/sse |
| Auth header | X-API-Key: p8g_... (or Authorization: Bearer p8g_... on /mcp) |
| Key source | https://<your-host>/lcnc/api-keys, or POST https://<your-host>/auth/api-keys |
<your-host> is your deployment's DNS name (e.g. graph.acme.com, acme-p8g.fly.dev, or your ALB/CloudFront hostname).
Keys are per-instance
An API key from your local instance won't work against the hosted one, and a hosted key won't work locally. The license key and your login password are not MCP keys either.
1. Verify from your machine
HOST=https://graph.acme.com
KEY=p8g_...
curl -s $HOST/health # 200, no auth
curl -s -o /dev/null -w "%{http_code}\n" -X POST $HOST/mcp \
-H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
# 200 = OK · 401 = bad/missing key · 404 = MCP extra not installed on the server2. Configure the client
Claude Code (CLI)
claude mcp add purple8 --transport http https://graph.acme.com/mcp \
--header "X-API-Key: p8g_..."Claude Desktop / claude.ai — use Settings → Connectors → Add custom connector with URL https://graph.acme.com/mcp. Don't add a url entry to claude_desktop_config.json, because that file only supports local commands. If your plan can't add custom headers on connectors, use the bridge described under stdio-only clients below.
VS Code (Copilot agent mode) — .vscode/mcp.json:
{
"servers": {
"purple8": {
"type": "http",
"url": "https://graph.acme.com/mcp",
"headers": { "X-API-Key": "${input:p8gKey}" }
}
},
"inputs": [
{ "id": "p8gKey", "type": "promptString", "description": "Purple8 API key", "password": true }
]
}Cursor — ~/.cursor/mcp.json:
{
"mcpServers": {
"purple8": {
"url": "https://graph.acme.com/mcp",
"headers": { "X-API-Key": "p8g_..." }
}
}
}Stdio-only clients (older Claude Desktop, Windsurf, Cline, and similar) — run the bundled stdio bridge locally and point it at the hosted REST API:
{
"mcpServers": {
"purple8": {
"command": "purple8-hyper-graph",
"args": ["mcp-server", "--url", "https://graph.acme.com", "--api-key", "p8g_..."]
}
}
}The bridge requires pip install 'purple8-hyper-graph[mcp]' on the client machine. All data stays on the hosted instance.
Custom agents (Python MCP SDK)
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async with streamablehttp_client(
"https://graph.acme.com/mcp", headers={"X-API-Key": "p8g_..."}
) as (read, write, _):
async with ClientSession(read, write) as s:
await s.initialize()
tools = await s.list_tools()3. Operator checklist (self-hosted on AWS / Fly / Kubernetes)
| Item | Why |
|---|---|
Install the [mcp] extra in the image | Without it, /mcp and /mcp/sse are not mounted, and the log shows mcp_*_transport_skipped |
| Terminate TLS at the load balancer / ingress | API keys travel in headers. Never expose /mcp over plain HTTP |
Set P8G_PUBLIC_URL=https://<your-host> | Each MCP session sends its tool calls back to this URL. If it isn't set, the server uses the request's base_url, which can be an internal http:// address behind a proxy |
Disable response buffering on the proxy for /mcp* | Both transports stream. In nginx use proxy_buffering off;; the server already sends X-Accel-Buffering: no |
| Raise idle timeouts to ≥ 300 s | Long-running tools (rag.tune_collection, quantum.*) hold the stream open. The AWS ALB default of 60 s drops them |
Use sticky sessions only if clients use /mcp/sse | SSE sends GET /mcp/sse and POST /mcp/messages/, and both must reach the same replica. /mcp (Streamable HTTP) is stateless per request and doesn't need stickiness |
Allow the /mcp paths in your WAF | Some managed rule sets block text/event-stream or JSON-RPC bodies |
RBAC and tenancy behave exactly as they do locally. The API key's role and tenant decide which tools and data the agent can reach.
Tools by namespace (125 tools across 16 namespaces)
| Namespace | Count | What agents can do |
|---|---|---|
graph.* | 28 | labels, query, semantic_query, assert_statement, retract_statement (human_only), import_nquads, vector_search, hybrid_search, traverse, add_node, get_node, update_node, delete_node, add_edge, get_edge, delete_edge, edges_exist, pagerank, communities, centrality, link_prediction, shortest_path, counts, poll_changes, wait_for_changes, auto_config, morans_i, local_morans_i |
journey.* | 9 | define, list, start, advance, status, sla_alerts, list_hitl, resolve_hitl, audit |
schema.* | 12 | get, define_node_type, get_node_type, update_node_type, delete_node_type, list_node_types, define_edge_type, get_edge_type, update_edge_type, delete_edge_type, list_edge_types, validate |
rag.* | 6 | query, graph_query, hybrid_query, profile_collection, tune_collection, rerank |
registry.* | 6 | list_tools, register_tool, register_mcp_server, register_a2a_agent, remove_tool, test_tool |
models.* | 7 | list, search_known, upsert, remove, get_policy, set_policy, route_explain |
egress.* | 4 | add_sink, list_sinks, remove_sink, status |
feedback.* | 4 | open, record, resolve, summary |
scheduler.* | 4 | add_trigger, list_triggers, remove_trigger, status |
data.* | 10 | ingest_text, ingest_file, commit, replace_file, patch_file_properties, list_file_versions, get_table, link_table_rows, index_table_columns, query_table_rows |
memory.* | 2 | history, search |
quantum.* | 8 | anneal, optimize, simulate, encode_state, measure, vqe_step, compare, profile |
admin.* | 10 | smtp_status, smtp_configure (human_only — JWT required), audit_verify, hold_place, hold_release, hold_list, retention_set, retention_list, retention_sweep, retention_delete |
evolution.* | 8 | create_experiment, list_experiments, propose, suggest, report, production, rag_retrieval, routing_policy |
sentinel.* | 5 | summary, list_findings, get_finding, reject, drift_check |
guardrails.* | 1 | evaluate |
Self-Hosted Licensing Tiers: Developer (Self-Hosted Free) includes up to
50Knodes and1concurrent MCP agent. Enterprise / Custom (Self-Hosted) supports unlimited nodes, unlimited MCP agents, and full compliance/HA features.
RBAC per tool
RBAC is enforced per tool call via the role on the API key:
| Role | Access |
|---|---|
viewer | Read-only: graph.* reads, rag.*, memory.*, schema.* reads |
editor | Viewer + writes: graph.* mutations, data.*, journey.start/advance, quantum.*, scheduler.*, egress.*, feedback.* |
admin | Editor + admin: journey.define/resolve_hitl, schema.* mutations, registry.*, admin.smtp_status |
super_admin | All tools |
human_only tools —
admin.smtp_configureand other destructive tools markedhuman_only=Trueare blocked for API-key (agent) callers. A human must authenticate via interactive JWT session to call them.
Authentication
All tool calls require X-API-Key: p8g_<key> or Authorization: Bearer <jwt> in the HTTP headers. API keys are created via POST /auth/api-keys or the LCNC dashboard at /lcnc/api-keys.
Example agent interactions
Query RDF and LPG evidence together
graph.semantic_query accepts the same tagged pattern body as POST /semantic/query. It returns typed bindings, ordered evidence and mixed LPG views; output_format: "nquads" instead returns a JSON envelope with the selected asserted RDF document and limit_reached. No LPG facts are silently converted to RDF.
Existing graph read permissions and row/field/PII/classification policies apply before matching. N-Quads additionally requires data:export; API-key label and tool scopes still constrain access. Raw work statistics are withheld (stats: null) rather than exposing hidden index sizes. See the semantic network contract for the request shape, policy behavior, complete-response limits and errors.
Assert a native RDF fact
graph.assert_statement takes {"statement": {...}} using native RDF term objects. The tenant comes from authentication; blank nodes require a scope. Authorized retries preserve the same canonical assertion and its existing owner. Read/create permissions, whole-assertion visibility and per-label restrictions apply to the canonical record and its projections before mutation.
graph.retract_statement targets exactly one quad and physically removes it; it requires an admin, read/delete permissions and legal-hold clearance. It is human_only, matching the existing destructive tools: AI agent API-key callers cannot retract, even with an admin role. See governed semantic mutations for request/response shapes, restrictions, retry semantics and error behavior.
Import externally produced RDF
graph.import_nquads accepts nquads, a stable blank_node_scope and optional lower max_input_bytes/max_statements bounds. It uses the active REST request schema, full syntax/authorization preflight and the same canonical assertions; DocIntel is not required. Existing data:import plus node/edge read/create permissions and policy/tool scopes apply.
Writes are not atomic across the document. An interrupted import can report an acknowledged prefix while its failing write may also be durable. Retry the entire original document with the same scope and tenant, not a guessed suffix. See bounded N-Quads import for exact caps, response counts, blank-node mapping and recovery behavior.
Build a knowledge graph from documents
User: "Extract these public contract documents for review before adding them"
Agent calls:
data.ingest_file { "url": "https://example.com/contract.pdf", "source_name": "contract.pdf", "auto_commit": false }
# If pending, repeat the call with the returned job_id to resume.
# Review the completed preview, including evidence and warnings.
data.commit { ...reviewed preview... }
# Inspect embedding/document/structured-content/original-file outcomes.data.ingest_file accepts public URLs, not local paths. For local files, upload/extract through REST or docintel.process_file, then use data.commit. DocIntel is optional: data.commit also accepts your own extractor's complete REST ingestion payload. See Bring Your Own Ingestion for review, job resumption, original retention, and complete evidence retrieval.
Answer a relational question
User: "What are the key risks across all projects owned by Alice?"
Agent calls:
rag.hybrid_query {
"question": "risks in projects owned by Alice",
"max_hops": 2,
"seed_k": 10
}
fusion_alphadefaults to0.0(pure vector search) — do not pass a nonzero value unless you've confirmed graph edges carry real semantic relationships (not just ANN-neighbor artifacts). See When to enable graph fusion before settingfusion_alpha > 0. Prefer callingrag.profile_collectionorrag.tune_collectionfirst — they derive a safe, corpus-specific value instead of guessing.
Solve a scheduling optimisation problem
User: "Schedule 8 jobs across 3 machines to minimise total makespan"
Agent calls:
quantum.optimize {
"n": 24,
"Q": [[ ... ]], // n×n QUBO coupling matrix encoding job/machine assignment costs
"domain": "scheduling"
}Define and run a workflow
User: "Set up a loan approval workflow and start one for customer C-001"
Agent calls:
journey.define { "name": "loan_approval", "stages": [...] }
journey.start { "journey_name": "loan_approval", "entity_id": "C-001" }
journey.status { "instance_id": "..." }See Also
- RAG Pipeline — tuning retrieval for your corpus
- Journey Engine — workflows, SLA, and HITL
- Hybrid Search — vector + graph retrieval
- Encryption & KMS — what the agent can and cannot see