(cl *ast.CompositeLit, aliases map[string]struct{}, file string, line int)
| 114 | } |
| 115 | |
| 116 | func checkToolLiteral(cl *ast.CompositeLit, aliases map[string]struct{}, file string, line int) []ReadOnlyHintViolation { |
| 117 | toolName := extractToolName(cl) |
| 118 | if toolName == "" { |
| 119 | toolName = "<unknown>" |
| 120 | } |
| 121 | mk := func(reason string) ReadOnlyHintViolation { |
| 122 | return ReadOnlyHintViolation{File: file, Line: line, ToolName: toolName, Reason: reason} |
| 123 | } |
| 124 | |
| 125 | if hasUnkeyedFields(cl) { |
| 126 | return []ReadOnlyHintViolation{mk("mcp.Tool literal uses positional (unkeyed) fields; this check requires keyed fields so Annotations.ReadOnlyHint can be verified")} |
| 127 | } |
| 128 | |
| 129 | annotations := findFieldValue(cl, "Annotations") |
| 130 | if annotations == nil { |
| 131 | return []ReadOnlyHintViolation{mk("mcp.Tool literal is missing an Annotations field")} |
| 132 | } |
| 133 | |
| 134 | annoLit := unwrapAnnotationsLiteral(annotations, aliases) |
| 135 | if annoLit == nil { |
| 136 | return []ReadOnlyHintViolation{mk("Annotations is not an &mcp.ToolAnnotations{...} literal; ReadOnlyHint cannot be statically verified")} |
| 137 | } |
| 138 | |
| 139 | if hasUnkeyedFields(annoLit) { |
| 140 | return []ReadOnlyHintViolation{mk("mcp.ToolAnnotations literal uses positional (unkeyed) fields; use keyed fields so ReadOnlyHint can be verified")} |
| 141 | } |
| 142 | |
| 143 | if findFieldValue(annoLit, "ReadOnlyHint") == nil { |
| 144 | return []ReadOnlyHintViolation{mk("ToolAnnotations literal does not explicitly set ReadOnlyHint")} |
| 145 | } |
| 146 | return nil |
| 147 | } |
| 148 | |
| 149 | // mcpAliasesFor returns the set of local identifiers under which the given |
| 150 | // file imports the MCP go-sdk (MCPImportPath). The default unaliased import |
no test coverage detected