Skip to main content
Glama
SyncroMSP MCP Server

SyncroMSP MCP Server

by advenimus

SyncroMSP MCP Server

A fully-featured Model Context Protocol server for the SyncroMSP IT/MSP platform. Gives AI assistants full access to tickets, customers, assets, invoices, and 30+ resource types.

Features

  • 190 tools across 16 domains, covering every current endpoint in Syncro's API docs

  • Full CRUD for tickets, customers, invoices, estimates, appointments, contracts, products, and more

  • Ticket comments — email replies, public notes, and private/internal notes

  • Line items — add products from catalog or manual entries to tickets, invoices, estimates, schedules

  • RMM — create, read, mute, resolve alerts on assets; run scripts on an asset now or once later

  • Rate limiting — built-in 180 req/min token bucket (Syncro API limit)

  • Confirmation required for all destructive operations (DELETE, etc.)

  • Auto-update check — warns on startup if a newer version is available

Deployment Options

Method

Best For

Auto-Updates

Claude Code Plugin

One-command install of MCP + Skill in Claude Code

Yes (marketplace)

Claude Code (MCP only)

Developers wiring up the MCP without the bundled skill

Yes (npx)

Claude Desktop

Local desktop app users

Yes (npx)

Docker + Claude.ai

Teams, remote access, Claude.ai web

Yes (Watchtower)

From Source

Development and customization

Manual

Bundled Skill

This repo also ships a Claude Skill that gives Claude operating context for the MCP — workflows, API quirks (e.g. line_items ignored on *_create), the two-step labor logging pattern, ticket status transitions, ticket comment subject conventions, and a hyperlink rule that renders every Syncro record as a clickable link. It's company-agnostic; user-specific values are discovered at runtime via API calls and cached locally.

Recommended — Claude Code plugin (auto-updating, bundles skill + MCP, prompts for credentials on install):

/plugin marketplace add advenimus/syncromsp-mcp
/plugin install syncromsp@syncromsp

On enable, Claude Code prompts for your Syncro subdomain and API key. The subdomain is stored in settings.json; the API key goes to your system keychain. Both are passed to the bundled MCP server as SYNCRO_SUBDOMAIN and SYNCRO_API_KEY automatically — no shell exports or manual claude mcp add needed.

New commits to main propagate to installed users on the next marketplace refresh — no manual re-install needed.

Claude Desktop / claude.ai (no public plugin marketplace yet): download syncromsp-skill.zip from Releases and upload via Settings → Capabilities → Skills (paid plan, preview feature). It syncs across that account automatically. For Team/Enterprise plans, an org admin can upload via Organization settings and every member gets auto-updates when the admin re-uploads a revised zip. Full instructions in plugins/syncromsp/skills/syncromsp/README.md.

The release zip is always published at:

https://github.com/advenimus/syncromsp-mcp/releases/latest/download/syncromsp-skill.zip

The zip is rebuilt on every release. Skill-only changes committed as docs(skill): ... cut a patch release, so they reach the zip.


Related MCP server: Autotask MCP Server

Prerequisites

Getting Your Syncro API Key

  1. Log in to your Syncro account

  2. Go to Admin > API Tokens

  3. Click + New Token

  4. Select the Custom Permissions tab

  5. Name your token and set permissions for the resources you need

  6. Click Create and copy the token (it cannot be retrieved later)

Your subdomain is the part before .syncromsp.com in your Syncro URL (e.g., mycompany from mycompany.syncromsp.com).


Claude Code

claude mcp add syncromsp \
  --env SYNCRO_API_KEY=your-api-key \
  --env SYNCRO_SUBDOMAIN=your-subdomain \
  -- npx syncromsp-mcp

That's it. Claude Code will download and run the latest version automatically.


Claude Desktop

Option 1: MCPB Extension

Download the latest .mcpb file from Releases and double-click to install. Claude Desktop will prompt you for your API key and subdomain.

Option 2: Manual Configuration

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "syncromsp": {
      "command": "npx",
      "args": ["-y", "syncromsp-mcp"],
      "env": {
        "SYNCRO_API_KEY": "your-api-key",
        "SYNCRO_SUBDOMAIN": "your-subdomain"
      }
    }
  }
}

Config file location:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Restart Claude Desktop after saving. The server updates automatically via npx on each restart.


Docker Deployment (Remote MCP)

Deploy as a Docker container for remote access from Claude.ai, shared team usage, or running on a server. Each person signs in with their own Syncro API key, so every action in Syncro is made as that person, with that key's permissions. There is no shared password and no server-wide Syncro key.

Step 1: Clone and Configure

git clone https://github.com/advenimus/syncromsp-mcp.git
cd syncromsp-mcp
cp .env.example .env

Edit .env with your settings:

# Required: The public URL where this server will be reachable
# Must be HTTPS for production (put behind Traefik, Caddy, nginx, etc.)
MCP_BASE_URL=https://mcp.yourcompany.com

# Required: 32 random bytes that encrypt every sign-in token.
# Generate with: openssl rand -hex 32
# Keep it secret and keep it stable: changing it signs everyone out.
MCP_TOKEN_ENCRYPTION_KEY=

# Required: which Syncro accounts may sign in. Set ONE of these three.
SYNCRO_SUBDOMAIN=yourcompany              # lock sign-in to one account
# MCP_ALLOWED_SUBDOMAINS=acme,beta         # or allow a list of accounts
# MCP_ALLOW_ANY_SUBDOMAIN=true             # or allow any Syncro account

Do not set SYNCRO_API_KEY for this setup. It is ignored in HTTP sign-in mode.

Hosting for any Syncro account: set MCP_ALLOW_ANY_SUBDOMAIN=true and leave SYNCRO_SUBDOMAIN unset. The sign-in page then asks for both the subdomain and the API key, so any Syncro tenant can connect with its own credentials. Each tenant only ever reaches its own Syncro account, gets its own outbound rate budget inside a server-wide one, and each user is limited to MCP_MAX_SESSIONS_PER_USER open sessions.

