Halo ITSM MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Halo ITSM MCP Servershow me open high priority tickets"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Halo ITSM MCP Server
A Model Context Protocol (MCP) server that exposes the full Halo ITSM REST API to AI assistants. Provides 172 tools across 43 resource domains, enabling AI-driven IT service management through any MCP-compatible client.
Built by the City of San Marcos, CA - IT Division.
Features
172 tools across 43 Halo ITSM resource domains (tickets, agents, assets, categories, SLAs, priorities, workflows, services, and more)
6 read-only MCP resources for configuration context (statuses, teams, agents, ticket types, rate limit status)
Three transport modes: stdio, HTTP (Streamable HTTP + SSE), HTTP with OAuth proxy
Two authentication modes: environment-based Client Credentials, or OAuth proxy (credentials passed at connection time)
PKCE auto-injection for OAuth clients that don't natively support it
Rate limiting: 700 requests per 5-minute sliding window with 80% usage warnings
Circuit breaker: pauses requests after 5 consecutive failures (30-second cooldown)
Retry logic: exponential backoff with jitter for 429 and 5xx responses
Docker-ready: multi-stage build, non-root user, health check endpoint, read-only filesystem
Reverse proxy compatible: runs plain HTTP internally, TLS handled externally
Security hardened: security headers on all responses, input size validation, fetch timeouts, error sanitization, secret redaction in logs
Related MCP server: NowAIKit
Supported Resource Domains
Domain | Tools | API Path |
Tickets | list, get, upsert, delete | /Tickets |
Actions | list, get, upsert, delete | /Actions |
Agents | list, get, upsert, delete, me | /Agent |
Assets | list, get, upsert, delete | /Asset |
Asset Groups | list, get, upsert, delete | /AssetGroup |
Asset Types | list, get, upsert, delete | /AssetType |
Attachments | list, get, upsert, delete | /Attachment |
Appointments | list, get, upsert, delete | /Appointment |
Audit | list, get, upsert, delete | /Audit |
Canned Text | list, get, upsert, delete | /CannedText |
Categories | list, get, upsert, delete | /Category |
Clients | list, get, upsert, delete | /Client |
Contracts | list, get, upsert, delete | /ClientContract |
Downtime | list, get, upsert, delete | /Downtime |
Feedback | list, get, upsert, delete | /Feedback |
Fields | list, get, upsert, delete | /Field |
Holidays | list, get, upsert, delete | /Holiday |
Invoices | list, get, upsert, delete | /Invoice |
Items | list, get, upsert, delete | /Item |
Knowledge Base | list, get, upsert, delete | /KBArticle |
Opportunities | list, get, upsert, delete | /Opportunities |
Organisations | list, get, upsert, delete | /Organisation |
Priorities | list, get, upsert, delete | /Priority |
Projects | list, get, upsert, delete | /Projects |
Purchase Orders | list, get, upsert, delete | /PurchaseOrder |
Quotes | list, get, upsert, delete | /Quotation |
Reports | list, get, upsert, delete | /Report |
Services | list, get, upsert, delete | /Service |
Service Status | list, get, upsert, delete | /ServiceStatus |
Sites | list, get, upsert, delete | /Site |
SLAs | list, get, upsert, delete | /SLA |
Software Licences | list, get, upsert, delete | /SoftwareLicence |
Status | list, get, upsert, delete | /Status |
Suppliers | list, get, upsert, delete | /Supplier |
Tags | list, get, upsert, delete | /Tags |
Teams | list, get, upsert, delete | /Team |
Ticket Rules | list, get, upsert, delete | /TicketRules |
Ticket Types | list, get, upsert, delete | /TicketType |
Timesheets | list, get, upsert | /Timesheet |
Top Level | list, get, upsert, delete | /TopLevel |
Users | list, get, upsert, delete | /Users |
Workdays | list, get, upsert, delete | /Workday |
Workflows | list, get, upsert, delete | /Workflow |
Prerequisites
Node.js 20 or later
A Halo ITSM instance with API access enabled
A Halo API application (Configuration > Integrations > HaloPSA API) with Client Credentials grant type
Quick Start
1. Clone and install
git clone https://github.com/omichelbraga/halo-itsm-mcp.git
cd halo-itsm-mcp
npm install
npm run build2. Configure
Copy the example environment file:
cp .env.example .envMinimum configuration for OAuth proxy mode:
HALO_BASE_URL=https://yourinstance.haloitsm.com
MCP_TRANSPORT=http
MCP_AUTH=oauth
MCP_PUBLIC_URL=https://your-public-url.com3. Run
node --env-file=.env dist/index.jsThe server starts on port 3000 and logs connection details to the console.
Authentication Modes
OAuth Proxy Mode (Recommended)
Credentials are passed by the MCP client at connection time. No secrets stored on the server.
HALO_BASE_URL=https://yourinstance.haloitsm.com
MCP_TRANSPORT=http
MCP_AUTH=oauth
MCP_PUBLIC_URL=https://your-public-url.comThe server acts as an OAuth Authorization Server to the MCP client, then internally exchanges the credentials with Halo using Client Credentials grant. The Halo API application only needs Client Credentials enabled (not Authorization Code).
How the OAuth bridge works:
MCP client sends credentials via the standard OAuth Authorization Code + PKCE flow
Server auto-approves the authorization request (no user interaction)
Client exchanges the authorization code for a token
Server takes the client_id + client_secret and calls Halo's token endpoint with
grant_type=client_credentialsThe Halo access token is returned to the MCP client
All subsequent MCP API calls use the Halo token directly
Environment Auth Mode
Credentials stored in environment variables. Useful for trusted environments or headless deployments.
HALO_BASE_URL=https://yourinstance.haloitsm.com
HALO_CLIENT_ID=your_client_id
HALO_CLIENT_SECRET=your_client_secret
MCP_TRANSPORT=http
MCP_AUTH=envStdio Mode
For local use with Claude Desktop or Claude Code. No HTTP server, communicates over stdin/stdout.
HALO_BASE_URL=https://yourinstance.haloitsm.com
HALO_CLIENT_ID=your_client_id
HALO_CLIENT_SECRET=your_client_secretRun without setting MCP_TRANSPORT (defaults to stdio).
Connecting MCP Clients
Claude.ai Connector
Run the server with
MCP_AUTH=oauthbehind HTTPS (reverse proxy or ngrok)In Claude, go to Settings and add a new MCP integration:
URL:
https://your-public-url.comOAuth Client ID: your Halo API Client ID
OAuth Client Secret: your Halo API Client Secret
Claude will complete the OAuth handshake automatically
n8n
Add an MCP Client node to your workflow
Configure the node:
Server Transport: HTTP Streamable
MCP Endpoint URL:
https://your-public-url.com/mcpAuthentication: MCP OAuth2
Create a new MCP OAuth2 credential:
Grant Type: PKCE
Authorization URL:
https://your-public-url.com/authorizeAccess Token URL:
https://your-public-url.com/tokenClient ID: your Halo API Client ID
Client Secret: your Halo API Client Secret
Scope:
allAuthentication: Body
Click Connect to MCP, then select your tool from the dropdown
Note: Use the PKCE grant type in n8n, not plain Authorization Code. The MCP protocol requires PKCE (OAuth 2.1).
Claude Desktop (stdio)
Add to your claude_desktop_config.json:
{
"mcpServers": {
"halo-itsm": {
"command": "node",
"args": ["/path/to/halo-itsm-mcp/dist/index.js"],
"env": {
"HALO_BASE_URL": "https://yourinstance.haloitsm.com",
"HALO_CLIENT_ID": "your_client_id",
"HALO_CLIENT_SECRET": "your_client_secret"
}
}
}
}Claude Code (stdio)
Add to your Claude Code MCP configuration:
{
"mcpServers": {
"halo-itsm": {
"command": "node",
"args": ["--env-file=/path/to/.env", "/path/to/halo-itsm-mcp/dist/index.js"]
}
}
}Docker Deployment
Build and run
docker build -t halo-itsm-mcp .
docker run -d \
--name halo-itsm-mcp \
-p 3000:3000 \
-e HALO_BASE_URL=https://yourinstance.haloitsm.com \
-e MCP_PUBLIC_URL=https://your-public-url.com \
halo-itsm-mcpDocker Compose
# Configure .env with HALO_BASE_URL and MCP_PUBLIC_URL, then:
docker compose up -dThe container runs as a non-root user, exposes port 3000, and includes a health check at /health. Docker Compose also enforces a read-only filesystem and no-new-privileges security option.
Behind a Reverse Proxy
The server runs plain HTTP on port 3000. TLS termination is handled by your reverse proxy (nginx, Caddy, Traefik, etc.).
Set MCP_PUBLIC_URL to the external HTTPS URL so OAuth metadata and redirect URIs resolve correctly:
MCP_PUBLIC_URL=https://halo-mcp.yourdomain.comExample nginx configuration:
server {
listen 443 ssl;
server_name halo-mcp.yourdomain.com;
ssl_certificate /etc/ssl/certs/your-cert.pem;
ssl_certificate_key /etc/ssl/private/your-key.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE support
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
}
}Environment Variables
Variable | Required | Default | Description |
| Yes | - | Halo instance URL (no trailing slash) |
| No |
| Transport mode: |
| No |
| Auth mode: |
| No |
| HTTP server port |
| No |
| External HTTPS URL (for OAuth metadata) |
| env mode | - | Halo API Client ID |
| env mode | - | Halo API Client Secret |
| No |
| API permission scope |
| No | - | Tenant name (multi-tenant deployments) |
| No |
| Log level: |
API Endpoints
When running in HTTP mode, the server exposes:
Endpoint | Method | Auth | Description |
| POST | Bearer | MCP message endpoint (initialize, tool calls) |
| GET | Bearer | SSE stream for server-sent events |
| DELETE | Bearer | Close an MCP session |
| GET | None | Health check (returns |
| GET | None | OAuth server metadata (RFC 8414) |
| GET | None | Protected resource metadata (RFC 9728) |
| GET | None | OAuth authorization endpoint |
| POST | None | OAuth token endpoint |
| POST | None | Dynamic client registration (RFC 7591) |
Tool Reference
All tools follow the naming pattern halo_{resource}_{operation}:
Operation | Description | Example |
| Search/list with filters and pagination |
|
| Get a single record by ID |
|
| Create (no id) or update (with id) |
|
| Delete by ID |
|
Special tools:
Tool | Description |
| Get the currently authenticated agent's info |
Common list parameters supported across all domains:
Parameter | Type | Description |
| number | Results per page (default varies by endpoint) |
| number | Page number (1-based) |
| string | Field to sort ascending |
| string | Field to sort descending |
| string | Full-text search query |
The halo_tickets_list tool supports 50+ additional filter parameters including open_only, agent_id, status_id, priority_id, client_id, site_id, category_1, advanced_search, and more. The halo_categories_list tool supports type_id (1-4) to filter by category level.
Rate Limiting and Resilience
The server implements multiple layers of protection against API overuse and transient failures:
Sliding window rate limiter: 700 requests per 5-minute window, with warnings at 80% capacity and automatic request delays when the limit is reached
Circuit breaker: opens after 5 consecutive API failures, pauses all requests for 30 seconds, then auto-resets
Retry with exponential backoff: 429 (rate limited) responses retry up to 3 times; 500/502/503 responses retry once. Backoff includes random jitter to prevent thundering herd
Token caching: access tokens are cached in memory and auto-refreshed 60 seconds before expiry
Request deduplication: concurrent token refresh requests are deduplicated to a single HTTP call
Automatic 401 recovery: if a Halo API call returns 401, the token is force-refreshed and the request is retried once
Health Check
The /health endpoint returns a minimal status response without requiring authentication. No server details are exposed to prevent information disclosure.
curl http://localhost:3000/health{
"status": "ok"
}Project Structure
halo-itsm-mcp/
├── src/
│ ├── index.ts # Entry point, transport setup, route config
│ ├── auth/
│ │ ├── oauth2.ts # Client Credentials token management + caching
│ │ └── proxy-provider.ts # Custom OAuth server (AuthCode+PKCE -> ClientCreds bridge)
│ ├── client/
│ │ ├── halo-client.ts # HTTP client with retry, circuit breaker, token injection
│ │ └── rate-limiter.ts # Sliding window rate limiter (700 req / 5 min)
│ ├── resources/
│ │ └── context.ts # MCP read-only resources (config, schemas)
│ ├── tools/
│ │ ├── registry.ts # Tool registry (builds and registers all 172 tools)
│ │ ├── resource-factory.ts # Generic CRUD tool factory
│ │ └── resources/ # 43 resource domain definitions
│ │ ├── tickets.ts # Ticket management (50+ filter params)
│ │ ├── actions.ts # Ticket actions/notes
│ │ ├── agents.ts # Agent management + /Agent/me
│ │ ├── assets.ts # IT asset tracking
│ │ ├── asset-groups.ts # Asset grouping
│ │ ├── asset-types.ts # CMDB asset type definitions
│ │ ├── attachments.ts # File attachments
│ │ ├── appointments.ts # Scheduling
│ │ ├── audit.ts # Audit trail
│ │ ├── canned-text.ts # Response templates
│ │ ├── categories.ts # Ticket categorization (4 levels)
│ │ ├── clients.ts # Client/customer records
│ │ ├── contracts.ts # Service contracts
│ │ ├── downtime.ts # Maintenance windows
│ │ ├── feedback.ts # Customer satisfaction
│ │ ├── fields.ts # Custom field definitions
│ │ ├── holidays.ts # Holiday schedules
│ │ ├── invoices.ts # Billing
│ │ ├── items.ts # Catalog items
│ │ ├── knowledge-base.ts # KB articles
│ │ ├── opportunities.ts # Sales opportunities
│ │ ├── organisations.ts # Organizational structure
│ │ ├── priorities.ts # Priority levels
│ │ ├── projects.ts # Project management
│ │ ├── purchase-orders.ts # IT procurement
│ │ ├── quotes.ts # Quotes
│ │ ├── reports.ts # Reports
│ │ ├── services.ts # Service catalog
│ │ ├── service-status.ts # Service status page
│ │ ├── sites.ts # Locations
│ │ ├── sla.ts # Service Level Agreements
│ │ ├── software-licences.ts # Software license tracking
│ │ ├── status.ts # Status configuration
│ │ ├── suppliers.ts # Supplier records
│ │ ├── tags.ts # Ticket tagging
│ │ ├── teams.ts # Team configuration
│ │ ├── ticket-rules.ts # Auto-assignment rules
│ │ ├── ticket-types.ts # Ticket type configuration
│ │ ├── timesheets.ts # Time tracking
│ │ ├── top-level.ts # Customer hierarchy
│ │ ├── users.ts # End-user management
│ │ ├── workdays.ts # Working day definitions
│ │ └── workflows.ts # Workflow definitions
│ ├── types/
│ │ ├── config.ts # Configuration interfaces, env loader
│ │ └── halo.ts # Halo API TypeScript type definitions
│ └── utils/
│ └── logger.ts # Structured stderr logger with secret redaction
├── Dockerfile # Multi-stage Docker build (node:22-alpine)
├── docker-compose.yml # Docker Compose with health check
├── .env.example # Environment variable template
├── package.json
├── tsconfig.json
└── README.mdArchitecture
┌─────────────┐ ┌──────────────┐ ┌─────────────────────────┐ ┌──────────────┐
│ MCP Client │────>│ Reverse Proxy│────>│ MCP Server │────>│ Halo ITSM │
│ (Claude, │ TLS │ (nginx/ngrok)│ HTTP│ (Express) │ API │ REST API │
│ n8n, etc) │<────│ │<────│ │<────│ │
└─────────────┘ └──────────────┘ │ ┌───────────────────┐ │ └──────────────┘
│ │ OAuth Server │ │
│ │ (AuthCode -> CC) │ │
│ ├───────────────────┤ │
│ │ Rate Limiter │ │
│ │ Circuit Breaker │ │
│ │ Token Auto-Refresh │ │
│ │ Auto-PKCE │ │
│ └───────────────────┘ │
└─────────────────────────┘Halo API Application Setup
In Halo ITSM, go to Configuration > Integrations > HaloPSA API
Create a new API application
Set Auth Method to Client ID and Secret (Services)
Under Permissions, grant access to the resource domains you need (or select all)
Copy the Client ID and Client Secret
The server uses Client Credentials grant regardless of the authentication mode selected. The OAuth proxy translates the MCP protocol's Authorization Code + PKCE flow into Client Credentials transparently.
Development
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run with environment file
node --env-file=.env dist/index.js
# Clean build artifacts
npm run cleanTroubleshooting
"Configuration error: HALO_BASE_URL required"
Node.js doesn't auto-load .env files. Use node --env-file=.env dist/index.js instead of node dist/index.js.
"Invalid Host" errors behind a reverse proxy
The server binds to 0.0.0.0 with DNS rebinding protection disabled. Verify your MCP_PUBLIC_URL matches the external URL.
"X-Forwarded-For" validation errors
The server sets trust proxy = 1 for single-proxy setups. If you have multiple proxy layers, adjust this value in src/index.ts.
OAuth "unauthorized_client" from Halo Halo only supports Client Credentials grant. The MCP server handles the protocol translation automatically. Verify your Halo API application is configured for "Client ID and Secret (Services)", not Authorization Code.
n8n "Could not load list"
Verify the MCP Endpoint URL includes /mcp at the end (e.g., https://your-url.com/mcp). Use the PKCE grant type in n8n's credential, not plain Authorization Code.
Token expires during long sessions The server caches tokens and auto-refreshes them 60 seconds before expiry. In OAuth proxy mode, the server retains client credentials for the session lifetime and re-authenticates via Client Credentials grant automatically. Sessions survive indefinitely without manual re-authentication.
Security
Transport
The server runs plain HTTP internally. TLS must be terminated by a reverse proxy (nginx, Caddy, Traefik, etc.) in production. Set MCP_PUBLIC_URL to your external https:// URL so OAuth metadata and redirects resolve correctly.
Security Headers
All HTTP responses include the following headers:
Header | Value |
|
|
|
|
|
|
|
|
|
|
|
|
Input Validation
All tool inputs are validated with Zod schemas before reaching the Halo API
String parameters enforce maximum length limits (50-2000 characters depending on field)
Array filter parameters are capped at 100 items
Upsert data objects are limited to 512KB when serialized
Request bodies are limited to 1MB via Express middleware
Timeouts and Resilience
All outbound fetch calls to Halo enforce a 10-15 second timeout via
AbortControllerRate limiter, circuit breaker, and retry logic protect against API overuse (see Rate Limiting and Resilience)
Secret Handling
Client secrets captured during OAuth flows are retained in memory for the duration of the session to support automatic token refresh, and expire after a 24-hour TTL
The logger redacts 15+ sensitive field patterns (tokens, secrets, passwords, API keys) in both snake_case and camelCase
Error messages returned to clients are sanitized; detailed error information is only written to server-side logs
The
.envfile is excluded from both git and Docker builds
Docker Hardening
The Docker Compose configuration enforces:
Non-root user (
mcp:mcp)Read-only filesystem with tmpfs for
/tmpno-new-privilegessecurity optionHealth check at
/health
URL Validation
HALO_BASE_URL is validated at startup to ensure it is a well-formed http:// or https:// URL. Invalid URLs cause an immediate exit with a descriptive error.
Reporting Vulnerabilities
If you discover a security vulnerability, please open an issue at github.com/omichelbraga/halo-itsm-mcp/issues.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/omichelbraga/halo-itsm-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server