Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and inject them as `AIContextProviders` so the agent can discover and use them a

- Connecting to a Foundry toolbox's MCP endpoint via Streamable HTTP transport
- Injecting a fresh Azure AI bearer token (`https://ai.azure.com/.default`) on every MCP request
- Using `AgentSkillsProviderBuilder.UseMcpSkills(client)` to discover skills from the toolbox
- Using `AgentSkillsProviderBuilder.UseMcpSkills(client)` to discover skills from the toolbox through `skills/list`
- Injecting the discovered skills into `AIProjectClient.AsAIAgent(...)` via `AIContextProviders`
- Auto-approving skill tools with `UseToolApproval` and `AgentSkillsProvider.AllToolsAutoApprovalRule`

Expand All @@ -19,7 +19,7 @@ for a walkthrough of the full approval flow.
## Prerequisites

- A Microsoft Foundry project with a toolbox already configured
- The toolbox MCP endpoint must expose `skill://index.json` with `skill-md` entries (SEP-2640). If the resource is absent, the sample runs but the skills provider will be empty.
- The toolbox MCP endpoint must declare the `io.modelcontextprotocol/skills` extension and implement `skills/list` and resource reads for the advertised skills. If the extension is not declared, the skills provider will be empty.
- An authenticated Azure identity (for example, sign in with `az login`)

Set the following environment variables:
Expand Down
3 changes: 1 addition & 2 deletions dotnet/samples/02-agents/AgentProviders/foundry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Some samples require extra tool-specific environment variables. See each sample
| [Local MCP](./Agent_Step23_LocalMCP/) | Local MCP client with HTTP transport |
| [Code interpreter file download](./Agent_Step24_CodeInterpreterFileDownload/) | Download container files generated by code interpreter |
| [Foundry toolbox via MCP](./Agent_Step25_FoundryToolboxMcp/README.md) | Use a Foundry Toolbox from a non-hosted agent via its MCP endpoint |
| [Foundry toolbox MCP skills](./Agent_Step26_FoundryToolboxMcpSkills/README.md) | Use a Foundry Toolbox with MCP-based skills discovery (SEP-2640) via AIContextProviders |
| [Foundry toolbox MCP skills](./Agent_Step26_FoundryToolboxMcpSkills/README.md) | Use a Foundry Toolbox with MCP Skills extension discovery through `skills/list` via AIContextProviders |

## Running the samples

Expand All @@ -87,4 +87,3 @@ dotnet run --project .\Agent_Step01_Basics

If you want to exercise the full create-run-delete lifecycle, run `Agent_Step00_FoundryAgentLifecycle`.


338 changes: 245 additions & 93 deletions dotnet/src/Microsoft.Agents.AI.Mcp/Skills/AgentMcpSkill.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
Expand All @@ -12,48 +11,36 @@
namespace Microsoft.Agents.AI;

/// <summary>
/// An <see cref="AgentSkillResource"/> backed by content fetched from an MCP server.
/// A supporting resource for a skill discovered through <c>skills/list</c>.
/// </summary>
/// <remarks>
/// The <see cref="ReadResourceResult"/> is fetched eagerly by <see cref="AgentMcpSkill.GetResourceAsync"/>
/// at construction time; <see cref="ReadAsync"/> extracts the content from the result.
/// </remarks>
internal sealed class AgentMcpSkillResource : AgentSkillResource
{
private readonly ReadResourceResult _result;
private readonly ResourceContents _content;

/// <summary>
/// Initializes a new instance of the <see cref="AgentMcpSkillResource"/> class with a pre-fetched result.
/// Initializes a supporting resource with verified content fetched from the MCP server.
/// </summary>
/// <param name="name">The resource name (e.g. a relative path or identifier).</param>
/// <param name="result">The result returned by the MCP server's <c>resources/read</c> request.</param>
/// <param name="name">The resource's relative name.</param>
/// <param name="content">The resource content that has passed size and digest verification.</param>
/// <param name="description">An optional description of the resource.</param>
public AgentMcpSkillResource(string name, ReadResourceResult result, string? description = null)
public AgentMcpSkillResource(string name, ResourceContents content, string? description = null)
: base(Throw.IfNullOrWhitespace(name), description)
{
this._result = Throw.IfNull(result);
this._content = Throw.IfNull(content);
}

/// <inheritdoc/>
/// <returns>
/// A <see cref="DataContent"/> when the resource contains binary content, a <see cref="string"/> when
/// it contains text, or <see langword="null"/> when the server returned no content blocks.
/// Binary content as <see cref="DataContent"/>, text as a <see cref="string"/>,
/// or <see langword="null"/> for empty text.
/// </returns>
public override Task<object?> ReadAsync(IServiceProvider? serviceProvider = null, CancellationToken cancellationToken = default)
{
BlobResourceContents? blob = this._result.Contents.OfType<BlobResourceContents>().FirstOrDefault();
if (blob is not null)
return Task.FromResult<object?>(this._content switch
{
return Task.FromResult<object?>(blob.ToAIContent());
}

string text = string.Join("\n", this._result.Contents.OfType<TextResourceContents>().Select(c => c.Text));

if (text.Length == 0)
{
return Task.FromResult<object?>(null);
}

return Task.FromResult<object?>(text);
TextResourceContents text => text.Text.Length == 0 ? null : text.Text,
BlobResourceContents binary => binary.ToAIContent(),
_ => throw new InvalidOperationException("Unsupported listed skill content."),
});
}
}
Loading
Loading