- Context
- Service-desk work already lived in Halo. An agent in the editor could only help if it had a small, safe tool surface: enough ticket context to talk, and no way for a hallucinated call to email a store or mutate a ticket. Constraint: one process, one instance, credentials only from the environment.
- Intervention
-
Built halo-itsm-mcp as a conversation-driven server, sibling of the Wrike MCP pattern: one tool per file, typed config, lazy client so a missing secret does not block the MCP handshake.
Phase 1 — Identity and read path
HaloClient does OAuth2 client-credentials, caches the token, and refreshes it with a 60s skew. A 401 drops the cache. Tools: halo_whoami, halo_search_tickets, halo_get_ticket. Status codes map to unauthorized, forbidden, not found, rate limit, or external API.
Phase 2 — Writes that preview first
add_ticket_note and reply_to_store_user (Halo outcome PL - Reply to Store User - IT) return a preview and do not write unless confirm is true. Reply checks workflow step so a ticket still on Triage cannot send. Zod schemas are strict. Tests cover the write tools.
Phase 3 — Toward the agentic loop
The tool set is the hand the loop will use. Next planned tool is resolve-ticket. The planner, traces, and evals live in the TypeScript agent harness and the AI_devs work, not inside this server.
- Risks & how they were handled
-
- A model call that emails a store → confirm: true, plus a Triage guard on the reply outcome.
- Token or secret leakage → secrets stay in env; logs record the auth host, not the bearer token.
- REST-shaped tool sprawl → new endpoints stay out until a ticket needs them.
- What I'd do differently
-
- Add the resolve tool before widening into assets, appointments, or billing.
- Record a small fixture suite of search/load/preview responses for the agent harness evals.
- Outcome
- An agent can prove identity, search, load a ticket, and either preview or — only with confirm — add a note or reply to the store user on a live Halo instance. The server is the tool layer for a full ticket loop; closing the ticket is the next tool, not a claim that the loop already runs unattended.
System map
Agent in the editor
stdio MCP
Search and load tickets
whoami · search · get
Cached OAuth tokens
client-credentials
Writes need confirmation
confirm: true
One live desk
Halo instance
Proves identity on the live desk
Loads a ticket with the fields a tech needs
Notes or replies only after confirm
What shipped
- stdio MCP server Conversation tools to prove identity, search, load a ticket, add a note, or reply — not a REST mirror.
- OAuth client with token cache Client-credentials flow with in-memory cache and refresh skew.
- Confirm gate on mutations Mutating tools preview and refuse unless confirm is true; tests cover the write path.
Stack TypeScript Bun MCP SDK Zod OAuth2 stdio Biome