Skip to content

Commit 240793a

Browse files
committed
address review comments
1 parent 3e7d9e8 commit 240793a

File tree

10 files changed

+259
-56
lines changed

10 files changed

+259
-56
lines changed

README.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -180,36 +180,50 @@ When no query parameters are provided, the MCP server loads the following `tools
180180
- `docs`
181181
- `apify/rag-web-browser`
182182

183-
It is thus the same as if you configured the `tools` parameter like this:
183+
If the tools parameter is specified, only the listed tools or categories will be enabled - no default tools will be included.
184+
185+
> **Easy configuration:**
186+
>
187+
> Use the [UI configurator](https://mcp.apify.com/) to configure your server, then copy the configuration to your client.
188+
189+
**Configuring the hosted server:**
190+
191+
The hosted server can be configured using query parameters in the URL. For example, to load the default tools, use:
184192

185-
**For the hosted server:**
186193
```
187194
https://mcp.apify.com?tools=actors,docs,apify/rag-web-browser
188195
```
189196

190-
**For the CLI:**
197+
For minimal configuration, if you want to use only a single Actor tool - without any discovery or generic calling tools, the server can be configured as follows:
198+
199+
```
200+
https://mcp.apify.com?tools=apify/my-actor
201+
```
202+
203+
This setup exposes only the specified Actor (`apify/my-actor`) as a tool. No other tools will be available.
204+
205+
**Configuring the CLI:**
206+
207+
The CLI can be configured using command-line flags. For example, to load the same tools as in the hosted server configuration, use:
208+
191209
```bash
192210
npx @apify/actors-mcp-server --tools actors,docs,apify/rag-web-browser
193211
```
194212

195-
If the tools parameter is specified, only the listed tools or categories will be enabled - no default tools will be included.
213+
The minimal configuration is similar to the hosted server configuration:
214+
215+
```bash
216+
npx @apify/actors-mcp-server --tools apify/my-actor
217+
```
218+
219+
As above, this exposes only the specified Actor (`apify/my-actor`) as a tool. No other tools will be available.
196220

197221
> **⚠️ Important recommendation**
198222
>
199223
> **The default tools configuration may change in future versions.** When no `tools` parameter is specified, the server currently loads default tools, but this behavior is subject to change.
200224
>
201225
> **For production use and stable interfaces, always explicitly specify the `tools` parameter** to ensure your configuration remains consistent across updates.
202226
203-
**Minimal configuration**
204-
205-
For example, to use only a single Actor tool - without any discovery or generic calling tools, the server can be configured like this:
206-
207-
```
208-
https://mcp.apify.com?tools=apify/my-actor
209-
```
210-
211-
This setup exposes only the specified Actor (`apify/my-actor`) as a tool. No other tools will be available.
212-
213227
### Backward compatibility
214228

215229
The v2 configuration preserves backward compatibility with v1 usage. Notes:

src/input.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import type { Input, ToolSelector } from './types.js';
1111

1212
// Helpers
1313
// Normalize booleans that may arrive as strings or be undefined.
14-
function toBoolean(value: unknown, defaultValue: boolean): boolean {
14+
export function toBoolean(value: unknown, defaultValue: boolean): boolean {
1515
if (value === undefined) return defaultValue;
1616
if (typeof value === 'boolean') return value;
1717
if (typeof value === 'string') return value.toLowerCase() === 'true';
1818
return defaultValue;
1919
}
2020

2121
// Normalize lists from comma-separated strings or arrays.
22-
function normalizeList(value: string | string[] | undefined): string[] | undefined {
22+
export function normalizeList(value: string | unknown[] | undefined): string[] | undefined {
2323
if (value === undefined) return undefined;
2424
if (Array.isArray(value)) return value.map((s) => String(s).trim()).filter((s) => s !== '');
2525
const trimmed = String(value).trim();

src/mcp/server.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,23 @@ export class ActorsMcpServer {
170170
}
171171

172172
if (actorsToLoad.length > 0) {
173-
const actorTools = await getActorsAsTools(actorsToLoad, apifyToken);
174-
if (actorTools.length > 0) {
175-
this.upsertTools(actorTools);
176-
}
173+
await this.loadActorsAsTools(actorsToLoad, apifyToken);
174+
}
175+
}
176+
177+
/**
178+
* Load actors as tools, upsert them to the server, and return the tool entries.
179+
* This is a public method that wraps getActorsAsTools and handles the upsert operation.
180+
* @param actorIdsOrNames - Array of actor IDs or names to load as tools
181+
* @param apifyToken - Apify API token for authentication
182+
* @returns Promise<ToolEntry[]> - Array of loaded tool entries
183+
*/
184+
public async loadActorsAsTools(actorIdsOrNames: string[], apifyToken: string): Promise<ToolEntry[]> {
185+
const actorTools = await getActorsAsTools(actorIdsOrNames, apifyToken);
186+
if (actorTools.length > 0) {
187+
this.upsertTools(actorTools, true);
177188
}
189+
return actorTools;
178190
}
179191

180192
/**
@@ -401,8 +413,6 @@ export class ActorsMcpServer {
401413
apifyToken,
402414
userRentedActorIds,
403415
progressTracker,
404-
// Passing as argument to prevent circular dependency
405-
getActorsAsTools,
406416
}) as object;
407417

408418
if (progressTracker) {

src/stdio.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Deprecated: use tools experimental category instead.`,
7070
type: 'string',
7171
describe: `Comma-separated list of tools to enable. Can be either a tool category, a specific tool, or an Apify Actor. For example: --tools actors,docs,apify/rag-web-browser.
7272
73-
For more details visit https://github.com/apify/actors-mcp-server`,
73+
For more details visit https://mcp.apify.com`,
7474
example: 'actors,docs,apify/rag-web-browser',
7575
})
7676
.help('help')

