# Servo

> Open-source, self-hostable AI service desk - email becomes tickets, AI drafts replies and works them with real tools, humans approve anything risky. BYOK, SSO, MCP.

- **Type:** MCP server
- **Install:** `agentstack add mcp-ricauts-servo`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [ricauts](https://agentstack.voostack.com/s/ricauts)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [ricauts](https://github.com/ricauts)
- **Source:** https://github.com/ricauts/Servo
- **Website:** https://servoai.org

## Install

```sh
agentstack add mcp-ricauts-servo
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Servo

[](https://github.com/ricauts/servo/actions/workflows/ci.yml)

**An open-source, self-hostable AI-powered service desk.** Email becomes tickets; AI agents triage them, draft every reply and work requests with real tools (SQL, device inventory, **real GitHub repos/branches/PRs, live Azure queries**), pause for **human approval** before anything risky, get an automated **QA review** afterwards — and everything feeds a **KPI dashboard**, including how many AI replies ship untouched.

  

Bring your own model — Anthropic, Z.AI GLM, or any OpenAI-compatible endpoint — or evaluate entirely offline with the built-in deterministic mock provider. Self-host it with real SSO (any OIDC IdP), per-requester data isolation, secrets encrypted at rest, and a first-run wizard that takes a clean install to a working desk in one screen.

> **Status: production-ready for self-hosting.** Fresh installs start clean (no demo data), sign in through your identity provider, and store secrets encrypted. Follow the [production checklist](#production-checklist) and read [SECURITY.md](SECURITY.md) before exposing an install to real users.

## Features

- **Tickets for humans and AI** — assign any ticket to a human agent or to the AI resolver; the resolver works the ticket end to end.
- **Automatic triage** — new tickets are categorized, prioritized, and routed by an AI triage agent (toggleable in Settings).
- **Tool-using resolver** — the AI resolver operates a registry of built-in tools plus any custom ones you define: read-only and mutating SQL against a sandboxed ops database, device inventory lookups, password resets, GitHub read/commit/PR/merge operations, page screenshots, Azure resource listing, and cloud deployment plan/apply.
- **Readable audit trail** — a run is one entry in the ticket story: who acted, the outcome, the tools it used and who approved what. Unfold it and every step is there verbatim — folded by default so a ticket reads like a conversation, never truncated.
- **Human-approval gates** — each tool carries a risk level (LOW/MEDIUM/HIGH) and an editable *requires approval* policy. When the agent reaches a gated tool, the run pauses, an approval lands in the Approvals inbox, and the run resumes exactly where it left off after a decision. Rejections flow back to the agent, which adapts instead of retrying.
- **Automated QA review** — after a run that executed medium/high-risk tools, a QA agent reviews the transcript and issues a PASS/FAIL verdict; failures reassign the ticket to a human with an explanatory comment.
- **KPI dashboard** — open tickets, resolution times, first-response times, AI-vs-human resolution split, approval stats, ticket volume over 30 days, and **AI reply acceptance** (sent as-is vs edited vs discarded) so you can see exactly how much typing the AI is saving.
- **BYOK with offline mock mode** — plug in an Anthropic API key via Settings or environment variable, or run fully offline with the deterministic mock provider.
- **API key pool with per-agent assignment and throughput metering** — register multiple named credentials (any provider mix), assign one per specialized agent from the Agents page, and every model call is logged (tokens in/out, latency, key, agent). A throughput panel on Agents aggregates the last 7 days per key and agent.
- **SLA targets with automatic escalation** — per-priority response and resolution targets (editable in Settings). Every ticket carries live SLA state on the queue and detail views, the dashboard tracks breaches, and a scan escalates missed targets one tier up the group hierarchy — assigning the least-loaded eligible member and logging why. Schedule `POST /api/sla/scan` to run it unattended.
- **Groups & escalation hierarchy** — assignment groups (Development, Analytics, Engineering…) own ticket categories; members carry JUNIOR → MID → SENIOR tiers per group, or STANDALONE for specialists outside the ladder. Priority sets the minimum tier, and any agent can escalate a ticket up a tier or across to another group — the least-loaded eligible member picks it up and the move is logged on the timeline.
- **Real Azure integration (read-only)** — a service principal with `Reader` is enough: `azure_list_resources` runs live Resource Manager queries (subscription-wide or scoped to a resource group), with a Test-connection button that acquires a token and lists resources. Mutating cloud actions stay simulated behind the approval gate.
- **Ships code, gated by humans** — the GitHub tools go past "open an empty branch": `github_read_file` inspects the real source, **`github_edit_file`** commits a precise find/replace on a feature branch (the approval card shows the exact before/after, and an ambiguous match is refused rather than guessed), `github_open_pr` raises the PR and **`github_merge_pr`** lands it — which triggers whatever deployment workflow the repo already has. Both write steps require human approval.
- **The desk remembers** — before it acts, the resolver searches the tickets *this* desk has already closed: `search_tickets` ranks past tickets by relevance (title, description and the resolution someone actually recorded, preferring tickets that reached an outcome), `read_ticket` opens one in full — replies sent, tools used, resolution — and `requester_history` shows what else this person has filed, so the third replacement dock in two months reads as a hardware fault instead of a new request. Precedent without the privacy leak: another requester's name and email are withheld unless the past ticket is the same person's, so nothing gets quoted back to the wrong requester. No embedding model, no vector store, no configuration — it works on a fresh install and offline.
- **Reads the web, safely** — `fetch_url` opens an http(s) page and hands the agent readable text (HTML flattened to headings, bullets and links), so a resolver can quote the vendor status page or the release notes a requester linked to instead of guessing. Because tickets arrive by email, the URL may be attacker-chosen: every outbound request — this tool, screenshots and custom HTTP integrations alike — resolves the host first and refuses loopback, private, CGNAT and link-local addresses (`169.254.169.254` included), then re-checks each redirect. An optional allowlist in Integrations narrows it to named hosts, and a literal entry there is how you deliberately reach an intranet host.
- **Screenshots for review** — `take_screenshot` renders a page in a real browser on the server and attaches the image to the ticket, so a reviewer *sees* a proposed UI change before approving it. It renders a raw branch file too, which means the "after" image exists before anything is merged. Needs a Chrome/Chromium on the host (`PUPPETEER_EXECUTABLE_PATH`); without one the tool says so instead of pretending.
- **Real GitHub integration** — add a personal access token (env `GITHUB_TOKEN` or Settings, with a Test-token button) and `github_create_repo` / `github_create_branch` / `github_open_pr` hit the real GitHub API — still behind their risk levels and approval gates. A ticket like "implement feature X" becomes a real feature branch (and PR) created by the resolver. Without a token they stay simulated so the offline demo keeps working. A base-URL override supports GitHub Enterprise.
- **Outbound webhooks** — stream `ticket.created/resolved/escalated` and `approval.pending/decided` to any endpoint as signed JSON (`x-servo-signature: sha256=HMAC(secret, body)`). Manage endpoints and subscriptions from Settings, send test pings, and watch a per-endpoint delivery log with latencies.
- **Command palette (Ctrl/⌘ K)** — search tickets by number, title, or text and jump to any page from the keyboard, anywhere in the app.
- **Email in and out** — outbound: ticket received / resolved to the requester and pending approvals to every admin, over any SMTP server (`SMTP_URL` or Settings, with a test-send button); sending is best-effort so a broken mail setup never blocks ticket flows. Inbound: point a provider webhook (SendGrid Inbound Parse, Mailgun, Postmark) at `POST /api/inbound/email` — or, for Gmail / Google Workspace, run the bundled `scripts/imap-relay.mjs` against the mailbox — and mail becomes tickets — unknown senders are created as requesters, and a subject carrying `#1029` files the message as a comment on that ticket instead. Bounces and auto-replies never become tickets: a delivery failure is posted on the ticket of the person it failed to reach ("the requester has not received the last reply"), so a dead address surfaces where it matters instead of as noise — or a mail loop.
- **AI reply drafts with human approval** — the everyday support loop: when an email opens a ticket, the AI drafts the answer (in the requester's language, using the category's specialized agent and its own API key) and a human reviews it — edit in place, approve, or discard. Approving posts it as a public comment and emails it to the requester with a subject that threads their reply back onto the same ticket; the first-response SLA clock starts on send. Pending drafts queue on **Approvals** next to tool sign-offs; a requester follow-up regenerates a stale pending draft with the new context, and closing a ticket auto-discards its draft. Toggle auto-drafting in Settings → AI (`Draft replies for inbound email`), or draft on demand from any ticket.
- **MCP server** — Servo speaks the Model Context Protocol: point any MCP client (Claude Code, Claude Desktop, other agents) at `POST /api/mcp` with a bearer token (Integrations -> MCP server) and it can file, search and read tickets (`create_ticket`, `search_tickets`, `read_ticket`, `requester_history`) and operate the whole tool registry — custom tools included, excluding ticket-bound core tools, policy-disabled tools and anything gated on human approval (no human is in the loop over MCP, so those stay behind a ticket).
- **Custom tools & integrations** — admins define new HTTP tools from Settings (method, URL, headers, body template with `{input.field}` placeholders, and a stored secret injected via `{secret}`). They join the resolver's registry like built-in tools, with the same risk levels and human-approval gates — the fastest path to integrating a webhook, an internal API, or a SaaS endpoint.
- **Specialized agents as `.md` files** — resolver personas (Analytics, Developer, Cybersecurity…) are Markdown documents with YAML frontmatter (`name`, `categories`, `tools`) and a system-prompt body. Drop files into `agents/` or create/edit them from the UI; the resolver automatically uses the enabled specialist covering the ticket's category. A **visual tool picker** per agent (checkboxes with each tool's risk and approval policy) narrows what it may call — no YAML editing — while core tools stay always-on and the .md frontmatter is rewritten to match.
- **Desk skills as `.md` files** — the procedures the desk has agreed to follow (how a lockout is handled, what to check before a database change, when to escalate instead of resolving), versioned as `skills//SKILL.md` and editable from the **Skills** page. Progressive disclosure, the way Claude Code loads skills: the resolver's prompt carries only each skill's name and description, and the body costs one `read_skill` call — so a desk can hold dozens of procedures without bloating every prompt. `categories: []` makes a skill desk-wide; a skill never overrides an approval gate; and **QA is told which skills applied and which the run actually read**, so an agreed procedure that gets ignored is caught before the ticket closes. External MCP clients can read them too.
- **Role-based permissions** — ADMIN, AGENT, and REQUESTER roles with a permission matrix; HIGH-risk approvals and group management are admin-only.
- **Offline evaluation mode** — without an OIDC tenant Servo runs a demo user switcher, so you can experience every role (and the whole agent loop, on the mock provider) with no auth provider, no API key and no network.
- **shadcn/ui frontend** — Tailwind v4 + [shadcn/ui](https://ui.shadcn.com) components and charts (Recharts), themed with Servo's green-accent OKLCH palette; light mode by default with a dark-mode toggle.
- **Docker-ready** — one `docker compose up --build` gives you a self-contained instance with persistent SQLite volumes.

## A real ticket, end to end

Someone emailed the desk to say a button on our own landing page was unreadable. Servo triaged it, the frontend specialist read the source, diagnosed the CSS, and **captured what it looked like before and after its fix — from the branch, before anything was merged** — so a human could approve on evidence rather than on a diff:

  

Both screenshots land on the ticket, next to the AI-drafted reply waiting for review. The run itself folds into one line — agent, outcome, QA verdict, the tools it used and who approved what — and unfolds to the full step-by-step trace:

  

The commit and the merge each stopped for a human. Approvals — tool sign-offs and reply drafts — share one queue:

  

## Screenshots

| Integrations — SSO, mail, GitHub, MCP connected | Specialized agents, their tools and throughput |
|---|---|
|  |  |

| Ticket queue | Settings — BYOK & tool permissions |
|---|---|
|  |  |

  
  Fully responsive — same app on mobile.

## Quickstart

Requires **Node.js 20+**.

```bash
npm install
npm run setup   # prisma generate + db push + core bootstrap (no sample data)
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) — the **first-run wizard** creates your admin account (and, optionally, connects your SSO tenant). You start with a clean desk: zero tickets, no sample users, nobody else's data. The database is SQLite — no external services needed.

Want a populated playground instead? `npm run demo` loads a fictional showcase dataset (~28 tickets, completed AI runs, pending approvals) so every screen is meaningful instantly. It **wipes the database** — demo evaluation only, never a live install.

Run the unit tests with `npm test`, the RBAC matrix with `node scripts/permissions-audit.mjs`, and the responsive check with `node scripts/responsive-audit.mjs` (both need the dev server running).

### Run with Docker

```bash
docker compose up --build
```

The container bootstraps its SQLite databases on a named volume (`/data`) on first boot, then serves on [http://localhost:3000](http://localhost:3000) — visit it to run the setup wizard. Set `SERVO_DEMO=1` for the showcase dataset instead, and `ANTHROPIC_API_KEY` (or configure a key in Settings) for real model calls; without one Servo runs in mock mode.

### Production checklist

Before exposing an install to real users, set these (see [SECURITY.md](SECURITY.md) for the full model):

- `AUTH_SECRET` — session signing (any long random string).
- `SERVO_ENCRYPTION_KEY` — encrypts stored secrets (API keys, tokens, SMTP URLs) at rest with AES-256-GCM. Existing plaintext rows migrate with `node scripts/encrypt-secrets.cjs`.
- An OIDC tenant (wizard or Integrations → SSO) plus `AUTH_ALLOWED_DOMAINS` — real sign-in, scoped to your org.
- HTTPS in front (reverse proxy) and `APP_URL` set to your public URL.
- Scoped credentials for integrations: a fine-grained GitHub PAT, a Reader-role Azure service principal, an app password for the mailbox.

## Authentication (SSO / OIDC)

Servo ships with real sign-in for self-hosted deployments: connect any OIDC identity provider (Entra ID, Google, Okta, Keycloak, Auth0...) via env vars or from **Integrations -> Single sign-on**. Users are provisioned on first sign-in (REQUESTER by default; `AUTH_ADMIN_EMAILS` keeps admins), roles are managed from **Settings -> Team**, and a **first-run setup wizard** (`/setup`) bootstraps fresh installs: the first admin, the system AI agents, default policies, and (optionally) your SSO tenant. Without OIDC config Servo stays in the offline demo mode with the user switcher. For local development, `node scripts/mock-idp.mjs you@x.com` starts a

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ricauts](https://github.com/ricauts)
- **Source:** [ricauts/Servo](https://github.com/ricauts/Servo)
- **License:** MIT
- **Homepage:** https://servoai.org

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-ricauts-servo
- Seller: https://agentstack.voostack.com/s/ricauts
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
