Scott Brooks

My corner of the internet to share my thoughts and experiences.

What is the best way to provide content to Copilot

Claude conversation about how to use existing data in Copilot

What is the best way to provide content to Copilot

At my work, we have a knowledge base that is primarily a website. We call it AcmeKnowledgebase. Most of the content is stored in a SQL Server database in markdown. There are screenshots (png, jpg) and videos (mp4). It is organized as a page full of books. The user clicks the book they are interested in, and from there they go to the next page that displays all the chapters for the book along the left side of the screen and the content of each chapter displayed on the right. The content for the first chapter is displayed as soon as the page is loaded.

I would like to create a Microsoft Teams app to allow the user to have a conversation with this data. What is the best way to make this content available to an LLM? We use Microsoft Copilot at work and that would be something we prefer to use. One thing that we do not really want to do is ingest all of the AcmeKnowledgebase content into the Microsoft Graph.

Can you suggest how I should make this content available to Copilot running in a Teams application?

Given that your AcmeKnowledgebase content lives in SQL Server and you specifically don't want to ingest everything into Microsoft Graph, you have a solid path forward. Here's the approach I'd recommend.

Build a REST API over your AcmeKnowledgebase data, then expose it as an API Plugin for a Declarative Agent

The core idea is straightforward: you create a thin REST API that sits in front of your SQL Server database, then register it with Microsoft Copilot as an API plugin attached to a declarative agent. This lets Copilot call your API at query time rather than requiring you to push all your content into Graph or SharePoint.

Step 1 — Build a AcmeKnowledgebase REST API. You likely already have some data access layer for your web app. You'd create (or extend) a set of API endpoints that Copilot can call to search and retrieve content. Think along the lines of GET /books, GET /books/{id}/chapters, GET /chapters/{id}/content, and critically a GET /search?q=... endpoint. Since your content is markdown in SQL Server, you'll want to add full-text search if you haven't already — SQL Server's built-in full-text indexing works fine here, or you could stand up Azure AI Search over that same data for better semantic retrieval. The search endpoint is the most important one, because that's how Copilot will find relevant content to ground its answers.

Step 2 — Write an OpenAPI specification. This is what Microsoft's tooling uses to understand what your API can do. You describe each endpoint, its parameters, and — this is crucial — write clear, natural-language descriptions of what each operation does and when it should be called. Copilot's orchestrator uses these descriptions to decide which endpoints to invoke for a given user question. For example, your search endpoint description might say "Search the AcmeKnowledgebase knowledge base for chapters and content matching the user's question about internal processes, tools, or procedures."

Step 3 — Create a Declarative Agent with the API Plugin. Using either Copilot Studio or the Microsoft 365 Agents Toolkit (in VS Code), you create a declarative agent. This is where you define the agent's persona and instructions — something like "You are the AcmeKnowledgebase assistant. Answer questions using only content from the AcmeKnowledgebase knowledge base. Always cite which book and chapter your answer comes from." You then attach your API as an API plugin by pointing it at your OpenAPI spec and configuring authentication. The agent gets published to Teams, and your users can chat with it there.

Step 4 — Handle the media content. For screenshots and videos, your API can return URLs pointing to wherever those assets are hosted (your existing web server, Azure Blob Storage, etc.). Copilot can include these links in its responses, so users can click through to view images or videos. You won't get inline video playback in the chat, but you can give users a direct link.

Why this approach works well for your situation

Your content stays in SQL Server — no duplication into Graph or SharePoint. Copilot queries your API on demand, so updates to AcmeKnowledgebase are immediately reflected. You also maintain full control over what gets returned and how search works. The book/chapter structure you already have maps naturally to API endpoints, and the markdown content is already in a format that LLMs handle well.

A few things to keep in mind

Authentication is the main complexity. Your API needs to support OAuth or API key auth, and you'll configure this when registering the plugin. If your org already uses Azure AD (Entra ID), OAuth with delegated permissions is the cleanest approach.

Also, there's a practical limit on how much text Copilot can process per API call. You'll want your search endpoint to return focused, relevant chunks rather than entire chapters. If some of your chapters are very long, consider breaking the content into smaller sections or returning just the most relevant portions based on the search query. This is where Azure AI Search with semantic ranking could really help — it can pull back the most relevant passages rather than whole documents.

