microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
kavin/agents-sdk-interop

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/samples/McpServer/GraphClient.cs

66lines · modecode

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