Work · MCP · TypeScript

Halo ITSM MCP — conversation tools for a live service desk

A Model Context Protocol server for one live Halo ITSM instance. Tools match what a technician says — prove identity, find a ticket, load it, leave a note, reply to the store user — instead of wrapping the REST catalog. Writes preview and refuse unless confirm is true. Resolve-ticket is the next step that closes the loop.

My role Builder: server, Halo HTTP client, tool schemas, and the confirm gate on every mutation.

Stack TypeScript · Bun · @modelcontextprotocol/sdk · Zod · OAuth2 client-credentials · stdio MCP · Biome

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

Halo MCP

Writes need confirmation

confirm: true

One live desk

Halo instance

What it does

Proves identity on the live desk

Loads a ticket with the fields a tech needs

Notes or replies only after confirm

Conversation-shaped tools over one Halo instance — writes stop unless confirm is true.

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

← All work Next: Agent harness →