
Denied Dev Hook
- 5 repo stars
- Updated August 4, 2026
- denied-dev/denied-sdk
Enforce authorization before each tool call using Denied.dev
About
denied-dev-hook is a Claude Code skill in the AI & Agent Building category. Enforce authorization before each tool call using Denied.dev
- denied-dev-hook
- AI & Agent Building
- AI-coding skill
Denied Dev Hook by the numbers
- Data as of Aug 5, 2026 (Skillselion catalog sync)
/plugin marketplace add denied-dev/denied-sdk/plugin install denied-dev-hook@denied-devAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| repo stars | ★ 5 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | denied-dev/denied-sdk ↗ |
What it does
Enforce authorization before each tool call using Denied.dev
README.md
Denied SDK Plugin for Claude Code
Claude Code can execute powerful tools — shell commands, file operations, web searches, MCP servers, and more. Denied defines the boundaries of what your Claude Code can and cannot do: before any tool executes, this plugin checks with the Denied authorization server whether the agent is permitted to run it. If the policy says no, the tool call is blocked and the reason is returned to the agent. You define the boundaries; the plugin enforces them.
Prerequisites
- Claude Code installed and working
- Node.js 18+ (already required by Claude Code)
- A Denied account and API key. Sign up at app.denied.dev
Quickstart
Step 1: Add the marketplace
claude plugin marketplace add denied-dev/denied-sdk
Step 2: Install the plugin
claude plugin install denied-dev-hook
Step 3: Set your API key
Set your API key as an environment variable:
export DENIED_API_KEY="your-api-key"
Alternatively, add the config directly in ~/.claude/settings.json or ./claude/settings.local.json:
{
"enabledPlugins": {
"denied-dev-hook@denied-dev": true
},
"env": {
"DENIED_API_KEY": "your-api-key"
}
}
Step 4: Restart Claude Code
Start a new Claude Code session. The plugin is now active — every tool call will be checked against your Denied policies.
Step 5: Verify it's working
When a tool call is blocked, Claude Code will display the denial reason inline. You can also check stderr output for lines like:
[denied-dev] Blocked tool call: Bash
[denied-dev] Blocked tool call: Write
Configuration reference
Settings resolve in this order: environment variable → config file → built-in default. Environment variables always win when present.
| Environment variable | Config file key (~/.denied/config.json) |
Default | Description |
|---|---|---|---|
DENIED_API_KEY |
apiKey |
— | Required. API key for the Denied PDP. |
DENIED_URL |
url |
https://api.denied.dev |
PDP endpoint. Only change for custom deployments. |
DENIED_FAIL_MODE |
failMode |
open |
open = allow on error, closed = deny when PDP is unreachable. |
DENIED_TIMEOUT_MS |
timeoutMs |
15000 |
Timeout in milliseconds. |
DENIED_CONFIG |
— | ~/.denied/config.json |
Path to the JSON config file. Set to read config from elsewhere. |
Example ~/.denied/config.json:
{
"apiKey": "your-api-key",
"url": "https://api.denied.dev",
"failMode": "open",
"timeoutMs": 15000,
"request": {
"includeToolInput": true,
"includeHookPayload": true,
"includeLastUserPrompt": true,
"maxContextBytes": 20000
},
"audit": {
"enabled": false,
"dir": "~/.denied/audit",
"includeRawPayload": true,
"includeMappedRequest": true,
"includeDecision": true
}
}
request.includeToolInput controls whether raw tool input is sent as bounded request context. request.includeLastUserPrompt (default true) controls whether the user's most recent prompt is read from the session transcript and added to the check context as last_user_prompt; it is extracted via a bounded tail read of transcript_path and is best-effort (a missing transcript, parse failure, or marker miss simply omits the field — it never delays or fails the authorization decision). The read has its own short independent deadline (~1s), so a stalled filesystem aborts the read rather than blocking the decision. request.maxContextBytes replaces oversized JSON values (e.g. tool_input, hook_payload) with a { "truncated": true, ... } preview; last_user_prompt is always kept as a string and, when over the cap, is truncated with an inline … [truncated N bytes] marker so the field type stays stable for policy matching. When audit.enabled is true, local JSONL debug records are written to ~/.denied/audit/denied-claude-code-hook.jsonl by default.
Security note: Audit logs may contain sensitive data. When
audit.enabledis true,audit.includeRawPayload,audit.includeMappedRequest,request.includeToolInput,request.includeHookPayload, andrequest.includeLastUserPromptdefault totrue, so requests and audit records can include full tool inputs, hook payloads, the user's last prompt, file contents, shell commands, URLs, and credentials. Store audit logs only in a location with appropriate access controls.
Default behavior
Default-deny: With no policies configured in Denied, every tool call is blocked. This is intentional — you must explicitly define the boundaries for your agent by creating policies in the Denied dashboard.
Fail-open on error: If the Denied server is unreachable (network issue, server down) or DENIED_API_KEY is not set, tool calls are allowed through. This prevents the plugin from completely breaking the agent. Set DENIED_FAIL_MODE=closed for stricter enforcement. You'll see log entries like:
[denied-dev] Failed to reach Denied PDP: fetch failed
Authorization enforcement
The plugin enforces Denied authorization checks on every tool call, regardless of Claude Code's permission mode.
| Permission mode | Claude Code permissions | Denied authorization |
|---|---|---|
default, plan, acceptEdits |
Normal prompts | Enforced |
dontAsk |
Auto-approved | Enforced |
bypassPermissions (--dangerously-skip-permissions) |
Skipped | Enforced |
--dangerously-skip-permissions bypasses Claude Code's built-in permission prompts, but it does not bypass Denied policy checks. This is intentional — Denied enforces organizational boundaries that are orthogonal to local permission delegation.
How it works
For each tool call, the plugin sends an authorization check to the Denied server:
- Subject:
claude-code://<sessionId>withcwdandpermission_modeas properties - Action:
execute - Resource:
tool://<toolName>withtool_use_idand, by default, boundedtool_inputas properties - Context: the integration name, the hook event, and — by default — the bounded hook payload and the user's most recent prompt (
last_user_prompt)
The Denied server evaluates the request against your policies and returns allow or deny. If denied, the block reason is fed back to the agent so it can adapt its behavior.
Creating policies
After installing the plugin, all tool calls are blocked by default. You need to create policies to define what your agent is allowed to do.
Every blocked tool call is logged as an authorization decision in the Denied dashboard. These decision logs capture the full context of each request — the session identity, the tool name, and the parameters (command, file path, etc.).
The Denied dashboard includes an AI policy generator that can read these decision logs and produce least-privilege policies for you. This means you can start with default-deny, let the agent run into the boundaries, then review the decision logs and use the AI policy generator to create precise allow rules.
Troubleshooting
| Symptom | Meaning | Fix |
|---|---|---|
Blocked tool call: <name> |
Policy denied the tool call | Working as intended. Create an allow policy in the Denied dashboard if the tool should be permitted. |
Failed to reach Denied PDP: ... |
Plugin can't reach the Denied server | Check DENIED_URL is correct and network connectivity. |
HTTP 401 or 403 |
Invalid or missing API key | Check DENIED_API_KEY env var. |
DENIED_API_KEY is not set |
No API key configured | Set the DENIED_API_KEY environment variable. |
No [denied-dev] lines, tools run freely |
Plugin not loaded | Verify the plugin is installed (claude plugin list) and restart Claude Code. |
Uninstalling
claude plugin uninstall denied-dev-hook
Restart Claude Code after uninstalling. This removes the plugin and its hooks — no manual cleanup needed.
Links
- Claude Code — Anthropic's agentic coding tool
- Denied — Define the boundaries of AI agents
- Denied Dashboard — Manage policies and API keys