(buf *strings.Builder, tool inventory.ServerTool)
| 216 | } |
| 217 | |
| 218 | func writeToolDoc(buf *strings.Builder, tool inventory.ServerTool) { |
| 219 | // Tool name (no icon - section header already has the toolset icon) |
| 220 | fmt.Fprintf(buf, "- **%s** - %s\n", tool.Tool.Name, tool.Tool.Annotations.Title) |
| 221 | |
| 222 | // OAuth scopes if present |
| 223 | if len(tool.RequiredScopes) > 0 { |
| 224 | // Scope filtering uses "any of" semantics (see scopes.HasRequiredScopes), |
| 225 | // so when multiple required scopes are listed, render them as alternatives |
| 226 | // rather than implying all are required. |
| 227 | scopeList := "`" + strings.Join(tool.RequiredScopes, "`, `") + "`" |
| 228 | if len(tool.RequiredScopes) > 1 { |
| 229 | fmt.Fprintf(buf, " - **Required OAuth Scopes (any of)**: %s\n", scopeList) |
| 230 | } else { |
| 231 | fmt.Fprintf(buf, " - **Required OAuth Scopes**: %s\n", scopeList) |
| 232 | } |
| 233 | |
| 234 | // Only show accepted scopes if they differ from required scopes |
| 235 | if len(tool.AcceptedScopes) > 0 && !scopesEqual(tool.RequiredScopes, tool.AcceptedScopes) { |
| 236 | fmt.Fprintf(buf, " - **Accepted OAuth Scopes**: `%s`\n", strings.Join(tool.AcceptedScopes, "`, `")) |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | // MCP App UI metadata (only rendered when the remote_mcp_ui_apps flag |
| 241 | // applied to the inventory; for the no-flags README this section is |
| 242 | // stripped by inventory.ToolsForRegistration before rendering). |
| 243 | if ui, ok := tool.Tool.Meta["ui"].(map[string]any); ok { |
| 244 | if uri, ok := ui["resourceUri"].(string); ok && uri != "" { |
| 245 | fmt.Fprintf(buf, " - **MCP App UI**: `%s`\n", uri) |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | // Parameters |
| 250 | if tool.Tool.InputSchema == nil { |
| 251 | buf.WriteString(" - No parameters required") |
| 252 | return |
| 253 | } |
| 254 | schema, ok := tool.Tool.InputSchema.(*jsonschema.Schema) |
| 255 | if !ok || schema == nil { |
| 256 | buf.WriteString(" - No parameters required") |
| 257 | return |
| 258 | } |
| 259 | |
| 260 | if len(schema.Properties) > 0 { |
| 261 | // Get parameter names and sort them for deterministic order |
| 262 | var paramNames []string |
| 263 | for propName := range schema.Properties { |
| 264 | paramNames = append(paramNames, propName) |
| 265 | } |
| 266 | sort.Strings(paramNames) |
| 267 | |
| 268 | for i, propName := range paramNames { |
| 269 | prop := schema.Properties[propName] |
| 270 | required := slices.Contains(schema.Required, propName) |
| 271 | requiredStr := "optional" |
| 272 | if required { |
| 273 | requiredStr = "required" |
| 274 | } |
| 275 |
no test coverage detected