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/ExtAIBot/McpTools.cs

86lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Extensions.AI;
5using ModelContextProtocol.Client;
6
7namespace ExtAIBot;
8
9// Owns the McpClient lifetime, lists tools at startup, and returns them wrapped
10// with citation extraction so search results populate the CitationCollector.
11sealed class McpToolSet : IAsyncDisposable
12{
13 private readonly McpClient _client;
14 private readonly IList<McpClientTool> _tools;
15 private readonly ILogger<McpToolSet> _logger;
16
17 private McpToolSet(McpClient client, IList<McpClientTool> tools, ILogger<McpToolSet> logger)
18 {
19 _client = client;
20 _tools = tools;
21 _logger = logger;
22 }
23
24 public static async Task<McpToolSet> CreateAsync(ILogger<McpToolSet> logger, CancellationToken cancellationToken = default)
25 {
26 McpClient client = await McpClient.CreateAsync(
27 new HttpClientTransport(new HttpClientTransportOptions
28 {
29 Endpoint = new Uri("https://learn.microsoft.com/api/mcp"),
30 Name = "MSLearn",
31 TransportMode = HttpTransportMode.StreamableHttp
32 }),
33 cancellationToken: cancellationToken);
34
35 IList<McpClientTool> tools =
36 await client.ListToolsAsync(cancellationToken: cancellationToken);
37
38 return new McpToolSet(client, tools, logger);
39 }
40
41 // Returns each MCP tool wrapped so its results feed into the CitationCollector.
42 public IList<AITool> GetTools(CitationCollector citations) =>
43 [.. _tools.Select(t => new CitationCapturingTool(t, citations, _logger))];
44
45 public ValueTask DisposeAsync() => _client.DisposeAsync();
46}
47
48sealed class McpToolSetLifetimeService(ILogger<McpToolSet> logger) : IHostedService
49{
50 private McpToolSet? _value;
51
52 public McpToolSet Value => _value ?? throw new InvalidOperationException("MCP tool set is not initialized.");
53
54 public async Task StartAsync(CancellationToken cancellationToken)
55 {
56 _value = await McpToolSet.CreateAsync(logger, cancellationToken);
57 }
58
59 public async Task StopAsync(CancellationToken cancellationToken)
60 {
61 if (_value is null) return;
62
63 await _value.DisposeAsync();
64 _value = null;
65 }
66}
67
68// Wraps an McpClientTool, delegating all metadata to it while intercepting
69// InvokeCoreAsync to extract citation data from the raw result string.
70file sealed class CitationCapturingTool(McpClientTool inner, CitationCollector citations, ILogger logger)
71 : DelegatingAIFunction(inner)
72{
73 protected override async ValueTask<object?> InvokeCoreAsync(
74 AIFunctionArguments arguments,
75 CancellationToken cancellationToken)
76 {
77 logger.LogInformation("[tool] {Name}({Args})",
78 inner.Name,
79 string.Join(", ", arguments.Select(a => $"{a.Key}={a.Value}")));
80
81 object? result = await inner.InvokeAsync(arguments, cancellationToken);
82 if (result?.ToString() is string text)
83 citations.TryExtract(text);
84 return result;
85 }
86}
87