Ivanti MCP
# Ivanti MCP
Production [FastMCP](https://gofastmcp.com) server for **Ivanti Neurons ITSM**. Tools call Ivanti's OData REST API. MCP is only the contract between a client (IDE, gateway, or agent) and this process.
Auth follows the same split used by Azure MCP and Atlassian Rovo:
1. **Inbound** — the MCP client authenticates to this server (HTTP only).
2. **Outbound** — this server authenticates to Ivanti with a tenant API key.
STDIO is for a local subprocess. HTTP is fail-closed: it will not start without `MCP_JWKS_URI` or `MCP_JWT_SECRET`.
Author: **Clinton Follette**.
## Tools
| Tool | Access | Notes |
|---|---|---|
| `get_incident` | read | Loads the incident by `RecId`. Maps `Subject` / `Symptom` (not Description). |
| `add_note` | write | Creates a journal (`Notes`, `Summary`, `ParentLink_RecID`). Hidden when `MCP_READ_ONLY=true`. |
| `resolve_incident` | write, destructive | Reads `Status` first; no-op if already closed. Hidden when `MCP_READ_ONLY=true`. |
Structured output is typed. Tool annotations set `readOnlyHint` / `destructiveHint` / `idempotentHint` so clients can skip confirmations on reads.
## Install (local STDIO)
Cursor / Claude Desktop / VS Code:
```json
{
"mcpServers": {
"ivanti": {
"command": "mcp-ivanti",
"env": {
"IVANTI_BASE_URL": "https://your-tenant.ivanti.com",
"IVANTI_API_KEY": "<rest-api-key>"
}
}
}
}
```
```bash
pip install .
mcp-ivanti
```
## Remote HTTP
```bash
export MCP_TRANSPORT=http
export MCP_HOST=127.0.0.1
export MCP_PORT=8001
export MCP_JWKS_URI=https://login.example.com/.well-known/jwks.json
export MCP_JWT_ISSUER=https://login.example.com
export MCP_JWT_AUDIENCE=mcp-ivanti
mcp-ivanti
```
Endpoint: `http://127.0.0.1:8001/mcp`. Health: `GET /health`.
Internal machine-to-machine can use an HMAC JWT (`MCP_JWT_SECRET`, HS256) instead of JWKS. Do not put this process on the public internet without TLS in front of it.
## Outbound Ivanti configuration
| Variable | Required | Purpose |
|---|---|---|
| `IVANTI_BASE_URL` | yes | Tenant origin |
| `IVANTI_API_KEY` | yes | REST key. Sent as `Authorization: rest_api_key=...` (or `Bearer` if `IVANTI_AUTH_SCHEME=bearer`) |
| `IVANTI_RESOLVED_STATUS` | no | Default `Closed` |
| `IVANTI_CLOSE_VIA` | no | `patch_status` or `quick_action` |
| `IVANTI_CLOSE_ACTION_ID` | if quick action | Quick Action id |
| `IVANTI_JOURNAL_OBJECT` | no | Default `journal.notess` — confirm in `$metadata` |
| `MCP_READ_ONLY` | no | Hide write tools |
Tokens never go in git. Copy `.env.example` to `.env`.
## Security
- HTTP without inbound JWT configuration refuses to boot.
- `mask_error_details` is on. Vendor 401s do not echo keys.
- RecId values are rejected if they contain path characters.
- Prefer binding HTTP to loopback or an internal hostname; set `MCP_ALLOWED_HOSTS` to enable Host/Origin protection.
See [SECURITY.md](SECURITY.md).
## License
MIT. Copyright (c) 2026 Clinton Follette.
TDQS
Scored across 3 tools
Each tool has a clearly distinct purpose: get_incident reads incident data, add_note writes to the journal, and resolve_incident changes status. There is no meaningful overlap between read and write operations.
All tool names use lowercase snake_case with a verb-first pattern. However, add_note does not explicitly reference incidents like the other two tools, so using add_incident_note would make the naming more consistent.
Three tools is at the lower end of the typical range but appropriate for a focused incident-handling server. Each tool is essential and earns its place without redundancy.
The set covers the common workflow of viewing an incident, adding a note, and resolving it. However, it lacks create, update, search/list, and other lifecycle operations, so it only partially covers the broader incident management domain.