Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ objects (`ListToolsResult`, `CallToolResult`, `ListResourcesResult`).
4. Add test in `tests/test_server.py`
5. Run `make docs` to regenerate `docs/tools.md`

## Feature Completion Expectations

- Every behavior change must include tests.
- User-facing tools and workflows must include or update examples in
`README.md`, generated tool docs, or test fixtures that demonstrate expected
usage.
- When a roadmap item or milestone meaningfully changes status, update
`README.md` and the roadmap document in the same change.
- When work is packaged as a release-ready change, also update
`pyproject.toml`, `src/deep_agentic_core_mcp/__init__.py`, and
`CHANGELOG.md`.

## Package Boundaries

- This server is a **thin orchestration layer** — reuse agenticlens and
Expand Down
14 changes: 11 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ uv sync --extra dev

1. Create a focused branch from `main`.
2. Add or update tests with every behavior change.
3. Run:
3. Add or update user-facing examples when the tool behavior, CLI contract, or
MCP output changes.
4. If a roadmap item is completed or its status changes, update `README.md`
and the roadmap document in the same pull request.
5. If the work is release-ready, update `pyproject.toml`,
`src/deep_agentic_core_mcp/__init__.py`, and `CHANGELOG.md` as part of the
release.
6. Run:

```bash
ruff check .
Expand All @@ -31,15 +38,16 @@ mypy
pytest
```

4. Keep PRs focused — one concern per pull request.
5. Write clear commit messages describing *why*, not just *what*.
7. Keep PRs focused — one concern per pull request.
8. Write clear commit messages describing *why*, not just *what*.

## Adding a tool

1. Create handler in `src/deep_agentic_core_mcp/tools/`
2. Register in `tools/registry.py` with name, title, description, and `input_schema`
3. Add entry to `_TOOL_DISPATCH` in `server.py`
4. Add tests in `tests/`
5. Add or update usage examples or generated docs for user-facing behavior

## Releases

Expand Down
57 changes: 57 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,46 @@ Current shipped version: `0.2.0` (2026-08-08) — see [CHANGELOG.md](CHANGELOG.m
- **Phase 5: Publishing and Adoption** 🚧 Planned
- **Phase 6: Operational Intelligence** 🚧 Planned

## Cross-Project Dependencies

This server is an orchestration layer across sibling projects, so roadmap
status should explicitly account for upstream and downstream dependencies.

- `agenticlens`
Provides workflow analysis, evaluation, comparison, and reporting behavior
surfaced through `lens.*` tools.
- `agentic-chaos`
Provides resilience/fault injection behavior surfaced through `chaos.*`
tools.
- `ai-operations-spec`
Provides the canonical artifact model and validation rules surfaced through
`spec.*` tools and used as the ecosystem exchange contract.

For roadmap work, distinguish:

- `Depends on`: a sibling capability or spec milestone that must exist first.
- `Blocked by`: a hard upstream constraint that prevents shipping the feature.
- `Coordinate with`: sibling repos whose docs, examples, or contracts should
be updated together.
- `Validate in`: sibling CLIs, fixtures, or adapters that should be checked
before the item is marked done.

## Definition of Done

A roadmap item is done only when all applicable work is complete:

- implementation is merged and reachable through the intended MCP tool,
prompt, or resource surface
- tests cover the behavior, including integration boundaries where practical
- user-facing examples and generated docs are added or updated
- `README.md` and this roadmap are updated when the feature changes user
expectations or milestone status
- sibling-project dependencies and end-to-end checks are recorded for any
cross-repo tool surface
- release metadata (`pyproject.toml`,
`src/deep_agentic_core_mcp/__init__.py`, `CHANGELOG.md`) is updated when the
work is part of a release-ready change set

## Vision

Build one public MCP server for the DeepAgentLabs ecosystem that unifies:
Expand Down Expand Up @@ -197,6 +237,12 @@ Success criteria:
- chaos results are readable as or convertible to AI Operations Specification
artifacts — still open; `chaos.run_experiment`'s output is `ChaosReport`-shaped
but not yet run through `spec.validate_artifact`
- [ ] script-path allow/deny-list for `chaos.run_experiment`, layered on top
of the existing workspace-root confinement — an opt-in instance-level
allowlist (e.g. `MCP_SERVER_ALLOWED_SCRIPT_GLOBS`) plus a deny-list, modeled
on `devops-open-agent`'s layered MCP-server allowlist/whitelist/blacklist
pattern; tracked as a prerequisite for widening `chaos.run_experiment`
exposure beyond trusted local stdio clients (see Known Limitations)

## Phase 3c: AI Operations Specification Conformance

Expand Down Expand Up @@ -289,6 +335,17 @@ Goals:
or make handlers genuinely async) before any remote/multi-session/SSE
transport (Phase 4+) is added — it would otherwise let one slow call stall
every other client.
- **`chaos.run_experiment` has no allowlist beyond workspace-path
confinement.** Any script inside the workspace root can be executed today;
there's no further restriction on *which* scripts within that root are
permitted, and per `SECURITY.md` the tool doesn't authenticate or authorize
the calling client either. `devops-open-agent` solves the equivalent
problem for its own MCP integration with a layered allow/deny-list
(instance-level allowlist + per-user whitelist + per-user blacklist) —
the same shape (see Phase 3b) is a reasonable model here. Like the
async-blocking limitation above, this should be closed before any
remote/multi-client transport (Phase 4+) is considered, not concurrently
with it.

