Table of Contents

Introduction

Hey everyone!

You can find all the C# code samples here: MicrosoftAgentFramework GitHub Repo

Before you read this post about Foundry IQ MCP server and Foundry IQ HTTP in Microsoft Agent Framework, I encourage you to get familiar with these posts too:

What if you could set up a fully production-ready RAG layer for your Microsoft Agent Framework within… not days, not hours, but minutes?

When you start building AI agents, the biggest challenge is always how to give them the right data. Usually, you have to create a custom data pipeline, generate vectors, and set up a search index. When you build multiple agents, you quickly realize you are writing the exact logic over and over again.

There is a much cleaner way. In this post, I will show you how to connect secure company data to your agents with a simple, repeatable workflow.

We will look at how to build a Microsoft Agent Framework RAG setup using two different methods. First, we will connect to a Knowledge Base HTTP Retrieve endpoint using the Foundry IQ HTTP API, which gives you absolute control over your requests. Second, we will look at the MCP protocol path, where we can pull knowledge_base_retrieve tool from Knowledge Base MCP Server.

Foundry IQ in a nutshell

To understand why this setup is so fast, we need to look at how the resources are connected behind the scenes.

You can actually create a standalone Knowledge Base directly inside your Azure AI Search service, and it does not have to be connected to your Microsoft Foundry project at all.

A screenshot of the Azure Portal showing the Azure AI Search service. The left menu highlights the Agentic Retrieval section with the Knowledge Bases tab selected, showing a specific knowledge base named kb-microsoft-agent-framework-rag in the main list.

However, the exact moment you link that standalone Knowledge Base to a project inside the Microsoft Foundry portal (ai.azure.com), it becomes Foundry IQ.

A screenshot of the Microsoft Foundry portal interface. The left navigation panel shows the Knowledge menu selected, and the main page displays the Knowledge Foundry IQ dashboard highlighting the active kb-microsoft-agent-framework-rag data connection.

In simple terms, Foundry IQ is just an Azure AI Search knowledge base that is connected to a Foundry project. It serves as a smart, central data layer for your AI agents.

At its core, Foundry IQ organizes your data using two main concepts:

  • Knowledge Base: This is a central container for your settings. It defines how your data is searched. For example, it chooses which AI model to use, the reasoning effort level, and the output mode (such as raw text chunks or a summarized natural language answer).
  • Knowledge Source: This defines where your data comes from. It is a connection to a specific data provider. It does not have to be a folder with files; a knowledge source can be an Azure Blob Storage container, a live Web Search, an MCP endpoint, or even an existing search index that you have already optimized. You can add multiple different knowledge sources to a single knowledge base, or reuse them across different bases to save time.

Knowledge sources are split into two main types:

A pop-up window in the portal titled Choose a knowledge type. It shows different cards for connecting data sources, including options like Azure Blob Storage, Web search, Azure SQL, Fabric IQ, and a preview option for an MCP Server.

1. Remote Sources

These connect directly to outside platforms (like web search or an external protocol) to pull live data when a user asks a question. The data is never copied or stored directly inside your search service.

2. Indexed Sources

When you connect an indexed source like an Azure Blob Storage container, Foundry IQ automatically builds all the search infrastructure for you inside Azure AI Search. Behind the scenes, it automatically sets up four important pieces:

  • The target Search Index
  • The Indexer pipeline to look for new data
  • The Data Source connection settings
  • A custom Skillset configuration

By using the standard settings, this automatically created skillset connects straight to Azure’s Content Understanding Service. It automatically handles difficult tasks like parsing complex layouts, creating vector embeddings, and splitting multi page PDFs. You get a complete Microsoft Agent Framework RAG data layer without writing any custom ingestion code at all.

Calling Knowledge Base via Retrieve HTTP endpoint

Once your data is successfully ingested into the platform, the first method for integrating it into the Microsoft Agent Framework RAG loop is by using a direct HTTP REST pattern.

Azure AI Search introduces a dedicated /retrieve endpoint specifically designed to interface with configured Knowledge Bases. In a standard .NET application, you can consume this endpoint by instantiating the KnowledgeBaseRetrievalClient available within the Azure.Search.Documents SDK.

When configuring this pipeline, I highly recommend avoiding API keys entirely. Instead, protect your infrastructure by using DefaultAzureCredential (or a more specific type like ManagedIdentityCredential) to establish a secure, token based connection. Simply grant your application’s Entra ID identity the Search Index Data Reader RBAC role over your search resource (more about that approach here).

