Skip to main content
1

Install the Package

Add ClaudeEssentials to your project:
dotnet add package ClaudeEssentials
For AOT publishing, ensure your project targets .NET 8+ and has PublishAot enabled.
2

Create a Hook Processor

Create a console app that reads JSON from stdin and writes the response to stdout:
Program.cs
using CloudNimble.ClaudeEssentials.Hooks;
using CloudNimble.ClaudeEssentials.Hooks.Enums;
using CloudNimble.ClaudeEssentials.Hooks.Outputs;

// Read hook input from stdin
var inputJson = Console.In.ReadToEnd();

// Deserialize the input
var input = ClaudeHooksSerializer.DeserializePreToolUseInput(inputJson);

// Create your response
var output = new PreToolUseHookOutput<object>
{
    Continue = true,
    HookSpecificOutput = new PreToolUseSpecificOutput<object>
    {
        PermissionDecision = PermissionDecision.Allow,
        PermissionDecisionReason = "Auto-approved by policy"
    }
};

// Write JSON to stdout
Console.WriteLine(ClaudeHooksSerializer.SerializePreToolUseOutput(output));
3

Configure Claude Code

Add your hook to Claude Code’s settings.json:
settings.json
{
  "hooks": {
    "PreToolUse": [{
      "command": "dotnet run --project /path/to/your/project"
    }]
  }
}
For production, publish your app as a native executable for faster startup:
dotnet publish -c Release
4

Test It Out

Start a Claude Code session. Your hook will now intercept tool calls and apply your custom logic.
You’re ready to customize Claude’s behavior with strongly-typed C# code!

Common Patterns

if (input.ToolName is "Read" or "Glob" or "Grep")
{
    return new PreToolUseHookOutput<object>
    {
        Continue = true,
        HookSpecificOutput = new PreToolUseSpecificOutput<object>
        {
            PermissionDecision = PermissionDecision.Allow
        }
    };
}
var command = input.ToolInput?.ToString() ?? "";
if (command.Contains("rm -rf"))
{
    return new PreToolUseHookOutput<object>
    {
        Continue = true,
        HookSpecificOutput = new PreToolUseSpecificOutput<object>
        {
            PermissionDecision = PermissionDecision.Deny,
            PermissionDecisionReason = "Dangerous command blocked"
        }
    };
}
return new PostToolUseHookOutput
{
    Continue = true,
    HookSpecificOutput = new PostToolUseSpecificOutput
    {
        AdditionalContext = "Remember to run tests after making changes."
    }
};

Next Steps