Snipeit Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Snipeit Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A comprehensive Model Context Protocol (MCP) server for managing Snipe-IT inventory systems. This server enables AI assistants to perform full CRUD operations across your entire Snipe-IT instance with 39 tools covering all major API endpoints.
git clone https://github.com/jameshgordy/snipeit-mcp.git
cd snipeit-mcpuv syncThe server supports two authentication modes; pick one.
#### Mode A — API key (stdio or HTTP, single shared identity)
Create a .env file:
SNIPEIT_URL=https://your-snipeit-instance.com
SNIPEIT_TOKEN=your-api-token-here
SNIPEIT_ALLOWED_TOOLS=manage_assets,system_info # Optional: restrict exposed tools| Variable | Required | Description |
|---|---|---|
SNIPEIT_URL | Yes | Your Snipe-IT instance URL |
SNIPEIT_TOKEN | Yes | API token for authentication |
SNIPEIT_ALLOWED_TOOLS | No | Comma-separated list of tool names to expose. If unset, all tools are available. |
Getting an API Token:
#### Mode B — Interactive OAuth login (HTTP only, per-user identity)
In this mode the MCP server runs as a web service and acts as an OAuth proxy in front of Snipe-IT's built-in Laravel Passport provider. Each user logs in to Snipe-IT (going through your normal SAML / SSO if configured) and the MCP server uses that user's own access token for every tool call.
One-time Snipe-IT setup (admin):
https://your-snipeit-instance.com/admin/oauthhttps://your-mcp-public-url/auth/callbackEnvironment variables:
SNIPEIT_URL=https://your-snipeit-instance.com
SNIPEIT_OAUTH_CLIENT_ID=... # from /admin/oauth
SNIPEIT_OAUTH_CLIENT_SECRET=... # from /admin/oauth
SNIPEIT_MCP_BASE_URL=https://your-mcp-public-url
MCP_TRANSPORT=http
MCP_PORT=8000
# MCP_HOST=0.0.0.0 # defaults to 127.0.0.1| Variable | Required | Description |
|---|---|---|
SNIPEIT_URL | Yes | Your Snipe-IT instance URL |
SNIPEIT_OAUTH_CLIENT_ID | Yes | OAuth client ID from /admin/oauth |
SNIPEIT_OAUTH_CLIENT_SECRET | Yes | OAuth client secret from /admin/oauth |
SNIPEIT_MCP_BASE_URL | Yes | Public URL where this MCP server is reachable (used in the OAuth callback) |
SNIPEIT_MCP_REDIRECT_PATH | No | Override OAuth callback path (default /auth/callback) |
MCP_TRANSPORT | Yes | Must be http for OAuth mode |
MCP_HOST | No | Bind address (default 127.0.0.1; use 0.0.0.0 behind a reverse proxy) |
MCP_PORT | Yes | TCP port for the HTTP server |
LOG_LEVEL | No | DEBUG/INFO/WARNING/ERROR/CRITICAL (default INFO) |
[!NOTE] OAuth mode requires HTTP transport — starting with MCP_TRANSPORT=stdio while OAuth env vars are set fails at startup with a clear error.For running the server as a long-lived HTTPS service on a Linux VM (the typical shape for OAuth mode), the repo ships two helper scripts under scripts/:
| Script | Purpose |
|---|---|
scripts/setup-snipeit-mcp.sh | One-shot installer. Creates a snipeit-mcp service user, installs uv if missing, writes /etc/snipeit-mcp.env (seeding SNIPEIT_* values from a .env at the repo root if present), installs and starts a hardened systemd unit, and probes the local OAuth metadata endpoint. Idempotent. |
scripts/update-snipeit-mcp.sh | Routine update — git pull, re-uv sync, restart the service, re-probe. |
Quick-start on a fresh VM (assumes Debian/Ubuntu with systemd; needs root):
# 1. Clone the source tree
sudo git clone https://github.com/jameshgordy/snipeit-mcp.git /opt/snipeit-mcp
# 2. (Optional) Drop a .env at the repo root so setup can seed
# SNIPEIT_URL / SNIPEIT_OAUTH_CLIENT_ID / _SECRET / SNIPEIT_MCP_BASE_URL.
# Missing values become __FILL_ME__ placeholders in /etc/snipeit-mcp.env.
scp .env you@vm:/tmp/snipeit-seed.env
sudo mv /tmp/snipeit-seed.env /opt/snipeit-mcp/.env
# 3. Install and start
sudo bash /opt/snipeit-mcp/scripts/setup-snipeit-mcp.sh
# 4. Future updates
sudo bash /opt/snipeit-mcp/scripts/update-snipeit-mcp.sh[!NOTE] The scripts are committed with the executable bit set, sosudo /opt/snipeit-mcp/scripts/...works once they're checked out viagit clone. Thesudo bash ...form above is the bullet-proof alternative — it doesn't care about file permissions, useful if you transferred the scripts viascp/drag-and-drop and the bit didn't come along.
What the installer configures:
| Path | Purpose |
|---|---|
/opt/snipeit-mcp/ | Source tree (owned by service user) |
/var/lib/snipeit-mcp/ | FASTMCP_HOME — DCR'd MCP client registrations persist here |
/etc/snipeit-mcp.env | Secrets and deployment-specific URLs (SNIPEIT_* only) |
/etc/systemd/system/snipeit-mcp.service | systemd unit; infra settings (MCP_TRANSPORT, MCP_HOST, MCP_PORT, LOG_LEVEL, FASTMCP_HOME) are baked into its Environment= directives |
Configurable at install time via environment variables on the setup-snipeit-mcp.sh invocation:
| Variable | Default | Notes |
|---|---|---|
SOURCE_DIR | /opt/snipeit-mcp | Source tree path |
STATE_DIR | /var/lib/snipeit-mcp | Service-user home / FASTMCP_HOME |
ENV_FILE | /etc/snipeit-mcp.env | Generated env file |
SEED_ENV_FILE | $SOURCE_DIR/.env | Optional seed for SNIPEIT_* values |
MCP_TRANSPORT | http | Always http for OAuth mode |
MCP_HOST | 127.0.0.1 | Bind address. Loopback by default; set MCP_HOST=0.0.0.0 to expose on all interfaces (e.g. a reverse proxy on a different host) |
MCP_PORT | 8000 | TCP port |
LOG_LEVEL | INFO |
[!IMPORTANT] The scripts do not configure TLS — the server listens on plain HTTP on the chosenMCP_PORT. For public OAuth use, terminate TLS in front of it (corporate reverse proxy, Caddy, nginx, …) with a trusted certificate for the hostname inSNIPEIT_MCP_BASE_URL.
Web-based MCP clients (Claude.ai, Mistral's Le Chat, …) run their MCP transport through the client vendor's own backend, which needs to reach SNIPEIT_MCP_BASE_URL from the public internet — a VPN-only address won't work. If your Snipe-IT instance itself is VPN-only, the typical shape is to keep the MCP VM internal and put a public-facing reverse proxy in a DMZ in front of it:
Web client backend ──HTTPS──► public reverse proxy (DMZ) ──HTTP──► MCP VM (internal) ──HTTPS──► Snipe-IT (internal)What the DMZ proxy needs:
SNIPEIT_MCP_BASE_URL (e.g. snipeit.mcp.example.com).accept internal CAs.
http://<vm-ip>:<MCP_PORT>.Host, X-Forwarded-Proto: https, X-Forwarded-Host,X-Forwarded-For. FastMCP uses these to build correct OAuth metadata URLs.
(Inspector, mcp-remote, web clients) rely on it to discover the OAuth flow. Header-allowlist proxies are a common culprit.
[!NOTE] VPN is still required for the initial Snipe-IT login. The OAuth flow redirects the user's browser to https://<your-snipeit>/oauth/authorize for sign-in (and SSO bounce), which is VPN-only by definition. Once the user has signed in once, subsequent MCP tool calls and refresh-token rotation go client backend → DMZ → MCP VM → Snipe-IT entirely server-side, so users can keep using the web client from anywhere until the refresh token expires or is revoked, at which point a one-time VPN reconnect is needed to re-login.The right configuration depends on whether the server runs in API-key mode (stdio, local, one shared identity) or OAuth mode (HTTP, remote, per-user identity). See the previous section for how the server picks between them.
Add to your MCP configuration file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json
Option A: Install directly from GitHub (no clone required)
{
"mcpServers": {
"snipeit": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/jameshgordy/snipeit-mcp",
"snipeit-mcp"
],
"env": {
"SNIPEIT_URL": "https://your-snipeit-instance.com",
"SNIPEIT_TOKEN": "your-api-token-here"
}
}
}
}Option B: Run from a local clone
{
"mcpServers": {
"snipeit": {
"command": "uv",
"args": [
"--directory",
"/path/to/snipeit-mcp",
"run",
"snipeit-mcp"
],
"env": {
"SNIPEIT_URL": "https://your-snipeit-instance.com",
"SNIPEIT_TOKEN": "your-api-token-here"
}
}
}
}mcp-remote)When the server runs in OAuth mode it speaks HTTP, not stdio, so it cannot be launched directly by Claude Desktop. Use mcp-remote as a stdio bridge — it handles Dynamic Client Registration, opens the browser for interactive login, caches the resulting tokens, and refreshes them transparently. The server must already be running and reachable at the URL below (e.g. on a VM, behind a reverse proxy, or on localhost for dev).
{
"mcpServers": {
"snipeit": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://your-mcp-public-url/mcp"
]
}
}
}[!NOTE] No SNIPEIT_* env vars belong here — the server holds them. The first connection opens a browser tab for Snipe-IT login (going through your SSO if configured); subsequent connections reuse the cached refresh token.Open https://claude.ai → Settings → Connectors → Add custom connector. Paste the public URL of your running MCP server (e.g. https://your-mcp-public-url/mcp) and follow the OAuth prompt.
[!IMPORTANT] Claude.ai web requires the MCP server to be reachable from the public internet over HTTPS —localhostand unencrypted HTTP do not work here. Use themcp-remotebridge instead if you only have a localhost deployment.
Add to your Cursor MCP settings using the same JSON shape as the Claude Desktop examples above — stdio (API-key) or mcp-remote bridge (OAuth) — whichever matches your server mode.
npx @modelcontextprotocol/inspectorThen in the Inspector UI:
Streamable HTTP/mcp endpoint[!NOTE] Why "Via Proxy"? The Inspector UI is a static frontend served athttp://localhost:6274and the MCP server lives at a different origin (e.g.http://localhost:8000). In Direct mode the browser tries to talk to the MCP server itself, which fails for two compounding reasons: (a) FastMCP doesn't emit CORS headers for the Inspector origin, so requests are blocked client-side, and (b) the OAuth redirect flow needs server-side state the browser-only client can't keep. Via Proxy routes traffic through Inspector's own backend (atlocalhost:6277), which is same-origin from the MCP server's perspective and handles OAuth state correctly.
<!-- separates the two adjacent GH alerts -->
[!WARNING] Leave Client ID and Client Secret in the OAuth panel empty — Inspector will perform Dynamic Client Registration with the MCP server. Pasting your Snipe-IT (upstream) client_id and secret there is the most common misconfiguration; those credentials belong only in the server's .env, not in any MCP client.| Tool | Description |
|---|---|
manage_assets | CRUD operations with bytag/byserial lookup and advanced filtering |
asset_operations | State operations (checkout, checkin, audit, restore) |
asset_files | File attachments (upload, list, download, delete) |
asset_labels | Generate printable PDF labels |
asset_maintenance | Create maintenance records |
asset_licenses | View licenses assigned to an asset |
asset_requests | Submit/cancel checkout requests for requestable assets |
| Tool | Description |
|---|---|
manage_consumables | CRUD operations for consumables |
manage_components | CRUD operations for components |
component_operations | Checkout/checkin components to assets |
manage_accessories | CRUD operations for accessories |
accessory_operations | Checkout/checkin accessories to users, assets, or locations |
| Tool | Description |
|---|---|
manage_users | CRUD operations for users (+ restore, me) |
user_assets | Get items checked out to a user (assets, accessories, licenses, consumables, eulas) |
user_two_factor | Reset user two-factor authentication |
manage_companies | CRUD operations for companies |
manage_departments | CRUD operations for departments |
manage_groups | CRUD operations for permission groups |
| Tool | Description |
|---|---|
manage_categories | Manage categories for all item types |
manage_manufacturers | Manage manufacturer information |
manage_models | Manage asset models (+ list assets by model) |
manage_status_labels | Manage status labels (+ list assets by status) |
manage_locations | Manage locations (+ list assets/users by location) |
manage_suppliers | Manage supplier information |
manage_depreciations | Manage depreciation schedules |
| Tool | Description |
|---|---|
manage_fields | CRUD + associate/disassociate fields with fieldsets |
manage_fieldsets | CRUD operations for fieldsets (+ field listing, reorder) |
| Tool | Description |
|---|---|
manage_licenses | CRUD operations for licenses |
license_seats | Manage license seat assignments |
license_files | Manage license file attachments |
| Tool | Description |
|---|---|
activity_reports | Query activity logs and item history |
status_summary | Get asset counts grouped by status label |
audit_tracking | Track assets due/overdue for audit |
| Tool | Description |
|---|---|
manage_imports | CSV import workflow (upload, map columns, process) |
| Tool | Description |
|---|---|
system_info | Get Snipe-IT version information |
manage_backups | List and download database backups |
ldap_operations | LDAP sync and connection testing |
model_files | Manage file attachments for asset models |
{
"action": "create",
"asset_data": {
"status_id": 1,
"model_id": 5,
"asset_tag": "LAP-001",
"name": "MacBook Pro 14",
"serial": "C02X12345"
}
}{
"action": "create",
"user_data": {
"first_name": "John",
"last_name": "Doe",
"username": "jdoe",
"email": "[email protected]",
"password": "securepassword",
"password_confirmation": "securepassword",
"department_id": 1
}
}{
"user_id": 123,
"asset_type": "all"
}{
"action": "checkout",
"component_id": 45,
"checkout_data": {
"assigned_to": 123,
"assigned_qty": 2,
"note": "RAM upgrade"
}
}{
"action": "list",
"action_type": "checkout",
"limit": 50
}{
"action": "create",
"field_data": {
"name": "MAC Address",
"element": "text",
"format": "MAC"
}
}{
"action": "associate",
"field_id": 5,
"fieldset_id": 1,
"required": true,
"order": 1
}All tools return structured JSON responses:
Success (create):
{
"success": true,
"action": "create",
"asset": {
"id": 123,
"asset_tag": "LAP-001",
"name": "MacBook Pro 14"
}
}Success (list):
All list endpoints return pagination metadata:
{
"success": true,
"action": "list",
"count": 3,
"total": 1602,
"limit": 20,
"offset": 0,
"has_more": true,
"assets": [ ... ]
}| Field | Description |
|---|---|
count | Number of items in this page |
total | Total items matching the query |
limit | Page size used |
offset | Starting offset |
has_more | true if more pages remain |
Error:
{
"success": false,
"error": "Asset not found: Asset with tag LAP-999 not found."
}src/snipeit_mcp/
├── __init__.py # Public API re-exports
├── __main__.py # Entry point (snipeit-mcp script)
├── mcp_server.py # FastMCP instance + tool whitelist
├── client.py # SnipeIT API clients
├── schemas.py # Pydantic input schemas
└── tools/ # 9 modules grouped by Snipe-IT domain
├── assets.py
├── inventory.py
├── foundational.py
├── licenses.py
├── people.py
├── custom_fields.py
├── reports.py
├── imports.py
└── system.pyBuilt with:
https://)status_id and model_id for assets)MIT License
Contributions welcome! Please open an issue or submit a pull request.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.