End-to-end test plan for the 15 MSP prompts exposed by ad_list_msp_prompts and ad_execute_msp_prompt.
- The MCP server is running (HTTP transport,
server_http.py). - A tenant
ad-config.jsonis configured and the server can reach LDAP. - You have a Bearer token if
automation.tokenis configured in your tenant config.
Throughout this document we use
https://ad-mcp.example.com:8813/activedirectory-mcpas the endpoint andYOUR_TOKENas the Bearer. Replace both with your real values.
curl -X POST https://ad-mcp.example.com:8813/activedirectory-mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": { "name": "ad_health_check_mcp_server", "arguments": {} },
"id": 1
}'Expected: status: ok, ldap_verified: true.
curl -X POST https://ad-mcp.example.com:8813/activedirectory-mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": { "name": "ad_list_msp_prompts", "arguments": {} },
"id": 1
}'Expected: total: 15, two categories (managers, analysts).
curl -X POST https://ad-mcp.example.com:8813/activedirectory-mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "ad_execute_msp_prompt",
"arguments": {
"name": "ad_security_audit",
"arguments": { "include_disabled": false }
}
},
"id": 1
}'Expected: instructions reference ad_get_inactive_users_by_days, ad_get_password_policy_violations, ad_get_privileged_security_groups, ad_audit_administrative_accounts.
... "name": "ad_user_growth_trends", "arguments": { "period_months": 6 } ...Expected: instructions reference ad_list_users_with_filters for current and historical counts.
... "name": "ad_group_policy_compliance", "arguments": {} ...Expected: instructions for GPO inventory and per-OU compliance check.
... "name": "ad_privileged_access_review", "arguments": {} ...Expected: instructions reference ad_get_privileged_security_groups and ad_get_group_members_recursive.
... "name": "ad_password_policy_health", "arguments": { "check_expiration": true } ...Expected: instructions reference ad_get_domain_security_policy_info + ad_get_password_policy_violations.
... "name": "ad_inactive_account_report", "arguments": { "inactive_days": 90 } ...Expected: instructions reference ad_get_inactive_users_by_days and ad_get_inactive_computers_by_days.
... "name": "ad_licensing_optimization", "arguments": {} ...Expected: instructions for user inventory cross-referenced with M365 licensing.
... "name": "ad_user_lookup", "arguments": { "search_term": "jdoe" } ...Expected: instructions to call ad_get_user_details_by_username and ad_get_user_group_memberships.
... "name": "ad_password_reset_guide", "arguments": { "username": "jdoe" } ...Expected: pre-reset safety checks, then ad_reset_user_password_forced with force_change=true.
... "name": "ad_user_onboarding", "arguments": { "username": "jdoe", "template_user": "template-finance" } ...Expected: checklist with ad_create_user_account + ad_add_member_to_group referencing the template.
... "name": "ad_user_offboarding", "arguments": { "username": "jdoe" } ...Expected: instructions for ad_disable_user_account_access, group removal via ad_remove_member_from_group, OU move.
... "name": "ad_group_membership_check", "arguments": { "username": "jdoe" } ...Expected: instructions to call ad_get_user_group_memberships.
... "name": "ad_account_unlock", "arguments": { "username": "jdoe" } ...Expected: instructions to call ad_get_user_details_by_username then ad_modify_user_attributes with lockoutTime=0.
... "name": "ad_permission_troubleshooting", "arguments": { "username": "jdoe" } ...Expected: instructions to call ad_get_user_effective_permissions + ad_get_user_group_memberships.
... "name": "ad_computer_join_guide", "arguments": { "computer_name": "WS01" } ...Expected: instructions to call ad_create_computer_account("WS01") first, with troubleshooting via ad_get_computer_details_by_name.
... "name": "ad_execute_msp_prompt", "arguments": { "name": "ad_nonexistent" } ...Expected: error response with the list of valid prompt names.
... "name": "ad_execute_msp_prompt", "arguments": { "name": "ad_user_lookup", "arguments": {} } ...Expected: error response naming the missing required argument.
curl -X POST https://ad-mcp.example.com:8813/activedirectory-mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "ad_create_user_account",
"arguments": {
"username": "test.user",
"password": "Temp@2026",
"first_name": "Test",
"last_name": "User"
}
},
"id": 1
}'Expected: permitted: false with a message asking either for client_confirmation matching the tenant slug, or automation_token.
Run the same ad_get_client_tenant_info request against each tenant endpoint and confirm the returned client.slug matches the tenant you expect.
# Tenant A
curl -X POST https://ad-mcp.example.com:8821/activedirectory-mcp \
-H 'Authorization: Bearer TOKEN_TENANT_A' \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"ad_get_client_tenant_info","arguments":{}},"id":1}'
# Tenant B
curl -X POST https://ad-mcp.example.com:8822/activedirectory-mcp \
-H 'Authorization: Bearer TOKEN_TENANT_B' \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"ad_get_client_tenant_info","arguments":{}},"id":1}'Each instance must return its own slug and domain — no leakage between tenants.
- Health check passes (
ldap_verified: true). -
ad_list_msp_promptsreturns 15 prompts in 2 categories. - All 7 manager prompts return well-formed instructions referencing
ad_*tools. - All 8 analyst prompts return well-formed instructions referencing
ad_*tools. - Unknown prompt name returns the prompt catalog in the error.
- Required-argument validation rejects empty calls.
- Write protection rejects mutating calls without confirmation/token.
- Multi-tenant smoke test returns the correct slug for each port.