Skip to main content
Toolshed uses role-based access control to filter which tools a user can see and invoke. Each role defines a set of glob patterns that match tool paths.

Role schema

{
  id: string;       // Unique role ID
  name: string;     // Human-readable name
  patterns: string[]; // Array of glob patterns
}

Pattern syntax

Patterns match against dot-separated tool paths:
PatternMatchesDoes not match
github.issues.*github.issues.create, github.issues.listgithub.repos.search
github.**github.issues.create, github.repos.searchlinear.issues.list
gmail.messages.readgmail.messages.read onlygmail.messages.list
*Everything
  • * matches exactly one segment
  • ** matches zero or more segments at any depth

Example roles

Developer — full GitHub access, read-only Slack:
{
  "id": "developer",
  "name": "Developer",
  "patterns": ["github.**", "linear.issues.*", "slack.channels.list"]
}
Admin — access to everything:
{
  "id": "admin",
  "name": "Admin",
  "patterns": ["*"]
}
Read-only — only list and read operations:
{
  "id": "viewer",
  "name": "Viewer",
  "patterns": ["*.*.list", "*.*.read", "*.*.search", "*.*.get"]
}

How filtering works

The policy engine provides two functions from @toolshed/policy:

filterToolsByRole(tools, role)

Returns only the tools whose paths match at least one pattern in the role:
import { filterToolsByRole } from "@toolshed/policy";

const visibleTools = filterToolsByRole(allTools, userRole);
// Only tools matching userRole.patterns are included

matchPattern(toolPath, pattern)

Tests whether a single tool path matches a single pattern:
import { matchPattern } from "@toolshed/policy";

matchPattern("github.issues.create", "github.issues.*");  // true
matchPattern("github.issues.create", "github.**");         // true
matchPattern("github.issues.create", "linear.**");         // false

Annotation resolution

The policy package also resolves whether a tool requires approval:
import { resolveAnnotations } from "@toolshed/policy";

const annotations = resolveAnnotations(tool);
// { requiresApproval: true, reason: "Tool is marked as destructive" }
Resolution priority (first match wins):
  1. OpenAPI tools: metadata.httpMethod — GET, HEAD, OPTIONS are safe
  2. GraphQL tools: metadata.operationType — queries are safe, mutations require approval
  3. MCP tools: metadata.mcpAnnotations.destructiveHint
  4. Plugin tools: falls back to the explicit destructive flag