Step 2: Deploy

docker compose up -d

The container runs on port 8080 by default. You need a reverse proxy (Traefik, Caddy, nginx) in front to provide HTTPS.

Step 3: Connect from Claude.ai

  1. In Syncro, create an API token for yourself under Admin > API Tokens. Its permissions decide what Claude can do.

  2. In Claude.ai, go to Settings > MCP Servers > Add Remote Server

  3. Enter your MCP URL: https://mcp.yourcompany.com/mcp

  4. Claude.ai will auto-discover the OAuth endpoints

  5. A sign-in page appears. Enter your Syncro subdomain (unless the server is locked to one) and your API key.

  6. The server checks the key with Syncro. If Syncro accepts it, Claude.ai connects and all tools become available.

Each teammate repeats steps 1 to 6 with their own key.

How Sign-in Works

The server implements the MCP OAuth 2.1 + PKCE spec. The login step is your own Syncro key:

Client connects → 401 Unauthorized
  → Client discovers /.well-known/oauth-authorization-server
  → Client dynamically registers (RFC 7591)
  → Client redirects user to /authorize
  → User enters Syncro subdomain + API key
  → Server calls https://<subdomain>.syncromsp.com/api/v1/me with that key (5 second timeout)
  → Syncro says OK: auth code issued → tokens granted → MCP access
  → Anything else: one generic "Sign-in failed" message, nothing issued
  • No credential database. Your subdomain and key live only inside your tokens, which are encrypted and signed with AES-256-GCM using MCP_TOKEN_ENCRYPTION_KEY. The server stores nothing on disk.

  • Sign-ins survive restarts. Tokens and client registrations are self-contained encrypted blobs, so a restart or redeploy does not sign anyone out (as long as MCP_TOKEN_ENCRYPTION_KEY stays the same).

  • Each session uses its owner's key. A session is bound to the key that opened it. Another user presenting that session id gets a 404.

  • Access tokens last 1 hour. Refresh tokens last 30 days and are replaced on every use. Reusing an old refresh token cancels that whole sign-in.

  • The subdomain must be a plain name like mycompany. The server always builds https://<subdomain>.syncromsp.com itself, so it cannot be pointed at another host.

  • Failed sign-in attempts are limited to 5 per IP address per 15 minutes. Successful sign-ins do not count.

  • The key is never put in a URL, a log line, or an error message.

  • The sign-in page shows the redirect_uri so users can check where they will be sent (anti-phishing).

Signing out and revoking access

  • To cut off a key for sure, delete it in Syncro (Admin > API Tokens). Every token that carries it stops working at Syncro right away. This is the real kill switch.

  • Claude.ai's "disconnect" calls the revoke endpoint. Revoked tokens are remembered in memory until they expire. That memory resets when the server restarts, so a restart re-enables a revoked token that has not expired yet. If that matters, delete the key in Syncro.

Rotating the encryption key

  1. Generate a new key: openssl rand -hex 32

  2. Move the current value to MCP_TOKEN_ENCRYPTION_KEY_PREVIOUS and put the new one in MCP_TOKEN_ENCRYPTION_KEY. Restart.

  3. Existing sign-ins keep working. New and refreshed tokens use the new key.

  4. After 30 days (the refresh token lifetime), remove MCP_TOKEN_ENCRYPTION_KEY_PREVIOUS and restart. Client registrations are sealed with the key too, so a client that registered before the rotation (for example a Claude.ai connector) may then need to be removed and re-added.

If the key may have leaked, do not keep it as the previous key: just replace it. Everyone has to sign in again, and Claude.ai may need the connector removed and re-added. Also ask users to delete and re-create their Syncro API keys, because a leaked encryption key plus a captured token reveals the Syncro key inside.

For the full threat model, defense layers, and operator responsibilities, see SECURITY.md.

Example: Docker with Traefik

services:
  syncro-mcp:
    image: ghcr.io/advenimus/syncromsp-mcp:latest
    container_name: syncromsp-mcp
    restart: unless-stopped
    environment:
      - MCP_TRANSPORT=http
      - MCP_PORT=8080
      - MCP_BASE_URL=https://mcp.yourcompany.com
      - MCP_TOKEN_ENCRYPTION_KEY=${MCP_TOKEN_ENCRYPTION_KEY}
      - SYNCRO_SUBDOMAIN=${SYNCRO_SUBDOMAIN}
    # Container hardening — recommended for any public-facing deployment
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    tmpfs:
      - /tmp:size=10m,mode=1777
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.mcp.rule=Host(`mcp.yourcompany.com`)"
      - "traefik.http.routers.mcp.entrypoints=websecure"
      - "traefik.http.routers.mcp.tls.certresolver=letsencrypt"
      - "traefik.http.services.mcp.loadbalancer.server.port=8080"

Example: Docker with Caddy

services:
  syncro-mcp:
    image: ghcr.io/advenimus/syncromsp-mcp:latest
    container_name: syncromsp-mcp
    restart: unless-stopped
    environment:
      - MCP_TRANSPORT=http
      - MCP_BASE_URL=https://mcp.yourcompany.com
      - MCP_TOKEN_ENCRYPTION_KEY=${MCP_TOKEN_ENCRYPTION_KEY}
      - SYNCRO_SUBDOMAIN=${SYNCRO_SUBDOMAIN}
    expose:
      - "8080"
    # Container hardening — recommended for any public-facing deployment
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    tmpfs:
      - /tmp:size=10m,mode=1777

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data

volumes:
  caddy_data:

Caddyfile:

mcp.yourcompany.com {
    reverse_proxy syncro-mcp:8080
}

For testing on private networks only. The server refuses to start with MCP_AUTH=false unless you also set MCP_I_UNDERSTAND_INSECURE=true as an explicit foot-gun guard:

