MCPInstall
ITSM Integration logo

ITSM Integration

by madosh 2 stars madosh/mcp-itsm

Integrates with multiple IT Service Management systems (ServiceNow, Jira, Zendesk, Ivanti Neurons, and Cherwell) to enable ticket creation, retrieval, updates, assignment, and knowledge base searches through a unified interface.

READMEmain

MCP ITSM

Unified IT Service Management over the Model Context Protocol — create, track, and resolve tickets across ServiceNow, Jira, Zendesk, Ivanti Neurons, and Cherwell from any MCP-compatible LLM client.

MCP Spec SDK Node License Smithery Glama


Table of Contents


Overview

MCP ITSM exposes a standardised set of MCP tools, resources, and prompts so that any LLM client (Claude, Cursor, custom agents) can manage IT tickets without knowing the underlying ITSM system's API.

What it is:

LayerWhat's here
index.jsMCP server — 7 tools, 4 resources, 3 prompts over stdio
backend/Express REST API that bridges HTTP clients to the MCP server via the official SDK Client
frontend/React 18 web app — Ticket Manager UI + live Monitoring Dashboard

Why it matters:
Instead of writing separate integrations for ServiceNow, Jira, Zendesk, Ivanti, and Cherwell, an LLM calls create_ticket once and the correct system receives it. Every tool carries safety annotations (readOnlyHint, destructiveHint) so the model knows what it can call without risk.


Architecture

graph TB
    subgraph "MCP Clients"
        Claude["Claude / Cursor / Agent"]
        Inspector["MCP Inspector"]
        UI["React Frontend :3000"]
    end

    subgraph "Transport"
        Stdio["StdioServerTransport\n(Smithery / Inspector)"]
        Bridge["Express API :5000\nSDK Client + StdioClientTransport"]
    end

    subgraph "MCP Server  —  index.js  v3.0.0"
        McpSrv["McpServer\nspec 2025-11-25"]
        Tools["7 Tools\nZod · annotated"]
        Resources["4 Resources\nKB articles · Tickets"]
        Prompts["3 Prompts\nIncident · Status · KB-assist"]
        Store["In-Memory Store\nTickets & KB articles"]
    end

    subgraph "Backend Services"
        Auth["JWT Auth"]
        Metrics["Metrics Store"]
        Mongo[("MongoDB")]
    end

    Claude -->|stdio| Stdio
    Inspector -->|stdio| Stdio
    UI -->|HTTP + JWT| Bridge

    Stdio --> McpSrv
    Bridge -->|"MCP SDK Client\n(proper handshake)"| McpSrv

    McpSrv --> Tools
    McpSrv --> Resources
    McpSrv --> Prompts
    Tools <--> Store
    Resources --> Store

    Bridge --> Auth
    Bridge --> Metrics
    Auth --> Mongo

Request flow — browser tool call:

sequenceDiagram
    participant U as User
    participant UI as React UI :3000
    participant API as Express API :5000
    participant C as MCP SDK Client
    participant MCP as McpServer index.js

    U->>UI: Submit form
    UI->>API: POST /api/mcp/tools/call (JWT)
    API->>C: client.callTool(name, args)
    Note over C: StdioClientTransport
    C->>MCP: tools/call (MCP protocol)
    Note over MCP: Zod validates input
    MCP->>MCP: tool handler + in-memory store
    MCP-->>C: CallToolResult
    C-->>API: result
    API->>API: recordCall() → metrics
    API-->>UI: { success, data, _meta }
    UI->>U: Show result

Quick Start

Prerequisites

  • Node.js ≥ 18
  • MongoDB (local or Atlas — required by the backend)
  • Optional: Smithery CLI for cloud deployment

1 — Install dependencies

# Root (MCP server)
npm install

# Backend API
cd backend && npm install && cd ..

# Frontend
cd frontend && npm install && cd ..

2 — Configure environment

cp .env.example .env          # root — API key for Smithery / MCP auth
cp backend/.env.example backend/.env   # backend — Mongo URI, JWT secret, ITSM creds

Minimum required for local dev (edit backend/.env):

MONGODB_URI=mongodb://localhost:27017/mcp-itsm
JWT_SECRET=change-me-in-production

