Hearth
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., "@Hearthrun npm test in D:\projects\hearth and summarize any failures"
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.
Hearth v0
Hearth is a small local-first MCP server that gives ChatGPT a durable, bounded Windows operating substrate without turning every recipe into another MCP tool. It uses the current split MCP v2 packages and Node 24's built-in SQLite. The MCP layer is stateless; jobs, generic futures, per-conversation mailboxes, artifacts, runs, recipes, receipts, continuations, and append-only events share one WAL-mode database.
Requirements and install
Windows 10/11 and Node.js 24+
Windows PowerShell 5.1 for UI Automation
cd D:\userdata\docs\projects\hearth
npm install
Copy-Item config.example.json config.json
npm test
npm startThe server prints Hearth listening at http://127.0.0.1:3000/mcp. Override the config location with $env:HEARTH_CONFIG='D:\path\config.json'. The bind is deliberately localhost-only. /mcp accepts POST only; all other routes return 404. SDK Host and Origin safeguards reject DNS-rebinding and cross-origin requests.
Data defaults to .hearth/hearth.db, with artifacts and backups beside it. Keep config.json, the database, and artifacts private: they may contain user data and command output. Hearth does not intentionally store tunnel credentials or raw OpenAI routing identifiers.
Related MCP server: DeskMCP
Architecture and safety
src/server.js mounts the MCP v2 createMcpHandler through the Node HTTP adapter. This supplies MCP 2026-07-28 server/discover and modern stateless calls while retaining the SDK's stateless legacy fallback. Each request gets a cheap McpServer; src/hearth.js owns shared SQLite and services.
Commands use direct executable/argv spawning (shell: false), bounded timeout and output, and a configurable concurrency cap. Large output spills to an artifact. Files must remain under configured roots after real-path/symlink checks. Writes, patches, and undo restore through same-directory temporary files plus rename, require an optional expected_sha256, and produce durable undo receipts. Transient Windows EPERM, EACCES, and EBUSY rename failures are retried with bounded exponential backoff; a recovered mutation reports replace_attempts. UI work is globally serialized and goes through a bounded STA PowerShell UIA bridge; fallback input is off unless allow_fallback is explicitly true and the request scopes it to an hwnd or process_id.
The seven ChatGPT-facing tools use strict operation-specific JSON Schemas. Unknown fields are rejected rather than silently discarded. run.scatter is deliberately closed-world: its kind discriminator can select only known Hearth primitives, so it cannot redispatch an arbitrary tool name plus arbitrary input. The only top-level tool that is entirely read-only is artifact, and it carries MCP read-only/idempotent annotations; the other six mix read and write operations, so a tool-level readOnlyHint would be inaccurate without splitting the public surface.
Every operation returns a concise state: normally completed, pending, blocked, conflict, partial, cancelled, or superseded. Tool errors also set MCP isError. Each Hearth-produced MCP response also includes bounded arrivals from previously detached work for the same conversation. Dispatch starts immediately and races dispatch_inline_budget_ms (100 ms by default); a slower operation returns {state:"pending",future_id} and its durable completion piggybacks on a later Hearth call without polling. Every tool also accepts the common transport hint detach:true, which forces that immediate future response even for a fast primitive and is stripped before primitive execution. Arrival seq values reflect completion order, not submission order, and large detached JSON spills to artifacts. Delivery is at-least-once: a retry of the same MCP request can re-offer the previous cursor, while the next distinct request acknowledges what was offered previously. Hearth fingerprints the full request ID for this retry distinction and does not persist the raw identifier.
Seven static tool contracts
All tools accept a JSON object with operation plus operation fields.
machine
host_infoexec:command, optionalargs[],cwd,env,timeout_msbatch:commands[]using the exec shape; at most 16, run concurrentlyprocess_listprocess_kill: positivepid, optionalsignal; refuses Hearth's own PID
Output above max_output_bytes has preview and artifact instead of a large body. Capture is hard-capped by max_artifact_bytes.
fs
list:path, optionallimitread:path, optionaloffset,limit,encodingsearch:path,query, optionallimit,max_entriesstat:pathwrite:path,data, optionalexpected_sha256(nullasserts the file does not exist)patch:path,replacements: [{old,new}], optionalexpected_sha256; everyoldmust occur exactly onceundo:receipt_id; conflicts if the post-write hash no longer matches
Search skips symlinks and files over 2 MB. Reads and result counts are bounded.
task
submit:joborjobs[]for durable executable work. Each job usescommandplus optionalargs,cwd,env,timeout_ms,delay_ms/run_at, anddependencies.get/status:idwait:id, optional boundedtimeout_mscancel:id; queued generic futures cancel atomically, while already-running generic work reports that it cannot be cancelled safelylist: optionallimit
Independent due jobs and generic futures run concurrently under separate configured caps. Failed or missing dependencies block dependents explicitly. Routed task status/list/wait/cancel access is isolated to the same conversation. Queued executable jobs survive restart and an interrupted durable exec job is requeued; interrupted generic work becomes blocked instead of replaying a possibly side-effecting call. The old open-world calls:[{tool,input}] descriptor is intentionally not exposed by the ChatGPT-facing MCP schema; heterogeneous orchestration is provided by the closed typed run.scatter contract below.
artifact
list: optionallimitmetadata:idread/range:id, optionaloffset,limit,encodingsearch:id,query, optionallimit
ui
snapshot: optionalhwndorprocess_id,max_depth(max 12),max_nodes(max 5000)query: same root plustargetaction: same root,target: {automation_id?,name?,control_type?},action(invoke,focus,set_value, or opted-in fallbackclick/key), optionalvalue,virtual_key,allow_fallback
Nodes normalize names, automation IDs, classes, control types, process IDs, state, bounds, patterns, children, and snapshot-local locators. UIA needs the same interactive desktop and generally cannot automate elevated applications from a normal process; these cases return blocked, never silent input. Tests safely snapshot and invoke only a disposable WPF fixture.
recipe
create:name, optionaldescription,steps[]list,get,stats,deleterun:name, optionalparamssuggest: optionalthreshold(default 3),min_length(default 2)
A step is {primitive,input}. Allowed primitives are machine.exec, fs.read, fs.write, fs.patch, and task.submit. ${name} substitution occurs inside data/argv, never a shell string. Runs are sequential, bounded by recipe_max_steps, traced, counted, and auditable. Suggestions report repeated contiguous primitive patterns; they never auto-create recipes.
run
checkpoint: optionalid, plusobjective,acceptance_criteria[],summary,next_actions[],pending_task_ids[]; pass the existingidto update a durable runscatter:calls[]with a closedkinddiscriminated union. Current kinds cover selectedmachine,fs,artifact, semanticui,recipe.run, plus read-onlytask.get/status/listandrun.get/list/eventsdiagnostics. Every call becomes a durable future immediately.list,get,closeschedule_continuation:run_id,prompt, optionaldelay_msor ISOrun_at, optionaltarget: {session}or{browser_tab}continuation_status:idevents: optionallimit,entity_id
Scatter dependencies may be batch indexes or same-conversation future IDs. A typed input field may instead be an exact future reference object {$future: index-or-id, $path: "result.field"}; that reference also creates the dependency automatically and is resolved only after the referenced future completes. The primitive itself remains fixed by kind, so dataflow does not become arbitrary redispatch. For example:
{
"operation": "scatter",
"calls": [
{ "kind": "fs.stat", "path": "D:\\userdata\\docs\\projects\\hearth\\README.md" },
{ "kind": "fs.read", "path": { "$future": 0, "$path": "result.path" }, "limit": 4096 }
]
}Independent calls can finish in any order and arrive by completion sequence. A dependent call waits without polling. Missing, failed, or cross-conversation references become explicit blocked futures. The read-only diagnostic kinds are intended for failure handling: one scatter can collect host/process state, files, artifacts, task/future state, durable run state, and events without a chain of explicit polling calls.
Due continuation records are explicit. With no Oracle target they become blocked; with a target and default oracle.armed: false, they expose a safe dry-run command. Hearth stores no Oracle/API secret and does not claim an external continuation happened. Arming only changes command construction metadata in v0; execution remains an explicit machine/task operation.
Windows unattended recovery
The canonical Windows recovery path uses short periodic Task Scheduler checks rather than a long-lived supervisor process. This avoids depending on a PowerShell parent process surviving indefinitely. Because UI Automation must remain in the logged-in interactive desktop, the tasks run as the current interactive user rather than as a Session 0 service.
For a command-free setup, double-click Hearth-Tunnel-Setup.cmd. The small Windows UI accepts the OpenAI Tunnel runtime key once, protects it with Windows DPAPI CurrentUser, locks the credential file ACL to the current user plus SYSTEM, installs the recovery tasks, and verifies Tunnel readiness. The plaintext key is not written to disk and the clipboard is cleared after saving.
Two tasks are installed:
Hearth-Runtime-Ensureruns at logon and every minute.scripts\ensure-hearth.ps1checks the configured localhost port and startsnode src/server.jsonly when Hearth is missing.Hearth-Tunnelruns at logon and every minute.scripts\ensure-tunnel.ps1checks/readyzand, when the Tunnel is missing, decrypts.hearth\secrets\hearth-tunnel.dpapionly in memory and launchestunnel-client run --profile hearth-local.
Both ensure operations are short-lived; a normal Task state is therefore Ready, not permanently Running. MultipleInstances=IgnoreNew prevents overlapping checks. Operational logs and the DPAPI credential stay under the gitignored .hearth directory. A full Windows reboot needs no Tunnel key re-entry after the user session logs on. Pre-login GUI operation is intentionally unsupported because Windows UI Automation cannot operate the interactive desktop before that session exists.
The older scripts\supervise-hearth.ps1 and run-tunnel-from-clipboard.ps1 remain as compatibility/manual fallback paths, but they are not required for unattended recovery.
Secure MCP Tunnel hookup
Hearth does not need a public bind or inbound firewall rule. Obtain a tunnel ID in OpenAI Platform tunnel settings, associate it with the ChatGPT workspace, and create a runtime key with Tunnels Read + Use. Download the current Windows build from https://github.com/openai/tunnel-client/releases/latest (use v0.0.14 or newer for MCP 2026-07-28).
With Hearth already running:
$env:CONTROL_PLANE_API_KEY = 'sk-...'
tunnel-client init `
--sample sample_mcp_remote_no_auth `
--profile hearth-local `
--tunnel-id tunnel_0123456789abcdef0123456789abcdef `
--mcp-server-url http://127.0.0.1:3000/mcp
tunnel-client doctor --profile hearth-local --explain
tunnel-client run --profile hearth-localThen enable ChatGPT developer mode, open Plugins, create an app, select Tunnel, and choose/paste that tunnel ID. Never place the runtime key in Hearth config or the tunnel profile itself. On Windows, use Hearth-Tunnel-Setup.cmd once to store the key with DPAPI and enable unattended recovery. run-tunnel-from-clipboard.ps1 remains available as a non-persistent manual fallback. This tunnel is for private/developer use, not public plugin submission.
Hearth hashes the tunnel's openai/session metadata with SHA-256 for mailbox routing and never passes raw OpenAI session, subject, organization, or request identifiers into persistence. Anonymous local stateless requests receive isolated request-scoped routes; local clients that need cross-request delivery can send hearth/conversation metadata. An early pending OpenAI response may include a reusable expiring route_probe nonce. The built-in loopback-only CDP binder can map that exact nonce to one unique ChatGPT tab with a stable /c/<id> conversation URL and persists only the route fingerprint, target ID, and stable conversation URL. Automatic wake is implemented but disabled by default; set wake.armed:true to enable it. It uses the already-bound loopback CDP target directly and does not require oracle.armed. When armed, Hearth wakes only a bound quiet conversation with undelivered terminal arrivals, using debounce, cooldown, and attempt limits; pending work alone never wakes a tab. The wake prompt asks ChatGPT to make one known-safe machine.host_info call, whose normal response piggybacks the ready arrivals, then continue the prior task from them. Oracle remains separate for explicitly scheduled continuation records.
Validation
npm testThe tests pin MCP 2026-07-28 and exercise discovery/list/call, strict schema rejection, the closed scatter contract including typed future references and read-only runtime diagnostics, two clients, fs conflict/undo/containment, transient atomic-replace retry injection, spill/range/search, immediate delayed/dependent/concurrent jobs, automatic and explicit detachment, async mailbox isolation/completion order/request-key retry cursors/bounds, heterogeneous future DAGs and restart safety, routing metadata normalization, CDP route binding and bounded wake submission, recipe stats/traces/suggestions, due dry-run continuations, a bounded desktop snapshot, and a semantic invoke against a disposable WPF window. The live Secure MCP Tunnel path, ChatGPT model behavior, DPAPI credential round trip, periodic Task Scheduler ensure path, and signed-in Windows recovery path are additionally verified manually because they require the interactive environment.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted runtime for persistent agent teams, durable workflows, memory, schedules, and goals.
Private-by-default, local-first memory/context/task orchestrator for MCP apps and agents.
- OctopadOAuthapp.octopad
The back-office workspace for your team's AIs: tasks, knowledge and context shared over MCP.
Cross-agent artifact workspace with provenance across Claude Code, Codex, Cursor, LangGraph.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables ChatGPT to securely operate a single Windows development workspace via a local MCP server, offering file editing, Git status, static analysis, approved test/build, and limited ADB operations with audit logging.MIT
- AlicenseNot gradedqualityBmaintenanceOpen-source Windows MCP policy gateway that gives ChatGPT controlled access to local files and terminal sessions with workspace-scoped permissions, guarded writes, session-owned processes, and audit logging.2Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables ChatGPT to safely interact with Windows workspaces through a loopback-only MCP server, with OAuth, bounded PowerShell tools, persistent jobs, and optional Cloudflare Tunnel exposure.MIT
- AlicenseNot gradedqualityAmaintenanceEnables ChatGPT on Windows to securely access codebases, Git, terminals, language servers, debuggers, SQLite, local HTTP services, adaptive project memory, engineering skills, checkpoints, audit logs, and sandboxed command execution through MCP tools.MIT