Skip to content

feat: demo-infra MCP server (simulated infrastructure + 8 tools) - #4

Open
ADITYA-tp01 wants to merge 7 commits into
mainfrom
feat/mcp-demo-infra
Open

feat: demo-infra MCP server (simulated infrastructure + 8 tools)#4
ADITYA-tp01 wants to merge 7 commits into
mainfrom
feat/mcp-demo-infra

Conversation

@ADITYA-tp01

@ADITYA-tp01 ADITYA-tp01 commented Aug 25, 2026

Copy link
Copy Markdown
Owner

User description

What

Core simulation layer for MissionControl: a custom MCP server modeling fake production infrastructure that the agent investigates and remediates.

Components

  • state.py — thread-safe in-memory infra model: 4 services, deploy history, chaos profiles, cascading-failure dependency graph with dampened propagation, ring-buffered metrics/logs/alerts/timeline
  • server.py — FastMCP server exposing 8 tools over streamable-http (:8000/mcp); docstrings double as agent-facing tool instructions
  • http_api.py — dependency-free stdlib REST sidecar (:8001) powering the dashboard
  • test_state.py — 9 tests covering chaos, alerts, rollback, cascading failures, validation, reset, sampling (9/9 passing)
  • Dockerfile + requirements (sole dep: \mcp==1.29.0)

Design notes

  • All data is fabricated by design - deterministic, resettable demos via /api/reset\
  • Destructive tools (
    ollback_deploy,
    estart_service) are the ones gated by TrueForge approval checkpoints; \inject_chaos\ is human-only per agent safety rules
  • Qodo findings on this PR will be addressed in-repo

CodeAnt-AI Description

Add a secure simulated infrastructure server for incident investigation and recovery

What Changed

  • Adds an MCP service and dashboard API for viewing service health, metrics, deployments, logs, alerts, incident timelines, and agent sessions across four simulated services.
  • Allows approved rollback and restart actions through MCP, while dashboard mutations require a bearer token; chaos injection remains dashboard-only.
  • Cascading incidents now recover all affected dependencies, repeated incidents replace the previous event cleanly, and invalid chaos requests no longer alter an active incident.
  • Rollbacks reject deployments belonging to another service, reset restores seeded versions and clean baseline logs, and invalid request limits return clear HTTP 400 errors.
  • Packages the server for Docker with health checks and automatically re-runs review checks when the pull request changes.

Impact

✅ Safer incident remediation
✅ Complete recovery after cascading failures
✅ Fewer accidental infrastructure state changes

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add simulated infrastructure MCP server and dashboard API

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Adds a thread-safe, resettable simulation of four production services.
• Exposes incident investigation and remediation through eight MCP tools and REST endpoints.
• Covers chaos, recovery, alerting, validation, reset, and metric sampling behaviors.
Diagram

sequenceDiagram
    actor Agent
    participant MCP as MCP Server
    participant Dashboard
    participant REST as REST API
    participant State as Infra State
    participant Sampler
    Agent->>MCP: Call tool
    MCP->>State: Query or mutate
    State-->>MCP: Simulated result
    MCP-->>Agent: JSON response
    Dashboard->>REST: Request state
    REST->>State: Query or mutate
    State-->>REST: State snapshot
    REST-->>Dashboard: JSON response
    Sampler->>State: Record metrics
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Separate API processes with shared persistence
  • ➕ Isolates MCP and dashboard API failures
  • ➕ Supports independent scaling and restarts
  • ➖ Requires Redis, a database, or synchronization protocol
  • ➖ Adds deployment complexity unnecessary for an ephemeral demo
2. Use a web framework for the sidecar
  • ➕ Provides declarative routing and validation
  • ➕ Improves standardized error handling and API extensibility
  • ➖ Adds dependencies and container weight
  • ➖ Offers limited value for the current small endpoint set

Recommendation: Keep the single-process shared state and stdlib REST sidecar for this deterministic demo: it minimizes dependencies and guarantees both interfaces observe identical state. Revisit process isolation or a framework only if persistence, authentication, stronger validation, or independent scaling becomes necessary.

Files changed (6) +947 / -0

Enhancement (3) +827 / -0
http_api.pyAdd dashboard REST sidecar +196/-0

Add dashboard REST sidecar

• Adds dependency-free JSON endpoints for health, metrics, deploys, logs, alerts, timeline, and sessions. Mutation routes support chaos injection, remediation, alert acknowledgement, session attachment, and demo reset.

mcp-servers/demo-infra/http_api.py

server.pyExpose eight FastMCP incident tools +191/-0

Expose eight FastMCP incident tools

• Registers investigation, chaos, rollback, and restart tools over streamable HTTP with agent-facing safety instructions. Starts the REST sidecar and periodic metrics sampler in daemon threads.

mcp-servers/demo-infra/server.py

state.pyImplement the simulated infrastructure state machine +440/-0

Implement the simulated infrastructure state machine

