Skip to content

Commit 4928e69

Browse files
docs: rewrite telegram bot guide for readability
Rewrote the page to lead with screenshots, cut design-doc tone, and speak directly to platform/k8s engineers. Added image references for real Telegram conversation screenshots. Signed-off-by: Sebastian Maniak <sebastian@maniak.io> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 26f32b0 commit 4928e69

1 file changed

Lines changed: 53 additions & 195 deletions

File tree

  • src/app/docs/kagent/examples/telegram-bot

src/app/docs/kagent/examples/telegram-bot/page.mdx

Lines changed: 53 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,48 @@
11
---
22
title: "Telegram Bot"
33
pageOrder: 5
4-
description: "Learn how to build a Telegram bot that interacts with kagent via A2A"
4+
description: "Build a Telegram bot that talks to your Kubernetes cluster through kagent and A2A"
55
---
66

77
export const metadata = {
8-
title: "Building a Telegram Bot for Kubernetes with kagent and A2A",
9-
description: "Learn how to build a Telegram bot that interacts with kagent via A2A",
8+
title: "Build a Telegram Bot for Kubernetes with kagent and A2A",
9+
description: "Build a Telegram bot that talks to your Kubernetes cluster through kagent and A2A",
1010
author: "kagent.dev",
1111
};
1212

13-
# Building a Telegram Bot for Your Kubernetes Cluster with kagent and A2A
13+
# Talk to Your Kubernetes Cluster from Telegram
1414

15-
Kagent enables you to create AI agents that run inside your Kubernetes cluster. They can access a variety of [built-in tools](/docs/kagent/concepts/tools) and use other [external tools via MCP](/docs/kagent/examples/documentation).
15+
This is what you'll have by the end of this guide:
1616

17-
This guide shows how to build a Telegram bot that connects to a kagent agent using the A2A (Agent-to-Agent) protocol. The bot uses polling (no webhooks or public endpoints required) and forwards messages to your agent, which handles LLM orchestration, tool invocation, and response generation.
17+
![Telegram bot listing pods in a Kubernetes cluster](/images/telegram-a2a/telegram-list-pods.png)
1818

19-
We'll walk through:
19+
Your phone. Your cluster. No VPN, no laptop, no `kubectl`. Just a Telegram message.
2020

21-
1. Setting up a Telegram bot with BotFather.
22-
2. Deploying a kagent agent with A2A enabled.
23-
3. Writing the bot code.
24-
4. Deploying and testing the integration.
21+
It handles real operations too — ask it to create a namespace and it confirms before doing anything destructive:
2522

26-
---
27-
28-
## Architecture Overview
23+
![Telegram bot asking to create a namespace](/images/telegram-a2a/telegram-create-namespace.png)
2924

30-
```
31-
┌──────────────────┐
32-
│ Telegram Cloud │
33-
│ (Bot API) │
34-
└────────┬─────────┘
35-
│ Long Polling
36-
37-
┌──────────────────────────────────────────────┐
38-
│ Kubernetes Cluster │
39-
│ │
40-
│ ┌──────────────────┐ │
41-
│ │ telegram-bot │ │
42-
│ │ (Pod) │ │
43-
│ └────────┬─────────┘ │
44-
│ │ HTTP POST (A2A JSON-RPC) │
45-
│ ▼ │
46-
│ ┌──────────────────┐ ┌────────────────┐ │
47-
│ │ kagent-controller│───▶│ Agent Pod │ │
48-
│ │ :8083/api/a2a/ │ │ + LLM + Tools │ │
49-
│ └──────────────────┘ └────────────────┘ │
50-
└──────────────────────────────────────────────┘
51-
```
25+
![Human-in-the-loop approval for destructive operations](/images/telegram-a2a/telegram-human-in-the-loop.png)
5226

53-
The key insight: the Telegram bot doesn't talk to the LLM directly. It sends messages to the kagent controller's A2A endpoint, which routes them to the correct agent. The agent handles LLM orchestration, tool invocation, and response generation. The bot is just a thin transport layer.
27+
The bot itself is ~120 lines of Python. It doesn't know anything about Kubernetes or LLMs — it's a dumb pipe between Telegram and kagent's [A2A](https://google.github.io/A2A/) endpoint. All the intelligence lives in the agent.
5428

5529
---
5630

57-
## Creating a Telegram Bot
31+
## Create Your Telegram Bot
5832

