Pyre MCP Server
Allows Pyre's built-in assistant to use Google Gemini models (e.g. gemini-2.5-flash) to respond to talk-lane messages and manage notes using a Gemini API key.
Integrates with a local Ollama server to run models like qwen2.5:3b entirely on-machine, letting Pyre's assistant respond to talk-lane messages and manage notes without external APIs or API keys.
Allows Pyre's built-in assistant to use OpenAI models (e.g. gpt-5) to respond to talk-lane messages and manage notes using an OpenAI API key.
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., "@Pyre MCP Servercreate a note: team sync / prepare agenda / tomorrow 10am"
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.
Pyre — burning desktop notes
A frameless, transparent, always-on-top rail of square paper notes on the edge of your monitor. A note with a deadline burns away from its bottom edge in the last two hours. Drag notes anywhere; burning ones claim the top-left unless you moved them yourself. Everything is readable and writable by an AI agent. One portable Pyre.exe.
Build contract: CLAUDE.md. Design files: reference/.
Run it
Portable: put Pyre.exe anywhere and double-click it. No install, no admin. To keep the data beside the exe (USB stick, synced folder), create an empty file named pyre.portable next to it — or just create a pyre-data/ folder next to it.
Installed: Pyre-Setup-x.y.z.exe adds a Start Menu entry. Data lives in %USERPROFILE%\.stickyburn\.
From source:
npm install
npm test # 63 tests: burn curve, grid, parser, talk lane, store, MCP stdio + HTTP end-to-end
npm start # build + launch
npm run dist # release/Pyre.exe (single-file portable) · Pyre-Setup-*.exe (installer) · Pyre-*-win.zip (portable folder)Related MCP server: Bruin
Use it
Type into the entry box: topic / comment / due then Enter.
winwater / send BEP to Powell / fri 9am/ just a thought→ files under UNSORTEDDue forms:
todaytonighttomorrowfri3d2w4h8/212026-08-218/21 3pmfri 9am3pm. Default time 17:00 (settings).Three chips preview the parse live. The due chip always shows the resolved date, never what you typed. If the last segment isn't a date it stays part of the comment.
Talk lane. A line starting with
>is a message to the connected agent rather than a note:> move winwater to friday. It appears in a lane under the composer, the agent reads it withlist_messagesand answers withsend_message.>outranks everything, so a message may contain slashes freely.Bar commands. A line with no
/readingquit(orexit) closes the app — the chip row swaps to a singleQUIT PYREchip so you can see what Enter will do before you commit to it. Anything containing a/is always a note, so/ quitstill files a note.
Action | Mouse | Keyboard (note focused) |
Mark done (snuff) | hover → ✓ |
|
Edit in place | hover → ✎ |
|
Bank 2h / rest of day (snooze — never changes the due date) | hover → ◐ / |
|
Release a pinned note back to auto | hover → ⇱ |
|
Delete | hover → ✕ |
|
Move (pin) | drag it | — |
Cancel a drag |
| |
New note from anywhere |
| |
Done archive / Settings |
|
|
Resize the rail (280–420) | drag its inner edge — open Settings for a visible ◂▸ handle (arrow keys work too) | |
Move the window | drag the invisible 28px strip along its top | |
Quit | tray icon → Quit Pyre | type |
Talk to the agent |
| type |
Placement rule. Auto notes sort by heat (hottest top-left), then dated above undated, then newest. A note you dragged holds its cell forever — a fire will flow around it, never evict it. If a pinned note catches fire below the fold, an ember pip appears bottom-right; click it to scroll there.
States. cold · warming (due set, > 2h out; the firelight ramps over the last 24h) · due · burning · critical · overdue · gone-out (> 7 days overdue) · banked.
Where the data is
One JSON file, resolved in this order (first hit wins):
PYRE_DATAenvironment variable → that folderpyre.portablemarker beside the exe →.\pyre-data\.\pyre-data\beside the exe already exists and is writable → portable%USERPROFILE%\.stickyburn\→ installed
notes.json and settings.json live together there. Settings → Data shows the exact resolved path (and warns if a portable location wasn't writable and it fell back). Writability is tested with a real temp-file write, because USB sticks and locked corporate folders lie.
The file format is the contract (see src/shared/types.ts):
{
"version": 2,
"notes": [{
"id": "n_8f2k4a", "topic": "WINWATER", "comment": "Send BEP to Powell",
"due": "2026-08-21T22:00:00.000Z", // ISO UTC or null
"created": "…", "updated": "…", "done": false, "doneAt": null,
"bankedUntil": null, "bankedAt": null, // snooze; never touches due
"placement": { "mode": "auto" }, // or { "mode":"manual", "col":0, "row":2, "pinnedAt":"…" }
"source": "user" // "agent" draws a 2px tick on the left edge
}],
"messages": [{ // optional; the talk lane
"id": "m_4k2p9a", "role": "user", // "user" | "agent"
"text": "move winwater to friday",
"created": "…", "read": false // read = the OTHER side has seen it
}]
}burn, warmth and state are never stored — they are derived from due and the clock every tick.
AI access
Two paths, one file, one watcher.
1. Just edit the file
Point Claude Code (or any script) at notes.json. Writes are picked up within ~300 ms and animate onto the wall; the app's own writes are atomic (.tmp → fsync → rename) so a half-written file is never read. Give the agent src/shared/types.ts as the schema.
2. Built-in MCP server — two transports, same tools
Tools: list_notes (with computed burn/state/slot) · add_note · update_note · move_note · release_note · bank_note · snuff_note · restore_note · delete_note · parse_line (dry-run the grammar) · get_grid (occupancy map + first free cell) · list_messages and send_message (the talk lane).
The talk lane is pull-based. When the user types > … it is written to the file and shown as waiting, but nothing pushes it to you — an agent sees it when it calls list_messages. Poll that at the start of a session, or whenever the user mentions Pyre. The app and the MCP server share src/shared/heat.ts, so an agent's burn is exactly what's on the screen.
Settings → AI access shows both configs with the correct path/port and COPY buttons.
(a) Local HTTP endpoint — works from any build while the rail is open, no Node needed
The running app serves streamable-HTTP MCP at http://127.0.0.1:41777/mcp (port in Settings; 0 = off; loopback only).
{ "mcpServers": { "pyre": { "url": "http://127.0.0.1:41777/mcp" } } }Client | How |
Cursor | paste into |
Claude Code |
|
Claude Desktop | stdio only today — use (b), or bridge with |
(b) stdio — Pyre.exe --mcp, works even when the rail is closed
{ "mcpServers": { "pyre": { "command": "C:\\path\\to\\Pyre.exe", "args": ["--mcp"] } } }Client | Where |
Claude Desktop |
|
Cursor |
|
Claude Code |
|
Which Pyre.exe? The installer build and the portable folder build (
Pyre-x.y.z-win.zip, unzip anywhere) are the real binary and answer stdio MCP. The single-file portablePyre.exeis an NSIS launcher that extracts and starts the real app without passing stdio through, so--mcpcannot work through it — Settings detects this and shows only the HTTP config. Running from source:command=node_modules\electron\dist\electron.exe,args=["<repo path>", "--mcp"].
3. Built-in assistant — Pyre answers > lines itself
If no external agent is connected, Pyre can drive the same tools directly. Settings → Assistant picks a provider; TEST does one real round trip and tells you what it found — reachable, model missing, or reachable-but-cannot-call-tools.
Provider | Key | Default model |
Anthropic |
|
|
Google Gemini |
|
|
OpenAI |
|
|
OpenRouter |
|
|
Ollama (local) | none |
|
Custom | optional | any OpenAI-shaped endpoint |
Keys are encrypted at rest with the OS keystore (DPAPI on Windows) in credentials.bin, never written to settings.json and never handed to the renderer — the window can set, clear and check a key but cannot read one back. ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY and OPENROUTER_API_KEY are picked up from the environment if no key is stored.
Ollama — local models, measured
Nothing leaves the machine, no key, no bill:
ollama pull qwen2.5:3bThen Settings → Assistant → Ollama (local) → TEST. Pyre talks to http://127.0.0.1:11434/v1; the first message after a cold start takes ~20 s while the model loads.
Why qwen2.5:3b is the default — a head-to-head on casual phrasing, same prompt, same wall:
Ask | qwen2.5:3b (1.9 GB) | phi4-mini (2.5 GB) |
"add get groceries tmorrow" | ✓ | ✓ |
"remind me to call mom on friday" | ✓ one note | added a spurious second note, garbled reply |
"im done with the groceries one" | ✓ | ✓ |
"push bob back a couple days" | ✓ deadline moved | grid-moved the note; deadline untouched |
phi4-mini still works — Pyre reads its text-formatted tool calls, holds it to temperature 0, and corrects it when it claims a change it never made ("Added X" with an empty wall behind it gets a warning appended rather than repeated as fact). But interpreting casual language into the right tool is the actual job, and the smaller qwen does it reliably. llama3.1 (4.9 GB) is also solid if you have the headroom. Plain phi4 (14B) has no tool template at all: it can talk about the wall, never change it.
Queue — one Smartsheet sheet as paper on the rail
QUEUE next to the TALK button opens the lane with one Smartsheet sheet laid out as paper boxes above the notes: one box per top-level row, the column header stated once on the desktop, the first open task carrying the warm tint. A sheet is another kind of paper — same stock, same ink, same cut corner — and it never burns.
Two-way, for cells. Check tasks off and edit any editable cell in place (text, dates typed the way the composer takes them, picklists, contacts). Structure — rows, steps, columns — is made in Smartsheet; the lane shows it. Writes paint at once (a hollow tick until Smartsheet answers, solid once synced), and a refused write is held and retried every 10s with the strip under the stack saying what is held and when. Locked rows and columns, computed cells and view-only tokens say why a click did nothing.
Every column type renders. Text and numbers, dates, durations, contacts (initials squares), picklists (hairline chips), every symbol set re-encoded as shape and fill — a filled pennant, a Harvey-ball ring, ascending priority bars, a ringed check — formula cells with an fx mark, system columns, predecessors, and an unknown type shows its value with a
RAWmark rather than nothing.Subtasks. A row with children carries a strip:
3 STEPS · 1 DONEand one segment per step. Click it and the steps unroll on the same sheet;+ STEPadds a child row in Smartsheet.Sheet order, always. The filter (one column, values to show or hide, or hide done) only shows and hides, and
12 · 4 HIDDENstays up while it is on.Read-only tokens get the same live table with every edit affordance removed.
Setup: Settings → Queue — paste a Personal Access Token (Smartsheet → Personal Settings → API Access; stored encrypted, same rules as AI keys), then the sheet id or the sheet's URL (resolved through your sheet list), then TEST. Pick the done column and up to three columns beside the task there too. Advanced Smartsheet logic stays in Smartsheet; formulas arrive computed and are never evaluated here. Sync is a poll of the sheet's version (10s by default, 2s for half a minute after your own edits).
What the queue assumes about the sheet, and what it does when that is not so.
Smartsheet side | Pyre |
A primary column (every sheet has one) | The task text. Always shown, always first. |
A plain CHECKBOX column | The done toggle, hide-done and the subtask strips. Flag and star checkboxes are symbols, not "done". More than one plain box → the first, or pick one in Settings → Queue. No CHECKBOX at all → no check slot, no hide-done, no strips; everything else works. |
Up to three more columns beside the task | Guessed as owner (contact), phase (picklist), due (date); override in Settings. Narrow rails drop the last one first so the task text keeps 130px. Hidden columns are never guessed. |
Contact columns with a contact list | The list becomes the picker. Without one, a free |
Picklists with validation on (all symbol sets) | Only the options are offered. Validation off → the options plus a free entry. |
Formula cells, formula columns, system columns (auto-number, created/modified), cell links | Rendered read-only with a mark; a click explains why. |
Locked rows and columns | Drawn locked; a click explains. When Smartsheet unlocks them, the next poll makes them editable. |
Parent/child rows | One level of steps per parent; deeper levels flatten with a |
Hierarchy, dependencies, Gantt, cross-sheet references, automations | Left to Smartsheet. Durations and predecessors show as text. |
More than 1000 rows | The first 1000, with a note. |
Viewer or commenter share | Live, display only. |
A regional instance (EU, AU) |
|
Rate limits | Backs off for the |
UI tests: bash test/ui/qa.sh start s1 then bash test/ui/qa.sh run s1 test/ui/scenarios/*.mjs
drives the real window with real input through the debug driver against the fake
(reference/queue/QUEUE-UI-TEST-PLAN.md is the checklist).
Dev: npx tsc -p tsconfig.fake.json && node dist-fake/test/run-fake.js runs a fake Smartsheet on
:41999 with a sheet of every column type; point the app at it with
SMARTSHEET_API_BASE=http://127.0.0.1:41999/2.0 and SMARTSHEET_ACCESS_TOKEN=good-token, sheet
id 7351019284673412. The same fixture renders in the vite preview at ?queue
(?queue=viewer, =fail, =offline, =empty, =stale for the other states).
Accessibility
prefers-reduced-motion gives a full alternate presentation (no transitions, no embers, no flicker — front position, bloom and the countdown carry the reading). Every note is Tab-reachable with all actions on keys; the accessible name reads like "WINWATER. Send BEP to Powell. Due in 6 minutes. Pinned to column 1, row 3."
Layout of the repo
src/main Electron main: window, tray, hotkey, paths, store (atomic + watcher), IPC, --mcp branch
src/preload contextBridge → window.pyre
src/renderer plain TS + CSS: grid, drag, composer, sheets, embers (styles/burn-system.css is verbatim, do not edit)
src/shared heat.ts (THE burn curve) · grid.ts (THE placement rule) · parse.ts (THE grammar) · types · migrate
src/mcp stdio MCP server (imports the shared modules + the store)
test vitest: pyre (spec), parse, store, mcp
reference/ the approved design files, kept for comparison
scripts/ dev helpers (debug driver client, icon)Dev notes
PYRE_DEBUG=1logs store/watcher activity.PYRE_DEVTOOLS=1opens DevTools.PYRE_DEBUG_DRIVER=<dir>enables a file-driven debug driver (scripts/drive.mjs) that can screenshot and script the live window.Running the renderer outside Electron (
npx vite --config vite.config.mts) installs an in-memory mock bridge with sample notes in every state — handy for visual checks againstreference/note-example.html.Decisions taken on the spec's open items: 9A gone-out (not 9B stalled); 24h ambient warmth kept; a pinned note never auto-releases; no snuff confirmation; single display via settings.
reserveScreenSpaceregisters the rail as a Windows AppBar (via koffi →SHAppBarMessage), so maximised windows stop at its edge. Two things make it fiddly and are worth knowing if you touch it: the reservation must be computed from the monitor rect, not the work area (which already excludes your own bar, so it walks across the screen), and it must be in physical pixels, not Electron's DIPs. The whole path is wrapped so an FFI failure only means the setting does nothing.Talk-lane messages live in the same
notes.jsonunder an optionalmessagesarray, so the one watcher delivers both. A file without the key stays valid and only gains it when the lane is first used.One correction to the shipped spec:
spec/heat.tsfroze a banked note's burn as ofbankedUntil(the future) and failed its own test; the record now carriesbankedAtand the front freezes at the burn it held when banking began.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
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 Connectors
Google Keep-style notes app with an MCP server for AI agents to read/write notes.
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
MCP-native notes and memory for ChatGPT, Claude, and other AI tools.
One memory, every AI. A shared, user-owned markdown memory your AI clients read and write over MCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to create, read, and summarize sticky notes stored in a local text file through MCP tools, resources, and prompts.-
- AlicenseNot gradedqualityCmaintenanceMCP server for AI agents to read, write, and organize notes in a local-first, human-in-the-loop note-taking app.62MIT
- AlicenseAqualityDmaintenanceEnables creating, reading, updating, and deleting sticky notes with persistent text file storage through the MCP protocol.4MIT
- AlicenseAqualityBmaintenanceA local MCP server that provides AI agents with persistent sticky-note memory, storing Markdown notes on disk and offering tools for creating, reading, updating, deleting, searching, and listing notes across sessions.13MIT
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/MothBlight-Git/pyre'
If you have feedback or need assistance with the MCP directory API, please join our Discord server