LDAP Assistant MCP
MCP server for 389 Directory Server health diagnostics, log analysis, and SOS-report investigation
Open source Open in the app JSON README (API)
About
MCP server for 389 Directory Server health diagnostics, log analysis, and SOS-report investigation
Details
- Kind
- MCP servers
- Topic
- Government & public data
- Publisher
- droideck
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 0.5.0
- Stars
- 1
- Forks
- 2
- Open pull requests
- 4
- Last push
- 2026-09-04T02:25:52Z
- Repository state
- ativo
- Language
- Python
- License
- GPL-3.0
- Added
- 2026-08-29 03:02:43
- Updated
- 2026-08-29 03:02:43
- Origin id
io.github.droideck/ldap-assistant-mcp
README
# LDAP Assistant MCP
<!-- mcp-name: io.github.droideck/ldap-assistant-mcp -->
[](CHANGELOG.md)
[](LICENSE)
> **Beta** — read-only 389 Directory Server diagnostics, suitable for evaluation and internal troubleshooting. Tool schemas, output formats, and configuration fields may still change before 1.0.0.
**LDAP Assistant MCP turns your AI assistant into a 389 Directory Server support engineer.** Point it at live servers, stopped instances, or an SOS report from a customer case, and ask:
- *"What's wrong with my directory servers?"*
- *"Summarize this SOS report before I open the case."*
- *"Replication looks broken between these servers — why?"*
It answers with prioritized findings (severity, impact, remediation) from 42 read-only diagnostic tools built on [lib389](https://pypi.org/project/lib389/), the official 389 DS administration library. **Privacy mode is on by default**: DNs, hostnames, and IPs are redacted before anything reaches the LLM.
**Supported provider: 389 Directory Server.** (OpenLDAP provider code exists experimentally behind an opt-in flag, with no privacy guarantees, and is not part of the support contract — see [Environment Variables](#environment-variable-reference).)
## Install
### Prerequisites
- Linux (primary) or macOS. Windows is not supported natively (python-ldap has no official Windows wheels) — use WSL2 (see the [install playbook](docs/playbooks/install-troubleshooting.md#windows-and-wsl2))
- Python 3.11+ (3.13 is what CI tests against)
- [`uv`](https://docs.astral.sh/uv/) package manager
- MCP client (Claude Desktop, Claude Code, Cursor, Gemini CLI, etc.)
- System development libraries (needed to build `python-ldap`):
**Fedora / RHEL / CentOS:**
```bash
sudo dnf install python3-devel openldap-devel cyrus-sasl-devel openssl-devel gcc
```
**Ubuntu / Debian:**
```bash
sudo apt install python3-dev libldap2-dev libsasl2-dev libssl-dev gcc
```
**macOS (Homebrew):**
```bash
brew install openldap
export LDFLAGS="-L$(brew --prefix openldap)/lib"
export CPPFLAGS="-I$(brew --prefix openldap)/include"
```
Anything failing during install? → [Installation troubleshooting playbook](docs/playbooks/install-troubleshooting.md)
### From PyPI (recommended)
No clone needed — your MCP client runs the published package via `uvx`. Skip ahead to [Configure your servers](#configure-your-servers), then use this client configuration (Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"ldap-assistant-mcp": {
"command": "uvx",
"args": ["ldap-assistant-mcp"],
"env": {
"LDAP_SERVERS_CONFIG": "/absolute/path/to/servers.json"
}
}
}
}
```
Use an **absolute path** for `LDAP_SERVERS_CONFIG` — relative paths resolve against the MCP client's working directory, which is usually not where you think.
### From source (development)
```bash
git clone https://github.com/droideck/ldap-assistant-mcp.git
cd ldap-assistant-mcp
uv venv && source .venv/bin/activate
uv pip install -e .[dev]
# Register with your MCP client via FastMCP:
fastmcp install claude-desktop fastmcp.json # or: fastmcp install claude-code fastmcp.json
```
Then set `LDAP_SERVERS_CONFIG` in the generated client entry as above. See the [Development Guide](docs/DEVELOPMENT.md) for test containers and architecture.
## Configure your servers
Create a `servers.json` with your LDAP server(s). **Note:** the `name` field is never redacted in privacy mode — it is passed as-is to AI agents so they can reference servers across tool calls. Do not put hostnames, IPs, or other private information in server names.
```json
{
"servers": [
{
"name": "local-ds",
"ldap_url": "ldap://localhost:389",
"base_dn": "dc=example,dc=com",
"bind_dn": "cn=Directory Manager",
"bind_password": "your-password",
"provider_type": "389ds",
"is_local": true,
"serverid": "localhost"
},
{
"name": "remote-ds",
"ldap_url": "ldap://ldap.example.com:389",
"base_dn": "dc=example,dc=com",
"bind_dn": "cn=Directory Manager",
"bind_password": "your-password",
"provider_type": "389ds"
},
{
"name": "stopped-ds",
"ldap_url": "ldap://localhost:389",
"base_dn": "dc=example,dc=com",
"bind_dn": "cn=Directory Manager",
"bind_password": "unused-in-offline-mode",
"provider_type": "389ds",
"is_local": true,
"serverid": "localhost",
"is_offline": true
},
{
"name": "sos-report",
"provider_type": "389ds",
"is_archive": true,
"archive_path": "/path/to/sosreport-host-2025/",
"instance_name": "slapd-instance"
}
]
}
```
**Note:** `serverid` is the instance name *without* the `slapd-` prefix (e.g. `localhost` for the instance `slapd-localhost`).
### The four server modes
| Mode | Config | What you get |
|------|--------|--------------|
| **Remote** | `ldap_url` only | Health, replication, performance, config, and entry tools over LDAP. No log/disk/cert access |
| **Local** | + `is_local: true`, `serverid` | Everything above **plus** log analysis, disk monitoring, certificate checks, process metrics |
| **Offline** | + `is_offline: true` | A stopped local instance, analyzed via dse.ldif and log files — no LDAP connection. For post-mortems and instances that won't start |
| **Archive** | `is_archive: true`, `archive_path` | An SOS report or config/log extract from **any** machine (tarball or directory, auto-detected layout). No LDAP, no local instance required |
Tools that need a live connection tell you so — the error names the tools that *do* work in that server's mode, so the investigation continues instead of dead-ending.
**Remote LDAPS certificate verification:** remote `ldaps://` servers verify the server certificate by default (`"tls_verify": true`, or the `LDAP_TLS_VERIFY` environment variable for env-based config). To connect to a server with a self-signed or otherwise untrusted certificate, set `"tls_verify": false` on that server entry — this disables certificate verification entirely, so use it only for trusted lab environments. Local instances (`is_local: true`) instead use the instance's own NSS certificate directory.
After editing the client config or `servers.json`, restart your MCP client, then verify: ask *"Which LDAP servers are configured?"*
## Privacy mode
By default, **privacy mode is enabled** — sensitive data (DNs, hostnames, IPs, user details) is redacted from tool outputs. Tools that expose individual entries (`get_user_details`, `ldap_search`) are disabled; list tools return counts only. Diagnostic metrics (counts, ratios, percentages) remain visible.
To enable full data access in **trusted environments only**:
```json
{
"env": {
"LDAP_MCP_EXPOSE_SENSITIVE_DATA": "true"
}
}
```
**Important:** Only enable this with local models, private cloud LLM instances, or when working with test/sample data. Avoid enabling with public LLMs when connected to production directories — your directory information could be included in their training data or logs.
When privacy mode is enabled (default):
- Hostnames, DNs, IP addresses, and suffixes are anonymized
- Configuration values are redacted
- Sensitive tools are disabled
- Diagnostic metrics (counts, ratios, percentages) remain visible
- Server names (the `name` field in `servers.json`) are **never** redacted — they are user-chosen labels that must remain stable across tool calls. Do not put hostnames, IPs, or other private information in server names.
### Data handling
- **No telemetry.** The server collects nothing and phones home to no one.
- **Everything runs locally.** Directory data is read from your LDAP servers, local instances, or archive files and returned only to your MCP client — which forwards tool results to whatever LLM you have configured. Privacy mode (on by default) redacts sensitive values before they leave the server process.
- **Credentials** stay in your local `servers.json` / environment variables; they are never included in tool output and tool errors are sanitized.
## First questions to ask
| You want | Ask / use | Behind it |
|----------|-----------|-----------|
| A health overview of everything | *"What's wrong with my directory servers?"* | `first_look` |
| An SOS report summarized | *"Analyze the sos-report archive"* or the `archive_investigation` prompt | `analyze_archive` → [archive playbook](docs/playbooks/archive-sos.md) |
| To know which tool fits a goal | the `tool_navigator` prompt | tool map for a stated goal |
| Guided replication triage | the `diagnose_replication` prompt | replication tool sequence |
| Guided performance triage | the `performance_investigation` prompt | performance tool sequence |
| A morning ops review | the `daily_health_check` prompt | health + monitoring sweep |
**Playbooks** (symptom → tools → what they can't know → how to verify by hand):
- [Summarize an SOS report before opening the case](docs/playbooks/archive-sos.md)
- [Installation troubleshooting](docs/playbooks/install-troubleshooting.md)
## Tools by group
42 read-only tools — full reference with parameters in [TOOLS.md](src/ldap_assistant_mcp/dirsrv_mcp/TOOLS.md).
| Group | Tools | Highlights |
|-------|-------|-----------|
| Health | `first_look`, `run_healthcheck`, `list_healthchecks`, `server_health` | Multi-server overview; full `dsctl healthcheck` equivalent |
| Replication | `get_replication_status`, `get_replication_topology`, `check_replication_lag`, `list_replication_conflicts`, `get_agreement_status` | RUV/CSN analysis, topology mapping, conflict entries |
| Performance | `get_performance_summary`, cache/connection/operation/thread/resource statistics | Bottleneck detection and tuning recommendations |
| Indexes | `list_indexes`, `analyze_index_configuration`, `find_unindexed_searches` | Access-log-driven unindexed search hunting |
| Configuration | `get_server_configuration`, `compare_server_configurations`, `list_plugins`, `get_backend_configuration` | Live **and** offline (dse.ldif) paths |
| Logs | `analyze_access_log` / `analyze_error_log` / `analyze_audit_log` (stats, privacy-safe), `parse_*_log` (full entries, requires sensitive-data mode) | Traditional and JSON log formats |
| Archive / SOS | `analyze_archive`, `validate_configuration`, `compare_dse_configs` | Inventory, offline config lint, full dse.ldif diff |
| Users & Groups | list/search/inspect users, active/locked filters, groups | Count-only in privacy mode |
| Advanced | `ldap_search`, `run_monitor`, `list_servers`, cn=config resources | Generic search (sensitive-data mode only) |
## Environment Variable Reference
| Variable | Default | Purpose |
|----------|---------|---------|
| `LDAP_SERVERS_CONFIG` | – | Absolute path to `servers.json` (multi-server config; preferred) |
| `LDAP_PROVIDER` | `dirsrv` | Server implementation. Only `dirsrv` (389 DS) is supported; `openldap` requires the opt-in flag below |
| `LDAP_MCP_EXPERIMENTAL_OPENLDAP` | `false` | Opt in to the experimental OpenLDAP provider (two tools, **no privacy guarantees**) |
| `LDAP_MCP_EXPOSE_SENSITIVE_DATA` | `false` | Disable privacy mode (see [Privacy Mode](#privacy-mode)) |
| `LDAP_MCP_DEBUG` | `false` | Enable debug logging and tracebacks in tool errors |
| `LDAP_MCP_TOOL_TIMEOUT` | `30` | Per-tool-call timeout in seconds |
| `LDAP_MCP_MAX_TOOL_TIMEOUT` | `120` | Timeout ceiling for heavy tools (`first_look`, archive comparison, …) |
| `LDAP_CONNECT_TIMEOUT` | `30` | LDAP network/operation timeout in seconds (prevents hangs on unreachable servers) |
Single-server fallback (used only when `LDAP_SERVERS_CONFIG` is not set):
| Variable | Default | Purpose |
|----------|---------|---------|
| `LDAP_URL` | – | Full LDAP URL (alternative to hostname/port/SSL vars) |
| `LDAP_HOSTNAME` / `LDAP_PORT` / `LDAP_USE_SSL` | `localhost` / `389` / `false` | Connection parameters |
| `LDAP_BASE_DN` | – | Default search base |
| `LDAP_BIND_DN` / `LDAP_BIND_PASSWORD` | `cn=Directory Manager` / – | Bind credentials (no default password) |
| `LDAP_AUTH_METHOD` | `simple` | `simple` or `anonymous` (the only implemented binds; LDAPI/SASL EXTERNAL is selected via `LDAP_USE_LDAPI`, not here) |
| `LDAP_IS_LOCAL` / `LDAP_SERVERID` | `false` / – | Enable local-instance features (logs, disk, certs) |
| `LDAP_USE_LDAPI` | `false` | Connect over the LDAPI unix socket |
| `LDAP_IS_OFFLINE` | `false` | Treat the local instance as stopped (offline mode) |
| `LDAP_TLS_VERIFY` | `true` | Verify the server certificate on remote `ldaps://` connections (set `false` only for trusted labs) |
## Troubleshooting
| Symptom | Cause / Fix |
|---------|-------------|
| `python-ldap` fails to build during install | Missing system headers — see the [install playbook](docs/playbooks/install-troubleshooting.md#python-ldap-build-failures) |
| Tools report a default `localhost` server instead of your config | `LDAP_SERVERS_CONFIG` not set or not loadable in the MCP client config — see [servers.json path resolution](docs/playbooks/install-troubleshooting.md#serversjson-path-resolution) |
| `list_*` tools return only counts; `ldap_search`/`get_user_details` refuse to run | That's privacy mode (default, working as intended) — see [Privacy Mode](#privacy-mode) |
| "requires a running server with a live LDAP connection" errors | The target is an offline/archive server — the error message lists the tools that do work there |
| LDAPI connection fails for a local server | Check `serverid` has no `slapd-` prefix and the instance socket exists |
No LDAP server to test against? The [Development Guide](docs/DEVELOPMENT.md) spins up test containers with Docker.
## Limitations
- **Beta** — Tool schemas, output formats, and configuration fields may change before 1.0.0
- **LLM interpretation** — Tools return accurate data, but the LLM interprets it. Hallucinations are possible. Always verify recommendations before acting.
- **Read-only** — No write operations yet
- **Plain text passwords** — Use restrictive file permissions on config files
- **STDIO transport only** — No HTTP/SSE support yet
## Documentation
| Document | Description |
|----------|-------------|
| [Changelog](CHANGELOG.md) | Version history and release notes |
| [Archive / SOS playbook](docs/playbooks/archive-sos.md) | Summarize an SOS report before opening the case |
| [Install playbook](docs/playbooks/install-troubleshooting.md) | python-ldap builds, WSL2, uvx, config path resolution |
| [Development Guide](docs/DEVELOPMENT.md) | Dev environment setup, configuration, architecture |
| [Testing Guide](docs/TESTING.md) | Running and writing tests |
| [Contributing Guide](docs/CONTRIBUTING.md) | How to contribute |
| [Release Checklist](docs/RELEASE.md) | How releases are cut and verified |
| [389 DS Tools](src/ldap_assistant_mcp/dirsrv_mcp/TOOLS.md) | 389 Directory Server tools reference |
| [OpenLDAP Tools](src/ldap_assistant_mcp/openldap_mcp/TOOLS.md) | OpenLDAP tools reference (experimental, opt-in only) |
## License
[GPL-3.0-or-later](LICENSE). Built on [lib389](https://pypi.org/project/lib389/) (the official 389 Directory Server administration library) and [FastMCP](https://gofastmcp.com).
## References
- [PyPI package](https://pypi.org/project/ldap-assistant-mcp/)
- [Official MCP Registry](https://registry.modelcontextprotocol.io) — listed as `io.github.droideck/ldap-assistant-mcp`
- [Model Context Protocol](https://modelcontextprotocol.io/introduction)
- [389 Directory Server](https://www.port389.org/docs/389ds/documentation.html)
- [FastMCP 2.0](https://gofastmcp.com)