Percepxion MCP Server

August 12, 2026 · View on GitHub

A Python FastMCP server that exposes the Percepxion REST API as MCP tools. Connect it to Claude Desktop, Claude Code, or any MCP-compatible client to manage out-of-band infrastructure through natural language.

What is Percepxion?

Percepxion is a SaaS platform for out-of-band (OOB) network device management. It connects to console servers, serial port aggregators, and remote access devices to provide fleet-wide visibility, configuration management, firmware updates, CLI access, and compliance reporting, independent of the primary network path.

This MCP server gives AI assistants direct access to Percepxion's management capabilities.


Use cases

  • Inventory discovery, find all devices in an organization, filter by model or firmware version
  • Remote CLI execution, run commands on a device and retrieve output through Percepxion
  • Config management, push individual property changes or clone a full config from a reference device
  • Firmware compliance, compare fleet firmware against a target and identify non-compliant devices
  • Firmware updates, upload firmware and target a Smart Group for coordinated rollout
  • Log retrieval, pull syslogs or access logs from devices on demand
  • Audit investigation, search platform audit records by user, time range, or action
  • Organization management, list organizations and scope operations to a specific one

How it works

The server runs locally and communicates with the Percepxion API over HTTPS. Authentication uses username/password; the server exchanges these for session tokens and holds them in memory for the lifetime of the process.

Many Percepxion operations are asynchronous. Tools that trigger device actions (CLI commands, config pushes, firmware updates, syslog requests) create a Percepxion job group and return the job record. Use search_job_groups or get_job_group to poll status. Neither of those returns CLI output text, once a CLI command job reaches "Completed", call get_cli_command_output for the actual device response.

Response envelope, all tools return this structure:

{ "ok": true, "data": { ... }, "status_code": 200 }
{ "ok": false, "error": "...", "status_code": 401, "details": { ... } }

Prerequisites

  • Python 3.11 or later (3.12 recommended)
  • Network access to your Percepxion API endpoint
  • A Percepxion username and password with appropriate permissions

Quick start

Linux or WSL

git clone https://github.com/Lantronix/percepxion-mcp-server.git
cd percepxion-mcp-server

python3 -m venv .venv
source .venv/bin/activate
pip install -e .

cp .env.example .env
# Edit .env, set PERCEPXION_USERNAME, PERCEPXION_PASSWORD
# Default API URL is https://api.percepxion.ai/api
# Lantronix employees: use https://api.gopercepxion.ai/api for the internal sandbox

Test the server starts:

python percepxion_mcp.py

The server blocks and waits for an MCP client connection. Connect a client, then call login_with_env to authenticate.

Docker

docker build -t percepxion-mcp-server .
docker run --rm -it --env-file .env percepxion-mcp-server

Environment variables

VariableRequiredDefaultDescription
PERCEPXION_USERNAMEYes,Percepxion login username
PERCEPXION_PASSWORDYes,Percepxion login password
PERCEPXION_API_URLNohttps://api.percepxion.ai/apiPercepxion API base URL. Use https://api.gopercepxion.ai/api for the Lantronix internal sandbox.
PERCEPXION_DEFAULT_ORGANIZATION_IDNo,Default organization ID used when callers omit organization_id. Useful for single-organization deployments.
PERCEPXION_DEFAULT_TENANT_IDNo,Deprecated alias for PERCEPXION_DEFAULT_ORGANIZATION_ID. Still works; if both are set, the new variable wins.
PERCEPXION_REQUEST_TIMEOUTNo45HTTP timeout in seconds. Raise for large log downloads or slow links.
PERCEPXION_FIRMWARE_DIRNo,If set, firmware uploads are restricted to files in this directory. Recommended for shared or automated deployments.
PERCEPXION_CREDENTIAL_PROVIDERNoenvCredential backend: env (default), vault, aws, or cyberark.

Keep .env out of version control. The repo includes .env.example as a starting point.

CLI command policy

send_direct_cli_command is read-only by default. Only show, get, ping, traceroute, and similar read commands are allowed. Configure write access and filtering in .env:

