---
summary: ScaiMCP module permissions and how per-tool permissions inherit from the
  underlying REST endpoints.
title: Permissions
path: reference/permissions
status: published
---

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:

1. If it resolves to a value of the core `Permission` enum (`models:use`, `webhooks:manage`, `admin:access`, ...), `user.has_permission(...)` is called.
2. 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](./api) 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:

```bash
curl "$SCAIGRID_HOST/v1/audit/events?actor_user_id=$USER_ID&limit=50" \
  -H "Authorization: Bearer $SCAIGRID_API_KEY"
```

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:use` plus 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.