59-
1. Open Telegram and search for **@BotFather**.
60-
2. Send `/newbot` and follow the prompts to choose a name and username.
61-
3. BotFather will give you a **bot token** — save it, you'll need it later.
33+
1. Open Telegram, search for **@BotFather**.
34+
2. Send `/newbot`, pick a name and username.
35+
3. Copy the **bot token** it gives you.
36+
37+
Done. 30 seconds.
6238

6339
---
6440

65-
## Deploying an Agent
41+
## Deploy a kagent Agent
6642

67-
Before deploying an agent, make sure you have installed kagent in your cluster. If you haven't, you can install it by following the instructions [here](https://kagent.dev/docs/kagent/getting-started/quickstart). Make sure that you also install the `kmcp` CRDs as shown in that guide so that you can create an MCPServer.
43+
> **Prerequisite:** kagent running in your cluster with `kmcp` CRDs installed. If not, hit the [quickstart](https://kagent.dev/docs/kagent/getting-started/quickstart) first.
6844
69-
Deploy a Kubernetes agent with A2A enabled:
45+
This agent has Kubernetes tools, Helm tools, and Prometheus — and it's exposed over A2A so any client (our Telegram bot, or anything else) can talk to it:
7046

7147
```shell
7248
kubectl apply -f - <<EOF
@@ -122,80 +98,22 @@ spec:
12298
EOF
12399
```
124100

125-
The `a2aConfig.skills` section tells A2A clients what this agent can do. The `requireApproval` list ensures destructive operations go through [Human-in-the-Loop](/docs/kagent/examples/human-in-the-loop) approval in the kagent UI before executing.
101+
Notice `requireApproval` — anything destructive (deleting resources, applying manifests, Helm upgrades) goes through [Human-in-the-Loop](/docs/kagent/examples/human-in-the-loop) approval in the kagent UI first. Nobody's accidentally nuking prod from a Telegram chat.
126102

127-
To access the agent through A2A locally, port-forward the kagent controller:
103+
Verify it's working:
128104

129105
```shell
130106
kubectl port-forward -n kagent svc/kagent-controller 8083:8083
131-
```
132-
133-
Verify the agent is accessible by fetching its A2A agent card:
134-
135-
```shell
136107
curl localhost:8083/api/a2a/kagent/telegram-k8s-agent/.well-known/agent.json
137108
```
138109

139-
You should see a JSON response with the agent's name, description, and skills.
140-
141-
---
142-
143-
## The A2A Protocol
144-
145-
A2A (Agent-to-Agent) is a Google-backed open protocol for agent interoperability. kagent implements A2A on its controller, meaning any A2A-compatible client can talk to any kagent agent.
146-
147-
The protocol uses JSON-RPC 2.0 over HTTP. A message exchange looks like:
148-
149-
**Request:**
150-
```json
151-
{
152-
"jsonrpc": "2.0",
153-
"id": "unique-task-id",
154-
"method": "message/send",
155-
"params": {
156-
"id": "unique-task-id",
157-
"message": {
158-
"role": "user",
159-
"parts": [{"kind": "text", "text": "list my pods"}]
160-
}
161-
}
162-
}
163-
```
164-
165-
**Response:**
166-
```json
167-
{
168-
"result": {
169-
"artifacts": [{
170-
"parts": [{
171-
"kind": "text",
172-
"text": "Here are your pods: ..."
173-
}]
174-
}]
175-
}
176-
}
177-
```
178-
179-
> **Important notes about kagent's A2A implementation:**
180-
> - The method is `message/send` (not `tasks/send` as in the older A2A draft spec)
181-
> - Parts use `"kind": "text"` (not `"type": "text"`)
182-
> - The URL pattern is `/api/a2a/{namespace}/{agent-name}/`**trailing slash required**
183-
> - Responses come back in `result.artifacts[].parts[]`
110+
You should get back JSON with the agent's name and skills.
184111

185112
---
186113

187-
## Writing the Bot Code
188-
189-
The bot is ~120 lines of Python using `python-telegram-bot` (polling mode) and `httpx` for A2A calls.
114+
## Write the Bot
190115

191-
### Project Structure
192-
193-
```
194-
telegram-bot/
195-
├── main.py
196-
├── requirements.txt
197-
└── Dockerfile
198-
```
116+
Three files. That's the whole thing.
199117

200118
### requirements.txt
201119

@@ -311,12 +229,7 @@ if __name__ == "__main__":
311229
main()
312230
```
313231

314-
Key design decisions:
315-
316-
- **Polling, not webhooks**: No ingress, no public endpoint, no TLS termination. The bot makes outbound connections to Telegram's API from inside the cluster.
317-
- **Session per user**: Each Telegram user gets their own A2A session ID, so conversations have context continuity.
318-
- **Chunked responses**: Telegram has a 4096-character message limit. Long agent responses are split automatically.
319-
- **Health file**: A simple `/tmp/bot-healthy` file is created on startup for Kubernetes liveness/readiness probes.
232+
It uses polling — no ingress, no webhooks, no TLS to set up. Each Telegram user gets their own session so conversations keep context. Long responses get chunked automatically (Telegram's 4096-char limit). The `/tmp/bot-healthy` file is there for Kubernetes liveness probes.
320233

321234
### Dockerfile
322235

@@ -329,56 +242,43 @@ COPY main.py .
329242
CMD ["python", "main.py"]
330243
```
331244

332-
Build and push the image:
333-
334245
```shell
335246
docker build -t your-registry/telegram-kagent-bot:latest .
336247
docker push your-registry/telegram-kagent-bot:latest
337248
```
338249

339250
---
340251

341-
## Running Locally (Quick Test)
342-
343-
Before deploying to Kubernetes, you can test the bot locally:
344-
345-
1. Create a `.env` file:
252+
## Test It Locally
346253

347-
```dotenv
348-
TELEGRAM_BOT_TOKEN=your_token_from_botfather
349-
KAGENT_A2A_URL=http://127.0.0.1:8083/api/a2a/kagent/telegram-k8s-agent/
350-
```
351-
352-
2. Make sure the kagent controller is port-forwarded:
254+
Before you deploy anything, make sure it actually works.
353255

354256
```shell
257+
# Terminal 1: port-forward kagent
355258
kubectl port-forward -n kagent svc/kagent-controller 8083:8083
356-
```
357-
358-
3. Install dependencies and run:
359259

360-
```shell
260+
# Terminal 2: run the bot
361261
pip install -r requirements.txt
362-
export $(cat .env | xargs)
262+
export TELEGRAM_BOT_TOKEN=your_token_from_botfather
263+
export KAGENT_A2A_URL=http://127.0.0.1:8083/api/a2a/kagent/telegram-k8s-agent/
363264
python main.py
364265
```
365266

366-
Open Telegram, find your bot, and send it a message like "What pods are running?"
267+
Open Telegram, find your bot, send "What pods are running?" — if you get a real answer, you're golden.
367268

368269
---
369270

370-
## Deploying to Kubernetes
371-
372-
### Store the Bot Token as a Secret
271+
## Deploy to Kubernetes
373272

374-
You can create the secret directly:
273+
Store the bot token:
375274

376275
```shell
377276
kubectl create secret generic telegram-bot-token -n kagent \
378277
--from-literal=TELEGRAM_BOT_TOKEN="your_token_from_botfather"
379278
```
380279

381-
Or, if you use an external secrets manager like HashiCorp Vault with the External Secrets Operator:
280+
<details>
281+
<summary>Using External Secrets Operator instead?</summary>
382282

383283
```yaml
384284
apiVersion: external-secrets.io/v1
@@ -401,7 +301,9 @@ spec:
401301
property: api_key
402302
```
403303
404-
### Deploy the Bot
304+
</details>
305+
306+
Deploy the bot:
405307
406308
```yaml
407309
apiVersion: apps/v1
@@ -414,7 +316,7 @@ metadata:
414316
spec:
415317
replicas: 1
416318
strategy:
417-
type: Recreate # Only one poller at a time
319+
type: Recreate
418320
selector:
419321
matchLabels:
420322
app: telegram-bot
@@ -448,75 +350,31 @@ spec:
448350
memory: 128Mi
449351
```
450352
451-
> **Important:** The `Recreate` strategy is required — Telegram's polling API doesn't support multiple consumers. With `RollingUpdate`, you'd briefly have two pods pulling the same messages, causing duplicates or missed messages.
452-
453-
Apply the deployment:
353+
The strategy is `Recreate` on purpose — Telegram polling doesn't support multiple consumers. Two pods polling the same bot token means duplicate or missed messages.
454354

455355
```shell
456356
kubectl apply -f deployment.yaml
457357
```
458358

459359
---
460360

461-
## Gotchas and Tips
462-
463-
### The trailing slash matters
464-
465-
The kagent A2A endpoint at `/api/a2a/kagent/telegram-k8s-agent` returns a 307 redirect to `/api/a2a/kagent/telegram-k8s-agent/`. Most HTTP clients won't follow redirects on POST requests by default. Always include the trailing slash.
466-
467-
### kagent uses `message/send`, not `tasks/send`
468-
469-
If you're reading the A2A spec or looking at other implementations, be aware that kagent uses `message/send` as the method name. The older `tasks/send` method returns a "Method not found" error.
470-
471-
### Parts use `kind`, not `type`
472-
473-
The A2A spec uses `"kind": "text"` for message parts. Some implementations use `"type": "text"`. kagent expects `kind`.
474-
475-
### Telegram message limits
476-
477-
Telegram enforces a 4096-character limit per message. The bot code handles this automatically by chunking responses.
478-
479-
---
480-
481-
## Testing It
482-
483-
Once deployed, open Telegram and find your bot:
484-
485-
1. `/start` — Shows available commands
486-
2. `/new` — Resets your conversation session
487-
3. Send any message — It goes to the agent and comes back with a real answer
361+
## Things That Will Bite You
488362

489-
Example interactions:
363+
**Always include the trailing slash** in the A2A URL: `/api/a2a/kagent/telegram-k8s-agent/`. Without it you get a 307 redirect. Most HTTP clients don't follow redirects on POST. You will lose time to this.
490364

491-
```
492-
You: What pods are running in kagent namespace?
493-
494-
Bot: Here are the running pods in the kagent namespace:
495-
• kagent-controller-57864fdf69-xfgmr (1/1 Running)
496-
• telegram-bot-5469669bf-v8h7p (1/1 Running)
497-
• telegram-k8s-agent-5f696bf4b9-pl7cl (1/1 Running)
498-
```
365+
**The method is `message/send`**, not `tasks/send`. If you're copying from other A2A examples, they might use the older spec. kagent uses `message/send` — `tasks/send` gives you "Method not found."
499366

500-
```
501-
You: What helm releases are installed?
502-
503-
Bot: Here are the Helm releases across all namespaces:
504-
• kagent (kagent) v0.8.0-beta6
505-
• vault (vault) v0.29.1
506-
• istio-base (istio-system) v1.25.2
507-
```
367+
**Parts use `kind`, not `type`.** The payload needs `"kind": "text"`, not `"type": "text"`. Easy to miss if you're referencing other implementations.
508368

509369
---
510370

511-
## What's Next
512-
513-
From here you could:
371+
## Take It Further
514372

515-
- **Add more agents**: Create separate agents for security, Istio, or monitoring and route to them from the bot.
516-
- **Route by command**: Use different Telegram commands (`/k8s`, `/istio`, `/security`) to route to different kagent agents.
517-
- **Add image support**: kagent supports multi-modal parts — you could send screenshots of dashboards and ask "what's wrong here?"
518-
- **Connect via AgentGateway**: Route the A2A traffic through [AgentGateway](https://agentgateway.dev) for rate limiting, authentication, and observability.
373+
The interesting part isn't the Telegram bot — it's the pattern. The bot is just a transport layer. A2A is the interface. That means:
519374

520-
The pattern works for any chat platform. Swap `python-telegram-bot` for `discord.py` or `slack-bolt` and the rest stays the same — the A2A protocol is the universal adapter.
375+
- **Multiple agents, one bot.** Add Telegram commands like `/k8s`, `/istio`, `/security` that route to different kagent agents.
376+
- **Any chat platform.** Swap `python-telegram-bot` for `discord.py` or `slack-bolt`. Everything else stays the same.
377+
- **Multi-modal.** kagent supports image parts — screenshot a Grafana dashboard and ask "what's wrong here?"
378+
- **AgentGateway.** Put [AgentGateway](https://agentgateway.dev) in front of the A2A endpoint for rate limiting, auth, and observability.
521379

522-
For questions or support, join our [Discord community](https://discord.gg/Fu3k65f2k3).
380+
Questions? Come find us on [Discord](https://discord.gg/Fu3k65f2k3).

0 commit comments

Comments
 (0)