Skip to content

feat: bring the mcp to parity with the app and cli - #929

Open
leynier wants to merge 17 commits into
mainfrom
feat/mcp-parity
Open

leynier wants to merge 17 commits into
mainfrom
feat/mcp-parity

Conversation

@leynier

@leynier leynier commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

Brings the MCP close to parity with the GUI and CLI, following docs/mcp-parity-implementation-plan.md (revision 2, decisions U1-U10 and F1-F4). One global PR by request; the commits follow the plan's phases.

  • Access model: a separate admin level (mcp:admin scope, unchecked at consent, never granted to existing grants) next to off/read/full. Catalog v2 adds idempotentHint, clientRequestId, structured content and error codes. 220 tools: 86 read, 112 full, 22 admin.
  • New Workspace from Prompt as a runtime operation: project inference, naming, section, creation, setup and agent launch run in the host, with persisted operations, retry keys and retryLaunch. Desktop and mobile now use the same service.
  • Management over MCP: runtime settings, workspaces (remove with UI semantics, wake, tree, buffer save), terminals, profiles (admin), inbox with attribution, orchestration, workflows and automations.
  • Pull requests on GitHub, GitLab and Azure DevOps in the runtime, with Watch and Fix, agent dispatch and stacks.
  • Events: a runtime event journal (list_events, wait_for_events), MCP stdio resource subscriptions, Standard Webhooks through the cloud, and MCP Events behind MCP_EVENTS_ENABLED.
  • Security review fixes: the last commit addresses the review findings; see section 13.1 of the plan.

Validation

  • Rust alera-cli: 2050 passed, plus integration binaries; alera-core runtime tests pass. fmt and clippy -D warnings clean.
  • Cloud: 87 unit tests, 14 Postgres contract tests plus 1 Postgres test, against a throwaway container.
  • Edge: 106/106. The catalog copy is regenerated and checked against the runtime.
  • Flutter: desktop 4505 and mobile 937 tests at the feature commits; mobile analyze clean after the last change.
  • Max-lines ratchet and git diff --check clean.
  • tool/ci/mcp_parity_acceptance.py: 10/10 scenarios against an isolated runtime with a real alera mcp serve stdio client.

Review loop

Reviewed with Codex GPT-6.1 Sol at high effort against main until a pass came back clean. That took 8 passes; the first 7 found 21 issues, all fixed in 7 fix: commits with a regression test each where practical. Notable fixes:

  • Desktop: the From Prompt start is no longer rejected after the origin hardening.
  • Webhooks and MCP Events:
    • callbacks ignore proxy variables;
    • turning the MCP Events switch off pauses deliveries instead of revoking subscriptions;
    • refreshing an expired subscription resumes its stopped deliveries;
    • the webhook quota holds under concurrent creation.
  • Workspaces:
    • wake types each slept tab's command or agent launch;
    • a From Prompt workspace created during a cancel stays recoverable;
    • Setup runs at most once across retries;
    • satellite workspace show reads from the hub.
  • Forges:
    • Azure merges keep their server-side head guard on SSH hosts;
    • Azure names with spaces are decoded before they reach az;
    • GitLab and alera.toml forge overrides are honored;
    • mobile replies and edits name their Azure thread.
  • Generation: pull request detail generation is resumable past the MCP deadline. The CLI capability it needs was never advertised before.

Validation after the last fix:

  • Rust alera binary: 2075 passed.
  • Mobile: 945 passed.
  • Edge: 106 passed.
  • Cloud: 87 unit tests plus 17 Postgres contract tests.
  • Acceptance: 10/10.
  • clippy, fmt, analyze, the max-lines ratchet and diff --check are clean.

