Skip to content

Commit 66ed6ce

Browse files
committed
Merge remote-tracking branch 'origin/master' into feat/cancellable-actor
2 parents b47cfcc + fb8c81a commit 66ed6ce

File tree

12 files changed

+240
-63
lines changed

12 files changed

+240
-63
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.3.9](https://github.com/apify/actors-mcp-server/releases/tag/v0.3.9) (2025-08-21)
6+
7+
8+
## [0.3.9](https://github.com/apify/actors-mcp-server/releases/tag/v0.3.9) (2025-08-21)
9+
10+
### 🚀 Features
11+
12+
- **dxt:** Add mcp tool configuration options ([#221](https://github.com/apify/actors-mcp-server/pull/221)) ([5b305c5](https://github.com/apify/actors-mcp-server/commit/5b305c5239ae515a966c055b356d0bc3b90ee301)) by [@MQ37](https://github.com/MQ37), closes [#219](https://github.com/apify/actors-mcp-server/issues/219)
13+
- Prepare for dockerhub integration, prepare dockerfile, add support for reading config from env vars for stdio ([#224](https://github.com/apify/actors-mcp-server/pull/224)) ([08c62be](https://github.com/apify/actors-mcp-server/commit/08c62be284032bc1d5f4ccb25301129fc1ea2c58)) by [@MQ37](https://github.com/MQ37)
14+
15+
### 🐛 Bug Fixes
16+
17+
- Improve beta release cicd ([#202](https://github.com/apify/actors-mcp-server/pull/202)) ([2bd1ad7](https://github.com/apify/actors-mcp-server/commit/2bd1ad74e2aec01d7ae37750846abe3fbf66a4e7)) by [@MQ37](https://github.com/MQ37), closes [#80](https://github.com/apify/actors-mcp-server/issues/80)
18+
- Actor input transform array items type inference ([#225](https://github.com/apify/actors-mcp-server/pull/225)) ([eb38160](https://github.com/apify/actors-mcp-server/commit/eb381603edd00c82678c89a2fa6aa31720295e99)) by [@MQ37](https://github.com/MQ37), closes [#217](https://github.com/apify/actors-mcp-server/issues/217)
19+
- Dockerhub init unauth server start and tool list ([#227](https://github.com/apify/actors-mcp-server/pull/227)) ([87b2f2c](https://github.com/apify/actors-mcp-server/commit/87b2f2c470e1bddc92a568e8e826fc2ed8bfc1a9)) by [@MQ37](https://github.com/MQ37)
20+
- Dxt rename sever to apify-mcp-server ([#232](https://github.com/apify/actors-mcp-server/pull/232)) ([c5e51df](https://github.com/apify/actors-mcp-server/commit/c5e51df79c3a6dd037f114d4aa4bd3ecf7db8641)) by [@MQ37](https://github.com/MQ37)
21+
22+
523
## [0.3.8](https://github.com/apify/actors-mcp-server/releases/tag/v0.3.8) (2025-08-08)
624

725
### 🐛 Bug Fixes

Dockerfile

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
2-
# Stage 1: Build the TypeScript project
3-
FROM node:18-alpine AS builder
1+
# Stage 1: Build the project
2+
FROM node:24-alpine AS builder
43

54
# Set working directory
65
WORKDIR /app
@@ -17,7 +16,7 @@ COPY tsconfig.json ./
1716
RUN npm run build
1817

1918
# Stage 2: Set up the runtime environment
20-
FROM node:18-alpine
19+
FROM node:24-alpine
2120

2221
# Set working directory
2322
WORKDIR /app
@@ -29,11 +28,5 @@ COPY package.json package-lock.json ./
2928
# Install production dependencies only
3029
RUN npm ci --omit=dev
3130

32-
# Expose any necessary ports (example: 3000)
33-
EXPOSE 3000
34-
35-
# Set the environment variable for the Apify token
36-
ENV APIFY_TOKEN=<your-apify-token>
37-
3831
# Set the entry point for the container
39-
ENTRYPOINT ["node", "dist/main.js"]
32+
ENTRYPOINT ["node", "dist/stdio.js"]

manifest.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"dxt_version": "0.1",
3-
"name": "apify-actors-mcp-server",
4-
"version": "0.0.0",
3+
"name": "apify-mcp-server",
4+
"version": "0.3.9",
55
"description": "Extract data from any site with Apify Store, home to thousands of web scrapers.",
66
"long_description": "Apify is the world's largest marketplace of tools for web scraping, data extraction, and web automation. You can extract structured data from social media, e-commerce, search engines, maps, travel sites, or any other website.",
77
"keywords": [
@@ -94,4 +94,4 @@
9494
"node": ">=20.0.0"
9595
}
9696
}
97-
}
97+
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@apify/actors-mcp-server",
3-
"version": "0.3.8",
3+
"version": "0.3.9",
44
"type": "module",
55
"description": "Model Context Protocol Server for Apify",
66
"engines": {

src/apify-client.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ export function getApifyAPIBaseUrl(): string {
2424

2525
export class ApifyClient extends _ApifyClient {
2626
constructor(options: ApifyClientOptions) {
27+
/**
28+
* In order to publish to DockerHub, we need to run their build task to validate our MCP server.
29+
* This was failing since we were sending this dummy token to Apify in order to build the Actor tools.
30+
* So if we encounter this dummy value, we remove it to use Apify client as unauthenticated, which is sufficient
31+
* for server start and listing of tools.
32+
*/
33+
if (options.token?.toLowerCase() === 'your-apify-token') {
34+
// eslint-disable-next-line no-param-reassign
35+
delete options.token;
36+
}
37+
2738
super({
2839
...options,
2940
baseUrl: getApifyAPIBaseUrl(),

src/stdio.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,16 @@ log.setLevel(log.LEVELS.ERROR);
4747
// Parse command line arguments using yargs
4848
const argv = yargs(hideBin(process.argv))
4949
.usage('Usage: $0 [options]')
50+
.env()
5051
.option('actors', {
5152
type: 'string',
52-
describe: 'Comma-separated list of Actor full names to add to the server.',
53+
describe: 'Comma-separated list of Actor full names to add to the server. Can also be set via ACTORS environment variable.',
5354
example: 'apify/google-search-scraper,apify/instagram-scraper',
5455
})
5556
.option('enable-adding-actors', {
5657
type: 'boolean',
5758
default: true,
58-
describe: 'Enable dynamically adding Actors as tools based on user requests.',
59+
describe: 'Enable dynamically adding Actors as tools based on user requests. Can also be set via ENABLE_ADDING_ACTORS environment variable.',
5960
})
6061
.option('enableActorAutoLoading', {
6162
type: 'boolean',
@@ -65,7 +66,7 @@ const argv = yargs(hideBin(process.argv))
6566
})
6667
.options('tools', {
6768
type: 'string',
68-
describe: `Comma-separated list of specific tool categories to enable.
69+
describe: `Comma-separated list of specific tool categories to enable. Can also be set via TOOLS environment variable.
6970
7071
Available choices: ${Object.keys(toolCategories).join(', ')}
7172

src/tools/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ export function transformActorInputSchemaProperties(input: Readonly<IActorInputS
284284
const inputClone: IActorInputSchema = structuredClone(input);
285285
let transformedProperties = markInputPropertiesAsRequired(inputClone);
286286
transformedProperties = buildApifySpecificProperties(transformedProperties);
287-
transformedProperties = filterSchemaProperties(transformedProperties);
288287
transformedProperties = inferArrayItemsTypeIfMissing(transformedProperties);
288+
transformedProperties = filterSchemaProperties(transformedProperties);
289289
transformedProperties = shortenProperties(transformedProperties);
290290
transformedProperties = addEnumsToDescriptionsWithExamples(transformedProperties);
291291
transformedProperties = encodeDotPropertyNames(transformedProperties);

tests/helpers.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface McpClientOptions {
1111
actors?: string[];
1212
enableAddingActors?: boolean;
1313
tools?: ToolCategory[]; // Tool categories to include
14+
useEnv?: boolean; // Use environment variables instead of command line arguments (stdio only)
1415
}
1516

1617
export async function createMcpSseClient(
@@ -97,24 +98,40 @@ export async function createMcpStdioClient(
9798
if (!process.env.APIFY_TOKEN) {
9899
throw new Error('APIFY_TOKEN environment variable is not set.');
99100
}
100-
const { actors, enableAddingActors, tools } = options || {};
101+
const { actors, enableAddingActors, tools, useEnv } = options || {};
101102
const args = ['dist/stdio.js'];
102-
if (actors) {
103-
args.push('--actors', actors.join(','));
104-
}
105-
if (enableAddingActors !== undefined) {
106-
args.push('--enable-adding-actors', enableAddingActors.toString());
107-
}
108-
if (tools && tools.length > 0) {
109-
args.push('--tools', tools.join(','));
103+
const env: Record<string, string> = {
104+
APIFY_TOKEN: process.env.APIFY_TOKEN as string,
105+
};
106+
107+
// Set environment variables instead of command line arguments when useEnv is true
108+
if (useEnv) {
109+
if (actors) {
110+
env.ACTORS = actors.join(',');
111+
}
112+
if (enableAddingActors !== undefined) {
113+
env.ENABLE_ADDING_ACTORS = enableAddingActors.toString();
114+
}
115+
if (tools && tools.length > 0) {
116+
env.TOOLS = tools.join(',');
117+
}
118+
} else {
119+
// Use command line arguments as before
120+
if (actors) {
121+
args.push('--actors', actors.join(','));
122+
}
123+
if (enableAddingActors !== undefined) {
124+
args.push('--enable-adding-actors', enableAddingActors.toString());
125+
}
126+
if (tools && tools.length > 0) {
127+
args.push('--tools', tools.join(','));
128+
}
110129
}
111130

112131
const transport = new StdioClientTransport({
113132
command: 'node',
114133
args,
115-
env: {
116-
APIFY_TOKEN: process.env.APIFY_TOKEN as string,
117-
},
134+
env,
118135
});
119136
const client = new Client({
120137
name: 'stdio-client',

tests/integration/suite.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,57 @@ export function createIntegrationTestsSuite(
538538
}
539539
return false;
540540
}, { timeout: 30000, interval: 1000 });
541+
});
542+
543+
// Environment variable tests - only applicable to stdio transport
544+
it.runIf(options.transport === 'stdio')('should load actors from ACTORS environment variable', async () => {
545+
const actors = ['apify/python-example', 'apify/rag-web-browser'];
546+
const client = await createClientFn({ actors, useEnv: true });
547+
const names = getToolNames(await client.listTools());
548+
expect(names.length).toEqual(defaultTools.length + actors.length + addRemoveTools.length);
549+
expectToolNamesToContain(names, DEFAULT_TOOL_NAMES);
550+
expectToolNamesToContain(names, actors.map((actor) => actorNameToToolName(actor)));
551+
expectToolNamesToContain(names, addRemoveTools.map((tool) => tool.tool.name));
552+
553+
await client.close();
554+
});
555+
556+
it.runIf(options.transport === 'stdio')('should respect ENABLE_ADDING_ACTORS environment variable', async () => {
557+
// Test with enableAddingActors = false via env var
558+
const client = await createClientFn({ enableAddingActors: false, useEnv: true });
559+
const names = getToolNames(await client.listTools());
560+
expect(names.length).toEqual(defaultTools.length + defaults.actors.length);
561+
562+
expectToolNamesToContain(names, DEFAULT_TOOL_NAMES);
563+
expectToolNamesToContain(names, DEFAULT_ACTOR_NAMES);
564+
await client.close();
565+
});
566+
567+
it.runIf(options.transport === 'stdio')('should load tool categories from TOOLS environment variable', async () => {
568+
const categories = ['docs', 'runs'] as ToolCategory[];
569+
const client = await createClientFn({ tools: categories, useEnv: true });
570+
571+
const loadedTools = await client.listTools();
572+
const toolNames = getToolNames(loadedTools);
573+
574+
const expectedTools = [
575+
...toolCategories.docs,
576+
...toolCategories.runs,
577+
];
578+
const expectedToolNames = expectedTools.map((tool) => tool.tool.name);
579+
580+
// Handle case where tools are enabled by default
581+
const selectedCategoriesInDefault = categories.filter((key) => toolCategoriesEnabledByDefault.includes(key));
582+
const numberOfToolsFromCategoriesInDefault = selectedCategoriesInDefault
583+
.flatMap((key) => toolCategories[key]).length;
584+
585+
const numberOfToolsExpected = defaultTools.length + defaults.actors.length + addRemoveTools.length
586+
// Tools from tool categories minus the ones already in default tools
587+
+ (expectedTools.length - numberOfToolsFromCategoriesInDefault);
588+
expect(toolNames.length).toEqual(numberOfToolsExpected);
589+
for (const expectedToolName of expectedToolNames) {
590+
expect(toolNames).toContain(expectedToolName);
591+
}
541592

542593
await client.close();
543594
});

0 commit comments

Comments
 (0)