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/GraphClient.cs

67lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Net.Http.Headers;
5using System.Net.Http.Json;
6using System.Text.Json.Serialization;
7using Azure.Core;
8using Azure.Identity;
9
10namespace McpServer;
11
12// App-only Microsoft Graph client. Reuses the bot's AzureAd:TenantId / ClientId /
13// ClientCredentials[0]:ClientSecret to acquire a token for graph.microsoft.com,
14// then calls /users with $search. Requires User.ReadBasic.All (Application) consent.
15public sealed class GraphClient
16{
17 private static readonly TokenRequestContext TokenContext = new(["https://graph.microsoft.com/.default"]);
18 private readonly TokenCredential _credential;
19 private readonly HttpClient _http;
20
21 public GraphClient(IConfiguration config, HttpClient http)
22 {
23 string tenantId = config["AzureAd:TenantId"]
24 ?? throw new InvalidOperationException("AzureAd:TenantId is not configured.");
25 string clientId = config["AzureAd:ClientId"]
26 ?? throw new InvalidOperationException("AzureAd:ClientId is not configured.");
27 string clientSecret = config["AzureAd:ClientCredentials:0:ClientSecret"]
28 ?? throw new InvalidOperationException("AzureAd:ClientCredentials:0:ClientSecret is not configured.");
29
30 _credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
31 _http = http;
32 }
33
34 public async Task<IReadOnlyList<UserMatch>> SearchUsersAsync(
35 string query, int top, CancellationToken cancellationToken)
36 {
37 AccessToken token = await _credential.GetTokenAsync(TokenContext, cancellationToken);
38
39 string search = $"\"displayName:{query}\" OR \"userPrincipalName:{query}\"";
40 string url = "https://graph.microsoft.com/v1.0/users"
41 + $"?$search={Uri.EscapeDataString(search)}"
42 + "&$select=id,displayName,userPrincipalName"
43 + $"&$top={top}";
44
45 using HttpRequestMessage req = new(HttpMethod.Get, url);
46 req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
47 req.Headers.Add("ConsistencyLevel", "eventual");
48
49 using HttpResponseMessage resp = await _http.SendAsync(req, cancellationToken);
50 resp.EnsureSuccessStatusCode();
51
52 GraphUsersResponse? body = await resp.Content.ReadFromJsonAsync<GraphUsersResponse>(
53 cancellationToken: cancellationToken);
54
55 return body?.Value
56 .Select(u => new UserMatch(u.Id, u.DisplayName, u.UserPrincipalName))
57 .ToArray() ?? [];
58 }
59
60 private sealed record GraphUser(
61 [property: JsonPropertyName("id")] string Id,
62 [property: JsonPropertyName("displayName")] string? DisplayName,
63 [property: JsonPropertyName("userPrincipalName")] string? UserPrincipalName);
64
65 private sealed record GraphUsersResponse(
66 [property: JsonPropertyName("value")] List<GraphUser> Value);
67}
68