Skip to main content
Glama
MSPbotsAI

duo-mcp

by MSPbotsAI

duo-mcp

MCP server for the Cisco Duo Admin API — Duo's multi-factor authentication and zero-trust access platform. Built to cover MFA synchronization, user pre-enrollment, and enrollment validation (the capabilities this task specifically requested), and — per an explicit follow-up scope decision — the full Admin API beyond that.

Overview

  • Stateless HTTP service. No credentials are ever persisted — each request supplies its own integration key/secret key/API host via headers, used only for the lifetime of that single request.

  • Supports concurrent requests; per-request credential isolation is done via Python contextvars, not a global/shared client instance.

  • Entry points: POST /mcp (MCP protocol) and GET /health (health check).

  • Default port: 8080 (configurable via MCP_HTTP_PORT).

Related MCP server: syncro-mcp

Scope

28 tools, trimmed down from an original 160-tool full-Admin-API build (2026-08-04). Duo has no existing MSPbots integration to anchor scope against — it was never previously configured in MSPbots' platform — so unlike other trimmed vendors in this program, "actual usage" was taken from the original ClickUp task's own stated requirement instead of a live API config: MFA synchronization, user pre-enrollment, and enrollment validation. Those 3 capabilities map to the users category almost entirely, plus the resource types a user's MFA methods reference:

  • MFA synchronization: duo_users_retrieve_users, duo_users_retrieve_user_by_id, duo_users_synchronize_user_from_directory, plus per-user MFA-method listing (retrieve_phones_by_user_id, retrieve_hardware_tokens_by_user_id, retrieve_web_authn_credentials_by_user_id, retrieve_groups_by_user_id)

  • User pre-enrollment: duo_users_create_user, duo_users_enroll_user, duo_users_associate_phone_with_user (+ disassociate), duo_users_associate_hardware_token_with_user, duo_users_associate_group_with_user, plus phone provisioning (duo_phones_create_phone, duo_phones_create_activation_code_via_sms)

  • Enrollment validation: duo_users_send_verification_push + duo_users_retrieve_verification_push_response

Plus core CRUD/list for the referenced resource types themselves — duo_users_modify_user, duo_users_delete_user, duo_users_create_bypass_codes_for_user, duo_phones_retrieve_phones, duo_phones_retrieve_phone_by_id, duo_tokens_retrieve_hardware_tokens, duo_tokens_retrieve_hardware_token_by_id, duo_webauthn_credentials_retrieve_web_authn_credentials (+ _by_key), duo_groups_retrieve_groups, duo_groups_get_group_info.

Everything else from the original 160-tool build — Desktop Authenticators, Bypass Codes (standalone category; the per-user variant under users was kept), Integrations, Policies, Endpoints, Registered Devices, Passport, Administrators, Administrative Units, Logs, Trust Monitor, Settings, Custom Branding, Account Info, Bulk Operations (15 categories, ~132 tools) — was removed as unrelated to the original task's 3 stated capabilities.

No official Duo MCP exists — searched GitHub (duo-mcp, "duosecurity" mcp) and both wyre-technology/MSPbotsAI orgs before building; the only existing "Duo + MCP" material is Duo SSO for MCP, which is the reverse relationship (Duo as an identity provider protecting MCP servers), not a Duo Admin API MCP server.

Source data for the kept tools was originally extracted from Duo's own official Postman collection (https://github.com/duosecurity/duo_postman_collection, duo-admin-api folder), cross-referenced against the live duo.com/docs/adminapi documentation. If a removed category is needed later, that same source can be re-parsed the same way.

Authentication

Duo Admin API auth is not a simple bearer token — every request is individually signed with HMAC-SHA512 (Duo's "sig_version 5") using an Integration Key (ikey) and Secret Key (skey). This server implements that signing logic itself (ported line-for-line from Duo's own reference Python client and verified byte-for-byte identical against that reference implementation for both GET-style query-signed requests and POST-style JSON-body-signed requests, including special-character percent-encoding) — callers only need to supply the raw ikey/skey/host per request; this server computes the canonical string, HMAC, and Authorization: Basic header on every call.

Per Duo's own signing convention: GET/DELETE requests sign and send params as a url-encoded query string; POST/PUT/PATCH requests sign and send params as a single JSON object request body. Tools in this server don't need to know which — the client picks the correct transport based on the HTTP method automatically, exactly matching the reference implementation's behavior.

HEADER 授权参数说明

Header

类型

是否必填

默认值

枚举值

字段描述

Example

X-Duo-Ikey

string

Duo Admin API 集成的 Integration Key(用作 HMAC 签名的 Basic Auth 用户名部分)

X-Duo-Ikey: DIABCDEFGHIJ1234567K

X-Duo-Skey

string

Duo Admin API 集成的 Secret Key(HMAC-SHA512 签名密钥,仅用于当次请求签名计算,不落盘)

X-Duo-Skey: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b

X-Duo-Api-Host

string

该 Duo 账号的 Admin API host(每个账号独立子域名,如 api-xxxxxxxx.duosecurity.com),无需带协议前缀

