Skip to content

Release: MCP 2025-11-25 compliance, elicitation, and Cursor decoupling#1189

Merged
cgcardona merged 10 commits into
mainfrom
dev
Mar 17, 2026
Merged

Release: MCP 2025-11-25 compliance, elicitation, and Cursor decoupling#1189
cgcardona merged 10 commits into
mainfrom
dev

Conversation

@cgcardona

Copy link
Copy Markdown
Owner

Summary

Test plan

  • mypy agentception/ tests/ — zero errors
  • typing_audit.py --max-any 0 — passes
  • pytest — all tests green
  • npx tsc --noEmit — zero errors
  • generate.py --check — no drift
  • npm run build — bundles built

AgentCeption Bot and others added 10 commits March 16, 2026 16:17
…download in CI

find on a non-existent subdirectory exits 1, which aborts the entrypoint under
set -euo pipefail before mypy/pytest ever run. Fix: search from the base
fastembed dir (always present after mkdir -p) with -path filters.

Also skip the model download entirely in CI (GitHub Actions sets CI=true) — the
ephemeral runner has no persistent cache and models are not needed for mypy or
pytest.
fix: entrypoint find never fails under set -euo pipefail; skip model download in CI
- Bump _MCP_PROTOCOL_VERSION from 2025-03-26 to 2025-11-25
- Add description field to McpServerInfo (optional per new spec)
- Add icon: NotRequired[str] to ACToolDef, ACResourceDef, ACResourceTemplate,
  and ACPromptDef (icon metadata support per new spec)
- HTTP endpoint: validate Origin header → 403 for non-localhost origins
  (DNS rebinding protection, now a MUST per 2025-11-25 spec)
- HTTP endpoint: validate MCP-Protocol-Version header → 400 for unsupported
  versions; absent header accepted for backwards compat (assume 2025-03-26)
- HTTP endpoint: add GET /api/mcp → 405 so 2025-11-25 clients correctly
  identify this as Streamable HTTP (not the deprecated 2024-11-05 SSE transport)
- Add 12 new tests covering protocol version, server description, Origin
  security, MCP-Protocol-Version header, and GET transport disambiguation

Elicitation, sampling, roots, and experimental tasks are intentionally
omitted — they are not applicable to this server's use case.
feat(mcp): upgrade to MCP 2025-11-25 spec compliance
- Replace "Cursor-free agent loop" with "agent loop" throughout Python
  docstrings and comments (agent_loop.py, agent_run.py, config.py,
  tools/__init__.py, github_mcp_client.py, models/__init__.py)
- Delete docs/cursor-agent-spawning.md — describes Cursor's Task tool,
  which AgentCeption does not use; already marked legacy
- Update docs/architecture.md: remove "Browser / Cursor MCP", remove
  "Cursor-free" section headers, fix mcp/ package description
- Update docs/guides/mcp.md: update transport table language, bump spec
  version ref to 2025-11-25, add HTTP security section (Origin +
  MCP-Protocol-Version requirements from previous PR)
- Update docs/guides/integrate.md: remove "Cursor, Claude, and any
  MCP-compatible client" → "any MCP-compatible client"
- Update docs/reference/mcp.md: bump protocol version, add HTTP
  endpoints table and security notes
- stdio example config now says "e.g. Cursor" rather than implying
  Cursor is required; transport table updated for neutrality

LLM-agnostic backend has been in place for months; these were
leftover mentions from before that was true.
chore: remove Cursor domain bleed from code, comments, and docs
Implement the full elicitation protocol from MCP 2025-11-25, making
AgentCeption a best-in-class showcase for server-initiated human input
during live agent swarms.

## Backend

agentception/mcp/sessions.py (new)
  - McpSession dataclass: session_id, elicitation_form/url flags,
    asyncio outbound queue, pending Future dict
  - McpSessionStore: create/get/delete/update_capabilities,
    elicitation_sessions(mode), resolve_response
  - Module-level singleton via get_store()

agentception/mcp/elicitation.py (new)
  - _build_json_schema: converts ElicitationField list → MCP JSON Schema
  - send_form_elicitation: puts elicitation/create in outbound queue,
    awaits asyncio.Future with timeout
  - request_human_input: MCP tool implementation — finds first form-capable
    session, returns accept/decline/cancel/timeout/no_client actions

agentception/mcp/types.py
  - ElicitationField, ElicitationResult — typed field spec and result
  - ClientElicitationFormCapability, ClientElicitationUrlCapability,
    ClientElicitationCapabilities, ClientCapabilities — initialize parsing

agentception/mcp/server.py
  - request_human_input registered in TOOLS list (with full JSON Schema)
  - call_tool_async: validates + dispatches request_human_input
  - call_tool: adds request_human_input to async-redirect list

agentception/routes/api/mcp.py (rewritten)
  - GET /api/mcp: SSE stream for sessions with Accept: text/event-stream
    + MCP-Session-Id; 405 without Accept; 403 on evil Origin; 400/404 on
    bad session
  - DELETE /api/mcp: terminates session, cancels pending futures
  - POST /api/mcp on initialize: creates session, returns MCP-Session-Id
    header, parses elicitation capabilities
  - POST /api/mcp with JSON-RPC response (has id, no method): routes to
    resolve_response → unblocks waiting request_human_input call
  - SSE keepalive pings every 15s to prevent proxy timeouts

## Frontend

agentception/static/js/mcp_session.ts (new)
  - initMcpSession: POST initialize with elicitation.form capability
  - openSseStream: fetch-based SSE with exponential backoff reconnect
  - sendMcpResponse: POST JSON-RPC response back to server
  - terminateMcpSession: DELETE on pagehide
  - registerMcpHandler: route server-initiated methods to handlers

