snapshot of c000b74a5b09c433f96a69fa102c5c8f6583dff0 Annotations about the code that implements koment.

internal/mcp/hooks.go

1 package mcp
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "strings"
8
9 sdk "github.com/modelcontextprotocol/go-sdk/mcp"
10
11 "github.com/koment-dev/koment/internal/agentpolicy"
12 )
13
14 const preToolDescription = "Run the koment pre-tool hook against an edit or write intent. " +
15 "Returns a decision: 'allow' when the proposed content does not add ordinary comment intent, " +
16 "'deny' when it does, with the reason naming the offending file and line. " +
17 "Use this from a client plugin to gate tool calls the same way `koment agents hook pre-tool` does."
18
19 type PreToolInput struct {
20 ToolName string `json:"tool_name" jsonschema:"the agent's tool name; recognised values are apply_patch and opencode_edit"`
21 FilePath string `json:"filePath,omitempty" jsonschema:"for opencode_edit, the path the tool would write"`
22 Content string `json:"content,omitempty" jsonschema:"for opencode_edit, the content the tool would write"`
23 Command string `json:"command,omitempty" jsonschema:"for apply_patch, the patch command body"`
24 }
25
26 type PreToolOutput struct {
27 Decision string `json:"decision"`
28 Reason string `json:"reason,omitempty"`
29 }
30
31 func preTool(_ context.Context, _ *sdk.CallToolRequest, input PreToolInput) (*sdk.CallToolResult, PreToolOutput, error) {
32 payload, err := json.Marshal(map[string]any{
33 "tool_name": input.ToolName,
34 "tool_input": map[string]any{
35 "command": input.Command,
36 "filePath": input.FilePath,
37 "content": input.Content,
38 },
39 })
40 if err != nil {
41 return nil, PreToolOutput{}, fmt.Errorf("encoding pre-tool input: %w", err)
42 }
43 raw, err := agentpolicy.PreToolOutput(payload)
44 if err != nil {
45 return nil, PreToolOutput{}, err
46 }
47 trimmed := strings.TrimSpace(string(raw))
48 if trimmed == "" || trimmed == "{}" {
49 return nil, PreToolOutput{Decision: "allow"}, nil
50 }
51 var decoded struct {
52 HookSpecificOutput struct {
53 PermissionDecision string `json:"permissionDecision"`
54 PermissionDecisionReason string `json:"permissionDecisionReason"`
55 } `json:"hookSpecificOutput"`
56 }
57 if err := json.Unmarshal(raw, &decoded); err != nil {
58 return nil, PreToolOutput{}, fmt.Errorf("decoding pre-tool output: %w", err)
59 }
60 decision := "allow"
61 reason := decoded.HookSpecificOutput.PermissionDecisionReason
62 if strings.EqualFold(decoded.HookSpecificOutput.PermissionDecision, "deny") {
63 decision = "deny"
64 }
65 return nil, PreToolOutput{Decision: decision, Reason: reason}, nil
66 }

Find an annotation

Search file paths, rationale, kinds, and authors.

moveEnter openEsc close