Risks and open items

  • Decision needed (M6): full is not a sandbox. Terminal input, tab commands, project setup and automation prechecks can run any command, including CLI commands no tool exposes. This is documented in docs/remote-mcp.md; access levels are unchanged until decided.
  • Not run against real services: GitLab (glab api --input -) and Azure (az devops invoke, lastMergeSourceCommit) are covered by fixtures only. The Windows direct-exec path for forge CLIs was not compiled.
  • Remote hosts: Azure request bodies reach an SSH host on stdin (--in-file /dev/stdin). A Windows SSH host refuses guarded Azure merges, and its remote process runs still use cmd.exe (no free text reaches it now).
  • MCP Events end to end with ChatGPT needs a deploy with ALERA_WEBHOOK_SECRET_KEY, MCP_EVENTS_ENABLED and the delivery pump enabled.
  • Ship may exceed the 58 s client limit; it keeps running and can be read with get_pull_request.
  • Manual GUI E2E is pending: dirty-buffer removal, wake, host-launched setup, the shared inbox, Windows and macOS.
  • Follow-ups:
    • mobile pull request dialogs still say "GitHub" for every forge;
    • aiText.cancel does not stop a resumable details job;
    • resumable results are kept in memory, so a runtime restart loses them.

Add a fourth MCP Control level, admin, and the mcp:admin OAuth scope end
to end: runtime settings and relay authorization, cloud consent (admin is
never granted by default and needs an explicit tick), gateway and call
grants, edge routing, and the desktop MCP Control pane.

Split the runtime tool catalog by domain and move to catalog v2: tools
declare idempotentHint, can accept a clientRequestId forwarded to the CLI,
return structuredContent with a shared error model, and receive the
caller's origin through ALERA_MCP_ORIGIN. `alera mcp enable` and
`alera mcp serve` take --access read|full|admin.

New catalog tests check that every tool builds a command the CLI parser
accepts, that no tool reaches an excluded command, and that access
classes follow the parity plan.
The runtime now runs the New Workspace from Prompt flow as a persisted
operation (workspace.promptStart.*): it resolves the project (inferred by
AI Assist when not given, with candidates instead of a guess), the profile,
mode and source branch, generates the name, branch and section with an
Others fallback, retries taken branches, creates the workspace with its
setup deferred, assigns the section, launches the agent idempotently and
starts the Setup tab. Desktop and mobile delegate to it when the runtime
advertises promptWorkspaceServiceV1 and keep their pipeline otherwise.

Adds `alera workspace prompt-start`, six MCP tools, and an end-to-end
acceptance script that drives `alera mcp serve` against an isolated runtime.
…management over mcp

Workspaces and projects: show, rename, pin, archive, wake, focus, hand off
and on, recovery and setup, sections, tags, relations, linked issues, and
removal with the app's semantics (branch choice, dependent automations
paused, storage blockers, editor buffers saved or discarded through a new
checkoutBuffersSaveRequested event). Projects: register, clone, rename,
remove, hosts, branches, and configuration. Remote storage impact is now
measured on the owning host, and mobile may run workspace.runSetup.

Tabs, terminals, and runtime: tab create, close, rename and titles,
headless terminal restart, terminate, prune, pulse, allowlisted runtime
settings, agent integrations, quotas, resources, and voice. Agent profile
changes are admin; launching can resume a session.

Inbox and orchestration: questions record the asking MCP client, threads and
waits filter by own or all, and asks deduplicate by request key. Tasks,
dispatch, coordinators, gates, run policies, recipes, workflow proposals,
plans, execution and cleanup are exposed without human decisions.
Automations get full lifecycle tools.

The MCP catalog grows to every tool in the parity plan, with access classes
checked by the catalog tests and the edge copy regenerated.
…ntime

Adds a forge provider layer to the runtime with GitHub (gh), GitLab (glab)
and Azure DevOps (az) behind one interface, ported from the desktop
providers and checked against shared JSON fixtures in Dart and Rust.
Snapshots, summaries, create, link, comments, draft, close, merge, ship,
Watch and Fix with fixAndMerge, and stacks (GitHub only, as in the app) run
on every supported forge; a missing or signed-out CLI reports
provider_unavailable.