agentception/static/js/elicitation_modal.ts (new)
  - elicitationModal Alpine component: renders form from JSON Schema,
    validates required fields, submits via sendMcpResponse
  - dispatchElicitationEvent: window event bridge from SSE → Alpine

agentception/static/js/app.ts
  - Imports mcp_session.ts and elicitation_modal.ts
  - Registers elicitation/create handler → dispatches window event
  - Calls initMcpSession() and registers terminateMcpSession on pagehide

agentception/templates/base.html
  - Adds global elicitation modal HTML (present on every page)
  - Uses Alpine elicitationModal() + @mcp:elicitation.window event

agentception/static/scss/_components.scss
  - .elicitation-* styles: modal, form fields, labels, validation,
    decline/submit buttons — matches existing design system

## Tests

agentception/tests/test_mcp_elicitation.py (new, 71 tests → 71 pass)
  - McpSessionStore: CRUD, capability lookup, resolve_response
  - _build_json_schema: all field types + constraints
  - send_form_elicitation: round-trip, decline, timeout
  - request_human_input: no_client, accept, timeout, JSON serialisation

agentception/tests/test_mcp_http.py
  - TestMcpHttpSession: MCP-Session-Id header, elicitation capability
    session creation, DELETE, RPC response routing
  - TestMcpHttpTransportDisambiguation: renamed test to reflect that
    GET with evil Origin → 403; added SSE tests (400, 404 cases)
  - TestMcpHttpElicitationTool: tools/list includes request_human_input,
    no_client path when store is empty

## Docs

docs/guides/mcp.md
  - HTTP endpoint table (POST/GET/DELETE with purposes)
  - "Elicitation — human-in-the-loop for running agents" section:
    flow diagram, session lifecycle, tool usage, return values, when
    to use it, no-client graceful degradation

mypy: 0 errors (309 files)
tsc: 0 errors
typing_audit: 0 violations (max-any 0)
71 tests, all pass
feat: MCP 2025-11-25 elicitation — human-in-the-loop for running agents
Completes the domain decoupling started in earlier PRs. Every instance of
"Cursor" that implied the IDE was required or was the only way to work has
been replaced with generic language (interactive session, agent session,
server-side agent loop, MCP client, etc.).

Changes by category:

Source code:
- code_indexer.py: docstring no longer frames as "Cursor-free" or "replaces @codebase"
- task_runner.py: "(Cursor, Anthropic, etc.)" → "(Anthropic, local models, etc.)"
- config.py: ".cursor/ which belongs to the IDE" → "IDE config directory"
- smoke_test_agent_loop.py / debug_loop.py: remove "Cursor-free" from print/docstring
- test_agentception_pipeline_config.py: rename cursor_dir → config_dir

Templates:
- overview.html: "open in Cursor" → "open the worktree" (two instances)

Agent contracts:
- AGENTS.md: "Cursor sessions" → "interactive development sessions"
- .agentception/agent-engineer.md: "Cursor worktree/composer" → "git worktree/agent session"
- .agentception/agent-conductor.md: "Cursor composer window" → "agent session"
- .agentception/pipeline-howto.md: "Open two Cursor composer windows" → Task tool / agent sessions
- .agentception/agent-command-policy.md: clarify section heading as "(Cursor as MCP client)"

Generated role template:
- scripts/gen_prompts/templates/roles/architect.md.j2: "Cursor/Claude tool integration" → "MCP client integration"
- .agentception/roles/architect.md: regenerated

Documentation:
- docs/README.md: "Cursor-Free Agent Loop" → "AgentCeption Agent Loop"; generalize MCP section
- README.md: "MCP Integration (Cursor / Claude)" → "MCP Integration"
- docs/guides/agent-loop.md: rename guide, replace "What Cursor provided" table with "Built-In Capabilities"
- docs/guides/security.md: generalize stdio section heading and "Cursor-free loop" references
- docs/guides/setup.md: "Cursor-free agent loop" → "AgentCeption agent loop" (index section)
- docs/guides/ci.md / dispatcher-walkthrough.md / mcp.md: keep Cursor as explicit "e.g." example
- docs/architecture.md / cognitive-arch-audit.md / agent-tree-protocol.md: clean IDE refs
- docs/reference/api.md: "Cursor-free dispatch" → "server-side dispatch"; "Cursor agent" → "planning agent"
- docs/reference/type-contracts.md: "Cursor transcript", "CursorTaskRunner", "@codebase" references removed
- docs/architecture/llm-provider-abstraction.md: "Cursor-free runs" → "server-side agent runs"
- docs/plan-spec.md: "running in Cursor via MCP" → "running via MCP"
- docs/plans/agent-speedup.md: "Cursor solves" → "IDE agents solve"

Deleted:
- docs/reference/cursor-decoupling-taxonomy.md: work-tracking doc for completed decoupling effort

Kept intentionally:
- .cursorrules (Cursor IDE config file)
- docs/guides/dispatcher-walkthrough.md (Cursor-as-MCP-client example guide, already scoped)
- docs/guides/setup.md Step 8/N (Cursor MCP setup, explicitly framed as "e.g. Cursor")
- .agentception/agent-command-policy.md body (already prefixed with "When using Cursor as the MCP client")
- CSS cursor: pointer, data cursors (api.md, sql.yaml), UI blinking cursor (thought_block.ts)
Remove IDE-specific Cursor references throughout the codebase
@cgcardona cgcardona merged commit c9726ab into main Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant