Skip to content

feat(mcp): read-only MCP server — FOREMAN as an open node (F7) - #16

Merged
patricznr1 merged 2 commits into
mainfrom
feat/f7-mcp-server
Jun 16, 2026
Merged

feat(mcp): read-only MCP server — FOREMAN as an open node (F7)#16
patricznr1 merged 2 commits into
mainfrom
feat/f7-mcp-server

Conversation

@patricznr1

@patricznr1 patricznr1 commented Jun 16, 2026

Copy link
Copy Markdown
Owner

F7 — MCP-Server (FOREMAN als offener Knoten)

Eine neue Schicht src/foreman/mcp/ macht FOREMAN zum read-only Model-Context-Protocol-Server (Anthropic SDK / FastMCP, Streamable HTTP): die aggregierten Reasoner-Erkenntnisse werden als saubere, maschinenlesbare Tools an Drittsysteme (Simulation / ERP / Energiemanagement) gereicht. Schnittstellen-Schicht über dem schon Gebauten — keine neue Logik.

Was

  • 11 read-only Tools auf die bestehenden Reasoner-Read-Pfade: list_machines / get_machine (+ komponierter Status), get_drift_status, get_alarms(machine_id?, since?, severity?), list_failure_predictions / get_failure_prediction, get_worker_recommendation, list_event_chains / get_event_chain, search_notes, get_readings. Kein Schreib-Tool, kein Reasoner-/LLM-Trigger über MCP.
  • MCP-eigene Read-Schicht (reads.py): die Read-Logik der Reasoner lag bisher inline in den HTTP-Routern ohne wiederverwendbare Service-Methode. Statt sechs Router zu refactoren, bekommt MCP eine eigene, injizierte, testbare Read-Schicht (ausschließlich SELECT) — chirurgisch. (Entscheidung im Review geklärt.)
  • AI-Act-Transparenz-Wrapper (Art. 50(2)): jeder KI-stämmige Output trägt ai_generated/generated_by="foreman-ai"/requires_human_review/model_version (Vorhersage/Empfehlung zusätzlich validation_status/data_regime/validation_caveat). Nicht-KI-Daten tragen keine KI-Flags — ein Validator erzwingt die Ehrlichkeit strukturell.
  • Auth (auth.py): dedizierter FOREMAN_MCP_-Token (SecretStr, zeitkonstant, Fail-Closed, Produktions-Fail-Fast), Token-Bucket-Rate-Limit; eigenständige ASGI-App mit eigener /health//metrics.
  • PII: nur HMAC-Token / NER-maskierter Text raus, Token nie aufgelöst; kein Vektor, keine users-Felder.
  • IP-Wording: kein internes Vokabular in Tool-Strings (Hidden-Term-Scan als Test); SHAP heißt extern contribution.
  • foreman_mcp_*-Metriken.

Warum

Zweiter Differenzierungs-Pfeiler „Plattform statt App"; erfüllt zugleich AI-Act-Maßnahme §10.5(2) — das MCP-Transparenz-Flag von „dokumentiert" auf „gebaut".

Getestet

tests/mcp/ — Transparenz-Ehrlichkeit, Tool-Korrektheit + Read-only (keine Seiteneffekte), Auth-Reject (401) + Rate-Limit (429), PII-Schutz, echter SDK-Handshake (Tool-Registry), struktureller No-Actuation- + Hidden-Term-Scan.

  • mypy --strict 0 (109 Dateien) · ruff + ruff format --check clean
  • MCP-Tests 47 passed; restliche Suite 483 passed, 2 skipped; MCP-Coverage 96 %
  • Die F-PRED-shap/lightgbm-Tests crashen lokal auf Python 3.13 (bekannt) und laufen in CI/Linux — ihr Modul ist unberührt.

Doku (selber Commit)

