officedev/microsoft-365-agents-toolkit-samples/proxy-agent-dev

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Develop, debug, and extend a proxy agent that connects any external AI agent to Microsoft 365 Copilot and Teams via direct SDK integration. Use when: understanding the project, explaining the architecture, onboarding to the codebase, building or modifying a Teams proxy agent with M365 Agents SDK, wiring a new backend SDK into agent.ts, configuring SSO, updating Bicep infrastructure, troubleshooting bot messaging, streaming responses, or managing environment config. Stop if: backend has no Node/TS SDK or cannot be called from a Node process.

Install

npx vskill@latest install officedev/microsoft-365-agents-toolkit-samples/proxy-agent-dev

SKILL.md

Proxy Agent Skill — Connect Any External Agent to M365 Copilot (SDK Pattern)

Wire any backend AI SDK — whether a direct LLM provider or an orchestration framework — into a stable M365 Copilot/Teams integration layer. The proxy handles Teams/M365 Copilot transport, SSO, and streaming — you replace only the backend call.


Use When / Stop When

Use this skill when:

  • Backend has a TypeScript/Node SDK (LLM provider or orchestrator)
  • Backend supports streaming or can map to chunked responses
  • Starting from the ProxyAgent-NodeJS sample

Stop and reassess if:

  • Backend has no Node SDK (consider an adapter service instead)
  • Backend is synchronous-only with no streaming path and cannot be adapted

Principles

These are non-negotiable. Every code change must preserve them:

  1. No secrets in source — never commit API keys, passwords, or tokens to the repository
  2. Secure secret storage — use ${{SECRET_*}} for local dev (ATK encrypts to .env.local.user), Key Vault references for production
  3. Prefer managed identity — when the backend supports Entra ID / TokenCredential, use federated credentials and managed identities (zero-secret). This is the ideal.
  4. User identity where possible — the user's Entra ID identity flows from M365 Copilot through SSO. Propagate it to the backend when supported; at minimum, use it for audit/logging even when the backend uses a separate auth mechanism.
  5. Bot Service is control plane only — discovery and auth setup; no user messages flow through it
  6. Backend is replaceable — swap the SDK without changing the M365 integration shell
  7. Streaming-first — real-time token streaming from backend to Teams/Copilot UI
Backend Auth Tiers
TierWhenHowExample
TokenCredential (ideal)Backend accepts Entra ID tokensUserAuthorizationTokenWrapper → SDK credential; zero secretsAzure AI Foundry, Azure OpenAI
API key + user context (practical)Backend requires an API key but you still want per-user contextKey Vault for key; SSO token for user identity/loggingOpenAI, Anthropic with user ID pass-through
API key only (acceptable)Backend has no user-identity conceptKey Vault for key; no per-user contextSimple LLM endpoints
Hardcoded secrets (forbidden)Never❌—

Forbidden Patterns

  • ❌ Hardcoding secrets in source — use ${{SECRET_*}} for local dev, Key Vault for production
  • ❌ Committing .env.local.user or any file containing plaintext secrets
  • ❌ Collapsing all users into one backend identity/session when the backend supports per-user identity
  • ❌ Routing user messages through Bot Service
  • ❌ Storing API keys in plain-text app settings in production (use Key Vault references)

Architecture

Control Plane (setup-time only)
Azure Bot Service
├── Registers bot endpoint (DNS-like discovery: bot ID → App Service URL)
├── Stores OAuth connection config, caches tokens
├── Facilitates token exchange: access_as_user → backend scopes
└── Supports multiple OAuth connections for scope separation
Data Plane (every message)
User (M365 Copilot / Teams)
│ direct connection via Teams infrastructure
▼
Proxy Agent (App Service)
│ SDK call to backend (in-process)
▼
Backend: LLM SDK or Orchestrator (Foundry, OpenAI, Agent Framework, LangChain, CrewAI, etc.)

All conversation traffic flows directly from Teams infrastructure to the App Service. Bot Service handles only auth flows.

Network Security

The proxy agent endpoint (/api/messages) must be publicly accessible. Private Endpoints are not supported when the agent is exposed to public channels like Teams or M365 Copilot — Teams backend services need to reach the endpoint directly over the internet.

Securing the public endpoint:

LayerHowNotes
Channel auth (built-in)SDK validates JWT bearer token on every inbound requestAutomatic via startServer() — proves request is from Microsoft Teams/Copilot
IP-based ACL (recommended)Restrict inbound traffic to Teams backend IPs listed in Microsoft 365 URLs and IP rangesApp Service Access Restrictions or NSG rules
API Gateway / WAF / Front Door / Azure FirewallAdvanced filtering, rate limiting, DDoS protectionOptional — for additional defense-in-depth
Note: The AzureBotService service tag is not relevant here — messages flow from Teams infrastructure, not through Bot Service.
SSO with Federated Credentials
Reference: Add user authorization using Federated Identity Credential — covers app registration setup, redirect URIs, pre-authorized client IDs, federated credential configuration, and OAuth connection creation on the Azure Bot.
  • Entra ID App Registration with access_as_user scope
  • Federated Credentials link App Registration to Bot's Managed Identity — no client secret
  • Bot OAuth Connection uses AADv2 with Federated Credentials for token exchange
  • Pre-authorized client IDs: Teams desktop, web, mobile — full list
  • Redirect URI: depends on data residency and cloud — see table below
