When skills are exposed over HTTP (via skillsConfig.enabled), FrontMCP supports four authentication modes to protect skill endpoints.
Auth Modes
| Mode | Header | Validation | Use Case |
|---|
inherit (default) | Same as parent server | Same as parent server | Reuse the server’s primary auth |
public | None | None | Development, internal networks |
api-key | X-API-Key or Authorization: ApiKey <key> | Timing-safe equality | Machine-to-machine, server-to-server |
bearer | Authorization: Bearer <jwt> | JWT signature via JWKS | User-scoped access, IdP integration |
Configuration
Public Mode
Disable authentication on skill endpoints regardless of the parent server’s auth.
@FrontMcp({
skillsConfig: {
enabled: true,
auth: 'public',
},
})
class Server {}
API Key Mode
@FrontMcp({
skillsConfig: {
enabled: true,
auth: 'api-key',
apiKeys: [
process.env.SKILL_API_KEY_1,
process.env.SKILL_API_KEY_2,
],
},
})
class Server {}
Clients authenticate with either header format:
X-API-Key: sk-your-api-key
Authorization: ApiKey sk-your-api-key
API key comparison uses timing-safe equality to prevent timing attacks. All configured keys are checked even after a match is found to maintain constant-time behavior.
JWT Bearer Mode
@FrontMcp({
skillsConfig: {
enabled: true,
auth: 'bearer',
jwt: {
issuer: 'https://auth.example.com',
audience: 'skills-api', // Optional audience claim validation
},
},
})
class Server {}
JWT tokens are validated against the issuer’s JWKS endpoint (auto-discovered from {issuer}/.well-known/jwks.json).
Validation Result
The validator returns a structured result:
interface SkillHttpAuthResult {
authorized: boolean;
error?: string; // Human-readable error message
statusCode?: number; // HTTP status code (401, 403)
}
Factory Function
Use createSkillHttpAuthValidator() to create a validator from skill config:
import { createSkillHttpAuthValidator } from '@frontmcp/sdk';
const validator = createSkillHttpAuthValidator(skillsConfig, logger);
// Returns null when no validation is needed: `auth: 'public'`, `auth: 'inherit'`,
// or when `skillsConfig.auth` is unset.
if (validator) {
const result = await validator.validate({ headers: request.headers });
if (!result.authorized) {
return new Response(result.error, { status: result.statusCode });
}
}
When a skill session is active, the Tool Authorization Guard enforces which tools the skill is allowed to call. This prevents skill sessions from accessing tools outside their declared allowlist.
Policy Modes
| Mode | Behavior |
|---|
strict | Tool must be in the skill’s allowlist. Denied tools throw ToolNotAllowedError. |
approval | Unlisted tools trigger an approval flow. Throws ToolApprovalRequiredError until approved. |
permissive | Unlisted tools are allowed with a warning (logging only) — not silently unenforced. This is the default policy mode. |
Usage
import { ToolAuthorizationGuard } from '@frontmcp/sdk';
const guard = new ToolAuthorizationGuard(sessionManager, logger, {
throwOnDenied: true,
onApprovalRequired: async (toolName, skillId) => {
// Custom approval logic (e.g., prompt user)
return true; // or false to deny
},
});
// Check if tool is allowed
const result = await guard.check('my_tool');
// Simple boolean check (never throws)
const allowed = await guard.isAllowed('my_tool');
// Manual approval/denial
guard.approveTool('my_tool');
guard.denyTool('dangerous_tool');
// Inspect current state
const mode = guard.getPolicyMode(); // 'strict' | 'approval' | 'permissive'
const tools = guard.getAllowlist(); // ['tool_a', 'tool_b']
const hasSkill = guard.hasActiveSkill(); // true/false
Error Types
Thrown when a tool is not in the skill’s allowlist (strict mode).
| Property | Type | Description |
|---|
toolName | string | The denied tool name |
skillId | string | undefined | Active skill session ID |
reason | string | The denial reason carried by this error: not_in_allowlist, denied, rate_limited, or no_active_skill. (The underlying ToolAuthorizationResult['reason'] enum also includes the grant reasons skill_allowlist and dynamically_approved, which appear on a successful guard.check() result rather than on this error.) |
allowedTools | string[] | List of allowed tool names |
mcpErrorCode | number | MCP error code (INVALID_REQUEST) |
Thrown when a tool requires approval but has not been approved yet.
| Property | Type | Description |
|---|
toolName | string | The tool requiring approval |
skillId | string | undefined | Active skill session ID |
mcpErrorCode | number | MCP error code (INVALID_REQUEST) |