Platform-specific types and helpers for Cursor hooks. Use these when you need features not available in the unified API.
See Cursor Hooks Documentation for official docs.
| Event | Input | Output | Description |
|---|---|---|---|
| stop | StopInput |
StopOutput |
Agent loop ended |
| beforeShellExecution | BeforeShellExecutionInput |
BeforeExecutionOutput |
Before shell command |
| afterShellExecution | AfterShellExecutionInput |
AfterShellExecutionOutput |
After shell command |
| beforeMCPExecution | BeforeMCPExecutionInput |
BeforeExecutionOutput |
Before MCP tool |
| afterMCPExecution | AfterMCPExecutionInput |
AfterMCPExecutionOutput |
After MCP tool |
| beforeReadFile | BeforeReadFileInput |
BeforeReadFileOutput |
Before agent reads file |
| afterFileEdit | AfterFileEditInput |
AfterFileEditOutput |
After agent edits file |
| beforeSubmitPrompt | BeforeSubmitPromptInput |
BeforeSubmitPromptOutput |
Before prompt submission |
| afterAgentResponse | AfterAgentResponseInput |
AfterAgentResponseOutput |
After agent message |
| afterAgentThought | AfterAgentThoughtInput |
AfterAgentThoughtOutput |
After thinking block |
| Event | Input | Output | Description |
|---|---|---|---|
| beforeTabFileRead | BeforeTabFileReadInput |
BeforeTabFileReadOutput |
Before Tab reads file |
| afterTabFileEdit | AfterTabFileEditInput |
AfterTabFileEditOutput |
After Tab edits file |
Called when the agent loop ends.
type StopInput struct {
ConversationID string `json:"conversation_id"`
Status string `json:"status"` // "completed", "aborted", "error"
LoopCount int `json:"loop_count"`
}type StopOutput struct {
FollowupMessage string `json:"followup_message,omitempty"`
}func Continue() StopOutput // Allow stopping
func Followup(message string) StopOutput // Auto follow-up (max 5)hookshot.Register("cursor-stop", func() {
hookshot.Run(func(input cursor.StopInput) cursor.StopOutput {
// Limit follow-ups to prevent loops (max 5 enforced by Cursor)
if input.LoopCount >= 3 {
return cursor.Continue()
}
if input.Status == "completed" && input.LoopCount == 0 {
return cursor.Followup("Please verify the changes")
}
return cursor.Continue()
})
})Called before a shell command is executed.
type BeforeShellExecutionInput struct {
ConversationID string `json:"conversation_id"`
Command string `json:"command"`
Cwd string `json:"cwd"`
}type BeforeExecutionOutput struct {
Decision string `json:"decision,omitempty"` // "deny", "ask"
UserMessage string `json:"user_message,omitempty"`
AgentMessage string `json:"agent_message,omitempty"`
}func Allow() BeforeExecutionOutput
func AllowWithMessage(msg string) BeforeExecutionOutput
func Deny(userMsg, agentMsg string) BeforeExecutionOutput
func DenyWithUserMessage(msg string) BeforeExecutionOutput
func DenyWithAgentMessage(msg string) BeforeExecutionOutput
func Ask(msg string) BeforeExecutionOutputhookshot.Register("cursor-before-shell", func() {
hookshot.Run(func(input cursor.BeforeShellExecutionInput) cursor.BeforeExecutionOutput {
if strings.Contains(input.Command, "rm -rf /") {
return cursor.Deny(
"Command blocked for safety",
"Dangerous command not allowed",
)
}
return cursor.Allow()
})
})Called after a shell command completes.
type AfterShellExecutionInput struct {
ConversationID string `json:"conversation_id"`
Command string `json:"command"`
Cwd string `json:"cwd"`
ExitCode int `json:"exit_code"`
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
Duration int `json:"duration"` // milliseconds
}type AfterShellExecutionOutput struct{}func AfterShellOK() AfterShellExecutionOutputCalled before an MCP tool is executed.
type BeforeMCPExecutionInput struct {
ConversationID string `json:"conversation_id"`
URL string `json:"url"`
ToolName string `json:"tool_name"`
ToolInput string `json:"tool_input"` // JSON string
}Uses BeforeExecutionOutput (same as shell).
hookshot.Register("cursor-before-mcp", func() {
hookshot.Run(func(input cursor.BeforeMCPExecutionInput) cursor.BeforeExecutionOutput {
if strings.Contains(input.URL, "blocked.com") {
return cursor.Deny(
"MCP server blocked",
"This MCP server is not allowed",
)
}
return cursor.Allow()
})
})Called after an MCP tool completes.
type AfterMCPExecutionInput struct {
ConversationID string `json:"conversation_id"`
URL string `json:"url"`
ToolName string `json:"tool_name"`
ToolInput string `json:"tool_input"`
ToolResult string `json:"tool_result"`
Duration int `json:"duration"` // milliseconds
}type AfterMCPExecutionOutput struct{}func AfterMCPOK() AfterMCPExecutionOutputCalled before the agent reads a file.
type BeforeReadFileInput struct {
ConversationID string `json:"conversation_id"`
FilePath string `json:"file_path"`
}type BeforeReadFileOutput struct {
Decision string `json:"decision,omitempty"` // "deny", "ask"
UserMessage string `json:"user_message,omitempty"`
AgentMessage string `json:"agent_message,omitempty"`
}func AllowRead() BeforeReadFileOutput
func DenyRead(userMsg, agentMsg string) BeforeReadFileOutput
func AskRead(msg string) BeforeReadFileOutputhookshot.Register("cursor-before-read-file", func() {
hookshot.Run(func(input cursor.BeforeReadFileInput) cursor.BeforeReadFileOutput {
if strings.Contains(input.FilePath, ".env") {
return cursor.DenyRead(
"Cannot read sensitive files",
"File access blocked",
)
}
return cursor.AllowRead()
})
})Called after the agent edits a file.
type AfterFileEditInput struct {
ConversationID string `json:"conversation_id"`
FilePath string `json:"file_path"`
Edits []Edit `json:"edits"`
}
type Edit struct {
OldString string `json:"old_string"`
NewString string `json:"new_string"`
}type AfterFileEditOutput struct{}func AfterFileEditOK() AfterFileEditOutputCalled before a prompt is submitted.
type BeforeSubmitPromptInput struct {
ConversationID string `json:"conversation_id"`
Prompt string `json:"prompt"`
}type BeforeSubmitPromptOutput struct {
Decision string `json:"decision,omitempty"` // "block"
UserMessage string `json:"user_message,omitempty"`
}func AllowPrompt() BeforeSubmitPromptOutput
func BlockPrompt(msg string) BeforeSubmitPromptOutputCalled before Tab reads a file for context.
type BeforeTabFileReadInput struct {
FilePath string `json:"file_path"`
}
type BeforeTabFileReadOutput struct {
Decision string `json:"decision,omitempty"` // "deny"
}func AllowTabRead() BeforeTabFileReadOutput
func DenyTabRead() BeforeTabFileReadOutputhookshot.Register("cursor-before-tab-read", func() {
hookshot.Run(func(input cursor.BeforeTabFileReadInput) cursor.BeforeTabFileReadOutput {
if strings.Contains(input.FilePath, ".env") {
return cursor.DenyTabRead()
}
return cursor.AllowTabRead()
})
})Called after Tab edits a file.
type AfterTabFileEditInput struct {
FilePath string `json:"file_path"`
Edits []Edit `json:"edits"`
}
type AfterTabFileEditOutput struct{}func AfterTabEditOK() AfterTabFileEditOutput