feat(narratives): add agent sidecar (Gemini MCP proxy)#426
feat(narratives): add agent sidecar (Gemini MCP proxy)#426ddebasmita-lab wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a sidecar agent for a multi-container Cloud Run service, including a Dockerfile, a build script, dependency definitions, and documentation of patches applied to the upstream agent code. Feedback on these changes suggests running the Docker container as a non-root user to improve security and prevent runtime permission errors, correcting a comment in the Dockerfile regarding the number of documented patches, and adding strict argument validation to the build script to avoid silently ignoring invalid flags.
| WORKDIR /app | ||
|
|
||
| # System deps: tzdata is needed for ZoneInfo on slim images. | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends tzdata ca-certificates \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| COPY requirements.txt ./ | ||
| RUN pip install -r requirements.txt | ||
|
|
||
| COPY mcp_proxy_only.py ./ | ||
|
|
||
| EXPOSE 5001 | ||
|
|
||
| # Cloud Run health probes hit the agent on PROXY_PORT; the agent is the | ||
| # sidecar container, so the ingress container's nginx reverse-proxies | ||
| # /agent/* to 127.0.0.1:${PROXY_PORT}. | ||
| CMD ["python", "-u", "mcp_proxy_only.py"] |
There was a problem hiding this comment.
Running the container as root poses a security risk. Additionally, if the deployment environment enforces a non-root user, the application will crash with a PermissionError when attempting to write config.json to /app (Patch 7 bootstrap).
To resolve this, create a dedicated non-root user, change the ownership of the /app directory to this user, and run the container using the USER directive.
WORKDIR /app
# System deps: tzdata is needed for ZoneInfo on slim images.
RUN apt-get update \
&& apt-get install -y --no-install-recommends tzdata ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Create a non-root user and group
RUN groupadd -g 10001 appuser \
&& useradd -u 10001 -g appuser -m -s /sbin/nologin appuser
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY mcp_proxy_only.py ./
# Ensure the non-root user owns /app to write config.json at runtime
RUN chown -R appuser:appuser /app
USER appuser
EXPOSE 5001
# Cloud Run health probes hit the agent on PROXY_PORT; the agent is the
# sidecar container, so the ingress container's nginx reverse-proxies
# /agent/* to 127.0.0.1:${PROXY_PORT}.
CMD ["python", "-u", "mcp_proxy_only.py"]
| @@ -0,0 +1,36 @@ | |||
| # Sidecar agent for the Custom Data Commons multi-container Cloud Run service. | |||
| # Vendored from upstream additional_features/mcp_proxy_only.py (Apache-2.0) | |||
| # with six documented patches in PATCHES.md. | |||
| if [[ "${1:-}" == "--push" ]]; then | ||
| echo "Pushing ${FULL_IMAGE}" | ||
| docker push "${FULL_IMAGE}" | ||
| docker push "${AR_REGION}-docker.pkg.dev/${PROJECT}/${AR_REPO}/${IMAGE_NAME}:latest" | ||
| echo "Done. Tag for tfvars: ${TAG}" | ||
| fi |
There was a problem hiding this comment.
Currently, any argument other than --push is silently ignored, which can lead to unexpected behavior (e.g., running ./build.sh --help or ./build.sh -p will build the image but silently skip pushing without warning the user).
Enforce strict argument validation to improve usability and prevent silent failures.
| if [[ "${1:-}" == "--push" ]]; then | |
| echo "Pushing ${FULL_IMAGE}" | |
| docker push "${FULL_IMAGE}" | |
| docker push "${AR_REGION}-docker.pkg.dev/${PROJECT}/${AR_REPO}/${IMAGE_NAME}:latest" | |
| echo "Done. Tag for tfvars: ${TAG}" | |
| fi | |
| if [[ $# -gt 0 ]]; then | |
| if [[ "${1}" == "--push" ]]; then | |
| echo "Pushing ${FULL_IMAGE}" | |
| docker push "${FULL_IMAGE}" | |
| docker push "${AR_REGION}-docker.pkg.dev/${PROJECT}/${AR_REPO}/${IMAGE_NAME}:latest" | |
| echo "Done. Tag for tfvars: ${TAG}" | |
| else | |
| echo "Error: Invalid argument '${1}'" >&2 | |
| echo "Usage: $0 [--push]" >&2 | |
| exit 1 | |
| fi | |
| fi |
e7ee12b to
fe35a51
Compare
Adds the backend the narratives UI talks to at /agent/chat/stream: a Flask SSE proxy that runs the MCP tool loop, optional knowledge-base lookup, and Gemini synthesis, streaming thoughts/text/tool-calls/chart-config back to the client. Mirrors the agent/ layout from the downstream app. - agent/mcp_proxy_only.py — the proxy + streaming chat pipeline - agent/requirements.txt, Dockerfile, build.sh — deploy API keys are read from Secret Manager / config.json / env at runtime; none are committed. Comments describe current behavior (no change-history narration).
fe35a51 to
6288be2
Compare
Overview
Adds the agent sidecar the narratives UI depends on. Until now
narratives/shipped only the front end, which talks to
/agent/chat/stream; this PR addsthat backend under
narratives/agent/, mirroring the layout used in thedownstream app.
What it is
A Flask server (
mcp_proxy_only.py) that proxies the chat pipeline and streamsServer-Sent Events back to the UI:
follow-up questions.
It emits the
session_id,tool_call,thought,text,chart_config,*_sources,usage, anddoneevents the UI already consumes.Files
agent/mcp_proxy_only.py— proxy + streaming chat pipelineagent/requirements.txt— Python depsagent/Dockerfile,agent/build.sh— container build / deploynarratives/.gitignore— negation so the agent's Python stays tracked(the project-level
*.pyignore otherwise excluded it)Security
API keys are read at runtime from Secret Manager (
GEMINI_API_KEYS_SECRET),config.json, or environment variables — none are committed.config.jsonis not included (kept out of version control, as in the source app).
Testing done
python3 -m py_compile agent/mcp_proxy_only.py— clean.