Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Microsoft 365 Agents SDK for TypeScript/Node.js.
.claude/skills/m365-agents-ts/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | — | — |
| case-15 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
| case-21 | ✗→✓ | ▲ Improved | — | — |
| case-20 | ✗→✓ | ▲ Improved | — | — |
Build enterprise agents for Microsoft 365, Teams, and Copilot Studio using the Microsoft 365 Agents SDK with Express hosting, AgentApplication routing, streaming responses, and Copilot Studio client integrations.
bashnpm install @microsoft/agents-hosting @microsoft/agents-hosting-express @microsoft/agents-activity npm install @microsoft/agents-copilotstudio-client
bashPORT=3978 AZURE_RESOURCE_NAME=<azure-openai-resource> AZURE_API_KEY=<azure-openai-key> AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-mini TENANT_ID=<tenant-id> CLIENT_ID=<client-id> CLIENT_SECRET=<client-secret> COPILOT_ENVIRONMENT_ID=<environment-id> COPILOT_SCHEMA_NAME=<schema-name> COPILOT_CLIENT_ID=<copilot-app-client-id> COPILOT_BEARER_TOKEN=<copilot-jwt>
typescriptimport { AgentApplication, TurnContext, TurnState } from "@microsoft/agents-hosting"; import { startServer } from "@microsoft/agents-hosting-express"; const agent = new AgentApplication<TurnState>(); agent.onConversationUpdate("membersAdded", async (context: TurnContext) => { await context.sendActivity("Welcome to the agent."); }); agent.onMessage("hello", async (context: TurnContext) => { await context.sendActivity(`Echo: ${context.activity.text}`); }); startServer(agent);
typescriptimport { azure } from "@ai-sdk/azure"; import { AgentApplication, TurnContext, TurnState } from "@microsoft/agents-hosting"; import { startServer } from "@microsoft/agents-hosting-express"; import { streamText } from "ai"; const agent = new AgentApplication<TurnState>(); agent.onMessage("poem", async (context: TurnContext) => { context.streamingResponse.setFeedbackLoop(true); context.streamingResponse.setGeneratedByAILabel(true); context.streamingResponse.setSensitivityLabel({ type: "https://schema.org/Message", "@type": "CreativeWork", name: "Internal", }); await context.streamingResponse.queueInformativeUpdate("starting a poem..."); const { fullStream } = streamText({ model: azure(process.env.AZURE_OPENAI_DEPLOYMENT_NAME || "gpt-4o-mini"), system: "You are a creative assistant.", prompt: "Write a poem about Apollo.", }); try { for await (const part of fullStream) { if (part.type === "text-delta" && part.text.length > 0) { await context.streamingResponse.queueTextChunk(part.text); } if (part.type === "error") { throw new Error(`Streaming error: ${part.error}`); } } } finally { await context.streamingResponse.endStream(); } }); startServer(agent);
typescriptimport { Activity, ActivityTypes } from "@microsoft/agents-activity"; import { AgentApplication, TurnContext, TurnState } from "@microsoft/agents-hosting"; const agent = new AgentApplication<TurnState>(); agent.onActivity("invoke", async (context: TurnContext) => { const invokeResponse = Activity.fromObject({ type: ActivityTypes.InvokeResponse, value: { status: 200 }, }); await context.sendActivity(invokeResponse); await context.sendActivity("Thanks for submitting your feedback."); });
typescriptimport { CopilotStudioClient } from "@microsoft/agents-copilotstudio-client"; const settings = { environmentId: process.env.COPILOT_ENVIRONMENT_ID!, schemaName: process.env.COPILOT_SCHEMA_NAME!, clientId: process.env.COPILOT_CLIENT_ID!, }; const tokenProvider = async (): Promise<string> => { return process.env.COPILOT_BEARER_TOKEN!; }; const client = new CopilotStudioClient(settings, tokenProvider); const conversation = await client.startConversationAsync(); const reply = await client.askQuestionAsync("Hello!", conversation.id); console.log(reply);
typescriptimport { CopilotStudioWebChat } from "@microsoft/agents-copilotstudio-client"; const directLine = CopilotStudioWebChat.createConnection(client, { showTyping: true, }); window.WebChat.renderWebChat({ directLine, }, document.getElementById("webchat")!);
| File | Contents | | --- | --- | | references/acceptance-criteria.md | Import paths, hosting pipeline, streaming, and Copilot Studio patterns |
| Resource | URL | | --- | --- | | Microsoft 365 Agents SDK | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/ | | JavaScript SDK overview | https://learn.microsoft.com/en-us/javascript/api/overview/agents-overview?view=agents-sdk-js-latest | | @microsoft/agents-hosting-express | https://learn.microsoft.com/en-us/javascript/api/%40microsoft/agents-hosting-express?view=agents-sdk-js-latest | | @microsoft/agents-copilotstudio-client | https://learn.microsoft.com/en-us/javascript/api/%40microsoft/agents-copilotstudio-client?view=agents-sdk-js-latest | | Integrate with Copilot Studio | https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/integrate-with-mcs | | GitHub samples | https://github.com/microsoft/Agents/tree/main/samples/nodejs |
This skill is applicable to execute the workflow or actions described in the overview.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +82 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.