@cuylabs/agent-a365-tooling
v4.4.0
Published
Microsoft Agent 365 tooling adapter for @cuylabs/agent-core turn-scoped MCP tools
Maintainers
Readme
@cuylabs/agent-a365-tooling
Microsoft Agent 365 tooling adapter for @cuylabs/agent-core.
This package exposes a turn-scoped tool provider factory and an optional Agent 365 chat-history middleware. On each chat turn, the provider uses Microsoft's Agent 365 tooling SDK to discover MCP servers for the active Microsoft 365 turn, connects those servers through agent-core's MCP manager, and returns MCP tools for that turn only. The middleware can submit recent agent-core chat history to Microsoft's Agent 365 real-time threat protection registration endpoint.
Use this when your agent is hosted behind Microsoft 365 / Agent 365 and should
use the MCP tool servers configured for the current tenant, user, and agent
identity. Do not use it for static MCP servers known at startup; use
agent-core MCP config for those.
What It Gives You
- Agent 365 MCP server discovery per Microsoft 365 turn.
- Microsoft SDK token exchange and per-server bearer headers.
- Agent 365 platform headers for real Microsoft-hosted MCP servers.
- Turn-scoped MCP tools that are cleaned up after the turn.
- Opt-in Agent 365 chat-history submission for Microsoft's real-time threat protection registration API.
- A Microsoft-specific adapter without importing Microsoft SDK types into
agent-core.
Install
pnpm add @cuylabs/agent-a365-tooling @microsoft/agents-a365-toolingIf you use the default ambient TurnContext lookup, also use
@cuylabs/agent-channel-m365 and configure @microsoft/agents-hosting in the
host app.
Usage
import { createAgent } from "@cuylabs/agent-core";
import { createA365ToolingTurnToolProvider } from "@cuylabs/agent-a365-tooling";
const agent = createAgent({
model,
turnToolProviders: [
createA365ToolingTurnToolProvider({
authorization,
authHandlerName: "agentic",
toolOptions: {
orchestratorName: "dory",
},
}),
],
});By default the provider reads the active TurnContext from
currentM365TurnContext() in @cuylabs/agent-channel-m365. For tests or custom
hosts, pass getTurnContext.
createA365ToolingTurnToolProvider({
authorization,
authHandlerName: "agentic",
getTurnContext: () => myTurnContext,
});Chat History Submission
Microsoft's RTP API is developer-invoked; this package does not automatically intercept every conversation. Register the middleware only when the host should submit chat history for Microsoft Agent 365 analysis.
import { createAgent } from "@cuylabs/agent-core";
import {
createA365ChatHistoryMiddleware,
createA365ToolingTurnToolProvider,
} from "@cuylabs/agent-a365-tooling";
const agent = createAgent({
model,
turnToolProviders: [
createA365ToolingTurnToolProvider({
authorization,
authHandlerName: "agentic",
}),
],
middleware: [
createA365ChatHistoryMiddleware({
toolOptions: {
orchestratorName: "dory",
},
}),
],
});The middleware defaults to timing: "after-turn", limit: 20, and
maxContentChars: 32000. It reads history lazily from agent-core's lifecycle
context, converts text content to Microsoft's
{ id, role, content, timestamp } shape, keeps the most recent converted
messages within the content budget, and calls sendChatHistory even when the
converted history is []. Empty arrays are intentional: Microsoft uses the
current TurnContext.activity as the message being registered, with
chatHistory as context.
On onChatEnd, agent-core resolves this lazy history from the completed turn
before any automatic context compaction rewrites the visible session history.
Tool messages are included by default to match Microsoft's standard-message
expectation. Set includeTools: false only when the host has a privacy or
compliance reason to filter tool outputs; doing so can reduce RTP context.
For non-M365 hosts, missing TurnContext is skipped by default:
createA365ChatHistoryMiddleware({
onMissingTurnContext: "skip", // "skip" | "warn"
getTurnContext: () => myTurnContext,
});If another onChatEnd hook must run after the submission attempt, place it
later in the middleware array. The captured history leaf is fixed before
automatic compaction and before onChatEnd hooks run. The Microsoft SDK returns
transport success/failure only; threat verdicts are not returned inline through
sendChatHistory.
Submission failures are non-blocking by design; use failureMode: "warn" to
log them or failureMode: "ignore" to silence them.
Concept Docs
The package docs are split by concern:
| Doc | What it explains |
| --- | --- |
| docs/architecture.md | The four-layer split between agent-core, M365 channel ingress, Microsoft's A365 tooling SDK, and this adapter. |
| docs/agent-core-mcp.md | How this package uses agent-core turnToolProviders and how that differs from static agent-core MCP config. |
| docs/microsoft-a365-tooling.md | What Microsoft's Agent 365 tooling SDK owns: discovery, dev manifest mode, gateway calls, and auth headers. |
| docs/lifecycle-and-limits.md | Per-turn lifecycle, cleanup, latency, filtering, connector replacement, and v0 limits. |
Start with docs/README.md if you want the reading order.
Required Host Inputs
| Option | Meaning | Typical source |
| --- | --- | --- |
| authorization | Microsoft Agents SDK Authorization object used for token exchange | Your M365 host application / @microsoft/agents-hosting setup |
| authHandlerName | Name of the auth handler configured for Agent 365 token exchange | Your Microsoft Agents SDK app configuration |
| getTurnContext | Optional resolver for the active Microsoft TurnContext | Defaults to @cuylabs/agent-channel-m365 ambient context |
| authToken | Optional pre-resolved gateway token | Local development or specialized hosts; normally omitted in production |
| toolOptions | Optional metadata such as orchestratorName | Host/application configuration |
In production, prefer omitting authToken so Microsoft's SDK performs token
exchange for the active turn. In local dev-manifest mode, examples can pass a
fake decoded token because the SDK reads ToolingManifest.json instead of
calling the cloud gateway.
Open Microsoft Questions
These do not block the v0 adapter, but they matter for production policy:
- Does
/agents/real-time-threat-protection/chat-messagerequire anAuthorizationheader in production? Microsoft's current SDK call path passesundefinedas the auth token. - What is the server-side HTTP body size cap for
chatHistory, and does Microsoft recommend a client-side history limit? - Are retries with the same
activity.id/messageIdguaranteed idempotent? - Should post-turn
chatHistoryinclude the current user/assistant/tool messages, or only messages before the current activity?
What This Does Not Do
This package is not a channel, not an LLM provider, and not a replacement for
agent-core's MCP engine. It does not implement cross-turn MCP pooling,
mid-turn token refresh, or inline threat blocking. sendChatHistory is a
submit/register call; hosts should not treat it as a synchronous threat verdict.
Examples
Runnable examples live in the repository under
packages/agent-a365-tooling/examples.
They are intentionally not included in the npm tarball because the example
tsconfig.json and ToolingManifest.json are monorepo-local development
harness files.
Start with examples/01-dev-manifest.ts; it uses Microsoft's local
ToolingManifest.json path and an in-process MCP fixture, so it verifies the
provider without Azure.
