Connect Sibyl to an AI agent
Sibyl trains BigQuery ML propensity models on a GA4 property's BigQuery export and delivers High / Medium / Low audiences back to GA4, all inside your own Google Cloud project. Every install also serves a Model Context Protocol endpoint, so an agent can operate it in conversation: performance briefings, building and running models, run status, export health. Two ways to connect a Gemini Enterprise agent are below; any MCP client works the same way.
The endpoint belongs to your install, not to this website — it is your own Cloud Run service, at /api/mcp. Queries hit it directly, so nothing about your models or your GA4 data passes through Making Science.
- Connection details
- Path A — native connector
- Path B — ADK agent
- System instructions
- The tools
- Demo script
- Troubleshooting
Connection details
Everything below uses these two values — the only blanks on this page. If someone else runs the install you're connecting to, they send you both.
| MCP endpoint | SIBYL_MCP_URL — e.g. https://sibyl-app-acme-backend-xxxxxxxxxx-uc.a.run.app/api/mcp. It is the install's backend service, a different host from the dashboard you sign into — the dashboard's address will not work. Sibyl's own Settings → Agent access shows the exact URL, copyable. |
| Credential | Path A: none to pass around — the install allowlists the service-account email your connector authenticates as. Path B: the install's access key (SIBYL_ACCESS_KEY) — the same key the dashboard asks for. Keep it server-side. |
Path A — native connector (no code)
- Gemini Enterprise console → add a data source → Custom MCP Server (Preview).
- URL: the MCP endpoint above. Authentication: the GCP service-account token option.
- The install allowlists the service account your connector authenticates as. Whoever operates it runs one env-var update (additive — it disturbs nothing else) and the connect succeeds:
gcloud run services update <prefix>-backend --region <region> \ --update-env-vars "SIBYL_MCP_INVOKERS=<connector-sa>@<project>.iam.gserviceaccount.com"
- "Reload custom actions" imports the 14 tools; enable them all. Read tools run without per-action confirmation; action tools prompt first — keep that.
- Create a dedicated agent that uses these actions and paste the system instructions below into its instructions field.
If your tenant doesn't show the Custom MCP Server source or its service-account auth option yet, use Path B.
Path B — ADK agent
A small agent built with Google's Agent Development Kit, deployed to Vertex AI Agent Engine, registered in Gemini Enterprise. The system instructions ship in the code.
sibyl-analyst/
.env # local trial only — never commit
requirements.txt # google-adk[mcp]>=2.6,<3 (the [mcp] extra is required)
sibyl_analyst/
__init__.py # from .agent import root_agent
agent.py # below, verbatimagent.py
"""The Sibyl analyst — an ADK agent over Sibyl's MCP surface."""
from __future__ import annotations
import os
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams
SIBYL_MCP_URL = os.environ["SIBYL_MCP_URL"]
SIBYL_ACCESS_KEY = os.environ.get("SIBYL_ACCESS_KEY", "")
INSTRUCTION = """\
You are the Sibyl analyst — the conversational operator of this
organization's Sibyl install (predictive GA4 audiences, running in the
customer's own Google Cloud project).
You have 14 Sibyl tools. Reach for them instead of asking the user for
anything a tool can fetch. Route intents like this:
- "How are my models doing / which performs best?" →
summarize_model_performance. It returns the ranking, lifts and caveats
precomputed — narrate it exactly; never recompute or re-rank.
- "What could I build a model on?" → list_candidate_events.
- "Build a model for X and run it" → list_candidate_events to confirm X is
trainable → confirm the audience policy with the user (publication_scope
is PERMANENT once the GA4 audiences are created) → create_model → report
the run_id → get_run_status when they ask later.
- "Run everything / retrain / just prediction / just delivery" →
run_pipeline with scope all | retrain | predict | deliver.
- "Is it done? What happened?" → get_run_status (namespace for a live
model, run_id for a first run).
- "How is the export / the data?" → get_export_health.
- Pause, resume or change a schedule → configure_automation.
Cancel a run → cancel_run. Delete a model → delete_model, only after the
user names the model and confirms — then pass confirm=True.
- One model in depth → get_model. "What is this install?" →
get_install_overview.
Numbers:
- Every figure comes from a tool result in this conversation; rates are
already percent. Quote them with their qualifiers, and repeat every
caveat the tool returns. "No conversions observed yet" is a young model,
not a failing one — say so.
Actions:
- Before any action tool: say exactly what will happen and get a yes here
in chat first.
- After dispatching: run_pipeline waits briefly for the worker.
confirmed=true → name the running stage. confirmed=false → "accepted,
not yet confirmed" — usually a cold start, not a failure. Either way the
run continues unattended on Google's side: the user can leave and ask
later.
- Offer to WATCH a dispatched run: get_run_status with wait_seconds=300
streams progress and returns when it settles, so the user is told it
finished instead of asking repeatedly. Say you're watching it.
- If a tool refuses because a model's first run is still in flight, relay
Sibyl's explanation and offer to watch that run instead.
Style:
- Lead with the answer, then the two or three numbers that support it.
Short paragraphs. Call models by their objective event ("the sign_up
model"), not by namespace, unless asked for identifiers.
"""
root_agent = LlmAgent(
# Set SIBYL_AGENT_MODEL to your organization's current Gemini model.
model=os.environ.get("SIBYL_AGENT_MODEL", "gemini-2.5-pro"),
name="sibyl_analyst",
description=(
"Analyzes and operates this organization's Sibyl install: model "
"performance briefings, building and running predictive-audience "
"models, run status, GA4 export health."),
instruction=INSTRUCTION,
tools=[
McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=SIBYL_MCP_URL,
headers={"x-sibyl-access": SIBYL_ACCESS_KEY},
),
),
],
)Run locally, then deploy
# .env — the two values from Connection details, plus your Vertex project SIBYL_MCP_URL=... SIBYL_ACCESS_KEY=... SIBYL_AGENT_MODEL=gemini-2.5-pro GOOGLE_GENAI_USE_VERTEXAI=1 GOOGLE_CLOUD_PROJECT=... GOOGLE_CLOUD_LOCATION=us-central1 pip install -r requirements.txt adk web # local chat against the live install adk deploy agent_engine \ --project ... --region us-central1 \ --staging_bucket gs://... \ ./sibyl_analyst
Set SIBYL_MCP_URL and SIBYL_ACCESS_KEY as environment variables on the deployment (the key via Secret Manager), then register the Agent Engine agent in your Gemini Enterprise agent gallery.
System instructions
The tools arrive with their schemas and descriptions when they import, so the agent already knows they exist and what each does. This block adds the part that doesn't travel with tools: which tool answers which request, when to chain them, what to confirm before acting, and the voice. Paste it as-is into the agent's instructions field (Path A) — it is already inside agent.py on Path B.
You are the Sibyl analyst — the conversational operator of this
organization's Sibyl install (predictive GA4 audiences, running in the
customer's own Google Cloud project).
You have 14 Sibyl tools. Reach for them instead of asking the user for
anything a tool can fetch. Route intents like this:
- "How are my models doing / which performs best?" →
summarize_model_performance. It returns the ranking, lifts and caveats
precomputed — narrate it exactly; never recompute or re-rank.
- "What could I build a model on?" → list_candidate_events.
- "Build a model for X and run it" → list_candidate_events to confirm X is
trainable → confirm the audience policy with the user (publication_scope
is PERMANENT once the GA4 audiences are created) → create_model → report
the run_id → get_run_status when they ask later.
- "Run everything / retrain / just prediction / just delivery" →
run_pipeline with scope all | retrain | predict | deliver.
- "Is it done? What happened?" → get_run_status (namespace for a live
model, run_id for a first run).
- "How is the export / the data?" → get_export_health.
- Pause, resume or change a schedule → configure_automation.
Cancel a run → cancel_run. Delete a model → delete_model, only after the
user names the model and confirms — then pass confirm=True.
- One model in depth → get_model. "What is this install?" →
get_install_overview.
Numbers:
- Every figure comes from a tool result in this conversation; rates are
already percent. Quote them with their qualifiers, and repeat every
caveat the tool returns. "No conversions observed yet" is a young model,
not a failing one — say so.
Actions:
- Before any action tool: say exactly what will happen and get a yes here
in chat first.
- After dispatching: run_pipeline waits briefly for the worker.
confirmed=true → name the running stage. confirmed=false → "accepted,
not yet confirmed" — usually a cold start, not a failure. Either way the
run continues unattended on Google's side: the user can leave and ask
later.
- Offer to WATCH a dispatched run: get_run_status with wait_seconds=300
streams progress and returns when it settles, so the user is told it
finished instead of asking repeatedly. Say you're watching it.
- If a tool refuses because a model's first run is still in flight, relay
Sibyl's explanation and offer to watch that run instead.
Style:
- Lead with the answer, then the two or three numbers that support it.
Short paragraphs. Call models by their objective event ("the sign_up
model"), not by namespace, unless asked for identifiers.Agent name: Sibyl analyst. Description: Analyzes and operates this organization's Sibyl install: model performance briefings, building and running predictive-audience models, run status, GA4 export health.
The tools
| Tool | What it does | Notes |
|---|---|---|
| get_install_overview | Which install this is: project, connected GA4 property, versions. | Good first call in a conversation. |
| list_models | Every model with objective, trained date, band sizes, latest per-band conversion rates. | Rates arrive as conversion_rate_pct — already percent. |
| get_model | One model in depth: schedules, recent activity, performance trend. | — |
| summarize_model_performance | Cross-model comparison, pre-ranked by High-vs-Low lift, caveats attached. | The answer to “how are my models performing?” — the agent narrates, never recomputes. |
| list_candidate_events | GA4 events that could be a model's objective, ranked; blocked events say why. | no_positives / no_negatives are the two blockers. |
| get_export_health | GA4→BigQuery export freshness and daily volume vs the standard-tier cap. | Includes the grant command when a cross-project export still needs one. |
| get_run_status | Live stage-by-stage state for a model (namespace) or a first run (run_id). | wait_seconds (≤300) turns it into a watch: streams progress, returns when the run settles. |
| create_model | Create a model for an objective event and start its first training run. | publication_scope is permanent — the agent must confirm it first. |
| run_pipeline | Run now: scope all · retrain · predict · deliver. Waits briefly for the worker's first heartbeat. | confirmed=false means accepted, not failed. |
| continue_first_run | Advance a staged first run (predict, then deliver). | Only for models created with stop_after_training. |
| cancel_run | Cancel the active run for a model or a first run. | Schedules untouched; delivered audiences are not undone. |
| configure_automation | Change cadence or pause/resume: retrain, or the coupled predict_deliver. | Presets: daily, weekly, monthly, hourly, every 6/12 hours — or cron. |
| delete_model | Permanently delete one model and everything Sibyl created for it. | Refuses without confirm=true; Gemini Enterprise will also ask. |
| update_model_engine | Rebuild a model's generated SQL on the current engine version. | Frozen (whole-script-overridden) stages are reported, not silently skipped. |
One behavior worth knowing: a run takes minutes to hours, so no tool blocks on completion. run_pipeline dispatches and waits up to ~90s for the worker's first heartbeat — confirmed: true names the running stage and includes a Cloud Console link; confirmed: false means accepted-but-not-yet-reporting (normal during a cold start, not a failure). Either way the run continues unattended; asking "did that finish?" later reads the same durable state the Sibyl dashboard renders, so chat and dashboard can't disagree. Or ask the agent to watch: get_run_status with wait_seconds streams progress and answers the moment the run settles.
Demo script — the first conversation
- “What is this install connected to?”
- “How is the export looking?”
- “What events could I build a model on?”
- “How are my models performing?”
- “Build a model that optimizes toward sign_up and run it.” — the agent confirms the audience policy (permanent), creates the model, reports the run id, and tells you that you can leave.
- “Run just the prediction for the sign_up model.”
- Come back later: “Did that run finish?”
Troubleshooting
| Symptom | Fix |
|---|---|
| 401 on connect (Path A) | The token's identity isn't on SIBYL_MCP_INVOKERS — check the exact service-account email. The backend log names what it received; the audience can then be pinned with SIBYL_MCP_AUDIENCE. |
| 401 (Path B) | Wrong or missing access key. It rides the x-sibyl-access header (the agent code does this); Authorization: Bearer <key> also works on the MCP endpoint. |
| No Custom MCP Server source in your console | The Preview hasn't reached your tenant — use Path B. |
| “accepted, not yet confirmed” after starting a run | Cold start; nothing to fix. Ask for the run status a minute later. |
| A tool call returns a product message | Sibyl's own guardrails speaking — “this model is already live”, “deletion is permanent — confirm first”. They are answers, not failures. |
Questions: sibyl@group.makingscience.com. Data handling: privacy policy.