Restack and Fix Failed Checks prompts move to the runtime
(pullRequest.agentDispatch). Desktop and mobile leave a forge's watch to the
runtime when it advertises pullRequestWatchExecutionV2, so fixes are never
dispatched twice. Adds `alera pr` and the pull request MCP tools.
The runtime records a cursor-ordered journal of domain events (inbox
replies and question states, agent states, terminal exits, task states,
gates, escalations, automation runs, workspace starts and lifecycle, pull
request watch actions), with ids and states only. Clients read it with
`alera events` and the list_events and wait_for_events tools, and
`alera mcp serve` offers subscribable resources that send
notifications/resources/updated.

The runtime forwards the journal to the cloud only while a webhook or MCP
Events subscription wants it. The cloud stores events for 24 hours and
delivers them as Standard Webhooks with SSRF guards, retries, and 410/413
handling; webhooks are managed from the CLI, MCP (admin), and a new desktop
Settings group. The edge supports protocol 2026-07-28 and OpenAI MCP Events
(server/discover, events/list, events/subscribe, events/unsubscribe) behind
MCP_EVENTS_ENABLED, off by default.
Forge CLIs no longer take free text on the command line and run without cmd.exe on Windows; az refuses @file arguments and Azure merges pin the expected head. MCP Events follow MCP Control, the delivery pump always needs the origin token, and one rejected event no longer stalls forwarding. Satellites cannot start From Prompt runs, wake workspaces, name an origin, or resolve other clients' editors; list_webhooks is admin; the MCP origin stays out of runtimes and terminals.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Reviewed the MCP parity branch against main, including the security-review fixes in 4e21a3a6: the admin access model, host-owned workspace and management tools, the three-forge pull request path, and cloud event delivery.

  • Admin access. mcp:admin is kept only when the client names it, starts unchecked at consent, and is granted only together with mcp:execute. Existing grants are not upgraded. The edge, the cloud call grant, and the runtime each refuse an admin tool that lacks the scope or MCP Control set to Admin.
  • Host operations. New Workspace from Prompt, removal with editor save or discard, wake, and inbox attribution run in the runtime. A satellite cannot start or wake those operations, and only a local removal can ask other clients to save or discard editors.
  • Pull requests. GitHub, GitLab, and Azure DevOps share one forge provider. Watch and Fix ownership is per forge, so a runtime that only advertises the GitHub capability does not take GitLab or Azure DevOps away from the desktop. GitLab and Azure free text stays off the command line.
  • Events and webhooks. Delivered events are ids and states. Callbacks are limited to public HTTPS and pinned to the checked address. The delivery pump requires the origin token even when direct origin access is allowed, and MCP Control off stops MCP Events delivery without affecting account webhooks.

Full remains an unsandboxed shell, as docs/remote-mcp.md already records (M6). That is an open product decision, not a regression in this branch.

Pullfrog  | View workflow run | Using grok-4.7 | 𝕏

Comment thread cloud/src/events/ingest.rs Fixed
Comment thread cloud/src/events/secrets.rs Fixed
…k providers

The host only treats an origin with an MCP transport as a client claim, so the desktop's From Prompt start is no longer rejected. Inbox replies keep their workspace, answered questions are journaled with the inbox's status precedence, and the webhook providers are generated.
…e show reads

A From Prompt workspace creation runs to completion once sent, so a cancel records the workspace and its launch can be retried. The runtime forge override falls back to the repository alera.toml like the app, and workspace show on a satellite reads from the hub through a new workspace.show verb.
…d mobile cancel

Callback requests ignore proxy variables so they reach the validated address. The MCP Events switch leaves queued deliveries pending instead of revoking subscriptions. Waking a slept tab types its command or agent launch, and mobile cancels and retries a runtime From Prompt operation through workspace.promptStart.
… once

