Inspiration
Content teams decide what to make next without evidence. Most tooling in this space answers that question with a guess dressed up as a recommendation, which is worse than no recommendation, because it looks like data.
What it does
CinemaForge is a content production studio built as four Gemini agents orchestrated with the Google Agent Development Kit, running in sequence:
- Analyst connects to the Grafana Cloud MCP server over stdio and runs live PromQL against Grafana Cloud Prometheus
- Writer turns the grounded brief into a hook/body/CTA script
- Director expands it into a shot list with camera, lighting and thumbnail concept
- SEO produces title, description, tags and a scheduling recommendation
One line of brief in, a complete production package out, in about sixty seconds.
How Grafana is used at runtime
The Analyst is bound to the official grafana/mcp-grafana server, launched as a stdio subprocess and filtered to the five tools it needs: query_prometheus, list_datasources, list_prometheus_metric_names, list_prometheus_label_names, search_dashboards. Exposing all 73 tools measurably degraded tool selection, so the toolset is deliberately narrowed.
Before any creative work happens, the Analyst runs real PromQL against the grafanacloud-prom datasource.
The feedback loop
Every production emits OpenTelemetry metrics into Grafana Cloud via the OTLP gateway, tagged with a topic label derived from the brief: productions_started_total, productions_completed_total, productions_duration_seconds, agent_calls_total and agent_duration_seconds (per agent), tool_calls_total, production_events_total.
A later production on the same topic queries those series back out through MCP. The studio measures itself, and the measurement feeds the next run. A dashboard ships in the repo at grafana/dashboard.json.
What the data is, and what it is not
The Analyst reads this pipeline's own production telemetry. It has no viewership data, no views, retention or click-through rate, because no audience analytics source is wired in.
That constraint is enforced rather than hidden. analyze_content_performance returns an explicit data_source field of either grafana_cloud_prometheus or unavailable, and the Analyst is instructed that every figure it cites must come from a tool call it actually made in that session. On an unseen topic it reports "data_source": "unavailable", "productions_recorded": 0, and says so in the brief instead of filling the gap. Reporting "no historical data" is treated as a correct answer.
How we built it
Python, google-adk with Gemini 3.6 Flash, the official mcp-grafana server, OpenTelemetry with an OTLP exporter to Grafana Cloud, FastAPI with SSE streaming, deployed on Cloud Run.
Challenges we ran into
A dead integration looks exactly like a working one. The first build defined the Grafana MCP toolset and never attached it to any agent. The app ran, the health check was green, and briefs were full of confident numbers, all from a hardcoded dict. The tell was that three different topics produced byte-identical figures: 342 views, 38.2% retention, 2.4% CTR. Plausible output is not evidence of a working data path.
increase() and rate() lie on a fresh counter. They need two samples inside the window, so a topic's first production reported zero prior runs while its own data sat in Prometheus, and the duration quantile returned NaN. Fixed by reading instant counter values for existence questions and screening NaN/Inf rather than passing them to the model as measurements.
Cloud Run silently eats OpenTelemetry. CPU is throttled the moment a request finishes, so the 30-second periodic OTLP exporter usually never fires and the run's metrics die in the container. An explicit force_flush() per production was the difference between an empty dashboard and a working loop.
A dependency floor is not a ceiling. mcp 2.0.0 removed mcp.shared.session, which google-adk imports. ADK bounds mcp<2 only under its [mcp] extra, so an unpinned mcp>=1.0 quietly resolved to 2.x in the container and the Grafana toolset vanished at runtime while everything else kept working. It reproduced only in the deployed image.
What we learned
The interesting engineering was not making the agent cite data. It was making it comfortable reporting that there is none. That took an explicit tool contract, a hard instruction that untraceable figures are forbidden, and deleting the mock that made guessing easy.
Also: instrument the thing that instruments. agent_calls_total was named in the Analyst's own prompt and never incremented anywhere, so that query could only ever return empty. The agent dutifully reported "no data" and was right for the wrong reason.
What's next
Ingest a real audience analytics source into Grafana so the same MCP path carries viewership alongside pipeline telemetry, and the honesty guard has richer data to be honest about.
Log in or sign up for Devpost to join the conversation.