qsmcp
Click on "Install 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., "@qsmcpPreview components/Bar.qml with modelData screen:0"
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.
qsmcp
An MCP server for developing Quickshell widgets with an AI coding agent. Its headline tool renders one widget to a PNG — offscreen, sandboxed, without capturing your screen and without touching your running shell.
The image comes back in the tool result, so the model actually sees what it built.
Why
Iterating on a Quickshell widget normally means restarting the whole shell and
screenshotting the entire desktop. That is slow, leaks everything else on
screen, and disturbs the live session. qs_preview renders just the component.
The render primitive is Qt's Item.grabToImage(callback, targetSize), which
re-renders the item subtree into a framebuffer object. It is not a screen
grab and not a window readback, and it works on an item far larger than its
host window — a 1920×40 bar is captured from an 8×8 window.
Related MCP server: can-see
Requirements
Quickshell (
qs) and Qt 6Node 22+ — Node strips the TypeScript natively, so there is no build step
kwin_wayland(optional) — only needed to previewPanelWindow-rooted components
Install
git clone https://github.com/fedsfarm/qsmcp ~/Projects/qsmcp
cd ~/Projects/qsmcp && npm installRegister it with Claude Code, project-scoped so it only loads inside your shell
repo. QSMCP_SHELL_ROOT is the directory holding your shell's root QML file:
cd ~/my-shell-repo
claude mcp add qsmcp --scope local \
-e QSMCP_SHELL_ROOT=$HOME/my-shell-repo/quickshell \
-- node $HOME/Projects/qsmcp/src/index.ts serveOr write it into .mcp.json / any MCP client config by hand:
{
"mcpServers": {
"qsmcp": {
"type": "stdio",
"command": "node",
"args": ["/home/you/Projects/qsmcp/src/index.ts", "serve"],
"env": { "QSMCP_SHELL_ROOT": "/home/you/my-shell-repo/quickshell" }
}
}
}With QSMCP_SHELL_ROOT unset it falls back to $XDG_CONFIG_HOME/quickshell.
Verify the handshake without a client:
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' \
| QSMCP_SHELL_ROOT=~/my-shell-repo/quickshell node src/index.ts serveTools
tool | what it does |
| Render one component to a PNG and return the image plus structured QML diagnostics |
| A component's root type, imports, properties, signals, functions, and a preview hint |
| List components with root types, optionally filtered |
| Read-only. Running Quickshell instances |
| Read-only. |
| Read-only. |
| Read-only. |
No tool mutates the running shell. There is deliberately no ipc call, no
reload and no screen capture.
qs_preview arguments
arg | meaning |
| Bare name ( |
| Raw QML to render instead of an existing component. A complete document; relative imports like |
| Property values to set on the component, e.g. |
| Explicit logical size. Omit to use the component's implicit size |
| Device pixel ratio, 0.25–4. Default 2 |
|
|
| Colour for |
| Logical px around the component. Default 12 |
| Value for a required |
| QML evaluated before mounting, e.g. |
| QML that must be true before capture, e.g. |
| Deadline for reaching a stable frame. Default 8000 |
| Also copy the PNG to this path |
Two traps worth knowing:
A component gated on an
open/visibleproperty renders as a solid rectangle and still reportsok: true. Pass the gating props, or you are looking at a confidently-empty image.qs_symbollists which props exist.widthalso sets the virtual screen width forPanelWindowcomponents, and they size themselves against it. Pass the real monitor width and crop afterwards rather than shrinking the surface.
How isolation works
Offscreen (default). The generated harness sets its own environment before
QGuiApplication exists, using Quickshell's pragma block:
//@ pragma Env QT_QPA_PLATFORM=offscreen
//@ pragma ShellId qsmcp
//@ pragma DataDir/StateDir/CacheDir <scratch>The server additionally unsets WAYLAND_DISPLAY and
HYPRLAND_INSTANCE_SIGNATURE, so the Wayland backend and Hyprland IPC stay
down — a preview cannot map a surface, capture a screen or dispatch a
compositor command. DISPLAY is kept, because libqoffscreen has a GLX path
(QOffscreenX11GLXContext) that gives hardware GL, which is what makes
ShaderEffect and MultiEffect blur render correctly. No window is ever
mapped on it.
Nested. PanelWindow cannot even be constructed offscreen ("No PanelWindow
backend loaded"), so window-rooted components get a private kwin_wayland --virtual instance on its own XDG_RUNTIME_DIR. KWin implements
zwlr_layer_shell_v1, so the type resolves. That virtual output exposes no
usable EGL config to clients, so Qt Quick falls back to the software renderer
there — reported as degraded: [shadereffect, blur, layer_effects] rather than
silently producing a flat image. Blur fidelity still means a real session.
The overlay
Quickshell.shellDir is the directory of the root QML file, and real configs
derive shellDir + "/themes" and shellDir + "/scripts" from it. So the
harness runs from an overlay: a scratch directory under
$XDG_RUNTIME_DIR/qsmcp/<hash>/shell that mirrors your tree file-by-file.
That buys three things:
Script stubs. Scripts that apply GTK/Qt/kitty themes, set wallpaper or change hardware are replaced with executable no-ops. The stub must exist, not be absent — some configs fall back to a hardcoded absolute path when the
shellDircopy is missing, so a missing stub would silently run the real theme applier. Anything a preview tried to invoke is reported asside_effects_blocked. Only executables are ever stubbed; QML is never touched.Correct relative imports. A file loaded as
<overlay>/components/Foo.qmlresolvesimport "../config"against its URL, so singletons instantiate exactly once. Mixing real and overlay URLs would create twoThemeobjects.A shadow
$HOME. Per-entry symlinks to your real home, except the config directories your shell writes to, which are real copies — soConfig.set()and equivalents land in scratch. Directories referenced viaQuickshell.env("HOME") + "/..."are detected automatically; large trees (wallpaper libraries) stay symlinked.
Nothing is ever written under your shell tree. $XDG_RUNTIME_DIR is tmpfs, so
logout is a hard reset.
Settle
Previews never sleep a fixed interval. The harness waits for ready_expr, then
grabs a downscaled probe frame every 120 ms and requires two byte-identical
frames before the real capture. A component that never settles (a clock with
seconds, a spinner) returns the image with settled: false and
reason: "timeout" rather than pretending. pre_script: "Anim.enabled = false"
usually fixes it.
CLI
The same renderer without a client, for scripting or debugging:
QSMCP_SHELL_ROOT=~/my-shell/quickshell node src/index.ts render ClockWidget --out /tmp/clock.png
QSMCP_SHELL_ROOT=~/my-shell/quickshell node src/index.ts render Bar --width 1920 --height 40 --dpr 1 --padding 0
node src/index.ts render --help
node src/index.ts info # resolved config, scratch paths, detected copy pathsThe CLI writes JSON to stdout and progress to stderr; you then have to open the PNG yourself. Through MCP the image is in the tool result, which is why the server is the intended path.
Environment
var | effect |
| Directory holding the shell's root QML. Defaults to |
| Path to the |
| Comma-separated script names to stub on top of the defaults |
| Comma-separated script names to force-keep live |
|
|
Known limits
Contact sheets, synthetic hover/press (
QtTest), and a warm daemon are not implemented; every render is a cold spawn (~2 s).The Quickshell API knowledge layer (parsing
*.qmltypesand the annotated headers) is not built yet.qmllintintegration is not wired. Note that/usr/bin/qmllintis often Qt 5.15; the usable one is/usr/lib/qt6/bin/qmllint.Nested (window-rooted) previews are software-rendered, so blur and shader effects do not appear.
probes/RESULTS.md records the containment and non-interference probes the
design is built on.
License
GPL-3.0-or-later. See LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceAn AI-agent-first framework for building MCP servers that deliver interactive React widgets directly within AI chat interfaces like ChatGPT and Claude. It includes automated visual testing and a zero-config local development environment designed for autonomous agent workflows.MIT
- Alicense-qualityBmaintenanceMCP server that lets AI agents see and interact with terminal/CLI applications through virtual terminals and PNG screenshots.81MIT
- Alicense-qualityDmaintenanceAn MCP server that enables AI agents to capture targeted screenshots of specific application windows on Windows and Linux, with smart window state restoration and focus management.MIT
- Alicense-qualityDmaintenanceMCP server for capturing screenshots of desktop windows on Windows. Allows AI assistants to see what's on screen for UI development, debugging, and iterating on designs.MIT
Related MCP Connectors
MCP server for Wan AI video generation
Screenshot and HTML render MCP server for AI agents
MCP server for Hailuo (MiniMax) AI video generation
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/fedsfarm/qsmcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server