1+ using Microsoft . Extensions . DependencyInjection ;
2+ using Microsoft . Extensions . Hosting ;
3+ using Microsoft . Extensions . Logging ;
4+ using SuperSocket . MCP ;
5+ using SuperSocket . MCP . Abstractions ;
6+ using SuperSocket . MCP . Extensions ;
7+ using SuperSocket . MCP . Models ;
8+ using SuperSocket . WebSocket . Server ;
9+
10+ namespace McpWebSocketServer
11+ {
12+ /// <summary>
13+ /// MCP Server implementation that communicates over WebSocket connections
14+ /// This uses the SuperSocket.MCP library's command system for proper MCP protocol handling
15+ /// </summary>
16+ class Program
17+ {
18+ static async Task Main ( string [ ] args )
19+ {
20+ Console . WriteLine ( "Starting MCP WebSocket Server..." ) ;
21+ Console . WriteLine ( "This server provides MCP functionality over WebSocket connections." ) ;
22+ Console . WriteLine ( "Connect using: ws://localhost:8080 with sub-protocol 'mcp'" ) ;
23+ Console . WriteLine ( "----------------------------------------" ) ;
24+
25+ var host = WebSocketHostBuilder . Create ( args )
26+ . ConfigureServices ( ( hostCtx , services ) =>
27+ {
28+ // Register MCP services
29+ services . AddSingleton < IMcpHandlerRegistry , McpHandlerRegistry > ( ) ;
30+ services . AddSingleton < McpServerInfo > ( new McpServerInfo
31+ {
32+ Name = "SuperSocket MCP WebSocket Server" ,
33+ Version = "1.0.0" ,
34+ ProtocolVersion = "2024-11-05"
35+ } ) ;
36+
37+ // Register sample tools
38+ services . AddSingleton < IMcpToolHandler , EchoToolHandler > ( ) ;
39+ services . AddSingleton < IMcpToolHandler , MathToolHandler > ( ) ;
40+ services . AddSingleton < IMcpToolHandler , TimeToolHandler > ( ) ;
41+ services . AddSingleton < IMcpToolHandler , WebInfoToolHandler > ( ) ;
42+ } )
43+ . UseMcp ( "mcp" ) // Use MCP with "mcp" sub-protocol - this handles all the command routing
44+ . ConfigureLogging ( ( hostCtx , loggingBuilder ) =>
45+ {
46+ loggingBuilder . AddConsole ( ) ;
47+ loggingBuilder . SetMinimumLevel ( LogLevel . Information ) ;
48+ } )
49+ . Build ( ) ;
50+
51+ await host . RunAsync ( ) ;
52+ }
53+ }
54+
55+ /// <summary>
56+ /// Simple echo tool that returns the input message
57+ /// </summary>
58+ public class EchoToolHandler : IMcpToolHandler
59+ {
60+ public Task < McpTool > GetToolDefinitionAsync ( )
61+ {
62+ return Task . FromResult ( new McpTool
63+ {
64+ Name = "echo" ,
65+ Description = "Echo back the input message via WebSocket" ,
66+ InputSchema = new
67+ {
68+ type = "object" ,
69+ properties = new
70+ {
71+ message = new { type = "string" , description = "Message to echo back" }
72+ } ,
73+ required = new [ ] { "message" }
74+ }
75+ } ) ;
76+ }
77+
78+ public Task < McpToolResult > ExecuteAsync ( Dictionary < string , object > arguments )
79+ {
80+ var message = arguments . TryGetValue ( "message" , out var msg ) ? msg . ToString ( ) : "Hello WebSocket!" ;
81+ return Task . FromResult ( new McpToolResult
82+ {
83+ Content = new List < McpContent >
84+ {
85+ new McpContent { Type = "text" , Text = $ "WebSocket Echo: { message } " }
86+ }
87+ } ) ;
88+ }
89+ }
90+
91+ /// <summary>
92+ /// Math tool that performs basic arithmetic operations
93+ /// </summary>
94+ public class MathToolHandler : IMcpToolHandler
95+ {
96+ public Task < McpTool > GetToolDefinitionAsync ( )
97+ {
98+ return Task . FromResult ( new McpTool
99+ {
100+ Name = "math" ,
101+ Description = "Perform basic math operations (add, subtract, multiply, divide)" ,
102+ InputSchema = new
103+ {
104+ type = "object" ,
105+ properties = new
106+ {
107+ operation = new { type = "string" , description = "The operation to perform" , @enum = new [ ] { "add" , "subtract" , "multiply" , "divide" } } ,
108+ a = new { type = "number" , description = "First number" } ,
109+ b = new { type = "number" , description = "Second number" }
110+ } ,
111+ required = new [ ] { "operation" , "a" , "b" }
112+ }
113+ } ) ;
114+ }
115+
116+ public Task < McpToolResult > ExecuteAsync ( Dictionary < string , object > arguments )
117+ {
118+ try
119+ {
120+ var operation = arguments . TryGetValue ( "operation" , out var op ) ? op . ToString ( ) : "" ;
121+ var a = Convert . ToDouble ( arguments . TryGetValue ( "a" , out var aVal ) ? aVal : 0 ) ;
122+ var b = Convert . ToDouble ( arguments . TryGetValue ( "b" , out var bVal ) ? bVal : 0 ) ;
123+
124+ double result = operation switch
125+ {
126+ "add" => a + b ,
127+ "subtract" => a - b ,
128+ "multiply" => a * b ,
129+ "divide" => b != 0 ? a / b : throw new DivideByZeroException ( "Cannot divide by zero" ) ,
130+ _ => throw new ArgumentException ( $ "Unknown operation: { operation } ")
131+ } ;
132+
133+ return Task . FromResult ( new McpToolResult
134+ {
135+ Content = new List < McpContent >
136+ {
137+ new McpContent { Type = "text" , Text = $ "Result: { a } { operation } { b } = { result } " }
138+ }
139+ } ) ;
140+ }
141+ catch ( Exception ex )
142+ {
143+ return Task . FromResult ( new McpToolResult
144+ {
145+ Content = new List < McpContent >
146+ {
147+ new McpContent { Type = "text" , Text = $ "Error: { ex . Message } " }
148+ } ,
149+ IsError = true
150+ } ) ;
151+ }
152+ }
153+ }
154+
155+ /// <summary>
156+ /// Time tool that provides current server time
157+ /// </summary>
158+ public class TimeToolHandler : IMcpToolHandler
159+ {
160+ public Task < McpTool > GetToolDefinitionAsync ( )
161+ {
162+ return Task . FromResult ( new McpTool
163+ {
164+ Name = "time" ,
165+ Description = "Get the current server time in various formats" ,
166+ InputSchema = new
167+ {
168+ type = "object" ,
169+ properties = new
170+ {
171+ format = new { type = "string" , description = "Time format" , @enum = new [ ] { "iso" , "local" , "utc" , "unix" } }
172+ }
173+ }
174+ } ) ;
175+ }
176+
177+ public Task < McpToolResult > ExecuteAsync ( Dictionary < string , object > arguments )
178+ {
179+ var format = arguments . TryGetValue ( "format" , out var fmt ) ? fmt . ToString ( ) : "iso" ;
180+ var now = DateTime . Now ;
181+ var utcNow = DateTime . UtcNow ;
182+
183+ var timeString = format switch
184+ {
185+ "iso" => utcNow . ToString ( "yyyy-MM-ddTHH:mm:ss.fffZ" ) ,
186+ "local" => now . ToString ( "yyyy-MM-dd HH:mm:ss" ) ,
187+ "utc" => utcNow . ToString ( "yyyy-MM-dd HH:mm:ss UTC" ) ,
188+ "unix" => ( ( DateTimeOffset ) utcNow ) . ToUnixTimeSeconds ( ) . ToString ( ) ,
189+ _ => utcNow . ToString ( "yyyy-MM-ddTHH:mm:ss.fffZ" )
190+ } ;
191+
192+ return Task . FromResult ( new McpToolResult
193+ {
194+ Content = new List < McpContent >
195+ {
196+ new McpContent { Type = "text" , Text = $ "Current time ({ format } ): { timeString } " }
197+ }
198+ } ) ;
199+ }
200+ }
201+
202+ /// <summary>
203+ /// Web information tool that provides details about the WebSocket connection
204+ /// </summary>
205+ public class WebInfoToolHandler : IMcpToolHandler
206+ {
207+ public Task < McpTool > GetToolDefinitionAsync ( )
208+ {
209+ return Task . FromResult ( new McpTool
210+ {
211+ Name = "webinfo" ,
212+ Description = "Get information about the WebSocket connection and server" ,
213+ InputSchema = new
214+ {
215+ type = "object" ,
216+ properties = new { }
217+ }
218+ } ) ;
219+ }
220+
221+ public Task < McpToolResult > ExecuteAsync ( Dictionary < string , object > arguments )
222+ {
223+ var info = new
224+ {
225+ Protocol = "WebSocket" ,
226+ SubProtocol = "mcp" ,
227+ Server = "SuperSocket MCP WebSocket Server" ,
228+ Framework = ".NET 8.0" ,
229+ Transport = "WebSocket over TCP" ,
230+ Features = new [ ] { "Binary frames" , "Text frames" , "Ping/Pong" , "Sub-protocols" , "Extensions" }
231+ } ;
232+
233+ var infoText = $ """
234+ WebSocket MCP Server Information:
235+ - Protocol: { info . Protocol }
236+ - Sub-Protocol: { info . SubProtocol }
237+ - Server: { info . Server }
238+ - Framework: { info . Framework }
239+ - Transport: { info . Transport }
240+ - Features: { string . Join ( ", " , info . Features ) }
241+ """ ;
242+
243+ return Task . FromResult ( new McpToolResult
244+ {
245+ Content = new List < McpContent >
246+ {
247+ new McpContent { Type = "text" , Text = infoText }
248+ }
249+ } ) ;
250+ }
251+ }
252+ }
0 commit comments