VariableDefaultDescription
PERCEPXION_CLI_WRITE_ENABLEDfalseSet to true to allow write commands (set, configure, etc.).
PERCEPXION_CLI_YOLOfalseSet to true to disable all command filtering. Use with extreme caution.
PERCEPXION_CLI_MAX_LENGTH512Maximum command length in characters.
PERCEPXION_CLI_DENY_COMMANDS,Comma-separated commands to block in addition to built-in defaults (reload, factory-reset, write erase, etc.).
PERCEPXION_CLI_PERMIT_COMMANDS,Comma-separated explicit allowlist. If set, only matching commands (and their subcommands) are permitted.

Credential providers

By default, credentials are read from environment variables. Three additional backends are available:

HashiCorp Vault:

PERCEPXION_CREDENTIAL_PROVIDER=vault
VAULT_ADDR=https://vault.example.com
VAULT_TOKEN=hvs.XXXX
VAULT_SECRET_PATH=secret/data/percepxion

AWS Secrets Manager:

PERCEPXION_CREDENTIAL_PROVIDER=aws
AWS_SECRET_NAME=percepxion/credentials
AWS_REGION=us-east-1

Install the AWS extra: pip install -e ".[aws]"

CyberArk Central Credential Provider (CCP):

Fetches Percepxion admin credentials from the CyberArk AIM Web Service at login time. No password stored in config files. Recommended for enterprises already running CyberArk.

PERCEPXION_CREDENTIAL_PROVIDER=cyberark
CYBERARK_URL=https://cyberark.internal
CYBERARK_APP_ID=PercepxionMCP
CYBERARK_SAFE=PercepxionSafe
CYBERARK_OBJECT=percepxion-admin-account

Optional mutual TLS (both vars required to enable):

CYBERARK_CERT_PATH=/path/to/client.pem
CYBERARK_KEY_PATH=/path/to/client.key

Set CYBERARK_VERIFY_SSL=false to skip server cert verification in lab environments. The AppID must be registered in CyberArk with access to the specified safe, and the host running this server must be an allowed machine for that AppID.

Full setup details in config/setup-instructions.md.


Connect an MCP client

claude mcp add percepxion -- /path/to/percepxion-mcp-server/.venv/bin/python /path/to/percepxion-mcp-server/percepxion_mcp.py

Or add manually to ~/.claude/settings.json:

{
  "mcpServers": {
    "percepxion": {
      "command": "/path/to/.venv/bin/python",
      "args": ["/path/to/percepxion-mcp-server/percepxion_mcp.py"],
      "env": {
        "PYTHONUNBUFFERED": "1",
        "PERCEPXION_USERNAME": "your-email@example.com",
        "PERCEPXION_PASSWORD": "your-password"
      }
    }
  }
}

Claude Desktop (Linux or macOS)

Copy config/claude_desktop_config.example.json, fill in your paths, and place it at:

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

Claude Desktop (Windows + WSL)

Use config/claude_desktop_config.wsl_windows.example.json. Replace the path placeholder with the WSL path to this repo.

First connection check

Once connected:

  1. Call login_with_env
  2. Call get_device_list with search_query: "*"

If get_device_list returns devices, the server is working.


Tool reference

Full reference in docs/tools.md. Summary below.

Call login_with_env before any other tool. The session persists for the lifetime of the process. On a 401 response, call login_with_env again.

Authentication and credentials

ToolDescription
login_with_envAuthenticate using the configured credential provider. Call once per session.
reconfigure_credentialsSwitch credential provider at runtime (env, vault, aws, cyberark) and clear the current session.

Organization management

ToolDescription
list_organizationsList organizations you have permission for. Use to discover organization_id values.
list_tenantsDeprecated alias for list_organizations, kept for backward compatibility.

Most tools accept an organization_id parameter to scope the call. The older tenant_id name is still accepted everywhere as a deprecated alias (see Environment variables).

organization_id/tenant_id accept either a UUID or an exact (case-insensitive) organization name. Name resolution only works for organizations with at least one visible device (it's derived from device search results, there's no dedicated organization-lookup endpoint), and is always scoped to your own login-derived permissions, a name match for an organization you aren't permitted for is rejected, not returned. If a name matches zero or more than one permitted organization, the tool call fails with a clear error instead of guessing; use the organization_id (UUID) directly to disambiguate.

Project Admin accounts: Percepxion requires an explicit organization_id on job/telemetry/content/Smart-Group/audit calls when the authenticated account is a Project Admin, that role's access spans every organization in the project, so Percepxion can't infer a single default the way it does for tenant_user/tenant_admin accounts (auto-scoped to their one organization, organization_id is optional for those). Omitting it as a Project Admin now raises a clear error instead of a generic API rejection; call list_organizations first to find the ID to pass. Device-inventory tools (get_device_list, get_device_details, list_device_ports) don't require it for any role.

