Permissions
ScaiMCP defines one module-level permission for its own admin surface; every individual tool inherits the permission its underlying service requires. There is no MCP-specific permission for "may call any tool" — the per-tool checks do the work.
Module permission#
| Key | What it grants |
|---|---|
scaimcp:access |
Call the admin REST surface (GET /v1/modules/scaimcp/tools and friends). Does not grant any tool calls. |
By default this is held by super_admin, partner_admin, and tenant_admin via ScaiGrid's catch-all in CurrentUser.has_module_permission. The MCP transport itself (/mcp) is open to any authenticated identity — what they see is determined entirely by their other permissions.
Per-tool permission#
Each McpToolDef declares a required_permission. The string is checked against the caller's CurrentUser in two ways:
- If it resolves to a value of the core
Permissionenum (models:use,webhooks:manage,admin:access, ...),user.has_permission(...)is called. - Otherwise it is treated as a module-permission key and
user.has_module_permission(...)is called.
Tools whose required_permission is an empty string are open to any authenticated caller (e.g., me_get, modules_list).
See the API reference for each tool's permission.
Filter behaviour#
list_tools filters the catalog before returning it. A caller who lacks a tool's permission never sees the tool — they cannot guess the name and call it anyway, because call_tool re-runs the same check and returns PERMISSION_DENIED.
This is the whole reason MCP is safe: the catalog the LLM sees is exactly what it can invoke. The LLM can't hallucinate a privileged tool into existence.
Cloud MCP gating#
Tools aggregated from cloud MCP servers via ScaiLink are gated on scailink:remote.use. A caller without that permission sees only platform-native tools; with it, they additionally see their personal cloud registrations and any tenant-shared ones. Revoke scailink:remote.use from a group's custom role to remove the feature for those users while leaving platform tools intact.
Audit#
Every permission-gated MCP call goes through ScaiGrid's standard audit log under the underlying service's action name (inference.chat, webhooks.create, etc.). Filter by actor_user_id to see what a given identity has been doing through MCP:
1 2 | |
There is no separate "MCP" facet — by design, MCP is just another front door into the same audited services.
Granting selectively#
Most tenants run two patterns side by side:
- A service-account identity (API key) for the desktop / agent client, scoped to exactly what the agent should be allowed to do — usually
models:useplus any module permissions for the modules the agent needs. - The user's own JWT for interactive use, carrying their full role-based permissions.
Keep the service-account narrow — its key sits in a config file or env var and is the highest-impact thing to leak.