Integrating with the GPTree Knowledge API
Everything an engineer needs to get their own content into the engine and their own agent talking to it, plus an honest account of what has and has not been verified.
The endpoint reference lives in the Knowledge API reference. This document is the shape of a working integration around it.
There are two ways in, and they answer different questions.
| you want to | use |
|---|---|
| get your documents in, from your own sync | the REST ingest contract, and gptree_sync.py |
| let your AI query them | the MCP server, and mcp_session.py |
Files here
| file | what it is |
|---|---|
gptree_sync.py |
Reference sync client. Standard library only. Upsert, idempotent retry, delete, reconcile, job polling, ask, evidence. |
worked-example.md |
A transcript of a full integration, generated from real responses and re-checked on every test run. |
mcp_session.py |
A real MCP session over the wire. On protocol 2026-07-28 by default: server/discover, tools/list, tools/call, each request standing alone. Falls back to 2025-06-18 (initialize, notifications/initialized, tools/list, tools/call) against a server that does not speak it, or when GPTREE_MCP_PROTOCOL=2025-06-18. |
claude_desktop_config.json |
Claude Desktop configuration for the laptop client shape, with the token kept out of the process list. |
All four are served alongside this page: gptree_sync.py, mcp_session.py and
claude_desktop_config.json are linked from the developer documentation index.
Start here
export GPTREE_TOKEN="..." # a token holding kee:ingest and kee:read
export GPTREE_BASE="https://gp-tree.com"
python3 gptree_sync.py demo
The demo upserts a document, retries the same delivery, sends an edit, lists
what GPTree holds, and deletes. Then read worked-example.md. It covers that
ground with the responses in full, and goes further than the demo: a queued
ingest and its job, a cited answer with its judgment, citation resolution, and
a refusal.
The four things a sync has to get right
1. Send an Idempotency-Key on every write. It is your name for a delivery.
The response tells you idempotent_replay and event_sequence, which is how a
retry after a timeout becomes answerable instead of a guess. Derive the key from
your document id plus its revision, never from the body: content hashing is what
the engine already does one level down, and it cannot tell a retry from an edit
that happened to produce the same bytes.
2. Deletion is explicit. A document you stop sending is not deleted. "Not in
this batch" is also what a partial sync, a filtered query and a crashed job look
like, so the engine will not infer removal from absence. Your sync notices the
removal and calls DELETE. The cost is real; the benefit is that one bad run
cannot erase a corpus.
3. Large documents go to a queue. Bodies past the size ceiling and every
file upload return 202 with a job_id. Poll it. Terminal states are
succeeded and failed, and failed always carries a reason.
4. Read the judgment, not just the answer. answer: null with a
refusal_reason means the corpus could not support an answer, and
verification_status then says which kind: no_evidence (nothing usable
matched) or uncited (the model answered without a real citation). That is a
correct outcome, not an error. corpus_coverage tells you how much was
actually read, with a denominator, and known_gaps names what was left out.
MCP scopes
MCP credentials use the finer scope vocabulary, not the coarser REST abilities, because the difference is meaningful.
| MCP scope | REST ability it also grants |
|---|---|
search:read |
kee:read |
evidence:read |
kee:read |
ask:execute |
kee:ask |
admin:ingest |
kee:ingest |
investigation:create |
kee:investigate |
Three of those carry a warning worth reading twice. search:read does not
include reading passages back. ask:execute spends the account owner's credits
and is rate limited far more tightly than the read endpoints. admin:ingest
covers polling a job, not pushing a document: content goes in over REST.
A client only ever sees the tools its credential can use: a model that can see a tool will try it, and a plan built around a tool it may not call fails late.
Credentials
A server-side agent keeps a long-lived scoped API token. It lives somewhere you control and rotate.
A laptop client gets a short-lived pair, because a personal machine gets lost, shared, backed up somewhere nobody can see, and has its config pasted into support tickets.
GET /api/account/mcp-sessions your live sessions (metadata only)
POST /api/account/mcp-sessions -> access token (1 hour, holds the scopes)
refresh token (up to 30 days, holds nothing else)
POST /api/kee/v1/mcp/token -> a fresh access token
DELETE /api/account/mcp-sessions/{id} revokes the session AND everything it minted
Both are managed from Account, on the Developer tab. The refresh token's life is capped by the credential that authorised it, so a session started from the web app inherits that session's own expiry rather than outliving it. The id in the list and the delete is the refresh token's: revoking an access token would leave the session alive to mint a replacement.
The access token is not refreshed for you. Exchange the refresh token at
POST /api/kee/v1/mcp/token before the hour is up, or when a call returns
401. A client that sends a fixed header, such as Claude Desktop through
mcp-remote, needs the new access token put in place by hand or by a script.
The refresh token cannot search, cannot read a passage, cannot ask a question and cannot ingest. That asymmetry is what makes a thirty-day credential safe to leave on a laptop, and it is the reason a session is started by a signed-in person rather than by another token.
What has been verified, and what has not
Stated plainly, because a demo that overstates what it proves is worse than no demo.
Verified by the test suite, end to end against the real endpoints: ingest including delivery identity and replay, the explicit delete, background jobs and their terminal states, file upload and extraction, the MCP handshake, scoped tool listing, scope enforcement on every tool, short-lived token expiry, refresh that cannot widen its own grant, session revocation, citation resolution against stored bytes, and the refusal path.
Verified against real Postgres: the query planner's strategy selection, scored at 1.0 on the committed coverage corpus, negative controls included.
Read that list before the next one. Everything a working integration depends on is in it: pushing documents, deleting them, polling jobs, the MCP protocol conversation, scope enforcement, and citations that resolve against stored bytes. The gaps below are narrower than they look.
Both MCP protocol revisions are supported: 2026-07-28, the stateless one
without an initialize handshake, and 2025-06-18. Each request picks its own,
so existing 2025-06-18 clients keep working unchanged. The test suite covers
both, including header validation and version negotiation for 2026-07-28.
NOT yet verified: a session driven by Claude Desktop specifically. The
protocol conversation is exercised by mcp_session.py and by the test suite, and
claude_desktop_config.json is the configuration to run it, but nobody has sat
in front of Claude Desktop and completed a session yet. That is the remaining
step for the laptop client shape, and it is listed here rather than quietly
counted as done.
Also not verified: answer quality. The eval measures which strategy the engine picks, which needs no model call. Whether the resulting answers are correct needs a model-backed run and is deliberately unscored rather than guessed at.
This page as raw markdown: /docs/integration-guide.md. Everything at once: /docs/all.md. New to GPTree? Start with Learn.