@@ -12,6 +12,7 @@ import {
1212 ListResourcesRequestSchema ,
1313 ListToolsRequestSchema ,
1414 ListToolsResultSchema ,
15+ ListPromptsRequestSchema ,
1516 CallToolRequestSchema ,
1617 CallToolResultSchema ,
1718 CreateMessageRequestSchema ,
@@ -1558,6 +1559,225 @@ test('should handle multiple list changed handlers configured together', async (
15581559 expect ( promptNotifications [ 0 ] [ 1 ] ) . toHaveLength ( 2 ) ;
15591560} ) ;
15601561
1562+ /***
1563+ * Test: Handler not activated when server doesn't advertise listChanged capability
1564+ */
1565+ test ( 'should not activate listChanged handler when server does not advertise capability' , async ( ) => {
1566+ const notifications : [ Error | null , Tool [ ] | null ] [ ] = [ ] ;
1567+
1568+ // Server with tools capability but WITHOUT listChanged
1569+ const server = new Server ( { name : 'test-server' , version : '1.0.0' } , { capabilities : { tools : { } } } ) ;
1570+
1571+ server . setRequestHandler ( InitializeRequestSchema , async request => ( {
1572+ protocolVersion : request . params . protocolVersion ,
1573+ capabilities : { tools : { } } , // No listChanged: true
1574+ serverInfo : { name : 'test-server' , version : '1.0.0' }
1575+ } ) ) ;
1576+
1577+ server . setRequestHandler ( ListToolsRequestSchema , async ( ) => ( {
1578+ tools : [ { name : 'test-tool' , inputSchema : { type : 'object' } } ]
1579+ } ) ) ;
1580+
1581+ // Configure listChanged handler that should NOT be activated
1582+ const client = new Client (
1583+ { name : 'test-client' , version : '1.0.0' } ,
1584+ {
1585+ listChanged : {
1586+ tools : {
1587+ debounceMs : 0 ,
1588+ onChanged : ( err , tools ) => {
1589+ notifications . push ( [ err , tools ] ) ;
1590+ }
1591+ }
1592+ }
1593+ }
1594+ ) ;
1595+
1596+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
1597+
1598+ await Promise . all ( [ client . connect ( clientTransport ) , server . connect ( serverTransport ) ] ) ;
1599+
1600+ // Verify server doesn't have tools.listChanged capability
1601+ expect ( client . getServerCapabilities ( ) ?. tools ?. listChanged ) . toBeFalsy ( ) ;
1602+
1603+ // Send a tool list changed notification manually
1604+ await server . notification ( { method : 'notifications/tools/list_changed' } ) ;
1605+ await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
1606+
1607+ // Handler should NOT have been activated because server didn't advertise listChanged
1608+ expect ( notifications ) . toHaveLength ( 0 ) ;
1609+ } ) ;
1610+
1611+ /***
1612+ * Test: Handler activated when server advertises listChanged capability
1613+ */
1614+ test ( 'should activate listChanged handler when server advertises capability' , async ( ) => {
1615+ const notifications : [ Error | null , Tool [ ] | null ] [ ] = [ ] ;
1616+
1617+ // Server with tools.listChanged: true capability
1618+ const server = new Server ( { name : 'test-server' , version : '1.0.0' } , { capabilities : { tools : { listChanged : true } } } ) ;
1619+
1620+ server . setRequestHandler ( InitializeRequestSchema , async request => ( {
1621+ protocolVersion : request . params . protocolVersion ,
1622+ capabilities : { tools : { listChanged : true } } ,
1623+ serverInfo : { name : 'test-server' , version : '1.0.0' }
1624+ } ) ) ;
1625+
1626+ server . setRequestHandler ( ListToolsRequestSchema , async ( ) => ( {
1627+ tools : [ { name : 'test-tool' , inputSchema : { type : 'object' } } ]
1628+ } ) ) ;
1629+
1630+ // Configure listChanged handler that SHOULD be activated
1631+ const client = new Client (
1632+ { name : 'test-client' , version : '1.0.0' } ,
1633+ {
1634+ listChanged : {
1635+ tools : {
1636+ debounceMs : 0 ,
1637+ onChanged : ( err , tools ) => {
1638+ notifications . push ( [ err , tools ] ) ;
1639+ }
1640+ }
1641+ }
1642+ }
1643+ ) ;
1644+
1645+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
1646+
1647+ await Promise . all ( [ client . connect ( clientTransport ) , server . connect ( serverTransport ) ] ) ;
1648+
1649+ // Verify server has tools.listChanged capability
1650+ expect ( client . getServerCapabilities ( ) ?. tools ?. listChanged ) . toBe ( true ) ;
1651+
1652+ // Send a tool list changed notification
1653+ await server . notification ( { method : 'notifications/tools/list_changed' } ) ;
1654+ await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
1655+
1656+ // Handler SHOULD have been called
1657+ expect ( notifications ) . toHaveLength ( 1 ) ;
1658+ expect ( notifications [ 0 ] [ 0 ] ) . toBeNull ( ) ;
1659+ expect ( notifications [ 0 ] [ 1 ] ) . toHaveLength ( 1 ) ;
1660+ } ) ;
1661+
1662+ /***
1663+ * Test: No handlers activated when server has no listChanged capabilities
1664+ */
1665+ test ( 'should not activate any handlers when server has no listChanged capabilities' , async ( ) => {
1666+ const toolNotifications : [ Error | null , Tool [ ] | null ] [ ] = [ ] ;
1667+ const promptNotifications : [ Error | null , Prompt [ ] | null ] [ ] = [ ] ;
1668+ const resourceNotifications : [ Error | null , Resource [ ] | null ] [ ] = [ ] ;
1669+
1670+ // Server with capabilities but NO listChanged for any
1671+ const server = new Server ( { name : 'test-server' , version : '1.0.0' } , { capabilities : { tools : { } , prompts : { } , resources : { } } } ) ;
1672+
1673+ server . setRequestHandler ( InitializeRequestSchema , async request => ( {
1674+ protocolVersion : request . params . protocolVersion ,
1675+ capabilities : { tools : { } , prompts : { } , resources : { } } ,
1676+ serverInfo : { name : 'test-server' , version : '1.0.0' }
1677+ } ) ) ;
1678+
1679+ // Configure listChanged handlers for all three types
1680+ const client = new Client (
1681+ { name : 'test-client' , version : '1.0.0' } ,
1682+ {
1683+ listChanged : {
1684+ tools : {
1685+ debounceMs : 0 ,
1686+ onChanged : ( err , tools ) => toolNotifications . push ( [ err , tools ] )
1687+ } ,
1688+ prompts : {
1689+ debounceMs : 0 ,
1690+ onChanged : ( err , prompts ) => promptNotifications . push ( [ err , prompts ] )
1691+ } ,
1692+ resources : {
1693+ debounceMs : 0 ,
1694+ onChanged : ( err , resources ) => resourceNotifications . push ( [ err , resources ] )
1695+ }
1696+ }
1697+ }
1698+ ) ;
1699+
1700+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
1701+
1702+ await Promise . all ( [ client . connect ( clientTransport ) , server . connect ( serverTransport ) ] ) ;
1703+
1704+ // Verify server has no listChanged capabilities
1705+ const caps = client . getServerCapabilities ( ) ;
1706+ expect ( caps ?. tools ?. listChanged ) . toBeFalsy ( ) ;
1707+ expect ( caps ?. prompts ?. listChanged ) . toBeFalsy ( ) ;
1708+ expect ( caps ?. resources ?. listChanged ) . toBeFalsy ( ) ;
1709+
1710+ // Send notifications for all three types
1711+ await server . notification ( { method : 'notifications/tools/list_changed' } ) ;
1712+ await server . notification ( { method : 'notifications/prompts/list_changed' } ) ;
1713+ await server . notification ( { method : 'notifications/resources/list_changed' } ) ;
1714+ await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
1715+
1716+ // No handlers should have been activated
1717+ expect ( toolNotifications ) . toHaveLength ( 0 ) ;
1718+ expect ( promptNotifications ) . toHaveLength ( 0 ) ;
1719+ expect ( resourceNotifications ) . toHaveLength ( 0 ) ;
1720+ } ) ;
1721+
1722+ /***
1723+ * Test: Partial capability support - some handlers activated, others not
1724+ */
1725+ test ( 'should handle partial listChanged capability support' , async ( ) => {
1726+ const toolNotifications : [ Error | null , Tool [ ] | null ] [ ] = [ ] ;
1727+ const promptNotifications : [ Error | null , Prompt [ ] | null ] [ ] = [ ] ;
1728+
1729+ // Server with tools.listChanged: true but prompts without listChanged
1730+ const server = new Server ( { name : 'test-server' , version : '1.0.0' } , { capabilities : { tools : { listChanged : true } , prompts : { } } } ) ;
1731+
1732+ server . setRequestHandler ( InitializeRequestSchema , async request => ( {
1733+ protocolVersion : request . params . protocolVersion ,
1734+ capabilities : { tools : { listChanged : true } , prompts : { } } ,
1735+ serverInfo : { name : 'test-server' , version : '1.0.0' }
1736+ } ) ) ;
1737+
1738+ server . setRequestHandler ( ListToolsRequestSchema , async ( ) => ( {
1739+ tools : [ { name : 'tool-1' , inputSchema : { type : 'object' } } ]
1740+ } ) ) ;
1741+
1742+ server . setRequestHandler ( ListPromptsRequestSchema , async ( ) => ( {
1743+ prompts : [ { name : 'prompt-1' } ]
1744+ } ) ) ;
1745+
1746+ const client = new Client (
1747+ { name : 'test-client' , version : '1.0.0' } ,
1748+ {
1749+ listChanged : {
1750+ tools : {
1751+ debounceMs : 0 ,
1752+ onChanged : ( err , tools ) => toolNotifications . push ( [ err , tools ] )
1753+ } ,
1754+ prompts : {
1755+ debounceMs : 0 ,
1756+ onChanged : ( err , prompts ) => promptNotifications . push ( [ err , prompts ] )
1757+ }
1758+ }
1759+ }
1760+ ) ;
1761+
1762+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
1763+
1764+ await Promise . all ( [ client . connect ( clientTransport ) , server . connect ( serverTransport ) ] ) ;
1765+
1766+ // Verify capability state
1767+ expect ( client . getServerCapabilities ( ) ?. tools ?. listChanged ) . toBe ( true ) ;
1768+ expect ( client . getServerCapabilities ( ) ?. prompts ?. listChanged ) . toBeFalsy ( ) ;
1769+
1770+ // Send notifications for both
1771+ await server . notification ( { method : 'notifications/tools/list_changed' } ) ;
1772+ await server . notification ( { method : 'notifications/prompts/list_changed' } ) ;
1773+ await new Promise ( resolve => setTimeout ( resolve , 100 ) ) ;
1774+
1775+ // Tools handler should have been called
1776+ expect ( toolNotifications ) . toHaveLength ( 1 ) ;
1777+ // Prompts handler should NOT have been called (no prompts.listChanged)
1778+ expect ( promptNotifications ) . toHaveLength ( 0 ) ;
1779+ } ) ;
1780+
15611781describe ( 'outputSchema validation' , ( ) => {
15621782 /***
15631783 * Test: Validate structuredContent Against outputSchema
0 commit comments