3 — Start all services

Open three terminals:

# Terminal 1 — MCP server (stdio)
npm start

# Terminal 2 — Backend API
cd backend && npm start        # http://localhost:5000

# Terminal 3 — Frontend
cd frontend && npm start       # http://localhost:3000
flowchart LR
    T1["Terminal 1\nnpm start\nMCP server on stdio"]
    T2["Terminal 2\ncd backend\nnpm start  :5000"]
    T3["Terminal 3\ncd frontend\nnpm start  :3000"]
    UI["localhost:3000\nTicket Manager\nMonitor Dashboard"]

    T1 -->|"SDK Client\nStdioClientTransport"| T2
    T2 -->|"HTTP + JWT"| T3
    T3 --> UI

Access points

URLWhat
http://localhost:3000React web app
http://localhost:3000/mcp-ticketsMCP Ticket Manager
http://localhost:3000/mcp-monitorLive Monitoring Dashboard
http://localhost:5000/healthBackend health check
http://localhost:5000/api/mcp/healthMCP server connectivity
http://localhost:5000/api/mcp/metricsTool-call metrics (auth required)

Project Structure

mcp-itsm/
├── index.js                  # MCP server (McpServer, Zod, stdio)
├── tools.json                # Static tool catalogue for Smithery browser
├── smithery.yaml             # Smithery deployment config
├── package.json              # Root deps: @modelcontextprotocol/sdk, zod
├── .env.example              # Root env template
│
├── backend/
│   ├── package.json          # Express, Mongoose, JWT, MCP SDK Client
│   ├── .env.example          # Backend env template
│   └── src/
│       ├── index.js          # Express app bootstrap
│       ├── config/config.js  # Env-driven configuration
│       ├── routes/
│       │   ├── mcp.routes.js      # MCP bridge + metrics endpoints
│       │   ├── auth.routes.js
│       │   ├── context.routes.js
│       │   ├── integration.routes.js
│       │   └── user.routes.js
│       ├── middleware/
│       │   ├── auth.middleware.js
│       │   └── validation.middleware.js
│       ├── models/
│       ├── validators/
│       └── utils/logger.js
│
├── frontend/
│   ├── package.json          # React 18, Bootstrap, react-router-dom
│   └── src/
│       ├── App.js
│       ├── pages/
│       │   ├── MCPTicketManager.js     # Ticket CRUD UI
│       │   ├── MCPMonitorDashboard.js  # Live monitoring (polls every 10s)
│       │   ├── Dashboard.js
│       │   ├── LLMChatClient.js
│       │   └── ...
│       ├── services/
│       │   ├── mcpService.js   # HTTP client for MCP tool calls
│       │   └── api.js          # Axios instance with JWT interceptor
│       └── components/
│           ├── Header.js
│           └── ...
│
└── docs/
    ├── api-documentation.md
    ├── mcp_relationship.md
    └── llm_enabled_tickets.md

MCP Tools

All 7 tools are registered via McpServer.tool() with Zod input schemas and safety annotations. LLM clients use the annotations to decide whether to call a tool without user confirmation.

ToolTitleRead-onlyIdempotentRequired params
create_ticketCreate Ticket——title, description
get_ticketGet Ticket✓✓ticket_id
update_ticketUpdate Ticket——ticket_id
list_ticketsList Tickets✓✓—
assign_ticketAssign Ticket—✓ticket_id, user_id
add_commentAdd Comment——ticket_id, comment
search_knowledge_baseSearch KB✓✓query

Supported systems (via the optional system parameter): servicenow · jira · zendesk · ivanti_neurons · cherwell (default: jira)

Every system runs against the in-memory mock store by default. system=servicenow is the one exception: when SERVICENOW_BASE_URL, SERVICENOW_USERNAME, and SERVICENOW_PASSWORD are all set (see Configuration), create_ticket, get_ticket, update_ticket, list_tickets, assign_ticket, and add_comment are routed to a real ServiceNow instance instead. search_knowledge_base is not covered — it's mock-only for every system, including ServiceNow, for now.

ServiceNow Live Adapter

