Skip to content

Commit 45b831a

Browse files
jirispilkaclaudeCopilot
authored
refactor: Unify call-actor tool across default and apps modes (#980)
## What - Single `createCallActorTool(description)` definition in `default/call_actor.ts`; `defaultCallActor` and `appsCallActor` are built from it, differing only in description (apps appends the widget addendum). - Deletes the duplicated `apps/call_actor.ts`. - `categories.ts` mode map unchanged — registry behavior is identical. closes #975 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent f25ca96 commit 45b831a

5 files changed

Lines changed: 38 additions & 70 deletions

File tree

src/tools/apps/call_actor.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/tools/categories.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import { HelperTools } from '../const.js';
2323
import type { ToolEntry } from '../types.js';
2424
import { ServerMode } from '../types.js';
25-
import { appsCallActor } from './apps/call_actor.js';
2625
import { appsCallActorWidget } from './apps/call_actor_widget.js';
2726
import { fetchActorDetailsWidgetTool } from './apps/fetch_actor_details_widget.js';
2827
import { getActorRunWidgetTool } from './apps/get_actor_run_widget.js';
@@ -41,16 +40,11 @@ import { getKeyValueStoreRecord } from './common/get_key_value_store_record.js';
4140
import { getUserKeyValueStoresList } from './common/key_value_store_collection.js';
4241
import { getUserRunsList } from './common/run_collection.js';
4342
import { searchApifyDocsTool } from './common/search_apify_docs.js';
44-
import { defaultCallActor } from './default/call_actor.js';
43+
import { appsCallActor, defaultCallActor } from './default/call_actor.js';
4544
import { defaultFetchActorDetails } from './default/fetch_actor_details.js';
4645
import { defaultGetActorRun } from './default/get_actor_run.js';
4746
import { defaultSearchActors } from './default/search_actors.js';
4847

49-
/**
50-
* A mode map: maps one or more ServerMode keys to their ToolEntry variant.
51-
* - All modes present → each mode gets its own implementation
52-
* - Subset of modes → tool is only included for those modes
53-
*/
5448
type ModeMap = Partial<Record<ServerMode, ToolEntry>>;
5549

5650
/** A category tool entry: plain ToolEntry (mode-independent) or a mode map. */

src/tools/default/call_actor.ts

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,47 @@ import { HelperTools } from '../../const.js';
22
import type { InternalToolArgs, ToolEntry } from '../../types.js';
33
import { TOOL_TYPE } from '../../types.js';
44
import {
5+
buildCallActorAppsDescription,
56
buildCallActorDescription,
67
callActorAjvValidate,
78
callActorInputSchema,
89
executeCallActor,
910
} from '../core/call_actor_common.js';
1011
import { getActorRunOutputSchema } from '../structured_output_schemas.js';
1112

12-
const CALL_ACTOR_DEFAULT_DESCRIPTION = buildCallActorDescription();
13+
/**
14+
* Single call-actor definition shared by both modes — only the description differs
15+
* (apps mode appends a widget addendum).
16+
*/
17+
function createCallActorTool(description: string): ToolEntry {
18+
return Object.freeze({
19+
type: TOOL_TYPE.INTERNAL,
20+
name: HelperTools.ACTOR_CALL,
21+
description,
22+
inputSchema: callActorInputSchema,
23+
outputSchema: getActorRunOutputSchema,
24+
ajvValidate: callActorAjvValidate,
25+
paymentRequired: true,
26+
annotations: {
27+
title: 'Call Actor',
28+
readOnlyHint: false,
29+
destructiveHint: true,
30+
idempotentHint: false,
31+
openWorldHint: true,
32+
},
33+
execution: {
34+
// Support long-running tasks
35+
taskSupport: 'optional',
36+
},
37+
call: async (toolArgs: InternalToolArgs) => executeCallActor(toolArgs),
38+
} as const);
39+
}
40+
41+
/** Default mode call-actor tool. */
42+
export const defaultCallActor: ToolEntry = createCallActorTool(buildCallActorDescription());
1343

1444
/**
15-
* Default mode call-actor tool.
45+
* Apps mode call-actor tool.
46+
* Renders no widget; for a live progress UI, use the call-actor-widget sibling.
1647
*/
17-
export const defaultCallActor: ToolEntry = Object.freeze({
18-
type: TOOL_TYPE.INTERNAL,
19-
name: HelperTools.ACTOR_CALL,
20-
description: CALL_ACTOR_DEFAULT_DESCRIPTION,
21-
inputSchema: callActorInputSchema,
22-
outputSchema: getActorRunOutputSchema,
23-
ajvValidate: callActorAjvValidate,
24-
paymentRequired: true,
25-
annotations: {
26-
title: 'Call Actor',
27-
readOnlyHint: false,
28-
destructiveHint: true,
29-
idempotentHint: false,
30-
openWorldHint: true,
31-
},
32-
execution: {
33-
// Support long-running tasks
34-
taskSupport: 'optional',
35-
},
36-
call: async (toolArgs: InternalToolArgs) => executeCallActor(toolArgs),
37-
} as const);
48+
export const appsCallActor: ToolEntry = createCallActorTool(buildCallActorAppsDescription());

tests/unit/mcp.server.capability_gating.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
66
import { HelperTools, SERVER_MODE_AUTO_DETECTION_ENABLED } from '../../src/const.js';
77
import { ActorsMcpServer } from '../../src/mcp/server.js';
88
import { RESOURCE_MIME_TYPE } from '../../src/resources/widgets.js';
9-
import { appsCallActor } from '../../src/tools/apps/call_actor.js';
109
import { searchActorsWidgetTool } from '../../src/tools/apps/search_actors_widget.js';
10+
import { appsCallActor } from '../../src/tools/default/call_actor.js';
1111
import { defaultSearchActors } from '../../src/tools/default/search_actors.js';
1212
import type { ServerModeOption } from '../../src/types.js';
1313
import { ServerMode } from '../../src/types.js';

tests/unit/tools.categories.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ describe('getCategoryTools', () => {
4747
const appsNames = appsResult.actors.map((t: ToolEntry) => t.name);
4848
expect(defaultNames).toEqual(appsNames);
4949

50-
// call-actor still has mode-specific variants (sync-capable default vs always-async apps).
50+
// call-actor still has mode-specific variants — distinct objects differing only in
51+
// description (apps mode appends a widget addendum).
5152
// search-actors and fetch-actor-details are mode-independent and share the same object.
5253
const defaultCallActor = defaultResult.actors.find((t: ToolEntry) => t.name === HelperTools.ACTOR_CALL);
5354
const appsCallActor = appsResult.actors.find((t: ToolEntry) => t.name === HelperTools.ACTOR_CALL);

0 commit comments

Comments
 (0)