Polybar Notification MCP
This server displays notification popups via Quickshell on Linux with rich customization options.
Core Capabilities:
Display notifications - Show customizable popup notifications with messages, titles, and severity levels (info/warn/crit)
Customize appearance - Control accent colors (via severity or custom hex), background patterns (waves, grid, stripes, sunset), and left panel images (ghost, claude, openai, or custom paths)
Configure behavior - Set auto-hide timeouts (0 for persistent), transition effects (glow, ghost, ripple, none), and actions on backtick keypress (switch workspace or focus app)
Check status - Verify if Quickshell is running with the ohai IPC target registered
Test notifications - Use the bundled CLI tool to test preset notifications or create custom ones
Client integration - Configure for use with Claude, Cursor, or codex-cli via environment variables for default image and transition settings
Setup Quickshell - Launch the popup daemon and verify IPC target registration for communication between the MCP server and Quickshell
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Polybar Notification MCPshow a warning notification saying 'Build failed' with title 'CI Pipeline'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ohai
MCP for displaying Quickshell-based notifications on Linux.
Installation
npm install
npm run buildRelated MCP server: ntfy-mcp-server
Tools
ohai
Display a notification popup.
Parameter | Type | Required | Description |
| string | yes | Message body |
| string | no | Popup title (defaults to message) |
|
| no | Accent color (default: |
| string | no | Custom accent color (CSS/hex; overrides severity) |
| number | no | Auto-hide delay (default: 8; 0 = persistent) |
| string | no | Background pattern ID or path |
| string | no | Image ID or path (default: |
| string | no | Path used to resolve configured project visual defaults |
| string | no | Workspace to switch to on backtick |
| string | no | App/window to focus on backtick |
| number | no | Niri window id to focus from the notification action button |
|
| no | Entry transition effect (default: |
ohai_status
Check if Quickshell is running with the ohai IPC target registered.
Transition Effects
Effect | Description |
| Soft blur glow pulse (default) |
| Expanding/fading copies of actual notification content |
| Border ripples emanating outward |
| No transition effect |
Set a default transition via environment variable:
export OHAI_TRANSITION=ghostQuickshell Setup
Start the popup daemon:
qs -p /path/to/ohai-mcp/ohai/shell.qmlVerify the IPC target is registered:
qs ipc -p /path/to/ohai-mcp/ohai/shell.qml --any-display show
# Should list: target ohaiTest manually:
qs ipc -p /path/to/ohai-mcp/ohai/shell.qml --any-display call ohai notify "Hello" "Test message" "info" 5 "" "" "" "" "glow"Controls while popup is visible:
Escape- hide popupBacktick (`) - switch to workspace or focus app (if configured), then hide
Testing with ohai-test
A CLI helper for testing notification appearance:
bin/ohai-test [preset] # Run a preset
bin/ohai-test themes # List project visual themes
bin/ohai-test theme <name> # Preview a project visual theme
bin/ohai-test theme all # Cycle through all project visual themes
bin/ohai-test project <path> # Preview the project profile matched by a path
bin/ohai-test custom [options] # Custom notificationPresets:
Preset | Description |
| Basic info notification (default) |
| Warning notification |
| Critical notification |
| Long text (test layout) |
| Minimal notification |
| Claude-themed |
| OpenAI-themed |
| Grid pattern background |
| Stripes pattern background |
| Waves pattern background |
| Test glow transition effect |
| Test ghost echo transition effect |
| Test ripple transition effect |
| Cycle through all presets |
Theme previews:
bin/ohai-test themes
bin/ohai-test theme aquatic
bin/ohai-test theme allProject profile diagnostics:
bin/ohai-theme resolve ~/d/software/ohai
bin/ohai-test project ~/d/software/ohaiohai-theme resolve prints JSON with the config path, normalized project path,
matched project, resolved visuals, and focus target. ohai-test project sends a
real notification using the same resolved profile values.
Custom options:
bin/ohai-test custom \
-t "Title" \
-b "Body text" \
-s warn \
-i claude \
-p grid-01 \
-d 10 \
-c "#ff00ff" \
-x ghostOption | Description |
| Title text |
| Body text |
|
|
| Image ID or path |
| Pattern ID or path |
| Timeout in seconds |
| Custom accent color (hex) |
| Workspace to switch on backtick |
| App to focus on backtick |
|
|
Bundled Assets
Patterns (background overlays):
waves-01,grid-01,stripes-01,sunset-01
Images (left panel):
ghost(default),claude,openai
Use an absolute path for custom assets.
Asset Trust Model
image and pattern accept bundled asset IDs or custom local asset paths. Custom
paths are treated as trusted desktop input and are passed to Quickshell for
rendering; ohai validates basic string shape at the MCP boundary, but it does not
sandbox QML asset loading.
Prefer bundled IDs for routine notifications. Use custom absolute paths only for local files you intend Quickshell to read, and configure this MCP server only for trusted clients.
Integration
Use environment variables to configure defaults per client.
Claude Code
To add via the CLI for user scope:
claude mcp add ohai \
--scope user \
--env OHAI_TRANSITION=glow \
--env OHAI_DEFAULT_IMAGE=claude \
-- node <OHAI_PATH>/bin/ohai-mcp~/.claude.json:
{
"mcpServers": {
"ohai": {
"type": "stdio",
"command": "node",
"args": ["/path/to/ohai-mcp/bin/ohai-mcp"],
"env": {
"OHAI_DEFAULT_IMAGE": "claude",
"OHAI_TRANSITION": "glow"
}
}
}
}Cursor
Add to MCP settings:
{
"mcpServers": {
"ohai": {
"command": "node",
"args": ["/path/to/ohai-mcp/bin/ohai-mcp"],
"env": {
"OHAI_DEFAULT_IMAGE": "claude",
"OHAI_TRANSITION": "ghost"
}
}
}
}codex-cli
~/.codex/config.toml:
[mcp_servers.ohai]
command = "node"
args = ["/path/to/ohai-mcp/bin/ohai-mcp"]
[mcp_servers.ohai.env]
OHAI_DEFAULT_IMAGE = "openai"
OHAI_TRANSITION = "glow"Auto-launch (Hyprland)
exec-once = qs -p /path/to/ohai-mcp/ohai/shell.qmlEnvironment Variables
Variable | Description |
| Default image when |
| Default transition effect (e.g., |
| Bezel rendering mode. Unset (default): translucent plate suitable for compositor blur. |
| Override project-profile config path. Defaults to |
| Override the global focus-action compositor. Supported values: |
| Override origin-window capture. Supported values: |
| Override the |
Project Profiles
Use project profiles to give notifications a visual identity based on the
originating project. The MCP caller passes projectPath; ohai matches it against
configured project roots and applies the matched profile's theme defaults.
Config path:
OHAI_CONFIG_PATHwhen set$XDG_CONFIG_HOME/ohai/config.yamlwhenXDG_CONFIG_HOMEis set~/.config/ohai/config.yamlotherwise
Global focus actions can be made compositor-aware with compositor: hyprland or
compositor: niri. OHAI_COMPOSITOR overrides the YAML value. If neither is
set, workspace and app focus actions keep the existing Hyprland command shape.
Origin capture is opt-in with originCapture: focusedWindow or
OHAI_ORIGIN_CAPTURE=focusedWindow.
Example:
compositor: niri
originCapture: focusedWindow
projects:
- id: ohai
name: ohai
roots:
- ~/d/software/ohai
theme: aquatic
workspace: dev
- id: infra
name: Infrastructure
roots:
- ~/d/infra
theme: terminal
transition: rippleBuilt-in themes:
Theme | Image | Color | Pattern | Transition |
|
|
| none |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Profile matching uses normalized roots and chooses the longest matching prefix.
Visual precedence is: explicit ohai tool input, then project overrides, then
theme defaults, then environment defaults and built-in defaults. Unknown themes
make config loading fail early.
Profiles may also define one focus target for notification actions:
compositor: niri
originCapture: focusedWindow
projects:
- id: ohai
roots:
- ~/d/software/ohai
theme: aquatic
workspace: dev
- id: terminal-tools
roots:
- ~/d/tools
theme: ghost
app: kittySupported profile focus fields are workspace, app, and windowId. Specify
only one per profile. Explicit workspace, app, or windowId values passed to
the ohai tool override the matched project profile focus target.
With compositor: niri, workspace uses
niri msg action focus-workspace <workspace> and windowId uses
niri msg action focus-window --id <id>. app focus is Hyprland-only because
niri does not expose a direct app-title focus action; for niri use workspace
or windowId.
With originCapture: focusedWindow, ohai runs
niri msg --json focused-window when sending a notification without an explicit
or project-profile focus target. If niri returns a focused window id, ohai uses
that id as the notification action target. Focus precedence is: explicit MCP
target, then project profile target, then captured focused window, then no focus
target.
Use the CLI helpers to inspect profile matching before wiring an MCP client:
bin/ohai-theme resolve ~/d/software/ohaiExample shape, with paths abbreviated for readability:
{
"configPath": "~/.config/ohai/config.yaml",
"projectPath": "~/d/software/ohai",
"matched": true,
"project": {
"id": "ohai",
"name": "ohai",
"theme": "aquatic",
"matchedRoot": "~/d/software/ohai"
},
"compositor": "niri",
"originCapture": "focusedWindow",
"visuals": {
"image": "openai",
"color": "#7ad7ff",
"pattern": "waves-01",
"transition": "glow"
},
"focus": {
"type": "workspace",
"value": "dev"
}
}When no profile matches, matched is false and project is null.
Hyprland Compositor Blur (recommended)
For the frosted-glass bezel effect, add the following to your hyprland.conf:
layerrule = blur, ^ohai$
layerrule = ignorezero, ^ohai$The anchored regex (^ohai$) ensures the rule applies only to the ohai notification surface. ignorezero tells Hyprland to skip blur on transparent regions of the surface — important because the popup window includes transparent padding around the visible bezel for transition-effect overflow; without this rule, the entire rectangular surface gets blurred.
Verify with hyprctl layers after reloading Hyprland — you should see ohai listed with the rules applied.
If you don't add these rules, the bezel will render as a translucent dark plate without compositor blur. This is still acceptable, just less visually distinct. Alternatively, set OHAI_BEZEL=fake for a more opaque bezel with a static noise texture.
Architecture
┌─────────────┐ qs ipc call ┌─────────────────┐
│ MCP Client │ ──────────────────▶ │ Quickshell │
│ (Claude) │ │ (shell.qml) │
└─────────────┘ └─────────────────┘
│ │
│ │
▼ ▼
┌─────────────┐ ┌─────────────────┐
│ ohai-mcp │ │ IpcHandler │
│ (Node.js) │ │ target: "ohai" │
└─────────────┘ └─────────────────┘The MCP server communicates with Quickshell via its built-in IPC mechanism (qs ipc call), which uses a Unix socket for instant, reliable delivery.
The internal notify IPC call takes 9 ordered arguments: title, body, severity, timeoutSeconds, pattern, image, actionTarget, color, transition. actionTarget is empty, an app/window title, workspace:<id>, window:<id>, or a compositor-prefixed target such as niri:workspace:<name> or hyprland:app:<title>.
Development
npm run dev # Watch mode
npm run build # Build
npm run lint # Lint
npm run format # FormatRequirements
Linux with Quickshell
Node.js 18+
Credit
Original implementation adapted from neotanx/neomcps - sound notification mcp.
Available Tools
2 toolsdisplay_polybar_messageA
Display a message in polybar status bar. Useful for notifying the user when an operation is complete or when waiting for user input.
| Name | Required | Description | Default |
|---|---|---|---|
| background | No | Background color for the message (default: #333333) | |
| color | No | Text color for the message (default: #ffffff) | |
| duration | No | Duration in seconds to display the message (default: 5) | |
| message | Yes | The message to display in polybar |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It describes the tool's purpose and use cases but lacks details on behavioral traits such as permissions needed, error handling, or whether it's read-only or destructive. The description doesn't contradict annotations (none exist), but it's minimal on behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately sized with two concise sentences that directly state the tool's function and its usefulness. It's front-loaded with the core purpose and avoids unnecessary details, making it efficient and easy to understand.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (4 parameters, no output schema, no annotations), the description is adequate but incomplete. It covers purpose and usage but lacks details on behavioral aspects like return values or error conditions. With no output schema, it should ideally mention what happens after display, but it's minimally viable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds no additional parameter semantics beyond what's in the schema, such as explaining parameter interactions or edge cases. This meets the baseline of 3 when schema coverage is high.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Display a message') and target ('in polybar status bar'), providing a specific verb+resource combination. However, it doesn't explicitly differentiate from the sibling tool 'show_popup_notification' beyond mentioning polybar specifically, which is good but not fully comparative.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use this tool ('Useful for notifying the user when an operation is complete or when waiting for user input'), giving practical scenarios. It doesn't explicitly state when not to use it or name alternatives like the sibling tool, but the context is sufficient for informed usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
show_popup_notificationB
Show a popup notification using notify-send/dunst. Useful for notifying the user when an operation is complete or when waiting for user input.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | Icon name or path for the notification | |
| message | Yes | The notification message | |
| timeout | No | Notification timeout in milliseconds (default: 5000) | |
| title | Yes | The notification title | |
| urgency | No | Notification urgency level (default: normal) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions the tool uses 'notify-send/dunst' which implies system-level notification behavior, but it doesn't disclose critical traits like whether it requires specific permissions, if it's synchronous/asynchronous, error handling, or platform dependencies. For a tool with no annotation coverage, this leaves significant gaps in understanding its operational behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise and well-structured: two sentences that directly state the tool's purpose and usage context. Every word earns its place with no redundancy or fluff. It's front-loaded with the core functionality and follows with practical guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (5 parameters, no output schema, no annotations), the description provides basic completeness but has gaps. It covers what the tool does and when to use it at a high level, but lacks details on behavioral traits, error conditions, or integration with the sibling tool. Without annotations or output schema, more context would be helpful for full understanding.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with all 5 parameters well-documented in the schema (e.g., 'message' as notification content, 'urgency' with enum values). The description adds no parameter-specific information beyond what's in the schema. According to the rules, with high schema coverage (>80%), the baseline is 3 even with no param info in the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Show a popup notification using notify-send/dunst.' It specifies the verb ('show'), resource ('popup notification'), and implementation method. However, it doesn't explicitly differentiate from its sibling tool 'display_polybar_message' beyond mentioning 'popup' vs. 'polybar' in names, leaving some ambiguity about when to choose one over the other.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides implied usage guidance: 'Useful for notifying the user when an operation is complete or when waiting for user input.' This gives context on when to use it, but it doesn't explicitly state when NOT to use it or mention the sibling tool as an alternative. The guidance is helpful but lacks explicit exclusions or comparisons.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v1.0.0- First observed
display_polybar_message - First observed
show_popup_notification
TDQS
Scored across 2 tools
The two tools have overlapping purposes—both are described as 'useful for notifying the user when an operation is complete or when waiting for user input.' This creates ambiguity, as an agent might struggle to choose between displaying a message in the polybar status bar versus showing a popup notification, since their use cases are not clearly differentiated. The descriptions do not specify distinct scenarios or advantages for each tool, leading to potential misselection.
The tool names follow a consistent verb_noun pattern with clear, descriptive terms: 'display_polybar_message' and 'show_popup_notification.' Both use snake_case and start with action verbs ('display' and 'show'), making them predictable and easy to understand. There are no deviations in naming conventions, ensuring readability and coherence.
With only 2 tools, the server feels thin for a notification domain that could benefit from more granular operations, such as different notification types, priorities, or durations. While the count is minimal, it may be too few to cover common notification workflows effectively, limiting the server's utility and causing agents to work around gaps in functionality.
Inferring the domain as user notifications, the tool set is severely incomplete. It lacks operations for managing notifications (e.g., dismissing, updating, or clearing notifications), handling different notification styles or priorities, or integrating with other notification systems. This creates significant gaps that could lead to agent failures when trying to perform common notification-related tasks beyond basic display.
Maintenance
Related MCP Connectors
Model Context Protocol server for Studex tools, notifications, and profile integrations
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
A Model Context Protocol (MCP) server for Selise Blocks Cloud integration
A Model Context Protocol server for Wix AI tools
Related MCP Servers
- AlicenseBqualityDmaintenanceAn educational implementation of a Model Context Protocol server that demonstrates how to build a functional MCP server for integrating with various LLM clients like Claude Desktop.1164MIT
- AlicenseNot gradedqualityAmaintenanceA Model Context Protocol server enabling AI systems to send real-time notifications to phones, desktops, and other devices through the ntfy publish/subscribe service.537 npm20Apache 2.0
- AlicenseAqualityDmaintenanceA Model Context Protocol (MCP) server designed to easily dump your codebase context into Large Language Models (LLMs).13 npm3Apache 2.0
- AlicenseBqualityDmaintenanceA Model Context Protocol server that allows LLMs to execute shell commands with explicit user permission through desktop notifications.214 npm1MIT