Device inventory

ToolDescription
get_device_listSearch and paginate the device inventory.
get_device_detailsGet full device properties by device_id or serial_num.
get_devices_by_organizationList all devices in a specific organization.

Device lifecycle

ToolDescription
import_and_assign_devicesAssign devices to an organization.
unassign_devicesRemove one or more devices from an organization.
remove_device_from_platformRemove a single device (convenience wrapper).

Smart Groups

ToolDescription
create_smart_groupCreate a Smart Group using a filter query or device ID list. Used to target bulk operations.
list_smart_groupsList Smart Groups by name.
delete_smart_groupDelete a Smart Group by ID.

CLI commands

ToolDescriptionAsync?
send_direct_cli_commandSend a CLI command to one device. Read-only by default (see CLI policy above). Commands are audit-logged.Yes, use get_job_group
get_cli_command_outputRetrieve the actual CLI output text for a completed send_direct_cli_command job. Poll search_job_groups/get_job_group for status first; calling before the job completes returns total_results: 0, not an error.No, call after the job completes

Device configuration

ToolDescriptionAsync?
get_device_configRead current telemetry config before modifying it.No
update_device_configSave config properties and optionally apply them immediately.Yes if apply_now=True
clone_device_configCopy config from a source device to a target device via a template.Yes, use get_job_group
list_templatesList saved config templates.No
delete_templateDelete a config template by ID.No

Device operations

ToolDescriptionAsync?
reboot_deviceReboot a device via Percepxion.Yes, use get_job_group
list_device_portsList serial and device ports on a device. Returns port names, numbers, and connection state. Does not include managed-device identity, use get_security_telemetry or get_port_telemetry for hostname, model, serial, and OS version.No

Firmware management

ToolDescriptionAsync?
get_device_firmware_statusGet firmware version and state for one device.No
firmware_compliance_reportCompare fleet firmware against an expected version.No
list_firmware_contentList firmware packages already uploaded to Percepxion storage.No
update_firmware_by_smart_groupUpload firmware and apply to devices in one or more Smart Groups.Yes, use get_job_group

Logging

ToolDescriptionAsync?
request_device_syslog_uploadTrigger devices to upload syslogs to Percepxion storage.Yes, use get_job_group
get_device_syslogsQuery syslog files already uploaded.No
query_device_access_logPaginated query of device access log entries.No
download_device_access_logDownload complete access log for one device.No

Security and audit

ToolDescription
get_security_telemetryRetrieve full device and per-port telemetry. Source of truth for managed-device inventory: returns per-port dp_info records with hostname, model, serial, IP, OS version, uptime, and CPU/memory/flash usage for every attached device. Also includes console manager info, firmware state, network probes, and audit records.
get_port_telemetryRetrieve telemetry for a single port. Returns a structured managed-device object for that port only, faster and cheaper than get_security_telemetry when only one port is needed.
investigate_audit_logsSearch platform audit records by user, time range, or keyword.
investigate_user_audit_logsSearch user records with last recorded audit action per user.

Job tracking

ToolDescription
search_job_groupsSearch and poll async job status by name prefix.
get_job_groupGet full job output and results by job group ID.
get_job_results_by_devicePer-device result rollup for a multi-device job (e.g. a Smart Group firmware push or a CLI command sent to several devices at once). Use get_cli_command_output instead when you already know the single device you want output for.

Async job workflow

When a tool creates a job, it returns a job group record immediately:

1. Call the action tool (e.g. send_direct_cli_command)
   → Returns: { "ok": true, "data": { "id": "jg-abc123", "name": "CLI_dev001_1748000000" } }

2. Call get_job_group (or search_job_groups) with the id to poll status
   → Status reaches "Completed" or "Failed"

3. For CLI commands specifically, call get_cli_command_output with the same job_group_id and device_id
   → Returns: the actual output text the device sent back

Job names include a Unix timestamp suffix to avoid collisions when multiple jobs run against the same device.


Security

This server executes operations on network infrastructure. Treat it accordingly.