src/tools/actor.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,6 @@ The step parameter enforces this workflow - you cannot call an Actor without fir
307307
}
308308
return {
309309
content: [
310-
{ type: 'text', text: `**Actor card**:\n${details.actorCard}` },
311-
{ type: 'text', text: `**README:**\n${details.readme}` },
312310
{ type: 'text', text: `**Input Schema:**\n${JSON.stringify(details.inputSchema, null, 0)}` },
313311
],
314312
};

src/tools/helpers.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const addTool: ToolEntry = {
4848
ajvValidate: ajv.compile(zodToJsonSchema(addToolArgsSchema)),
4949
// TODO: I don't like that we are passing apifyMcpServer and mcpServer to the tool
5050
call: async (toolArgs) => {
51-
const { apifyMcpServer, apifyToken, args, extra: { sendNotification }, getActorsAsTools } = toolArgs;
51+
const { apifyMcpServer, apifyToken, args, extra: { sendNotification } } = toolArgs;
5252
const parsed = addToolArgsSchema.parse(args);
5353
if (apifyMcpServer.listAllToolNames().includes(parsed.actor)) {
5454
return {
@@ -58,14 +58,12 @@ export const addTool: ToolEntry = {
5858
}],
5959
};
6060
}
61-
if (!getActorsAsTools) {
62-
throw new Error('Internal configuration error: getActorsAsTools must be passed via InternalToolArgs from the MCP server');
63-
}
64-
const tools = await getActorsAsTools([parsed.actor], apifyToken);
61+
62+
const tools = await apifyMcpServer.loadActorsAsTools([parsed.actor], apifyToken);
6563
/**
6664
* If no tools were found, return a message that the Actor was not found
6765
* instead of returning that non existent tool was added since the
68-
* getActorsAsTools function returns an empty array and does not throw an error.
66+
* loadActorsAsTools method returns an empty array and does not throw an error.
6967
*/
7068
if (tools.length === 0) {
7169
return {
@@ -75,14 +73,14 @@ export const addTool: ToolEntry = {
7573
}],
7674
};
7775
}
78-
const toolsAdded = apifyMcpServer.upsertTools(tools, true);
76+
7977
await sendNotification({ method: 'notifications/tools/list_changed' });
8078

8179
return {
8280
content: [{
8381
type: 'text',
8482
text: `Actor ${parsed.actor} has been added. Newly available tools: ${
85-
toolsAdded.map(
83+
tools.map(
8684
(t) => `${t.tool.name}`,
8785
).join(', ')
8886
}.`,

src/types.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,6 @@ export type InternalToolArgs = {
108108
userRentedActorIds?: string[];
109109
/** Optional progress tracker for long running internal tools, like call-actor */
110110
progressTracker?: ProgressTracker | null;
111-
/**
112-
* Injected dependency for turning Actor names/IDs into tools.
113-
* Passing it from the server avoids a stdio-only circular import that made the
114-
* add-actor tool null during module initialization.
115-
*/
116-
// eslint-disable-next-line no-use-before-define
117-
getActorsAsTools?: (actorIdsOrNames: string[], apifyToken: string) => Promise<ToolEntry[]>;
118111
}
119112

120113
/**

tests/integration/suite.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,6 @@ export function createIntegrationTestsSuite(
357357

358358
expect(infoResult.content).toBeDefined();
359359
const content = infoResult.content as { text: string }[];
360-
expect(content.some((item) => item.text.includes('Actor card'))).toBe(true);
361360
expect(content.some((item) => item.text.includes('Input Schema'))).toBe(true);
362361

363362
// Step 2: Call with proper input (should work)

0 commit comments

Comments
 (0)