Active Directory MCP Server - Query and update Active Directory with flexible parameters
The Koppla server has a single tool (query_ad) with severe definition quality issues. While the tool is explicitly registered via FastMCP and has a description, the schema is critically underdeveloped. The input schema shows only two top-level parameters (query_type and params) with minimal type information and no per-parameter descriptions. The 'params' parameter is defined as a generic object with no nested schema documentation, making it impossible for LLMs to understand what sub-parameters are valid for each query_type variant. The tool description is long (600+ chars) and embeds operational guidance, but parameter descriptions are missing entirely from the schema definition. The tool performs write operations (update_user, add_to_group, remove_from_group) without proper error handling or confirmation workflows. Output schemas are completely undocumented. The code shows sophisticated business logic (protected account lists, service account pattern detection, encryption support) but none of this surfaces in the tool interface definition.
Query or update Active Directory with flexible parameters. Args: query_type: One of "search_ldap", "search_users", "update_user", "add_to_group", "remove_from_group" params: Dictionary of parameters specific to the query_type: - "search_ldap": {"search_base": str (optional), "search_filter": str, "attributes": list (optional)} - Perform a direct LDAP search with a custom filter (read-only operation) - "search_users": {"search_term": str, "department": str (optional), "exact": bool (optional)} - Search for users by name, username, or email with flexible matching - "update_user": {"username": str, "field": str, "value": str} - Update a user attribute (requires AD_WRITE_ENABLED=true) - IMPORTANT: "confirmed" must only be set to True after explicit human approval - "add_to_group": {"username": str (exact sAMAccountName or name to resolve), "group_name": str} - Add user to a group ((requires AD_WRITE_ENABLED=true). Resolves names to usernames if unique. - IMPORTANT: "confirmed" must only be set to True after explicit human approval - "remove_from_group": {"username": str, "group_name": str} - Remove a user from a group (requires AD_WRITE_ENABLED=true) - IMPORTANT: "confirmed" must only be set to True after explicit human approval - "inactive_users": {"days": int (default 30)
Input schema lacks parameter descriptions and nested schema definitions. The 'params' parameter is declared as generic 'object' with description 'Dictionary of parameters specific to the query_type' but no schema for nested parameters (search_base, search_filter, attributes, username, field, value, group_name, days, etc.). LLMs cannot determine valid sub-parameters for each query_type variant.
Output schema completely undocumented. Tool description mentions return types implicitly ('search_ldap': perform a search, 'update_user': update attribute) but the actual JSON response structure, field types, pagination behavior, and error response format are not defined.
Tool name 'query_ad' is ambiguous. It does not clearly convey that the tool can search, update, or modify Active Directory. Better names would be 'search_ad', 'manage_ad_user', or split into separate tools: 'search_ad_users', 'update_ad_user', 'manage_ad_groups'. Current naming violates verb_noun convention and conflates read and write operations.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-21 | F | 38 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 25 | - | v1 |
query_type parameter lacks enum constraint. Accepts 'search_ldap', 'search_users', 'update_user', 'add_to_group', 'remove_from_group', 'inactive_users' but these are not declared as an enum in the schema. Free-form string invites hallucinated or misspelled query types. Schema should declare: type='string', enum=['search_ldap', 'search_users', 'update_user', 'add_to_group', 'remove_from_group', 'inactive_users'].
Destructive write operations (update_user, add_to_group, remove_from_group) are not marked with destructiveHint annotation and lack confirmation workflow. The code mentions 'confirmed' field must only be set after explicit human approval, but this is buried in the description and not enforced via a dedicated confirmation tool or dry-run mode. Violates pattern:confirmation-request.
Tool description embeds example parameter structures (search_ldap: {'search_base': str (optional), ...}) instead of using formal schema definitions. This mixes documentation with definition and makes it impossible for clients to validate input programmatically. Schema should be the sole source of truth.
No error handling guidance. The tool invokes LDAP operations that can fail (connection errors, invalid filters, permission denied, account protection violations) but the schema provides no error_handling or recovery guidance. Errors are not categorized as retryable vs user-fixable. If an LDM query fails, the LLM has no hint whether to retry or ask the user.
Tool combines two distinct concerns: READING (search_ldap, search_users, inactive_users) and WRITING (update_user, add_to_group, remove_from_group). This violates the single-responsibility principle. The LLM cannot compose read operations separately from write operations or reason about safety independently. Should be split into separate tools: 'search_ad_users', 'update_ad_user', 'add_user_to_group', 'remove_user_from_group'.
Permission checks rely on hardcoded lists and environment variables (AD_WRITE_ENABLED, PROTECTED_ACCOUNTS) but the tool definition does not declare required permissions or scopes. No pattern:scope-declaration. An external auditor cannot see from the tool definition alone what permissions it requires or what it is allowed to do.
Tool accepts 'username' parameter for add_to_group and remove_from_group with note 'exact sAMAccountName or name to resolve', but the schema does not declare this ambiguity. Parameter descriptions are missing entirely. Should clarify: does the parameter accept usernames, display names, email addresses, or only exact sAMAccountNames? If name resolution is attempted, what happens if multiple users match?