Credentials:

  • Keep .env out of version control (it's in .gitignore).
  • Set file permissions: chmod 600 .env
  • Use a dedicated Percepxion service account with minimum required permissions. Do not use an admin account for automated workflows.
  • For team environments, use the Vault or AWS Secrets Manager providers instead of plaintext .env files.

CLI command policy:

  • send_direct_cli_command is read-only by default. Only recognized read commands (show, get, ping, etc.) pass through.
  • A built-in deny list blocks destructive operations (reload, factory-reset, write erase, erase startup-config, etc.) even when write mode is enabled.
  • Set PERCEPXION_CLI_WRITE_ENABLED=true to allow write commands. All dispatched commands are logged to stderr with device ID and command string.
  • Set PERCEPXION_CLI_DENY_COMMANDS to add custom blocked commands. Set PERCEPXION_CLI_PERMIT_COMMANDS for an explicit allowlist.
  • PERCEPXION_CLI_YOLO=true disables all filtering. Use only in trusted, isolated environments.

Firmware uploads:

  • update_firmware_by_smart_group reads a local file path and uploads it to Percepxion.
  • Set PERCEPXION_FIRMWARE_DIR to restrict uploads to a specific directory. Without this, any file the server process can read can be uploaded.

Token handling:

  • Auth tokens are stored in memory only and are never written to disk.
  • On a 401 response, the session is cleared automatically. Call login_with_env again to restore.
  • There is no automatic token refresh. Long-running workflows should handle 401 responses and re-authenticate.

Network:

  • The server communicates with Percepxion over HTTPS only.
  • The default endpoint is api.percepxion.ai. The Lantronix internal sandbox is api.gopercepxion.ai. Verify PERCEPXION_API_URL before running in any automated context.

Organization-name resolution:

  • organization_id/tenant_id accept a name as a convenience, but resolution is hard-scoped to the authenticated session: candidate organizations come only from session.permitted_organization_ids, populated from your own /v2/user/login response (user.group[].tenant_id) at login time.
  • A device-derived name match for an organization outside that permitted set is rejected, not returned. This is a real permission boundary, not just a UX filter, it prevents organization-name lookup from being used to discover or probe organizations you aren't already RBAC-entitled to.
  • A UUID-shaped organization_id/tenant_id always skips name resolution entirely (no extra API call), identical to prior behavior.

Troubleshooting

SymptomLikely causeFix
"Not authenticated"login_with_env not called or token expiredCall login_with_env
401 on any toolToken expired mid-sessionCall login_with_env again
All calls fail or time outWrong PERCEPXION_API_URLCheck .env, production is api.percepxion.ai, sandbox is api.gopercepxion.ai
Slow log downloads time outDefault 45s timeout too shortSet PERCEPXION_REQUEST_TIMEOUT=120
Firmware upload rejectedFile outside PERCEPXION_FIRMWARE_DIRMove file to allowed directory or unset the variable
"Write commands are disabled"CLI policy in read-only modeSet PERCEPXION_CLI_WRITE_ENABLED=true in .env
"Command is in the deny list"Built-in deny list blocks the commandSet PERCEPXION_CLI_YOLO=true to bypass (use with caution)
Server exits immediatelyPython path or venv issueRun python percepxion_mcp.py directly to see the error
"No permitted organizations available" when resolving an organization nameLogin response had no user.group entries, or login_with_env wasn't calledCall login_with_env first; confirm the account has organization group membership in Percepxion
Organization name resolves to "0 matches"The org has no visible devices (name resolution is device-derived), or the name doesn't match any organization you're permitted forUse list_organizations to find the organization_id (UUID) directly and pass that instead
Organization name resolves to "ambiguous, multiple matches"Two or more permitted organizations share the same nameUse list_organizations to disambiguate and pass the organization_id (UUID) directly
"organization_id is required for this call when authenticated as project_admin"Project Admin account, organization_id not supplied on a job/telemetry/content/Smart-Group/audit callPass organization_id explicitly; use list_organizations to find it
get_cli_command_output returns total_results: 0Job hasn't reached "Completed" yet, or the job isn't a CLI command jobPoll get_job_group/search_job_groups until status is "Completed", then retry

Contributing

The project uses a feature branch workflow:

git checkout -b feat/your-feature
# make changes
git push -u origin feat/your-feature
# open a pull request to main

Run the test suite before submitting:

pip install -e ".[dev]"
pytest tests/ -v

See docs/adding-new-tools.md for conventions on adding new tools to the server.


Developer docs


Changelog

See CHANGELOG.md.

License

See LICENSE.