Skip to content

Return app invocation IDs without blocking - #145

Open
IlyaasK wants to merge 8 commits into
mainfrom
hypeship/nonblocking-app-invoke
Open

Return app invocation IDs without blocking#145
IlyaasK wants to merge 8 commits into
mainfrom
hypeship/nonblocking-app-invoke

Conversation

@IlyaasK

@IlyaasK IlyaasK commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

What

  • Return from manage_apps invoke immediately with the asynchronous Kernel invocation and an explicit invocation_id.
  • Keep polling guidance in the tool description instead of returning a bespoke next_action / browser_action / polling protocol.
  • Add list_invocation_browsers(invocation_id) so an agent can retrieve the session ID and live-view URL for browsers created by that invocation.
  • Return only the browser fields needed for Hypeship live view; CDP and WebDriver credentials are not exposed.
  • Share the authenticated in-memory MCP transport harness across the app and durable-contract tests.

Why

The old tool created an async invocation and then called invocations.follow(). Long CUA tasks could outlive the MCP timeout, causing the caller to receive a timeout without the invocation ID even though the paid invocation continued running.

Returning the ID immediately makes recovery deterministic. Exposing invocation browsers by that ID also lets Hypeship's artifact-only live-view flow save the browser as soon as it appears, without runner-side inference or correlation.

Behavior

Before:

  1. Create an asynchronous invocation.
  2. Follow it inside the MCP request.
  3. Risk losing the response and ID to the outer timeout.

After:

  1. Create the asynchronous invocation.
  2. Return the invocation's current fields plus invocation_id immediately.
  3. Let the agent use get_invocation and list_invocation_browsers based on the tool description.
  4. Preserve the invocation ID for later recovery if the task remains nonterminal.

Hypeship integration and staging

Hypeship PR #853 can be deployed and tested before this PR lands for direct browser creation, browser artifact rendering, live-tab replacement, and cleanup across Pi, Cursor, Claude, and Codex.

The Hypeship app-invocation live-view path depends on this PR's behavior being deployed to the hosted https://mcp.onkernel.com/mcp endpoint. Merging this PR is not sufficient by itself if deployment is separate. Once deployed, Hypeship agents can receive the invocation ID immediately, call list_invocation_browsers, and save the resulting browser artifact. A full pre-merge integration test would require deploying this branch to a temporary MCP canary endpoint and temporarily pointing Hypeship staging at it.

Verification

  • Merged current main and resolved the project-selection overlap without restoring blocking invocation behavior.
  • bun test — 139 passed, 0 failed.
  • bunx tsc --noEmit — passed.
  • bun run check:managed-auth-app — passed.
  • Hosted CI, security scan, Vercel preview deployment, and Vercel Agent Review — passed.
  • Focused MCP transport tests verify that invoke never enters follow, returns the explicit ID without orchestration fields, requests browsers by the exact invocation ID, and omits CDP/WebDriver URLs.
  • Project-selection and resource-template tests pass against the merged implementation.

No database or API migration is required.


Note

Medium Risk
Changes agent-facing invoke semantics and exposes a new browser-discovery path; behavior is well-tested but callers must adapt to async polling instead of a blocking completion response.

Overview
manage_apps invoke no longer blocks on invocations.follow() — it creates an async invocation and returns immediately with status and invocation_id, so long-running tasks are not lost to MCP timeouts.

A new list_invocation_browsers action looks up browsers for an invocation ID and returns only session_id and browser_live_view_url (CDP/WebDriver URLs are stripped). Tool docs now describe bounded polling via get_invocation instead of waiting inside the tool.

registerAppCapabilities accepts injectable McpDependencies for tests. Shared mcp-test-fixtures (connectTestMcp, etc.) backs new apps.test.ts contract tests and refactors durable-contracts.test.ts.

Reviewed by Cursor Bugbot for commit c289d6e. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mcp Ready Ready Preview Aug 10, 2026 2:46pm

IlyaasK added 2 commits August 8, 2026 12:41
Let agents discover the browser session and live-view URL for an asynchronous app invocation using its returned invocation ID, without exposing CDP or WebDriver credentials.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit df50ef6. Configure here.

Comment thread src/lib/mcp/tools/apps.test.ts Outdated
Comment thread src/lib/mcp/tools/apps.test.ts Outdated
Inject the Kernel client through standard MCP dependencies and exercise invoke and invocation-browser retrieval through a real McpServer, Client, authentication envelope, schema, and in-memory transport.

@masnwilliams masnwilliams left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

requesting changes on two maintainability issues:

  1. don’t establish a bespoke orchestration protocol in the invoke response (src/lib/mcp/tools/apps.ts:149-160)

next_action, browser_action, and top-level polling create a new, untyped protocol inside a JSON text result. The action objects differ from the existing { tool, arguments } contract, have no output schema/version/structuredContent, and leave it ambiguous which action the polling policy governs. max_attempts is also caller policy that this server cannot enforce. The linked consumer only needs the explicit invocation_id, so the simpler implementation is to return the asynchronous invocation plus invocation_id and keep model guidance in the tool description. If machine-readable follow-ups are required, define one versioned next_actions contract with complete { tool, arguments } entries in structuredContent instead of parallel one-off fields.

  1. extract the duplicated MCP transport test harness (src/lib/mcp/tools/apps.test.ts:27-60)

connectApps repeats the authenticated McpServer/Client/InMemoryTransport setup already present in durable-contracts.test.ts, including transport patching, token collection, cleanup, and text-result parsing. Please extract a shared MCP test helper so this 192-line file focuses on the two app behaviors and the unknown as KernelClient boundary remains centralized.

The core behavior change is otherwise directionally strong: removing invocations.follow() is the right simplification, invocation-browser lookup is in the correct module, the browser response is explicitly allowlisted, and the tests cover the real MCP transport boundary.

Return only the asynchronous invocation and explicit invocation_id, keeping polling guidance in the tool description. Share the authenticated MCP transport harness across app and durable contract tests.
Resolve project-selection overlaps while preserving dependency injection, asynchronous invocation responses, and shared MCP transport tests.
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