## Documentation Backlog

Expand Down
3 changes: 3 additions & 0 deletions examples/chaos_exit_nonzero.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sys

sys.exit(2)
3 changes: 3 additions & 0 deletions examples/chaos_exit_string.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sys

sys.exit("boom")
3 changes: 3 additions & 0 deletions examples/chaos_exit_zero.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sys

sys.exit(0)
9 changes: 6 additions & 3 deletions src/deep_agentic_core_mcp/adapters/agentic_chaos.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,22 @@ def run_experiment(
script_path = _resolve_sandboxed_script(script)
resolved_faults = resolve_faults(faults) # raises ValueError on unknown fault names

def _run() -> tuple[Any, Exception | None]:
def _run() -> tuple[Any, BaseException | None]:
with chaos_session(resolved_faults) as session:
crashed: Exception | None = None
crashed: BaseException | None = None
try:
runpy.run_path(str(script_path), run_name="__main__")
except SystemExit as exc:
if exc.code not in (None, 0):
crashed = exc
Comment thread
pramodbn27 marked this conversation as resolved.
except Exception as exc: # noqa: BLE001 - reported back, not swallowed silently
crashed = exc
return session, crashed

started_at = datetime.now(timezone.utc)
timed_out = False
session = None
crashed: Exception | None = None
crashed: BaseException | None = None
# Deliberately not a `with` block: ThreadPoolExecutor.__exit__ calls
# shutdown(wait=True), which would block for the worker thread to finish
# regardless of the timeout below - defeating the whole point of it.
Expand Down
43 changes: 43 additions & 0 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
SPEC_V04 = ROOT / "ai-operations-spec" / "specification" / "v0.4" / "examples"
AGENTICLENS_ARTIFACTS = ROOT / "agenticlens" / "examples" / "pitch_demo" / "artifacts"
CHAOS_TARGET_SCRIPT = "mcp-server/examples/chaos_target.py"
CHAOS_EXIT_ZERO_SCRIPT = "mcp-server/examples/chaos_exit_zero.py"
CHAOS_EXIT_NONZERO_SCRIPT = "mcp-server/examples/chaos_exit_nonzero.py"
CHAOS_EXIT_STRING_SCRIPT = "mcp-server/examples/chaos_exit_string.py"

WORKFLOW_ARTIFACT = {
"name": "Support workflow",
Expand Down Expand Up @@ -321,6 +324,46 @@ async def test_handle_call_tool_run_experiment_rejects_unknown_fault() -> None:
assert "error" in payload


@pytest.mark.asyncio
async def test_handle_call_tool_run_experiment_treats_system_exit_zero_as_success() -> None:
from deep_agentic_core_mcp.server import handle_call_tool

result = await handle_call_tool(
"chaos.run_experiment",
{"script": CHAOS_EXIT_ZERO_SCRIPT, "faults": ["silent_degradation"]},
)
payload = json.loads(result[0].text)
assert payload["ok"] is True
assert payload["timed_out"] is False
assert payload["crashed"] is None


@pytest.mark.asyncio
@pytest.mark.parametrize(
("script", "exit_code"),
[
(CHAOS_EXIT_NONZERO_SCRIPT, "2"),
(CHAOS_EXIT_STRING_SCRIPT, "boom"),
],
)
async def test_handle_call_tool_run_experiment_reports_failing_system_exit(
script: str,
exit_code: str,
) -> None:
from deep_agentic_core_mcp.server import handle_call_tool

result = await handle_call_tool(
"chaos.run_experiment",
{"script": script, "faults": ["silent_degradation"]},
)
payload = json.loads(result[0].text)
assert payload["ok"] is False
assert payload["timed_out"] is False
assert payload["crashed"] is not None
assert "SystemExit" in payload["crashed"]
assert exit_code in payload["crashed"]


# ---------------------------------------------------------------------------
# Prompts
# ---------------------------------------------------------------------------
Expand Down
Loading