Inspiration

Every developer has felt it: you push, CI goes red, and you context-switch to dig through logs for a fix you'll make in thirty seconds — once you find it. We wanted an agent that does the finding, drafts the fix, and hands it to you to approve, so a broken build becomes a single tap instead of a detour.

What it does

ShipMate is a mobile DevOps repair agent. When a GitLab CI pipeline fails, ShipMate:

  • reads the failed job's log and diagnoses the root cause in plain English,
  • drafts the minimal fix on a new branch and opens a merge request with its reasoning,
  • notifies you so you can review and approve from your phone — it never merges on its own,
  • retries the pipeline once you approve. The human stays in control by design: the agent can only diagnose and draft; the pipeline-retry is triggered exclusively by the Approve button.

How we built it

  • Agent: Gemini (gemini-3-flash-preview) on Google Cloud Agent Builder using the Agent Development Kit (ADK), deployed on Cloud Run.
  • Trigger: a GitLab pipeline webhook hits a FastAPI endpoint; the server resolves the failed job + log and invokes the agent.
  • Partner integration — GitLab MCP server: the agent integrates GitLab's official MCP server (gitlab.com/api/v4/mcp) for get_pipeline_jobs, get_job_log, get_merge_request, create_merge_request, manage_pipeline (pipeline retry), and search. We connect through mcp-remote, which manages the OAuth 2.0 lifecycle. agent/mcp_demo.py shows a live round-trip: ShipMate's MCP client connects, lists the GitLab tools, and fetches the very merge request the agent created.
  • State + notifications: Firestore for run state, Expo push for the phone alert.
  • App: React Native + Expo — iOS, Android, and a web export, with a diagnosis card and an approve/reject control.
  • Guardrails (in code, not just prompts): never touches protected branches, max 3 files per commit, branch names restricted to shipmate/*, and a hard "no merge" rule. ## Challenges we ran into
  • CI gating: GitLab requires account verification before shared runners will run pipelines — until we verified, pipelines failed with zero jobs and (correctly) no logs for the agent to read.
  • Stateless OAuth: GitLab's MCP server uses OAuth with rotating refresh tokens. Keeping a token alive inside a stateless Cloud Run container is awkward, so the always-on service uses GitLab's REST API for the same actions, while the agent's MCP integration runs through mcp-remote (which owns token refresh). Both paths exercise the same GitLab capabilities.
  • Agent convergence: early runs wandered through config files instead of fixing the source file named in the log. We fixed this by pre-resolving the failure log on the server, trimming the agent's tool surface, and tightening the instruction so it reads exactly the implicated file and acts.
  • Cloud Run gotchas: fresh run.app URLs 404 at the edge for a few minutes after deploy (not broken — just propagating), and CPU is throttled after the HTTP response unless you set --no-cpu-throttling, which matters for an agent that runs as a background task. ## Accomplishments we're proud of A real, end-to-end loop: break a build → pipeline fails → webhook → Gemini diagnoses → fix branch + merge request opened with reasoning → human approves from the app → pipeline goes green. And a genuine GitLab MCP integration demonstrated against the live server.

What we learned

  • Pre-feeding the agent the exact failure context (the log) and trimming its tools is more effective than a bigger model for getting reliable, fast convergence.
  • "Human-in-the-loop" is most trustworthy when enforced in code (only the Approve endpoint can retry), not just asked for in the prompt.
  • GitLab's MCP tools are plain REST under an OAuth token, so they don't consume Duo credits — the model reasoning does. That distinction unblocked our integration.

What's next

Self-refreshing MCP tokens for an always-on MCP path in production, support for more failure classes (dependency conflicts, config errors), multi-repo support, and learning from which fixes get approved vs rejected.

Built With

Share this project:

Updates