mcpAD - an MCP server for Microsoft Active Directory
2026-10-18
mcpAD
mcpAD is a FastMCP server that exposes the msad Active Directory / LDAP library as MCP tools.
It is built with Python 3.14 and uv, and reuses the typed, public msad API (search users/groups, memberships, account status). All the tools are read-only, which makes it a safe way to let an AI agent explore and audit an Active Directory domain without the risk of changing anything.
If you already know msad — the command line tool for working with AD/LDAP — mcpAD is its natural companion: the same library, now reachable from any MCP client (Claude Desktop, IDE agents, custom agents, …).
Tools
The server exposes a rich set of read-only tools, grouped by purpose.
Search and lookup
| Tool | Description |
|---|---|
find_users |
Find users by name/surname/mail/sam/department (criteria are ANDed, * wildcards) |
get_user |
Get a single user by sAMAccountName, UPN, mail or cn (exact match) |
find_groups |
Find groups by cn/name/sAMAccountName/displayName (* wildcards) |
get_group |
Get a single group by sAMAccountName or cn (exact match) |
find_computers |
Find computers by name (cn), dNSHostName or operatingSystem |
get_computer |
Get a single computer by sAMAccountName, cn or dNSHostName |
find_ous |
Find organizational units by name (* wildcards) |
get_ou |
Get a single OU by its ou name or full DN (exact match) |
get_ou_contents |
List objects under an OU (optionally filtered by object_class) |
get_by_dn |
Fetch any entry directly by its DN (resolve manager / managedBy) |
The find_* tools accept an optional base (a DN, e.g. an OU) to restrict the search to a subtree instead of the whole domain.
Memberships and account status
| Tool | Description |
|---|---|
group_members |
List members of a group (direct, or recursive with nested) |
user_groups |
List the groups a user belongs to (nested/recursive by default) |
is_member |
Check whether a user is a (nested) member of a group |
is_disabled |
Check whether an account is disabled |
is_locked |
Check whether an account is locked |
Audit
| Tool | Description |
|---|---|
find_inactive_users |
Users whose last logon is older than days (default 90), optionally including those who never logged on |
find_stale_computers |
Computers whose last logon is older than days (default 90) |
get_domain_info |
The domain object: security-relevant settings and metadata |
get_password_policy |
The default domain password policy (maxPwdAge, minPwdLength, …) |
get_password_policy_violations |
Enabled users whose password violates the domain policy |
get_privileged_groups |
Well-known privileged groups (Domain Admins, …) with member counts |
Diagnostics
| Tool | Description |
|---|---|
test_connection |
Test AD connectivity and bind (returns ok / target / auth / error) |
health |
Server health check (server name, msad version, connectivity) |
Authentication and connection
The server has no connection settings of its own: it reuses the msad configuration file, ~/.msad.toml by default (create it with msad init). Each tool call opens a short-lived connection through msad.connect().
A minimal ~/.msad.toml:
[defaults]
domain = "group"
[domains.group]
host = "dc.example.com"
base = "dc=group,dc=example,dc=com"
port = 636
use_ssl = true
# user = "svc_account" # optional
# password = "..." # optional
You can declare several [domains.*] blocks and pick one per call with the tool’s domain argument, or globally via MSAD_DOMAIN.
The authentication mode is chosen automatically from the domain block:
- Kerberos (SASL) when
user/passwordare not both set (the default) — obtain a ticket first withkinit. - User / password when both
userandpasswordare set — a simple LDAP bind. Restrict the file permissions (chmod 600 ~/.msad.toml) since the password is stored in clear text.
Use LDAPS (port = 636, use_ssl = true) whenever possible.
Running
Using uvx:
uvx --from git+https://github.com/matteoredaelli/mcpAD mcpAD
The transport is configurable (stdio, streamable-http, sse):
make start-stdio # stdio (for local MCP clients)
make start-http # streamable-http on 127.0.0.1:8000
make start-http HOST=0.0.0.0 PORT=9000 # bind elsewhere
Registering the server with an MCP client
Local (stdio) — the client launches the server
{
"mcpServers": {
"mcpAD": {
"command": "uv",
"args": ["run", "--directory", "/path/to/mcpAD", "mcpad"],
"env": { "MSAD_DOMAIN": "group" }
}
}
}
The spawned process inherits your environment, so a Kerberos ticket obtained with kinit in the same session is used automatically. For an unattended client, point it at a keytab via KRB5_CLIENT_KTNAME.
Remote (streamable-http) — the server runs as a service
Start the service on a host that has AD/Kerberos access:
make start-http HOST=0.0.0.0 PORT=9000
Then point the client at its URL:
{
"mcpServers": {
"mcpAD": {
"transport": "http",
"url": "http://mcp-host.example.com:9000/mcp"
}
}
}
The server itself performs no client authentication: if you expose it beyond localhost, put it behind a reverse proxy / network controls and treat the AD credentials on that host accordingly. Kerberos runs where the server runs — that host needs a valid ticket or keytab, not the client.
Why it is useful
With mcpAD an AI agent can answer questions like “which accounts have been inactive for more than 90 days?”, “who are the members of the Domain Admins group?” or “show me the password policy violations” — directly, in natural language, and safely, because every tool is read-only.
The code is available on GitHub.