package tools import ( "context" "fmt" "strings" "github.com/vektah/gqlparser/v2/ast" ) // CallToolResult represents the result of a tool call type CallToolResult struct { Content []ContentBlock `json:"content"` IsError bool `json:"isError,omitempty"` } // ContentBlock represents a content block in a tool result type ContentBlock struct { Type string `json:"type"` Text string `json:"text"` } // Introspect returns GraphQL schema information func Introspect(ctx context.Context, schema *ast.Schema, args map[string]interface{}) (CallToolResult, error) { typeName, _ := args["typeName"].(string) var result strings.Builder if typeName != "" { // Introspect specific type result.WriteString(introspectType(schema, typeName)) } else { // Full schema overview result.WriteString("# ARP GraphQL Schema Overview\n\n") // Query type result.WriteString("## Query Type\n") result.WriteString(introspectType(schema, "Query")) result.WriteString("\n") // Mutation type result.WriteString("## Mutation Type\n") result.WriteString(introspectType(schema, "Mutation")) result.WriteString("\n") // Subscription type result.WriteString("## Subscription Type\n") result.WriteString(introspectType(schema, "Subscription")) result.WriteString("\n") // Object types result.WriteString("## Object Types\n") for _, t := range schema.Types { if t.Kind == ast.Object && !strings.HasPrefix(t.Name, "__") && t.Name != "Query" && t.Name != "Mutation" && t.Name != "Subscription" { result.WriteString(fmt.Sprintf("### %s\n", t.Name)) result.WriteString(introspectType(schema, t.Name)) result.WriteString("\n") } } // Input types result.WriteString("## Input Types\n") for _, t := range schema.Types { if t.Kind == ast.InputObject && !strings.HasPrefix(t.Name, "__") { result.WriteString(fmt.Sprintf("### %s\n", t.Name)) result.WriteString(introspectInputType(t)) result.WriteString("\n") } } } return CallToolResult{ Content: []ContentBlock{ {Type: "text", Text: result.String()}, }, }, nil } func introspectType(schema *ast.Schema, typeName string) string { t := schema.Types[typeName] if t == nil { return fmt.Sprintf("Type '%s' not found\n", typeName) } var result strings.Builder switch t.Kind { case ast.Object: result.WriteString(fmt.Sprintf("Type: %s (Object)\n", t.Name)) if t.Description != "" { result.WriteString(fmt.Sprintf("Description: %s\n", t.Description)) } result.WriteString("\nFields:\n") for _, field := range t.Fields { result.WriteString(formatField(field)) } case ast.Interface: result.WriteString(fmt.Sprintf("Type: %s (Interface)\n", t.Name)) if t.Description != "" { result.WriteString(fmt.Sprintf("Description: %s\n", t.Description)) } result.WriteString("\nFields:\n") for _, field := range t.Fields { result.WriteString(formatField(field)) } case ast.Union: result.WriteString(fmt.Sprintf("Type: %s (Union)\n", t.Name)) result.WriteString("Members:\n") for _, member := range t.Types { result.WriteString(fmt.Sprintf(" - %s\n", member)) } case ast.Enum: result.WriteString(fmt.Sprintf("Type: %s (Enum)\n", t.Name)) result.WriteString("Values:\n") for _, val := range t.EnumValues { result.WriteString(fmt.Sprintf(" - %s\n", val.Name)) } default: result.WriteString(fmt.Sprintf("Type: %s (%s)\n", t.Name, t.Kind)) } return result.String() } func introspectInputType(t *ast.Definition) string { var result strings.Builder result.WriteString(fmt.Sprintf("Type: %s (Input)\n", t.Name)) if t.Description != "" { result.WriteString(fmt.Sprintf("Description: %s\n", t.Description)) } result.WriteString("\nFields:\n") for _, field := range t.Fields { result.WriteString(formatInputField(field)) } return result.String() } func formatField(field *ast.FieldDefinition) string { var result strings.Builder // Field name and type result.WriteString(fmt.Sprintf(" %s", field.Name)) if len(field.Arguments) > 0 { result.WriteString("(") for i, arg := range field.Arguments { if i > 0 { result.WriteString(", ") } result.WriteString(fmt.Sprintf("%s: %s", arg.Name, formatType(arg.Type))) } result.WriteString(")") } result.WriteString(fmt.Sprintf(": %s", formatType(field.Type))) // Description if field.Description != "" { result.WriteString(fmt.Sprintf(" - %s", field.Description)) } // Deprecation if field.Directives != nil { for _, d := range field.Directives { if d.Name == "deprecated" { result.WriteString(" [DEPRECATED]") if reason := d.Arguments.ForName("reason"); reason != nil { result.WriteString(fmt.Sprintf(" - %s", reason.Value.Raw)) } } } } result.WriteString("\n") return result.String() } func formatInputField(field *ast.FieldDefinition) string { var result strings.Builder result.WriteString(fmt.Sprintf(" %s: %s", field.Name, formatType(field.Type))) if field.Description != "" { result.WriteString(fmt.Sprintf(" - %s", field.Description)) } result.WriteString("\n") return result.String() } func formatType(t *ast.Type) string { if t == nil { return "Unknown" } base := t.Name() if t.NamedType != "" { base = t.NamedType } if t.Elem != nil { return fmt.Sprintf("[%s]", formatType(t.Elem)) } if t.NonNull { return base + "!" } return base }