GROUND_TRUTH §4 (MCP-Schnittstelle), §10.5 (Maßnahme 2 „gebaut"), neue §17 (MCP-Vertrag) + §18 Privacy & Compliance + §19 Security; WALKTHROUGH F7-Abschnitt; AI-Act-Assessment Maßnahme 2.

Hinweise für den Reviewer

  • .env.example: FOREMAN_MCP_TOKEN=<32+ Byte> muss manuell ergänzt werden (der Hook blockt .env*-Zugriffe). Ohne Token läuft der Server Fail-Closed; in Produktion bricht require_secure_token den Start ab.
  • tests/mcp/ bewusst ohne __init__.py: ein Test-Paket namens mcp würde sonst das SDK-Paket mcp überschatten. Dafür wurde tests/integration/__init__.py ergänzt, um eine test_auth-Basenamen-Kollision aufzulösen.
  • Hidden-Term-Scan: über alle Tool-Namen/-Beschreibungen/-Schemata sauber (tests/mcp/security/test_ip_wording.py).

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Neue read-only MCP-Schnittstelle über Streamable HTTP mit Tool-Set für Maschinenstatus, Alarme, Drift-Warnungen, Ausfallvorhersagen, Werker-Empfehlungen, Ereignisketten, Sensortrend-Daten und Notizsuche.
    • Token-basierte Authentifizierung mit Ratenbegrenzung; offene Zugänge für /health und /metrics.
    • Transparenzkennzeichnung für KI-generierte Outputs gemäß AI-Act.
  • Documentation

    • WALKTHROUGH- und Compliance-Dokumentation um den Status F7 (MCP-Schnittstelle) sowie detaillierte Transparenz-/Schema-Anforderungen ergänzt.

Expose the reasoners' stored insights to third parties (simulation, ERP,
energy management) via a read-only Model-Context-Protocol server (FastMCP,
Streamable HTTP) — the "platform, not app" pillar. New layer src/foreman/mcp/.
This is an interface layer over the existing read paths; it adds no reasoning.

What:
- 11 read-only tools mapped onto the existing reasoner read paths: machines +
  composed status, alarms (since/severity), drift status, failure predictions,
  worker recommendations, event-chain explanations, semantic note search,
  aggregated readings. No write tool, no reasoner/LLM trigger over MCP.
- MCP-owned read layer (reads.py): the reasoner read logic lived inline in the
  HTTP routers with no reusable service method; MCP gets its own injected,
  testable, SELECT-only read layer (decision confirmed in review) rather than
  refactoring six existing routers.
- Honest AI-Act transparency wrapper (Art. 50(2)): every AI-stemming output
  carries ai_generated / generated_by="foreman-ai" / requires_human_review /
  model_version (+ validation_status / data_regime / validation_caveat for
  predictions and recommendations). Non-AI data (master data, readings, alarms)
  carries no AI flags — enforced structurally by a model validator.
- Dedicated read-only token auth (FOREMAN_MCP_, SecretStr, constant-time
  compare, fail-closed, production fail-fast) + token-bucket rate limit;
  standalone ASGI app with its own /health and /metrics.
- PII: only HMAC tokens / NER-masked text leave MCP; tokens are never resolved;
  no embedding vector, no users fields.
- IP wording: no internal vocabulary in tool names/descriptions/schemas
  (hidden-term scan as an acceptance test); SHAP is exposed as "contribution".
- foreman_mcp_* metrics (tool, result, latency).

Why: second differentiator pillar; fulfils AI-Act measure §10.5(2) — the MCP
transparency flag moves from "documented" to "built".

Tested (tests/mcp/): transparency honesty, tool correctness + read-only (no
side effects), auth reject (401) + rate limit (429), PII safety, real SDK
handshake (tool registry), structural no-actuation + hidden-term scan. MCP
layer coverage 96%. mypy --strict 0, ruff + ruff format clean.
Note: the F-PRED shap/lightgbm suite is verified in CI (crashes locally on
Python 3.13); its module is untouched by this change.

Docs (same commit): GROUND_TRUTH §4 (MCP interface), §10.5 (measure 2 "built"),
new §17 (MCP contract) + §18 Privacy & Compliance + §19 Security; WALKTHROUGH
F7 section; eu-ai-act-assessment measure 2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f3ac731-b6d4-4e3f-819e-47f2ee10fa26

📥 Commits

Reviewing files that changed from the base of the PR and between 94b829d and 1b5b260.

📒 Files selected for processing (5)
  • pyproject.toml
  • src/foreman/mcp/reads.py
  • src/foreman/mcp/tools.py
  • tests/mcp/security/test_ip_wording.py
  • tests/mcp/security/test_no_actuation.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/mcp/security/test_ip_wording.py
  • pyproject.toml
  • tests/mcp/security/test_no_actuation.py
  • src/foreman/mcp/reads.py
  • src/foreman/mcp/tools.py

📝 Walkthrough

Walkthrough