X-Duo-Api-Host: api-12345678.duosecurity.com

Missing any header returns 401:

{
  "error": "Missing credentials",
  "message": "This server requires the X-Duo-Ikey, X-Duo-Skey, and X-Duo-Api-Host headers",
  "required_headers": ["X-Duo-Ikey", "X-Duo-Skey", "X-Duo-Api-Host"],
  "optional_headers": []
}

Environment Variables

Variable

类型

是否必填

默认值

说明

MCP_HTTP_PORT

int

8080

HTTP 监听端口

MCP_HTTP_HOST

string

0.0.0.0

HTTP 监听地址

MCP Endpoint

  • POST /mcp — MCP protocol (streamable HTTP transport)

  • GET /health — health check, returns {"status": "ok"} (local-only probe, does not call Duo)

Tool List

Tool names are duo_<category>_<operation>, derived from each operation's name in Duo's official Postman collection. body parameters (for endpoints whose payload is a nested/complex JSON object rather than flat scalar fields, e.g. bulk operations, SSO integration config, policy sections) are accepted as a generic dict — the docstring lists the top-level keys from Duo's own example, and the full schema is in the linked API reference.

The 3 capabilities this task specifically named map to these tools:

  • MFA synchronization: duo_users_retrieve_users, duo_users_synchronize_user_from_directory

  • User pre-enrollment: duo_users_create_user, duo_users_enroll_user

  • Enrollment validation: duo_users_send_verification_push + duo_users_retrieve_verification_push_response

Category

Tool

Description

Method + Path

Params

groups

duo_groups_get_group_info

Get Group Info.

GET /admin/v2/groups/{group_id}

group_id(required)

groups

duo_groups_retrieve_groups

Retrieve Groups.

GET /admin/v1/groups

limit(optional), offset(optional)

phones

duo_phones_create_activation_code_via_sms

Create Activation Code via SMS.

POST /admin/v1/phones/{phone_id}/send_sms_activation

phone_id(required), valid_secs(optional), install(optional), installation_msg(optional), activation_msg(optional)

phones

duo_phones_create_phone

Create Phone.

POST /admin/v1/phones

none

phones

duo_phones_retrieve_phone_by_id

Retrieve Phone by ID.

GET /admin/v1/phones/{phone_id}

phone_id(required)

phones

duo_phones_retrieve_phones

Retrieve Phones.

GET /admin/v1/phones

number(optional), extension(optional), limit(optional), offset(optional)

tokens

duo_tokens_retrieve_hardware_token_by_id

Retrieve Hardware Token by ID.

GET /admin/v1/tokens/{token_id}

token_id(required)

tokens

duo_tokens_retrieve_hardware_tokens

Retrieve Hardware Tokens.

GET /admin/v1/tokens

type(optional), serial(optional), limit(optional), offset(optional)

users

duo_users_associate_group_with_user

Associate Group with User.

POST /admin/v1/users/{user_id}/groups

user_id(required), group_id(required)

users

duo_users_associate_hardware_token_with_user

Associate Hardware Token with User.

POST /admin/v1/users/{user_id}/tokens

user_id(required), token_id(required)

users

duo_users_associate_phone_with_user

Associate Phone with User.

POST /admin/v1/users/{user_id}/phones

user_id(required), phone_id(required)

users

duo_users_create_bypass_codes_for_user

Create Bypass Codes for User.

POST /admin/v1/users/{user_id}/bypass_codes

user_id(required), count(optional), codes(optional), reuse_count(optional), valid_secs(optional)

users

duo_users_create_user

Create User.

POST /admin/v1/users

username(required), aliases(optional), realname(optional), email(optional), status(optional), notes(optional), firstname(optional), lastname(optional)

users

duo_users_delete_user

Delete User.

DELETE /admin/v1/users/{user_id}

user_id(required)

users

duo_users_disassociate_phone_from_user

Disassociate Phone from User.

DELETE /admin/v1/users/{user_id}/phones/{phone_id}

user_id(required), phone_id(required)

users

duo_users_enroll_user

Enroll User.

POST /admin/v1/users/enroll

username(required), email(required), valid_secs(optional)

users

duo_users_modify_user

Modify User.

POST /admin/v1/users/{user_id}

user_id(required), username(optional), aliases(optional), realname(optional), email(optional), status(optional), notes(optional), firstname(optional), lastname(optional)

users

duo_users_retrieve_groups_by_user_id

Retrieve Groups by User ID.

GET /admin/v1/users/{user_id}/groups

user_id(required), limit(optional), offset(optional)

users

duo_users_retrieve_hardware_tokens_by_user_id

Retrieve Hardware Tokens by User ID.

GET /admin/v1/users/{user_id}/tokens

user_id(required), limit(optional), offset(optional)

users

duo_users_retrieve_phones_by_user_id

Retrieve Phones by User ID.

GET /admin/v1/users/{user_id}/phones

user_id(required), limit(optional), offset(optional)

users

duo_users_retrieve_user_by_id