In this mode there is no sign-in page, and every session uses one server-wide SYNCRO_API_KEY. The default docker-compose.yml does not pass that variable in, so add these lines under environment: first:

      - SYNCRO_API_KEY=${SYNCRO_API_KEY}
      - MCP_I_UNDERSTAND_INSECURE=${MCP_I_UNDERSTAND_INSECURE}

Then start it:

MCP_AUTH=false MCP_I_UNDERSTAND_INSECURE=true SYNCRO_API_KEY=your-key SYNCRO_SUBDOMAIN=yourcompany docker compose up -d

Warning: Without auth, anyone who can reach the URL gets full access to your Syncro account. Never disable auth on a publicly-reachable deployment, even briefly.

Upgrading from the shared-password setup

Older versions used one shared password (MCP_AUTH_SECRET) and one server-wide SYNCRO_API_KEY. To upgrade:

  1. Add MCP_TOKEN_ENCRYPTION_KEY (openssl rand -hex 32).

  2. Keep SYNCRO_SUBDOMAIN to lock sign-in to your account, or set MCP_ALLOWED_SUBDOMAINS / MCP_ALLOW_ANY_SUBDOMAIN=true.

  3. Remove MCP_AUTH_SECRET and SYNCRO_API_KEY. (If left in place they are ignored, with a warning in the log.)

  4. Restart. Everyone signs in once with their own Syncro API key. In Claude.ai you may need to remove and re-add the connector, because old client registrations were kept in memory and are gone.


Environment Variables

Variable

Required

Default

Description

SYNCRO_API_KEY

stdio, or HTTP with MCP_AUTH=false

—

Your Syncro API token. Ignored (with a warning) in HTTP sign-in mode, where each user brings their own.

SYNCRO_SUBDOMAIN

stdio, or HTTP with MCP_AUTH=false

—

Your Syncro subdomain. In HTTP sign-in mode, setting it locks sign-in to that one subdomain.

MCP_TRANSPORT

No

stdio

stdio (local) or http (Docker/remote)

MCP_PORT

No

8080

HTTP listen port

MCP_BASE_URL

For Docker

—