Data ResidencyCloudOAuth URLOAuth Redirect URL
NonePublichttps://token.botframework.comhttps://token.botframework.com/.auth/web/redirect
EuropePublichttps://europe.token.botframework.comhttps://europe.token.botframework.com/.auth/web/redirect
United StatesPublichttps://unitedstates.token.botframework.comhttps://unitedstates.token.botframework.com/.auth/web/redirect
IndiaPublichttps://india.token.botframework.comhttps://india.token.botframework.com/.auth/web/redirect
NoneAzure Governmenthttps://token.botframework.azure.ushttps://token.botframework.azure.us/.auth/web/redirect
NoneAzure operated by 21Vianethttps://token.botframework.azure.cnhttps://token.botframework.azure.cn/.auth/web/redirect
  • OAuth connection on Azure Bot: use AAD v2 with Federated Credentials service provider — setup steps
  • Local dev: Single Tenant + Client Secret (auto-provisioned by toolkit)
  • Production: User Assigned Managed Identity — mutual authentication between Bot Service, Teams/Copilot services, and the App Service, ensuring all parties are who they claim to be
Multi-OAuth

If the backend uses a non-Entra IDP, configure a second OAuth connection in Bot Service. The SDK supports multiple OAuth configs declaratively — no proxy code changes needed.

Conversation History

M365 Copilot provides a Conversation ID (context.activity.conversation.id) each time a new conversation is created. The proxy receives this ID with every incoming message.

Key facts:

  • M365 Copilot shows chat history to the user — the user sees their past messages in the Copilot UI. This history is managed by M365 Copilot itself and is not accessible to the developer. The proxy agent never reads or controls it.
  • The backend owns conversation memory — the proxy should not accumulate or manage message history. If the backend is stateful (Foundry, Assistants API), use the Conversation ID to map to a backend thread/session. If the backend manages its own threads (like Foundry's AgentThread), store the backend thread ID in turnState.conversation and reuse it on subsequent messages.
  • Stateless backends create a UX mismatch — the user sees chat history in M365 Copilot and assumes the agent remembers prior context. If the backend is stateless (e.g., a raw chat completion API with no thread management), each message is processed independently and the agent has no memory of the conversation. This will confuse users. If you use a stateless backend, you must either:
  1. Add a conversation memory layer in the backend (recommended), or
  2. Make it clear to the user that each message is independent (e.g., in the agent's system prompt or welcome message)
Backend typeWho owns historyProxy responsibility
Stateful (Foundry, Assistants API)BackendMap Conversation ID → backend thread ID; store thread ID in turnState.conversation
Stateful with external sessionBackendPass Conversation ID as session key to the backend
Stateless (raw chat completion)Nobody — UX gapConsider adding a memory layer or clearly communicate the limitation

In the current Foundry sample, the proxy creates a Foundry AgentThread on first message and stores its ID in turnState.conversation.threadInfo. Foundry keeps the full message history server-side — the proxy only holds the thread reference.


Tech Stack

  • Runtime: Node.js 22/24, TypeScript 5.9+
Core Dependencies (invariant)
PackagePurpose
@microsoft/agents-hosting-expressExpress server + channel auth
@microsoft/agents-hostingAgentApplication, TurnContext, TurnState, storage, SSO pipeline
@microsoft/agents-activityActivity type definitions (ActivityTypes.Message)
@azure/identityTokenCredential for zero-secret auth
@azure/core-authShared auth interfaces
jsonwebtokenJWT decode/verify for SSO token exchange
Backend-Specific (you replace these)

LLM providers:

ExamplePackage
Groqgroq-sdk
OpenAIopenai
Anthropic@anthropic-ai/sdk
Foundry@azure/ai-agents

Orchestrators:

ExamplePackage
Agent Framework@microsoft/agents-framework
LangChain / LangGraphlangchain, @langchain/core
CrewAICustom HTTP client (Python backend via REST)

Project Structure

PathPurposeModify?
src/index.tsExpress server entry — startServer(agentApp)No
src/agent.tsProxyAgent class — backend client, message handling, streamingYes — backend seam
src/config.tsEnvironment-based configYes — backend vars
src/logger.tsLogging utilityNo
src/userAuthTokenWrapper.tsBridges Agent SDK SSO with AI Foundry auth (TokenCredential). Foundry-specific — not needed for backends that use API keys or other auth.Only if using Foundry
appPackage/manifest.jsonTeams app manifestDisplay strings only
m365agents.yml / .local.ymlATK project configOnly env var additions
infra/Bicep modules for Azure deploymentBackend-specific params
env/Environment variable templatesYes

Edit Boundaries in agent.ts

This is the critical file. The backend seam is clearly defined:

REPLACE (the backend seam)
WhatExample
Backend SDK importimport MySDK from "my-backend-sdk"
Client initializationconst client = new MySDK({ /* auth from config.ts */ })
Session/thread mappingMap Conversation ID → backend thread/session (stateful) or manage history (if needed)
Backend invoke + streamclient.chat.completions.create({ stream: true, ... })
Response assemblyfor await (const chunk of stream) { ... }
DO NOT MODIFY

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
493
Repository forks
319
7-day trend score
31

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 2 Aug 2026, 2 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/officedev/microsoft-365-agents-toolkit-samples/proxy-agent-dev/badge)](https://verified-skill.com/skills/officedev/microsoft-365-agents-toolkit-samples/proxy-agent-dev)

Something wrong with this skill or its page? Tell us.