Go remote MCP server exposing Telegram user-account access (via gotd/td MTProto) as MCP tools — list_dialogs, get_unread_messages, send_message — for Claude.ai and any MCP-compatible client.
Status: functional MVP (0.2.0). Three tools, OAuth-protected, draft-by-default send gate. Telegram session is per-operator and persisted encrypted.
| Path | Purpose |
|---|---|
/healthz, /readyz |
Probes — ok 200. |
/.well-known/oauth-protected-resource |
RFC 9728 metadata declaring api.mctl.ai as the authorization server. |
/mcp |
MCP Streamable HTTP endpoint. Auth: Authorization: Bearer <JWT-from-api.mctl.ai>. |
| Tool | Annotations | Notes |
|---|---|---|
list_dialogs |
readOnly |
inputs: limit (≤200, default 50), optional query. peer id format user:<id> / chat:<id> / channel:<id>. |
get_unread_messages |
readOnly |
inputs: optional peer, limit (≤200). only unread. |
send_message |
destructive |
inputs: peer, text, optional mode ∈ {draft, send}. Default draft. Real send requires all of: server ALLOW_SEND=true, identity has telegram:messages:send scope, mode=send. Otherwise returns dry-run preview with dry_reason. |
# 1. Build & run the server
ADDR=:8080 \
AUTH_MODE=local-dev AUTH_REQUIRED=false \
OPERATOR_GITHUB_LOGIN=your-github-handle \
DATABASE_URL='file:./mctl-telegram.db?_pragma=journal_mode(WAL)' \
go run ./cmd/server
# 2. First-time login (registers an app at https://my.telegram.org first)
TG_API_ID=12345 TG_API_HASH=hexhexhex... \
DATABASE_URL='file:./mctl-telegram.db?_pragma=journal_mode(WAL)' \
OPERATOR_GITHUB_LOGIN=your-github-handle \
go run ./cmd/login --phone +1...
# 3. Smoke test via MCP inspector or curl
curl -s -X POST localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"local","version":"0"}}}'Required env (set via Helm values.yaml + ExternalSecret pulling from Vault):
| Key | Source |
|---|---|
AUTH_MODE |
shared-hmac (recommended) or own-oauth (M6) |
AUTH_REQUIRED |
true |
OAUTH_JWT_SECRET |
Vault secret/platform/oauth-jwt-secret |
TG_API_ID, TG_API_HASH |
Vault secret/platform/mctl-telegram/api |
ENCRYPTION_KEY |
Vault secret/platform/mctl-telegram/encryption (32-byte hex) |
DATABASE_URL |
postgres://... (provisioned via mctl_provision_database) |
ALLOW_SEND |
false until bake-in completes |
- Verify the well-known is reachable:
curl https://labs-mctl-telegram.mctl.ai/.well-known/oauth-protected-resource
- In Claude.ai → Settings → Connectors → Add custom connector:
- Remote MCP URL:
https://labs-mctl-telegram.mctl.ai/mcp - Authentication: OAuth (the connector will discover
api.mctl.aifrom the well-known and prompt for GitHub login).
- Remote MCP URL:
- After GitHub auth, the access token issued by
api.mctl.aiis automatically used on every MCP request.mctl-telegramverifies it via shared HMAC.
This service is part of the mctlhq platform. Image builds and gitops commits are centralized in mctlhq/mctl-gitops. For a new version:
# tag, push, then dispatch
git tag X.Y.Z && git push origin X.Y.Z
mctl deploy -t labs -n mctl-telegram -r mctlhq/mctl-telegram -g X.Y.Z \
--host labs-mctl-telegram.mctl.ai --port 8080 \
--env AUTH_MODE=shared-hmac --env AUTH_REQUIRED=true --env ALLOW_SEND=false- Image:
ghcr.io/mctlhq/mctl-telegram:<semver>(novprefix) - GitOps values:
mctl-gitops/platform-gitops/services/labs/mctl-telegram/values.yaml - Public hostname:
https://labs-mctl-telegram.mctl.ai
See SECURITY.md — covers shared-HMAC coupling, send-gate invariants, and reporting channel.