Public HTTPS URL (e.g., https://mcp.yourcompany.com)

MCP_AUTH

No

true

true or false to disable OAuth (see foot-gun guard below)

MCP_TOKEN_ENCRYPTION_KEY

HTTP sign-in mode

—

32 random bytes as 64 hex chars or base64. Encrypts every token and client registration. Generate with openssl rand -hex 32. The server refuses to start without a valid one.

MCP_TOKEN_ENCRYPTION_KEY_PREVIOUS

No

—

The old key during a rotation. Used only to read existing tokens, never to create new ones.

MCP_ALLOWED_SUBDOMAINS

One of these three in HTTP sign-in mode

—

Comma list of subdomains allowed to sign in, e.g. acme,beta. Ignored if SYNCRO_SUBDOMAIN is set.

MCP_ALLOW_ANY_SUBDOMAIN

One of these three in HTTP sign-in mode

—

Set to true to let any Syncro subdomain sign in.

MCP_I_UNDERSTAND_INSECURE

If MCP_AUTH=false

—

Must be true to start the server with auth disabled. Foot-gun guard.

MCP_TOOL_MODE

No

flat

flat (all tools) or navigation (lazy domains)

MCP_SESSION_IDLE_MINUTES

No

60

HTTP sessions unused this long are closed (checked every 5 minutes). Clients reconnect automatically.

MCP_ALLOWED_REDIRECT_URIS

Recommended

—

Comma list of client return addresses allowed to register, e.g. https://claude.ai/api/mcp/auth_callback,http://localhost. Blocks sign-in phishing through look-alike clients. An entry without a port allows any port.

MCP_ALLOWED_HOSTS

No

—

Extra host names the server answers to besides the MCP_BASE_URL host. Requests for any other host get 421 (blocks DNS rebinding).

MCP_SERVER_RATE_LIMIT_PER_MINUTE

No

180

Syncro calls per minute from this whole server, on top of each tenant's own 180/min budget. Syncro documents its limit per IP, and all tenants share the server's IP. Raise it only if you know Syncro counts your tenants separately.

MCP_MAX_SESSIONS_PER_USER

No

25

Open HTTP sessions one signed-in user may hold. Past it, that user's least recently used session idle for at least a minute is closed; if none qualifies, the new one gets a 429.


Available Domains

Domain

Description

Key Operations

tickets

Service tickets

CRUD, comments (email/public/private), cross-ticket comment feed, line items, timers, attachments, blueprints

customers

Customer records

CRUD, phone numbers, autocomplete

assets

Customer assets

CRUD, list by contact, patches, installed applications, policy folder assignment, properties (OS, RAM, HDD, etc.)

contacts

Customer contacts

CRUD

invoices

Invoices

CRUD, line items (manual + product catalog), print, email, publish draft / convert to draft

estimates

Estimates/quotes

CRUD, line items, print, email, convert to invoice

appointments

Calendar appointments

CRUD, appointment types, ticket linking

products

Inventory/products

CRUD, serials, SKUs (incl. delete), categories, images

payments

Payment records

Create, read, multi-invoice distribution

leads

Leads/opportunities

Create, read, update

contracts

Service contracts

CRUD

rmm

RMM alerts and scripts

Create, read, mute, resolve alerts; run a script on an asset (confirmation required)

scheduling

Recurring invoices

CRUD, schedule line items (incl. asset, contact, policy folder, vendor, and backup line types)

time

Timers and time logs

Start, pause, resume, stop live ticket timers; set billable; list and edit time logs

admin

Search, users, vendors, wiki, portal, settings, purchase orders, and more

Various, incl. PO update and file attachments

policies

Policy folders (Syncro accounts only)

List, get, create, update, delete folders; assign assets via assets_update


Staying Up to Date

The server checks for updates on startup and logs a warning if a newer version is available.

Method

How to Update

npx / Claude Desktop

Automatic — npx pulls latest on each run

Docker

docker compose pull && docker compose up -d

Docker (auto)

Add Watchtower for automatic daily updates

MCPB

Download latest .mcpb from Releases

From Source

git pull && npm install && npm run build

Auto-Update with Watchtower

Recommended: run Watchtower in label-enable mode so it only auto-updates the containers that opt in. This is much safer than letting it auto-update every container on the host.

Watchtower stack (/root/docker/watchtower/docker-compose.yml):

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_LABEL_ENABLE=true       # only watch opted-in containers
      - WATCHTOWER_POLL_INTERVAL=300       # check every 5 minutes
      - WATCHTOWER_CLEANUP=true
      # On hosts running modern Docker (29.x with MinAPI 1.40), Watchtower's
      # bundled SDK can default to an older API version. Pin to 1.40 to avoid
      # `client version 1.25 is too old` errors at runtime.
      - DOCKER_API_VERSION=1.40

Then opt the syncromsp-mcp service in via a label in its compose:

labels:
  - "com.centurylinklabs.watchtower.enable=true"

Bring both up: docker compose up -d in each directory. New releases on :latest will be pulled and the container recreated within 5 minutes of publish.


Important Notes

  • Destructive operations (DELETE, remove line item, etc.) require explicit confirmation

  • Line items cannot be added inline during resource creation — always add them via separate API calls after creating the parent resource

  • Ticket comments have 3 modes: email reply (do_not_email: false), public note (do_not_email: true, hidden: false), and private note (hidden: true)

  • Some resources have no DELETE endpoint (vendors, leads, products, assets) — use disabled: true via update instead

  • Rate limit: 180 requests per minute per IP (enforced by Syncro, managed by built-in rate limiter). In HTTP mode each Syncro tenant has its own 180/min budget, and all calls also share one server-wide budget (MCP_SERVER_RATE_LIMIT_PER_MINUTE, default 180) because every tenant calls Syncro from the same IP.


From Source

git clone https://github.com/advenimus/syncromsp-mcp.git
cd syncromsp-mcp
npm install
npm run build

export SYNCRO_API_KEY=your-api-key
export SYNCRO_SUBDOMAIN=your-subdomain
npm start

Development

npm run dev      # Run with tsx (hot reload)
npm run build    # Compile TypeScript
npm test         # Run tests
npm run lint     # Lint source

License

MIT

Available Tools

190 tools
admin_add_po_attachmentA

Attach a file (vendor quote, invoice, packing slip) to a purchase order. Give exactly one source: file_url (a direct download link; works everywhere), file_path (a file on this computer; local installs only), or file_base64 (only for tiny files you create yourself, like a short CSV, because the whole file must be written out). Allowed: JPEG, PNG, GIF, PDF, CSV, XLS, XLSX. Max 10 MB and 10 files per PO; the extension must match the real file type.

Inputs: id (required), name, file_url, filename, +2 more.

admin_add_po_line_itemA

Add a line item to a purchase order. Note: the product must have maintain_stock=true or the API returns 422.

Inputs: id (required), quantity (required), product_id (required).

admin_caller_idB

Look up caller information by phone number

Inputs: phone_number (required).

admin_create_canned_responseB

Create a canned response

Inputs: title (required), body, subject, canned_response_category_id.

admin_create_portal_invitationC

Send a portal invitation to a user

Inputs: id (required).

admin_create_portal_userA

Create a customer portal user. Always provide contact_id to associate a person/name -- without it the portal user shows as an anonymous email.

Inputs: email (required), password, contact_id, customer_id, +2 more.

admin_create_purchase_orderC

Create a purchase order

Inputs: vendor_id (required), user_id, due_date, paid_date, +9 more.

admin_create_user_deviceB

Register a user device for push notifications

Inputs: model, device_name, device_uuid, screen_size, +2 more.

admin_create_vendorC

Create a new vendor. Note: there is no delete endpoint for vendors.

Inputs: name (required), zip, city, email, +8 more.

admin_create_wikiC

Create a wiki page

Inputs: name (required), body, slug, asset_id, +2 more.

admin_create_worksheetB

Create a worksheet result for a ticket. Note: worksheet_template_id is required and must reference an existing admin-configured template.

Inputs: ticket_id (required), title, worksheet_template_id.

admin_delete_canned_responseA

DELETE a canned response. The user MUST confirm.

Inputs: id (required), confirmed (required).

admin_delete_portal_userB

DELETE a portal user. The user MUST confirm.

Inputs: id (required), confirmed (required).

admin_delete_wikiA

DELETE a wiki page. The user MUST confirm.

Inputs: id (required), confirmed (required).

admin_delete_worksheetB

DELETE a worksheet result. The user MUST confirm.

Inputs: id (required), confirmed (required), ticket_id (required).

admin_get_canned_settingsC

Get canned response settings

Inputs: no named parameters.

admin_get_meA

Get the currently authenticated user's profile

Inputs: no named parameters.

admin_get_printingB

Get printing settings

Inputs: no named parameters.

admin_get_purchase_orderB

Get a purchase order by ID

Inputs: id (required).

admin_get_settingsB

Get system settings

Inputs: no named parameters.

admin_get_tabsA

Get settings tabs configuration

Inputs: no named parameters.

admin_get_ticket_formC

Get a ticket form by ID

Inputs: id (required).

admin_get_userC

Get a user by ID

Inputs: id (required).

admin_get_user_deviceB

Get a user device by ID

Inputs: id (required).

admin_get_vendorB

Get a vendor by ID

Inputs: id (required).

admin_get_wikiB

Get a wiki page by ID

Inputs: id (required).

admin_get_worksheetC

Get a worksheet result by ID

Inputs: id (required), ticket_id (required).

admin_list_canned_responsesC

List canned ticket responses

Inputs: no named parameters.

admin_list_itemsB

List items (global item catalog)

Inputs: no named parameters.

admin_list_line_itemsA

List line items across invoices/tickets

Inputs: no named parameters.

admin_list_portal_usersC

List customer portal users

Inputs: no named parameters.

admin_list_purchase_ordersC

List purchase orders

Inputs: page.

admin_list_ticket_formsC

List new ticket forms

Inputs: no named parameters.

admin_list_usersB

List all users/technicians

Inputs: no named parameters.

admin_list_vendorsC

List vendors

Inputs: no named parameters.

admin_list_wikiC

List wiki pages

Inputs: page.

admin_list_worksheetsC

List worksheet results for a ticket

Inputs: ticket_id (required).

admin_otp_loginB

Login with a one-time password code

Inputs: code (required).

admin_process_ticket_formC

Submit/process a new ticket form

Inputs: id (required), appointments, ticket_details, customer_details.

admin_receive_purchase_orderC

Receive a line item on a purchase order

Inputs: id (required), line_item_id (required).

admin_update_canned_responseC

Update a canned response

Inputs: id (required), body, title, subject, +1 more.

admin_update_portal_userD

Update a portal user

Inputs: id (required), email, password, portal_group_id, +1 more.

admin_update_purchase_orderA

Update a purchase order. Only status, general_notes, shipping_cents, delivery_tracking, and vendor_id can be changed. Syncro rejects the 'Finished' status while any line item is still unreceived.

Inputs: id (required), status, vendor_id, general_notes, +2 more.

admin_update_user_deviceC

Update a user device

Inputs: id (required), registration_token_gcm.

admin_update_vendorC

Update a vendor

Inputs: id (required), zip, city, name, +9 more.

admin_update_wikiC

Update a wiki page

Inputs: id (required), body, name, slug, +3 more.

admin_update_worksheetD

Update a worksheet result

Inputs: id (required), ticket_id (required), title, public, +4 more.

appointments_createA

Create a new appointment. When possible, link to a ticket via ticket_id -- this auto-populates the description with ticket details and the location from the customer address.

Inputs: end_at, all_day, summary, user_id, +10 more.

appointments_create_typeB

Create a new appointment type. Note: location_type is REQUIRED (API returns 422 without it). Integer value where 0 = 'customer'.

Inputs: name (required), location_type, email_instructions, location_hard_code.

appointments_deleteA

DELETE an appointment. The user MUST confirm.

Inputs: id (required), confirmed (required).

appointments_delete_typeA

DELETE an appointment type. The user MUST confirm.

Inputs: id (required), confirmed (required).

appointments_getB

Get a single appointment by ID

Inputs: id (required).

appointments_get_typeB

Get an appointment type by ID

Inputs: id (required).

appointments_listC

List appointments with optional date range filter

Inputs: mine, page, date_to, date_from.

appointments_list_typesA

List appointment types

Inputs: no named parameters.

appointments_updateC

Update an existing appointment

Inputs: id (required), end_at, all_day, summary, +10 more.

appointments_update_typeC

Update an appointment type

Inputs: id (required), name, location_type, email_instructions, +1 more.

assets_chat_infoB

Get chat information for assets by IDs

Inputs: ids (required).

assets_createA

Create a new customer asset. Note: asset_type_name must match an existing type in the account. Custom properties on create are IGNORED -- set them via assets_update after creation.

Inputs: name (required), customer_id (required), properties, asset_serial, +2 more.

assets_getA

Get a single asset by ID with full details

Inputs: id (required).

assets_get_installed_applicationsA

Get the paginated list of software applications installed on an asset. Returns name, vendor, version, and installed_at (falls back to first-seen date when no explicit install date is recorded).

Inputs: id (required), page, per_page.

assets_get_patchesB

Get patch information for an asset (available OS/software patches)

Inputs: id (required).

assets_listB

List customer assets with optional filters. Returns paginated results.

Inputs: page, query, customer_id, snmp_enabled, +1 more.

assets_list_by_contactA

List the assets linked to one contact, sorted by name. 50 per page.

Inputs: contact_id (required), page.

assets_updateA

Update an existing asset. Use this to set asset properties (hdd, manufacturer, model, os, cpu_name, ram, last_boot) after creation, as properties on create are ignored. Pass policy_folder_id to move the asset to a same-customer policy folder (see policies_list_folders).

Inputs: id (required), name, properties, customer_id, +4 more.

contacts_createB

Create a new contact for a customer

Inputs: name (required), customer_id (required), zip, city, +7 more.

contacts_deleteA

DELETE a contact permanently. The user MUST confirm.

Inputs: id (required), confirmed (required).

contacts_getA

Get a single contact by ID

Inputs: id (required).

contacts_listC

List contacts with optional filters

Inputs: page, customer_id.

contacts_updateB

Update an existing contact

Inputs: id (required), zip, city, name, +9 more.

contracts_createA

Create a new contract. Note: there is no 'notes' field -- 'description' is the only text field. The activity/notes section in the UI is not API-accessible.

Inputs: name (required), customer_id (required), sla_id, status, +7 more.

contracts_deleteA

DELETE a contract (soft-delete -- GET returns null after deletion, not 404). The user MUST confirm.

Inputs: id (required), confirmed (required).

contracts_getA

Get a contract by ID

Inputs: id (required).

contracts_listD

List contracts

Inputs: page.

contracts_updateB

Update a contract. Note: there is no 'notes' field -- 'description' is the only text field. The activity/notes section in the UI is not API-accessible.

Inputs: id (required), name, sla_id, status, +8 more.

customers_autocompleteB

Autocomplete search for customers by name or business

Inputs: query (required).

customers_createA

Create a new customer. Note: phone and mobile fields auto-create entries in the phones sub-resource.

Inputs: zip, city, email, notes, +22 more.

customers_create_phoneB

Add a phone number to a customer

Inputs: number (required), customer_id (required), label, extension.

customers_deleteA

DELETE a customer permanently. The user MUST confirm before executing.

Inputs: id (required), confirmed (required).

customers_delete_phoneA

Delete a phone number from a customer. The user MUST confirm.

Inputs: id (required), confirmed (required), customer_id (required).

customers_getA

Get a single customer by ID with full details

Inputs: id (required).

customers_latestB

Get the most recently created customers

Inputs: no named parameters.

customers_listA

List customers with optional filters. Returns paginated results.

Inputs: id, page, sort, email, +5 more.

customers_list_phonesA

List phone numbers for a customer

Inputs: customer_id (required).

customers_updateB

Update an existing customer. Only provided fields will be changed.

Inputs: id (required), zip, city, email, +13 more.

customers_update_phoneC

Update a phone number for a customer

Inputs: id (required), customer_id (required), label, number, +1 more.

estimates_add_line_itemA

Add a line item to an estimate. When using product_id, only product_id + quantity are needed -- the API auto-fills name, cost, and price from the product catalog.

Inputs: id (required), cost, item, name, +8 more.

estimates_convert_to_invoiceB

Convert an estimate to an invoice

Inputs: id (required).

estimates_createA

Create a new estimate. Note: line_items array in the create body is IGNORED by the API -- add line items via estimates_add_line_item after creation.

Inputs: customer_id (required), date, name, note, +5 more.

estimates_deleteA

DELETE an estimate permanently. The user MUST confirm.

Inputs: id (required), confirmed (required).

estimates_delete_line_itemB

Delete a line item from an estimate. The user MUST confirm.

Inputs: id (required), confirmed (required), line_item_id (required).

estimates_emailC

Email an estimate to the customer

Inputs: id (required).

estimates_getB

Get a single estimate by ID

Inputs: id (required).

estimates_listB

List estimates with optional filters

Inputs: page, status, customer_id, created_after.

estimates_printB

Generate a printable PDF for an estimate

Inputs: id (required).

estimates_updateB

Update an existing estimate

Inputs: id (required), date, name, note, +5 more.

estimates_update_line_itemC

Update a line item on an estimate

Inputs: id (required), line_item_id (required), cost, item, +5 more.

invoices_add_line_itemA

Add a line item to an invoice. When using product_id, only product_id + quantity are needed -- the API auto-fills name, cost, and price from the product catalog.

Inputs: id (required), cost, item, name, +11 more.

invoices_convert_to_draftA

Turn a published invoice back into a draft so it can be edited before re-publishing. Needs draft invoices turned on for the account (404 otherwise). Syncro refuses (422) if a payment has been applied, the invoice was posted to an accounting provider, or it is a refund.

Inputs: id (required).

invoices_createA

Create a new invoice. Note: line_items in the create body are IGNORED by the API -- add line items via invoices_add_line_item after creation.

Inputs: customer_id (required), date, note, number, +7 more.

invoices_deleteA

DELETE an invoice permanently. The user MUST confirm.

Inputs: id (required), confirmed (required).

invoices_delete_line_itemB

Delete a line item from an invoice. The user MUST confirm.

Inputs: id (required), confirmed (required), line_item_id (required).

invoices_emailC

Email an invoice to the customer

Inputs: id (required).

invoices_getA

Get a single invoice by ID

Inputs: id (required).

invoices_get_ticketC

Get the ticket associated with an invoice

Inputs: id (required).

invoices_listB

List invoices with optional filters

Inputs: page, paid, unpaid, ticket_id, +1 more.

invoices_printB

Generate a printable PDF for an invoice

Inputs: id (required).

invoices_publishA

Publish a draft invoice so it becomes a normal invoice. Needs draft invoices turned on for the account (404 otherwise). Refunds cannot be drafts, so Syncro refuses to publish them.

Inputs: id (required).

invoices_updateB

Update an existing invoice

Inputs: id (required), date, note, number, +7 more.

invoices_update_line_itemB

Update a line item on an invoice

Inputs: id (required), line_item_id (required), cost, item, +5 more.

leads_createB

Create a new lead. Note: leads have no DELETE endpoint. To soft-delete, use leads_update with disabled=true.

Inputs: zip, city, email, phone, +22 more.

leads_getA

Get a lead by ID

Inputs: id (required).

leads_listC

List leads/opportunities

Inputs: page.

leads_updateC

Update an existing lead

Inputs: id (required), zip, city, email, +15 more.

payments_createC

Record a payment. Use apply_payments to distribute a payment across multiple invoices.

Inputs: customer_id (required), ref_num, invoice_id, address_zip, +8 more.

payments_create_profileB

Create a stored payment profile for a customer

Inputs: customer_id (required), last_four, expiration, payment_profile_id, +1 more.

payments_delete_profileA

Delete a payment profile. The user MUST confirm.

Inputs: id (required), confirmed (required), customer_id (required).

payments_getA

Get a single payment by ID

Inputs: id (required).

payments_get_profileC

Get a payment profile by ID

Inputs: id (required), customer_id (required).

payments_listC

List payments

Inputs: page.

payments_list_methodsA

List available payment methods

Inputs: no named parameters.

payments_list_profilesA

List stored payment profiles for a customer

Inputs: customer_id (required).

payments_update_profileC

Update a payment profile

Inputs: id (required), customer_id (required), last_four, expiration.

policies_create_folderA

Create a new policy folder under an existing parent folder. customer_id, name, and parent_id are all required -- folders cannot be created at the root level (the root folder is auto-created per customer). Use policies_list_folders first to find the root or desired parent folder ID.

Inputs: name (required), parent_id (required), customer_id (required).

policies_delete_folderA

DELETE a policy folder permanently. The folder must not have child folders (422 otherwise). This action cannot be undone -- the user MUST confirm before executing this.

Inputs: id (required), confirmed (required).

policies_get_folderA

Get a single policy folder by ID with full details.

Inputs: id (required).

policies_list_foldersA

List policy folders for a customer. customer_id is required. Returns paginated results with each folder's parent_id, partial_policy_id (policy attached at this folder), and effective_policy_id (inherited policy).

Inputs: customer_id (required), page, per_page.

policies_update_folderA

Update an existing policy folder. Use this to rename, re-parent, or attach/detach a partial policy. Set partial_policy_id to null to detach the policy at this folder; the effective_policy_id then inherits from the parent.

Inputs: id (required), name, parent_id, partial_policy_id.

products_add_imagesB

Add images to a product

Inputs: id (required), files (required).

products_attach_serial_to_line_itemC

Attach serial numbers to a line item

Inputs: product_id (required), line_item_id (required), product_serial_ids (required), record_type.

products_barcodeB

Look up a product by barcode/UPC

Inputs: barcode (required).

products_categoriesB

List product categories

Inputs: no named parameters.

products_createA

Create a new product. Note: products have no DELETE endpoint -- use products_update with disabled=true instead. Custom properties on create may be ignored -- set via products_update after creation.

Inputs: name (required), notes, taxable, disabled, +17 more.

products_create_serialB

Create a serial number for a product

Inputs: product_id (required), serial_number (required), condition, price_cost_cents, +1 more.

products_create_skuB

Create a SKU for a product

Inputs: value (required), product_id (required), vendor_id.

products_delete_imageC

Delete an image from a product. The user MUST confirm.

Inputs: id (required), confirmed (required).

products_delete_skuA

DELETE a product SKU. The user MUST confirm.

Inputs: id (required), confirmed (required), product_id (required).

products_getB

Get a product by ID

Inputs: id (required).

products_listC

List products/inventory items

Inputs: page.

products_list_serialsB

List serial numbers for a product

Inputs: product_id (required).

products_list_skusC

List SKUs for a product

Inputs: product_id (required).

products_updateC

Update a product

Inputs: id (required), name, notes, taxable, +13 more.

products_update_location_qtyC

Update product quantity at a location

Inputs: id (required), quantity (required), location_quantity_id (required).

products_update_serialC

Update a product serial

Inputs: id (required), product_id (required), notes, condition, +3 more.

products_update_skuC

Update a product SKU

Inputs: id (required), product_id (required), value, vendor_id.

rmm_create_alertA

Create an RMM alert. Note: formatted_output (not in swagger) populates the 'Details' field in the UI. description maps to the 'Type' field. Include properties with trigger and description keys to match real alert structure.

Inputs: status, asset_id, resolved, properties, +3 more.

rmm_delete_alertA

DELETE (soft-resolve) an RMM alert. Sets resolved=true -- the alert is still readable via GET after deletion. The user MUST confirm.

Inputs: id (required), confirmed (required).

rmm_get_alertA

Get an RMM alert by ID

Inputs: id (required).

rmm_list_alertsC

List RMM alerts

Inputs: page, status, created_after.

rmm_mute_alertB

Mute an RMM alert. Requires mute_for parameter.

Inputs: id (required), mute_for (required).

rmm_schedule_scriptA

Run an RMM script on one asset, now or once at a later time. Recurring schedules are not allowed through the API. No API lists scripts, so get script_id from the script's page in Syncro. This runs code on a customer machine: the user MUST confirm.

Inputs: asset_id (required), run_type (required), confirmed (required), script_id (required), +4 more.

scheduling_add_line_itemA

Add a line item to a recurring invoice schedule. Needs name or product_id. Pricing is in cents (retail_cents, cost_cents) or dollars (price_retail, price_cost). recurring_type_id picks the line type, e.g. 4 bills a customer's assets by type and 9 bills the assets in a policy folder; the type-specific fields only apply to their type.

Inputs: id (required), name, taxable, user_id, +28 more.

scheduling_createA

Create a recurring invoice schedule. Always create with paused=true to prevent auto-firing during setup. Note: invoice-side fields (invoice name, employee, template, billing terms, memo) are NOT settable via API.

Inputs: customer_id (required), name, paused, next_run, +5 more.

scheduling_deleteA

DELETE a schedule. The user MUST confirm.

Inputs: id (required), confirmed (required).

scheduling_getC

Get a schedule by ID

Inputs: id (required).

scheduling_listC

List recurring invoice schedules

Inputs: page.

scheduling_remove_line_itemB

Remove a line item from a schedule. The user MUST confirm.

Inputs: id (required), confirmed (required), line_item_id (required).

scheduling_updateC

Update a schedule

Inputs: id (required), name, paused, next_run, +5 more.

scheduling_update_line_itemB

Update a line item on a schedule. Takes the same fields as scheduling_add_line_item; only the fields you send change.

Inputs: id (required), line_item_id (required), name, taxable, +29 more.

tickets_add_line_itemA

Add a line item (product/service charge) to a ticket. Note: name and description are REQUIRED even when using product_id (unlike invoices/estimates which auto-fill from catalog).

Inputs: id (required), name, taxable, quantity, +5 more.

tickets_add_timerA

Add a timer entry to a ticket for time tracking. Note: start_at is REQUIRED (API returns 422 without it). duration_minutes auto-calculates end_time from start_at.

Inputs: id (required), notes, end_at, user_id, +3 more.

tickets_attach_fileA

Attach a file to a ticket via URL

Inputs: id (required), files (required).

tickets_blueprints_applyA

Create tickets from a Blueprint -- spawns the parent ticket and any child tickets defined by the blueprint, all assigned to the given customer. Returns { ticket_id, child_ticket_ids }.

Inputs: id (required), customer_id (required).

tickets_blueprints_listA

List Ticket Blueprints available for ticket creation (hidden ones are excluded). Returns id, name, role, and children_count for each blueprint.

Inputs: page, per_page.

tickets_charge_timerB

Charge a timer entry on a ticket (convert to billable line item)

Inputs: id (required), timer_entry_id (required).

tickets_commentA

Add a comment to a ticket. Three comment modes: (1) Email reply: hidden=false, do_not_email=false sends email to customer. (2) Public note: hidden=false, do_not_email=true is visible on portal/invoice but no email sent. (3) Private note: hidden=true, do_not_email=true is tech-only and invisible to customer. Note: subject is REQUIRED or the API returns 422.

Inputs: id (required), body (required), subject (required), tech, +3 more.

tickets_createA

Create a new ticket. Requires customer_id and subject at minimum. Note: line_items cannot be added inline during creation -- use tickets_add_line_item after the ticket is created.

Inputs: subject (required), customer_id (required), end_at, number, +14 more.

tickets_deleteA

DELETE a ticket permanently. This action cannot be undone. The user MUST confirm before executing this.

Inputs: id (required), confirmed (required).

tickets_delete_attachmentA

Delete an attachment from a ticket. The user MUST confirm.

Inputs: id (required), confirmed (required), attachment_id (required).

tickets_delete_timerA

Delete a timer entry from a ticket. The user MUST confirm.

Inputs: id (required), confirmed (required), timer_entry_id (required).

tickets_getB

Get a single ticket by ID with full details

Inputs: id (required).

tickets_get_commentsC

Get all comments on a ticket

Inputs: id (required).

tickets_listA

List tickets with optional filters. Returns paginated results.

Inputs: mine, page, query, number, +10 more.

tickets_list_commentsA

List ticket comments across multiple tickets as a flat paginated feed (distinct from tickets_get_comments which is scoped to one ticket). Sorted by ticket_id ASC, created_at DESC. Scope via ticket_search_id, ticket_id, customer_id, contact_id, user_id, status, mine, or date filters. comment_created_after/before filter at the comment level; the other date filters operate on the parent ticket.

Inputs: mine, page, status, user_id, +11 more.

tickets_printB

Generate a printable PDF for a ticket

Inputs: id (required).

tickets_remove_line_itemA

Remove a line item from a ticket. The user MUST confirm before executing this.

Inputs: id (required), confirmed (required), ticket_line_item_id (required).

tickets_settingsA

Get ticket system settings

Inputs: no named parameters.

tickets_updateB

Update an existing ticket. Only provided fields will be changed.

Inputs: id (required), end_at, number, sla_id, +14 more.

tickets_update_line_itemB

Update a line item on a ticket

Inputs: id (required), ticket_line_item_id (required), name, taxable, +6 more.

tickets_update_timerC

Update a timer entry on a ticket

Inputs: id (required), timer_entry_id (required), notes, user_id, +3 more.

time_create_timerA

Start a live timer on a ticket for the API token's technician (user_id cannot be set). If that technician already has a running or paused timer on the ticket, Syncro resumes and returns it instead of making a second one. Omit billable to use the account default. For logging past work, use tickets_add_timer instead.

Inputs: ticket_id (required), notes, billable, comment_id, +1 more.

time_get_last_timelogA

Get the most recent time log entry for the current user

Inputs: no named parameters.

time_list_timelogsC

List employee time logs (clock in/out records)

Inputs: page, user_id.

time_list_timersA

List ticket timers across tickets. Filter by ticket, technician, timer state, customer, or derived billing status. Use status 'running' or 'paused' to find a timer to pause or stop.

Inputs: page, status, user_id, ticket_id, +2 more.

time_pause_timerA

Pause a running timer. Syncro returns 422 if the timer is not running.

Inputs: id (required).

time_start_timerA

Start a timer that has never run, or resume a paused one. Syncro returns 422 if the timer is already running or stopped.

Inputs: id (required).

time_stop_timerA

Stop a running or paused timer. If the account has 'charge timers by default' turned on, this also adds the charge line item to the ticket; otherwise charge it afterwards with tickets_charge_timer (timer_entry_id = this timer's ID). Syncro returns 422 if the timer is already stopped.

Inputs: id (required).

time_update_timelogC

Update a time log entry (clock in/out, lunch, notes)

Inputs: in_at, lunch, out_at, in_note, +1 more.

time_update_timerA

Change whether a ticket timer is billable. To start, pause, or stop a timer use time_start_timer, time_pause_timer, or time_stop_timer; to change notes or duration use tickets_update_timer.

Inputs: id (required), billable (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.8/5.0

    Scored across 190 tools

    Disambiguation3/5

    Many tools have clearly distinct purposes (e.g., tickets_list vs customers_get), but significant overlaps exist between timer tools (tickets_add_timer vs time_create_timer, time_start_timer, time_stop_timer) and comment tools (tickets_get_comments vs tickets_list_comments), which could lead to misselection despite descriptive hints.

    Naming Consistency4/5

    Tools mostly follow a consistent domain_prefix_action pattern (e.g., tickets_list, customers_get, invoices_create), but minor deviations like the 'time_' prefix for ticket timers and non-verb names (products_barcode, customers_latest) slightly break the otherwise predictable convention.

    Tool Count1/5

    With 190 tools, the set is extremely bloated far beyond a well-scoped range. Even for a large platform like SyncroMSP, this volume creates significant noise and makes it difficult for an agent to discover and select the right tool.

    Completeness4/5

    The surface covers most major domains with CRUD and lifecycle operations, but notable gaps exist such as no assets_delete and no payments_update. Agents can often work around these (e.g., using disabled=true for soft-delete), so the gaps are minor.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      C
      quality
      C
      maintenance
      Provides AI assistants with direct access to Autotask PSA for MSP operations. Enables natural language interaction for ticket management, time logging, company lookups, project tracking, and billing review through 39 comprehensive tools.
      100
      91 npm
      Apache 2.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to directly query and manage Autotask PSA data through natural language, including ticket triage, time entry creation, company lookups, project management, billing review, and more.
      Apache 2.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to interact with AutoTask PSA data, supporting natural language ticket queries, updates, notes, and bulk export.
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to work directly with ConnectWise Manage through natural conversation, providing 53 tools for tickets, time entry, companies, contacts, projects, agreements, invoices, opportunities, and more.
      Apache 2.0