Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
25f6250
feat(mcp): add 'apify mcp install <client>' command
MQ37 May 18, 2026
43dbc16
refactor(mcp): use 'code --add-mcp' for vscode + add vscode-insiders
MQ37 May 18, 2026
a34d8d9
refactor(mcp): drop 'Docs:' line from install success output
MQ37 May 18, 2026
e9e1916
feat(mcp): preserve JSONC comments and formatting on file edits
MQ37 May 18, 2026
a433090
refactor(mcp): drop 'Next steps' from install success output
MQ37 May 18, 2026
d7cc312
docs(mcp): apply szaganek's copy tweaks from PR review
MQ37 May 18, 2026
b980ad0
Merge branch 'master' into feat/mcp-install
MQ37 May 18, 2026
bd3acb8
Update src/commands/mcp/install.ts
MQ37 May 19, 2026
c1e65e9
test(mcp): move install JSONC fixture to test/__setup__/fixtures/
MQ37 May 19, 2026
e3e1ff1
Merge branch 'master' into feat/mcp-install
MQ37 May 20, 2026
4693f7c
Merge branch 'master' into feat/mcp-install
MQ37 May 25, 2026
d108cde
test(mcp): move cursor JSONC fixture into a real .jsonc file
MQ37 May 25, 2026
364270d
Merge branch 'master' into feat/mcp-install
MQ37 May 27, 2026
be72483
fix(mcp): drop redundant unstubAllEnvs that wipes ~/.apify
MQ37 May 27, 2026
f24826b
feat(mcp): print APIFY_TOKEN next-step on codex; tighten install copy
MQ37 May 27, 2026
d4febae
refactor(mcp): replace jsonc-parser with existing jju dep
MQ37 May 27, 2026
f6a8070
docs(api): regen reference.md for --describe/--search help drift
MQ37 May 27, 2026
772e042
refactor(mcp): dedupe --add-mcp server JSON into one builder
MQ37 May 29, 2026
c9ea062
Merge branch 'master' into feat/mcp-install
MQ37 May 29, 2026
80ea43a
refactor(mcp): collapse per-client handlers into two strategies
MQ37 May 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 58 additions & 7 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@ ARGUMENTS
FLAGS
-d, --body=<value> The request body (JSON string).
Use "-" to read from stdin.
--describe=<value> Describe an endpoint: print every HTTP
method on a path, its summary, and path parameters.
Accepts a path like "actor-runs/{runId}" or
"/v2/actor-runs/{runId}".
--describe=<value> Print a reference for an endpoint
path: its HTTP methods, summary, and path parameters.
Leading slashes and a version prefix in the path are
optional. For example, "actor-runs/{runId}" and
"/v2/actor-runs/{runId}" are both accepted.
-H, --header=<value> Additional HTTP header(s). Pass a
single "key:value" string, or a JSON object like
'{"X-Foo": "bar", "X-Baz": "qux"}' to send multiple
Expand All @@ -85,9 +86,11 @@ FLAGS
<options: GET|POST|PUT|PATCH|DELETE>
-p, --params=<value> Query parameters as a JSON object,
e.g. '{"limit": 1, "desc": true}'.
-s, --search=<value> Filter --list-endpoints by a
space-separated query. Each token must appear
(case-insensitive) in method, path, or summary.
-s, --search=<value> Filter results returned by
--list-endpoints. The query is case-insensitive and split
into tokens by spaces. For an endpoint to be returned,
every token must appear in that endpoint's method, path,
or summary.
```

##### `apify telemetry`
Expand Down Expand Up @@ -1573,3 +1576,51 @@ FLAGS
```
<!-- task-commands-end -->
<!-- prettier-ignore-end -->

### MCP

Use these commands to configure the Apify MCP server in your AI client.

<!-- prettier-ignore-start -->
<!-- mcp-commands-start -->
##### `apify mcp`

```sh
DESCRIPTION
Configure the Apify MCP server in your AI client: Claude Code, Cursor, VS
Code, Codex CLI, Kiro, or Antigravity.

SUBCOMMANDS
mcp install Configure a local MCP client to use the Apify MCP
server. Writes or merges a server entry named 'apify' into the
client's config file, or runs the client's own 'mcp add' command
when available.
```

##### `apify mcp install`

