1
+ import { Client } from '../../client/index.js' ;
2
+ import { StreamableHTTPClientTransport } from '../../client/streamableHttp.js' ;
3
+ import { SSEClientTransport } from '../../client/sse.js' ;
4
+ import {
5
+ ListToolsRequest ,
6
+ ListToolsResultSchema ,
7
+ CallToolRequest ,
8
+ CallToolResultSchema ,
9
+ LoggingMessageNotificationSchema ,
10
+ } from '../../types.js' ;
11
+
12
+ /**
13
+ * Simplified Backwards Compatible MCP Client
14
+ *
15
+ * This client demonstrates backward compatibility with both:
16
+ * 1. Modern servers using Streamable HTTP transport (protocol version 2025-03-26)
17
+ * 2. Older servers using HTTP+SSE transport (protocol version 2024-11-05)
18
+ *
19
+ * Following the MCP specification for backwards compatibility:
20
+ * - Attempts to POST an initialize request to the server URL first (modern transport)
21
+ * - If that fails with 4xx status, falls back to GET request for SSE stream (older transport)
22
+ */
23
+
24
+ // Command line args processing
25
+ const args = process . argv . slice ( 2 ) ;
26
+ const serverUrl = args [ 0 ] || 'http://localhost:3000/mcp' ;
27
+
28
+ async function main ( ) : Promise < void > {
29
+ console . log ( 'MCP Backwards Compatible Client' ) ;
30
+ console . log ( '===============================' ) ;
31
+ console . log ( `Connecting to server at: ${ serverUrl } ` ) ;
32
+
33
+ let client : Client ;
34
+ let transport : StreamableHTTPClientTransport | SSEClientTransport ;
35
+
36
+ try {
37
+ // Try connecting with automatic transport detection
38
+ const connection = await connectWithBackwardsCompatibility ( serverUrl ) ;
39
+ client = connection . client ;
40
+ transport = connection . transport ;
41
+
42
+ // Set up notification handler
43
+ client . setNotificationHandler ( LoggingMessageNotificationSchema , ( notification ) => {
44
+ console . log ( `Notification: ${ notification . params . level } - ${ notification . params . data } ` ) ;
45
+ } ) ;
46
+
47
+ // DEMO WORKFLOW:
48
+ // 1. List available tools
49
+ console . log ( '\n=== Listing Available Tools ===' ) ;
50
+ await listTools ( client ) ;
51
+
52
+ // 2. Call the notification tool
53
+ console . log ( '\n=== Starting Notification Stream ===' ) ;
54
+ await startNotificationTool ( client ) ;
55
+
56
+ // 3. Wait for all notifications (5 seconds)
57
+ console . log ( '\n=== Waiting for all notifications ===' ) ;
58
+ await new Promise ( resolve => setTimeout ( resolve , 5000 ) ) ;
59
+
60
+ // 4. Disconnect
61
+ console . log ( '\n=== Disconnecting ===' ) ;
62
+ await transport . close ( ) ;
63
+ console . log ( 'Disconnected from MCP server' ) ;
64
+
65
+ } catch ( error ) {
66
+ console . error ( 'Error running client:' , error ) ;
67
+ process . exit ( 1 ) ;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Connect to an MCP server with backwards compatibility
73
+ * Following the spec for client backward compatibility
74
+ */
75
+ async function connectWithBackwardsCompatibility ( url : string ) : Promise < {
76
+ client : Client ,
77
+ transport : StreamableHTTPClientTransport | SSEClientTransport ,
78
+ transportType : 'streamable-http' | 'sse'
79
+ } > {
80
+ console . log ( '1. Trying Streamable HTTP transport first...' ) ;
81
+
82
+ // Step 1: Try Streamable HTTP transport first
83
+ const client = new Client ( {
84
+ name : 'backwards-compatible-client' ,
85
+ version : '1.0.0'
86
+ } ) ;
87
+
88
+ client . onerror = ( error ) => {
89
+ console . error ( 'Client error:' , error ) ;
90
+ } ;
91
+ const baseUrl = new URL ( url ) ;
92
+
93
+ try {
94
+ // Create modern transport
95
+ const streamableTransport = new StreamableHTTPClientTransport ( baseUrl ) ;
96
+ await client . connect ( streamableTransport ) ;
97
+
98
+ console . log ( 'Successfully connected using modern Streamable HTTP transport.' ) ;
99
+ return {
100
+ client,
101
+ transport : streamableTransport ,
102
+ transportType : 'streamable-http'
103
+ } ;
104
+ } catch ( error ) {
105
+ // Step 2: If transport fails, try the older SSE transport
106
+ console . log ( `StreamableHttp transport connection failed: ${ error } ` ) ;
107
+ console . log ( '2. Falling back to deprecated HTTP+SSE transport...' ) ;
108
+
109
+ try {
110
+ // Create SSE transport pointing to /sse endpoint
111
+ const sseTransport = new SSEClientTransport ( baseUrl ) ;
112
+ const sseClient = new Client ( {
113
+ name : 'backwards-compatible-client' ,
114
+ version : '1.0.0'
115
+ } ) ;
116
+ await sseClient . connect ( sseTransport ) ;
117
+
118
+ console . log ( 'Successfully connected using deprecated HTTP+SSE transport.' ) ;
119
+ return {
120
+ client : sseClient ,
121
+ transport : sseTransport ,
122
+ transportType : 'sse'
123
+ } ;
124
+ } catch ( sseError ) {
125
+ console . error ( `Failed to connect with either transport method:\n1. Streamable HTTP error: ${ error } \n2. SSE error: ${ sseError } ` ) ;
126
+ throw new Error ( 'Could not connect to server with any available transport' ) ;
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * List available tools on the server
133
+ */
134
+ async function listTools ( client : Client ) : Promise < void > {
135
+ try {
136
+ const toolsRequest : ListToolsRequest = {
137
+ method : 'tools/list' ,
138
+ params : { }
139
+ } ;
140
+ const toolsResult = await client . request ( toolsRequest , ListToolsResultSchema ) ;
141
+
142
+ console . log ( 'Available tools:' ) ;
143
+ if ( toolsResult . tools . length === 0 ) {
144
+ console . log ( ' No tools available' ) ;
145
+ } else {
146
+ for ( const tool of toolsResult . tools ) {
147
+ console . log ( ` - ${ tool . name } : ${ tool . description } ` ) ;
148
+ }
149
+ }
150
+ } catch ( error ) {
151
+ console . log ( `Tools not supported by this server: ${ error } ` ) ;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Start a notification stream by calling the notification tool
157
+ */
158
+ async function startNotificationTool ( client : Client ) : Promise < void > {
159
+ try {
160
+ // Call the notification tool using reasonable defaults
161
+ const request : CallToolRequest = {
162
+ method : 'tools/call' ,
163
+ params : {
164
+ name : 'start-notification-stream' ,
165
+ arguments : {
166
+ interval : 1000 , // 1 second between notifications
167
+ count : 5 // Send 5 notifications
168
+ }
169
+ }
170
+ } ;
171
+
172
+ console . log ( 'Calling notification tool...' ) ;
173
+ const result = await client . request ( request , CallToolResultSchema ) ;
174
+
175
+ console . log ( 'Tool result:' ) ;
176
+ result . content . forEach ( item => {
177
+ if ( item . type === 'text' ) {
178
+ console . log ( ` ${ item . text } ` ) ;
179
+ } else {
180
+ console . log ( ` ${ item . type } content:` , item ) ;
181
+ }
182
+ } ) ;
183
+ } catch ( error ) {
184
+ console . log ( `Error calling notification tool: ${ error } ` ) ;
185
+ }
186
+ }
187
+
188
+ // Start the client
189
+ main ( ) . catch ( ( error : unknown ) => {
190
+ console . error ( 'Error running MCP client:' , error ) ;
191
+ process . exit ( 1 ) ;
192
+ } ) ;
0 commit comments