An open source, self-hosted Microsoft Teams bot that connects GitHub and Jira to your team's channels via webhooks. No paid subscriptions, no SaaS middleman — you run it.
teamshook receives webhook events (a push, a PR, a Jira transition), renders them into clean Adaptive Cards, and posts them to the right Teams channel based on a YAML routing config. Later phases layer AI-assisted summaries and workflow automation on top.
Status: Early development. Phase 1 (bot integration) is in progress. See Current State for what actually works today versus what is still on the roadmap.
Most GitHub ↔ Teams integrations are either paid, cloud-hosted, or both. teamshook is internal developer platform tooling you self-host: webhook-driven notifications first, AI-assisted workflow automation later.
Design priorities:
- Self-hosted first — Docker Compose to start, Helm later. No external service required.
- No database in Phase 1 — routing is a YAML file with hot-reload. Operators shouldn't need a DB just to point a repo at a channel.
- Standard library Go —
net/httpwith Go 1.22+ method routing, no web framework. Fewer abstractions over the concepts that matter. - No vendor lock-in for AI — a pluggable LLM interface (OpenAI, Anthropic, Ollama, …) when we get to Phase 2.
-
net/httpserver with graceful shutdown - GitHub push webhook receiver + payload parsing
- HMAC-SHA256 signature verification (
X-Hub-Signature-256) - Adaptive Card builder for GitHub push events
- Wire verification + card delivery into the request handler
-
internal/config— load & validate YAML, map GitHub repo / Jira project → Teams channel - Jira webhook receiver + cards
- Teams bot registration (Azure App Registration, single-tenant, free)
- Docker Compose packaging
- Conversational
@mentioncommands (natural language → GitHub/Jira API calls) - PR and issue AI summaries appended to notifications
- Duplicate-issue detection via embeddings + vector store (Chroma)
- LLM provider abstraction layer
- Auto-label / auto-assign (with human approval via a Teams card)
- PR description generator from diff + commits
- Definition-of-Done checker on Jira transitions
- GitHub/Jira state-drift detection (PR merged but Jira still In Progress)
- Weekly standup digest (scheduled, from live API data)
What is implemented today:
| Area | File | Status |
|---|---|---|
| HTTP server, graceful shutdown | cmd/server/main.go | Working; listens on localhost:8080 |
| Route wiring | cmd/server/routes.go | POST /webhook → GitHub push handler |
| GitHub push types + handler | internal/webhook/github.go | Parses payload, logs event |
| HMAC verification | internal/webhook/verify.go | Implemented, not yet called by the handler |
| Teams Adaptive Card builder | internal/teams/notify.go | Builds + can POST a card, not yet wired in |
Known gaps (next steps): signature verification and card delivery are written but
not connected in the handler, and there is no config package yet — the Teams
webhook URL has nowhere to come from until internal/config lands.
| Concern | Decision | Reason |
|---|---|---|
| Language | Go | Platform-engineering fit, systems-level control |
| HTTP | Standard library net/http |
Go 1.22 method routing is enough; avoids framework abstraction |
| Vector store | Chroma | Good client/docs; in-process for dev, Docker for prod |
| LLM layer | Pluggable interface | No vendor lock-in; supports self-hosted Ollama |
| Packaging | Docker Compose → Helm | Self-hosted first |
| Config | YAML, hot-reload, no DB (Phase 1) | Operators shouldn't need a database to configure routing |
teamshook/
├── cmd/
│ └── server/
│ ├── main.go # Entry point: server lifecycle + graceful shutdown
│ └── routes.go # Route registration
├── internal/
│ ├── webhook/
│ │ ├── github.go # GitHub payload types + handler
│ │ ├── jira.go # Jira payload types + handler (planned)
│ │ └── verify.go # HMAC signature verification
│ ├── teams/
│ │ └── notify.go # Builds + posts Adaptive Cards to Teams
│ └── config/
│ └── config.go # Loads and validates YAML config (planned)
├── config.example.yaml # (planned)
├── Dockerfile # (planned)
├── docker-compose.yml # (planned)
└── go.mod
- Go 1.26+ (see go.mod)
go run ./cmd/serverThe server starts on http://localhost:8080 and exposes POST /webhook for
GitHub push events. Send it a sample payload to see the parsed event logged:
curl -X POST http://localhost:8080/webhook \
-H "Content-Type: application/json" \
-d '{"ref":"refs/heads/main","head_commit":{"id":"abc1234def","message":"test"}}'go build -o bin/teamshook ./cmd/server- In your GitHub repo: Settings → Webhooks → Add webhook
- Payload URL: the public URL of your teamshook instance +
/webhook - Content type:
application/json - Secret: a strong shared secret (this is the key used for HMAC verification)
- Events: start with Just the push event
This project doubles as a hands-on learning vehicle for Go and platform engineering, so code favors clarity and well-commented intent over cleverness. Issues and PRs welcome as the Phase 1 surface stabilizes.