{
  "markdown": "# IT Glue MCP Server\n\nA Model Context Protocol (MCP) server that provides Claude with access to IT Glue documentation and asset management.\n\n## One-Click Deployment\n\n[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/WYRE-AI/itglue-mcp/tree/main)\n\n[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/WYRE-AI/itglue-mcp)\n\n> [!NOTE]\n> Unlike the other Wyre MCP servers, this one talks to the IT Glue API directly and\n> has **no private `@wyre-ai/*` runtime dependency**, so the one-click build\n> does not need a GitHub Packages token — the cloud builder's `npm ci` only pulls\n> public packages. (A `read:packages` token is only needed to install the published\n> `@wyre-ai/itglue-mcp` package itself; see [Installation](#installation).)\n> The DigitalOcean target builds the full Docker image and runs the complete MCP\n> server over HTTP and is the recommended path; this repo does not ship a Workers\n> entrypoint (`src/worker.ts`), so prefer DigitalOcean or the prebuilt container\n> image (`ghcr.io/wyre-ai/itglue-mcp`).\n\n## Installation\n\nThis package is published to the **GitHub Packages** npm registry, which requires a\ntoken even for public packages. Authenticate npm once, then install:\n\n```bash\n# Authenticate npm to GitHub Packages (token needs the read:packages scope)\nexport NODE_AUTH_TOKEN=$(gh auth token)   # or a PAT with read:packages\n\nnpm install @wyre-ai/itglue-mcp\n```\n\nThe repo's `.npmrc` already points the `@wyre-ai` scope at GitHub Packages and\nreads the token from `NODE_AUTH_TOKEN`, so no further config is needed. The same applies\nto `npx @wyre-ai/itglue-mcp`.\n\nOr use the Docker image:\n\n```bash\ndocker pull ghcr.io/wyre-ai/itglue-mcp:latest\n```\n\n## Configuration\n\nThe server accepts credentials via environment variables:\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `ITGLUE_API_KEY` | Your IT Glue API key (format: ITG.xxx) | Yes (env mode) |\n| `ITGLUE_JWT` | A user-session JWT used as an optional **fallback** for document-folder operations on tenants whose API key cannot access the Document Folders resource yet. See [JWT fallback for document-folder operations](#jwt-fallback-for-document-folder-operations). | No |\n| `ITGLUE_REGION` | API region: `us`, `eu`, or `au` (default: `us`) | No |\n| `ITGLUE_BASE_URL` | Override the IT Glue API base URL (advanced) | No |\n| `MCP_TRANSPORT` | Transport: `stdio` (local) or `http` (remote). Defaults to `stdio` when run via `npx`/`node`, and to `http` in the Docker image. | No |\n| `MCP_HTTP_PORT` | Port for HTTP transport (default: `8080`) | No |\n| `MCP_HTTP_HOST` | Bind address for HTTP transport (default: `0.0.0.0`) | No |\n| `AUTH_MODE` | `env` (read credentials from environment) or `gateway` (read per-request credentials from HTTP headers). Default: `env`. | No |\n\nAlternative: When `AUTH_MODE=gateway`, the MCP Gateway injects credentials per request via HTTP headers instead of environment variables. See [Remote Deployment](#remote-deployment-http-streamable).\n\n### JWT fallback for document-folder operations\n\n**A JWT is optional** — it is only needed if your tenant's API key can't access Document Folders yet. Every folder-related path tries your API key first:\n\n- `search_documents` — defaults to a folder-inclusive listing (`filter[document_folder_id]=null` returns all documents, foldered ones included; each result carries its `documentFolderId`). If the tenant's API rejects that filter, the server retries the `[ne]` filter form and finally degrades to the legacy root-only listing, saying so in the result. No JWT is involved at any layer.\n- `list_document_folders` — IT Glue's public (API-key) API now documents a Document Folders resource, which is rolling out across tenants through 2026. The server tries the API key first (on the organization-relationship path, then the top-level `/document_folders` path) and only falls back to a JWT if the key is rejected.\n- `create_document` — the name-based folder picker uses the same API-key-first enumeration, then a configured JWT; if neither can list folders, it prompts for a folder URL / sibling-document URL / numeric folder ID as the last resort.\n\nIf you do need the JWT fallback, provide it in whichever way matches your deployment:\n\n| Mode | How to supply the JWT |\n|------|-----------------------|\n| Local / env (`AUTH_MODE=env`) | Set the `ITGLUE_JWT` environment variable. |\n| Remote gateway (`AUTH_MODE=gateway`) | Send the `X-ITGlue-JWT` request header. |\n| Interactive clients (Claude Desktop/Code) | Leave it unset — the server prompts you to paste a JWT on first use and caches it for the session. |\n\n> **Headless deployments (Docker, cloud):** there is no one to answer the interactive prompt, so if your tenant's API key cannot enumerate folders you must set `ITGLUE_JWT` (env mode) or send `X-ITGlue-JWT` (gateway mode) for folder enumeration to work.\n\n**Retrieving a JWT from your browser:**\n\n1. Sign in to IT Glue in your browser.\n2. Open DevTools → **Network** tab.\n3. Click any request to `itg-api-*.itglue.com`.\n4. Copy the value of the `Authorization: Bearer <token>` request header — the `<token>` part is your JWT.\n\n> **Expiry:** IT Glue JWTs are short-lived (~2 hours). A JWT placed in `ITGLUE_JWT` on a long-running container will go stale and the JWT fallback will start failing until it is refreshed. Interactive clients are simply re-prompted on expiry. API-key operations are unaffected.\n\n## Available Tools\n\n### Organizations\n\n- **search_organizations** - Search for organizations with optional filtering by name, type, status, or PSA ID\n- **get_organization** - Get a specific organization by ID\n\n### Configurations (Devices/Assets)\n\n- **search_configurations** - Search for configurations with filtering by organization, name, type, status, serial number, RMM ID, or PSA ID\n- **get_configuration** - Get a specific configuration by ID\n\n### Locations (Addresses/Sites)\n\n- **search_locations** - Search an organization's locations (built-in address/site records), filtering by organization, name, city, region, or country. Results include the address fields and phone number.\n- **get_location** - Get a specific location by ID, including its full address and phone number\n- **create_location** - Create a new location for an organization (requires `name`, typically `country_id`)\n- **update_location** - Update an existing location; only the fields you supply are changed\n\n### Passwords\n\n- **search_passwords** - Search for password entries (metadata only, no actual passwords in results)\n- **get_password** - Get a specific password entry including the actual password value\n\n### Documents\n\n- **search_documents** - Search for documents with filtering by organization, name, or folder. Defaults to a folder-inclusive listing (each result carries its `documentFolderId`), degrading gracefully to a root-only listing on tenants whose API rejects the folder filter\n- **get_document** - Get a specific document by ID, including its sectioned body. Renders as an interactive card in MCP Apps hosts — see [Interactive Document Card](#interactive-document-card-mcp-apps)\n- **list_document_folders** - List an organization's document folders (names and IDs). Works with an API key on tenants where IT Glue exposes the Document Folders resource; falls back to a JWT otherwise — see [JWT fallback for document-folder operations](#jwt-fallback-for-document-folder-operations)\n- **create_document_image** - Upload an image into a document so it can be shown inline in the body. Base64 in, no multipart needed. See [Images in documents](#images-in-documents)\n\n### Attachments\n\n- **create_attachment** - Attach a file to a checklist, checklist template, configuration, contact, document, domain, flexible asset, location, password, SSL certificate or ticket. Base64 in\n- **list_attachments** - List a record's attachments, with their download URLs\n\n### Flexible Assets\n\n- **search_flexible_assets** - Search for flexible assets (requires flexible_asset_type_id)\n\n### User Metrics\n\n- **search_user_metrics** - Search user activity metrics: per-user, per-organization, per-resource-type counts of `created` / `viewed` / `edited` / `deleted` actions, bucketed by date. Filter by `user_id`, `organization_id`, `resource_type`, and a `start_date` / `end_date` range; sort by `id`, `created`, `viewed`, `edited`, `deleted`, or `date` (prefix `-` for descending).\n\n  This is the raw data behind IT Glue's user reputation scores, so it answers \"who is actually maintaining documentation\" — per tech, per client, per resource type.\n\n  **Date-range rules** (verified live against `api.itglue.com`, 2026-08-06):\n\n  - The range may span at most **7 days end-to-start** — so `2026-08-01,2026-08-08` is accepted (8 calendar days) and `2026-08-01,2026-08-09` returns 422. The API compares the *difference*, not the inclusive day count; reading \"longer than a week\" as 7 inclusive days is off by one in the direction that rejects valid queries.\n  - **`end_date` requires `start_date`.** IT Glue rejects a filter beginning with a wildcard (`*,2026-08-07` → 422), so an end alone is a guaranteed error rather than a narrower query. An open *end* (`2026-08-01,*`) is fine.\n  - Both violations return the **same** 422 title — *\"date range filter cannot be longer than a week, and cannot start with a wildcard\"* — so the API cannot tell you which one you hit. The tool checks both itself and says which, without spending the call.\n  - Omit both dates to let IT Glue apply its own default window.\n\n  **Gotcha — unknown filter keys are silently ignored.** `filter[not-a-real-key]=x` returns HTTP 200 with the *full unfiltered* result set, not an error (verified live). A typo'd or misremembered filter name therefore looks like a successful, correctly-scoped query while actually returning everything. Cross-check row counts against a deliberately impossible value (`filter[resource-type]=ZZZNoSuchType` correctly returns 0 rows) if a result looks too broad.\n\n### Utility\n\n- **itglue_health_check** - Verify connectivity to IT Glue API\n\n### Interactive Document Card (MCP Apps)\n\n`get_document` renders as an interactive card in MCP Apps hosts (Claude\nDesktop/web) showing the document's name, organization, folder, key dates, and a\nplain-text preview of its sections; plain-JSON behavior is unchanged in other\nhosts. The card is read-only — neutral by default, brandable via\n`window.__BRAND__` injection or `MCP_BRAND_*` env vars (`MCP_BRAND_NAME`,\n`MCP_BRAND_LOGO_URL`, `MCP_BRAND_PRIMARY_COLOR`, `MCP_BRAND_ACCENT_COLOR`,\n`MCP_BRAND_BG`, `MCP_BRAND_TEXT`) — no rebuild needed.\n\n### Images in documents\n\nIT Glue's HTML sanitiser is strict about how a picture gets into a document\nbody, and fails two of three ways (verified live against `api.itglue.com`,\n2026-08-31):\n\n| What you try | What happens |\n|---|---|\n| Inline `<svg>` in section HTML | **Silently stripped.** The section saves, returns 200, and the diagram is simply gone from the stored content |\n| `<img src=\"data:image/png;base64,…\">` | **Rejected with a 500**, not a validation error |\n| `<img src=\"https://…\">` | Accepted and preserved |\n\nSo the only route to an image in a document body is to upload the file first and\nreference the URL IT Glue gives back — which is what `create_document_image`\ndoes. The inline-SVG case is the one worth knowing about, because it looks like\na successful write.\n\n```\ncreate_document_image(document_id, file_name, content)   # content = raw base64\n→ reference the returned URL from an <img src> in update_document_section\n→ publish_document\n```\n\nPass **raw base64**. If a `data:...;base64,` prefix is left on the front the\ntool strips it rather than passing it through: IT Glue stores whatever it is\ngiven, so a prefixed payload uploads \"successfully\" and produces a corrupt file\nthat only surfaces when somebody opens it.\n\n## Usage with Claude Code\n\nAdd to your `.mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"itglue\": {\n      \"command\": \"npx\",\n      \"args\": [\"@wyre-ai/itglue-mcp\"],\n      \"env\": {\n        \"ITGLUE_API_KEY\": \"${ITGLUE_API_KEY}\",\n        \"ITGLUE_REGION\": \"us\"\n      }\n    }\n  }\n}\n```\n\nOr with Docker (local stdio):\n\n```json\n{\n  \"mcpServers\": {\n    \"itglue\": {\n      \"command\": \"docker\",\n      \"args\": [\n        \"run\", \"--rm\", \"-i\",\n        \"-e\", \"MCP_TRANSPORT=stdio\",\n        \"-e\", \"ITGLUE_API_KEY\",\n        \"ghcr.io/wyre-ai/itglue-mcp:latest\"\n      ],\n      \"env\": {\n        \"ITGLUE_API_KEY\": \"${ITGLUE_API_KEY}\"\n      }\n    }\n  }\n}\n```\n\n> **Note:** The Docker image defaults to HTTP transport. The `-e MCP_TRANSPORT=stdio` above is required to run it as a local stdio server for Claude Desktop/Code. For server deployments, see [Remote Deployment](#remote-deployment-http-streamable) below.\n\n## Remote Deployment (HTTP Streamable)\n\nFor server/cloud deployments, run the server with the HTTP Streamable transport. The Docker image already defaults to `MCP_TRANSPORT=http` on port `8080`, exposing two endpoints:\n\n- `POST /mcp` — MCP Streamable HTTP endpoint (stateless: a fresh server is created per request)\n- `GET /health` — unauthenticated health check\n\n### Env mode (single tenant)\n\nCredentials come from environment variables. Use this when one API key serves the deployment:\n\n```bash\ndocker run -d \\\n  --name itglue-mcp \\\n  -p 8080:8080 \\\n  -e ITGLUE_API_KEY=\"ITG.xxxxxxxx\" \\\n  -e ITGLUE_REGION=\"us\" \\\n  --restart unless-stopped \\\n  ghcr.io/wyre-ai/itglue-mcp:latest\n\n# Verify\ncurl http://localhost:8080/health\n# {\"status\":\"ok\",\"transport\":\"http\",\"authMode\":\"env\",...}\n```\n\nClients connect to `http://<host>:8080/mcp` using the MCP Streamable HTTP transport.\n\n### Gateway mode (multi-tenant / hosted)\n\nWhen deployed behind an MCP Gateway (e.g. `mcp.wyre.ai`), set `AUTH_MODE=gateway`. Credentials are then injected per request via HTTP headers rather than environment variables:\n\n```bash\ndocker run -d \\\n  --name itglue-mcp \\\n  -p 8080:8080 \\\n  -e AUTH_MODE=gateway \\\n  --restart unless-stopped \\\n  ghcr.io/wyre-ai/itglue-mcp:latest\n```\n\nThe gateway supplies credentials on each request via these headers:\n\n| Header | Description | Required |\n|--------|-------------|----------|\n| `X-ITGlue-API-Key` (or `X-API-Key`) | IT Glue API key | One of API-Key or JWT |\n| `X-ITGlue-JWT` | JWT for elevated-scope operations | One of API-Key or JWT |\n| `X-ITGlue-Region` | API region: `us`, `eu`, or `au` (default: `us`) | No |\n| `X-ITGlue-Base-URL` | Override the IT Glue API base URL | No |\n\nRequests missing both `X-ITGlue-API-Key` and `X-ITGlue-JWT` receive a `401`. The `/health` endpoint reports `\"authMode\":\"gateway\"` in this mode.\n\n### Running without Docker\n\nThe same transport works from an installed/built copy by setting `MCP_TRANSPORT=http`:\n\n```bash\nMCP_TRANSPORT=http MCP_HTTP_PORT=8080 ITGLUE_API_KEY=\"ITG.xxxxxxxx\" \\\n  npx @wyre-ai/itglue-mcp\n```\n\n## Example Queries\n\nOnce configured, you can ask Claude:\n\n- \"Search for organizations containing 'Acme' in IT Glue\"\n- \"Get the configuration details for device ID 12345\"\n- \"Find all passwords for organization ID 100\"\n- \"Search for flexible assets of type 54321\"\n\n## Security Notes\n\n- Password search results do not include actual password values for security\n- Use `get_password` with explicit ID to retrieve password values\n- Store your API key securely using environment variables or a secrets manager\n- The API key should have appropriate read permissions in IT Glue\n\n## License\n\nApache-2.0\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n",
  "bytes": 15580,
  "sha": "38652983ce3f7369f25f7af5e2b8a4e0c6fc88c0ce5bd09e65e362ccd835c247",
  "repo_slug": "wyre-technology/itglue-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_wyre_technology_itglue_mcp_a9708444/readme"
}