Skip to main content
Glama

xyte-mcp

An MCP server for the Xyte platform API. It gives an AI agent typed, validated access to the public Xyte REST API — endpoint discovery plus a generic call tool — over a local stdio transport.

Reads and writes are both available by default; XYTE_MCP_READ_ONLY=1 makes it a read-only server.

Xyte is a device management platform: connected device fleets, the spaces they live in, their telemetry and incidents, service tickets, commands, models and warranties. This server puts that API in front of an agent as three tools instead of 77 hand-written ones — it can discover the right endpoint, read its exact contract, and call it with arguments validated against that contract before a request goes out.

Useful for asking an agent to investigate a fleet ("which devices in the Tel Aviv office went offline this week, and what do their open tickets say"), to script routine operations, or to work against the Xyte API without you writing the client.

It runs locally as a subprocess of your MCP host and talks to hub.xyte.io directly — there is nothing to deploy and no third party in the path.

Install

# In an MCP host, e.g. Claude Code:
claude mcp add xyte -e XYTE_ORG_API_KEY=<your-key> -- npx -y @xyteai/mcp

Or configure a host directly:

{
  "command": "npx",
  "args": ["-y", "@xyteai/mcp"],
  "env": { "XYTE_ORG_API_KEY": "<your-key>" }
}

Requires Node.js 22 or newer.

MCP servers are loaded when a session starts, so restart your host — or open a new session — after adding it.

Related MCP server: Jamf Pro MCP Server

Getting an API key

Create one in the Xyte portal under Settings → API Keys; see Core API Keys for the walkthrough. An organization key acts as that organization, a partner key as the partner — set whichever scopes you need, or both.

The key is all the tenancy there is: it is bound to its tenant server-side, so there is no tenant or URL to configure.

Documentation

docs.xyte.io

Platform documentation — concepts, portal guides, how the pieces fit together.

API reference

The REST API this server wraps: authentication, pagination, every endpoint.

llms.txt

The whole documentation set as Markdown, plus the endpoints as OpenAPI — written for agents. Worth pointing your agent at alongside this server.

github.com/xyte-io/xyte-mcp

This server's source, issues and releases.

Everything below is about running the server itself.

Configuration

Variable

Purpose

XYTE_ORG_API_KEY

Organization-scoped API key.

XYTE_PARTNER_API_KEY

Partner-scoped API key. Set either or both.

XYTE_MCP_READ_ONLY

Set to 1 to refuse every mutating endpoint. Writes are permitted by default.

XYTE_HUB_URL

Override the hub base URL. Defaults to https://hub.xyte.io.

XYTE_ENTRY_URL

Override the entry base URL.

XYTE_MCP_TIMEOUT_MS

Per-request timeout. Defaults to 15000.

The last three are escape hatches for non-production hubs; the defaults are what you want.

XYTE_MCP_ALLOW_WRITES from 0.1.x is still honoured with its original meaning: if it is set at all, it decides, so a server pinned shut with XYTE_MCP_ALLOW_WRITES=0 stays shut across the upgrade. Where the two disagree, the restrictive one wins. New configuration should use XYTE_MCP_READ_ONLY.

Tools

Tool

What it does

xyte_endpoints_list

Discover endpoints. Filter by namespace, group, method, search, readOnly. Returns compact rows.

xyte_endpoint_describe

Full contract for one endpoint: path params, query params, body shape, required credential.

xyte_api_call

Invoke an endpoint by key with path, query and body.

The intended sequence is list → describe → call. Endpoint keys are stable identifiers such as organization.devices.getDevices; the server rejects unknown keys with suggestions rather than guessing.

Arguments are validated against the endpoint spec before any request is sent, so a wrong parameter name produces a precise message instead of an opaque HTTP 4xx.

Write policy

  • Default: GET/HEAD/POST/PUT/PATCH are all permitted.

  • DELETE: additionally requires confirm set to the endpoint key verbatim. This holds even with writes enabled — an irreversible call is the one that cannot be walked back.

  • XYTE_MCP_READ_ONLY=1: only GET/HEAD can be called. Anything else is refused before a request is built, and the model is told an operator alone can lift it.

xyte_api_call's MCP annotations (readOnlyHint, destructiveHint) follow the live configuration, so a host prompts according to what the server can actually do.

Worth knowing before you point it at a live fleet. The caller is a model, and read tools return content a third party can influence — device names, ticket bodies, notes. Nothing stops a model from treating text it just read as an instruction, so the server tells it outright that fleet content is untrusted and that mutating intent must come from you. That is a mitigation, not a guarantee. Prefer read-only when the agent runs unattended, when it is working through content you do not control, or when you simply want to look around:

claude mcp add xyte-ro -e XYTE_ORG_API_KEY=<key> -e XYTE_MCP_READ_ONLY=1 -- npx -y @xyteai/mcp