adapters/servicenow.js talks to a real instance's Table API over Basic Auth, against the incident table by default (override with SERVICENOW_TABLE).

Our fieldServiceNow fieldNotes
titleshort_description
descriptiondescription
priority (low/medium/high/critical)impact + urgencyWe set both to the same mapped value (1–4); most instances derive priority from this pair rather than accepting direct writes.
status (open/in_progress/resolved/closed)stateMapped to the out-of-box incident state values (1/2/6/7). Many instances customise these — if your state field uses different numbers, adjust STATUS_TO_SN_STATE / SN_STATE_TO_STATUS in adapters/servicenow.js.
commentcomments (public) or work_notes (internal, via internal: true)Both are journal fields — writes append a new entry, they never overwrite history.
ticket idnumber (e.g. INC0010023)The real ServiceNow ticket number is returned as id, replacing the synthetic SN-1000-style ID used by the mock store.

assign_ticket passes user_id as a display value (sysparm_input_display_value=true), so a ServiceNow username works directly without first resolving it to a sys_id.

Any ServiceNow API error (auth failure, invalid field, ticket not found, network timeout) is caught and returned as { success: false, error: "ServiceNow: ..." } rather than throwing — callers get the same response shape as the mock store.

Out of scope for now: CMDB/asset/CI records (ticket + KB only), knowledge base search against ServiceNow's KM API, and live adapters for Jira/Zendesk/Ivanti Neurons/Cherwell — see Roadmap.

graph LR
    subgraph RO["Read-only  —  safe to call freely"]
        GT["get_ticket\nreadOnly · idempotent"]
        LT["list_tickets\nreadOnly · idempotent"]
        SK["search_knowledge_base\nreadOnly · idempotent"]
    end
    subgraph WR["Write  —  require user intent"]
        CT["create_ticket\nwrite"]
        UT["update_ticket\nwrite"]
        AT["assign_ticket\nwrite · idempotent"]
        AC["add_comment\nwrite"]
    end
    style RO fill:#f0fdf4,stroke:#86efac
    style WR fill:#fff1f2,stroke:#fecdd3

Example — create a ticket

// Tool call
{
  "name": "create_ticket",
  "arguments": {
    "title": "VPN not connecting after Windows update",
    "description": "Since the KB5034441 update, VPN client fails to authenticate on first attempt.",
    "priority": "high",
    "system": "jira"
  }
}

// Response
{
  "success": true,
  "ticket": {
    "id": "JIRA-1000",
    "title": "VPN not connecting after Windows update",
    "system": "jira",
    "status": "open",
    "priority": "high",
    "url": "https://example.com/jira/tickets/JIRA-1000"
  }
}

MCP Resources

Resources expose live data that LLM clients can read without a tool call.

URINameDescription
kb://articleskb-articlesAll knowledge base articles (JSON)
kb://articles/{id}kb-articleSingle KB article by ID (e.g. KB-001)
itsm://tickets/openopen-ticketsAll currently open tickets (live)
itsm://tickets/{ticketId}ticketSingle ticket by ID (e.g. JIRA-1000)
graph LR
    McpSrv["McpServer"]
    McpSrv -->|"static\nkb://articles"| KBAll["kb-articles\nAll KB articles as JSON"]
    McpSrv -->|"template\nkb://articles/{id}"| KBOne["kb-article\nSingle article by ID"]
    McpSrv -->|"static\nitsm://tickets/open"| TOpen["open-tickets\nLive filtered view"]
    McpSrv -->|"template\nitsm://tickets/{id}"| TOne["ticket\nSingle ticket by ID"]
    style McpSrv fill:#f0fdf4,stroke:#86efac

MCP Prompts

Prompts are guided message templates that clients present to users before a tool call sequence.

NameDescriptionArguments
create-incident-ticketP1/P2 incident ticket templatetitle (req), system, affected_service
ticket-status-reportStructured queue summaryfilter_status
kb-search-assistSearch KB before creating a ticketissue_description (req)
sequenceDiagram
    participant U as User
    participant C as MCP Client
    participant MCP as McpServer

    U->>C: "My printer won't install"
    C->>MCP: prompts/get  kb-search-assist
    MCP-->>C: message template
    C->>MCP: tools/call  search_knowledge_base
    MCP-->>C: KB-005 Printer setup guide
    C->>U: Show article — no ticket needed
    Note over C,U: Only escalates to create_ticket if no article resolves it

