1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! The ASSISTANT-ONLY MCP surface: `/assistant/mcp`.
//!
//! A second MCP route beside the general `/mcp`, on the same listener and the
//! same protocol layer, with its OWN catalogue and its own credential. It
//! exists because its tools — the editing loop: `assistant_context`,
//! `assistant_document_edit`, `assistant_document_check` — are each about one
//! conversation and belong to nobody else.
//!
//! # Why they are not tools on the general catalogue
//!
//! The general catalogue is what any authorized caller sees listed, and its
//! tools are addressed by namespace. The session tools are addressed by
//! SESSION, and the only credential that can name a session is the bearer this
//! server minted for that session's own agent. Adding them to the general
//! catalogue would mean either publishing tools most callers cannot use, or
//! narrowing the catalogue per caller — and a catalogue that narrows is a
//! catalogue whose narrowing has to be got right on every request. Two routes
//! with two catalogues cannot be got wrong: the general dispatcher refuses
//! each session tool by NAME because the name is not in its catalogue at all.
//!
//! # Its only credential is a session bearer
//!
//! [`caller`] resolves `authorization: Bearer …` plus
//! `x-aion-assistant-session: <id>` into an [`AssistantSessionCaller`], and does
//! it by reading the STORE: the record's stored digest, and the session's
//! projected lifecycle. A human's token is refused because it is not a session
//! bearer; a session bearer presented against another session's id is refused
//! because the digest belongs to the other record; and a token whose session has
//! ENDED is refused because the projection says so — which is why revocation is
//! exactly as durable as the session and survives the restart of the process
//! that minted the token.
//!
//! - [`caller`] — the session-bearer resolution and its typed refusals.
//! - [`catalog`] — the three-tool catalogue.
//! - [`context_tool`] — `assistant_context` itself.
//! - [`document_edit_tool`] — `assistant_document_edit`.
//! - [`document_check_tool`] — `assistant_document_check`.
//! - [`route`] — the axum mount.
//! - [`runtime`] — the protocol server constructed at startup.
pub
pub
pub
pub
pub
pub
pub
pub
pub use SESSION_TOOL_NAMES;
/// The tools this route publishes. Exported so the descriptor, the doc pins
/// and the general catalogue's separation pin all name them from one place.
pub use ;
/// The route the assistant's own catalogue is served on. Exported for the same
/// reason: the descriptor publishes it and the docs quote it.
pub use ASSISTANT_MCP_PATH;
pub use assistant_mcp_router;
pub use AssistantMcpRuntime;
/// One sentence describing the credential this route accepts.
///
/// THE source: the descriptor publishes it, `docs/operations/API.md` quotes it,
/// and `token.rs` points at it. A second sentence written beside one of those
/// would be a second description of one credential, and the two would drift.
pub const SESSION_TOKEN_DESCRIPTION: &str = "server-minted and session-scoped: this server mints one bearer per assistant session, hands \
it only to that session's own agent process, stores only its digest, and stops honouring it \
the moment the session's projected lifecycle reads `ended`. It authorizes exactly one \
session's context and nothing else — not a namespace, not a workflow, and not the operator \
who opened the session.";
/// The wire word for the credential kind, for a client that branches on it.
pub const SESSION_TOKEN_KIND: &str = "assistant_session_bearer";