Inside a Microsoft 365 Copilot Agent Codebase - How the Teams AI Library Structures Things
There's a moment in most Copilot projects where the business outgrows the low-code tooling. Copilot Studio got the first agent live, everyone's happy, and then someone asks for behaviour the maker canvas can't express - custom orchestration, a specific model, integration logic that's genuinely code-shaped. At that point you're building a custom engine agent, and in the Microsoft world that means the Teams AI Library.
I've watched a few Australian dev teams hit this transition, and the thing that slows them down isn't the AI part. It's that nobody's told them what the codebase actually looks like before they commit. So this post is the tour I give developers before a project kicks off - what's in the scaffold, where your logic goes, and where the sharp edges are. Microsoft's own code basics documentation covers the reference detail; consider this the opinionated companion.
The mental model - it's a web server for conversations
Strip away the branding and a Teams AI Library agent is an event-driven server. If your team has built anything with Express or ASP.NET, the shape is immediately familiar. Messages arrive as activities, your handlers run, you send something back. That's it. The library's job is to hide the Bot Framework plumbing that used to make this genuinely unpleasant - and if you ever built on the old Bot Framework SDK directly, you'll appreciate just how much unpleasantness is being hidden.
That mental model matters because it tells you what skills you need. You don't need "AI engineers" to build one of these. You need developers who are comfortable with TypeScript or C#, understand webhooks and async code, and can be taught the agent-specific parts in a week. That's a much easier hiring conversation for most Australian teams.
What the scaffold gives you
Run the Teams CLI's new-project command (or scaffold through the Agents Toolkit in VS Code) and you get a small, legible project. The pieces that matter:
The entry point. In a TypeScript project this is index.ts, and it does three things: creates an instance of the App class, registers handlers, and starts the server. The App is the centre of gravity for the whole codebase. Everything else hangs off it.
Handlers. You register listeners against activity types. The one you'll live in is the message handler - it receives the incoming activity, and you're handed a context with the message content and a send function to reply with. Other activity types cover things like the app being installed, members joining, or card actions coming back. The docs show two styles here: a minimal style where you register handlers inline with app.on, and a controller style where related handlers are grouped into classes with decorators. For anything beyond a toy, use the controller style. Inline handlers feel fast for the first two days and then turn the entry point into a swamp. We learned that one the usual way.
The app package. Alongside your source sits an appPackage folder with a manifest.json and icons. This is the part web developers consistently underestimate. The manifest is the identity card for your agent - it declares what the agent is, what permissions it wants, where it can be installed, and how Microsoft 365 should present it. It's zipped and uploaded to make your agent exist in Teams and Copilot at all. Half the "my agent won't appear" issues we get called about are manifest issues, not code issues. Treat it as a first-class artefact, version it carefully, and don't let it be the thing one person edits by hand and nobody reviews.
DevTools. The scaffold wires in a DevTools plugin that gives you a local chat interface in the browser, so you can talk to your agent on localhost without deploying anything or standing up a tunnel. This is honestly the best thing in the whole toolkit. The old workflow of deploy-to-test made iteration miserable; being able to hit the agent locally, inspect the activities going back and forth, and debug in place changes the daily experience of building these things. Use it from day one.
Configuration. Environment files hold your app registration IDs, secrets, and model endpoints. Nothing novel, but note that the values differ per environment and the tooling leans on them heavily - keep local, dev and prod cleanly separated or you will, at some point, watch your production agent answer with your dev system prompt.
Where the AI actually plugs in
Notice that nothing above mentioned a language model. That's deliberate, and it's the design decision I like most about the library: the conversational plumbing and the intelligence are separate layers.
The AI side comes in through prompt objects - you configure a ChatPrompt with a model (Azure OpenAI being the usual choice for the enterprises we work with), give it instructions, optionally register functions it can call into your systems, and then invoke it from inside your message handler. Your handler receives the message, hands it to the prompt, and sends back the result.
The separation means you can build and test the whole skeleton - routing, cards, auth, deployment - with a handler that just echoes text, before a single model call exists. It also means swapping models later is a contained change, not a rewrite. Given how fast model pricing and capability have been moving, that containment is worth real money over an agent's lifetime.
This layering is also where your engineering standards live or die. The model call is one line. Everything around it - input validation, logging what the agent said and why, timeouts, fallbacks when the model is slow or down - is ordinary engineering, and it's exactly the stuff that separates a demo from something you'd let loose on staff. Our AI agent development work is mostly this layer, truth be told. The model is rarely the hard part.
Honest assessments before you commit
A few things I'd want to know before starting, having watched teams start without knowing them.
The library is a moving target. Teams AI Library v2 is a substantial improvement over v1, but the churn between versions was real, and samples floating around the internet mix eras freely. When something doesn't compile, check which generation of the library the example you're copying was written for. Pin your versions and upgrade deliberately.
TypeScript is the paved road. C# and Python support exists and is improving, but the TypeScript pivot of the docs is consistently the most complete and the samples land there first. If your shop is .NET-first - plenty of ours are - it's workable, just expect to translate examples occasionally rather than paste them.
The local experience is great; the deployment experience is Azure. DevTools makes the inner loop lovely, but getting to production still means app registrations, bot service resources, permissions and admin consent. Budget real time for the first deployment, and make friends with whoever administers your tenant, because you'll need them more than once.
And the big one: make sure you actually need this. A custom engine agent gives you total control and total responsibility. If the requirement is answering questions over SharePoint content with some guardrails, a declarative agent or a Copilot Studio build gets you there dramatically faster, and our Copilot Studio consultants spend a decent chunk of their time talking clients down from pro-code they don't need. The Teams AI Library is for when the orchestration logic, the model choice, or the integrations genuinely demand code. Choose it because you hit the ceiling, not because developers find it more interesting. We do both, so I have no horse in the race beyond project outcomes.
Where to start
Scaffold the TypeScript sample, run it against DevTools, and read the generated code before writing any. It's a small enough codebase to read in full, and an hour there beats a day of doc-skimming. Then build the dullest possible version of your agent - echo handler, no model - and get it deployed to a test tenant end to end. The deployment pipeline is where the unknown unknowns live, so flush them out while the code is trivial.
Once messages flow in both environments, add the prompt layer and start on the real behaviour. From there it's ordinary iterative software delivery, which is exactly the point of going pro-code in the first place.
If you're weighing up declarative versus custom engine, or you've got a Copilot Studio agent straining at its limits and want a second opinion on whether code is the answer, that's a conversation our Microsoft AI consulting team has every week. Happy to have it with you too.