microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feature/pabot-httpcontext-botid

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/samples/McpServer/Program.cs

111lines · modecode

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