Skip to main content

agents-sdk-typescript

Use when any code imports @microsoft/agents-hosting, @microsoft/agents-hosting-express, or related Agents SDK packages, or when the user is building, configuring, or asking questions about a Microsoft 365 Agents SDK agent in TypeScript. Trigger on questions about environment variables, connection configuration, AgentApplication patterns, OAuth sign-in flows, storage backends, cards, streaming, or local testing with the Agents Playground — even if no code exists yet and the user is planning or asking how to get started.

Source facts

Repository
microsoft/Agents
Last source activity
August 20, 2026 at 19:20
Detected SKILL.md language
English
Stars
1,092
Forks
346

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
agents-sdk-typescript
description
Use when any code imports @microsoft/agents-hosting, @microsoft/agents-hosting-express, or related Agents SDK packages, or when the user is building, configuring, or asking questions about a Microsoft 365 Agents SDK agent in TypeScript. Trigger on questions about environment variables, connection configuration, AgentApplication patterns, OAuth sign-in flows, storage backends, cards, streaming, or local testing with the Agents Playground — even if no code exists yet and the user is planning or asking how to get started.
## Overview The Microsoft 365 Agents SDK builds multichannel agents for Teams, Copilot Studio, and web chat. | Package | Purpose | |---|---| | `@microsoft/agents-hosting` | Core: AgentApplication, CloudAdapter, TurnContext, TurnState, storage | | `@microsoft/agents-hosting-express` | `startServer()` convenience wrapper | | `@microsoft/agents-hosting-storage-blob` | Azure Blob Storage backend | | `@microsoft/agents-hosting-storage-cosmos` | CosmosDB backend | | `@microsoft/agents-hosting-dialogs` | Dialog system | Requires Node 18+. Use `node --env-file .env` (Node 20+) to load environment variables. ## Azure Resources Required **Microsoft Entra App Registration** - `clientId` — Application (client) ID - `clientSecret` — Certificates & secrets - `tenantId` — Directory (tenant) ID **Azure Bot Resource** - Messaging endpoint: `https://<your-host>/api/messages` - Microsoft App ID must match `clientId` Local dev: Use Bot Framework Emulator. No Azure Bot needed until deployment. Leave `clientId` blank to skip auth validation. ## Environment Variables ### Modern format (recommended) Uses `connections__` and `connectionsMap__` prefixes. Double underscores (`__`) separate path segments; `.settings.` is stripped automatically. **Single connection (most common):** ``` connections__serviceConnection__settings__clientId=<your-app-id> connections__serviceConnection__settings__clientSecret=<your-secret> connections__serviceConnection__settings__tenantId=<your-tenant-id> connectionsMap__0__connection=serviceConnection connectionsMap__0__serviceUrl=* ``` **How `connectionsMap` works:** Each entry maps a `serviceUrl` pattern to a named connection. The first matching entry wins. - `serviceUrl=*` — matches any service URL (use as the default/fallback) - `serviceUrl` is treated as a regex for all other values `connectionsMap` can be omitted when there is only one connection — the SDK defaults it to `serviceUrl=*`. **Multiple connections** (e.g. different identities for different channels): ``` connections__mainConn__settings__clientId=<app-id-1> connections__mainConn__settings__clientSecret=<secret-1> connections__mainConn__settings__tenantId=<tenant-id> connections__teamsConn__settings__clientId=<app-id-2> connections__teamsConn__settings__clientSecret=<secret-2> connections__teamsConn__settings__tenantId=<tenant-id> connectionsMap__0__connection=teamsConn connectionsMap__0__serviceUrl=https://smba.trafficmanager.net/.* connectionsMap__1__connection=mainConn connectionsMap__1__serviceUrl=* ``` Optional `audience` field on a map entry restricts matching to activities whose JWT `aud` claim equals that value: ``` connectionsMap__0__connection=teamsConn connectionsMap__0__serviceUrl=* connectionsMap__0__audience=<teams-app-id> ``` **Available connection settings fields:** `clientId`, `clientSecret`, `tenantId`, `authority`, `certPemFile`, `certKeyFile`, `sendX5C`, `connectionName`, `scope` ### Legacy format — backwards compatibility only > **Never use the legacy format for new agents.** It exists solely for backwards compatibility with older BotFramework-based bots. Always use the modern `connections__` format above. ``` clientId=<your-app-id> clientSecret=<your-secret> tenantId=<your-tenant-id> ``` Both `startServer()` and `loadAuthConfigFromEnv()` auto-detect the format. Leave `clientId` blank locally to skip auth. ### OAuth authorization handler variables These control the user sign-in flow configured via `authorization: { [id]: { ... } }`. The `id` is the key used in the authorization options (e.g. `graph`). #### Modern format (recommended) Uses the prefix `AgentApplication__UserAuthorization__Handlers__<id>__Settings__<property>`. Case-insensitive. **AzureBot handler** (default — user OAuth flow via Azure Bot Service): ``` AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName=GraphOAuthConnection AgentApplication__UserAuthorization__Handlers__graph__Settings__title=Sign in with Microsoft AgentApplication__UserAuthorization__Handlers__graph__Settings__text=Please sign in to continue AgentApplication__UserAuthorization__Handlers__graph__Settings__invalidSignInRetryMax=3 AgentApplication__UserAuthorization__Handlers__graph__Settings__enableSso=false # OBO (on-behalf-of) — auto-exchange on routes using exchangeToken() AgentApplication__UserAuthorization__Handlers__graph__Settings__oboConnectionName=OBOConnection AgentApplication__UserAuthorization__Handlers__graph__Settings__oboScopes=https://graph.microsoft.com/.default,Mail.Read # Custom error messages AgentApplication__UserAuthorization__Handlers__graph__Settings__invalidSignInRetryMessage=That code was invalid, please try again. AgentApplication__UserAuthorization__Handlers__graph__Settings__invalidSignInRetryMessageFormat=Please enter the 6-digit code from the sign-in card. AgentApplication__UserAuthorization__Handlers__graph__Settings__invalidSignInRetryMaxExceededMessage=Too many failed attempts. Please try again later. ``` **AgenticUserAuthorization handler** (agent-to-agent, no user prompt): ``` AgentApplication__UserAuthorization__Handlers__myHandler__Settings__type=AgenticUserAuthorization AgentApplication__UserAuthorization__Handlers__myHandler__Settings__scopes=https://graph.microsoft.com/.default AgentApplication__UserAuthorization__Handlers__myHandler__Settings__altBlueprintConnectionName=altConn ``` #### Legacy format (deprecated) > **Do not use the legacy format for new agents.** It exists solely for backwards compatibility. The SDK emits deprecation warnings when legacy variables are detected. | Legacy variable | Modern equivalent | |---|---| | `graph_connectionName` | `...Handlers__graph__Settings__azureBotOAuthConnectionName` | | `graph_connectionTitle` | `...Handlers__graph__Settings__title` | | `graph_connectionText` | `...Handlers__graph__Settings__text` | | `graph_maxAttempts` | `...Handlers__graph__Settings__invalidSignInRetryMax` | | `graph_enableSso` | `...Handlers__graph__Settings__enableSso` | | `graph_obo_connection` | `...Handlers__graph__Settings__oboConnectionName` | | `graph_obo_scopes` | `...Handlers__graph__Settings__oboScopes` | | `graph_messages_invalidCode` | `...Handlers__graph__Settings__invalidSignInRetryMessage` | | `graph_messages_invalidCodeFormat` | `...Handlers__graph__Settings__invalidSignInRetryMessageFormat` | | `graph_messages_maxAttemptsExceeded` | `...Handlers__graph__Settings__invalidSignInRetryMaxExceededMessage` | | `myHandler_type=agentic` | `...Handlers__myHandler__Settings__type=AgenticUserAuthorization` | | `myHandler_scopes` | `...Handlers__myHandler__Settings__scopes` | | `myHandler_altBlueprintConnectionName` | `...Handlers__myHandler__Settings__altBlueprintConnectionName` | Priority when both formats are present: runtime options (code) > modern env vars > legacy env vars. ## Quick Start ```typescript import { startServer } from '@microsoft/agents-hosting-express' import { AgentApplication, MemoryStorage, TurnContext, TurnState } from '@microsoft/agents-hosting' class MyAgent extends AgentApplication<TurnState> { constructor() { super({ storage: new MemoryStorage() }) this.onConversationUpdate('membersAdded', async (ctx: TurnContext) => { await ctx.sendActivity('Hello! Send me a message.') }) this.onActivity('message', async (ctx: TurnContext, state: TurnState) => { let counter: number = state.getValue('conversation.counter') || 0 await ctx.sendActivity(`[${counter++}] You said: ${ctx.activity.text}`) state.setValue('conversation.counter', counter) }) } } startServer(new MyAgent()) ``` Run: `node --env-file .env dist/index.js` ## Server Setup ### Option A: startServer() (preferred) `startServer(agent)` creates an Express app with: - `express.json()` + `authorizeJWT()` middleware - `POST /api/messages` route - Listens on `PORT` env var (default 3978) - **Returns the Express instance** — add extra routes to the return value ```typescript import { startServer } from '@microsoft/agents-hosting-express' const server = startServer(agent) server.get('/health', (_req, res) => res.json({ ok: true })) ``` ### Option B: Manual Express ```typescript import express, { Response } from 'express' import { Request, CloudAdapter, authorizeJWT, loadAuthConfigFromEnv } from '@microsoft/agents-hosting' const authConfig = loadAuthConfigFromEnv() const adapter = new CloudAdapter(authConfig) const app = express() app.use(express.json()) app.use(authorizeJWT(authConfig)) app.post('/api/messages', async (req: Request, res: Response) => { await adapter.process(req, res, async (context) => await agent.run(context)) }) app.listen(process.env.PORT || 3978) ``` ### Proactive Messaging Save a reference during a turn, then use `adapter.continueConversation` from any route. `req.user` comes from `authorizeJWT` middleware. ```typescript import { ConversationReference } from '@microsoft/agents-activity' // During a turn — save the reference const ref = ctx.activity.getConversationReference() conversationReferences[ref.conversation.id] = ref // In a proactive route app.get('/api/notify', async (req, res) => { for (const ref of Object.values(conversationReferences)) { await adapter.continueConversation(req.user!, ref, async (ctx) => { await ctx.sendActivity('Proactive message') }) } res.json({ ok: true }) }) ``` ## Validating Your Configuration ### 1. Validate bot credentials (clientId / clientSecret / tenantId) This tests that your Entra app registration credentials are correct and can authenticate with the Bot Framework. A successful response includes `access_token`; an error response includes `error` and `error_description`. ```bash curl -s -X POST \ "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" \ -d "grant_type=client_credentials\ &client_id=$clientId\ &client_secret=$clientSecret\ &scope=https://api.botframework.com/.default" \ | jq '{token_type, expires_in, error, error_description}' ``` Common errors: - `AADSTS700016` — `clientId` not found in tenant (wrong ID or wrong tenant) - `AADSTS7000215` — invalid `clientSecret` (expired or incorrect) - `AADSTS90002` — `tenantId` not found ### 2. Validate the agent is running and reachable ```bash curl -s -o /dev/null -w "%{http_code}" \ -X POST http://localhost:3978/api/messages \ -H "Content-Type: application/json" \ -d '{}' ``` - `401` — agent is running; JWT auth rejected the empty request (expected — means auth is working) - `000` or connection refused — agent is not running or wrong port - `200` — agent is running with auth disabled (local dev with blank `clientId`) ### 3. Validate an OAuth connection name OAuth connection names (set via `AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName`) can only be tested end-to-end through a real sign-in flow. Use the Azure portal: **Azure Portal → Your Bot Resource → Settings → OAuth Connection Settings → [your connection] → Test Connection** This confirms the connection name matches, the OAuth app has the right scopes, and the redirect URI is configured correctly. ## Local Testing with Agents Playground The Agents Playground lets you test your agent locally without deploying to Azure or configuring a Bot resource. It acts as a mock connector service and channel client. **Install:** ```bash npm install -g agentsplayground ``` ### Recommended package.json scripts Include these scripts when creating a new agent's `package.json`. The `test` script starts the agent and playground together in parallel — no separate terminals needed: ```json "scripts": { "prebuild": "npm ci", "build": "tsc --build", "prestart": "npm run build", "prestart:anon": "npm run build", "start:anon": "node ./dist/index.js", "start": "node --env-file .env ./dist/index.js", "test-tool": "agentsplayground -c emulator", "test": "npm-run-all -p -r start:anon test-tool" } ``` - `npm start` — builds and runs with `.env` credentials - `npm test` — builds, starts the agent without auth (`start:anon`), and launches the playground in parallel. Use this for quick local dev without needing an `.env` file. - `-c emulator` — uses the emulator channel (no auth required). Change to `msteams`, `webchat`, etc. as needed. - Requires `npm-run-all` as a dev dependency: `npm install -D npm-run-all`
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub