Merge develop into main: AI² 2.0 (agent, MCP tools, CI, Helm), develop @ de66ac36

``> Correction (22 Oct 2026): this MR was opened with the title and description of a single fix

(AUTH_ENABLED / mcp_use OAuth discovery, kept below). It actually merged all of develop into main: 84 commits from 13 Mar to 21 Oct 2026 (Kostas Chartsias 51, Natalia Grigoriadou 29, Dimitrios Gogos 4), 63 files, +2803/−583. The merge commit 3cc859b1 keeps the original message; main is identical to develop at de66ac36. No history was rewritten.

What this merge brought into main

  • Agent: moved from Quart to FastAPI; agent memory with Redis; prompt types; multi-step agentic operations; OEG errors surfaced through ToolError; user token forwarded to OEG; AUTH_ENABLED switch.

  • MCP tools: edge application definitions, zones, instantiation and required resources (typed to the OEG schema); Traffic Influence; Location retrieval; QoD fixes; legacy tools removed; OEG base URL defaults for CAMARA.

  • Quality: pytest suite, static type checks, lint and format, GitLab SAST.

  • Build and deploy: CI image build and push with buildx on the docker runner (no provenance attestation); base-image and dependency fixes (langchain, mcp); Helm chart with Redis; vendored Helm dependencies untracked.

  • Docs: README updates.``

  • AUTH_ENABLED (default true, unchanged behaviour): when false, the agent accepts chats without a bearer token and the MCP server calls OEG without one. Needed for platforms deployed with global.auth.enabled=false, where the portal sends no token and the chat failed with 401. A token that is present is still forwarded.

  • No more OAuth discovery per chat: mcp_use defaults a server's auth to {}, which makes it probe /.well-known OAuth endpoints on every connection and log "Failed to discover OAuth/OIDC metadata". The agent now sets "auth": None; the token still travels in headers. The unused use-oauth2/use-oidc keys are removed.

Tests

  • New tests for both layers with AUTH_ENABLED=false; existing 401 tests unchanged.
  • ruff, format, mypy and pytest (143) pass. ``
Edited by Dimitrios Gogos

Merge request reports

Loading
+7 −0
Changes for ai_agent/prompts/__init__.py: 7 added lines, 0 removed lines.
Original line number Diff line number Diff line
from ai_agent.prompts.memory import CONVERSATION_MEMORY_PROMPT
from ai_agent.prompts.tool_agent import TOOL_USING_ASSISTANT_PROMPT

__all__ = [
    "CONVERSATION_MEMORY_PROMPT",
    "TOOL_USING_ASSISTANT_PROMPT",
]
+5 −0
Changes for ai_agent/prompts/memory.py: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
CONVERSATION_MEMORY_PROMPT = """
You are an intelligent assistant that uses available tools to respond to user requests.
1. Identify the appropriate tool.
2. Summarize the tool's result and be as concise as possible with the less 
"""
+6 −0
Changes for ai_agent/prompts/tool_agent.py: 6 added lines, 0 removed lines.
Original line number Diff line number Diff line
TOOL_USING_ASSISTANT_PROMPT = """
You are an intelligent assistant that uses available tools to respond to user requests.
1. Identify the appropriate tool.
2. Summarize the tool's result naturally.
3. Offer helpful next steps.
"""
+1 −0
Changes for ai_agent/routes/__init__.py: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
+14 −10
Changes for ai_agent/routes/debug.py: 14 added lines, 10 removed lines.
Original line number Diff line number Diff line
import io
import sys
import objgraph
from quart import Response, jsonify

def register_debug_routes(app):
import objgraph  # type: ignore[import-untyped]
from fastapi import APIRouter
from fastapi.responses import PlainTextResponse

    @app.route("/health", methods=["GET"])
router = APIRouter()


@router.get("/health")
async def health_check():
    """Health check endpoint for Kubernetes liveness/readiness probes"""
        return jsonify({"status": "healthy"}), 200
    return {"status": "healthy"}


    @app.route("/debug/objgraph", methods=["GET"])
@router.get("/debug/objgraph")
async def debug_objgraph():
    buf = io.StringIO()
    original_stdout = sys.stdout
    sys.stdout = buf

    try:
        objgraph.show_growth(limit=10)

        sys.stdout = sys.__stdout__
        return Response(buf.getvalue(), mimetype="text/plain")
    finally:
        sys.stdout = original_stdout
    return PlainTextResponse(buf.getvalue())
Loading
Loading