Developer docs / Integration guide

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.