Retrieve User by ID.

GET /admin/v1/users/{user_id}

user_id(required)

users

duo_users_retrieve_users

Retrieve Users.

GET /admin/v1/users

username(optional), limit(optional), offset(optional)

users

duo_users_retrieve_verification_push_response

Retrieve Verification Push Response.

GET /admin/v1/users/{user_id}/verification_push_response

user_id(required), push_id(required)

users

duo_users_retrieve_web_authn_credentials_by_user_id

Retrieve WebAuthn Credentials by User ID.

GET /admin/v1/users/{user_id}/webauthncredentials

user_id(required)

users

duo_users_send_verification_push

Send Verification Push.

POST /admin/v1/users/{user_id}/send_verification_push

user_id(required), phone_id(required)

users

duo_users_synchronize_user_from_directory

Synchronize User from Directory.

POST /admin/v1/users/directorysync/{directory_key}/syncuser

directory_key(required), username(required)

webauthn_credentials

duo_webauthn_credentials_retrieve_web_authn_credentials

Retrieve WebAuthn Credentials.

GET /admin/v1/webauthncredentials

limit(optional), offset(optional)

webauthn_credentials

duo_webauthn_credentials_retrieve_web_authn_credentials_by_key

Retrieve WebAuthn Credentials by Key.

GET /admin/v1/webauthncredentials/[webauthnkey]

webauthnkey(required)

测试示例

# Health check
curl -s http://localhost:8080/health

# Call a tool via the MCP protocol (streamable HTTP) — requires an
# initialize handshake first per the MCP spec; abbreviated example below
# shows the tool-call request body only:
curl -s -X POST http://localhost:8080/mcp \
  -H "X-Duo-Ikey: <your-integration-key>" \
  -H "X-Duo-Skey: <your-secret-key>" \
  -H "X-Duo-Api-Host: api-xxxxxxxx.duosecurity.com" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "mcp-session-id: <session-id-from-initialize>" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "duo_users_retrieve_users",
      "arguments": {"limit": "5"}
    }
  }'

Signing correctness verified independently of any live account: this server's HMAC-SHA512 request-signing implementation was directly compared, side-by-side in the same Python process, against Duo's own official reference client (duo_client_python) — the canonical string, the full Authorization: Basic header, and percent-encoding of special characters were all confirmed byte-for-byte identical for both a GET (query-signed) and a POST (JSON-body-signed) scenario. Structural checks were also completed: MCP handshake, tools/list returning all 160 registered tools, and 401 credential-gating with placeholder header values.

Live self-test against a real Duo account: passed. Using a real Admin API integration created in a live Duo account, three calls were made through the running MCP server end-to-end (real HTTP requests to api-4d02a5dd.duosecurity.com, real HMAC-SHA512 signing, real response parsing):

  • duo_administrators_retrieve_administrators → returned the real account owner (ryan.li@mspbots.ai, role Owner, an enrolled iPhone with push capability).

  • duo_integrations_retrieve_integrations → returned the Admin API integration itself, with integration_key matching the ikey used to authenticate — direct proof the signature was accepted by Duo.

  • duo_users_retrieve_users → returned []. Cross-checked against the two calls above (not assumed): since both other calls returned real, non-empty data through the identical auth/parsing path, the empty array reflects a genuinely fresh account with zero enrolled end users yet, not a masked auth failure.

API Reference

Known Gaps

  • Trimmed from 160 to 28 tools on 2026-08-04. The original build covered the full public Admin API per an earlier follow-up scope decision (justified at the time by there being no MSPbots-existing integration to anchor a narrower scope against). A later scope decision cut this back to the original ClickUp task's actual 3 stated capabilities (MFA sync / pre-enrollment / enrollment validation) plus minimal supporting CRUD — see the Scope section above for the exact mapping and the full list of the 15 removed categories (~132 tools). If a removed category is needed later, Duo's own Postman collection (linked below) still documents its exact operations and they can be re-added the same way the kept tools were generated.

  • Several kept tools still mutate real Duo account data (duo_users_create_user, duo_users_delete_user, duo_users_enroll_user, duo_phones_create_phone, etc.) — treat these as state-changing and confirm with a human before invoking.

  • body parameters are untyped (dict) where a tool has a nested/ complex JSON payload — each such tool's docstring lists the top-level keys from Duo's own example payload; the full schema is in the linked API reference.

  • Live functional self-test passed (pre-trim) against a real Duo account — administrators and integrations endpoints returned real data; the users endpoint correctly returned an empty list for this account's current (zero end-user) state. Not independently re-verified against the live API after the trim, though the trim only removed tools/files and changed no request-building logic for the ones kept.

F
license - not found
Not graded
quality - not tested
C
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.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • 34 production API tools over one hosted MCP endpoint.

  • XFA's remote MCP server — query device posture, compliance, policies & CVEs. Read-only.

  • MCP Server for agents to onboard, pay, and provision services autonomously with InFlow

View all MCP Connectors

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/MSPbotsAI/duo-mcp'

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