Map tools to scopes
This is the slow step. Every other step is mostly mechanical; this one is design work that you do once and refine over time.
What the UI does
The Tools tab shows your inventory as a grid. For each tool you pick a scope from a dropdown, or mark it Public, or leave it Unmapped. The header counters at the top of the page — Mapped / Public / Unmapped — update as you go.
The activation preview at the top of the Launch tab pulls from the same data:
| Total | Mapped | Public | Unmapped |
|---|---|---|---|
| 101 | 5 | 1 | 96 |
In this example, 96 unmapped tools is blocking activation. You can't launch until every tool is either mapped or explicitly public.
The four states
| State | What it means at runtime |
|---|---|
| Mapped | Tool requires the configured scope on the token. SDK returns 403 insufficient_scope if missing. |
| Public | No auth check on this tool. Even unauthenticated requests pass through. The SDK still validates the token if one is present — but a missing token is OK. Use sparingly. |
| Unmapped | Tool exists in inventory but cannot be called by anyone. SDK returns method_not_found / 403. This is the safe default — explicit allow is required. |
| Admin override | Admin manually set a mapping that differs from the SDK's suggested mapping. Survives manifest republishes — the SDK's suggestion never overwrites an admin override. |
The Unmapped default is intentional: tools you haven't thought about yet are denied by default, not allowed. This matches OAuth best practice — explicit grant only.
How the data flows
The Tools tab calls PUT /authsec/resource-servers/:id/tool-scope-map when you change a mapping. The payload looks like:
{
"mappings": [
{"tool_name": "actions_get_workflow", "scope_id": "rs-xyz:tools:read"},
{"tool_name": "actions_create_pr", "scope_id": "rs-xyz:tools:write"},
{"tool_name": "health_check", "public": true}
]
}
Stored in mcp_tool_scope_maps with source = 'admin_override'. The Go SDK at runtime fetches GET /authsec/resource-servers/:id/sdk-policy, which compiles the live mappings into a flat tool→scope map.
Bulk mapping
The UI doesn't yet have a bulk-edit interface. For now, use the API directly with the admin JWT:
TOKEN="..." # admin JWT from the auth flow
RS="525da3b4-4206-4070-ad68-90cc3a6de43b"
# Map all tools starting with actions_get_ to the read scope
curl -X PUT "https://api.authsec.dev/authsec/resource-servers/$RS/tool-scope-map" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mappings": [
{"tool_name": "actions_get_workflow", "scope_id": "rs-xyz:tools:read"},
{"tool_name": "actions_get_run", "scope_id": "rs-xyz:tools:read"},
{"tool_name": "actions_get_artifacts", "scope_id": "rs-xyz:tools:read"}
]
}'
You can script the same call from your local terminal using a saved JWT — see the auth headers your browser's devtools shows when you load the Tools tab.
What re-scan does to mappings
When you re-scan and a tool's inputSchema changes, the mapping is preserved (same tool name = same mapping). When a tool is renamed (different name), the old mapping points at a tool that no longer exists; it shows up as a stale mapping in the Tools tab. The activation preview ignores stale mappings.
Removing a mapped tool entirely (delete from the server, re-scan) doesn't delete the mapping row — it's preserved as audit trail. The Tools tab marks the row as referencing a missing tool.
Public tools — when to use
Public should be rare. Cases where it makes sense:
- Health checks. A
healthorpingtool that returns server status with no business data. - Capability discovery. A tool that returns a list of supported features but no protected data.
- Public read APIs. If your MCP server is fronting a public-by-design dataset (e.g. open weather data), some tools genuinely don't need scopes.
Things that should never be public:
- Anything that modifies state.
- Anything that returns user-specific data.
- Anything that wraps an authenticated upstream API.
Related
- Define scopes — the previous step
- Access policy and default role — the next step
- Scope design for MCP tools — design tradeoffs
- Go SDK reference — how the SDK consumes the mapping