```sh
DESCRIPTION
Configure a local MCP client to use the Apify MCP server. Writes or merges a
server entry named 'apify' into the client's config file, or runs the client's
own 'mcp add' command when available.

USAGE
$ apify mcp install <client> [-t <value>] [--tools <value>]
[--url <value>] [-y]

ARGUMENTS
client Target MCP client. One of: claude-code, cursor, vscode,
vscode-insiders, codex, kiro, antigravity.

FLAGS
-t, --token=<value> Apify API token to embed in the config.
Defaults to the token from 'apify login'.
--tools=<value> Comma-separated tool IDs or Actor full names
to expose. Forwarded as a '?tools=' query parameter.
--url=<value> Apify MCP server URL.
-y, --yes Overwrite an existing 'apify' entry
without prompting.
```
<!-- mcp-commands-end -->
<!-- prettier-ignore-end -->
5 changes: 5 additions & 0 deletions scripts/generate-cli-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ const categories: Record<string, CommandsInCategory[]> = {
{ command: Commands.task },
{ command: Commands.taskRun },
],
'mcp': [
//
{ command: Commands.mcp },
{ command: Commands.mcpInstall },
],
};

await renderDocs(categories);
9 changes: 9 additions & 0 deletions scripts/reference-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,12 @@ These commands help you manage scheduled and configured Actor runs. Use them to
<!-- task-commands-start -->
<!-- task-commands-end -->
<!-- prettier-ignore-end -->

### MCP

Use these commands to configure the Apify MCP server in your AI client.

