Talk to GitHub Copilot CLI on your dev machine — from Microsoft Teams on your phone. ~400 lines of JS. No cloud infra. No webhooks.
If you're working on a public or private GitHub repo, the official GitHub Copilot app for Teams and Copilot coding agent are excellent — they run in the cloud against your repo and handle PRs, issues, and code review natively. Use those.
This bridge is for everything else — when you need Copilot to access your local filesystem, private infrastructure, internal tooling, build systems, or anything that doesn't live in a GitHub repo. Your phone becomes a thin client for your entire dev environment — with full context, local auth, and persistent sessions.
- 💬 Natural conversation — Send any message in Teams. Get a Copilot response back. Session persists across messages — it remembers context.
- 📊 Live progress — Milestone-driven updates show what Copilot is actually doing — not just "Working..." spam. Tunable frequency and cap.
- ⚡ Fastpath shortcuts —
zzz file issue for flaky testbypasses Copilot entirely and drops a draft item on your GitHub Projects board in <1 second. - 🛑 Mid-run cancel — Send
cancelto abort a long-running job. The bridge polls for commands while Copilot runs. - 📱 Phone-optimised — A mobile brevity hint biases Copilot toward short, scannable responses. Commands work without slash prefixes.
- 🔐 Your own identity — Runs under your own Entra app registration with minimal scopes. MFA-enforced. No shared secrets, no third-party services.
The bridge is deliberately simple:
- A systemd service runs on your dev machine (Linux VM, WSL, bare metal — anything that runs Node.js)
- It polls Microsoft Graph every 5 seconds for new messages in a dedicated Teams DM chat
- New messages are passed to GitHub Copilot CLI as prompts, with session continuity
- Copilot's response is posted back to the same Teams chat
- Special commands (
cancel,reset,zzz, etc.) are intercepted before reaching Copilot
No webhook endpoint. No Cloudflare Tunnel. No cloud functions. No Bot Framework. Just polling + child process + systemd.
copilot-teams-bridge/
├── src/
│ ├── index.mjs # Main loop: polling, commands, progress, replies
│ ├── teams-client.mjs # Microsoft Graph auth & chat API
│ ├── copilot-client.mjs # Spawns Copilot CLI, streams JSON output
│ └── github-projects.mjs # zzz fastpath → GitHub Projects GraphQL
├── config.json # Your configuration
├── config.example.json # Template
└── package.json
All commands work with or without a leading /. Case-insensitive.
| Command | What it does |
|---|---|
help |
Show all available commands |
status |
Bridge health, auth status, token expiry, uptime |
cancel |
Abort the running Copilot job mid-execution |
last |
Resend the most recent successful answer |
reset |
Clear Copilot session — next message starts fresh |
stop |
Stop the bridge (systemd restarts it automatically) |
zzz <text> |
Quick-capture a draft item to GitHub Projects board |
- Node.js 18+
- GitHub Copilot CLI installed and authenticated
- An M365 tenant with two user accounts (one for the "bot", one for you)
- An Entra app registration (public client, delegated permissions:
Chat.ReadWrite,ChatMessage.Send,ChatMessage.Read,User.Read)
git clone https://github.com/adstuart/copilot-teams-bridge.git
cd copilot-teams-bridge
cp config.example.json config.json
# Edit config.json with your tenant, client ID, recipient UPN, etc.node src/index.mjs
# Follow the device code prompt — sign in as your bot account
# Tokens are cached locally and refresh automatically# Copy the systemd unit file
sudo cp copilot-teams-bridge.service /etc/systemd/system/
sudo systemctl enable --now copilot-teams-bridgePin the DM chat with your bot account. Send any message. Copilot responds.
Long-running tasks show milestone updates streamed from Copilot's intermediate reasoning — not generic "Working..." spam:
Updates are throttled (configurable interval and cap) so they inform without flooding your phone notifications.
| Approach | Local tools? | Persistent sessions? | Phone UX? | Setup complexity |
|---|---|---|---|---|
| This bridge | ✅ Full access | ✅ Per-thread | ✅ Optimised | Low — config + systemd |
| GitHub Copilot for Teams (official) | ❌ Cloud only | ❌ | ✅ | Minimal |
| Copilot coding agent (cloud) | ❌ Repo-scoped | ✅ Per-issue | ✅ Via GitHub | Minimal |
| Squad | ✅ | ✅ | ✅ | Moderate — Teams app + AI orchestration |
| VS Code + SSH/tunnel | ✅ | ✅ | ❌ Not phone-friendly | Moderate |
| Bot Framework bot | ✅ | ✅ | ✅ | High — Azure hosting, bot registration, webhook endpoint |
| WhatsApp (Baileys/unofficial) | ✅ | ✅ | ✅ | Low but no chat isolation — bot sees all your messages |
| Key | Description | Default |
|---|---|---|
tenantId |
Your M365 Entra tenant | — |
clientId |
Your Entra app client ID | — |
recipientUpn |
UPN of the account you message from | — |
chatId |
Pin to a specific chat (optional) | Auto-created DM |
copilotTimeoutSeconds |
Max time for a single Copilot run | 3600 (60 min) |
pollIntervalMs |
How often to check for new messages | 5000 |
progressUpdateMinIntervalMs |
Min gap between progress updates | 20000 |
maxProgressUpdates |
Cap on progress messages per job | 8 |
mobileSystemPrompt |
Brevity hint prepended to prompts | "Reply will be read on a phone..." |
projectCapturePrefix |
Fastpath trigger for GitHub Projects | zzz |
githubProjectId |
GitHub Projects v2 node ID | — |
Many Teams-to-AI tutorials use Microsoft Graph Explorer's client ID for quick demos. That app has dozens of pre-registered scopes — Mail, Files, Calendar, Directory, Sites, and more. A leaked token from that app can access everything.
This bridge uses its own Entra app registration with only the 5 delegated permissions it needs:
| Permission | Why it's needed |
|---|---|
Chat.ReadWrite |
Access the dedicated DM chat |
ChatMessage.Send |
Post replies back to Teams |
ChatMessage.Read |
Read incoming commands/prompts |
User.Read |
Resolve the bot's own identity |
offline_access |
Refresh token (auto-renew auth) |
No mail. No files. No calendar. No directory. If a token is ever compromised, the blast radius is limited to chat messages in chats the bot account is a member of — nothing else.
- MFA enforced via Security Defaults on the M365 tenant.
- Dedicated accounts — the bot and receiver are single-purpose M365 identities with no other roles or data.
- No secrets in code — public client flow (PKCE). No client_secret to leak. Tokens cached locally with filesystem permissions.
- Auth health monitoring — the
statuscommand shows token expiry, refresh state, and consecutive failures in real time. - No inbound connectivity — the bridge polls outbound. No open ports, no webhook endpoints, no attack surface from the internet.
This project was inspired by and built alongside exploration of several community approaches:
- Squad by Brady Gaster — Teams adapter for collaborative AI workflows. Different goal (team collaboration vs. personal bridge) but the initial inspiration for "Teams + Copilot".
- Copilot CLI by the GitHub Copilot team — the engine under the hood. The
--output-format jsonflag makes streaming progress possible. - Tamir Dresher's work on Squad and community discussions around Teams as an AI surface.
- The broader community building personal AI assistants on messaging platforms (Baileys, MoltBot, etc.) — different transports, same philosophy.
Teams gives channel isolation. The bot account only sees the dedicated chat. WhatsApp unofficial libraries (Baileys) expose all your personal messages to the bot. Slack and Telegram are viable alternatives with similar API patterns — PRs welcome.
Zero infrastructure. No public endpoint, no DNS, no TLS cert, no tunnels. The bridge runs behind NAT/firewall with no inbound connectivity needed. 5-second polling latency is imperceptible for this use case.
The refresh token has a 90-day sliding window and gets renewed every ~1 hour while the bridge runs. As long as your machine is up and the M365 tenant is active, auth rolls indefinitely.
Yes — you just need two accounts in any M365 tenant where you can register an Entra app. A personal dev tenant works, or your org tenant if you have app registration permissions.
MIT
Built with GitHub Copilot. The entire bridge — including this README — was developed through conversational iteration with Copilot CLI.