@@ -2022,3 +2022,359 @@ func TestMCPServer_ProtocolNegotiation(t *testing.T) {
20222022 })
20232023 }
20242024}
2025+
2026+ func TestMCPServer_GetTools (t * testing.T ) { t .Run ("EmptyServer" , func (t * testing.T ) {
2027+ server := NewMCPServer ("test-server" , "1.0.0" )
2028+
2029+ tools , err := server .GetTools ()
2030+
2031+ assert .NoError (t , err )
2032+ assert .NotNil (t , tools )
2033+ assert .Len (t , tools , 0 )
2034+ })
2035+
2036+ t .Run ("SingleTool" , func (t * testing.T ) {
2037+ server := NewMCPServer ("test-server" , "1.0.0" )
2038+
2039+ expectedTool := mcp.Tool {
2040+ Name : "test-tool" ,
2041+ Description : "A test tool" ,
2042+ InputSchema : mcp.ToolInputSchema {
2043+ Type : "object" ,
2044+ Properties : map [string ]any {
2045+ "input" : map [string ]any {
2046+ "type" : "string" ,
2047+ "description" : "Test input" ,
2048+ },
2049+ },
2050+ },
2051+ }
2052+
2053+ expectedHandler := func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
2054+ return & mcp.CallToolResult {
2055+ Content : []mcp.Content {
2056+ mcp.TextContent {
2057+ Type : "text" ,
2058+ Text : "test result" ,
2059+ },
2060+ },
2061+ }, nil
2062+ }
2063+
2064+ server .AddTool (expectedTool , expectedHandler )
2065+
2066+ tools , err := server .GetTools ()
2067+
2068+ assert .NoError (t , err )
2069+ assert .NotNil (t , tools )
2070+ assert .Len (t , tools , 1 )
2071+
2072+ serverTool , exists := tools ["test-tool" ]
2073+ assert .True (t , exists )
2074+ assert .Equal (t , expectedTool , serverTool .Tool )
2075+ assert .NotNil (t , serverTool .Handler )
2076+ })
2077+
2078+ t .Run ("MultipleTools" , func (t * testing.T ) {
2079+ server := NewMCPServer ("test-server" , "1.0.0" )
2080+
2081+ tools := []struct {
2082+ tool mcp.Tool
2083+ handler ToolHandlerFunc
2084+ }{
2085+ {
2086+ tool : mcp.Tool {
2087+ Name : "tool1" ,
2088+ Description : "First tool" ,
2089+ InputSchema : mcp.ToolInputSchema {Type : "object" },
2090+ },
2091+ handler : func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
2092+ return & mcp.CallToolResult {}, nil
2093+ },
2094+ },
2095+ {
2096+ tool : mcp.Tool {
2097+ Name : "tool2" ,
2098+ Description : "Second tool" ,
2099+ InputSchema : mcp.ToolInputSchema {Type : "object" },
2100+ },
2101+ handler : func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
2102+ return & mcp.CallToolResult {}, nil
2103+ },
2104+ },
2105+ {
2106+ tool : mcp.Tool {
2107+ Name : "tool3" ,
2108+ Description : "Third tool" ,
2109+ InputSchema : mcp.ToolInputSchema {Type : "object" },
2110+ },
2111+ handler : func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
2112+ return & mcp.CallToolResult {}, nil
2113+ },
2114+ },
2115+ }
2116+
2117+ // Add tools one by one
2118+ for _ , tool := range tools {
2119+ server .AddTool (tool .tool , tool .handler )
2120+ }
2121+
2122+ retrievedTools , err := server .GetTools ()
2123+
2124+ assert .NoError (t , err )
2125+ assert .NotNil (t , retrievedTools )
2126+ assert .Len (t , retrievedTools , 3 )
2127+
2128+ // Verify each tool exists with correct data
2129+ for _ , expectedTool := range tools {
2130+ serverTool , exists := retrievedTools [expectedTool .tool .Name ]
2131+ assert .True (t , exists , "Tool %s should exist" , expectedTool .tool .Name )
2132+ assert .Equal (t , expectedTool .tool , serverTool .Tool )
2133+ assert .NotNil (t , serverTool .Handler )
2134+ }
2135+ })
2136+
2137+ t .Run ("AfterToolDeletion" , func (t * testing.T ) {
2138+ server := NewMCPServer ("test-server" , "1.0.0" )
2139+
2140+ // Add multiple tools
2141+ server .AddTool (mcp.Tool {Name : "tool1" , Description : "Tool 1" }, nil )
2142+ server .AddTool (mcp.Tool {Name : "tool2" , Description : "Tool 2" }, nil )
2143+ server .AddTool (mcp.Tool {Name : "tool3" , Description : "Tool 3" }, nil )
2144+
2145+ // Verify all tools exist
2146+ tools , err := server .GetTools ()
2147+ assert .NoError (t , err )
2148+ assert .Len (t , tools , 3 )
2149+
2150+ // Delete one tool
2151+ server .DeleteTools ("tool2" )
2152+
2153+ // Verify tool is removed
2154+ tools , err = server .GetTools ()
2155+ assert .NoError (t , err )
2156+ assert .Len (t , tools , 2 )
2157+
2158+ _ , exists := tools ["tool1" ]
2159+ assert .True (t , exists )
2160+ _ , exists = tools ["tool2" ]
2161+ assert .False (t , exists )
2162+ _ , exists = tools ["tool3" ]
2163+ assert .True (t , exists )
2164+ })
2165+
2166+ t .Run ("SetToolsReplacesExisting" , func (t * testing.T ) {
2167+ server := NewMCPServer ("test-server" , "1.0.0" )
2168+
2169+ // Add initial tools
2170+ server .AddTool (mcp.Tool {Name : "old-tool1" , Description : "Old Tool 1" }, nil )
2171+ server .AddTool (mcp.Tool {Name : "old-tool2" , Description : "Old Tool 2" }, nil )
2172+
2173+ // Verify initial tools
2174+ tools , err := server .GetTools ()
2175+ assert .NoError (t , err )
2176+ assert .Len (t , tools , 2 )
2177+
2178+ // Set new tools (should replace existing)
2179+ newTools := []ServerTool {
2180+ {
2181+ Tool : mcp.Tool {Name : "new-tool1" , Description : "New Tool 1" },
2182+ Handler : func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
2183+ return & mcp.CallToolResult {}, nil
2184+ },
2185+ },
2186+ {
2187+ Tool : mcp.Tool {Name : "new-tool2" , Description : "New Tool 2" },
2188+ Handler : func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
2189+ return & mcp.CallToolResult {}, nil
2190+ },
2191+ },
2192+ }
2193+ server .SetTools (newTools ... )
2194+
2195+ // Verify only new tools exist
2196+ tools , err = server .GetTools ()
2197+ assert .NoError (t , err )
2198+ assert .Len (t , tools , 2 )
2199+
2200+ _ , exists := tools ["old-tool1" ]
2201+ assert .False (t , exists )
2202+ _ , exists = tools ["old-tool2" ]
2203+ assert .False (t , exists )
2204+ _ , exists = tools ["new-tool1" ]
2205+ assert .True (t , exists )
2206+ _ , exists = tools ["new-tool2" ]
2207+ assert .True (t , exists )
2208+ })
2209+
2210+ t .Run ("ConcurrentAccess" , func (t * testing.T ) {
2211+ server := NewMCPServer ("test-server" , "1.0.0" )
2212+
2213+ // Number of goroutines for testing
2214+ numGoroutines := 100
2215+ numToolsPerGoroutine := 10
2216+
2217+ // Channel to collect results
2218+ results := make (chan map [string ]ServerTool , numGoroutines )
2219+
2220+ // Start goroutines that concurrently call GetTools
2221+ for i := 0 ; i < numGoroutines ; i ++ {
2222+ go func (id int ) {
2223+ // Add some tools specific to this goroutine
2224+ for j := 0 ; j < numToolsPerGoroutine ; j ++ {
2225+ toolName := fmt .Sprintf ("tool-%d-%d" , id , j )
2226+ server .AddTool (mcp.Tool {
2227+ Name : toolName ,
2228+ Description : fmt .Sprintf ("Tool %d from goroutine %d" , j , id ),
2229+ }, nil )
2230+ }
2231+
2232+ // Get tools
2233+ tools , err := server .GetTools ()
2234+ assert .NoError (t , err )
2235+ results <- tools
2236+ }(i )
2237+ }
2238+
2239+ // Collect all results
2240+ var allResults []map [string ]ServerTool
2241+ for i := 0 ; i < numGoroutines ; i ++ {
2242+ result := <- results
2243+ allResults = append (allResults , result )
2244+ }
2245+
2246+ // Verify that no data races occurred and all results are valid
2247+ for _ , result := range allResults {
2248+ assert .NotNil (t , result )
2249+ // Each result should have at least some tools (may not have all due to timing)
2250+ assert .Greater (t , len (result ), 0 )
2251+ }
2252+
2253+ // Final check - get all tools at the end
2254+ finalTools , err := server .GetTools ()
2255+ assert .NoError (t , err )
2256+ assert .NotNil (t , finalTools )
2257+ // Should have exactly numGoroutines * numToolsPerGoroutine tools
2258+ assert .Equal (t , numGoroutines * numToolsPerGoroutine , len (finalTools ))
2259+ })
2260+ t .Run ("ConsistentResults" , func (t * testing.T ) {
2261+ server := NewMCPServer ("test-server" , "1.0.0" )
2262+
2263+ // Add a tool
2264+ server .AddTool (mcp.Tool {
2265+ Name : "test-tool" ,
2266+ Description : "Test tool" ,
2267+ }, nil )
2268+
2269+ // Get tools multiple times
2270+ tools1 , err1 := server .GetTools ()
2271+ tools2 , err2 := server .GetTools ()
2272+ tools3 , err3 := server .GetTools ()
2273+
2274+ assert .NoError (t , err1 )
2275+ assert .NoError (t , err2 )
2276+ assert .NoError (t , err3 )
2277+ assert .NotNil (t , tools1 )
2278+ assert .NotNil (t , tools2 )
2279+ assert .NotNil (t , tools3 )
2280+
2281+ // Verify all calls return consistent results
2282+ assert .Equal (t , tools1 , tools2 )
2283+ assert .Equal (t , tools2 , tools3 )
2284+ assert .Equal (t , tools1 , tools3 )
2285+
2286+ // All should have the same tool
2287+ assert .Len (t , tools1 , 1 )
2288+ assert .Len (t , tools2 , 1 )
2289+ assert .Len (t , tools3 , 1 )
2290+
2291+ assert .Contains (t , tools1 , "test-tool" )
2292+ assert .Contains (t , tools2 , "test-tool" )
2293+ assert .Contains (t , tools3 , "test-tool" )
2294+ })
2295+
2296+ t .Run ("WithComplexToolSchema" , func (t * testing.T ) {
2297+ server := NewMCPServer ("test-server" , "1.0.0" )
2298+
2299+ complexTool := mcp.Tool {
2300+ Name : "complex-tool" ,
2301+ Description : "A complex tool with detailed schema" ,
2302+ InputSchema : mcp.ToolInputSchema {
2303+ Type : "object" ,
2304+ Properties : map [string ]any {
2305+ "stringParam" : map [string ]any {
2306+ "type" : "string" ,
2307+ "description" : "A string parameter" ,
2308+ "enum" : []string {"option1" , "option2" , "option3" },
2309+ },
2310+ "numberParam" : map [string ]any {
2311+ "type" : "number" ,
2312+ "description" : "A number parameter" ,
2313+ "minimum" : 0 ,
2314+ "maximum" : 100 ,
2315+ },
2316+ "objectParam" : map [string ]any {
2317+ "type" : "object" ,
2318+ "properties" : map [string ]any {
2319+ "nestedString" : map [string ]any {
2320+ "type" : "string" ,
2321+ },
2322+ "nestedArray" : map [string ]any {
2323+ "type" : "array" ,
2324+ "items" : map [string ]any {
2325+ "type" : "integer" ,
2326+ },
2327+ },
2328+ },
2329+ "required" : []string {"nestedString" },
2330+ },
2331+ },
2332+ Required : []string {"stringParam" , "numberParam" },
2333+ },
2334+ Annotations : mcp.ToolAnnotation {
2335+ Title : "Complex Tool" ,
2336+ ReadOnlyHint : mcp .ToBoolPtr (false ),
2337+ DestructiveHint : mcp .ToBoolPtr (true ),
2338+ IdempotentHint : mcp .ToBoolPtr (false ),
2339+ OpenWorldHint : mcp .ToBoolPtr (true ),
2340+ },
2341+ }
2342+
2343+ handler := func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
2344+ return & mcp.CallToolResult {
2345+ Content : []mcp.Content {
2346+ mcp.TextContent {
2347+ Type : "text" ,
2348+ Text : "Complex tool executed" ,
2349+ },
2350+ },
2351+ IsError : false ,
2352+ }, nil
2353+ }
2354+
2355+ server .AddTool (complexTool , handler )
2356+
2357+ tools , err := server .GetTools ()
2358+
2359+ assert .NoError (t , err )
2360+ assert .NotNil (t , tools )
2361+ assert .Len (t , tools , 1 )
2362+
2363+ retrievedTool , exists := tools ["complex-tool" ]
2364+ assert .True (t , exists )
2365+ assert .Equal (t , complexTool , retrievedTool .Tool )
2366+ assert .NotNil (t , retrievedTool .Handler )
2367+
2368+ // Verify the complex schema is preserved
2369+ assert .Equal (t , "object" , retrievedTool .Tool .InputSchema .Type )
2370+ assert .Contains (t , retrievedTool .Tool .InputSchema .Properties , "stringParam" )
2371+ assert .Contains (t , retrievedTool .Tool .InputSchema .Properties , "numberParam" )
2372+ assert .Contains (t , retrievedTool .Tool .InputSchema .Properties , "objectParam" )
2373+ assert .Equal (t , []string {"stringParam" , "numberParam" }, retrievedTool .Tool .InputSchema .Required )
2374+
2375+ // Verify annotations
2376+ assert .Equal (t , "Complex Tool" , retrievedTool .Tool .Annotations .Title )
2377+ assert .NotNil (t , retrievedTool .Tool .Annotations .DestructiveHint )
2378+ assert .True (t , * retrievedTool .Tool .Annotations .DestructiveHint )
2379+ })
2380+ }
0 commit comments