Das PR implementiert das vollständige F7-MCP-Interface als neues foreman.mcp-Paket: ein AI-Act-konformer Transparenz-Wrapper mit gefrorener Pydantic-Validierung, Ausgabe-Schemata, eine read-only DB-Zugriffsschicht, 11 MCP-Tools, ASGI-Auth-Middleware mit Token-Bucket-Rate-Limiting, ein Starlette-ASGI-Server mit Prometheus-Metriken und eine vollständige Test-Suite (Security, PII, Tool-Verhalten).

Changes

F7 MCP-Schnittstelle

Layer / File(s) Summary
Transparenz-Wrapper und Output-Schemata
src/foreman/mcp/__init__.py, src/foreman/mcp/transparency.py, src/foreman/mcp/schemas.py
AiTransparency (eingefroren, Pydantic) erzwingt per model_validator strukturelle Ehrlichkeit für KI- vs. Nicht-KI-Outputs; ai_transparency/non_ai_transparency-Builder sowie alle Output-Schemata (MachineOut bis NoteSearchOut) mit geschlossenen Literal-Typen und durchgängigem transparency-Feld.
Read-only DB-Zugriffsschicht
src/foreman/mcp/reads.py
Implementiert alle asynchronen SELECT-Funktionen für Maschinen, Alarme, Drift, Vorhersagen, Empfehlungen, Ereignisketten, readings_1m-Aggregation (inkl. truncated-Flag) und vektorielle Notizsuche ohne LLM-Trigger.
Auth-Middleware und Token-Rate-Limiting
src/foreman/mcp/auth.py
McpSettings (env-basiert, SecretStr, Entropie-Check), extract_bearer, verify_mcp_token (Fail-Closed, hmac.compare_digest), TokenBucket-Limiter mit injizierbarer Uhr sowie McpAuthMiddleware (401/429, offene Pfade /health//metrics ausgenommen).
MCP Tool-Implementierungen
src/foreman/mcp/tools.py
Definiert alle 11 read-only MCP-Tools mit read-only Session-Context-Manager, Latenz-/Erfolgs-Metrik-Wrapper, ORM-zu-Schema-Mapping-Helfern und Input-Validierung (Severity-Allowlist, k/hours-Clamp, ValueError bei fehlenden Entitäten).
Prometheus-Metriken für MCP
src/foreman/observability/metrics.py
Fügt MCP_REQUESTS-Counter und MCP_LATENCY-Histogramm sowie den observe_mcp_call-Helper dem gemeinsamen Metrik-Modul hinzu.
MCP ASGI-Server und Prozess-Einstiegspunkt
src/foreman/mcp/server.py
build_mcp_server() (FastMCP, read-only Tool-Registry), build_mcp_app() (Starlette mit DB-Lifecycle, McpAuthMiddleware, gemounteter MCP-Transport) und main()-Einstiegspunkt via uvicorn.
Abhängigkeit und mypy-Konfiguration
pyproject.toml
mcp>=1.9 als Runtime-Dependency; mypy-Overrides für mcp/mcp.* (untyped-calls, fehlende Stubs).
Unit-Tests: Transparenz und Auth
tests/mcp/test_transparency.py, tests/mcp/test_auth.py
Prüft AiTransparency-Invarianten, Builder-Korrektheit, frozen/extra="forbid"-Verhalten sowie verify_mcp_token, extract_bearer, Middleware-Statuscodes (200/401/429) und TokenBucket-Determinismus.
Integrations-Tests: Server, Tools und PII
tests/mcp/conftest.py, tests/mcp/test_server.py, tests/mcp/test_tools.py, tests/mcp/test_pii.py
Test-Fixture mcp_session, Server-Registry-/Handshake-/Metrik-Tests, vollständige Tool-Integrationstests (Status, Alarme, Drift, Vorhersagen, Ereignisketten, Notizen, Readings, Read-only-Garantie) sowie PII-Prüfungen (Schema-Felder, Pseudonym-Token, NER-Maskierung).
Security-Tests: No-Actuation und IP-Wording
tests/mcp/security/test_no_actuation.py, tests/mcp/security/test_ip_wording.py
Statischer Quelltext-Scan auf Schreib-/Trigger-Muster, dynamische Annotation-Prüfung (readOnlyHint/destructiveHint) und Scan aller Tool-Strings auf internes Vokabular.
Dokumentation und Compliance-Updates
GROUND_TRUTH.md, docs/WALKTHROUGH.md, docs/compliance/eu-ai-act-assessment.md
Ergänzt F7-Status, §17-MCP-Vertrag, Security-Hardening-Nachweise in GROUND_TRUTH; neuer F7-Abschnitt in WALKTHROUGH; konkretisierte Transparenzmaßnahme 2 und Implementierungsstatus in der EU-AI-Act-Bewertung.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Externes System
  participant Middleware as McpAuthMiddleware
  participant Server as Starlette/FastMCP
  participant Tools as tools.py
  participant Reads as reads.py
  participant DB as PostgreSQL

  rect rgba(100, 150, 200, 0.5)
    note over Client,Middleware: Authentifizierung & Rate-Limiting
    Client->>Middleware: POST /mcp (Authorization: Bearer token)
    Middleware->>Middleware: extract_bearer()
    Middleware->>Middleware: verify_mcp_token() [hmac.compare_digest]
    Middleware->>Middleware: TokenBucket.allow()
    alt Ungültiger Token
      Middleware-->>Client: 401 WWW-Authenticate: Bearer
    else Rate-Limit überschritten
      Middleware-->>Client: 429 Too Many Requests
    else Token gültig & Limit ok
      Middleware->>Server: Request weiterleiten
    end
  end

  rect rgba(100, 200, 150, 0.5)
    note over Server,DB: Tool-Ausführung (read-only)
    Server->>Tools: Tool-Dispatch (z.B. get_failure_prediction)
    Tools->>Tools: _read_session() [kein commit]
    Tools->>Reads: SELECT-Funktion (get_prediction)
    Reads->>DB: parameterisierte SQL-Query
    DB-->>Reads: ORM-Records
    Reads-->>Tools: Sequence[FailurePredictionRecord]
    Tools->>Tools: ai_transparency(model_version, caveat)
    Tools-->>Server: FailurePredictionOut (+ AiTransparency)
    Server-->>Client: JSON (ai_generated/requires_human_review)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • patricznr1/foreman#8: Die neue MCP-Read-Schicht src/foreman/mcp/reads.py::search_notes und mcp/tools.py::search_notes konsumieren direkt die EmbeddingProvider/semantische Notizsuche-Abstraktionen aus diesem PR.
  • patricznr1/foreman#6: Die MCP-Tools list_event_chains/get_event_chain lesen ReasonerExplanationRecord-Felder (Anchor-Alarm, Hypothese, Confidence) aus, die in diesem PR persistiert werden.
  • patricznr1/foreman#4: Beide PRs erweitern src/foreman/observability/metrics.py um Prometheus-Metriken — direkter Überlappungspunkt im gemeinsamen Metrik-Modul.

Poem

🐇 Ein neues Fenster öffnet sich weit,
Maschinen reden, doch schreiben — nein, nie!
Bearer-Token, Rate-Limit, bereit,
AI-Act-Transparenz ziert jede Partie.
Read-only bleibt goldene Regel hier,
Das Kaninchen hüpft stolz durchs MCP-Tor. 🚪✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 49.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Der Titel beschreibt präzise den Hauptzweck: Einführung eines Read-only MCP-Servers, der FOREMAN als offenen Knoten exponiert. Dies spiegelt den Kern der Änderungen wider.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/f7-mcp-server

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
GROUND_TRUTH.md (1)

5-5: 💤 Low value

Statuszeile F7: Prägnant und vollständig, aber sehr dicht.

Die Zusammenfassung der drei Invarianten, der Architektur-Entscheidungen (ASGI, Token, Metriken, AI-Act-Maßnahme 2) ist komplett und richtig. Zur Lesbarkeit wäre eine Auflösung in Stichpunkte nach dem ersten Satz („read-only) hilfreich — die aktuelle Inline-Liste ist verständlich, aber bei einer zukünftigen Aktualisierung schnell fehleranfällig (jeder Punkt sollte einzeln editierbar bleiben). Optional: den Satz ab „Drei Invarianten" ggf. in eine nummerierte Liste (I/II/III mit jeweils einer kurzen Erklär-Klammer) umwandeln, damit Punkt-Updates leichter fallen.

📋 Vorschlag (optional): Gliederung der Invarianten

Anstelle von:

Drei Invarianten, strukturell verankert: **(I) read-only** — … ; **(II) AI-Act-Transparenz** … ; **(III) IP-Wording** —

Könnte man schreiben:

Drei tragende Invarianten (strukturell verankert):
1. **Read-only, keine Aktorik, kein Reasoner-/LLM-Trigger** — keine Schreib-Pfade, MCP-eigene Read-Schicht `reads.py`.
2. **AI-Act-Transparenz an jedem KI-Output (Art. 50(2))** — gemeinsamer Wrapper `AiTransparency`, Validator erzwingt Ehrlichkeit.
3. **IP-Wording** — kein internes Vokabular in Tool-Namen/-Beschreibungen/-Schemata; Hidden-Term-Scan als Akzeptanzkriterium.

Dies macht zukünftige Aktualisierungen granularer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@GROUND_TRUTH.md` at line 5, The section "Drei Invarianten, strukturell
verankert" in GROUND_TRUTH.md (line 5) is currently formatted as a dense inline
list with semicolons separating three invariants labeled (I) read-only, (II)
AI-Act-Transparenz, and (III) IP-Wording. To improve readability and
maintainability for future updates, refactor this section into a clearly
numbered list (1, 2, 3) where each invariant becomes its own separate line item
with a brief descriptive label and explanation. Break the long inline
definitions into individual, self-contained points so that each invariant can be
independently understood and edited without the risk of punctuation or syntax
errors that come from managing a complex single-sentence structure.
src/foreman/mcp/tools.py (1)

270-285: 💤 Low value

Eingabevalidierung vor Session-Erwerb würde unnötige Ressourcen-Allokation vermeiden.

Die Severity-Validierung (Zeile 277) erfolgt erst nach dem Betreten beider Context Manager. Bei ungültiger Eingabe wurde die Session bereits erworben. Eine frühere Validierung vor async with würde dies vermeiden.

♻️ Optionale Optimierung
 async def get_alarms(
     machine_id: int | None = None,
     since: datetime | None = None,
     severity: str | None = None,
 ) -> AlarmListOut:
     """Liest Alarme (inkl. Drift-Warnungen), optional gefiltert nach Maschine/Zeit/Schwere."""
+    if severity is not None and severity not in _ALLOWED_SEVERITIES:
+        raise ValueError(
+            f"Unbekannte severity '{severity}'. Erlaubt: {sorted(_ALLOWED_SEVERITIES)}."
+        )
     async with _measured("get_alarms"), _read_session() as session:
-        if severity is not None and severity not in _ALLOWED_SEVERITIES:
-            raise ValueError(
-                f"Unbekannte severity '{severity}'. Erlaubt: {sorted(_ALLOWED_SEVERITIES)}."
-            )
         alarms = await reads.list_alarms(
             session, machine_id=machine_id, since=since, severity=severity
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/foreman/mcp/tools.py` around lines 270 - 285, The severity parameter
validation in the get_alarms function occurs inside the async with block after
the session is already acquired, which wastes resources if invalid input is
provided. Move the severity validation check (the condition checking if severity
is not None and severity not in _ALLOWED_SEVERITIES) to execute before entering
the async with statement containing _measured and _read_session, so that invalid
inputs are rejected without acquiring unnecessary resources.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyproject.toml`:
- Around line 60-62: The mcp dependency constraint currently uses only a lower
bound (mcp>=1.9), which allows automatic upgrades to version 2.0.0 when it
becomes stable, introducing breaking changes to the project. Update the mcp
dependency constraint to include an upper bound that prevents major version
upgrades by changing it from mcp>=1.9 to mcp>=1.25,<2 (or minimally
mcp>=1.9,<2), ensuring the project stays within the stable 1.x release series
while also updating to a more recent stable version within that series as
recommended by the official SDK.

In `@src/foreman/mcp/reads.py`:
- Around line 224-252: The load_readings function accepts a limit parameter that
defaults to MAX_READING_POINTS but does not enforce this as an upper bound,
allowing callers to request larger result sets than intended. Add validation in
the load_readings function to cap the limit parameter to not exceed
MAX_READING_POINTS, either by clamping the value to the maximum or by raising an
error if the caller attempts to exceed it, ensuring the documented limit is
actually enforced regardless of what value is passed by the caller.

In `@tests/mcp/security/test_ip_wording.py`:
- Around line 40-43: The test function
test_tool_strings_carry_no_internal_vocabulary is missing a validation check to
ensure tools are actually registered before verifying their content. After
calling await server.list_tools() and storing the result in tool_list, add an
assertion to confirm that tool_list is not empty. This prevents the security
test from passing trivially if the tool registry happens to be empty, ensuring
the verification of tool strings is actually meaningful.

In `@tests/mcp/security/test_no_actuation.py`:
- Around line 37-45: The pattern matching in the
test_mcp_source_has_no_write_or_reasoner_trigger function performs a
case-sensitive check when comparing patterns against the source code (line 43:
`if pattern in source`). This allows SQL write strings in lowercase (like
`insert into`) to bypass the security check. Make the pattern comparison
case-insensitive by converting the source text to lowercase before checking if
each pattern from _FORBIDDEN_PATTERNS exists within it, ensuring the security
scan catches forbidden patterns regardless of their case.
- Around line 48-56: Add explicit pytest async markers to both test files for
clarity and robustness. In tests/mcp/security/test_no_actuation.py (lines
48-56): add `import pytest` at the top of the file if not already present, then
add the `@pytest.mark.asyncio` decorator immediately before the
`test_every_registered_tool_is_read_only` function. In
tests/mcp/security/test_ip_wording.py (lines 40-57): add `import pytest` at the
top of the file if not already present, then add the `@pytest.mark.asyncio`
decorator immediately before the
`test_tool_strings_carry_no_internal_vocabulary` function.

---

Nitpick comments:
In `@GROUND_TRUTH.md`:
- Line 5: The section "Drei Invarianten, strukturell verankert" in
GROUND_TRUTH.md (line 5) is currently formatted as a dense inline list with
semicolons separating three invariants labeled (I) read-only, (II)
AI-Act-Transparenz, and (III) IP-Wording. To improve readability and
maintainability for future updates, refactor this section into a clearly
numbered list (1, 2, 3) where each invariant becomes its own separate line item
with a brief descriptive label and explanation. Break the long inline
definitions into individual, self-contained points so that each invariant can be
independently understood and edited without the risk of punctuation or syntax
errors that come from managing a complex single-sentence structure.

In `@src/foreman/mcp/tools.py`:
- Around line 270-285: The severity parameter validation in the get_alarms
function occurs inside the async with block after the session is already
acquired, which wastes resources if invalid input is provided. Move the severity
validation check (the condition checking if severity is not None and severity
not in _ALLOWED_SEVERITIES) to execute before entering the async with statement
containing _measured and _read_session, so that invalid inputs are rejected
without acquiring unnecessary resources.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 65877bd5-6419-4ea4-baa2-ab2273d87cd0

📥 Commits

Reviewing files that changed from the base of the PR and between 8b194ed and 94b829d.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • GROUND_TRUTH.md
  • docs/WALKTHROUGH.md
  • docs/compliance/eu-ai-act-assessment.md
  • pyproject.toml
  • src/foreman/mcp/__init__.py
  • src/foreman/mcp/auth.py
  • src/foreman/mcp/reads.py
  • src/foreman/mcp/schemas.py
  • src/foreman/mcp/server.py
  • src/foreman/mcp/tools.py
  • src/foreman/mcp/transparency.py
  • src/foreman/observability/metrics.py
  • tests/integration/__init__.py
  • tests/mcp/conftest.py
  • tests/mcp/security/test_ip_wording.py
  • tests/mcp/security/test_no_actuation.py
  • tests/mcp/test_auth.py
  • tests/mcp/test_pii.py
  • tests/mcp/test_server.py
  • tests/mcp/test_tools.py
  • tests/mcp/test_transparency.py

Comment thread pyproject.toml Outdated
Comment thread src/foreman/mcp/reads.py
Comment thread tests/mcp/security/test_ip_wording.py
Comment thread tests/mcp/security/test_no_actuation.py
Comment thread tests/mcp/security/test_no_actuation.py
- pyproject: pin mcp>=1.9,<2 — mcp 2.0 (July 2026) is breaking and moves
  transport config to streamable_http_app(); avoid an unguarded auto-upgrade.
- reads.load_readings: clamp limit to MAX_READING_POINTS so the documented
  load cap is enforced regardless of caller (defense-in-depth).
- tools.get_alarms: validate severity before acquiring the read session.
- tests/mcp/security/test_no_actuation: scan source case-insensitively so a
  lowercase write/SQL pattern can't slip the read-only gate.
- tests/mcp/security/test_ip_wording: assert a non-empty tool registry so the
  hidden-term scan can't pass trivially.

Declined (with reasoning, see PR threads):
- explicit @pytest.mark.asyncio on the two security tests — the whole suite
  runs under asyncio_mode=auto without markers; adding them to 2 of N async
  tests is inconsistent and gives no real protection.
- bulletizing the GROUND_TRUTH status line — the dense single-paragraph
  **Status:** line is the established format across all phases.

mypy --strict 0, ruff + format clean, 47 MCP tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@patricznr1
patricznr1 merged commit 3588c9c into main Jun 16, 2026
2 checks passed
@patricznr1
patricznr1 deleted the feat/f7-mcp-server branch June 16, 2026 13:00
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