Metadata-Version: 2.4
Name: kizano-ldap-mcp-server
Version: 1.0.1.dev0
Summary: MCP endpoint for talking to a configurable LDAP service
Author-email: Markizano Draconus <markizano@markizano.net>
Project-URL: Homepage, https://github.com/markizano/ldap-mcp-server
Project-URL: Repository, https://github.com/markizano/ldap-mcp-server
Project-URL: Issues, https://github.com/markizano/ldap-mcp-server/issues
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: kizano>=1.0.8
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: ldap3>=2.9.1
Requires-Dist: mcp<2.0.0,>=1.27.0
Requires-Dist: starlette>=0.40.0
Requires-Dist: uvicorn>=0.30.0
Provides-Extra: test
Requires-Dist: pytest>=7.0.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
Requires-Dist: httpx>=0.27.0; extra == "test"

# LDAP MCP Server

A Python-based [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that exposes LDAP directory operations to AI agents and coding assistants like Claude, Cursor, and OpenCode.

This is a Python port of the original [trxo/ldap-mcp](https://github.com/trxo/ldap-mcp) implementation written in Go by [@trxo](https://github.com/trxo). Full credit to the original author for the design and concept.

## Features

- **Full LDAP CRUD operations** - Search, read, add, modify, and delete directory entries
- **Dual transport modes** - stdio (local) and SSE (networked)
- **LDIF and JSON output** - RFC 2849 compliant LDIF formatting or structured JSON
- **API key authentication** - Secure Bearer token or X-API-Key header validation (SSE mode)
- **Read-only or read-write** - Control write access via `--read-write` flag
- **TLS support** - LDAPS and StartTLS with certificate validation
- **MCP resources** - Expose Root DSE and individual entries as resources
- **Comprehensive tests** - 50+ unit tests with full coverage

## Installation

### From PyPI (when published)

```bash
pip install kizano-ldap-mcp-server
```

### From Source

```bash
git clone https://github.com/markizano/ldap-mcp-server.git
cd ldap-mcp-server
pip install -e .
```

### Using uv (recommended)

```bash
git clone https://github.com/markizano/ldap-mcp-server.git
cd ldap-mcp-server
uv pip install -e .
```

## Quick Start

### 1. Configuration

Create a `.env` file (copy from `.env.example`):

```bash
# LDAP connection
LDAP_URI=ldap://localhost:389
LDAP_BIND_DN=cn=admin,dc=example,dc=com
LDAP_BIND_PASSWORD=your_password_here

# MCP server (SSE mode)
MCP_HOST=0.0.0.0
MCP_PORT=9090
LDAP_MCP_SERVER_API_KEY=your-secure-api-key-here

# Logging
LOG_LEVEL=INFO
```

### 2. Start the Server

**stdio mode (local development, no API key needed):**

```bash
ldap-mcp-server --transport stdio --read-write
```

**SSE mode (network server with authentication):**

```bash
ldap-mcp-server --transport sse --host 0.0.0.0 --port 9090
```

### 3. Configure Your MCP Client

**For stdio mode (Claude Desktop, Cursor, OpenCode):**

Add to your MCP client configuration (e.g., `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`):

```json
{
  "mcpServers": {
    "ldap": {
      "command": "ldap-mcp-server",
      "args": ["--transport", "stdio", "--read-write"],
      "env": {
        "LDAP_URI": "ldap://localhost:389",
        "LDAP_BIND_DN": "cn=admin,dc=example,dc=com",
        "LDAP_BIND_PASSWORD": "your_password_here"
      }
    }
  }
}
```

**For SSE mode (remote server):**

```json
{
  "mcpServers": {
    "ldap": {
      "url": "http://your-server:9090/sse",
      "headers": {
        "Authorization": "Bearer YOUR_LDAP_MCP_SERVER_API_KEY"
      }
    }
  }
}
```

## Usage

### Available Tools

The server exposes the following MCP tools:

#### `search_entries`
Search for LDAP entries matching a filter.

```python
search_entries(
    base_dn="ou=users,dc=example,dc=com",
    filter="(objectClass=inetOrgPerson)",
    scope="sub",  # base, one, or sub
    attributes=["cn", "mail", "uid"],  # None for all attributes
    output_format="ldif"  # ldif or json
)
```

#### `get_entry`
Retrieve a single entry by DN.

```python
get_entry(
    dn="uid=jsmith,ou=users,dc=example,dc=com",
    attributes=["cn", "mail"],
    output_format="ldif"
)
```

#### `add_entry` (requires `--read-write`)
Create a new LDAP entry.

```python
add_entry(
    dn="uid=newuser,ou=users,dc=example,dc=com",
    attributes={
        "objectClass": ["inetOrgPerson", "organizationalPerson"],
        "cn": ["New User"],
        "sn": ["User"],
        "mail": ["newuser@example.com"]
    }
)
```

#### `modify_entry` (requires `--read-write`)
Modify an existing entry.

```python
modify_entry(
    dn="uid=jsmith,ou=users,dc=example,dc=com",
    changes=[
        {
            "operation": "replace",
            "attribute": "mail",
            "values": ["newemail@example.com"]
        }
    ]
)
```

#### `delete_entry` (requires `--read-write`)
Delete an entry.

```python
delete_entry(dn="uid=olduser,ou=users,dc=example,dc=com")
```

### Available Resources

#### `ldap://root-dse`
Server metadata and capabilities (JSON format).

#### `ldap://entry/{url-encoded-dn}`
Individual entry by DN (LDIF format).

Example: `ldap://entry/uid%3Djsmith%2Cou%3Dusers%2Cdc%3Dexample%2Cdc%3Dcom`

## Command-Line Options

```
usage: ldap-mcp-server [-h] [--transport {stdio,sse}] [--host HOST]
                       [--port PORT] [--url URL] [--bind-dn BIND_DN]
                       [--bind-password BIND_PASSWORD] [--starttls]
                       [--insecure] [--read-write] [--timeout TIMEOUT]
                       [--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]

options:
  --transport {stdio,sse}    Transport mode (default: sse)
  --host HOST                Host to bind to (default: 0.0.0.0)
  --port PORT                Port to listen on (default: 9090)
  --url URL                  LDAP server URL
  --bind-dn BIND_DN          Bind DN for service account
  --bind-password BIND_PASSWORD
                             Bind password
  --starttls                 Use StartTLS (cannot be used with ldaps://)
  --insecure                 Skip TLS certificate verification (INSECURE)
  --read-write               Enable write operations
  --timeout TIMEOUT          LDAP operation timeout in seconds (default: 30)
  --log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
                             Logging level (default: INFO)
```

CLI flags override environment variables.

## Development

### Running Tests

```bash
# Install test dependencies
pip install -e ".[test]"

# Run all tests
pytest tests/

# Run with coverage
pytest tests/ --cov=ldap_mcp_server --cov-report=html
```

### Project Structure

```
ldap-mcp.py/
├── lib/ldap_mcp_server/
│   ├── __init__.py         # Entry point with dotenv loading
│   ├── __main__.py         # python -m support
│   ├── cli.py              # Argument parsing
│   ├── config.py           # Configuration dataclass
│   ├── ldap_client.py      # LDAP connection wrapper
│   ├── ldif.py             # LDIF formatter (RFC 2849)
│   ├── middleware.py       # API key authentication
│   ├── resources.py        # MCP resource registrations
│   ├── server.py           # Main serve() function
│   └── tools.py            # MCP tool registrations
├── tests/
│   ├── test_config.py      # Configuration tests
│   ├── test_ldif.py        # LDIF formatting tests
│   └── test_middleware.py  # Authentication tests
├── pyproject.toml          # Package metadata
└── pytest.ini              # Test configuration
```

## Contributing

Contributions are welcome! Please follow these guidelines:

1. **Fork the repository** and create a feature branch
2. **Write tests** for new functionality (maintain >90% coverage)
3. **Follow the coding style:**
   - Use type hints for all function signatures
   - All imports at the top of the module (no JIT imports)
   - Follow DRY principles (no copy-paste code)
   - Docstrings for all public functions
4. **Run the test suite** before submitting:
   ```bash
   pytest tests/ -v
   ```
5. **Update documentation** if adding features
6. **Submit a pull request** with a clear description

### Development Setup

```bash
# Clone the repo
git clone https://github.com/markizano/ldap-mcp-server.git
cd ldap-mcp-server

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # or `.venv\Scripts\activate` on Windows

# Install in editable mode with test dependencies
pip install -e ".[test]"

# Run tests
pytest tests/ -v
```

## Security

- **API keys** are read from environment variables only, never from CLI or config files
- **TLS certificate validation** is enabled by default (use `--insecure` to disable for testing)
- **Write operations** are disabled by default (requires explicit `--read-write` flag)
- **Bind credentials** should use service accounts with minimal required privileges

**Warning:** Never commit `.env` files or hardcode credentials in code.

## License

MIT License - see LICENSE file for details

## Credits

- **Original Implementation:** [trxo/ldap-mcp](https://github.com/trxo/ldap-mcp) by [@trxo](https://github.com/trxo) - Go implementation
- **Python Port:** [@markizano](https://github.com/markizano)
- **MCP Protocol:** [Anthropic](https://modelcontextprotocol.io)

## Troubleshooting

### "Failed to connect to LDAP"
- Verify `LDAP_URI` is correct (`ldap://` or `ldaps://`)
- Check firewall rules allow connections to LDAP port (389 or 636)
- Test with `ldapsearch` to verify credentials

### "401 Unauthorized" (SSE mode)
- Ensure `LDAP_MCP_SERVER_API_KEY` is set in environment
- Verify client is sending `Authorization: Bearer <key>` or `X-API-Key: <key>` header
- Check server logs for auth attempts

### "405 Method Not Allowed"
- Client may be POSTing to `/sse` instead of `/messages`
- Client URL should be `http://host:port/sse` (not `/messages`)

### Write operations fail
- Ensure server started with `--read-write` flag
- Verify bind DN has write permissions in LDAP directory
- Check LDAP server logs for permission errors

## Support

- **Issues:** https://github.com/markizano/ldap-mcp-server/issues
- **Discussions:** https://github.com/markizano/ldap-mcp-server/discussions
- **Original Go version:** https://github.com/trxo/ldap-mcp

## Roadmap

- [ ] Connection pooling for high-traffic deployments
- [ ] Schema introspection and validation
- [ ] LDAP server discovery (DNS SRV records)
- [ ] Prometheus metrics endpoint
- [ ] Docker image and Kubernetes manifests
- [ ] Interactive schema browser MCP resource

---

**Made with ❤️ for the MCP community**
