setup-bot-connection
Skill16 starsChanged 3 days agoApache-2.0View on GitHub
End-to-end guide for setting up a new bot connection in swiss_ai_hub.bot. Covers Azure App Registration, Bot Channels Registration, Teams/Slack channel config, PathEntity creation in bot_paths collection, and DevTunnel for local dev. Use when user says "set up a bot", "connect bot to Teams", "connect bot to Slack", "configure bot channel", "create bot connection", "DevTunnel setup", "bot local development", or "Azure bot registration". Do NOT use for bot handler code scaffolding (use scaffold-bot-handler), bot architecture questions (use bot-framework), or agent debugging (use debug-agent).
- Reads credentials
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 17
Read packages/bot/CLAUDE.md for full architecture, routes, and essential files.
- Distinctive: line 118
Use the helper script or insert directly:
- Distinctive: line 161
Create Slack App at https://api.slack.com/apps
- Very specific: line 247
If you see address already in use, run pkill -9 -f main.py, wait until ss -ltnp | grep :8001 is empty, then start again.
- Distinctive: line 258
Leave App ID / Password empty → Connect → send a message.
- Very specific: line 340
Use that URL as the Azure Bot resource's messaging endpoint (see *Option A: Automated Setup* / setup_azure_bot.py above) and enable the …
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Bot Connection Setup Guide
Set up a new bot connection. Target channel or question via $ARGUMENTS.
Before You Start
Read packages/bot/CLAUDE.md for full architecture, routes, and essential files.
Key concept: Each bot endpoint has a PathEntity in MongoDB (bot_paths collection) containing Azure AD
credentials (APP_ID, APP_PASSWORD, APP_TENANTID), system message template, and Slack OAuth token.
Prerequisites
- Azure CLI installed and authenticated:
az login - Azure subscription with permission to create App Registrations and Bot resources
- MongoDB/FerretDB running (for storing PathEntity credentials)
- NATS server running (for agent communication)
- Public endpoint or DevTunnel for the bot server
Option A: Automated Setup (Recommended)
Script: packages/bot/swiss_ai_hub/bot/setup_azure_bot.py
For Teams (Single-Tenant)
cd packages/bot
uv run python swiss_ai_hub/bot/setup_azure_bot.py \
--resource-group "my-resource-group" \
--bot-name "ai-hub-bot" \
--token-url "https://my-domain.com" \
--token-path "/api/v1/active/agent/chat/completions/MyAgent/my_agent_id/json" \
--mongo-connection-string "mongodb://localhost:27017" \
--tenant-id "your-azure-tenant-id" \
--system-message "You are {assistant_name}. The user's name is {username}." \
--location "westeurope" \
--sku "F0"For Slack (Multi-Tenant)
cd packages/bot
uv run python swiss_ai_hub/bot/setup_azure_bot.py \
--resource-group "my-resource-group" \
--bot-name "ai-hub-slack-bot" \
--token-url "https://my-domain.com" \
--token-path "/api/v1/active/agent/chat/completions/MyAgent/my_agent_id/json" \
--mongo-connection-string "mongodb://localhost:27017" \
--slack-token "xoxb-your-slack-bot-token" \
--system-message "You are {assistant_name}. The user's name is {username}."What the script does:
- Creates Azure AD App Registration (
az ad app create) - Creates Service Principal (
az ad sp create) - Resets credentials → generates APP_PASSWORD
- Saves PathEntity to MongoDB
bot_pathscollection - Creates Azure Bot Resource (
az bot create)
Option B: Manual Setup (Step-by-Step)
Step 1: Azure AD App Registration
# Create app registration
az ad app create --display-name "ai-hub-bot" --sign-in-audience "AzureADMyOrg"
# Output: { "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }
# Create service principal
az ad sp create --id <appId>
# Generate client secret
az ad app credential reset --id <appId>
# Output: { "appId": "...", "password": "...", "tenant": "..." }Save these values:
APP_ID=appIdAPP_PASSWORD=passwordAPP_TENANTID=tenant(for single-tenant/Teams)
Step 2: Create Azure Bot Resource
az bot create \
--app-type "SingleTenant" \
--appid "<APP_ID>" \
--name "ai-hub-bot" \
--resource-group "my-resource-group" \
--display-name "Swiss AI Hub Bot" \
--endpoint "https://your-domain.com/api/v1/active/agent/chat/completions/MyAgent/my_id/json" \
--location "westeurope" \
--sku "F0" \
--tenant-id "<APP_TENANTID>"Step 3: Create PathEntity in MongoDB
Use the helper script or insert directly:
# Option 1: Use add_path_entity.py
export BOT_APP_ID="<APP_ID>"
export BOT_APP_PASSWORD="<APP_PASSWORD>"
export BOT_TENANT_ID="<APP_TENANTID>"
export MONGO_CONNECTION_STRING="mongodb://localhost:27017"
cd packages/bot && uv run python swiss_ai_hub/bot/add_path_entity.py# Option 2: Direct MongoDB insert
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017")
client["aihub"]["bot_paths"].update_one(
{"path": "/api/v1/active/agent/chat/completions/MyAgent/my_id/json"},
{"$set": {
"path": "/api/v1/active/agent/chat/completions/MyAgent/my_id/json",
"credentials": {
"APP_TYPE": "SingleTenant", # or "MultiTenant" for Slack
"APP_ID": "<APP_ID>",
"APP_PASSWORD": "<APP_PASSWORD>",
"APP_TENANTID": "<TENANT_ID>", # None for MultiTenant
},
"system_message": "You are {assistant_name}. The user's name is {username}.",
"slack_token": None, # Set for Slack bots: "xoxb-..."
}},
upsert=True,
)Step 4: Configure Channel in Azure Portal
For Teams:
- Azure Portal → Bot Services → your bot → Channels
- Click "Microsoft Teams" → Configure
- Enable messaging
- Save
For Slack:
- Create Slack App at https://api.slack.com/apps
- Enable "Bot User OAuth Token" → copy token (
xoxb-...) - In Azure Portal → Bot Services → your bot → Channels
- Click "Slack" → Configure with OAuth redirect
- Save the
slack_tokenin PathEntity
For Web Chat:
- Azure Portal → Bot Services → your bot → Channels
- Web Chat is enabled by default
- Copy the secret key for embedding
Local Development Setup
You can drive the bot locally from the Microsoft Bot Framework Emulator with no Azure registration, using the testing playground. (For an authentic Teams/Slack test with real identities, see Real channel via DevTunnel + Azure at the end.)
Step 0 — The testing playground runner (main.py)
The runner is packages/bot/playground/testing/main.py — read that file for the full implementation. It is
local-only — never an entry point for a deployed bot. It does three things so a live emulator can drive the bot
without Azure:
- Forces the SDK's unauthenticated mode — monkeypatches
RestChannelServiceClientFactory.create_connector_clientandcreate_user_token_clientto passuse_anonymous=True. Without it theCloudAdapterrequires MSAL: you hitTENANT_ID is not seton inbound and a401(rejected token) on the outbound reply. - Binds
0.0.0.0:8001(not the runner's hard-codedlocalhost) so a Windows-hosted emulator can reach it across the WSL2 boundary, and callsrunner.start_simulation()before serving — that starts the simulated agent's NATS subscribers; skip it and every chat times out waiting for a reply. - Optionally stubs the user email via
BOT_DEV_FAKE_EMAIL(see Step 4) — a no-op when unset, so it never affects real connector-based resolution.
Step 1 — Seed a PathEntity for the endpoint
The runner serves the agent at /api/v1/agent/chat/completions/my_agent_class/my_agent_id/json. Seed a matching
PathEntity (empty credentials are fine — the runner is unauthenticated):
cd packages/bot
uv run python - <<'PY'
from dotenv import load_dotenv; load_dotenv("../../.env")
from mongoengine import connect
from swiss_ai_hub.core.infrastructure import AIHubSettings, MongoSettings
from swiss_ai_hub.bot.persistence.entities.path_entity import Credentials, PathEntity
connect(db=AIHubSettings().MONGO_MAIN_DB_NAME, host=MongoSettings().CONNECTION_STRING.get_secret_value(), uuidRepresentation="standard")
path = "/api/v1/agent/chat/completions/my_agent_class/my_agent_id/json"
PathEntity.objects(path=path).delete()
PathEntity(path=path, credentials=Credentials(APP_TYPE="MultiTenant"),
system_message="You are a helpful local dev assistant.").save()
print("seeded", path)
PY⚠️ Keep the
system_messagefree of placeholders, or use only one of{username}/{assistant_name}.CompletionHandler.get_system_messagecalls.format()twice, so a message containing both placeholders raisesKeyError.
Step 2 — Start (or restart) the runner
# 1. stop any running playground instance (no-op if none; matches any python "main.py")
pkill -f main.py
# 2. go to the testing playground
cd packages/bot/playground/testing
# 3. start it, backgrounded, logging to /tmp/bot_local.log (serves on 0.0.0.0:8001)
uv run python main.py > /tmp/bot_local.log 2>&1 &Watch logs with tail -f /tmp/bot_local.log. Requires the dev stack's MongoDB/FerretDB, NATS, and Keycloak to be
running. Re-run these three commands after changing BOT_DEV_FAKE_EMAIL (Step 4) — env vars are read once at startup.
Always confirm exactly one clean instance after (re)starting — uv run spawns two processes, and if you start a new
one before the old releases port 8001, the new one dies with address already in use while the old instance keeps
serving (so your .env change silently has no effect):
tail -5 /tmp/bot_local.log # GOOD: "Uvicorn running on http://0.0.0.0:8001"
# BAD: "address already in use" -> an old instance is still up
ss -ltnp | grep :8001 # must show exactly ONE listenerIf you see address already in use, run pkill -9 -f main.py, wait until ss -ltnp | grep :8001 is empty, then start
again.
Step 3 — Connect the Bot Framework Emulator
Download from https://github.com/microsoft/BotFramework-Emulator/releases. Open Bot → URL:
http://localhost:8001/api/v1/agent/chat/completions/my_agent_class/my_agent_id/jsonLeave App ID / Password empty → Connect → send a message.
WSL2 note (Windows + WSL2 only)
On native Linux/macOS, the emulator and bot share localhost — skip this section; the URL above and replies just
work. On WSL2 (bot in Linux, emulator on Windows), localhost bridges neither direction:
- Emulator → bot: connect via the WSL2 IP, not
localhost. Get it withhostname -I(e.g.172.23.171.112) and usehttp://<WSL_IP>:8001/.... (The runner already binds0.0.0.0.) Bot → emulator (replies): the emulator's reply URL is its own Windows
localhost, unreachable from WSL2 (Cannot connect to localhost:<port>). Bridge it with a devtunnel:a. Find the emulator's listening port. In the emulator's Live Chat tab, open the Log panel (right side) and read the line
Emulator listening on http://[::]:<port>— e.g.…:57705. This port is assigned per emulator session, so re-check it whenever you restart the emulator. (The emulator's Settings → Configure Tunnel section also prints the exact command pre-filled with the current port, e.g.devtunnel host -a -p 57705.)b. Host the tunnel on that port (run on Windows):
# One-time only — skip if devtunnel is already installed (check with: devtunnel --version) winget install Microsoft.devtunnel # One-time only — skip if already logged in (check with: devtunnel user show) devtunnel user login # Every session — host the tunnel on the emulator's current port from step (a) devtunnel host -a -p <emulator-port> # e.g. 57705c. Paste the public URL into the emulator.
devtunnel hostprints two URLs — copy the "Connect via browser" one (NOT the-inspectone):Hosting port: 57705 Connect via browser: https://g25mmhp5-57705.asse.devtunnels.ms ← copy THIS Inspect network activity: https://g25mmhp5-57705-inspect.asse.devtunnels.ms ← NOT this (causes 401) Ready to accept connections for tunnel: jolly-cat-1xpgknv.assePaste it into Settings → Configure Tunnel → Tunnel Url → Save. Keep the
devtunnel hostwindow running; replies now route back through the tunnel.
Step 4 — Drive the user identity (BOT_DEV_FAKE_EMAIL)
The bot resolves the user's email via the Teams connector (get_conversation_member), which the emulator does not
implement (returns 404). To exercise identity-dependent logic (auth, Keycloak provisioning) from the emulator, set
BOT_DEV_FAKE_EMAIL in .env — the runner resolves that email directly:
# .env (gitignored, DEV ONLY)
BOT_DEV_FAKE_EMAIL='admin@your-company.com' # a provisioned Keycloak user -> happy path
# BOT_DEV_FAKE_EMAIL='ghost@example.com' # an unknown email -> UserNotProvisionedErrorRestart the runner after changing it; leave it unset for real connector-based resolution. To fake other identity fields
for future features, add another env-gated monkeypatch in the runner (same pattern) — never in swiss_ai_hub/
production code.
Emulator troubleshooting
| Symptom | Cause | Fix |
No credentials found for path | PathEntity missing | Seed it (Step 1) |
Emulator POST 400, nothing in the bot log | Emulator can't reach the bot (WSL2) | Connect via the WSL2 IP, not localhost |
Reply fails: Cannot connect to localhost:<port> | Bot (WSL2) can't reach the emulator reply URL | Set up the devtunnel and paste the Tunnel Url |
Reply 401 to *.devtunnels.ms | Pasted the -inspect tunnel URL | Use the "Connect via browser" URL, not the -inspect one |
KeyError: 'assistant_name' | system_message uses both placeholders | Use ≤1 placeholder in the seeded system_message |
Bot 404s on .../members/... → generic error | Emulator can't do the Teams member lookup | Set BOT_DEV_FAKE_EMAIL to drive identity (Step 4) |
| 60s typing then "taking too long" | The simulated agent didn't reply (harness) | Identity resolved fine; use a real agent or test a pre-agent branch |
Real channel via DevTunnel + Azure (authentic Teams/Slack)
For a true end-to-end test (real display names, real connector emails, genuinely unprovisioned users), expose the local bot and register it as an Azure Bot:
devtunnel host -a -p 8001 # public URL for the bot itselfUse that URL as the Azure Bot resource's messaging endpoint (see Option A: Automated Setup / setup_azure_bot.py
above) and enable the Teams/Slack channel in the Azure portal.
System Message Templates
System messages support placeholders:
{username}→ replaced with the user's display name{assistant_name}→ replaced with the bot's display name
Example:
You are {assistant_name}, an AI assistant for the Swiss AI Hub platform.
The user's name is {username}. Be helpful, concise, and professional.
Always respond in the user's language.Verification Checklist
After setup, verify:
- PathEntity exists in MongoDB
bot_pathscollection with correct path - Credentials (APP_ID, APP_PASSWORD) are valid and not expired
- Azure Bot Resource endpoint URL matches your server's public endpoint
- Channel is configured in Azure Portal (Teams/Slack/Web Chat)
- Bot server is running and reachable at the endpoint URL
- NATS server is running (for agent-based bots)
- MongoDB/FerretDB is running (for conversation persistence)
- Test with Bot Framework Emulator or direct message in Teams/Slack
Troubleshooting Quick Reference
| Symptom | Likely Cause | Fix |
| "No credentials found for path" | PathEntity missing | Insert PathEntity in MongoDB |
| 401 Unauthorized from Azure | APP_PASSWORD expired | az ad app credential reset --id <appId> |
| Bot doesn't respond in Teams | Wrong endpoint URL | Update Azure Bot Resource endpoint |
| Bot doesn't respond in Slack | Missing slack_token | Add slack_token to PathEntity |
| "Connection refused" locally | Bot server not running | Start with python main.py or make run-prod |
| DevTunnel not forwarding | Port mismatch | Verify tunnel port matches bot server port (8001) |
In this repository50 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- docs/CLAUDE.mdCLAUDE.md
- .github/actions/CLAUDE.mdCLAUDE.md
- arc42Skill
- audit-frontendSkill
- bot-frameworkSkill
- create-or-audit-claude-mdSkill
- create-or-audit-skillSkill
- create-or-audit-subagentSkill
- create-prSkill
- dagster-pipelinesSkill
- debug-agentSkill
- debug-frontendSkill
- debug-pipelineSkill
- design-systemSkill
- docker-devSkill
- document-decisionSkill
- document-featureSkill
- explainSkill
- generate-sdkSkill
- i18nSkill
- implement-feedback-from-prSkill
- likec4-dslSkill
- lintSkill
- merge-mainSkill
- nats-eventsSkill
- plan-issueSkill
- pr-demo-videoSkill
- primevue-lookupSkill
- rclone-guideSkill
- reflectSkill
- review-diffSkill
- scaffold-agentSkill
- scaffold-api-endpointSkill
- scaffold-api-repositorySkill
- scaffold-api-serviceSkill
- scaffold-bot-handlerSkill
- scaffold-composableSkill
- scaffold-event-displaySkill
- scaffold-frontend-componentSkill
- scaffold-frontend-pageSkill
- scaffold-frontend-subpageSkill
- scaffold-pipelineSkill
- splice-issueSkill
- sync-agent-docsSkill
- test-scopeSkill
- update-docSkill
- verify-sdk-syncSkill
- write-docSkill
- write-issueSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

