Introducing slackker! π₯
slackker sends real-time notifications, custom updates, and metric plots from any Python script directly to Slack, Telegram, Microsoft Teams, or Discord β so you can step away from the screen and still stay informed. β
slackker.in.action.mp4
Table of contents π
- Installation
- Quick Start
- Create a Client
- SimpleCallback β any Python function
- Interactive Pipeline
- MCP Server
- Universal MCP Configuration
- Keras
- Lightning
- Support
- Citation
- Maintainer
Installation β¬οΈ
Install slackker from UV (recommended) or pip. Requires Python >= 3.10.
Quick Start π
from slackker.core import TelegramClient from slackker.callbacks.simple import SimpleCallback client = TelegramClient( token="1234567890:AAAAA_A111BBBBBCCC2DD3eEe44f5GGGgGG", verbose=1, ) notify = SimpleCallback(client) @notify.notifier def train_model(): # ... your training code ... return {"accuracy": 0.94, "loss": 0.12} train_model()
Refer to our website for platform setup instructions (Slack, Telegram, Teams, Discord).
Create a Client
All slackker callbacks use a client object. Create one for your platform and pass it to any callback.
from slackker.core import SlackClient, TelegramClient, TeamsClient, DiscordClient
# Slack client = SlackClient( token="xoxb-123234234235-123234234235-adedce74748c3844747aed", channel_id="C04AAB77ABC", verbose=0, ) # Telegram client = TelegramClient( token="1234567890:AAAAA_A111BBBBBCCC2DD3eEe44f5GGGgGG", verbose=0, ) # Discord client = DiscordClient( token="your_bot_token_here", channel_id="123456789012345678", verbose=0, ) # Microsoft Teams client = TeamsClient( app_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", tenant_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", chat_id="19:xxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxx@thread.v2", verbose=0, )
First-time Teams setup: On the first run,
TeamsClientprints a short URL and a code. Visit the URL, enter the code, and sign in. The token is then cached and silently refreshed on every subsequent run.
Client parameters
Shared parameters (Slack, Telegram, Discord):
| Parameter | Type | Default | Description |
|---|---|---|---|
token |
str |
required | Slack app / Telegram bot / Discord bot token |
channel_id |
str |
required (Slack & Discord only) | Slack or Discord channel ID |
chat_id |
str |
None (Telegram only) |
Telegram chat ID β auto-discovered if omitted |
verbose |
int |
0 |
0 = WARNING/ERROR, 1 = INFO, 2 = DEBUG |
Teams-specific parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
app_id |
str |
required | Azure AD application (client) ID |
tenant_id |
str |
"common" |
Azure AD tenant ID, or "common" for personal + org accounts |
chat_id |
str |
required | Teams chat ID (e.g. 19:..._...@thread.v2) β right-click a message β Copy link, extract from URL |
token_cache_path |
str |
~/.slackker/teams_<app_id[:8]>.json |
Path to cache the access/refresh token |
verbose |
int |
0 |
0 = WARNING/ERROR, 1 = INFO, 2 = DEBUG |
SimpleCallback β any Python function
from slackker.core import TelegramClient from slackker.callbacks.simple import SimpleCallback client = TelegramClient( token="1234567890:AAAAA_A111BBBBBCCC2DD3eEe44f5GGGgGG", verbose=1, ) notify = SimpleCallback(client) # Decorator β automatically sends function name, execution time, and return value @notify.notifier def train(): # ... your training code ... return {"accuracy": 0.94, "loss": 0.12} train() # notify() β send a custom update anywhere, with optional file attachment notify.notify( event="training_complete", attachment="./artifacts/model.ckpt", best_val_loss=0.0123, epoch=20, )
Works with any client:
SlackClient,TelegramClient,TeamsClient, orDiscordClient.
Async: use await notify.async_notify(event="step_done", accuracy=0.95) in async contexts.
SimpleCallback parameters
Constructor:
| Parameter | Type | Description |
|---|---|---|
client |
BaseClient |
A slackker client instance |
notify() / async_notify() parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
event |
str |
None |
Label in the notification header; defaults to script filename |
attachment |
str |
None |
Path to a file to send alongside the notification |
**kwargs |
β | β | Any key-value pairs to include in the notification body |
ask() / async_ask() parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
question |
str |
required | Message sent to the platform asking for a reply |
timeout |
float |
60.0 |
Seconds to wait for a reply; auto-continues on timeout |
halt_on |
str |
"no" |
Reply text (case-insensitive) that halts the flow |
Returns True to continue, False to halt.
Interactive Pipeline
Use ask() to send a message and wait for the user's reply β ideal for checkpoints, confirmations, or human-in-the-loop pipelines:
from slackker.core import TelegramClient from slackker.callbacks.simple import SimpleCallback client = TelegramClient( token="1234567890:AAAAA_A111BBBBBCCC2DD3eEe44f5GGGgGG", verbose=1, ) notifier = SimpleCallback(client) def pipeline(): notifier.notify(event="preprocessing_done", samples=10000) reply = notifier.ask("Preprocessing done. Start training? (yes/no)") if reply.lower() != "yes": return # ... training ... notifier.notify(event="training_complete", accuracy=0.94) pipeline()
Async version: use await notifier.async_ask("...") in async contexts.
MCP Server
Use slackker as an MCP server so AI agents can call tools like a remote SimpleCallback:
notifyβ send event notifications (optional attachment + metadata)askβ human approval gate (continue/halt)get_messagesβ fetch recent channel messagesget_statusβ connection + listener status
Install MCP extras:
pip install "slackker[mcp]"Start the server (stdio transport):
CLI run methods
If slackker-mcp is not on your shell PATH, use one of these:
# Run via uv in the current project environment uv run slackker-mcp # Run as a Python module (bypasses PATH lookup) python -m slackker.mcp.server
You can also configure everything with CLI flags only:
slackker-mcp --platform slack --token xoxb-... --channel-id C04AAB77ABC
Or load config from a JSON file and still override via flags:
slackker-mcp --config ./slackker_mcp.json --poll-interval 1.0
Configuration is read from environment variables:
# Required for Slack / Telegram / Discord SLACKKER_PLATFORM=slack # slack | telegram | discord | teams SLACKKER_TOKEN=xoxb-... # required for slack/telegram/discord # Platform-specific target SLACKKER_CHANNEL_ID=C04AAB77ABC # Slack / Discord channel ID SLACKKER_CHAT_ID=123456 # Telegram chat ID (optional if auto-discovery works) # Teams-specific SLACKKER_APP_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx SLACKKER_TENANT_ID=common SLACKKER_CHAT_ID=19:...@thread.v2 # Optional runtime tuning SLACKKER_POLL_INTERVAL=2.0 SLACKKER_VERBOSE=1
slackker-mcp automatically reads a local .env file from the current working directory.
If the same SLACKKER_* key is set in both places, the exported process environment value wins.
Universal MCP Configuration
slackker-mcp runs as a local stdio MCP server, so it works with MCP clients that can launch a local command.
Compatibility Matrix
| Client | Status | Config shape | Notes |
|---|---|---|---|
| VS Code | β Verified | servers.<name> |
MCP extension config (.vscode/mcp.json) |
| Zed | β Verified | context_servers.<name>.command |
Uses settings.json |
| Claude Desktop | β Verified | mcpServers.<name> |
Uses claude_desktop_config.json |
| Claude Code (terminal) | β Verified | claude mcp add ... or .mcp.json with mcpServers |
Native MCP management commands |
| OpenCode (terminal) | β Verified | mcp.<name> with type: "local" |
Uses opencode.json |
| Roo Code | β Verified | mcpServers.<name> |
Global/project MCP files |
| Cursor | π‘ Common stdio pattern | mcpServers.<name> |
AI-native fork; follows Claude/MCP standard |
| Continue | π‘ Common stdio pattern | mcpServers.<name> |
Confirm exact file path per plugin/version |
| Antigravity | π‘ Common stdio pattern | mcpServers.<name> |
AI-native fork; follows Claude/MCP standard |
| Hermes / other terminal agents | π‘ Stdio-compatible by design | Local stdio command + env | Works if host supports stdio MCP servers |
Snippets by client
Click to expand configuration snippets
VS Code (`.vscode/mcp.json`)
{
"servers": {
"slackker": {
"type": "stdio",
"command": "slackker-mcp",
"env": {
"SLACKKER_PLATFORM": "slack",
"SLACKKER_TOKEN": "xoxb-...",
"SLACKKER_CHANNEL_ID": "C04AAB77ABC"
}
}
}
}Zed (`settings.json`)
{
"context_servers": {
"slackker": {
"command": {
"path": "slackker-mcp",
"env": {
"SLACKKER_PLATFORM": "slack",
"SLACKKER_TOKEN": "xoxb-...",
"SLACKKER_CHANNEL_ID": "C04AAB77ABC"
}
}
}
}
}Claude Desktop (`claude_desktop_config.json`)
{
"mcpServers": {
"slackker": {
"command": "slackker-mcp",
"env": {
"SLACKKER_PLATFORM": "slack",
"SLACKKER_TOKEN": "xoxb-...",
"SLACKKER_CHANNEL_ID": "C04AAB77ABC"
}
}
}
}Claude Code (terminal)
Add from CLI:
claude mcp add --transport stdio \ --env SLACKKER_PLATFORM=slack \ --env SLACKKER_TOKEN=xoxb-... \ --env SLACKKER_CHANNEL_ID=C04AAB77ABC \ slackker -- slackker-mcp
Or project config (.mcp.json):
{
"mcpServers": {
"slackker": {
"type": "stdio",
"command": "slackker-mcp",
"env": {
"SLACKKER_PLATFORM": "slack",
"SLACKKER_TOKEN": "xoxb-...",
"SLACKKER_CHANNEL_ID": "C04AAB77ABC"
}
}
}
}OpenCode (`opencode.json`)
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"slackker": {
"type": "local",
"command": ["slackker-mcp"],
"enabled": true,
"environment": {
"SLACKKER_PLATFORM": "slack",
"SLACKKER_TOKEN": "xoxb-...",
"SLACKKER_CHANNEL_ID": "C04AAB77ABC"
}
}
}
}File-based config variant:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"slackker": {
"type": "local",
"command": ["slackker-mcp", "--config", "/absolute/path/slackker_mcp.json"],
"enabled": true
}
}
}Cursor / Continue / Roo / Antigravity (common stdio shape)
{
"mcpServers": {
"slackker": {
"command": "slackker-mcp",
"env": {
"SLACKKER_PLATFORM": "slack",
"SLACKKER_TOKEN": "xoxb-...",
"SLACKKER_CHANNEL_ID": "C04AAB77ABC"
}
}
}
}File path and top-level key names can vary by client/plugin version.
Hermes / other terminal MCP hosts
Use whichever config file your host expects, with these required fields:
- transport: local/stdio
- command:
slackker-mcp - env:
SLACKKER_*(or args--config /path/to/slackker_mcp.json)
Minimal generic example:
{
"mcpServers": {
"slackker": {
"command": "slackker-mcp",
"env": {
"SLACKKER_PLATFORM": "slack",
"SLACKKER_TOKEN": "xoxb-...",
"SLACKKER_CHANNEL_ID": "C04AAB77ABC"
}
}
}
}Use with Keras
from slackker.core import TelegramClient from slackker.callbacks.keras import KerasCallback client = TelegramClient( token="1234567890:AAAAA_A111BBBBBCCC2DD3eEe44f5GGGgGG", verbose=1, ) slackker = KerasCallback( client=client, model_name="MyModel", export="png", send_plot=True, ) history = model.fit( x_train, y_train, epochs=10, batch_size=32, validation_data=(x_val, y_val), callbacks=[slackker], )
Works with any client:
SlackClient,TelegramClient,TeamsClient, orDiscordClient.
KerasCallback parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
client |
BaseClient |
required | A slackker client instance |
model_name |
str |
required | Name used in messages and plot titles |
export |
str |
"png" |
Plot format (eps, jpeg, jpg, pdf, pgf, png, ps, raw, rgba, svg, svgz, tif, tiff) |
send_plot |
bool |
False |
Send training/validation plots when training ends |
Use with Lightning
Unlike Keras, Lightning requires you to explicitly log metrics using self.log() inside your LightningModule. Use on_epoch=True in training_step so slackker can read them at the end of each epoch.
import torch import torch.nn as nn from torch.utils.data import DataLoader import torchvision as tv import torch.nn.functional as F from lightning.pytorch import LightningModule, Trainer from slackker.core import TelegramClient from slackker.callbacks.lightning import LightningCallback class LightningModel(LightningModule): def __init__(self): super().__init__() self.fc1 = nn.Linear(28 * 28, 256) self.fc2 = nn.Linear(256, 128) self.out = nn.Linear(128, 10) def forward(self, x): batch_size, _, _, _ = x.size() x = x.view(batch_size, -1) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) return self.out(x) def configure_optimizers(self): return torch.optim.Adam(self.parameters(), lr=1e-3) def training_step(self, batch, batch_idx): x, y = batch y_hat = self.forward(x) loss = F.cross_entropy(y_hat, y) accuracy = (torch.max(y_hat, 1)[1] == y).float().mean() # log with on_epoch=True so slackker can read them at epoch end self.log("train_loss", loss, on_epoch=True) self.log("train_acc", accuracy, on_epoch=True) return loss def validation_step(self, batch, batch_idx): x, y = batch y_hat = self.forward(x) loss = F.cross_entropy(y_hat, y) accuracy = (torch.max(y_hat, 1)[1] == y).float().mean() # on_epoch=True by default in validation_step self.log("val_loss", loss) self.log("val_acc", accuracy) return loss train_data = tv.datasets.MNIST(".", train=True, download=True, transform=tv.transforms.ToTensor()) val_data = tv.datasets.MNIST(".", train=False, download=True, transform=tv.transforms.ToTensor()) train_loader = DataLoader(train_data, batch_size=128) val_loader = DataLoader(val_data, batch_size=128) model = LightningModel() client = TelegramClient( token="1234567890:AAAAA_A111BBBBBCCC2DD3eEe44f5GGGgGG", verbose=1, ) slackker = LightningCallback( client=client, model_name="MyModel", track_logs=["train_loss", "train_acc", "val_loss", "val_acc"], monitor="val_loss", export="png", send_plot=True, ) trainer = Trainer(max_epochs=10, callbacks=[slackker]) trainer.fit(model, train_loader, val_loader)
Works with any client:
SlackClient,TelegramClient,TeamsClient, orDiscordClient.
LightningCallback parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
client |
BaseClient |
required | A slackker client instance |
model_name |
str |
required | Name used in messages and plot titles |
track_logs |
list[str] |
required | Metrics to track and report each epoch |
monitor |
str |
None |
Metric used to determine the best epoch |
export |
str |
"png" |
Plot format (eps, jpeg, jpg, pdf, pgf, png, ps, raw, rgba, svg, svgz, tif, tiff) |
send_plot |
bool |
False |
Send training plots when training ends |
Support β¨
If you get stuck, weβre here to help. The following are the best ways to get assistance working through your issue:
- Use our Github Issue Tracker for reporting bugs or requesting features.
Contribution are the best way to keep
slackkeramazing πͺ - If you want to contribute please refer Contributor's Guide for how to contribute in a helpful and collaborative way π
Citation π
Please cite slackker in your publications if this is useful for your project/research. Here is an example BibTeX entry:
@misc{siddheshgunjal2023slackker, title={slackker}, author={Siddhesh Gunjal}, year={2023}, howpublished={\url{https://github.com/siddheshgunjal/slackker}}, }