Both can coexist — register a read-only server for exploration and a writable one for the sessions where you want changes to land.

API keys are never echoed back: output is filtered both by field name (api_key, token, authorization, …) and by literal secret value.

The endpoint catalog

src/catalog/endpoints.generated.json holds 77 endpoints (63 organization, 14 partner). It is generated from hub's Bruno collection — hub/docs/api/Xyte Public/ — which is the upstream source the public API reference is built from, and committed so this repo has no dependency on a hub checkout at runtime.

Device API endpoints are deliberately excluded: they authenticate as a device with a device access token, not as an operator.

To refresh after an API change:

npm run catalog:generate -- --hub-path ../hub
git diff src/catalog/endpoints.generated.json    # review, then commit

CI cannot regenerate this (it has no hub checkout), so refreshing it is a maintainer step in the release checklist. --check verifies a committed file matches a fresh generation:

node scripts/generate-catalog.mjs --hub-path ../hub --check

Verifying a real setup

To check the server, your credential and production connectivity in one step — independently of any MCP host wiring:

npm run smoke:live -- --key-stdin     # paste the key; keeps it out of shell history
# or
XYTE_ORG_API_KEY=<key> npm run smoke:live

It spawns the built server and drives real MCP requests against the live API. The child is started with XYTE_MCP_READ_ONLY=1 regardless of your environment, only GET endpoints are exercised, and the key is never printed — this runs against production, so it cannot mutate anything. A pass means the whole path works; if this passes but your host still shows nothing, the problem is the host registration, not the server.

Development

npm install
npm run typecheck
npm test          # builds, then runs unit + protocol tests
npm run lint
npm run inspect   # build and open the MCP Inspector

Two invariants are enforced by lint rather than convention, because both erode silently:

  • Nothing but JSON-RPC may reach stdout. On stdio, stdout is the protocol channel; one stray console.log corrupts the stream and the host disconnects. no-console is an error in src/; use src/log.ts, which writes to stderr.

  • Tools take (args, ctx) and nothing else. No environment reads, no transport imports. Credential resolution and transport wiring live in src/transports/.

That second rule is what keeps the door open for a remote transport (see below).

Releasing

A release is a pushed semver tag. .github/workflows/publish.yml then packs the tarball, installs and runs it on Linux, macOS and Windows, re-checks the gates, and publishes to npm with provenance. The npm token lives on the repo's ci GitHub environment, so it is not reachable from a run on an arbitrary branch.

Checklist, from main with a clean tree:

# 1. Refresh the endpoint catalog. CI cannot do this — it has no hub checkout —
#    so a stale catalog is the one release defect nothing else will catch.
npm run catalog:generate -- --hub-path ../hub
git diff src/catalog/endpoints.generated.json    # review, commit if changed

# 2. Verify everything the publish job will verify, locally.
npm run typecheck && npm run lint && npm test
npm run smoke:pack-install                       # packs, installs, runs the binary

# 3. Bump the version and tag it. The publish job refuses to ship a tag that
#    disagrees with package.json.
npm version 0.1.0 --no-git-tag-version
git commit -am "Release v0.1.0" && git push origin main
git tag v0.1.0 && git push origin v0.1.0

node scripts/generate-catalog.mjs --hub-path ../hub --check exits non-zero when the committed catalog is stale, if you would rather assert than diff.

Then verify from a machine that has never seen the repo:

npx -y @xyteai/mcp --version
claude mcp add xyte --scope user -e XYTE_ORG_API_KEY=<key> -- npx -y @xyteai/mcp
claude mcp list | grep -i xyte      # expect: ✔ Connected

MCP servers load at session start, so confirm the tools in a new session.

Why stdio only, and what about OAuth

MCP's OAuth 2.1 authorization applies to HTTP transports; for stdio the specification directs servers to take credentials from the environment, which is what this server does. Supporting OAuth is therefore not an additive feature — it means running as a remote HTTP service, which additionally needs an authorization server (Xyte has none today; hub's oauth2 gem is a client for Zoho/Salesforce/Zoom), a hosted deployment, and a decision about whether hub accepts OAuth tokens directly or this server holds customer API keys.

That is a separate program, so v1 ships stdio. The tool layer is written to be transport-agnostic so it can be reused unchanged: adding src/transports/http.ts plus a resource-server layer would not touch any tool.

Security notes

  • Read tools return fleet data (device names, notes, ticket text) that a third party can influence, and that data enters the model's context. Treat it as untrusted input — see Write policy for what the server does about it and where XYTE_MCP_READ_ONLY is the right call.

  • The API key sets the blast radius, and it is the one control the model cannot talk its way around. Scope it to what the agent needs.

  • Prefer passing keys through your host's secret handling rather than committing them into a shared .mcp.json.

License

Apache-2.0

Install Server
A
license - permissive license
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/xyte-io/xyte-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server