To expose this capability cleanly to your local LLM orchestrator, you wrap the client call inside a native tool using the AIFunctionFactory.Create primitive from Microsoft.Extensions.AI.

public class PullContextViaHttpExample
{
    private readonly AIAgent _mafAgent;
    private readonly KnowledgeBaseRetrievalClient _knowledgeBaseRetrievalClient;

    public PullContextViaHttpExample()
    {
        var credential = new DefaultAzureCredential();
        var openAiClient = new AzureOpenAIClient(new Uri(Environment.GetEnvironmentVariable("AZURE_OPEN_AI_RESPONSES_CLIENT_URI")!), credential);

        var responesClient = openAiClient
            .GetResponsesClient()
            .AsIChatClientWithStoredOutputDisabled(Environment.GetEnvironmentVariable("AZURE_OPEN_AI_MODEL_DEPLOYMENT_NAME")!);

        _knowledgeBaseRetrievalClient = new KnowledgeBaseRetrievalClient(
            new Uri(Environment.GetEnvironmentVariable("AZURE_AI_SEARCH_URI")!),
            knowledgeBaseName: Environment.GetEnvironmentVariable("AZURE_AI_SEARCH_KNOWLEDGE_BASE")!,
            credential);

        _mafAgent = new ChatClientAgent(responesClient, new ChatClientAgentOptions()
        {
            Name = "Agent powered by Foundry IQ data via HTTP",
            ChatOptions = new ChatOptions
            {
                Instructions = "You are a helpful agent which provides useful information about Contoso and returns references to data source if provided",
                Reasoning = new ReasoningOptions() { Effort = ReasoningEffort.None, Output = ReasoningOutput.None },
                Tools = [AIFunctionFactory.Create(GetDataFromKnowledgeBaseAsync)]
            }
        });
    }
}

This is how the search method looks like (you can find the full code in the GitHub repo, it’s trimmed for brevity here):

[Description("Searches for information about Contoso's Microsoft cloud architecture, including services, configurations, and design decisions.")]
public async Task<string> GetDataFromKnowledgeBaseAsync([Description("The search query used to retrieve relevant information about MSFT cloud architecture for Contoso from the knowledge base.")] string query)
{
    var outputMode = KnowledgeRetrievalOutputMode.ExtractiveData;

    var kbRetrievalRequest = new KnowledgeBaseRetrievalRequest()
    {
        Intents =
        {
            new KnowledgeRetrievalSemanticIntent(query)
        },
        KnowledgeSourceParams =
        {
            // you can customize various knowledge source specific settings here
        },
        OutputMode = outputMode
    };

    var result = await _knowledgeBaseRetrievalClient.RetrieveAsync(kbRetrievalRequest);
    
	return FormatResults(result);
}

The Runtime Customization Advantage

The biggest architectural incentive to choose the Foundry IQ HTTP retrieval path is absolute runtime control. Because you are manually formulating the request object in C#, you can dynamically override the configuration parameters on a per-chat-turn basis. For instance, you can programmatically inject custom filters at runtime using a FilterAddOn. This is an absolute necessity when enforcing user-specific security trimming or tenancy boundaries across a shared corporate data index (unless you use Document Level Access in Azure AI Search: A Complete Guide to Secure RAG).

When your application executes a retrieval call against this endpoint, the returning payload is cleanly structured into three distinct data segments:

Payload ComponentArchitectural Purpose
responseThe raw text results containing short-lived inline citation tokens (e.g., [ref_id: 0]).
activityExecution metadata detailing internal query planning, selected sources, and automatic query expansions or rewrites.
referencesThe concrete source document chunks matching the generated reference tracking IDs.

Calling Knowledge Base via MCP Server tool

The second approach removes the need to write custom REST client orchestration code by leveraging the Model Context Protocol (MCP).

Every single time you provision an active Knowledge Base inside the platform, the underlying cloud infrastructure automatically provisions a fully compliant, native MCP Server with a single knowledge_base_retrieve tool. You do not need to host, manage, or deploy any separate server infrastructure to use it.

To connect your agent pipeline to this protocol layer, you use the official ModelContextProtocol NuGet package extensions. You instantiate an McpClient over a standard network transport channel, directing it explicitly to the /mcp extension path of your Azure AI Search service URI.

