@@ -10,15 +10,16 @@ import {
1010 HYPHEN_ENCODING ,
1111 MAX_TOOL_NAME_LENGTH ,
1212 HASH_SUFFIX_LENGTH ,
13- mcpToolNameRegistry ,
14- clearMcpToolNameRegistry ,
13+ clearEncodedNameCache ,
1514 computeHashSuffix ,
15+ findToolByEncodedMcpName ,
16+ hasHashSuffix ,
1617} from "../mcp-name"
1718
1819describe ( "mcp-name utilities" , ( ) => {
19- // Clear the registry before each test to ensure isolation
20+ // Clear the cache before each test to ensure isolation
2021 beforeEach ( ( ) => {
21- clearMcpToolNameRegistry ( )
22+ clearEncodedNameCache ( )
2223 } )
2324
2425 describe ( "constants" , ( ) => {
@@ -175,27 +176,27 @@ describe("mcp-name utilities", () => {
175176 expect ( result ) . toMatch ( / _ [ a - f 0 - 9 ] { 8 } $ / )
176177 } )
177178
178- it ( "should use hash suffix for long names and register them" , ( ) => {
179+ it ( "should use hash suffix for long names and cache them" , ( ) => {
179180 const longServer = "a" . repeat ( 80 )
180181 const longTool = "b" . repeat ( 80 )
181182 const result = buildMcpToolName ( longServer , longTool )
182183
183- // The shortened name should be registered
184- expect ( mcpToolNameRegistry . has ( result ) ) . toBe ( true )
185- const registered = mcpToolNameRegistry . get ( result )
186- expect ( registered ) . toEqual ( {
187- serverName : longServer ,
188- toolName : longTool ,
189- } )
184+ // The shortened name should be deterministic
185+ expect ( result . length ) . toBe ( 128 )
186+ expect ( result ) . toMatch ( / _ [ a - f 0 - 9 ] { 8 } $ / )
187+
188+ // Building again should return the same result (from cache)
189+ const result2 = buildMcpToolName ( longServer , longTool )
190+ expect ( result2 ) . toBe ( result )
190191 } )
191192
192193 it ( "should produce deterministic hash suffixes" , ( ) => {
193194 const longServer = "a" . repeat ( 80 )
194195 const longTool = "b" . repeat ( 80 )
195- // Build the same name twice
196- clearMcpToolNameRegistry ( )
196+ // Build the same name twice with cache cleared between
197+ clearEncodedNameCache ( )
197198 const result1 = buildMcpToolName ( longServer , longTool )
198- clearMcpToolNameRegistry ( )
199+ clearEncodedNameCache ( )
199200 const result2 = buildMcpToolName ( longServer , longTool )
200201 // Should produce identical results
201202 expect ( result1 ) . toBe ( result2 )
@@ -427,97 +428,132 @@ describe("mcp-name utilities", () => {
427428 } )
428429
429430 describe ( "hash suffix roundtrip - fixes issue #10766" , ( ) => {
430- it ( "should preserve original names through roundtrip with long hyphenated tool names" , ( ) => {
431- // This is the exact scenario from issue #10766
432- // Tool name with many hyphens that exceeds 128 chars when encoded
433- const serverName = "abcdefghij-kl-mnop-qrs-tuv-with-extra-long-suffix-to-exceed-limit"
434- const toolName = "wxyz-abcd-efghijk-lmno-plus-additional-long-suffix-here"
431+ it ( "should work correctly when names do not need truncation" , ( ) => {
432+ // Short names that don't need truncation
433+ const serverName = "server"
434+ const toolName = "get-data"
435435
436- // Build the tool name
437436 const builtName = buildMcpToolName ( serverName , toolName )
438437
439- // Should be truncated to 128 chars with hash suffix
440- expect ( builtName . length ) . toBe ( 128 )
441- expect ( builtName ) . toMatch ( / _ [ a - f 0 - 9 ] { 8 } $ / )
438+ // Should NOT have hash suffix
439+ expect ( builtName ) . toBe ( "mcp--server--get___data" )
440+ expect ( builtName . length ) . toBeLessThan ( 128 )
442441
443- // The critical fix: parsing should return the ORIGINAL names
442+ // Normal decode path should work
444443 const parsed = parseMcpToolName ( builtName )
445444 expect ( parsed ) . toEqual ( {
446- serverName : serverName , // Original with hyphens!
447- toolName : toolName , // Original with hyphens!
445+ serverName : "server" ,
446+ toolName : "get-data" , // Hyphen decoded from ___
448447 } )
449448 } )
450449
451- it ( "should not corrupt hyphen encoding when truncation is needed " , ( ) => {
452- // Long server and tool names that would cause truncation mid-encoding
453- const serverName = "very-long-server-name- with-many-hyphens-and- extra-content- to-exceed"
454- const toolName = "another-long-tool-name-with-hyphens-and-extra-content-to-exceed "
450+ it ( "should find tool by encoded name comparison for shortened names " , ( ) => {
451+ // This is the new approach: instead of registry, compare encoded names
452+ const serverName = "abcdefghij-kl-mnop-qrs-tuv- with-extra-long-suffix- to-exceed-limit "
453+ const toolName = "wxyz-abcd-efghijk-lmno-plus-additional-long-suffix-here "
455454
456- // Build the tool name
457- const builtName = buildMcpToolName ( serverName , toolName )
455+ // Build the encoded tool name
456+ const encodedName = buildMcpToolName ( serverName , toolName )
458457
459- // Should be truncated to 128 chars
460- expect ( builtName . length ) . toBe ( 128 )
458+ // Should be truncated to 128 chars with hash suffix
459+ expect ( encodedName . length ) . toBe ( 128 )
460+ expect ( encodedName ) . toMatch ( / _ [ a - f 0 - 9 ] { 8 } $ / )
461461
462- // Parse should return original names (via registry lookup)
463- const parsed = parseMcpToolName ( builtName )
464- expect ( parsed ) . toEqual ( {
465- serverName : serverName ,
466- toolName : toolName ,
467- } )
462+ // The new approach: use findToolByEncodedMcpName to find the matching tool
463+ const availableTools = [ toolName , "other-tool" , "another-tool" ]
464+ const foundTool = findToolByEncodedMcpName ( serverName , encodedName , availableTools )
465+ expect ( foundTool ) . toBe ( toolName )
468466 } )
469467
470- it ( "should work correctly when names do not need truncation" , ( ) => {
471- // Short names that don't need truncation
468+ it ( "should not find tool when none matches the encoded name" , ( ) => {
472469 const serverName = "server"
473- const toolName = "get-data "
470+ const encodedName = "mcp--server--nonexistent_tool_a1b2c3d4 "
474471
475- const builtName = buildMcpToolName ( serverName , toolName )
472+ const availableTools = [ "tool1" , "tool2" , "tool3" ]
473+ const foundTool = findToolByEncodedMcpName ( serverName , encodedName , availableTools )
474+ expect ( foundTool ) . toBeNull ( )
475+ } )
476+ } )
476477
477- // Should NOT have hash suffix
478- expect ( builtName ) . toBe ( "mcp--server--get___data" )
479- expect ( builtName . length ) . toBeLessThan ( 128 )
478+ describe ( "findToolByEncodedMcpName" , ( ) => {
479+ it ( "should find tool by exact encoded name match" , ( ) => {
480+ const serverName = "myserver"
481+ const toolName = "get-data"
482+ const encodedName = buildMcpToolName ( serverName , toolName )
480483
481- // Normal decode path should work
482- const parsed = parseMcpToolName ( builtName )
483- expect ( parsed ) . toEqual ( {
484- serverName : "server" ,
485- toolName : "get-data" , // Hyphen decoded from ___
486- } )
484+ const availableTools = [ "other-tool" , "get-data" , "another-tool" ]
485+ const foundTool = findToolByEncodedMcpName ( serverName , encodedName , availableTools )
486+ expect ( foundTool ) . toBe ( "get-data" )
487487 } )
488488
489- it ( "should handle the registry lookup for shortened names" , ( ) => {
489+ it ( "should find tool for shortened names with hash suffix " , ( ) => {
490490 const serverName = "a" . repeat ( 80 )
491491 const toolName = "b" . repeat ( 80 ) + "-hyphen"
492+ const encodedName = buildMcpToolName ( serverName , toolName )
492493
493- // Build registers the shortened name
494- const builtName = buildMcpToolName ( serverName , toolName )
494+ // The encoded name should have a hash suffix
495+ expect ( hasHashSuffix ( encodedName ) ) . toBe ( true )
495496
496- // Verify it's in the registry
497- expect ( mcpToolNameRegistry . has ( builtName ) ) . toBe ( true )
497+ const availableTools = [ "other-tool" , toolName , "another-tool" ]
498+ const foundTool = findToolByEncodedMcpName ( serverName , encodedName , availableTools )
499+ expect ( foundTool ) . toBe ( toolName )
500+ } )
498501
499- // Parse uses the registry to get original names
500- const parsed = parseMcpToolName ( builtName )
501- expect ( parsed ) . toEqual ( {
502- serverName : serverName ,
503- toolName : toolName ,
504- } )
502+ it ( "should return null when no tool matches" , ( ) => {
503+ const serverName = "server"
504+ const encodedName = buildMcpToolName ( serverName , "nonexistent" )
505+
506+ const availableTools = [ "tool1" , "tool2" ]
507+ const foundTool = findToolByEncodedMcpName ( serverName , encodedName , availableTools )
508+ expect ( foundTool ) . toBeNull ( )
509+ } )
510+
511+ it ( "should handle empty available tools list" , ( ) => {
512+ const serverName = "server"
513+ const encodedName = buildMcpToolName ( serverName , "tool" )
514+
515+ const foundTool = findToolByEncodedMcpName ( serverName , encodedName , [ ] )
516+ expect ( foundTool ) . toBeNull ( )
505517 } )
506518 } )
507519
508- describe ( "clearMcpToolNameRegistry" , ( ) => {
509- it ( "should clear all registered tool names" , ( ) => {
510- // Register some tool names via buildMcpToolName
520+ describe ( "hasHashSuffix" , ( ) => {
521+ it ( "should return true for names with hash suffix" , ( ) => {
522+ expect ( hasHashSuffix ( "mcp--server--tool_a1b2c3d4" ) ) . toBe ( true )
523+ expect ( hasHashSuffix ( "mcp--server--tool_12345678" ) ) . toBe ( true )
524+ expect ( hasHashSuffix ( "mcp--server--tool_abcdef00" ) ) . toBe ( true )
525+ } )
526+
527+ it ( "should return false for names without hash suffix" , ( ) => {
528+ expect ( hasHashSuffix ( "mcp--server--tool" ) ) . toBe ( false )
529+ expect ( hasHashSuffix ( "mcp--server--tool_name" ) ) . toBe ( false )
530+ expect ( hasHashSuffix ( "mcp--server--tool___name" ) ) . toBe ( false )
531+ } )
532+
533+ it ( "should return false for names with partial hash patterns" , ( ) => {
534+ // Not enough hex chars
535+ expect ( hasHashSuffix ( "mcp--server--tool_abc" ) ) . toBe ( false )
536+ // No underscore before hash
537+ expect ( hasHashSuffix ( "mcp--server--toola1b2c3d4" ) ) . toBe ( false )
538+ } )
539+ } )
540+
541+ describe ( "clearEncodedNameCache" , ( ) => {
542+ it ( "should clear the encoded name cache" , ( ) => {
543+ // Build some names to populate cache
511544 const longServer = "a" . repeat ( 80 )
512545 const longTool = "b" . repeat ( 80 )
513546 buildMcpToolName ( longServer , longTool )
547+ buildMcpToolName ( "server" , "tool" )
514548
515- expect ( mcpToolNameRegistry . size ) . toBeGreaterThan ( 0 )
516-
517- // Clear the registry
518- clearMcpToolNameRegistry ( )
549+ // Clear the cache
550+ clearEncodedNameCache ( )
519551
520- expect ( mcpToolNameRegistry . size ) . toBe ( 0 )
552+ // Verify cache is cleared by checking that rebuilding takes the same path
553+ // (we can't directly access the cache, but the function should work)
554+ const result = buildMcpToolName ( longServer , longTool )
555+ expect ( result . length ) . toBe ( 128 )
556+ expect ( result ) . toMatch ( / _ [ a - f 0 - 9 ] { 8 } $ / )
521557 } )
522558 } )
523559} )
0 commit comments