Configuration

Root .env (MCP server + Smithery)

The MCP server process (index.js) is what actually calls out to ITSM systems, so live-adapter credentials belong here, not in backend/.env.

# API key used when running via Smithery (injected as API_KEY env var)
API_KEY=your-smithery-api-key

# ServiceNow live adapter (optional — leave unset to keep system=servicenow
# calls on the in-memory mock). See "ServiceNow Live Adapter" below.
SERVICENOW_BASE_URL=https://your-instance.service-now.com
SERVICENOW_USERNAME=
SERVICENOW_PASSWORD=

Backend backend/.env

# Server
PORT=5000
NODE_ENV=development

# Database
MONGODB_URI=mongodb://localhost:27017/mcp-itsm

# Auth
JWT_SECRET=change-me-to-a-long-random-string
JWT_EXPIRES_IN=1d

# ITSM integrations reserved for future backend-level adapters. Only the
# ServiceNow adapter is implemented so far, and it reads its credentials
# from root `.env` (above), not from here — these are unused placeholders
# until adapters move into the backend or the other systems get one too.
SERVICENOW_BASE_URL=https://your-instance.service-now.com
SERVICENOW_USERNAME=admin
SERVICENOW_PASSWORD=

JIRA_BASE_URL=https://your-org.atlassian.net
JIRA_EMAIL=you@example.com
JIRA_API_TOKEN=

ZENDESK_BASE_URL=https://your-org.zendesk.com
ZENDESK_USERNAME=you@example.com
ZENDESK_TOKEN=

IVANTI_BASE_URL=https://your-instance.ivanti.com
IVANTI_CLIENT_ID=
IVANTI_CLIENT_SECRET=

CHERWELL_BASE_URL=https://your-instance.cherwell.com
CHERWELL_CLIENT_ID=
CHERWELL_USERNAME=
CHERWELL_PASSWORD=

# Logging
LOG_LEVEL=info

Monitoring Dashboard

The frontend includes a live monitoring dashboard at /mcp-monitor that polls the backend every 10 seconds.

What it shows:

  • Server connected / disconnected status with uptime
  • Total calls, success rate, failed call count
  • Per-tool call statistics with average latency badges
  • Available tools with annotation labels (read-only, write, idempotent)
  • Registered resources and prompts
  • Live activity log of the last 20 tool calls

The data is sourced from the in-memory metrics store in backend/src/routes/mcp.routes.js and reset on backend restart.

flowchart TD
    DB["React Dashboard\n/mcp-monitor\npoll every 10 s"]
    DB -->|"GET /api/mcp/health"| H["connected · uptimeSeconds"]
    DB -->|"GET /api/mcp/metrics"| M["totalCalls · successRate\ntoolStats · recentCalls"]
    DB -->|"GET /api/mcp/tools/list"| T["tool names + annotations"]
    DB -->|"GET /api/mcp/resources/list"| R["resource URIs"]
    DB -->|"GET /api/mcp/prompts/list"| P["prompt names"]

API Reference

