dev.debug-test-ts
purpose
Developer tools, local debugging, DevTools plugin, sideloading, tunneling, ConsoleLogger configuration, and build verification for Teams SDK v2.
rules
- Always include
DevtoolsPluginfrom@microsoft/teams.devin thepluginsarray during local development. It provides a web-based DevTools UI, WebSocket-based real-time activity inspection, and message replay capabilities. github.com/microsoft/teams.ts -- dev - The DevTools UI runs at
http://localhost:{PORT+1}/devtools(defaulthttp://localhost:3979/devtools). It starts automatically when the app starts withDevtoolsPluginregistered. Open it in a browser to inspect inbound and outbound activities. github.com/microsoft/teams.ts -- dev - The bot endpoint is
http://localhost:{PORT}/api/messages(defaulthttp://localhost:3978/api/messages). This is the URL that Teams (or the Bot Framework Emulator) sends activities to. For Azure deployment, update the messaging endpoint tohttps://your-domain/api/messages. github.com/microsoft/teams.ts - Run locally with
npm run devwhich executestsx watch -r dotenv/config src/index.ts. This provides TypeScript execution with automatic file watching -- changes to source files trigger an instant restart without a build step. github.com/microsoft/teams.ts - Configure logging with
ConsoleLoggerat the appropriate level:'trace'for deep SDK internals,'debug'for development,'info'for staging,'warn'or'error'for production. Usepattern: '-azure/msal-node'to suppress noisy MSAL authentication logs. Child loggers are created withlogger.child('name'). github.com/microsoft/teams.ts -- common - Run
npx tsc --noEmitas a build verification gate before testing or deploying. This type-checks all TypeScript source without producing output files. The project must compile cleanly -- type errors caught here prevent runtime failures. github.com/microsoft/teams.ts - For testing with real Teams clients locally, use a tunneling solution (ngrok, dev tunnels, Cloudflare Tunnel) to expose your local
localhost:3978endpoint over HTTPS. Update the Azure Bot messaging endpoint to the tunnel URL (e.g.,https://abc123.ngrok.io/api/messages). learn.microsoft.com -- Dev tunnels - Sideload the app by opening the Teams sideloading URL after provisioning:
https://teams.microsoft.com/l/app/${{TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&appTenantId=${{TENANT_ID}}&login_hint=${{USER_EMAIL}}UseTEAMS_APP_IDandUSER_EMAILfromenv/.env.local; useTENANT_IDfrom.localConfigs(generated byatk deploy --env local, mapped fromTEAMS_APP_TENANT_IDinenv/.env.local). Alternatively, zip theappPackage/directory (manifest.json + icons) and upload in Teams via Apps > Manage your apps > Upload a custom app. Both paths require admin-enabled custom app upload or a developer tenant. learn.microsoft.com -- Sideload apps - Use
skipAuth: trueinAppOptionsfor purely local development against DevTools without Azure Bot credentials. This disables JWT validation. Never use it in production or when testing against real Teams clients. github.com/microsoft/teams.ts -- apps - Use Agents Toolkit (ATK) as the recommended provisioning path for local development. First start a devtunnel (
devtunnel host -p 3978 --allow-anonymous) and setBOT_ENDPOINTinenv/.env.localto the tunnel URL. Then runatk provision --env local -i falseto create the Entra ID app registration, Bot Framework registration, and Teams app. Then runatk deploy --env local -i falseto generate.localConfigswith CLIENT_ID, CLIENT_SECRET, TENANT_ID, and PORT. Verify.localConfigshas TENANT_ID — if missing, copy the value ofTEAMS_APP_TENANT_IDfromenv/.env.localintoTENANT_IDin.localConfigs. See →toolkit.lifecycle-cli.mdfor the full m365agents.yml reference. learn.microsoft.com -- Agents Toolkit - For manual bot registration without ATK, create the Entra ID app registration and Azure Bot resource through the Azure Portal or Azure CLI, then copy the credentials into
.env. This path is useful for understanding what ATK automates or when ATK is not available. See →azure-bot-deploy-ts.mdfor the full manual workflow.
patterns
Local development setup with DevTools
import { App } from '@microsoft/teams.apps';
import { ConsoleLogger } from '@microsoft/teams.common';
import { DevtoolsPlugin } from '@microsoft/teams.dev';
const app = new App({
// Use skipAuth for local DevTools-only testing (no Azure credentials needed)
// Remove skipAuth when testing against real Teams clients
skipAuth: true,
logger: new ConsoleLogger('dev-bot', { level: 'debug' }),
plugins: [new DevtoolsPlugin()],
});
app.on('message', async ({ reply, activity }) => {
await reply(`Echo: ${activity.text}`);
});
// Bot endpoint: http://localhost:3978/api/messages
// DevTools UI: http://localhost:3979/devtools
app.start(3978);Production-ready logging configuration
import { App } from '@microsoft/teams.apps';
import { ConsoleLogger } from '@microsoft/teams.common';
import { DevtoolsPlugin } from '@microsoft/teams.dev';
// Development logger: verbose, noisy auth logs suppressed
const devLogger = new ConsoleLogger('my-bot', {
level: 'debug',
pattern: '-azure/msal-node',
});
// Production logger: only warnings and errors
const prodLogger = new ConsoleLogger('my-bot', {
level: 'warn',
});
// Choose based on environment
const isProduction = process.env.NODE_ENV === 'production';
const logger = isProduction ? prodLogger : devLogger;
const app = new App({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
tenantId: process.env.TENANT_ID,
logger,
// Only include DevtoolsPlugin in non-production
plugins: isProduction ? [] : [new DevtoolsPlugin()],
});
// Child loggers for scoped output
app.on('message', async (ctx) => {
const handlerLog = ctx.log; // Scoped to this activity
handlerLog.info(`Message from ${ctx.activity.from.name}`);
// Output: [my-bot] Message from John Doe
await ctx.send('Hello!');
});
app.start(process.env.PORT || 3978).catch(console.error);Build verification and debugging workflow
// === Option A: ATK-provisioned workflow (recommended) ===
// 1. Install dependencies
// npm install
// 2. Start a dev tunnel (must be running BEFORE provisioning)
// devtunnel host -p 3978 --allow-anonymous
// Copy the tunnel URL and set BOT_ENDPOINT in env/.env.local
// 3. Provision bot resources via ATK (creates Entra ID app + Bot Framework + Teams app)
// atk provision --env local -i false
// 4. Generate .localConfigs with credentials
// atk deploy --env local -i false
// Verify .localConfigs contains CLIENT_ID, CLIENT_SECRET, TENANT_ID, PORT.
// If TENANT_ID is missing, copy the value of TEAMS_APP_TENANT_ID from env/.env.local into TENANT_ID in .localConfigs.
// 5. Type-check the project (build gate)
// npx tsc --noEmit
// 6. Start dev server with file watching
// npm run dev
// 7. Open DevTools in browser
// http://localhost:3979/devtools
// 7. For real Teams testing, start a dev tunnel and provision:
// devtunnel host -p 3978 --allow-anonymous
// Set BOT_ENDPOINT in env/.env.local to the tunnel URL
// atk provision --env local -i false
// atk deploy --env local -i false
// Open Teams sideload URL (TEAMS_APP_ID from env/.env.local):
// https://teams.microsoft.com/l/app/$TEAMS_APP_ID?installAppPackage=true&webjoin=true&appTenantId=$TENANT_ID
// (TEAMS_APP_ID from env/.env.local; TENANT_ID from .localConfigs, mapped from TEAMS_APP_TENANT_ID in env/.env.local)
// === Option B: Manual workflow (no ATK) ===
// 1. npm install
// 2. Create .env: CLIENT_ID, CLIENT_SECRET, TENANT_ID, PORT=3978
// (Register bot manually -- see azure-bot-deploy-ts.md)
// 3. npx tsc --noEmit
// 4. npm run dev
// 5. devtunnel host -p 3978 --allow-anonymous → update Azure Bot messaging endpoint
// 6. Sideload: zip appPackage/ → upload in Teams
// === Build for production (both options) ===
// npm run build # Compiles to dist/ via tsup
// npm run start # Runs compiled JS: node -r dotenv/config .
// Common troubleshooting:
// - Bot not responding in Teams?
// Check: tunnel running, messaging endpoint updated, manifest scopes correct
// - DevTools blank?
// Check: DevtoolsPlugin in plugins array, port+1 not blocked
// - Type errors on import?
// Check: tsconfig module is "NodeNext", not "commonjs"
// - .env not loaded?
// Check: dotenv in devDependencies, -r dotenv/config in scripts
// - 401 Unauthorized after atk deploy?
// Check: .localConfigs has TENANT_ID; if missing, copy the value of TEAMS_APP_TENANT_ID from env/.env.local into TENANT_ID in .localConfigspitfalls
- Forgetting to start the tunnel: Without ngrok or dev tunnels, the Azure Bot Framework cannot reach your local endpoint. Teams messages never arrive. Always verify the tunnel is running and the messaging endpoint is updated.
- DevTools port conflict: DevTools runs on
PORT + 1. If port 3979 is already in use, DevTools fails silently. Check for port conflicts or change the bot'sPORT. - Using
skipAuthwith real Teams clients:skipAuth: truedisables JWT validation. Real Teams activities require proper authentication. UseskipAuthonly with DevTools for rapid iteration. - Not running
npx tsc --noEmit: Skipping the type-check means errors surface only at runtime. Always run this gate after changes, especially before committing or deploying. - Stale tunnel URL in Azure Bot config: Ngrok generates a new URL each time it restarts (unless on a paid plan). Forgetting to update the Azure Bot messaging endpoint after restarting ngrok means the bot stops receiving messages.
- Missing sideload permissions: Sideloading requires either admin-enabled custom app upload or a Microsoft 365 developer tenant. Without it, the "Upload a custom app" option does not appear in Teams.
- Wrong log level in production: Running with
'debug'or'trace'in production floods logs and can impact performance. Switch to'info'or'warn'for deployed environments. - Testing only in DevTools: DevTools simulates a Teams client but does not replicate all Teams behaviors (e.g., @mention stripping, SSO token exchange, card rendering differences). Always test in a real Teams client before shipping.
.localConfigsmissing TENANT_ID: Afteratk deploy --env local, the generated.localConfigsmay omitTENANT_ID. Without it, MSAL defaults to the wrong token authority, causing 401 errors. Copy the value ofTEAMS_APP_TENANT_IDfromenv/.env.localintoTENANT_IDin.localConfigs.- Dev tunnel URL blacklisted or expired: Dev tunnel URLs can be flagged by corporate proxies or expire after inactivity. If the bot suddenly stops receiving messages with a working tunnel, create a fresh tunnel and update the messaging endpoint.
references
- Teams SDK v2 -- @microsoft/teams.dev (DevtoolsPlugin)
- Teams SDK v2 GitHub repository
- Teams: Sideload apps
- Azure Dev Tunnels
- ngrok documentation
- M365 Agents Toolkit
- Teams: Test and debug
instructions
This expert covers local development, debugging, and testing workflows for Teams SDK v2 bots. Use it when you need to:
- Set up
DevtoolsPluginand access the DevTools UI atlocalhost:3979/devtools - Run the bot locally with
npm run dev(tsx watch with hot reload) - Configure
ConsoleLoggerlevels and noise filtering for different environments - Use
skipAuth: truefor credential-free local testing - Provision bot credentials locally with
atk provision --env localandatk deploy --env local - Set up dev tunnels or ngrok for testing with real Teams clients
- Sideload the bot via the Teams sideloading URL or by packaging and uploading
appPackage/as a zip - Run
npx tsc --noEmitas a build verification gate - Troubleshoot common issues (bot not responding, DevTools blank, import errors)
- Understand the difference between DevTools testing and real Teams testing
Pair with runtime.app-init-ts.md for App constructor setup and project.scaffold-files-ts.md for npm scripts and project file structure. Pair with project.scaffold-files-ts.md for npm scripts and build verification, and runtime.app-init-ts.md for DevtoolsPlugin configuration.
research
Deep Research prompt:
"Write a micro expert on developing, debugging, and testing Teams SDK v2 bots in TypeScript. Cover DevtoolsPlugin setup from @microsoft/teams.dev, DevTools UI at localhost:3979/devtools with WebSocket activity inspection and message replay, local development with npm run dev (tsx watch), bot endpoint at localhost:3978/api/messages, ConsoleLogger configuration (levels: error/warn/info/debug/trace, pattern filtering, child loggers), skipAuth for local testing, ngrok and dev tunnels for HTTPS exposure, sideloading via zip upload, npx tsc --noEmit build gate, M365 Agents Toolkit VS Code extension, and a troubleshooting flowchart for common issues (bot not responding, port conflicts, stale tunnel URLs, missing sideload permissions). Include a step-by-step local workflow and production logging patterns."