Connecting the MCP Server
The Adcyma MCP server lets AI assistants query your tenant through the Model Context Protocol. Connect Claude, Cursor, VS Code, n8n, or any MCP client and ask questions in plain language — who has access to the finance share and how they got it, which accounts haven't signed in for six months, what's been sitting in the approval queue longest — and the assistant answers from your actual Adcyma data.
Adcyma hosts it. There is nothing to install or run yourself; you connect with a URL and one secret.
Every tool reads. None of them create, update, or delete anything. An assistant connected through the MCP server can investigate your tenant in depth, but provisioning, role changes, and offboarding still run through the People Hub and Workflows, where each change is approved, queued, and logged.
What it can see
The MCP server reads the same data the portal shows you — not just a directory dump. It answers across four areas:
- Directory and access — users from Entra ID or Active Directory, and for any user or resource, every membership with its source: granted by a role template, approved through an access request, matched by a dynamic group rule, or assigned directly.
- Hygiene — stale accounts, empty groups, inactive guests, and accounts with no manager.
- Governance — role templates and what they grant, pending approvals with their age and eligible approvers, and the tenant audit log.
- Automation — lifecycle workflows and their run history, including which action failed on which user and why.
Prerequisites
- The Public API module enabled for your tenant. If it isn't, contact support@adcyma.com.
- An API key with the read scope (created below)
- An MCP client. Claude Code, Cursor, VS Code, and n8n connect directly; Claude Desktop needs a small bridge, covered below.
Step 1: Create a read-only API key
- Open Administration → User Permissions
- Click Add User and choose the API role
- Enter a username for the key, for example
claude-mcp - Under Access level, choose Read only, for AI assistants (MCP)
- Add a Label — a short description like
Claude, IT department. The label appears in the audit log next to every call this key makes, which is how you tell two assistants apart later. Maximum 100 characters. - Click Create
The confirmation dialog shows two values, each with a copy button:
| Field | What it's for |
|---|---|
| API key | The raw key, for calling the REST API directly |
| MCP secret | The same key pre-formatted as companyId:username:apiKey — this is what you paste into your MCP client |
Copy the MCP secret before closing the dialog; it can't be retrieved afterwards. If you lose it, create a new API key and delete the old one. Treat it like a password — it grants read access to your whole tenant.
Keys appear in the User Permissions table with their label and a Read only badge, so you can see at a glance which keys can do what.
Create one key per assistant or team rather than sharing a single key. Revoking one then doesn't disturb the others, and the audit log tells you exactly which one ran a query.
Step 2: Connect your client
The server lives at https://mcp.adcyma.com and takes the MCP secret as a bearer token.
One command:
claude mcp add --transport http adcyma https://mcp.adcyma.com \
--header "Authorization: Bearer <your-mcp-secret>"Claude Desktop's custom connectors have no field for a custom header yet, so bridge through mcp-remote. Add this to claude_desktop_config.json:
{
"mcpServers": {
"adcyma": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.adcyma.com",
"--header",
"Authorization: Bearer <your-mcp-secret>"
]
}
}
}Restart Claude Desktop afterwards.
The server speaks MCP Streamable HTTP in stateless mode: every request is an independent JSON-RPC 2.0 POST, with no session handshake. The Accept header must list both application/json and text/event-stream.
curl -s https://mcp.adcyma.com \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer <your-mcp-secret>" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'Call a tool with method: "tools/call" and params: { "name": "<tool>", "arguments": { ... } }. Only POST is supported; GET and DELETE return 405.
Clients that support separate headers can send X-Company-ID, X-Api-User, and X-Api-Key individually instead of the combined bearer secret — the same three headers the REST API uses.
Pass it as a header, never as a query string or path segment. URLs end up in browser history, proxy logs, and server access logs.
Step 3: Try it
Ask your assistant something that needs real tenant data:
Which enabled accounts haven't signed in for 90 days, and what access would we be removing if we disabled them?
A good first check is whether the tool list loads at all — ask the assistant to list the Adcyma tools it can see. All eleven should appear.
Tool reference
You never call these by name; the assistant picks them from your question. The list is useful for knowing what's answerable.
Directory and access
| Tool | What it returns |
|---|---|
search_users | Paginated user search with filters for free text, department, status (active / disabled / guest), and last sign-in date |
get_user | One user by object ID or user principal name, with department, manager, and sign-in activity |
get_user_access | Everything one user has access to — each membership with its source, when it was granted, and what granted it |
get_resource_access | Everyone with access to one group or resource, with the same per-member attribution |
Hygiene and governance
| Tool | What it returns |
|---|---|
get_hygiene_report | Stale accounts, empty groups, inactive guests, and accounts with no manager, with a configurable staleness threshold |
get_audit_log | Tenant audit log — portal actions, workflow events, and API calls — filtered by actor, category, and time range |
list_role_templates | Role templates with the groups each one grants |
get_role_template | One role template with its full group list and license count |
list_pending_approvals | Open access requests with requester, service, age in days, and eligible approvers |
Automation
| Tool | What it returns |
|---|---|
list_workflows | Lifecycle workflows with trigger, active state, and action count |
get_workflow_runs | Run history for one workflow, one run per triggering event, with per-action status and error detail |
Access attribution
get_user_access and get_resource_access are the two tools with no real equivalent elsewhere in the product. Each membership comes back tagged with how it was granted:
| Source | Meaning |
|---|---|
role_template | Granted by a role template the user is assigned |
iga_request | Approved through an access request in the Access Center |
dynamic_group | Matched by a dynamic group membership rule |
direct | Assigned directly to the group, outside any Adcyma process |
A direct result on a sensitive resource is usually the interesting one — it means someone was added outside the governed paths.
grantedAt is only known for access granted through the Access Center, where Adcyma recorded the assignment. Direct and dynamic memberships come back with grantedAt: null, because the directory doesn't store when the membership was added.
Questions that work well
- "Show me everyone with access to the Finance SharePoint site and how each person got it."
- "Which accounts have been inactive for 6 months? Group them by department."
- "What's in the approval queue, and which requests have been waiting longest?"
- "Did the offboarding workflow fail for anyone last month? Show me the failed actions."
- "Compare what the Sales role template grants against what this sales hire actually has."
Limits and behavior
- Rate limit. 120 requests per minute per API key by default. A
429carries aRetry-Afterheader, and the assistant sees the wait time in the error message. Per-tenant increases are possible — ask support. Broader per-company and per-IP limits also apply. - Result size. Tool results are capped at roughly 50 KB. Longer lists are trimmed to the first N items and flagged with a note telling the assistant to narrow the query or paginate, so it never silently loses data.
- Pagination. List tools return a
nextCursor(null on the last page) which the assistant passes back ascursor.limitranges from 1 to 200 and defaults to 50. - Audit logging. Every call is written to your audit log with the key's username, its label, the endpoint, and sanitized parameters. Key material is never logged. You can review MCP activity in Logs like any other activity.
- Errors. Failures come back to the assistant as readable tool errors rather than protocol errors, so it can explain the problem or adjust. Transient upstream failures are retried once before reporting.
- No bypass. Tool calls are forwarded internally to the Adcyma read API, so MCP traffic passes through exactly the same authentication, scope checks, rate limiting, and audit logging as a direct API call. There's no shortcut path into the data.
Module requirements
Two tools depend on modules:
list_pending_approvalsrequires the Access Governance (IGA) module. Without it, the tool returns a clear error naming the module rather than an empty list.- The guest section of
get_hygiene_reportneeds Entra ID. Active Directory-only tenants get that section marked unavailable with a reason; the other three sections work normally.
Troubleshooting
"API key is missing the required 'read' scope" The key exists but wasn't created with read access. Scopes are fixed at creation and can't be added later — create a new key with Read only, for AI assistants (MCP) and delete the old one. Keys created before access levels existed show as Full access and are rejected here by design; new capabilities are opt-in.
"The Public API module is not enabled for this tenant" Contact support@adcyma.com to have public read API access enabled for your organization.
401 Unauthorized The Authorization header is missing, or the secret isn't in companyId:username:apiKey shape. Check you copied the MCP secret from the creation dialog and not the raw API key — they're different values. Your company ID is under Settings → Company profile.
406 Not Acceptable The client's Accept header doesn't list both application/json and text/event-stream. Most MCP clients handle this; a hand-rolled HTTP client may not.
"Rate limit exceeded. Retry after N seconds." The key passed 120 requests in a minute — usually an assistant paginating hard through a large tenant. Narrow the question, or ask support about a higher per-tenant limit.
Workflow run history looks incomplete Runs are linked to workflows by name, so history from before a workflow was renamed won't appear under the new name. Run depth is also bounded by how long task history is retained.