A checkout on an SSH host gets Azure request bodies on stdin, so a guarded merge keeps its server-side head check there, and a host that cannot read /dev/stdin refuses the merge instead. The Setup tab id is saved before the tab is created; a retry reconciles it and never runs the setup twice, leaving an unconfirmed command to the user.
…ct resource uris

Refreshing an expired subscription resumes deliveries stopped by its expiry, and webhook creation checks the account quota under a row lock. Azure project and repository names are decoded before reaching az and encoded again in links, mobile offers GitLab's project-settings merge, and resource subscriptions keep their exact URIs.
…secret dialog

Pull request detail generation can run as a resumable runtime job: the CLI and MCP tool wait a bounded time and a retry with the same operation id returns the result. The runtime now advertises the capability the CLI requires. The add webhook dialog cannot be dismissed while creation is pending, so its one-time secret is always shown.
Azure DevOps comment ids repeat across threads, so mobile replies now send replyToThreadId and edits send threadId, which the runtime already reads.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Reviewed the seven fix commits since 4e21a3a6: callback egress, event delivery, Azure merges, From Prompt setup, pull request detail generation, and mobile review threads.

  • Proxy-proof callbacks. Callback clients call no_proxy(), so a proxy cannot resolve the host and skip the pinned address check.
  • MCP Events pause. Turning the switch off leaves queued deliveries pending and subscriptions active. A refresh requeues only deliveries stopped as subscription_expired.
  • Webhook quota. Creations lock the account row, so concurrent requests cannot pass the limit together. The add dialog stays open until the one-time secret returns.
  • Resumable details. generate_pull_request_details answers running after about 45 seconds. A later call with the same retry key, workspace, and base branch reads the result. The key is scoped to the caller.
  • Azure and setup. Remote Azure request bodies go on stdin with lastMergeSourceCommit. Percent-encoded project and repo names are decoded before they reach az. Prompt-workspace setup claims a tab id before creating the tab, so a retry does not run it twice.
  • Wake, cancel, and threads. Waking a slept workspace types that tab's command or agent resume line. Mobile cancel reaches the running workspace.promptStart operation, including a cancel that arrives before the runtime names it. Replies and edits send the Azure thread id.
  • Identity and reads. Desktop From Prompt no longer sends an origin the host rejects. A repository alera.toml forge override is honored. A satellite workspace show reads the hub. Inbox answers are journaled when the reply is inserted. MCP resource subscriptions keep the exact URI, cursor included.

Full remains an unsandboxed shell, as docs/remote-mcp.md already records. That is unchanged.

Pullfrog  | View workflow run | Using grok-4.7 | 𝕏

… code scanning

The nested remote terminal lifecycle test overflowed the 2 MB debug test stack on CI: the authenticated request router's poll frame grew with the new verbs. Splitting it in two halves lowers the test's need from 1.87 MB to 1.51 MB locally.

Code scanning: the Standard Webhooks spec vector is prefixed at runtime so it is not read as a leaked Stripe secret, the second test key is generated, the batch vector no longer takes its capacity from input, and a wake test no longer prints a session id. A webhook date test covers epoch timestamps for the coverage gate.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Reviewed 02da82b0, the commit since a3bbe551. It splits the authenticated request router and clears code-scanning hits without changing request handling or webhook signing.

  • Request router split. Requests that handle_authenticated_request does not match now go to handle_more_authenticated_requests, boxed the same way lifecycle calls already avoid the full router. The arms from workspace.show through the unknown-request error are unchanged.
  • Scanner cleanups. The Standard Webhooks spec vector is prefixed at runtime, and the test still checks the published signature. Batch validation no longer reserves from the raw event count; the batch is already limited to 100. A wake assertion no longer prints the typed launch line.
  • Webhook dates. A unit test locks epoch seconds and epoch milliseconds to the same UTC instant.

Full remains an unsandboxed shell, as docs/remote-mcp.md already records. That is unchanged.

Pullfrog  | View workflow run | Using grok-4.7 | 𝕏

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants