Spaces:
Sleeping
Feature Specification: ACE MCP Server (Optional)
Feature Branch: 002-ace-mcp-server
Created: 2026-03-02
Status: Draft
Input: User description: "extend ACE to also work as an MCP server (optional but powerful)"
Summary
Add an optional MCP server mode for ACE using the ace architecture. The server exposes ACE capabilities as MCP tools so external MCP clients (IDEs, copilots, orchestrators) can call ACE for inference and learning.
The feature is opt-in via optional dependency extras and a dedicated CLI entrypoint. Existing ACE APIs and integrations remain unchanged when MCP is not enabled.
Scope
In Scope (MVP)
- MCP server over stdio transport.
- Tool endpoints for ask/learn/skillbook operations.
- Per-session ACE runner lifecycle with in-memory state.
- Input/output validation with Pydantic models.
- Structured error mapping to MCP tool errors.
- Unit + integration tests for handler behavior and transport startup.
- Docs and runnable example client.
Out of Scope (Post-MVP)
- HTTP/SSE MCP transport.
- Distributed or persistent multi-process session stores.
- AuthN/AuthZ and multi-tenant isolation beyond local process boundaries.
- Rich observability dashboards (basic logs only in MVP).
Design Constraints
- Implement in
aceintegration layer, not inpipeline/orace/core/for MVP. - Preserve backward compatibility: no breaking changes to existing runners.
- Keep MCP dependency optional (no mandatory install-time dependency).
- Use existing ACE runner constructors (
from_model,from_roles) and skillbook persistence APIs.
User Scenarios & Testing
User Story 1 — Ask Through MCP (P1)
A client calls ace.ask through MCP and gets an answer from a session-scoped ACE instance.
Independent test: start server, call ace.ask, assert valid answer payload and session state initialization.
User Story 2 — Learn Through MCP (P1)
A client calls ace.learn.sample and ace.learn.feedback; ACE updates the skillbook for that session.
Independent test: call learning tools, then ace.skillbook.get, assert strategy count changed.
User Story 3 — Persist/Restore Skillbook (P2)
A client saves skillbook to disk and later reloads it into the same/new session.
Independent test: save then load and assert stable strategy IDs/count.
User Story 4 — Safe Optionality (P1)
Projects without MCP extras continue functioning exactly as before.
Independent test: import and use non-MCP modules without MCP installed.
Functional Requirements
- FR-001: System MUST provide a CLI entrypoint to run ACE as an MCP server over stdio.
- FR-002: System MUST expose MCP tools:
ace.ask,ace.learn.sample,ace.learn.feedback,ace.skillbook.get,ace.skillbook.save,ace.skillbook.load. - FR-003: System MUST validate every tool request and response via typed schemas.
- FR-004: System MUST isolate state by
session_id. - FR-005: System MUST lazily initialize session runners with model/provider config.
- FR-006: System MUST support disabling mutating tools (
save/loadand learn tools) in safe mode. - FR-007: System MUST map internal exceptions to stable MCP error codes/messages.
- FR-008: System MUST cap request sizes (max samples per call, max payload size) to prevent runaway execution.
- FR-009: System MUST remain fully optional via
mcpextra dependency. - FR-010: System MUST include handler-level success tests for each tool and server startup/registration smoke tests when the
mcpextra is installed.
Non-Functional Requirements
- NFR-001: Tool roundtrip latency for
ace.askshould be < 2s p50 excluding model latency. - NFR-002: Session operations must be thread-safe within a process.
- NFR-003: Server startup failures must emit actionable install/config error text.
- NFR-004: Existing test suite behavior remains unchanged when MCP extras are absent.
Tool Contract Source of Truth
Canonical tool schemas are defined in: specs/002-ace-mcp-server/contracts/tool-schemas.md.
Exact Module Tree (Implementation)
ace/
integrations/
mcp/
__init__.py
server.py # create_server(), main(), startup wiring
registry.py # session registry + lifecycle (TTL, lazy init)
config.py # MCPServerConfig, limits, safe-mode flags
errors.py # domain errors + MCP error mapping
models.py # Pydantic request/response schemas
handlers.py # tool handler implementations
adapters.py # maps handlers <-> MCP SDK tool registration
tests/
test_ace_mcp_models.py # schema validation and serialization
test_ace_mcp_registry.py # session lifecycle and locking
test_ace_mcp_handlers.py # tool behavior with mocked runner
test_ace_mcp_server.py # server startup + tool registration smoke
docs/
integrations/
mcp.md # install, run, tool catalog, examples
examples/
ace/
mcp_client_demo.py # minimal MCP client invoking ace.ask
Package and CLI Changes
pyproject.toml- Add optional dependency group:
mcp = ["mcp>=<pinned_version>"](or official Python MCP SDK package used by maintainers)
- Add script entrypoint:
ace-mcp = "ace.integrations.mcp.server:main"
- Add optional dependency group:
Runtime Architecture
- MCP server starts and registers tool definitions from
models.py. - Incoming request is validated into typed request model.
registry.pyreturns/create session runner (ACELiteLLMby default).handlers.pyexecutes operation against runner.- Response serialized to typed output model.
- Exceptions mapped through
errors.pyinto stable MCP error responses.
Session Model
- Key:
session_id: str. - Value: session object containing runner, creation time, last access time, lock.
- Concurrency: per-session lock around mutating operations.
- Expiry: configurable TTL cleanup (lazy sweep on access in MVP).
Configuration Model
MCPServerConfig fields (MVP):
default_model: str = "gpt-4o-mini"safe_mode: bool = falsemax_samples_per_call: int = 25max_prompt_chars: int = 100_000session_ttl_seconds: int = 3600allow_save_load: bool = truelearn_timeout_seconds: int = 300skillbook_root: str | None = nulllog_level: str = "INFO"
Environment variable mapping (MVP):
ACE_MCP_DEFAULT_MODELACE_MCP_SAFE_MODEACE_MCP_MAX_SAMPLES_PER_CALLACE_MCP_MAX_PROMPT_CHARSACE_MCP_SESSION_TTL_SECONDSACE_MCP_ALLOW_SAVE_LOADACE_MCP_LEARN_TIMEOUT_SECONDSACE_MCP_SKILLBOOK_ROOT
Error Taxonomy
ACE_MCP_VALIDATION_ERRORACE_MCP_SESSION_NOT_FOUNDACE_MCP_FORBIDDEN_IN_SAFE_MODE— mutating tool called withsafe_mode=trueACE_MCP_SAVE_LOAD_DISABLED— save/load tool called withallow_save_load=false(independent of safe mode)ACE_MCP_PROVIDER_ERRORACE_MCP_TIMEOUTACE_MCP_INTERNAL_ERROR
Each error must include:
code(stable string)message(human-readable)details(optional structured dict)
Design Notes
ace.ask response
The applied_skill_ids field was removed from the ace.ask response. The underlying ACELiteLLM.ask() does not track which skills were applied during generation, so the field was always empty. Removed to avoid a misleading contract.
ace.learn.feedback response
The learned field reports True when the feedback handler successfully executed the learning path (either direct feedback or trace-based fallback), regardless of whether new skills were created. This avoids false negatives when the reflector modifies existing skills without adding new ones.
ace.learn.feedback fallback trace
When no prior ask() exists for the session, the handler builds a synthetic trace. The context field maps to context (not reasoning) in the trace dict, since context is background information and reasoning is the agent's chain of thought.
MCP learning limitations
Learning through MCP (ace.learn.sample) does not provide a task environment. The pipeline's EvaluateStep uses ground_truth comparison only. Environment-based evaluation is not available through MCP in the MVP.
Learn timeout
ace.learn.sample and ace.learn.feedback wrap their runner calls in asyncio.wait_for with learn_timeout_seconds (default 300s). When the timeout fires, the handler raises ACE_MCP_TIMEOUT instead of ACE_MCP_INTERNAL_ERROR.
Prompt limit fields
The per-tool fields included in the max_prompt_chars check:
ace.ask:question + contextace.learn.sample:question + contextper sample (0-indexed in error messages)ace.learn.feedback:question + context + answer + feedback + ground_truth
Save/load path resolution
ace.skillbook.save and ace.skillbook.load resolve the user-provided path to an absolute canonical path (via Path.resolve()) before validation and before passing it to the runner. The response echoes the resolved path, not the raw input. This eliminates TOCTOU races with symlinks or .. path components.
Security & Safety
- Safe mode blocks mutating operations by policy.
- Path validation for save/load (reject paths outside
ACE_MCP_SKILLBOOK_ROOTwhen set). Validation operates on the resolved canonical path. - Request size limits enforced before runner call.
- No secret values logged in payload dumps.
Testing Plan
Unit
models.py: required fields, type coercion, limits.registry.py: session create/get/delete, TTL expiry, lock semantics.handlers.py: tool success paths + mapped failures.
Integration
- Boot server with MCP SDK test harness when the
mcpextra is installed. - Validate tool registration/startup via smoke tests and cover tool success paths at the handler layer.
- Validate safe mode blocks expected tools.
Regression
- Run existing
acetests ensuring no MCP dependency required unless explicitly installed.
Delivery Plan (PR Sequence)
- PR-1: skeleton package + config + models + tests.
- PR-2: session registry + handlers for
ace.askandace.skillbook.get. - PR-3: learning + save/load handlers, error mapping, limits.
- PR-4: server bootstrap + CLI entrypoint + integration tests.
- PR-5: docs + example client + changelog entry.
Acceptance Criteria
- All implemented FR/NFR remain consistent with the optional dependency boundary.
- Tool schemas match contract doc exactly.
ace-mcpstarts successfully and serves all MVP tools.- Existing non-MCP workflows are unaffected.
Ready for Merge Checklist
- Module tree implemented under
ace/integrations/mcp/ - Tool schemas implemented and contract-aligned (
ace.ask,ace.learn.sample,ace.learn.feedback,ace.skillbook.get/save/load) - Optional dependency and CLI entrypoint wired (
mcpextra,ace-mcp) - Safe mode policy enforced for mutating tools
- Request-size limits enforced (
max_prompt_chars,max_samples_per_call) - Optional root-bound path validation enforced for save/load (
ACE_MCP_SKILLBOOK_ROOT) - MCP-focused tests passing (
test_ace_mcp_models/registry/handlers/server) - Optional dependency boundary enforced with actionable install errors
- Docs + example client updated (
docs/integrations/mcp.md,examples/ace/mcp_client_demo.py) - Changelog updated for this feature