All /api/mcp/* endpoints require a valid JWT in the Authorization: Bearer <token> header.

MethodPathDescription
GET/api/mcp/healthMCP server connectivity + backend uptime
GET/api/mcp/metricsTool-call metrics (counts, latency, recent calls)
GET/api/mcp/tools/listList all registered tools with schemas + annotations
POST/api/mcp/tools/callCall a tool — body: { name, arguments }
GET/api/mcp/resources/listList registered MCP resources
GET/api/mcp/prompts/listList registered MCP prompts

Tool call example (curl)

# Authenticate first
TOKEN=$(curl -s -X POST http://localhost:5000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@example.com","password":"password"}' | jq -r '.token')

# Call a tool
curl -X POST http://localhost:5000/api/mcp/tools/call \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "search_knowledge_base",
    "arguments": { "query": "vpn", "limit": 3 }
  }'

Smithery Deployment

The server is published at @madosh/mcp-itsm on Smithery.

flowchart LR
    Dev["Developer\nnpm publish via\nsmithery publish"]
    Smithery["Smithery Cloud\nDocker container\nenv API_KEY injected"]
    MCPSrv["McpServer v3.0.0\nnpm start → stdio"]
    Client["Claude / Cursor\nany MCP client"]

    Dev -->|"smithery.yaml\ntools.json"| Smithery
    Smithery -->|"spawn"| MCPSrv
    Client -->|"MCP protocol\nstdio"| Smithery
    Smithery <-->|"proxy"| MCPSrv

Install via Smithery CLI

npx -y @smithery/cli install @madosh/mcp-itsm --client claude

Manual Smithery deploy

npm install -g @smithery/cli
smithery login
smithery publish

The smithery.yaml configuration:

startCommand:
  type: stdio
  configSchema:
    type: object
    required: [apiKey]
    properties:
      apiKey:
        type: string
  commandFunction: |-
    (config) => ({ command: 'npm', args: ['start'], env: { API_KEY: config.apiKey } })
tools:
  path: ./tools.json

Use with Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "mcp-itsm": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-itsm/index.js"],
      "env": { "API_KEY": "your-key" }
    }
  }
}

Debug with MCP Inspector

npm run debug-mcp
# Opens MCP Inspector at http://localhost:5173

Development

Available scripts

# Root
npm start              # Start MCP server on stdio
npm run debug-mcp      # Start with MCP Inspector attached

# Backend
cd backend
npm start              # Production
npm run dev            # Development (nodemon hot-reload)
npm test               # Jest test suite

# Frontend
cd frontend
npm start              # Dev server on :3000
npm run build          # Production build

Tech stack

LayerTechnologies
MCP ServerNode.js 18+, @modelcontextprotocol/sdk 1.28.0, Zod 3.23
BackendExpress 4, Mongoose 7, JWT, Helmet, Winston
FrontendReact 18, React Router 6, Bootstrap 5
MCP Spec2025-11-25
DeploymentSmithery (stdio), Docker

Running with Docker

docker build -t mcp-itsm .
docker run -e API_KEY=your-key mcp-itsm

Contributing

Contributions are welcome. Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feat/my-feature)
  3. Commit your changes (git commit -m 'feat: add my feature')
  4. Push to the branch (git push origin feat/my-feature)
  5. Open a Pull Request

Roadmap

  • Live ITSM system adapter — ServiceNow (Table API, incident table)
  • Live ITSM system adapters — Jira, Zendesk, Ivanti Neurons, Cherwell
  • CMDB / asset / CI support (currently ticket + KB only, no CMDB surface)
  • OAuth 2.1 / OIDC authorization for external clients
  • Elicitation — server-initiated mid-call user prompts
  • Experimental Tasks — durable async ticket workflows
  • outputSchema / structuredContent on all tools
graph LR
    subgraph Done["Shipped in v3.0.0"]
        D1["SDK 1.28.0 + Zod"]
        D2["McpServer API"]
        D3["Tool annotations"]
        D4["Resources + Prompts"]
        D5["SDK Client transport"]
        D6["Metrics + Dashboard"]
        D7["ServiceNow live adapter"]
    end
    subgraph Next["Next"]
        N1["OAuth 2.1 / OIDC"]
        N2["Elicitation"]
        N3["Tasks API"]
        N4["Jira/Zendesk/Ivanti/Cherwell live adapters"]
        N5["CMDB support"]
    end
    style Done fill:#f0fdf4,stroke:#86efac
    style Next fill:#eff6ff,stroke:#bfdbfe

License

MIT — see LICENSE for details.


Resources

More Productivity & Workflow MCP servers

Similar servers from the same category.

Integrates with monday.com API to enable direct access to boards, workflows, and data for automating tasks and managing resources without context switching.

Productivity & Workflow

Activepieces

activepieces

Dynamic server to which you can add apps (Google Calendar, Notion, etc) or advanced Activepieces Flows (Refund logic, a research and enrichment logic, etc).

search

Time

modelcontextprotocol

Time and timezone conversion capabilities

timeconversion

Interact with Microsoft Word.

word