microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
releases/core

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/samples/McpServer/Program.cs

110lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using McpServer;
5using Microsoft.Teams.Apps;
6using Microsoft.Teams.Apps.Handlers;
7using Microsoft.Teams.Apps.Schema;
8
9WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
10builder.Services.AddTeamsBotApplication();
11// State is a singleton so the same maps are shared between the bot's
12// activity handlers and the MCP tools. Replace with a persistent store for production.
13builder.Services.AddSingleton<State>();
14builder.Services.AddHttpClient<GraphClient>();
15builder.Services
16 .AddMcpServer()
17 .WithHttpTransport()
18 .WithTools<McpTools>();
19
20WebApplication webApp = builder.Build();
21
22TeamsBotApplication bot = webApp.UseTeamsBotApplication();
23State state = webApp.Services.GetRequiredService<State>();
24ILogger<Program> logger = webApp.Services.GetRequiredService<ILogger<Program>>();
25
26
27bot.OnMessage(async (context, cancellationToken) =>
28{
29 string userId = context.Activity.From?.AadObjectId ?? string.Empty;
30 string conversationId = context.Activity.Conversation?.Id ?? string.Empty;
31
32 if (context.Activity.ServiceUrl is not null)
33 state.ServiceUrl = context.Activity.ServiceUrl;
34
35 // cache the personal conversation_id so MCP tools can DM this user later.
36 TeamsConversation? conv = TeamsConversation.FromConversation(context.Activity.Conversation);
37 if (conv?.ConversationType == ConversationType.Personal && !string.IsNullOrEmpty(userId))
38 {
39 state.Conversations[userId] = conversationId;
40 }
41
42 logger.LogInformation(
43 "Received message from user {UserId} in conversation {ConversationId}. Replies to asks now arrive via adaptive card actions.",
44 userId, conversationId);
45 await context.SendActivityAsync("Hi! I'll let you know if I need anything.", cancellationToken);
46});
47
48
49bot.OnAdaptiveCardAction(async (context, cancellationToken) =>
50{
51 AdaptiveCardAction? action = context.Activity.Value?.Action;
52
53 switch (action?.Verb)
54 {
55 case "approval_response":
56 return HandleApprovalResponse(action, state);
57 case "ask_reply":
58 return HandleAskReply(action, state);
59 default:
60 await Task.CompletedTask;
61 return AdaptiveCardResponse.CreateMessageResponse("Unknown action");
62 }
63});
64
65static InvokeResponse HandleApprovalResponse(AdaptiveCardAction action, State state)
66{
67 string? approvalId = TryGetString(action.Data, "approval_id");
68 string? decision = TryGetString(action.Data, "decision");
69
70 if (approvalId is not null
71 && (decision == ApprovalStatus.Approved || decision == ApprovalStatus.Rejected)
72 && state.Approvals.TryGetValue(approvalId, out string? currentDecision)
73 && state.Approvals.TryUpdate(approvalId, decision, currentDecision))
74 {
75 if (state.ApprovalWaiters.TryRemove(approvalId, out TaskCompletionSource<string>? waiter))
76 waiter.TrySetResult(decision);
77 return AdaptiveCardResponse.CreateMessageResponse("Response recorded");
78 }
79
80 return AdaptiveCardResponse.CreateMessageResponse(
81 "Unable to record response. The approval request may be invalid or expired.");
82}
83
84static InvokeResponse HandleAskReply(AdaptiveCardAction action, State state)
85{
86 string? requestId = TryGetString(action.Data, "request_id");
87 string? reply = TryGetString(action.Data, "reply");
88
89 if (requestId is not null
90 && state.PendingAsks.TryGetValue(requestId, out PendingAsk? entry)
91 && entry.Status == AskStatus.Pending)
92 {
93 PendingAsk answered = entry with { Status = AskStatus.Answered, Reply = reply ?? string.Empty };
94 if (state.PendingAsks.TryUpdate(requestId, answered, entry))
95 {
96 if (state.ReplyWaiters.TryRemove(requestId, out TaskCompletionSource<PendingAsk>? waiter))
97 waiter.TrySetResult(answered);
98 return AdaptiveCardResponse.CreateMessageResponse("Thanks for your reply!");
99 }
100 }
101
102 return AdaptiveCardResponse.CreateMessageResponse(
103 "Unable to record reply. The ask may be invalid or expired.");
104}
105
106webApp.MapMcp("/mcp");
107webApp.Run();
108
109static string? TryGetString(Dictionary<string, object>? data, string key)
110 => data is not null && data.TryGetValue(key, out object? value) ? value?.ToString() : null;
111