@hackettai/appengine-editor-mcp
v0.5.3
Published
MCP server that lets Cursor / Claude Desktop / Claude Code / any MCP-capable agent connect to AppEngine generated applications and edit their files with full file-system-like tools.
Maintainers
Readme
AppEngine Editor MCP
A Model Context Protocol (MCP) server that lets Cursor, Claude Desktop, Claude Code, or any MCP-capable agent connect directly to AppEngine-generated applications and edit their source files — with the same power you get from a local code editor.
It talks to the same endpoint the DIAL UI uses
(POST /chat/conversations/:id/save-files), so any change the agent makes
shows up instantly in the /appengine/conversation/:id page.
Quick start
Remote (recommended — zero config)
Just add a URL. First connection opens your browser for login — no secrets, no tokens, no env vars.
{ "mcpServers": { "appengine-editor": { "url": "https://dev.dialhackett.digital/api/mcp" } } }Works in Cursor, Claude Desktop, and Claude Code.
Local (stdio)
npx @hackettai/appengine-editor-mcpWhen started without credentials, the agent calls mcp_auth at runtime and
asks you for your Clerk secret key + session ID. No need to edit JSON configs.
What you get
31 tools exposed over MCP:
| Tool | What it does |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| Authentication | |
| mcp_auth | Provide Clerk credentials at runtime (stdio only — remote uses OAuth) |
| Project lifecycle | |
| list_projects | List AppEngine projects (my / accessible / shared-with-me / favorites / kainam / public) |
| search_projects | Search by keyword in conversation messages |
| get_project | Load a project into the session and list every file path it contains |
| activate_project | Lock the session to a single project (required before any mutations) |
| deactivate_project | Release the project lock |
| get_active_project | Return the currently locked project id |
| project_stats | File count, largest files, LOC summary |
| save_project | Persist everything via /save-files (same endpoint the DIAL UI hits) |
| delete_project | Delete the entire project (requires confirm: true) |
| Reading | |
| read_file | Return the full contents of one file |
| read_file_range | Return a line range from a file |
| read_all_files | Bulk read — return a JSON array with every file in the project in one call |
| search_in_files | Regex or literal search across all project files |
| Writing (staged) | |
| write_file | Create or fully overwrite a file |
| edit_file | Search-and-replace edit (unique match required unless replaceAll) |
| delete_file | Remove a file |
| rename_file | Rename or move a file to another path |
| copy_file | Duplicate a file to a new path |
| append_to_file | Append content to the end of a file |
| prepend_to_file | Prepend content to the beginning of a file |
| insert_at_line | Insert content at a specific line number |
| replace_lines | Replace a range of lines |
| regex_replace | Regex find-and-replace within a single file |
| global_replace | Regex find-and-replace across all project files |
| bulk_edit | Apply the same search-and-replace edit to multiple files |
| bulk_write | Create or overwrite multiple files in one call |
| CDN libraries | |
| add_cdn_library | Add a CDN script/stylesheet to the project |
| remove_cdn_library | Remove a CDN script/stylesheet from the project |
| Staging | |
| list_changes | Show what is staged for the next save |
| discard_changes | Revert staged edits back to the last loaded/saved baseline |
The agent can edit every file in a project — no allowlist, no extension
filter, no depth limit. Whatever is in the conversation (from App.tsx all
the way down to pages/…, components/…, data/…, config files, mock data,
.css, .svg, .yaml, etc.) is loaded into the working tree, and every
tool operates on an arbitrary path string. save_project always sends the
full file tree so new files and renames persist correctly.
All mutating operations (write_file, edit_file, delete_file,
rename_file) are staged in memory so agents can run many small edits
across dozens of files and commit them with a single save_project call —
matching how the DIAL frontend persists manual edits.
1. Install
Quick start (npx — no repo needed)
npx @hackettai/appengine-editor-mcpThat's it. npx downloads, caches and runs the MCP server in one step.
Use this in your Cursor / Claude Code config (see step 4).
From source (for contributors)
cd mcp-servers/appengine-editor
npm install
npm run buildThis produces dist/index.js which is the executable MCP server.
2. Authentication
Remote mode (OAuth — recommended)
When using the remote endpoint (https://dev.dialhackett.digital/api/mcp),
authentication is handled automatically via Clerk OAuth. The MCP client
opens your browser, you sign in with your DIAL account, and tokens are
managed transparently. No credentials to manage.
Local stdio mode
The local MCP needs to authenticate against the Mirador backend. Three options are supported:
Option A: Runtime mcp_auth tool (recommended for stdio)
No credentials in the JSON config at all. When the agent tries to use a
tool, it gets a 401 error and knows to call mcp_auth first. The agent
asks the user for their Clerk secret key and session id, passes them to
mcp_auth, and the MCP authenticates on the fly.
Get your Clerk secret key from the Clerk dashboard → API Keys (starts with
sk_test_orsk_live_).Get your session id from the browser console:
copy(window.Clerk.session.id);(starts with
sess_)When the agent asks, provide both values. The agent calls
mcp_auth({ clerkSecretKey, clerkSessionId })and you're authenticated.
If your session expires mid-conversation, the agent can call mcp_auth
again with a fresh session id — no need to restart anything.
Option B: Env vars (auto-refresh, for unattended use)
Pass CLERK_SECRET_KEY + CLERK_SESSION_ID as env vars in the MCP
config. The MCP mints and rotates JWTs automatically via the Clerk Backend
API. Useful for CI or unattended workflows where interactive prompts are
not available.
Option C: Static token (quick, short-lived)
- Open the browser JS console on any DIAL page.
- Run:
copy(await window.Clerk.session.getToken()); - Paste the JWT into
APPENGINE_API_TOKEN. Tokens expire in ~5 minutes.
3. Configure environment (stdio only)
Remote mode does not need any env vars — skip to section 4.
| Variable | Default | Required | Notes |
| ------------------------------ | ----------------------- | :------: | ------------------------------------------------------------------------- |
| APPENGINE_API_URL | http://localhost:3000 | ✓ | Same value DIAL uses for NEXT_PUBLIC_API_BASE_URL. |
| APPENGINE_TENANT_ID | — | | Clerk org id, sent as X-Tenant-ID (matches the DIAL proxy behaviour). |
| APPENGINE_REQUEST_TIMEOUT_MS | 60000 | | Per-request timeout in ms. |
| APPENGINE_MCP_DEBUG | 0 | | Set to 1 for verbose logs on stderr. |
| CLERK_SECRET_KEY | — | | Clerk secret key (sk_test_…). Optional — can use mcp_auth instead. |
| CLERK_SESSION_ID | — | | Active Clerk session id (sess_…). Optional — can use mcp_auth instead.|
| APPENGINE_API_TOKEN | — | | Static Clerk JWT. Short-lived fallback for quick one-off use. |
Authentication priority: mcp_auth (runtime) > env vars > static token.
If no credentials are configured at startup, the agent must call mcp_auth
before using any other tool.
4. Register the MCP with your client
Remote (recommended — no secrets needed)
Just provide the URL. The first connection opens a browser window for Clerk OAuth login, and tokens are managed automatically.
Cursor (~/.cursor/mcp.json):
{
"mcpServers": {
"appengine-editor": {
"url": "https://dev.dialhackett.digital/api/mcp"
}
}
}Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"appengine-editor": {
"url": "https://dev.dialhackett.digital/api/mcp"
}
}
}Claude Code:
claude mcp add --transport http appengine-editor https://dev.dialhackett.digital/api/mcpPrerequisite: The DIAL app must be deployed with the MCP route enabled, and Clerk must have OAuth + Dynamic Client Registration configured.
Local stdio (alternative — for offline/dev use)
If you prefer running the MCP locally (offline, dev, or when the remote
endpoint is unavailable), use the stdio transport with npx:
Cursor (via npx)
Open Cursor settings → MCP → Add new global MCP server (or edit
~/.cursor/mcp.json) and add:
{
"mcpServers": {
"appengine-editor": {
"command": "npx",
"args": ["-y", "@hackettai/appengine-editor-mcp"],
"env": {
"APPENGINE_API_URL": "https://your-api-host.example.com/api",
"APPENGINE_TENANT_ID": "org_your_clerk_org_id",
"CLERK_SECRET_KEY": "sk_test_your_clerk_secret_key",
"CLERK_SESSION_ID": "sess_your_active_session_id",
"APPENGINE_MCP_DEBUG": "1"
}
}
}
}Reload Cursor, open the MCP panel, and you should see appengine-editor
listed with 31 tools.
Claude Code (CLI)
Run this single command to register the MCP:
claude mcp add appengine-editor \
-e APPENGINE_API_URL=https://your-api-host.example.com/api \
-e CLERK_SECRET_KEY=sk_test_your_clerk_secret_key \
-e CLERK_SESSION_ID=sess_your_active_session_id \
-e APPENGINE_MCP_DEBUG=1 \
-- npx -y @hackettai/appengine-editor-mcpClaude Desktop
Edit the Claude Desktop config file:
- Windows (standalone):
%APPDATA%\Claude\claude_desktop_config.json - Windows (Microsoft Store):
%LOCALAPPDATA%\Packages\Claude_<id>\LocalCache\Roaming\Claude\claude_desktop_config.json - macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
Add the appengine-editor entry inside mcpServers:
{
"mcpServers": {
"appengine-editor": {
"command": "npx",
"args": ["-y", "@hackettai/appengine-editor-mcp"],
"env": {
"APPENGINE_API_URL": "https://your-api-host.example.com/api",
"CLERK_SECRET_KEY": "sk_test_your_clerk_secret_key",
"CLERK_SESSION_ID": "sess_your_active_session_id",
"APPENGINE_MCP_DEBUG": "1"
}
}
}
}Windows tip: If Claude Desktop doesn't find
npx, use the full path instead:"command": "C:\\Program Files\\nodejs\\npx.cmd".Microsoft Store version: The Store (MSIX) build reads its config from a sandboxed path under
AppData\Local\Packages\Claude_*\LocalCache\Roaming\Claude\. Use Settings → Developer → Edit Config to find the exact path.
Fully restart Claude Desktop after editing — changes are not hot-reloaded. Verify by checking Settings → Developer for the server status, or click the + button in the chat → Connectors to see available tools.
From source (local development)
If you have the DIAL repo cloned and prefer running from the built files:
{
"mcpServers": {
"appengine-editor": {
"command": "node",
"args": [
"/path/to/HackettAnalyticsLab/mcp-servers/appengine-editor/dist/index.js"
],
"env": {
"APPENGINE_API_URL": "http://localhost:3000",
"CLERK_SECRET_KEY": "sk_test_your_clerk_secret_key",
"CLERK_SESSION_ID": "sess_your_active_session_id"
}
}
}
}Windows note
If Windows rejects spawning .js directly, use the platform-specific
node.exe absolute path (find it with where node in PowerShell):
"command": "C:/Program Files/nodejs/node.exe"5. Typical agent session
> list_projects({ "scope": "my", "limit": 20 })
• 665f1c… — "Auto-match QA dashboard" — 42 msgs — updated 2026-05-01…
• 665dab… — "Interactive hierarchy explorer" — 18 msgs — …
> get_project({ "projectId": "665f1c..." })
{
"projectId": "665f1c...",
"flowType": "analytical_dashboard",
"fileCount": 24,
"files": [
"App.tsx",
"components/Chart.tsx",
...
]
}
> read_all_files({ "projectId": "665f1c..." }) # agent ingests full codebase
> edit_file({
"projectId": "665f1c...",
"path": "pages/MyTasksPage.tsx",
"oldString": "const REFRESH_MS = 30000;",
"newString": "const REFRESH_MS = 5000;"
})
> save_project({
"projectId": "665f1c...",
"instruction": "Refresh rate tuning"
})
Saved 1 change (24 total files sent): …Reload /appengine/conversation/665f1c... in DIAL and the UI will rebuild
against the new file tree immediately (the same way it does for manual
edits made in the built-in editor).
6. Safety model
- Staged, not live.
write_file/edit_file/delete_fileonly mutate an in-memory snapshot. The backend does not see a single byte untilsave_projectis called. That keeps agent exploration cheap and reversible viadiscard_changes. - Full-tree save. Just like the DIAL UI,
save_projectalways sends the full set of files currently in the working tree. This matches the backend's expectation and prevents drift from partial writes. - No HAL / AI side-channel. This MCP is purely the file-persistence surface. It does not trigger backend LLM runs or consume model budget. The agent doing the editing is whatever agent the user runs the MCP from (Cursor, Claude Code, etc.).
- Token scope. The server uses whatever bearer you pass. Give it a token with the same permissions the DIAL UI would have — nothing more.
7. Development
# Type-check without emitting
npm run typecheck
# Watch mode (runs against TS directly via tsx)
npm run dev
# Clean build output
npm run cleanUseful APPENGINE_MCP_DEBUG=1 prints every outbound HTTP call, tool
registration, and shutdown event on stderr — MCP clients show stderr in
their logs panel.
