feat: sturdier session handles — issue IDs first, structured mcp_session status - #53
Merged
Conversation
naji247
force-pushed
the
feat/session-copy-mcp-session
branch
2 times, most recently
from
August 27, 2026 20:04
2e6fee7 to
bb1bf52
Compare
…ion status Session IDs issued at the tail of long tool results can be lost to client-side truncation, so issuance now moves to the top of the response: the mint-back text block leads as the first content element, and the mcp_session mirror is inserted as the first key of structuredContent. The agent-facing copy is rewritten leaner and self-describing, with every response state pre-announced in the schema: - The structured mirror key is renamed _mcp_instructions -> mcp_session, and its prose instructions member is replaced by a machine-readable status enum (issued | active | unrecognized), present in prompted mode only; hook mode and customer-owned session parameters mirror agent_id alone. - New pre-announced text-block headers ([session_id issued ...] / [session_id unrecognized ...]) with a bare "session_id: <id>" line. - One agent_id description now serves both modes; hook mode gains its own mcp_session field description. - The session_id and agent_id parameter descriptions explain the handles in their own terms and name every place an ID can come back. - session_id and agent_id are now required, with an explicit start value and a validation pattern on session_id — every call states its session explicitly and sessions never split by accident. Servers still accept calls that omit the parameters (treated as the start of a new task), so requiredness is enforced only by schema-aware clients. Breaking wire changes, stated for reviewers: the structuredContent mirror key is renamed, the instructions member is removed, and the text block moved from last to first content element. Version 2.1.0.
naji247
force-pushed
the
feat/session-copy-mcp-session
branch
from
August 27, 2026 21:26
bb1bf52 to
23647eb
Compare
… expect session_id required handles.py now prepends the session-id mint-back block, and injection.py requires session_id. test_inner_tap.py followed; these three files are _LEGACY_ONLY (conftest) so the mcp 2.x dev env never ran them.
mcp 2.1 wraps a tool crash in UnexpectedToolError("Error executing tool
<name>") and keeps the crash's text off the wire; every earlier generation
put ": <text>" after the prefix. The cause still carries it, so
InnerTap.error() appends it to the event's message — matched by class name,
skipped when already present (nested crashes) — and error.message reads the
same on every generation. The wire is untouched.
Tests: MCPSERVER_CRASH_WRAPPER / MCPSERVER_CRASH_TEXT_ON_WIRE probes in
test_utils; TestOfficialV2 asserts the wrapper name via the probe and gates
only the wire check; two new tests pin the parity and no-duplication.
fastmcp < 3.4 has no ToolResult.is_error, so an error result gets no mint-back in front; >= 3.4 does. The backend's own error block is last on every version, so index it from the end instead of assuming the mint-back.
naji247
force-pushed
the
feat/session-copy-mcp-session
branch
from
August 31, 2026 17:15
2c9b9d9 to
cce91c7
Compare
kashishhora
approved these changes
Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Session handles are easy for callers to miss when they arrive at the tail of a long result — clients that truncate large tool outputs can drop the issuance notice entirely, and agents then start new sessions instead of continuing the one they were given. This change makes the handle mechanism sturdier and easier for agents to consume:
session_idandagent_idare now required parameters with an explicit value contract.session_idtakes one of two values — theses_ID issued for the task, or the literalstartto begin a new one — declared with a validation pattern in the schema (^(start|ses_[0-9A-Za-z]{27})$). Every call states its session explicitly, so sessions never split by accident. Servers keep accepting calls that omit the parameters (treated as the start of a new task), so requiredness is enforced only by schema-aware clients and nothing existing breaks server-side.mcp_sessionreplaces_mcp_instructionsas the structured mirror instructuredContent, declared in each tool's output schema. It carries a machine-readablestatusenum (issued/active/unrecognized) instead of prose — every response state an agent can encounter is pre-announced in the schema.session_idandagent_iddescriptions now explain the mechanism in terms of what this server does, pre-announce both text-block headers so responses match the schema's stated contract, and collapse the twoagent_iddescription variants into one.mcp_session.status: "unrecognized"and as a leading text block, with the recovery path (re-send the earlier ID, or sendstartif none was issued) stated in the schema.Breaking wire changes (for consumers that assert on response internals or schemas): the
structuredContentmirror key is renamed_mcp_instructions→mcp_session; itsinstructionsstring member is removed in favor ofstatus; the issuance text block moved from the last to the first content position and its header text changed ([session_id issued …]/[session_id unrecognized …]);session_idandagent_idnow appear in each tool'srequiredarray, andsession_iddeclares the validation pattern above.Version bumped to 2.1.0.
Testing
uv run --no-sync pytest: 815 passed / 26 skippeduvx ruff check .,uvx ruff format --check .,uvx mypy src/agentcat: no new findings vs main (pre-existing repo-wide debt unchanged; all touched files clean)Cross-SDK note: this lands the same strings and wire shape in all three SDKs, byte-identical where applicable. Sibling PRs: agentcathq/agentcat-typescript-sdk#67 · agentcathq/agentcat-go-sdk#13