Finally, the Copilot Studio route gives you a no-code/low-code way to iterate on the agent's behavior, while the Agents Toolkit approach gives you more developer control. Given you have a dev team maintaining a web app with SQL Server, the Agents Toolkit path will probably feel more natural.

Sources

Sorry, I should have been more clear. This site is entirely driven by an API (We actually did API first, so anything the client displays comes from the API). It is using OpenAPI and has a Swagger page. It is all written in .Net Core 6. So, we already have all that. One of things I was wondering is if we should consider adding MCP functionality to the existing API. I have seen videos where a developer adds them to an existing WebApi, and it seems pretty easy with the .Net MCP Toolkit (not sure of exact name).

My concern with either approach is how will Copilot query the endpoints? Relevant content could be "buried" in more than one book, and I don't want something to have to hit every endpoint for every request.

So, that may be the main point of my question. How can I provide this content to an LLM (is this a RAG question) without having to "read" it for every request?

You're asking exactly the right question, and yes, this is fundamentally a RAG question. Let me cut straight to the core issue.

The real problem: retrieval, not plumbing

You already have the API — that's great. But the challenge is this: if Copilot (or any LLM) has to figure out which books and chapters to call by browsing your endpoints, it's going to be slow, expensive, and unreliable. Imagine a user asking "what's our policy on escalating customer complaints?" — that answer might span content in a "Customer Service" book, a "Management" book, and an "HR Policies" book. No amount of clever endpoint descriptions in your OpenAPI spec will reliably guide Copilot to call the right three chapters out of potentially hundreds.

What you need is a search layer that sits between the LLM and your content. The LLM sends a query to one endpoint, and that endpoint returns the most relevant chunks of content regardless of which book or chapter they live in. That's RAG.

What I'd recommend

Stand up a vector search index over your AcmeKnowledgebase content. Since you're in the Microsoft ecosystem, Azure AI Search is the natural choice — it supports both traditional full-text and vector (semantic) search, and it integrates well with the rest of your stack. The flow would look like this:

Your SQL Server content gets indexed into Azure AI Search. You'd write a relatively simple indexer that pulls your markdown content, splits it into reasonable chunks (say 500–1000 tokens each, preserving section boundaries where possible), generates embeddings for each chunk using an embedding model (Azure OpenAI's text-embedding-ada-002 or similar), and stores them in the index along with metadata like book name, chapter title, and a URL back to that chapter in your web app.

Then you add one new endpoint to your existing API — something like POST /search that takes a natural language query, runs it against the Azure AI Search index, and returns the top N most relevant chunks with their metadata. This single endpoint is what the LLM calls. It doesn't need to know your book structure, it doesn't need to browse chapters — it just asks a question and gets back the relevant content.

Keeping the index fresh is straightforward since you already control the data layer. You can either run the indexer on a schedule (every few minutes, hourly, whatever matches your update frequency) or trigger reindexing when content changes in your API's write operations. Azure AI Search has built-in indexer support for SQL Server as a data source, so this can be largely configuration rather than code.

MCP vs. API Plugin — which to use

On the MCP question: the .NET library you're thinking of is likely the Model Context Protocol C# SDK (there's an official one from Microsoft/Anthropic). Adding MCP server capabilities to your existing .NET API is indeed pretty straightforward. But here's the practical consideration right now — Microsoft 365 Copilot declarative agents support API plugins via OpenAPI specs, and they recently added MCP support as well (it's in preview). Since you already have a full OpenAPI/Swagger setup, the API plugin route is the path of least resistance for getting into Teams via Copilot.

That said, MCP is worth considering for a couple of reasons. It's becoming a broader standard beyond just Microsoft's ecosystem, so if you ever want to connect your AcmeKnowledgebase to other AI tools (Claude, VS Code Copilot, etc.), you'd have that covered. And the MCP "tools" model maps nicely to what you'd be building — essentially a search_AcmeKnowledgebase tool that any MCP-compatible client can call. You could realistically support both: your existing OpenAPI spec for the Copilot declarative agent, and an MCP server endpoint for broader compatibility. They'd both call the same search index underneath.

The architecture in summary

The key insight is that you're adding one new capability — semantic search — not rearchitecting anything. Your existing API, your SQL Server database, your web app all stay as they are. You add an Azure AI Search index that mirrors your content, one new search endpoint on your API, and a declarative agent in Teams that's instructed to use that search endpoint. A user asks a question in Teams, Copilot calls your search endpoint, gets back the three or four most relevant chunks of content across all books, and uses those to generate an answer with citations.

