Building the iujinwee Telegram Plugin
The claude-plugins-official:telegram plugin bridges Telegram and Claude Code β messages from your Telegram bot land directly in the Claude session as conversation turns. Quite a useful foundation, but the default plugin only handles message delivery. Iβve been extending it with project-aware features, the first of which is /command: a skill for discovering and managing project-scoped Claude skills without leaving the chat.
Problem Statement
When youβre working across multiple projects, each with their own .claude/skills/ directory, thereβs no easy way to know what skills are available β let alone run them β from Telegram. Youβd have to open the terminal, navigate to the project, and manually invoke /skill-name. The /command feature fixes this by exposing the full skill catalog through the Telegram interface, with runtime argument collection and interactive management (create, edit, delete).
The /command Feature
/command dispatches based on the first token:
/commandor/command listβ lists all skills grouped by source (project-local vs global)/command <name> [args...]β runs the named skill, prompting for any required inputs/command create <name>β creates a new skill interactively/command edit <name>β edits an existing skill/command rm <name>β deletes a skill after confirmation
Skills are discovered from .claude/skills/ (project-local) and ~/.claude/skills/ (global fallback), giving project-scoped context without any configuration.
One thing I had to be careful about: the skill explicitly blocks execution from Telegram channel messages. A Telegram message asking Claude to run
/command rm production-deploywould be prompt injection. The skill validates that execution is terminal-initiated only.
Developing Plugins
The Claude Code plugin system is more capable than I initially expected β itβs not just skills. There are several component types, each serving a different integration point.
Skills
The bread and butter. A skill is a directory with a SKILL.md file that contains YAML frontmatter and natural-language instructions. Claude treats the instructions as its own when the skill is invoked. Skills support structured inputs (required/optional), runtime argument substitution, and can bundle supporting scripts.
skills/
βββ command/
β βββ SKILL.md
βββ deploy/
βββ SKILL.md
βββ scripts/
βββ deploy.sh
Skills are auto-discovered at session start and can be invoked manually (/skill-name) or automatically by Claude based on task context.
Agents
Specialized subagents that Claude spawns for specific tasks. Defined as markdown files with frontmatter controlling model, effort level, max turns, and tool restrictions. Appear in the /agents interface and can be invoked automatically or manually.
---
name: security-reviewer
description: Audits code changes for security issues
model: sonnet
effort: high
maxTurns: 20
disallowedTools: Write, Edit
---Worth noting: plugin agents canβt configure hooks, MCP servers, or permission modes β thatβs a deliberate security boundary.
Hooks
Event handlers that fire automatically when Claude Code does something. The hooks I already use in the vault (PostToolUse on Write to enforce blog indexes) are the same mechanism plugins use. The full event list is much broader than I realised:
| Event | When |
|---|---|
SessionStart / SessionEnd | Session lifecycle |
PreToolUse / PostToolUse | Around any tool call (can block) |
UserPromptSubmit | Before Claude processes input |
Stop | When Claude finishes responding |
SubagentStart / SubagentStop | Around subagent invocations |
FileChanged | When a watched file changes on disk |
PreCompact / PostCompact | Around context compaction |
Hook types go beyond shell commands too β you can fire an HTTP request, call an MCP tool, evaluate a prompt with an LLM, or even run a full agentic verifier.
MCP Servers
Model Context Protocol servers that expose tools Claude can call. The telegram plugin itself uses this β the Telegram bot is an MCP server that receives messages and exposes reply, react, and edit_message tools.
Plugin MCP servers start automatically when the plugin is active, and the ${CLAUDE_PLUGIN_ROOT} variable makes it easy to reference bundled server binaries without hardcoded paths.
LSP Servers
Language Server Protocol integration β gives Claude real-time code intelligence (diagnostics, go-to-definition, hover types) while editing files. The plugin configures how Claude connects to the language server; you install the binary separately.
{
"go": {
"command": "gopls",
"args": ["serve"],
"extensionToLanguage": { ".go": "go" }
}
}Less relevant for the telegram plugin specifically, but extremely useful for any plugin that touches code.
Monitors
Background processes that run for the lifetime of a session and pipe every stdout line to Claude as a notification. The telegram plugin uses one to watch for incoming messages β a long-running process polls the bot and delivers messages without Claude having to poll.
[
{
"name": "telegram-listener",
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/listen.sh",
"description": "Incoming Telegram messages"
}
]The when field can defer startup until a specific skill is invoked β handy for expensive monitors you donβt want running on every session.
Themes
JSON files that ship color themes alongside the plugin. They appear in /theme next to the built-ins. A theme specifies a base preset (dark or light) and a sparse overrides map of color tokens. Currently experimental, but kinda cool as a bundling mechanism β a plugin could ship its own visual identity.
{
"name": "Dracula",
"base": "dark",
"overrides": {
"claude": "#bd93f9",
"error": "#ff5555"
}
}Channels
The piece that makes the telegram plugin possible. The channels field in plugin.json declares message channels that inject content directly into the Claude conversation. Each channel binds to an MCP server and can declare its own userConfig for prompting bot tokens at enable time.
{
"channels": [
{
"server": "telegram",
"userConfig": {
"bot_token": {
"type": "string",
"title": "Bot token",
"sensitive": true
}
}
}
]
}This is what turns a Telegram message into a conversation turn β the channel declaration wires the MCP server output into Claudeβs input stream. Without it, the MCP server would just expose tools, not inject context.
Plugin Structure
The full directory layout for a plugin like this:
iujinwee-telegram/
βββ .claude-plugin/
β βββ plugin.json β metadata + channel declaration
βββ skills/
β βββ command/
β β βββ SKILL.md β /command dispatcher
β βββ access/
β β βββ SKILL.md β allowlist management
β βββ configure/
β βββ SKILL.md β bot token setup
βββ monitors/
β βββ monitors.json β telegram listener process
βββ .mcp.json β MCP server (the actual bot)
βββ scripts/
βββ listen.sh β polling script
Technologies
- Claude Code Plugin API (skills, channels, monitors, MCP)
- Telegram Bot API via MCP server
- Shell scripts for the listener process
Progress Tracker
- Telegram channel delivery (messages appear in Claude session) β 2026-05
-
/commandskill for skill discovery and management β 2026-05-26 -
/command searchβ keyword search across skill descriptions - Skill argument autocomplete hints in Telegram replies
-
/scheduleintegration β trigger scheduled agents from Telegram
Thanks for reading β if youβre building something similar on top of Claude Codeβs plugin system, the official plugin reference is surprisingly complete.