<!-- prettier-ignore-start -->
<!-- mcp-commands-start -->
<!-- mcp-commands-end -->
<!-- prettier-ignore-end -->
2 changes: 2 additions & 0 deletions src/commands/_register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { InitCommand } from './init.js';
import { KeyValueStoresIndexCommand } from './key-value-stores/_index.js';
import { LoginCommand } from './login.js';
import { LogoutCommand } from './logout.js';
import { MCPIndexCommand } from './mcp/_index.js';
import { TopLevelPullCommand } from './pull.js';
import { ToplevelPushCommand } from './push.js';
import { RequestQueuesIndexCommand } from './request-queues/_index.js';
Expand All @@ -43,6 +44,7 @@ export const apifyCommands = [
BuildsIndexCommand,
DatasetsIndexCommand,
KeyValueStoresIndexCommand,
MCPIndexCommand,
RequestQueuesIndexCommand,
RunsIndexCommand,
SecretsIndexCommand,
Expand Down
8 changes: 3 additions & 5 deletions src/commands/cli-management/install.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import assert from 'node:assert';
import { existsSync, openSync } from 'node:fs';
import { mkdir, readFile, symlink, unlink, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { dirname, join } from 'node:path';
import { ReadStream } from 'node:tty';

Expand All @@ -12,11 +11,10 @@ import { ApifyCommand } from '../../lib/command-framework/apify-command.js';
import { useCLIMetadata } from '../../lib/hooks/useCLIMetadata.js';
import { useYesNoConfirm } from '../../lib/hooks/user-confirmations/useYesNoConfirm.js';
import { error, info, simpleLog, success, warning } from '../../lib/outputs.js';
import { detectShell, shellConfigFile, tildify } from '../../lib/utils.js';
import { detectShell, shellConfigFile, tildify, userHomeDir } from '../../lib/utils.js';
import { cliDebugPrint } from '../../lib/utils/cliDebugPrint.js';

const pathToInstallMarker = (installPath: string) => join(installPath, '.install-marker');
const HOMEDIR = () => process.env.HOME ?? homedir();

export class InstallCommand extends ApifyCommand<typeof InstallCommand> {
static override name = 'install' as const;
Expand Down Expand Up @@ -77,7 +75,7 @@ export class InstallCommand extends ApifyCommand<typeof InstallCommand> {
}

private async symlinkToLocalBin(installPath: string) {
const userHomeDirectory = HOMEDIR();
const userHomeDirectory = userHomeDir();

cliDebugPrint('[install -> symlinkToLocalBin] user home directory', userHomeDirectory);

Expand Down Expand Up @@ -211,7 +209,7 @@ export class InstallCommand extends ApifyCommand<typeof InstallCommand> {
return;
}

const userHomeDirectory = HOMEDIR();
const userHomeDirectory = userHomeDir();

cliDebugPrint('[install -> promptAddToShell] user home directory', userHomeDirectory);

Expand Down
18 changes: 18 additions & 0 deletions src/commands/mcp/_index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApifyCommand } from '../../lib/command-framework/apify-command.js';
import { MCPInstallCommand } from './install.js';

export class MCPIndexCommand extends ApifyCommand<typeof MCPIndexCommand> {
static override name = 'mcp' as const;

static override description = `Configure the Apify MCP server in your AI client: Claude Code, Cursor, VS Code, Codex CLI, Kiro, or Antigravity.`;

static override group = 'MCP';

static override docsUrl = 'https://docs.apify.com/cli/docs/reference#apify-mcp';

static override subcommands = [MCPInstallCommand];

async run() {
this.printHelp();
}
}
102 changes: 102 additions & 0 deletions src/commands/mcp/install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import process from 'node:process';

import { ApifyCommand } from '../../lib/command-framework/apify-command.js';
import { Args } from '../../lib/command-framework/args.js';
import { Flags, YesFlag } from '../../lib/command-framework/flags.js';
import { CommandExitCodes } from '../../lib/consts.js';
import { getClientHandler, isSupportedClient, SUPPORTED_CLIENTS } from '../../lib/mcp/clients.js';
import { buildMcpUrl, DEFAULT_MCP_URL } from '../../lib/mcp/url.js';
import { error } from '../../lib/outputs.js';
import { getLocalUserInfo } from '../../lib/utils.js';

/**
* Resolution order: --token flag → APIFY_TOKEN env → stored login.
* Prints a user-facing error and sets process.exitCode when no token is available.
*/
async function resolveApifyToken(tokenFlag: string | undefined): Promise<string | null> {
if (tokenFlag) return tokenFlag;
if (process.env.APIFY_TOKEN) return process.env.APIFY_TOKEN;

const userInfo = await getLocalUserInfo();
if (userInfo.token) return userInfo.token;

error({
message: `You are not logged in to Apify. Run 'apify login' first, or pass --token <api-token>.`,
});
process.exitCode = CommandExitCodes.MissingAuth;
return null;
}

export class MCPInstallCommand extends ApifyCommand<typeof MCPInstallCommand> {
static override name = 'install' as const;

static override description = `Configure a local MCP client to use the Apify MCP server. Writes or merges a server entry named 'apify' into the client's config file, or runs the client's own 'mcp add' command when available.`;

static override group = 'MCP';

static override interactive = true;

static override interactiveNote =
'Prompts before overwriting an existing JSON config entry (cursor, kiro, antigravity). Pass --yes to overwrite without prompting. For clients delegated to their own CLI (claude-code, vscode, vscode-insiders, codex), overwrite behavior is controlled by that CLI.';

static override examples = [
{
description: 'Add Apify to Claude Code using the stored API token.',
command: 'apify mcp install claude-code',
},
{
description: 'Add Apify to Cursor.',
command: 'apify mcp install cursor',
},
{
description: `Add only the 'search-actors' tool and the 'apify/rag-web-browser' Actor to VS Code.`,
command: 'apify mcp install vscode --tools search-actors,apify/rag-web-browser',
},
{
description: 'Add Apify to Codex CLI with an explicit token (non-interactive).',
command: 'apify mcp install codex --token apify_api_xxxxx --yes',
},
];

static override docsUrl = 'https://docs.apify.com/cli/docs/reference#apify-mcp-install';

static override args = {
client: Args.string({
required: true,
description: `Target MCP client. One of: ${SUPPORTED_CLIENTS.join(', ')}.`,
}),
};

static override flags = {
...YesFlag(`Overwrite an existing 'apify' entry without prompting.`),
token: Flags.string({
char: 't',
description: `Apify API token to embed in the config. Defaults to the token from 'apify login'.`,
}),
url: Flags.string({
description: 'Apify MCP server URL.',
default: DEFAULT_MCP_URL,
}),
tools: Flags.string({
description: `Comma-separated tool IDs or Actor full names to expose. Forwarded as a '?tools=' query parameter.`,
}),
};

async run() {
const { client } = this.args;
const { token: tokenFlag, url: baseUrl, tools, yes } = this.flags;

if (!isSupportedClient(client)) {
error({
message: `Unknown MCP client '${client}'. Supported clients: ${SUPPORTED_CLIENTS.join(', ')}.`,
});
process.exitCode = CommandExitCodes.InvalidInput;
return;
}

const token = await resolveApifyToken(tokenFlag);
if (!token) return;

await getClientHandler(client)({ url: buildMcpUrl(baseUrl, tools), token, yes });
}
}
Loading
Loading