The indexing is the only part that might feel new if you haven't worked with vector search before, but Azure AI Search handles most of the complexity — especially since it can pull directly from SQL Server.

I apologize again for not being more clear. When we started this application (2016 I think), we wanted an easy way to search content. So, every time a section of content changes, we preserve the prior content (event sourcing type pattern) and add a new record with the user's change/addition. When we add that new record, the "Sync" column is null. Then, about every 5 minutes, a background job looks for all the null values and syncs those records to Azure Search (It has changed names a few times since we started using it, but underneath, it still works about the same).

I just wanted to stress that we already have all of the content in Azure Search.

Oh, that changes things significantly — you're much further along than I was assuming. If you already have the content indexed in Azure AI Search, then the RAG infrastructure is already in place. The remaining question is really just about the "last mile": how do you connect Copilot in Teams to that search index?

You have two clean options, and honestly they aren't mutually exclusive.

Option 1: Add a search endpoint to your existing API and register it as an API plugin. You'd add a single endpoint to your .NET API that takes a query string, hits your Azure AI Search index, and returns the top relevant chunks with metadata. Then you create a declarative agent in Teams that uses your OpenAPI spec (just pointing at that one endpoint, not your whole API). This is the most straightforward path since you already have the API and the Swagger doc. The declarative agent's instructions would tell Copilot to always use that search endpoint to answer questions, cite the book and chapter, and so on.

Option 2: Add MCP support alongside your existing API. You'd expose a search_AcmeKnowledgebase tool via MCP that does the same thing — takes a query, hits Azure AI Search, returns results. The .NET MCP SDK (it's the Microsoft.Extensions.AI ecosystem, with the ModelContextProtocol package) does make this pretty easy to bolt onto an existing Web API project. The advantage here is broader compatibility — any MCP-capable client could use it, not just Copilot. The downside is that MCP support in Microsoft 365 Copilot declarative agents is still in preview, so the API plugin route is more mature and stable for the Teams use case specifically.

What I'd actually do in your position: start with the API plugin approach since it's GA and you already have all the pieces. Add one POST /api/AcmeKnowledgebase/search endpoint that queries Azure AI Search, update your OpenAPI spec to include it with a good description, and wire up a declarative agent. You could have something working in Teams within a day or two. Then if you want broader MCP compatibility later, add that as a second interface to the same underlying search logic — it's the same code underneath, just a different protocol on top.

The key point is that since Azure AI Search is already handling the hard part — figuring out which content is relevant across all your books and chapters — Copilot only ever needs to call that one search endpoint. It never needs to browse your book and chapter endpoints trying to find relevant content. That was your core concern, and your existing search index already solves it.

I was trying to wire Copilot to Azure Search, but I like your idea better of hitting the Search in the API. We already have an endpoint that the Angular front end calls where the user types some text in an input, and we pass that value to the API in a REST call (can't remember if it is a POST or a GET). The code already calls Azure Search and returns the first 50 records it finds.

Just pointing Copilot to what we already have seems like a great idea, and very basic. I am bummed that I didn't think of going that route earlier. But, that is why you are here, right? ;-)

Ha, exactly — sometimes it just takes talking it through with someone to see what's already right in front of you. And honestly, the fact that you built the API-first architecture and already have Azure AI Search wired up means you did all the hard work already. You're literally one declarative agent configuration away from having this working in Teams.

The only thing I'd flag is that your existing endpoint returning 50 results is probably more than you want to send to Copilot. The LLM's context window is finite and you want it focused on the most relevant content, not sifting through 50 records. You might want to either add a parameter to that endpoint to limit the result count (top 5–10 is usually the sweet spot for RAG), or just create a slim variant of the endpoint specifically for the Copilot use case. That way your Angular front end keeps working exactly as it does now with its 50 results for the user to browse, and Copilot gets a tighter set of highly relevant chunks to reason over.

Other than that, you've got everything you need. Go wire it up — this should be a pretty satisfying win given how little work is left.

Dang, it is Sunday, so I should be off work, but this sounds so fun, I almost want to go do it right now. But, I will resist and wait until tomorrow. Thanks for your help.

Ha, I respect the discipline! Enjoy your Sunday — and tomorrow you get to walk into work with a plan that basically amounts to "we just need to point Copilot at what we already built." That's a pretty great Monday morning.

Good luck with it, Scott. Have a good rest of your weekend.