• Models four services with synchronized deployments, metrics, logs, alerts, sessions, and incident events. Implements chaos profiles, dampened dependency failures, recovery actions, bounded histories, and deterministic reset behavior.

mcp-servers/demo-infra/state.py

Tests (1) +107 / -0
test_state.pyTest simulation and remediation behavior +107/-0

Test simulation and remediation behavior

• Adds nine tests for baseline health, chaos and cascading failures, rollback, restart, validation, alert acknowledgement, reset, and metric sampling. Tests can run through pytest or the included standalone runner.

mcp-servers/demo-infra/test_state.py

Other (2) +13 / -0
DockerfileContainerize both demo infrastructure interfaces +12/-0

Containerize both demo infrastructure interfaces

• Builds a Python 3.11 image, installs the pinned requirements, and launches the MCP server. Exposes the MCP and REST ports used by agents and the dashboard.

mcp-servers/demo-infra/Dockerfile

requirements.txtPin the MCP runtime dependency +1/-0

Pin the MCP runtime dependency

• Adds MCP 1.29.0 as the server's sole runtime dependency.

mcp-servers/demo-infra/requirements.txt

@github-actions

Copy link
Copy Markdown

Failed to generate code suggestions for PR

@qodo-code-review

qodo-code-review Bot commented Aug 25, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Invalid chaos clears incident ✓ Resolved 🐞 Bug ≡ Correctness ⭐ New
Description
inject_chaos restores the active event's affected services before validating the new service and
chaos type, so an invalid replacement request returns an error after silently healing the current
incident. Because the old chaos metadata is left unchanged on that early return, health reports
healthy services while chaos_active still identifies the old incident.
Code

mcp-servers/demo-infra/state.py[R301-303]

+                        svc.error_rate = BASELINE_ERROR_RATE
+                        svc.latency_p99 = BASELINE_LATENCY
+                        svc.status = "healthy"
Evidence
The changed block resets every service in chaos_affected to baseline at state.py lines 297-303.
Only afterward do lines 311-314 validate chaos_type and service and return an error, while the
active metadata is assigned only on the success path at lines 338-341; http_api.py lines 190-194
expose this path to arbitrary request-body values.

mcp-servers/demo-infra/state.py[293-314]
mcp-servers/demo-infra/state.py[338-341]
mcp-servers/demo-infra/http_api.py[190-194]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Validate the requested service and chaos profile before restoring any currently affected services. An invalid replacement chaos request must return an error without mutating the active incident or its metadata.

## Issue Context
The REST chaos endpoint accepts caller-provided service and chaos type values. Currently, cleanup runs first, while the validation and early error return occur afterward, leaving health and chaos metadata inconsistent.

## Fix Focus Areas
- mcp-servers/demo-infra/state.py[293-314]
- mcp-servers/demo-infra/test_state.py[125-135]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Earlier chaos remains degraded ✓ Resolved 🐞 Bug ≡ Correctness
Description
A second inject_chaos replaces chaos_affected, so remediating the latest root restores only the
newest event's services before clearing the global chaos marker. For example, a cascade on
api-gateway followed by chaos on payment-service, then restarting payment, can leave
api-gateway down and notification-service degraded while chaos_active is false and the API
reports no active chaos.
Code

mcp-servers/demo-infra/state.py[325]

+            self.chaos_affected = list(affected)
Evidence
Each injection builds a fresh affected list containing only the current root and its direct
dependencies, mutates those services, and replaces the previous tracking assignment. Recovery
iterates only this replacement list and clears all global chaos fields, while remediation invokes
recovery only for the currently stored root, leaving services affected solely by the first event
degraded and untracked.

mcp-servers/demo-infra/state.py[300-325]
mcp-servers/demo-infra/state.py[351-375]
mcp-servers/demo-infra/state.py[408-410]
mcp-servers/demo-infra/state.py[435-436]
mcp-servers/demo-infra/state.py[351-376]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description

`inject_chaos` overwrites the affected-service tracking when another chaos event is already active. Remediating the newer event then clears the global chaos state while services affected only by the older event remain unhealthy and cannot be recovered through normal remediation.

## Issue Context

The model tracks one active event through `chaos_active`, `chaos_service`, and `chaos_affected`, but `/api/chaos` can invoke `inject_chaos` repeatedly. Before installing a new event, either reject concurrent injection or explicitly recover or retain all previously affected services so none are lost from recovery tracking; add a test that injects two events before remediation and verifies that all affected services return to healthy.

## Fix Focus Areas

- mcp-servers/demo-infra/state.py[293-336]
- mcp-servers/demo-infra/state.py[351-376]
- mcp-servers/demo-infra/test_state.py[113-124]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Cascade recovery leaves dependencies degraded ✓ Resolved 🐞 Bug ≡ Correctness
Description
A cascading failure mutates the root and all listed dependencies, but restarting or rolling back the
root restores only that one service and then clears the global chaos marker. Health checks
consequently continue to show dependent services degraded even though remediation reports success
and chaos is reported inactive.
Code

mcp-servers/demo-infra/state.py[R398-401]

