Home / Docs / Microsoft 365 Copilot

Microsoft 365 Copilot

Microsoft 365 Copilot

FabrCore.Services.Microsoft365Copilot is a server addon that surfaces your FabrCore agents in Microsoft 365 Copilot, Teams, and other Azure Bot Service channels as a custom engine agent. It hosts the /api/messages endpoint with the Microsoft 365 Agents SDK, validates channel and Entra identity, maps each Microsoft 365 user to a FabrCore principal, provisions their agent on first contact, and streams replies back—without any changes to FabrCore.Host or your agents.

Runs anywhere

Your FabrCore Host can run in Azure, on-premises, or any cloud. Azure Bot Service only needs a public HTTPS route to /api/messages—a dev tunnel works for development.

How It Works

Message flow
Microsoft 365 Copilot / Teams
        │  Activity (JWT from Azure Bot Service)
        ▼
POST /api/messages  ──►  Copilot bridge (Agents SDK)
        │                    │ Entra user ──► FabrCore principal handle
        │                    │ ensure agent ──► per-user agent instance
        │                    ▼
        ◄── streamed reply ── your [AgentAlias] agent on the Orleans silo

Each Microsoft 365 user maps to their own FabrCore principal (Entra object id by default), and each principal gets its own agent instance with isolated chat history, state, and ACL boundary—the same model the rest of FabrCore uses.

Setup

Add the package to your FabrCore server project and wire up two lines:

C# — Program.cs
builder.AddFabrCoreServer(new FabrCoreServerOptions
{
    AdditionalAssemblies = [typeof(MyAgent).Assembly]
});
builder.AddMicrosoft365Copilot();      // after AddFabrCoreServer

var app = builder.Build();
app.UseFabrCoreServer();
app.UseMicrosoft365Copilot();          // maps POST /api/messages
app.Run();

Configuration lives in one Microsoft365Copilot section of fabrcore.json (or appsettings.json):

fabrcore.json
{
  "Microsoft365Copilot": {
    "TenantId": "<entra-tenant-id>",
    "ClientId": "<bot-app-registration-client-id>",
    "ClientSecret": "<bot-app-registration-secret>",
    "Agent": {
      "AgentType": "chat-agent",
      "SystemPrompt": "You are a helpful assistant.",
      "Models": "default"
    },
    "Manifest": {
      "Name": "My FabrCore Agent",
      "Description": "Answers questions using my FabrCore agents.",
      "PublicHostName": "myagents.contoso.com"
    }
  }
}

Agent:AgentType is any [AgentAlias] registered through AdditionalAssemblies. The addon translates this one section into the Microsoft 365 Agents SDK configuration it needs (Connections, ConnectionsMap, AgentApplication); if you define any of those sections natively, the addon leaves them untouched.

On the Azure side you create one Entra app registration and one Azure Bot resource with the Teams channel enabled (Teams also carries Microsoft 365 Copilot traffic), pointing its messaging endpoint at your host. Supported credential types: client secret, certificate, managed identity, federated credentials, and workload identity.

Identity and Principals

Teams and Copilot stamp the user's Entra identity on every activity. The addon turns it into a FabrCore principal handle:

Principal:StrategyHandleUse when
EntraObjectId (default)Entra object idSingle-tenant bots
TenantAndObjectId{tenantId}-{objectId}Multi-tenant bots
UserPrincipalNameUPN from the SSO tokenReadable handles; needs user authorization
ChannelUserId{channelId}-{userId}Dev/test channels only

Every bridged message also carries the raw identity and conversation context in AgentMessage.Args (Microsoft365Copilot:AadObjectId, :TenantId, :UserName, :ConversationId, and more), and AgentMessage.Channel is "m365copilot" so agents can branch on ingress source. Custom mapping is one interface: register your own ICopilotPrincipalResolver before AddMicrosoft365Copilot().

Shared agent option

Set Agent:SharedAgentHandle (for example system:helpdesk) to route every user to one agent instead of per-user instances. Cross-principal messaging is subject to ACL—grant agent.message.allow to the mapped principals.

User SSO and On-Behalf-Of

No sign-in prompt is needed for identity—that comes from the channel. Configure user authorization when agents must call Microsoft Graph or your APIs as the user. Handlers use the Microsoft 365 Agents SDK schema and are forwarded verbatim:

fabrcore.json — SSO with automatic OBO exchange
"UserAuthorization": {
  "PassUserTokenToAgent": true,
  "Handlers": {
    "graph": {
      "Settings": {
        "AzureBotOAuthConnectionName": "<oauth-connection-name>",
        "OBOConnectionName": "ServiceConnection",
        "OBOScopes": ["https://graph.microsoft.com/.default"]
      }
    }
  }
}

With PassUserTokenToAgent enabled, agents and plugins receive the user's access token as Args["Microsoft365Copilot:UserToken"]. The token then flows through FabrCore messaging and any configured monitors—enable it deliberately and scope OBO minimally. The generated app manifest automatically gains the webApplicationInfo SSO section when handlers are configured.

App Package Generation

The addon generates the Microsoft 365 app manifest and uploadable package entirely from configuration—no hand-authored manifest. In Development (or with Manifest:EnableAppPackageEndpoint: true):

EndpointReturns
GET /m365copilot/manifest.jsonManifest (schema v1.22) with copilotAgents.customEngineAgents bound to your bot, conversation starters, and SSO metadata when configured
GET /m365copilot/appPackage.zipManifest + icons, ready to upload in Teams or the Microsoft 365 admin center

Upload for yourself via Teams (Apps → Manage your apps → Upload a custom app) or org-wide via the Microsoft 365 admin center (Settings → Integrated apps). The agent then appears in the Copilot Agents rail (Microsoft 365 Copilot license required) and answers 1:1 Teams chat.

Streaming and Responses

On streaming-capable channels the addon sends a configurable informative update ("Working on it..."), keeps a typing indicator running while the FabrCore agent works, and delivers the reply through the channel streaming protocol with the AI generated label. Feedback (thumbs up/down) buttons are available via Streaming:EnableFeedbackLoop. Other channels receive a single buffered reply.

Local Development

Test the full bridge with no Azure resources using the Microsoft 365 Agents Playground:

fabrcore.json — local dev
"Microsoft365Copilot": {
  "TokenValidation": { "Enabled": false },
  "Agent": { "AgentType": "chat-agent" }
}

With token validation off the endpoint accepts anonymous requests (a startup warning reminds you), and users map by channel user id. To exercise the real Teams/Copilot channels from a dev box, host a dev tunnel and point the Azure Bot's messaging endpoint at it.

Production Checklist

  • Keep TokenValidation:Enabled true (the default) anywhere internet-reachable. Inbound channel JWTs validate under a dedicated scheme that never interferes with your host's own authentication.
  • Register a durable Agents SDK IStorage (for example blob storage) before AddMicrosoft365Copilot() when scaling out or using SSO—sign-in and turn state default to memory.
  • Prefer certificate or managed-identity credentials over client secrets, and keep fabrcore.json out of source control.
  • Use SQL or Azure Orleans clustering so per-user agents survive restarts.
  • Replace the generated placeholder icons via Manifest:ColorIconPath / Manifest:OutlineIconPath before publishing.
Distributed skill

The FabrCore repository ships a fabrcore-microsoft365copilot skill under docs/skills with copyable configuration templates, an Azure provisioning script, and deep-dive references for Azure Bot setup and Entra SSO.

Documentation