Skip to main content
Glama
resolver.go6.58 kB
package proto import ( "strings" ) // Primitive proto types that don't need resolution var primitiveTypes = map[string]bool{ "string": true, "int32": true, "int64": true, "uint32": true, "uint64": true, "sint32": true, "sint64": true, "fixed32": true, "fixed64": true, "sfixed32": true, "sfixed64": true, "bool": true, "bytes": true, "float": true, "double": true, } // isPrimitiveType checks if a type is a primitive proto type func isPrimitiveType(typeName string) bool { return primitiveTypes[typeName] } // resolveServiceTypes recursively resolves all request and response types for a service // This is called by GetService when resolveTypes=true func (pi *ProtoIndex) resolveServiceTypes(service *ProtoService, maxDepth int) map[string]interface{} { if maxDepth <= 0 { return nil } resolved := make(map[string]interface{}) visited := make(map[string]bool) // Get the package context from the service full name contextPackage := service.FullName // Resolve all request and response types for _, rpc := range service.RPCs { // Resolve request type if !visited[rpc.RequestType] { if msg := pi.findMessageByType(rpc.RequestType, contextPackage); msg != nil { visited[rpc.RequestType] = true resolved[rpc.RequestType] = pi.messageToMap(msg) // Recursively resolve nested types nested := pi.resolveMessageTypes(msg, maxDepth-1, visited) for k, v := range nested { resolved[k] = v } } } // Resolve response type if !visited[rpc.ResponseType] { if msg := pi.findMessageByType(rpc.ResponseType, contextPackage); msg != nil { visited[rpc.ResponseType] = true resolved[rpc.ResponseType] = pi.messageToMap(msg) // Recursively resolve nested types nested := pi.resolveMessageTypes(msg, maxDepth-1, visited) for k, v := range nested { resolved[k] = v } } } } return resolved } // resolveMessageTypes recursively resolves all field types in a message // This is called by GetMessage when resolveTypes=true or by resolveServiceTypes func (pi *ProtoIndex) resolveMessageTypes(message *ProtoMessage, maxDepth int, visited map[string]bool) map[string]interface{} { if maxDepth <= 0 { return nil } if visited == nil { visited = make(map[string]bool) } resolved := make(map[string]interface{}) contextPackage := message.FullName for _, field := range message.Fields { fieldType := field.Type // Skip primitive types if isPrimitiveType(fieldType) { continue } // Skip if already visited (circular reference) if visited[fieldType] { continue } visited[fieldType] = true // Try to resolve as message if msg := pi.findMessageByType(fieldType, contextPackage); msg != nil { resolved[fieldType] = pi.messageToMap(msg) // Recursively resolve nested types nested := pi.resolveMessageTypes(msg, maxDepth-1, visited) for k, v := range nested { resolved[k] = v } continue } // Try to resolve as enum if enum := pi.findEnumByType(fieldType, contextPackage); enum != nil { resolved[fieldType] = pi.enumToMap(enum) } } return resolved } // findMessageByType finds a message by type name, considering package context // It tries multiple resolution strategies: // 1. Exact match with full name // 2. Match with context package prefix // 3. Match by simple name func (pi *ProtoIndex) findMessageByType(typeName, contextPackage string) *ProtoMessage { // Try exact match first if msg, exists := pi.messages[typeName]; exists { return msg } // Try with context package prefix // For context "api.v1.UserService", we try "api.v1.TypeName" if contextPackage != "" { packagePrefix := contextPackage // Remove the last component (the service/message name) if lastDot := strings.LastIndex(contextPackage, "."); lastDot != -1 { packagePrefix = contextPackage[:lastDot] } if packagePrefix != "" { qualifiedName := packagePrefix + "." + typeName if msg, exists := pi.messages[qualifiedName]; exists { return msg } } } // Try matching by simple name or suffix for fullName, msg := range pi.messages { if msg.Name == typeName || strings.HasSuffix(fullName, "."+typeName) { return msg } } return nil } // findEnumByType finds an enum by type name, considering package context // It tries multiple resolution strategies: // 1. Exact match with full name // 2. Match with context package prefix // 3. Match by simple name func (pi *ProtoIndex) findEnumByType(typeName, contextPackage string) *ProtoEnum { // Try exact match first if enum, exists := pi.enums[typeName]; exists { return enum } // Try with context package prefix if contextPackage != "" { packagePrefix := contextPackage // Remove the last component (the service/message name) if lastDot := strings.LastIndex(contextPackage, "."); lastDot != -1 { packagePrefix = contextPackage[:lastDot] } if packagePrefix != "" { qualifiedName := packagePrefix + "." + typeName if enum, exists := pi.enums[qualifiedName]; exists { return enum } } } // Try matching by simple name or suffix for fullName, enum := range pi.enums { if enum.Name == typeName || strings.HasSuffix(fullName, "."+typeName) { return enum } } return nil } // messageToMap converts a ProtoMessage to a map for JSON serialization func (pi *ProtoIndex) messageToMap(message *ProtoMessage) map[string]interface{} { fields := make([]map[string]interface{}, len(message.Fields)) for i, field := range message.Fields { fields[i] = map[string]interface{}{ "name": field.Name, "type": field.Type, "number": field.Number, "label": field.Label, "comment": field.Comment, } } return map[string]interface{}{ "kind": "message", "name": message.Name, "full_name": message.FullName, "comment": message.Comment, "fields": fields, "file": pi.findFileForDefinition(message.FullName, "message"), } } // enumToMap converts a ProtoEnum to a map for JSON serialization func (pi *ProtoIndex) enumToMap(enum *ProtoEnum) map[string]interface{} { values := make([]map[string]interface{}, len(enum.Values)) for i, value := range enum.Values { values[i] = map[string]interface{}{ "name": value.Name, "number": value.Number, "comment": value.Comment, } } return map[string]interface{}{ "kind": "enum", "name": enum.Name, "full_name": enum.FullName, "comment": enum.Comment, "values": values, "file": pi.findFileForDefinition(enum.FullName, "enum"), } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/umuterturk/mcp-proto'

If you have feedback or need assistance with the MCP directory API, please join our Discord server