+            if self.chaos_service == service:
+                self.chaos_active = False
+                self.chaos_service = None
+                self.chaos_type = None
Evidence
Cascade injection explicitly degrades every dependency, whereas both recovery methods modify only
the requested service and clear the single global chaos fields when that service is the root.

mcp-servers/demo-infra/state.py[33-38]
mcp-servers/demo-infra/state.py[296-320]
mcp-servers/demo-infra/state.py[364-373]
mcp-servers/demo-infra/state.py[388-411]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Root-service remediation clears cascade tracking without restoring the dependent services changed by the cascade.

## Issue Context
Track all affected services for an active chaos event and restore them consistently, or retain an active per-service incident state until each participant is recovered; cover both restart and rollback.

## Fix Focus Areas
- mcp-servers/demo-infra/state.py[296-320]
- mcp-servers/demo-infra/state.py[364-373]
- mcp-servers/demo-infra/state.py[388-411]
- mcp-servers/demo-infra/test_state.py[56-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (4)
4. Reset preserves mutated state ✓ Resolved 🐞 Bug ≡ Correctness
Description
reset_demo restores health metrics but does not restore service versions or logs, both of which
are mutated by rollback and incident actions. After a rollback and reset, the service remains on the
rolled-back version and old chaos/recovery logs remain, so repeated demos are not reset to the
declared baseline.
Code

mcp-servers/demo-infra/state.py[R417-420]

+            for svc in self.services.values():
+                svc.error_rate = BASELINE_ERROR_RATE
+                svc.latency_p99 = BASELINE_LATENCY
+                svc.status = "healthy"
Evidence
Initial versions are established in the constructor, rollback changes svc.version and appends
logs, while reset only resets health fields, alerts, timeline, sessions, and metrics history.

mcp-servers/demo-infra/state.py[101-134]
mcp-servers/demo-infra/state.py[364-379]
mcp-servers/demo-infra/state.py[415-433]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Reset leaves rolled-back versions and incident-generated logs in place instead of restoring a deterministic baseline.

## Issue Context
Restore each initial service version and regenerate or clear logs along with the other mutable collections; test reset after rollback and chaos.

## Fix Focus Areas
- mcp-servers/demo-infra/state.py[101-134]
- mcp-servers/demo-infra/state.py[364-379]
- mcp-servers/demo-infra/state.py[415-433]
- mcp-servers/demo-infra/test_state.py[81-87]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Rollback accepts foreign deploy ✓ Resolved 🐞 Bug ≡ Correctness
Description
rollback_deploy looks up deploy_id globally but never verifies that the matching deploy belongs
to service. For example, rollback_deploy("api-gateway", "deploy-003") succeeds using the
payment-service deploy as its cutoff and records a false API rollback.
Code

mcp-servers/demo-infra/state.py[R352-354]

+            deploy_idx = next((i for i, d in enumerate(self.deploys) if d.id == deploy_id), None)
+            if deploy_idx is None:
+                return {"status": "error", "message": f"Unknown deploy_id: {deploy_id}"}
Evidence
The seeded data identifies deploy-003 as a payment-service deploy, but rollback only checks that
the ID exists before searching earlier history for whichever service the caller supplied.

mcp-servers/demo-infra/state.py[107-117]
mcp-servers/demo-infra/state.py[346-367]
mcp-servers/demo-infra/test_state.py[66-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Rollback accepts a deploy ID belonging to a different service and performs a misleading mutation.

## Issue Context
After locating the deploy, reject it unless its `service` equals the requested service; add a cross-service regression test.

## Fix Focus Areas
- mcp-servers/demo-infra/state.py[352-360]
- mcp-servers/demo-infra/test_state.py[66-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. REST bypasses remediation approval ✓ Resolved 🐞 Bug ⛨ Security
Description
The public sidecar directly executes rollback and restart without authentication or an approval
check, so any client that can reach port 8001 can perform the operations the MCP contract marks
approval-required. Binding to all interfaces and allowing every CORS origin makes this bypass
available beyond the intended approval flow.
Code

mcp-servers/demo-infra/http_api.py[R162-165]

+            if action == "rollback":
+                result = state.rollback_deploy(service, body.get("deploy_id", ""))
+            elif action == "restart":
+                result = state.restart_service(service)
Evidence
The sidecar listens on 0.0.0.0, advertises wildcard CORS, and reaches the state mutations with no
intervening credential or approval validation; the server instructions explicitly classify these
operations as requiring human approval.

mcp-servers/demo-infra/http_api.py[18-19]
mcp-servers/demo-infra/http_api.py[44-51]
mcp-servers/demo-infra/http_api.py[147-169]
mcp-servers/demo-infra/server.py[24-29]
mcp-servers/demo-infra/state.py[346-413]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The REST remediation endpoint invokes approval-required operations without authenticating the caller or validating an approval artifact.

## Issue Context
Protect mutation endpoints with authentication and enforce the same approval semantics as the agent path; do not rely on CORS as authorization.

## Fix Focus Areas
- mcp-servers/demo-infra/http_api.py[18-19]
- mcp-servers/demo-infra/http_api.py[44-51]
- mcp-servers/demo-infra/http_api.py[159-169]
- mcp-servers/demo-infra/server.py[24-29]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Agent can inject chaos ✓ Resolved 🐞 Bug ⛨ Security
Description
inject_chaos is registered as an MCP tool under the no-approval section even though the agent
safety contract says it is exclusively a human control that the agent must never call. A model can
therefore invoke the tool and create the incident it is supposed to investigate.
Code

mcp-servers/demo-infra/server.py[R106-108]

+@mcp.tool()
+def inject_chaos(service: str, chaos_type: str = "error_spike") -> str:
+    """Inject a failure scenario into a service. Fires an alert and adds
Evidence
The registered tool directly calls the state mutation, while both agent policy files explicitly
prohibit agent use and reserve it for humans.

mcp-servers/demo-infra/server.py[106-118]
mcp-servers/demo-infra/state.py[289-331]
agent/system-prompt.md[22-28]
agent/skills/incident-response/SKILL.md[72-75]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`inject_chaos` is exposed to the agent as a normal MCP tool despite being designated as a human-only demo control.

## Issue Context
Keep chaos injection available only through the human-facing control path, or enforce authorization that makes it impossible for the agent to invoke.

## Fix Focus Areas
- mcp-servers/demo-infra/server.py[106-118]
- agent/system-prompt.md[22-28]
- agent/skills/incident-response/SKILL.md[72-75]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

8. Malformed limits abort requests ✓ Resolved 🐞 Bug ☼ Reliability
Description
The REST handlers pass query-string limits through int() without validation, so requests such as
/api/deploys?limit=abc raise ValueError and terminate the handler instead of returning a
controlled 4xx response. The same failure exists for metrics and logs endpoints.
Code

mcp-servers/demo-infra/http_api.py[R85-87]

+            service = match.group(1)
+            metrics = state.get_error_metrics(service)
+            history = state.get_metrics_history(service, limit=int(query.get("limit", 60)))
Evidence
All three endpoint paths directly convert untrusted query values with int() and contain no
exception handler around request dispatch.

mcp-servers/demo-infra/http_api.py[65-68]
mcp-servers/demo-infra/http_api.py[83-118]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Non-integer limit query parameters raise uncaught exceptions in request threads.

## Issue Context
Parse limits through a shared validator, enforce a non-negative bounded range, and return HTTP 400 for invalid values.

## Fix Focus Areas
- mcp-servers/demo-infra/http_api.py[83-88]
- mcp-servers/demo-infra/http_api.py[102-109]
- mcp-servers/demo-infra/http_api.py[112-118]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: The push changes runtime chaos-state recovery across affected services, with meaningful state and failure-mode risk, but is localized and not dense enough to warrant redundant extended review.

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit 393f789

Results up to commit d6bcea8 ⚖️ Balanced


🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Agent can inject chaos ✓ Resolved 🐞 Bug ⛨ Security
Description
inject_chaos is registered as an MCP tool under the no-approval section even though the agent
safety contract says it is exclusively a human control that the agent must never call. A model can
therefore invoke the tool and create the incident it is supposed to investigate.
Code

mcp-servers/demo-infra/server.py[R106-108]

+@mcp.tool()
+def inject_chaos(service: str, chaos_type: str = "error_spike") -> str:
+    """Inject a failure scenario into a service. Fires an alert and adds
Evidence
The registered tool directly calls the state mutation, while both agent policy files explicitly
prohibit agent use and reserve it for humans.

mcp-servers/demo-infra/server.py[106-118]
mcp-servers/demo-infra/state.py[289-331]
agent/system-prompt.md[22-28]
agent/skills/incident-response/SKILL.md[72-75]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`inject_chaos` is exposed to the agent as a normal MCP tool despite being designated as a human-only demo control.

## Issue Context
Keep chaos injection available only through the human-facing control path, or enforce authorization that makes it impossible for the agent to invoke.

## Fix Focus Areas
- mcp-servers/demo-infra/server.py[106-118]
- agent/system-prompt.md[22-28]
- agent/skills/incident-response/SKILL.md[72-75]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Rollback accepts foreign deploy ✓ Resolved 🐞 Bug ≡ Correctness
Description
rollback_deploy looks up deploy_id globally but never verifies that the matching deploy belongs
to service. For example, rollback_deploy("api-gateway", "deploy-003") succeeds using the
payment-service deploy as its cutoff and records a false API rollback.
Code

mcp-servers/demo-infra/state.py[R352-354]

+            deploy_idx = next((i for i, d in enumerate(self.deploys) if d.id == deploy_id), None)
+            if deploy_idx is None:
+                return {"status": "error", "message": f"Unknown deploy_id: {deploy_id}"}
Evidence
The seeded data identifies deploy-003 as a payment-service deploy, but rollback only checks that
the ID exists before searching earlier history for whichever service the caller supplied.

mcp-servers/demo-infra/state.py[107-117]
mcp-servers/demo-infra/state.py[346-367]
mcp-servers/demo-infra/test_state.py[66-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Rollback accepts a deploy ID belonging to a different service and performs a misleading mutation.

## Issue Context
After locating the deploy, reject it unless its `service` equals the requested service; add a cross-service regression test.

## Fix Focus Areas
- mcp-servers/demo-infra/state.py[352-360]
- mcp-servers/demo-infra/test_state.py[66-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. REST bypasses remediation approval 🐞 Bug ⛨ Security
Description
The public sidecar directly executes rollback and restart without authentication or an approval
check, so any client that can reach port 8001 can perform the operations the MCP contract marks
approval-required. Binding to all interfaces and allowing every CORS origin makes this bypass
available beyond the intended approval flow.
Code

mcp-servers/demo-infra/http_api.py[R162-165]

+            if action == "rollback":
+                result = state.rollback_deploy(service, body.get("deploy_id", ""))
+            elif action == "restart":
+                result = state.restart_service(service)
Evidence
The sidecar listens on 0.0.0.0, advertises wildcard CORS, and reaches the state mutations with no
intervening credential or approval validation; the server instructions explicitly classify these
operations as requiring human approval.

mcp-servers/demo-infra/http_api.py[18-19]
mcp-servers/demo-infra/http_api.py[44-51]
mcp-servers/demo-infra/http_api.py[147-169]
mcp-servers/demo-infra/server.py[24-29]
mcp-servers/demo-infra/state.py[346-413]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The REST remediation endpoint invokes approval-required operations without authenticating the caller or validating an approval artifact.

## Issue Context
Protect mutation endpoints with authentication and enforce the same approval semantics as the agent path; do not rely on CORS as authorization.

## Fix Focus Areas
- mcp-servers/demo-infra/http_api.py[18-19]
- mcp-servers/demo-infra/http_api.py[44-51]
- mcp-servers/demo-infra/http_api.py[159-169]
- mcp-servers/demo-infra/server.py[24-29]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (2)
4. Cascade recovery leaves dependencies degraded ✓ Resolved 🐞 Bug ≡ Correctness
Description
A cascading failure mutates the root and all listed dependencies, but restarting or rolling back the
root restores only that one service and then clears the global chaos marker. Health checks
consequently continue to show dependent services degraded even though remediation reports success
and chaos is reported inactive.
Code

mcp-servers/demo-infra/state.py[R398-401]

+            if self.chaos_service == service:
+                self.chaos_active = False
+                self.chaos_service = None
+                self.chaos_type = None
Evidence
Cascade injection explicitly degrades every dependency, whereas both recovery methods modify only
the requested service and clear the single global chaos fields when that service is the root.

mcp-servers/demo-infra/state.py[33-38]
mcp-servers/demo-infra/state.py[296-320]
mcp-servers/demo-infra/state.py[364-373]
mcp-servers/demo-infra/state.py[388-411]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Root-service remediation clears cascade tracking without restoring the dependent services changed by the cascade.

## Issue Context
Track all affected services for an active chaos event and restore them consistently, or retain an active per-service incident state until each participant is recovered; cover both restart and rollback.

## Fix Focus Areas
- mcp-servers/demo-infra/state.py[296-320]
- mcp-servers/demo-infra/state.py[364-373]
- mcp-servers/demo-infra/state.py[388-411]
- mcp-servers/demo-infra/test_state.py[56-64]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Reset preserves mutated state ✓ Resolved 🐞 Bug ≡ Correctness
Description
reset_demo restores health metrics but does not restore service versions or logs, both of which
are mutated by rollback and incident actions. After a rollback and reset, the service remains on the
rolled-back version and old chaos/recovery logs remain, so repeated demos are not reset to the
declared baseline.
Code

mcp-servers/demo-infra/state.py[R417-420]

+            for svc in self.services.values():
+                svc.error_rate = BASELINE_ERROR_RATE
+                svc.latency_p99 = BASELINE_LATENCY
+                svc.status = "healthy"
Evidence
Initial versions are established in the constructor, rollback changes svc.version and appends
logs, while reset only resets health fields, alerts, timeline, sessions, and metrics history.

mcp-servers/demo-infra/state.py[101-134]
mcp-servers/demo-infra/state.py[364-379]
mcp-servers/demo-infra/state.py[415-433]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Reset leaves rolled-back versions and incident-generated logs in place instead of restoring a deterministic baseline.

## Issue Context
Restore each initial service version and regenerate or clear logs along with the other mutable collections; test reset after rollback and chaos.

## Fix Focus Areas
- mcp-servers/demo-infra/state.py[101-134]
- mcp-servers/demo-infra/state.py[364-379]
- mcp-servers/demo-infra/state.py[415-433]
- mcp-servers/demo-infra/test_state.py[81-87]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
6. Malformed limits abort requests ✓ Resolved 🐞 Bug ☼ Reliability
Description
The REST handlers pass query-string limits through int() without validation, so requests such as
/api/deploys?limit=abc raise ValueError and terminate the handler instead of returning a
controlled 4xx response. The same failure exists for metrics and logs endpoints.
Code

mcp-servers/demo-infra/http_api.py[R85-87]

+            service = match.group(1)
+            metrics = state.get_error_metrics(service)
+            history = state.get_metrics_history(service, limit=int(query.get("limit", 60)))
Evidence
All three endpoint paths directly convert untrusted query values with int() and contain no
exception handler around request dispatch.

mcp-servers/demo-infra/http_api.py[65-68]
mcp-servers/demo-infra/http_api.py[83-118]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Non-integer limit query parameters raise uncaught exceptions in request threads.

## Issue Context
Parse limits through a shared validator, enforce a non-negative bounded range, and return HTTP 400 for invalid values.

## Fix Focus Areas
- mcp-servers/demo-infra/http_api.py[83-88]
- mcp-servers/demo-infra/http_api.py[102-109]
- mcp-servers/demo-infra/http_api.py[112-118]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 1394e9c 🧠 Deep


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Earlier chaos remains degraded ✓ Resolved 🐞 Bug ≡ Correctness
Description
A second inject_chaos replaces chaos_affected, so remediating the latest root restores only the
newest event's services before clearing the global chaos marker. For example, a cascade on
api-gateway followed by chaos on payment-service, then restarting payment, can leave
api-gateway down and notification-service degraded while chaos_active is false and the API
reports no active chaos.
Code

mcp-servers/demo-infra/state.py[325]

+            self.chaos_affected = list(affected)
Evidence
Each injection builds a fresh affected list containing only the current root and its direct
dependencies, mutates those services, and replaces the previous tracking assignment. Recovery
iterates only this replacement list and clears all global chaos fields, while remediation invokes
recovery only for the currently stored root, leaving services affected solely by the first event
degraded and untracked.

mcp-servers/demo-infra/state.py[300-325]
mcp-servers/demo-infra/state.py[351-375]
mcp-servers/demo-infra/state.py[408-410]
mcp-servers/demo-infra/state.py[435-436]
mcp-servers/demo-infra/state.py[351-376]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description

`inject_chaos` overwrites the affected-service tracking when another chaos event is already active. Remediating the newer event then clears the global chaos state while services affected only by the older event remain unhealthy and cannot be recovered through normal remediation.

## Issue Context

The model tracks one active event through `chaos_active`, `chaos_service`, and `chaos_affected`, but `/api/chaos` can invoke `inject_chaos` repeatedly. Before installing a new event, either reject concurrent injection or explicitly recover or retain all previously affected services so none are lost from recovery tracking; add a test that injects two events before remediation and verifies that all affected services return to healthy.

## Fix Focus Areas

- mcp-servers/demo-infra/state.py[293-336]
- mcp-servers/demo-infra/state.py[351-376]
- mcp-servers/demo-infra/test_state.py[113-124]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread mcp-servers/demo-infra/server.py Outdated
Comment thread mcp-servers/demo-infra/http_api.py
Comment thread mcp-servers/demo-infra/state.py
Comment thread mcp-servers/demo-infra/state.py
Comment thread mcp-servers/demo-infra/state.py Outdated
Comment thread mcp-servers/demo-infra/http_api.py Outdated
- remove inject_chaos from MCP surface (human-only via dashboard REST)
- bind REST sidecar to loopback by default, restrict CORS allowlist
- reject rollbacks referencing another service's deploy
- reset_demo restores seeded versions and regenerates baseline logs
- cascade recovery now restores all degraded dependencies
- validate limit params -> HTTP 400 instead of handler crash
- add regression tests (13 passing)
@ADITYA-tp01

Copy link
Copy Markdown
Owner Author

/review

@github-actions

Copy link
Copy Markdown

Preparing review...

Comment thread mcp-servers/demo-infra/state.py
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 1394e9c

@ADITYA-tp01

Copy link
Copy Markdown
Owner Author

/review

@github-actions

Copy link
Copy Markdown

Preparing review...

Comment thread mcp-servers/demo-infra/state.py
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 017cb73

@ADITYA-tp01

Copy link
Copy Markdown
Owner Author

Ruling on Qodo findings — all addressed

Finding 1 (High) — inject_chaos exposed as MCP tool:
Fixed. inject_chaos removed from MCP tool registry; incidents are human-created via dashboard REST (/api/chaos). System prompt updated: *" Never call inject_chaos — chaos scenarios are injected by humans via the demo dashboard

@codeant-ai

codeant-ai Bot commented Aug 27, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed 393f789 Aug 29, 2026 · 19:49 19:49
✅ Reviewed your PR dd019cf Aug 27, 2026 · 20:33 20:36

@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Aug 27, 2026
@ADITYA-tp01

Copy link
Copy Markdown
Owner Author

Fix for finding "Invalid chaos clears incident" (commit dd019cf)

Confirmed real and fixed. Root cause + fix:

  • Root cause: inject_chaos restored the previous incident's affected services to baseline before validating the new service/chaos_type. An invalid POST (e.g. {"service": "bogus"}) returned 400 but had already silently healed the running incident, while chaos_active/chaos_service still identified the old incident — healthy metrics + stale metadata. Exposed by http_api.py POST /api/chaos with arbitrary request bodies.
  • Fix (state.py): validation now runs first (profile + service lookup, early error return), then the previous origin is restored on the success path only, and only then is the new chaos applied + metadata set. An invalid replacement request is now a no-op — the active incident stays degraded and chaos_active stays coherent.
  • Regression test added (test_invalid_chaos_does_not_heal_active_incident): injects error_spike into payment-service, then triggers both an unknown-service and an unknown-chaos_type request; asserts each returns error and that the service stays degraded with chaos_active is True and the original chaos_service intact.
  • Also re-encoded the demo-infra sources from UTF-16 to UTF-8 (they were committed as UTF-16 blobs in the OTHER branch — un-runnable by CPython and invisible to text diffs). This branch's files were already UTF-8; the commit here is the pure logic fix.

Local verification: python test_state.py15/15 PASS, py_compile clean for state.py/http_api.py/server.py/test_state.py.

Note: this finding existed on both PR #4 and PR #5 (the demo-infra files are in both PR diffs). Both branches carry the identical fix (dd019cf on #4, b037f04 on #5).

Comment on lines +52 to +58
def _body(handler: BaseHTTPRequestHandler) -> dict:
length = int(handler.headers.get("Content-Length", 0) or 0)
if length == 0:
return {}
raw = handler.rfile.read(length)
try:
parsed = json.loads(raw.decode("utf-8"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Content-Length is converted with int() outside error handling and the declared number of bytes is read without any upper bound. A malformed header causes an uncaught ValueError in the request handler, while an excessively large declared length can tie up a thread waiting for bytes or consume substantial memory. Parse and cap the length before reading, and return a 400 response for invalid request bodies. [resource leak]

Severity Level: Major ⚠️
- ❌ Malformed requests can terminate individual request handling.<br/>- ❌ Slow large-body requests can exhaust sidecar threads.<br/>- ⚠️ REST dashboard operations may become unavailable.

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** mcp-servers/demo-infra/http_api.py
**Line:** 52:58
**Comment:**
	*Resource Leak: `Content-Length` is converted with `int()` outside error handling and the declared number of bytes is read without any upper bound. A malformed header causes an uncaught `ValueError` in the request handler, while an excessively large declared length can tie up a thread waiting for bytes or consume substantial memory. Parse and cap the length before reading, and return a 400 response for invalid request bodies.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +179 to +181
if path == "/api/sessions":
self._send(200, {"sessions": dict(state.agent_sessions)})
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The sessions endpoint copies state.agent_sessions without acquiring state.lock, while POST requests update that dictionary under the lock. Concurrent mutation during dict() iteration can raise RuntimeError: dictionary changed size during iteration, causing the request to fail, and the response can also be inconsistent with the other state fields. Add a locked snapshot accessor in InfraState and use it here. [race condition]

Severity Level: Major ⚠️
- ⚠️ Concurrent session dashboard requests can fail intermittently.<br/>- ⚠️ Session listings may not represent one consistent snapshot.

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** mcp-servers/demo-infra/http_api.py
**Line:** 179:181
**Comment:**
	*Race Condition: The sessions endpoint copies `state.agent_sessions` without acquiring `state.lock`, while POST requests update that dictionary under the lock. Concurrent mutation during `dict()` iteration can raise `RuntimeError: dictionary changed size during iteration`, causing the request to fail, and the response can also be inconsistent with the other state fields. Add a locked snapshot accessor in `InfraState` and use it here.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +197 to +207
if path == "/api/remediate":
# Intentional human bypass for demo scope: the dashboard's
# hold-to-confirm control plays the "human operator" role here.
# Approval semantics are enforced on the agent path (TrueForge
# approval gates on the MCP tools), not on this endpoint.
action = body.get("action")
service = body.get("service", "")
if action == "rollback":
result = state.rollback_deploy(service, body.get("deploy_id", ""))
elif action == "restart":
result = state.restart_service(service)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Destructive remediation is exposed without authentication, authorization, or an approval token. Because docker-compose.yml publishes port 8001 and binds the sidecar to 0.0.0.0, any network-reachable caller can POST a rollback or restart request and mutate infrastructure state, despite the documented requirement for human approval. Require an authenticated, explicitly approved request or keep this endpoint bound to a trusted dashboard-only channel. [security]

Severity Level: Major ⚠️
- ❌ Network callers can trigger dashboard remediation actions.<br/>- ❌ Simulated service state can be altered without approval.<br/>- ⚠️ Impact is limited to fabricated demo infrastructure.

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** mcp-servers/demo-infra/http_api.py
**Line:** 197:207
**Comment:**
	*Security: Destructive remediation is exposed without authentication, authorization, or an approval token. Because `docker-compose.yml` publishes port 8001 and binds the sidecar to `0.0.0.0`, any network-reachable caller can POST a rollback or restart request and mutate infrastructure state, despite the documented requirement for human approval. Require an authenticated, explicitly approved request or keep this endpoint bound to a trusted dashboard-only channel.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +139 to +141
state = get_state()
result = state.rollback_deploy(service, deploy_id)
return json.dumps(result, indent=2)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The MCP tools claim to require human approval, but these handlers perform the mutation immediately and do not inspect any approval state or request context. A direct MCP client can therefore invoke either tool without the approval workflow described in the tool instructions and incident-response contract. Enforce approval before calling the state mutation, or expose only an approval-aware execution path. [security]

Severity Level: Major ⚠️
- ❌ Direct MCP clients can bypass documented approval gates.<br/>- ❌ Rollback and restart mutate shared demo state immediately.<br/>- ⚠️ Consequences remain limited to simulated infrastructure.

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** mcp-servers/demo-infra/server.py
**Line:** 139:141
**Comment:**
	*Security: The MCP tools claim to require human approval, but these handlers perform the mutation immediately and do not inspect any approval state or request context. A direct MCP client can therefore invoke either tool without the approval workflow described in the tool instructions and incident-response contract. Enforce approval before calling the state mutation, or expose only an approval-aware execution path.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codeant-ai

codeant-ai Bot commented Aug 27, 2026

Copy link
Copy Markdown

CodeAnt Nitpicks

2 code suggestions

1. Unknown services are reported as successful metrics responses instead of missing resources.

Api mismatch · mcp-servers/demo-infra/http_api.py:121-137


2. Nonpositive limits produce incorrect collection sizes through Python slicing.

Logic error · mcp-servers/demo-infra/state.py:204-213

@ADITYA-tp01

Copy link
Copy Markdown
Owner Author

Fix for finding "REST bypasses remediation approval" (commit 047ca37 on #4, a44eb7c on #5)

Confirmed — the POST /api/remediate (rollback/restart), /api/chaos, and /api/reset endpoints were genuinely open to any client reaching port 8001 without a credential, while compose binds 0.0.0.0 for host reachability. This closed the loop on the approval gap.

Fix (http_api.py):

  • All state-mutating POST endpoints now require the shared Authorization: Bearer <token> header.
  • The token defaults to local-demo-token (read from DEMO_INFRA_TOKEN env); the dashboard, trigger_incident.py, and compose all use the same default, so the local demo works out of the box with zero configuration.
  • For shared/LAN-accessible deployments, override DEMO_INFRA_TOKEN in .env and the dashboard env; non-matching requests receive 401 Unauthorized.
  • GET endpoints remain open (read-only state queries for rendering).

Wiring:

  • apps/dashboard/lib/demo-infra.ts: now sends Authorization: Bearer <DEMO_INFRA_TOKEN> (defaults to local-demo-token).
  • scripts/trigger_incident.py: same token default on the /api/chaos call.
  • docker-compose.yml: DEMO_INFRA_TOKEN=${DEMO_INFRA_TOKEN:-local-demo-token} on the sidecar service.
  • .env.example: documents the token and its purpose.

Test added: test_http_mutations_require_token (correct header accepted, missing/wrong header rejected) — 16/16 pass.

Note on the original finding text: the claim "allowing every CORS origin" reflected a prior state; the allowlist (localhost:3000/3001 only) was already in place. The binding to 0.0.0.0 remains intentional so the host can reach the containerized sidecar — but it is now authenticated, which closes the bypass.

@ADITYA-tp01

Copy link
Copy Markdown
Owner Author

Requesting dismissal of PR #4 Finding #6 "REST bypasses remediation approval"

This finding was created against the original PR review. The CORS wildcard and loopback issues it describes were already fixed in earlier commits (CORS allowlist for localhost:3000/3001, loopback default). The remaining concern — unauthenticated state-mutating endpoints — was addressed in commit 047ca37:

Fix applied (http_api.py): all POST endpoints (/api/remediate, /api/chaos, /api/reset, /api/alerts/*/ack, /api/sessions) now require:

Authorization: Bearer <DEMO_INFRA_TOKEN>

Default token local-demo-token is wired consistently through the sidecar, compose, dashboard lib, and trigger script. The 0.0.0.0 bind remains intentional so the Windows host can reach the containerized sidecar — but it is now authenticated. Non-matching requests receive 401 Unauthorized. Test: test_http_mutations_require_token (16/16 pass).

The finding's evidence primarily cites state.py[346-413] and server.py[24-29] (the bind config), which are intentionally unchanged — the bind stays 0.0.0.0 for Docker, and the state mutations remain on the approval-required path for the agent (enforced by TrueForge), with the REST path now gated by the shared token for the human-operator role.

Please dismiss with this reasoning.

@codeant-ai

codeant-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot removed the size:XXL This PR changes 1000+ lines, ignoring generated files label Aug 29, 2026
@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant