Skip to content

feat: Split search-actors into data + -widget tools#723

Merged
jirispilka merged 5 commits intomasterfrom
claude/search-actors-decoupled-pattern-awSCQ
Apr 29, 2026
Merged

feat: Split search-actors into data + -widget tools#723
jirispilka merged 5 commits intomasterfrom
claude/search-actors-decoupled-pattern-awSCQ

Conversation

@jirispilka
Copy link
Copy Markdown
Collaborator

@jirispilka jirispilka commented Apr 19, 2026

🔗 PR chain — part of #577

Step 4 of 6 in the #577 umbrella rollout. Depends on #722 (stacked on claude/implement-issue-716-5kTwG; rebase to master once #720+#721+#722 merge).

# Issue PR Status
1 #714 — Rename openaiapps #720 merged
2 #715 — Capability auto-detect + gating #721 merged
3 #716fetch-actor-details split (pilot) #722 merged
4 #717search-actors split this PR approved
5 #718call-actor split #724 in review
6 #719get-actor-run split #734 in review

Closes #717.


Summary

Replicates the decoupled-pattern recipe validated in #722 on search-actors:

  • search-actors is now one mode-independent tool that returns pure data. Identical inputSchema / outputSchema / _meta across default and apps modes — no tool-level widget _meta anywhere.
  • New search-actors-widget (apps-only) renders the interactive UI element. Input is searchActorsBaseArgsSchema.strict()keywords/limit/offset only, stray keys rejected. Output is { actors, query, count, widgetActors }, matching what the existing search-actors-widget iframe consumes.
  • search-actors-internal is removed. The base tool now serves the silent name-resolution role — no LLM-opaque jargon.
  • Apps server instructions and the apps call-actor description flipped to steer between base (silent data) and -widget (renders an interactive UI element) using the "interactive UI element / widget" vocabulary.

File history is preserved via git mv src/tools/apps/search_actors.ts src/tools/apps/search_actors_widget.ts (the old apps variant already built the widget payload, so the widget tool is its closest successor).

Test plan

  • npm run type-check passes
  • npm run lint passes
  • npm run test:unit passes (599/599)
  • npm run build succeeds
  • mcpc-style JSON-RPC probe matrix — verified tools/list on first call, no notifications/tools/list_changed emitted in any case:
    • A default (no UI caps, no --ui) → only search-actors; base _meta has no ui/openai/* keys
    • B advertised initialize.capabilities.extensions['io.modelcontextprotocol/ui'] with text/html;profile=mcp-appsearch-actors + search-actors-widget; widget has ui.resourceUri=ui://widget/search-actors.html, ui.visibility=[model,app], non-empty ui.csp (connectDomains, resourceDomains)
    • C --ui=true (no caps) → same as B (forced)
    • D --ui=false + UI caps → same as A (override wins)
    • Base search-actors (name, _meta, inputSchema, outputSchema) byte-identical across all four cases
  • Integration tests (human-run — needs APIFY_TOKEN)
  • MCPJam widget round-trip (human)

apify-mcp-server-internal impact

  • HelperTools.STORE_SEARCH_INTERNAL removed. The private repo must not reference the literal 'search-actors-internal' or the enum member. Coordinate the audit.
  • Base search-actors no longer carries ui.* / openai/* _meta in apps mode. Any wrapper that previously read widget config from the base tool's _meta must switch to reading from the -widget sibling.
  • Widget structuredContent shape ({ actors, query, count, widgetActors }) matches what the existing search-actors-widget iframe already consumes — no web UI changes needed.

https://claude.ai/code/session_01BKQYsF54uFxW7ELzoQbQ3n

image

jirispilka pushed a commit that referenced this pull request Apr 20, 2026
Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun
jirispilka pushed a commit that referenced this pull request Apr 20, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
@jirispilka jirispilka force-pushed the claude/implement-issue-716-5kTwG branch from 527ad06 to e23510a Compare April 20, 2026 21:35
@jirispilka jirispilka force-pushed the claude/search-actors-decoupled-pattern-awSCQ branch from e7d755e to 3f2503b Compare April 20, 2026 21:35
jirispilka pushed a commit that referenced this pull request Apr 20, 2026
Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun
jirispilka pushed a commit that referenced this pull request Apr 20, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
@jirispilka jirispilka force-pushed the claude/implement-issue-716-5kTwG branch 2 times, most recently from c88e25a to 5909cb6 Compare April 22, 2026 12:53
@jirispilka jirispilka force-pushed the claude/search-actors-decoupled-pattern-awSCQ branch from 3f2503b to a833d7f Compare April 22, 2026 15:38
jirispilka pushed a commit that referenced this pull request Apr 22, 2026
Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun
jirispilka pushed a commit that referenced this pull request Apr 22, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
@jirispilka jirispilka force-pushed the claude/search-actors-decoupled-pattern-awSCQ branch from a833d7f to fab37f4 Compare April 24, 2026 11:11
@jirispilka jirispilka requested a review from Copilot April 24, 2026 14:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the search-actors tool to follow the decoupled “data tool + -widget UI tool” pattern in apps mode, removing the old -internal helper and updating instructions/tests accordingly.

Changes:

  • Makes search-actors mode-independent and data-only; removes tool-level widget _meta from the base tool.
  • Introduces search-actors-widget (apps-only) with strict input schema and widget-shaped output + widget _meta.
  • Removes search-actors-internal, updates helper tool constants, server instructions, and unit/integration tests to match the new tool surface.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/const.ts Replaces STORE_SEARCH_INTERNAL with STORE_SEARCH_WIDGET helper constant.
src/tools/core/search_actors_common.ts Removes widget _meta from base search-actors metadata; updates docs.
src/tools/apps/search_actors_widget.ts Adds new apps-only widget tool that renders the search widget and returns widget payload + _meta.
src/tools/apps/search_actors.ts Deleted apps-mode variant now that base tool is mode-independent.
src/tools/apps/search_actors_internal.ts Deleted obsolete internal helper tool.
src/tools/structured_output_schemas.ts Renames internal output schema to actorSearchWidgetOutputSchema and requires widgetActors.
src/tools/categories.ts Registers search-actors as a plain ToolEntry and adds search-actors-widget to ui category.
src/tools/core/call_actor_common.ts Simplifies call-actor description/error text to reference public search-actors when available.
src/tools/apps/call_actor.ts Updates apps call-actor description/error usage to match removal of -internal search.
src/tools/default/call_actor.ts Updates default call-actor description/error usage to match simplified call-actor helpers.
src/utils/server-instructions/index.ts Updates apps-mode disambiguation rules to search-actors (silent) vs search-actors-widget (UI).
tests/unit/tools.search_actors.widget.response.test.ts Rewrites tests to target search-actors-widget and assert tool/response widget _meta and strict schema behavior.
tests/unit/tools.mode_contract.test.ts Updates mode contract expectations for new widget tool and base tool no-widget-meta invariant.
tests/unit/mcp.server.capability_gating.test.ts Updates capability gating assertions to the new base+widget split.
tests/unit/utils.tools_loader.test.ts Updates tools-loader expectations from internal to widget helper tool.
tests/unit/tools.categories.test.ts Adjusts category tests to reflect mode-independent tools vs mode-specific call-actor.
tests/unit/tools.call_actor_common.test.ts Updates description/error-response tests for the new search tool guidance language.
tests/integration/suite.ts Updates integration expectations to STORE_SEARCH_WIDGET presence/absence in apps/default modes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/tools/apps/search_actors_widget.ts
Comment thread src/tools/apps/search_actors_widget.ts Outdated
@jirispilka jirispilka marked this pull request as ready for review April 24, 2026 14:32
jirispilka pushed a commit that referenced this pull request Apr 24, 2026
Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun
jirispilka pushed a commit that referenced this pull request Apr 24, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
@jirispilka jirispilka requested a review from MQ37 April 24, 2026 20:00
jirispilka pushed a commit that referenced this pull request Apr 24, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
@jirispilka jirispilka force-pushed the claude/implement-issue-716-5kTwG branch from 28c8530 to 72114c7 Compare April 27, 2026 11:30
@jirispilka jirispilka force-pushed the claude/search-actors-decoupled-pattern-awSCQ branch from 5d4e6dd to 7821f21 Compare April 27, 2026 11:32
jirispilka pushed a commit that referenced this pull request Apr 27, 2026
Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun
jirispilka pushed a commit that referenced this pull request Apr 27, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
@jirispilka jirispilka force-pushed the claude/implement-issue-716-5kTwG branch 2 times, most recently from 80e059a to 9b80bc4 Compare April 27, 2026 18:27
Base automatically changed from claude/implement-issue-716-5kTwG to master April 27, 2026 18:29
claude and others added 5 commits April 27, 2026 20:30
Step 4 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
validated by the fetch-actor-details split in #716 / PR #722:

- search-actors is now a mode-independent plain ToolEntry that returns
  pure data. Identical inputSchema / outputSchema / _meta across default
  and apps modes — no tool-level widget _meta anywhere.
- New search-actors-widget (apps-only) renders the interactive UI element.
  Input is searchActorsBaseArgsSchema.strict() — keywords/limit/offset
  only, stray keys rejected. Tool- and response-level widget _meta.
- search-actors-internal is removed. The base search-actors now serves
  the silent name-resolution role — no LLM-opaque jargon.
- Apps server instructions and call-actor apps description flipped to
  steer between base (silent data) and -widget (renders an interactive
  UI element) using the "interactive UI element / widget" vocabulary.

File history is preserved via git mv search_actors.ts -> search_actors_widget.ts.

https://claude.ai/code/session_01BKQYsF54uFxW7ELzoQbQ3n
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@jirispilka jirispilka force-pushed the claude/search-actors-decoupled-pattern-awSCQ branch from 7821f21 to 72ada61 Compare April 27, 2026 18:33
@github-actions github-actions Bot added t-ai Issues owned by the AI team. tested Temporary label used only programatically for some analytics. labels Apr 27, 2026
jirispilka pushed a commit that referenced this pull request Apr 27, 2026
Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun
jirispilka pushed a commit that referenced this pull request Apr 27, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
@jirispilka jirispilka merged commit fad46a9 into master Apr 29, 2026
12 checks passed
@jirispilka jirispilka deleted the claude/search-actors-decoupled-pattern-awSCQ branch April 29, 2026 07:36
jirispilka added a commit that referenced this pull request Apr 29, 2026
* feat: Split search-actors into data + -widget tools

Step 4 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
validated by the fetch-actor-details split in #716 / PR #722:

- search-actors is now a mode-independent plain ToolEntry that returns
  pure data. Identical inputSchema / outputSchema / _meta across default
  and apps modes — no tool-level widget _meta anywhere.
- New search-actors-widget (apps-only) renders the interactive UI element.
  Input is searchActorsBaseArgsSchema.strict() — keywords/limit/offset
  only, stray keys rejected. Tool- and response-level widget _meta.
- search-actors-internal is removed. The base search-actors now serves
  the silent name-resolution role — no LLM-opaque jargon.
- Apps server instructions and call-actor apps description flipped to
  steer between base (silent data) and -widget (renders an interactive
  UI element) using the "interactive UI element / widget" vocabulary.

File history is preserved via git mv search_actors.ts -> search_actors_widget.ts.

https://claude.ai/code/session_01BKQYsF54uFxW7ELzoQbQ3n

* refactor: Remove storeSearchTool and useInternalSearchWarning from call actor descriptions

* refactor: Improve error messaging and response structure for empty actor searches

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: reword .strict() comment to reflect AJV strip behavior; fix singular test

Agent-Logs-Url: https://github.com/apify/apify-mcp-server/sessions/ca1ebeb2-8719-4f87-b715-394215202a55

Co-authored-by: jirispilka <19406805+jirispilka@users.noreply.github.com>

* feat: Split call-actor into data + -widget tools

Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun

* fix: Remove storeSearchTool arg dropped from CallActorErrorResponseParams

* fix: Address review findings on call-actor split

- Remove contradictory 'NEVER call in UI mode' block from get-actor-run
  description; server instructions already scope the rule to call-actor-widget.
- Reject MCP actor:tool syntax in call-actor-widget — previously fell through
  to a non-widget response. Drop the misleading "For MCP server Actors" hint
  from the widget input description.
- Thread an explicit route parameter through callActorPreExecute so widget
  vs base traffic separates in telemetry.
- Server instructions: drop call-actor from the stale "still on base names"
  list; scope async-parameter guidance to non-apps mode.
- Widget docstring + test title: match silent-strip behavior (matches the
  correction in 5d4e6dd for the sibling widget).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: Update ActorSearch widget test to reflect name change in tool

* Update src/tools/apps/call_actor_widget.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/tools/core/get_actor_run_common.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
jirispilka pushed a commit that referenced this pull request Apr 29, 2026
Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf
jirispilka added a commit that referenced this pull request Apr 29, 2026
* feat: Split get-actor-run into data + -widget tools

Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf

* chore: Address staff review findings post-split

- Drop stale rolling-rollout note from server-instructions header (split is now complete)
- Extend "no polling after widget" rule to also cover get-actor-run-widget
- Rename integration test to drop misleading "widget" label (it exercises the data tool)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: Remove paymentRequired flag from get-actor-run tools

* Update src/tools/core/get_actor_run_common.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/tools/apps/get_actor_run_widget.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* test: Add integration test for x402 _meta advertising on paid tools (#768)

Asserts that tools with paymentRequired: true advertise _meta.x402 with
the expected fields (scheme, network, asset, payTo, amount) when the
server runs in x402 payment mode, and that free tools do not.

Pins the expected paid set with a hardcoded list so silent drift (e.g.
a tool losing paymentRequired) is caught here. Tracks #766.

* feat: Add paymentRequired flag to get-actor-run tools and update documentation

* refactor: Skip tests for auto mode client capabilities and rename itemCount to totalItemCount

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Kopecký <themq37@gmail.com>
jirispilka added a commit that referenced this pull request Apr 29, 2026
* feat: Split search-actors into data + -widget tools

Step 4 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
validated by the fetch-actor-details split in #716 / PR #722:

- search-actors is now a mode-independent plain ToolEntry that returns
  pure data. Identical inputSchema / outputSchema / _meta across default
  and apps modes — no tool-level widget _meta anywhere.
- New search-actors-widget (apps-only) renders the interactive UI element.
  Input is searchActorsBaseArgsSchema.strict() — keywords/limit/offset
  only, stray keys rejected. Tool- and response-level widget _meta.
- search-actors-internal is removed. The base search-actors now serves
  the silent name-resolution role — no LLM-opaque jargon.
- Apps server instructions and call-actor apps description flipped to
  steer between base (silent data) and -widget (renders an interactive
  UI element) using the "interactive UI element / widget" vocabulary.

File history is preserved via git mv search_actors.ts -> search_actors_widget.ts.

https://claude.ai/code/session_01BKQYsF54uFxW7ELzoQbQ3n

* refactor: Remove storeSearchTool and useInternalSearchWarning from call actor descriptions

* refactor: Improve error messaging and response structure for empty actor searches

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: reword .strict() comment to reflect AJV strip behavior; fix singular test

Agent-Logs-Url: https://github.com/apify/apify-mcp-server/sessions/ca1ebeb2-8719-4f87-b715-394215202a55

Co-authored-by: jirispilka <19406805+jirispilka@users.noreply.github.com>

* feat: Split call-actor into data + -widget tools

Step 5 of 6 in the #577 umbrella. Mirrors the decoupled-pattern recipe
from #716/PR #722 (fetch-actor-details) and #723 (search-actors):

- call-actor now always returns pure data in both modes. No tool-level
  widget _meta anywhere, and buildStartAsyncResponse({ widget: false })
  in apps mode returns runId without response-level widget _meta. The
  apps variant still runs asynchronously.
- New call-actor-widget (apps-only) renders the live progress widget.
  Input is strict: { actor, input, callOptions? } only — async and
  previewOutput are rejected. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html).
- Apps server instructions reshaped: the "never poll get-actor-run"
  rule now scopes to call-actor-widget only; polling after the silent
  call-actor is fine. Added a third disambiguation bullet pairing
  call-actor with call-actor-widget using the same "interactive UI
  element / widget" vocabulary as the other two splits.
- buildCallActorDescription alwaysAsync branch flipped to describe
  the silent-async behavior and point to call-actor-widget for UI.

https://claude.ai/code/session_01LPzCFY7ReLm8wvmFHJLyun

* fix: Remove storeSearchTool arg dropped from CallActorErrorResponseParams

* fix: Address review findings on call-actor split

- Remove contradictory 'NEVER call in UI mode' block from get-actor-run
  description; server instructions already scope the rule to call-actor-widget.
- Reject MCP actor:tool syntax in call-actor-widget — previously fell through
  to a non-widget response. Drop the misleading "For MCP server Actors" hint
  from the widget input description.
- Thread an explicit route parameter through callActorPreExecute so widget
  vs base traffic separates in telemetry.
- Server instructions: drop call-actor from the stale "still on base names"
  list; scope async-parameter guidance to non-apps mode.
- Widget docstring + test title: match silent-strip behavior (matches the
  correction in 5d4e6dd for the sibling widget).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: Update ActorSearch widget test to reflect name change in tool

* Update src/tools/apps/call_actor_widget.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/tools/core/get_actor_run_common.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* feat: Split get-actor-run into data + -widget tools

Final step (6 of 6) of the #577 umbrella rollout. Mirrors the
decoupled-pattern recipe from #722 (fetch-actor-details), #723
(search-actors), and #724 (call-actor):

- get-actor-run is now mode-independent and data-only. No tool-level
  widget _meta in either mode; runs category entry is a plain ToolEntry
  instead of a mode map.
- New get-actor-run-widget (apps-only) renders the live progress widget.
  Input is strict: { runId } only. Tool- and response-level widget _meta
  (ui.resourceUri = ui://widget/actor-run.html). Reuses the shared
  buildGetActorRunSuccessResponse({ widget: true }) helper.
- buildGetActorRunSuccessResponse widget branch now also sets
  openai/widgetDescription on the response _meta, matching the other
  three widget tools.
- Apps server instructions: added the fourth disambiguation bullet
  pairing get-actor-run (silent data lookup) with get-actor-run-widget
  (live progress widget), using the same vocabulary as the existing
  three splits. WORKFLOW_RULES untouched — the "NEVER poll
  get-actor-run after call-actor-widget" rule is orthogonal.
- Deleted src/tools/apps/get_actor_run.ts; widget rendering now lives
  in the sibling tool rather than a mode toggle.

https://claude.ai/code/session_01SF9P6g91UrVMahn4bLsUNf

* chore: Address staff review findings post-split

- Drop stale rolling-rollout note from server-instructions header (split is now complete)
- Extend "no polling after widget" rule to also cover get-actor-run-widget
- Rename integration test to drop misleading "widget" label (it exercises the data tool)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: Remove paymentRequired flag from get-actor-run tools

* Update src/tools/core/get_actor_run_common.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/tools/apps/get_actor_run_widget.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: Pair widget tools with their base tools instead of unconditional UI auto-include

Closes #746.

In apps mode, the loader unconditionally appended every entry of the `ui`
category whenever any non-empty tool selection was made. A caller asking for
`tools: ['docs']` silently received `search-actors-widget`,
`fetch-actor-details-widget`, `call-actor-widget`, and `get-actor-run-widget`
they did not request — including the paid call-actor widget added in #724.

Replace the unconditional `ui` push with a `WIDGET_BY_BASE_TOOL` map keyed by
base tool name. After the resolved tool list is composed (and after
get-actor-run / get-actor-output auto-inject), append a widget if and only if
its base tool is in the result. The existing de-dup pass handles uniqueness.

Removes the now-empty `ui` category. Tests updated to assert the new pairing
behavior end-to-end via `getToolsForServerMode`.

* chore: Note that pairing-pass duplicates are stripped by the de-dup pass

Inline reminder at the widget push so a future reader doesn't add a
redundant `result.some(...)` guard.

* test: Add integration test for x402 _meta advertising on paid tools (#768)

Asserts that tools with paymentRequired: true advertise _meta.x402 with
the expected fields (scheme, network, asset, payTo, amount) when the
server runs in x402 payment mode, and that free tools do not.

Pins the expected paid set with a hardcoded list so silent drift (e.g.
a tool losing paymentRequired) is caught here. Tracks #766.

* feat: Add paymentRequired flag to get-actor-run tools and update documentation

* refactor: Skip tests for auto mode client capabilities and rename itemCount to totalItemCount

* feat: Include widget tools in apps mode for direct selection

* Update tests/unit/utils.tools_loader.test.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* test: Add integration test for x402 _meta advertising on paid tools (#768)

Asserts that tools with paymentRequired: true advertise _meta.x402 with
the expected fields (scheme, network, asset, payTo, amount) when the
server runs in x402 payment mode, and that free tools do not.

Pins the expected paid set with a hardcoded list so silent drift (e.g.
a tool losing paymentRequired) is caught here. Tracks #766.

* feat: Add paymentRequired flag to get-actor-run tools and update documentation

* refactor: Skip tests for auto mode client capabilities and rename itemCount to totalItemCount

* refactor: Simplify tool selection logic in tools_loader and update tests

* feat: Enhance server mode handling for get-actor-run inclusion in tools

* feat: Add additional integration test scripts for actor server functionality

* feat: Update documentation for widget-base tool pairing and enhance test for duplicate handling

* feat: Update types for WIDGET_BY_BASE_TOOL and improve related documentation

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Kopecký <themq37@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-ai Issues owned by the AI team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Split search-actors into data + -widget tools

6 participants