private async Task InitializeAgentAsync()
{
    if (_mafAgent != null) return;

    var mcpUrl = $"{Environment.GetEnvironmentVariable("AZURE_AI_SEARCH_URI")!}/knowledgebases/{Environment.GetEnvironmentVariable("AZURE_AI_SEARCH_KNOWLEDGE_BASE")!}/mcp?api-version=2026-04-01";

    var httpClient = new HttpClient();
    var tokenResult = await new DefaultAzureCredential().GetTokenAsync(new TokenRequestContext(["https://search.azure.com/.default"]));
    httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokenResult.Token);

    var clientTransport = new HttpClientTransport(new HttpClientTransportOptions
    {
        Endpoint = new Uri(mcpUrl),
        Name = "ContosoSearchKBMcpServer"
    }, httpClient, ownsHttpClient: true);

    _mcpClient = await McpClient.CreateAsync(clientTransport);

    var discoveredMcpTools = await _mcpClient.ListToolsAsync();
    var retrieveTool = discoveredMcpTools.Single() as AIFunction;
    retrieveTool = new DescriptionOverridingFunction(retrieveTool, "Searches for information about Contoso's Microsoft cloud architecture, including services, configurations, and design decisions");

	var responsesClient = new AzureOpenAIClient(new Uri(Environment.GetEnvironmentVariable("AZURE_OPEN_AI_RESPONSES_CLIENT_URI")!), new DefaultAzureCredential())
		.GetResponsesClient()
		.AsIChatClientWithStoredOutputDisabled(Environment.GetEnvironmentVariable("AZURE_OPEN_AI_MODEL_DEPLOYMENT_NAME")!)
		.AsBuilder()
		.Use(inner => new InspectingChatClient(inner))
		.Build();

    _mafAgent = new ChatClientAgent(responsesClient, new ChatClientAgentOptions()
    {
        Name = "Agent powered by Foundry IQ data via MCP",
        ChatOptions = new ChatOptions
        {
            Instructions = "You are an expert cloud architect for Contoso. Always leverage the search_knowledge_base_retrieve tool to fetch real-world data files before answering.",
            Reasoning = new ReasoningOptions() { Effort = ReasoningEffort.None, Output = ReasoningOutput.None },
            Tools = [retrieveTool]
        }
    });
}

Optimizing with the Decorator Pattern

When you initialize the session and call ListToolsAsync(), the server automatically exposes its internal retrieval pipeline as a standalone, “ready to use tool”. However, the default tool description generated by the server is naturally generic.

To ensure the LLM understands exactly when to invoke the tool, I highly recommend using a decorator pattern. By building a lightweight class that inherits from DelegatingAIFunction, you can programmatically override the tool’s description property to make it highly domain-specific. This significantly boosts tool calling reliability during complex multi-turn chats.

internal class DescriptionOverridingFunction(AIFunction innerFunction, string customDescription) 
    : DelegatingAIFunction(innerFunction)
{
    public override string Description => customDescription;
}

Using the MCP server path completely redefines how your agent interacts with data. The protocol acts like an architectural “USB driver”. Instead of writing custom parsing, data binding, and manual payload mapping code inside your application layer, your agent simply discovers the tool over the protocol transport bridge and executes it natively.

Summary

Choosing between a direct HTTP REST connection and a native MCP server implementation comes down to a clear architectural trade-off between execution control and deployment simplicity.

RAG Protocol Decision Matrix

Architectural DimensionRetrieve HTTP Endpoint PathNative MCP Server Path
Implementation ComplexityRequires manual client orchestration and SDK code.Purely plug-and-play discovery via protocol primitives.
Runtime ControlHigh. Change parameters, reasoning effort, or modes on the fly.Fixed. Relies primarily on the backend server configuration schema.
Filtering FlexibilityDynamic. Easily append individual runtime queries via a filterAddOn.Static. Handled strictly through server-side knowledge base configurations.
Payload ExtractionGrants explicit, granular access to raw response, activity, and references blocks.Returns a standardized, pre-packaged text content block directly to the tool loop.

The Architectural Verdict: If you are building highly complex systems that demand dynamic, multitenant security filtering or per-request behavioral tweaks, choose the Foundry IQ HTTP endpoint path. If you want to build a rapid, heavily decoupled proof of concept that instantly connects diverse enterprise data to the Microsoft Agent Framework without writing boilerplate data plumbing, leverage the built-in Foundry IQ MCP Server.

Thanks for reading and see you in the next post!

Categorized in:

Agents, RAG,