1+ // SAMPLE: call the official MCP library for .NET
2+
3+ #: package OpenAI @2.2 . * - *
4+ #: package ModelContextProtocol. Core@* - *
5+ #: property PublishAot = false
6+
7+ using ModelContextProtocol. Client ;
8+ using OpenAI . Chat ;
9+
10+ string mcpCommand = Environment . GetEnvironmentVariable ( "MCP_SERVER_COMMAND" ) ! ; // path to a stdio mcp server
11+ string key = Environment . GetEnvironmentVariable ( "OPENAI_KEY" ) ! ;
12+
13+ ChatClient chatClient = new ( "gpt-4.1" , key ) ;
14+
15+ // Create MCP client to connect to the server
16+ IMcpClient mcpClient = await McpClientFactory . CreateAsync ( new StdioClientTransport ( new StdioClientTransportOptions
17+ {
18+ Name = "MyServer" ,
19+ Command = mcpCommand ,
20+ Arguments = [ ] ,
21+ } ) ) ;
22+
23+ // get avaliable tools and add them to completion options
24+ ChatCompletionOptions options = new ( ) ;
25+ await foreach ( McpClientTool chatTool in mcpClient . EnumerateToolsAsync ( ) )
26+ {
27+ options . Tools . Add ( chatTool . AsOpenAIChatTool ( ) ) ;
28+ }
29+
30+ // conversation thread
31+ List < ChatMessage > conversation = new ( )
32+ {
33+ ChatMessage . CreateUserMessage ( "Use an MCP tool" )
34+ } ;
35+
36+ start :
37+ ChatCompletion chatCompletion = chatClient . CompleteChat ( conversation , options ) ;
38+ conversation . Add ( ChatMessage . CreateAssistantMessage ( chatCompletion ) ) ;
39+ switch ( chatCompletion . FinishReason )
40+ {
41+ case ChatFinishReason . ToolCalls :
42+ foreach ( var call in chatCompletion . ToolCalls )
43+ {
44+ var mcpArguments = call . FunctionArguments . ToObjectFromJson < Dictionary < string , object > > ( ) ;
45+ Console . WriteLine ( "Tool call detected, calling MCP server..." ) ;
46+ ModelContextProtocol . Protocol . CallToolResult result = await mcpClient . CallToolAsync ( call . FunctionName , mcpArguments ! ) ;
47+ Console . WriteLine ( $ "tool call result { result . Content [ 0 ] } ") ;
48+ ChatMessage message = result . ToMessage ( call ) ;
49+ conversation . Add ( message ) ;
50+ }
51+ goto start ;
52+ case ChatFinishReason . Stop :
53+ Console . WriteLine ( chatCompletion . Content [ 0 ] . Text ) ;
54+ break ;
55+ default :
56+ throw new NotImplementedException ( ) ;
57+ }
58+
59+ #region TEMPORARY
60+ // this is temporary. all these APIs will endup being in one of the packages used here.
61+ public static class TemporaryExtensions
62+ {
63+ public static ChatMessage ToMessage ( this ModelContextProtocol . Protocol . CallToolResult mcpCallResult , ChatToolCall openaiCall )
64+ {
65+ List < ChatMessageContentPart > parts = new ( ) ;
66+ var sc = mcpCallResult . StructuredContent ;
67+
68+ foreach ( ModelContextProtocol . Protocol . ContentBlock block in mcpCallResult . Content )
69+ {
70+ if ( block is ModelContextProtocol . Protocol . TextContentBlock textContent )
71+ {
72+ parts . Add ( ChatMessageContentPart . CreateTextPart ( textContent . Text ) ) ;
73+ }
74+ else
75+ {
76+ throw new NotImplementedException ( ) ;
77+ }
78+ }
79+ ToolChatMessage message = ChatMessage . CreateToolMessage ( openaiCall . Id , parts ) ;
80+ return message ;
81+ }
82+
83+ // this is in the adapter package; waiting for package to be dropped.
84+ public static ChatTool AsOpenAIChatTool ( this Microsoft . Extensions . AI . AIFunction mcpTool )
85+ {
86+ return ChatTool . CreateFunctionTool (
87+ mcpTool . Name ,
88+ mcpTool . Description ,
89+ BinaryData . FromString ( mcpTool . JsonSchema . GetRawText ( ) ) ) ;
90+ }
91+ }
92+ #endregion
0 commit comments