feat: Build an MCP server for cao operations#166
Open
patricka3125 wants to merge 21 commits intoawslabs:mainfrom
Open
feat: Build an MCP server for cao operations#166patricka3125 wants to merge 21 commits intoawslabs:mainfrom
patricka3125 wants to merge 21 commits intoawslabs:mainfrom
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #166 +/- ##
=======================================
Coverage ? 90.94%
=======================================
Files ? 50
Lines ? 4076
Branches ? 0
=======================================
Hits ? 3707
Misses ? 369
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Add tests for invalid session-create responses, prompt delivery failures, and rejected non-.md install sources so the corresponding branches in the ops MCP server and install service are exercised. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the hard-coded 120s terminal ready timeout into a shared TERMINAL_READY_TIMEOUT constant so both cao-mcp-server handoff and cao-ops-mcp launch consume the same value, and narrow the broad except clauses in install_agent and the ops MCP _request_json helper so real bugs propagate instead of being flattened into error strings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap discover_profiles and list_sessions in ProfileListResult and SessionListResult envelopes so every tool returns a consistent shape with a success flag instead of mixing list and error dict payloads. Rewrite the tool docstrings in cao-mcp style and document the install_profile source-resolution order, including the CWD-collision gotcha where a bare agent name matching a file in the working directory routes to path resolution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shorten TERMINAL_READY_TIMEOUT from 120s to 30s since it acts only as a fallback after the provider's own initialize() hook has already run. Rename the discover_profiles tool to list_profiles so the name matches its sibling list_sessions and more accurately describes the operation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the prompt parameter from launch_session so it returns session
identifiers immediately without waiting. Add send_session_message which
delivers messages via the inbox API (POST /terminals/{id}/inbox/messages)
using sender_id="cao-ops-mcp", consistent with how the cao-mcp server
delivers messages but without requiring a CAO_TERMINAL_ID env var.
Add SendMessageResult model and 4 new tests for the new tool. Remove 3
now-irrelevant prompt-flow tests from the launch path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These changes were out of scope for the ops MCP PR. Remove the constant from constants.py and restore the hardcoded 120.0 timeout in mcp_server server.py to match the original upstream state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents the new cao-ops-mcp server — setup, available tools, and typical workflow — alongside the distinction from cao-mcp-server. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…y revert Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Upstream added compose_agent_prompt baking for Q/Copilot and skill:// resource support for Kiro during install. The merge conflict resolution kept the feature branch's service-layer thin wrapper but left those behaviours out of install_service.py. - Q: use compose_agent_prompt(profile) to bake skill catalog into prompt - Kiro: add skill://<SKILLS_DIR>/**/SKILL.md resource; use raw prompt (None when empty) so Kiro's native progressive loader handles skills - Copilot: use compose_agent_prompt(profile, base_prompt=...) to bake catalog into the .agent.md body TestInstallSkillCatalogBaking ported from upstream test_install.py into test_install_service.py with patch targets corrected for the service layer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Audit of the upstream/main diff found two classes of upstream tests that were dropped during merge conflict resolution without replacement: 1. TestInstallCommandEnvFlags (8 tests) — env var injection, context file secret isolation, unresolved-var detection, and env file lifecycle. Ported as TestInstallAgentEnvBehaviour in test_install_service.py against install_agent directly. 2. test_install_general_error — upstream CLI caught bare Exception; the service only caught (ValueError, OSError). Added except Exception fallback to install_agent and a matching test test_install_returns_failure_for_unexpected_errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. Collapse dead except (ValueError, OSError) into except Exception in
install_agent — both produced the same message; narrower branch was
unreachable after the broad fallback was added.
2. Restore upstream UX in install.py CLI wrapper:
- Unresolved-vars warning now reads "Unresolved env var(s) in
profile: X. Set them with \`cao env set\` or pass --env KEY=VALUE."
- "Set N env var(s)" now uses len(env_vars) (raw tuple) so duplicate
--env flags count correctly instead of deduplicating via dict.
3. Fix env_vars round-trip between ops_mcp_server and API: switch from
comma-separated KEY=VALUE (breaks on values containing ',') to
JSON-encoded dict. _serialize_env_vars emits json.dumps, _parse_env_vars
parses json.loads.
4. Return InstallResult directly from the FastAPI install endpoint with
a typed return annotation instead of result.model_dump() — gives
FastAPI a proper OpenAPI schema.
5. Inline the one-line _install_result_from_error helper in
ops_mcp_server/server.py.
6. Add parametrized coverage for the three previously-uncovered
_resolve_named_source branches: flat provider dir layout, extra dir
flat layout, extra dir nested layout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parse_env_assignment was the only caller in _parse_env_vars; the JSON migration in f8ad731 removed that call site, leaving an F401 dead import flagged by Ruff. Also remove the `import json as _json` local shadow inside _parse_env_vars — json is already imported at module top level. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lResult
Upstream's inline install command printed "Downloaded agent from URL to
local store" and "Copied agent from file to local store" before the
success message — this was lost when install logic was extracted to the
service layer. Adds a source_kind discriminator ('url'|'file'|'name')
to InstallResult and wires it through the CLI output and tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
Overview
Addresses #161
Introduces
cao-ops-mcp, a new MCP server that exposes CAO management operations as structured tools for a user's primary agent. It enables end-to-end agent-driven CAO workflows — discovering and installing profiles, launching sessions, delivering prompts, monitoring progress, and shutting down — without leaving the agent interface or switching to a separate terminal.Motivation
All CAO management operations currently require either the
caoCLI or direct HTTP API calls in a separate terminal. The existingcao-mcp-serverdoes not address this — its tools (handoff,assign,send_message) are scoped to inter-agent orchestration within active CAO sessions, not to managing CAO itself.cao-ops-mcpfills this gap as the agentic control plane alongside the existing Web UI (visual control plane) and CLI (manual control plane). A user's primary agent can now manage the full CAO lifecycle within a single conversation — no terminal switching required.Key Changes
src/.../ops_mcp_server/server.pycao-ops-mcp) with 8 tools across two groups: profile management (list_profiles,get_profile_details,install_profile) and session lifecycle (launch_session,send_session_message,list_sessions,get_session_info,shutdown_session)src/.../ops_mcp_server/models.pyLaunchResult,InstallResult,ProfileListResult,SessionListResult,SendMessageResult)src/.../ops_mcp_server/__init__.pyops_mcp_servermodulesrc/.../services/install_service.pyinstallcommand — pureinstall_agent()function returning a structuredInstallResult, reusable by both the CLI and the new API endpointsrc/.../api/main.pyGET /agents/profiles/{name}(full profile content) andPOST /agents/profiles/install(install via service layer), both consumed by the ops MCP toolssrc/.../cli/commands/install.pyinstall_service.install_agent()— behavior is unchanged, install logic now lives in the service layerpyproject.tomlcao-ops-mcp-serverentry pointREADME.mdCAO Ops MCP Serversection documenting setup, available tools, and the typical workflowtest/ops_mcp_server/test_server.pytest/api/test_api_profiles.pytest/services/test_install_service.pytest/cli/commands/test_install.pytest_install_service.pyTest Plan
uv run pytest test/ --ignore=test/e2e -v— full unit test suite passes with no regressionsuv run cao-ops-mcp-server— server starts without errors (exits cleanly when stdin closes)list_profiles→install_profile→launch_session→send_session_message→get_session_info→shutdown_session(
uvx --with-editable . fastmcp dev inspector src/cli_agent_orchestrator/ops_mcp_server/server.py)