Tool authorization workflow with PKCE webhook security for FrontMCP.
- Tool Approval Checking: Automatic approval check before tool execution via hook
- Multiple Approval Scopes: SESSION, USER, TIME_LIMITED, TOOL_SPECIFIC, CONTEXT_SPECIFIC
- PKCE Webhook Security: RFC 7636 compliant PKCE for secure external approval systems
- Recheck Mode: Poll external API for approval status
- Full Audit Trail: Track who approved/revoked and when
- Context Extension: Use
this.approvalin tools
npm install @frontmcp/plugin-approvalimport { ApprovalPlugin } from '@frontmcp/plugin-approval';
// Create plugin with default settings
const plugin = ApprovalPlugin.init();
// Install into scope
await plugin.install(scope);const plugin = ApprovalPlugin.init({
mode: 'webhook',
webhook: {
url: 'https://approval.example.com/webhook',
challengeTtl: 300, // 5 minutes
callbackPath: '/approval/callback',
},
});import { Tool, ToolContext } from '@frontmcp/sdk';
@Tool({
name: 'dangerous_operation',
approval: {
required: true,
defaultScope: 'session',
category: 'write',
riskLevel: 'high',
approvalMessage: 'Allow dangerous operation?',
},
})
class DangerousTool extends ToolContext {
async execute() {
// This tool requires approval before execution
// The approval hook automatically checks and throws ApprovalRequiredError if needed
// You can also programmatically check/grant approvals:
const approved = await this.approval.isApproved('other-tool');
await this.approval.grantSessionApproval('helper-tool');
}
}| Scope | Description |
|---|---|
SESSION |
Valid for current session only |
USER |
Persists across sessions for the user |
TIME_LIMITED |
Expires after specified TTL |
TOOL_SPECIFIC |
Specific to a particular tool |
CONTEXT_SPECIFIC |
Specific to a context (repo, project, etc.) |
interface ApprovalPluginOptions {
// Storage configuration
storage?: StorageConfig;
storageInstance?: RootStorage | NamespacedStorage;
namespace?: string; // default: 'approval'
// Approval mode
mode?: 'recheck' | 'webhook'; // default: 'recheck'
// Recheck mode options
recheck?: {
url?: string;
auth?: 'jwt' | 'bearer' | 'none' | 'custom';
interval?: number;
maxAttempts?: number;
};
// Webhook mode options
webhook?: {
url?: string;
includeJwt?: boolean; // default: false (security)
challengeTtl?: number; // seconds, default: 300
callbackPath?: string; // default: '/approval/callback'
};
// Audit options
enableAudit?: boolean; // default: true
maxDelegationDepth?: number; // default: 3
cleanupIntervalSeconds?: number; // default: 60
}When using webhook mode, ApprovalPlugin uses PKCE (RFC 7636) for secure authorization:
- Generate PKCE pair:
code_verifier(secret) +code_challenge(hash) - Store challenge in Redis:
challenge:{code_challenge}→ session info - Send to webhook:
{code_challenge, toolId, callbackUrl}(NO session ID!) - External system calls back:
POST /approval/callback {code_verifier, approved} - Plugin validates:
SHA256(code_verifier) === stored code_challenge - Grant approval if valid
This ensures:
- Session ID is never exposed to external systems
- Challenge is single-use (deleted after callback)
- Challenge expires after TTL
- Only holder of
code_verifiercan complete approval
Apache-2.0