Skip to content
Open
48 changes: 48 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1573,3 +1573,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 -->
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"jju": "~1.4.0",
"js-levenshtein": "^1.1.6",
"json-schema-to-typescript": "^15.0.4",
"jsonc-parser": "3.3.1",
"mime": "~4.1.0",
"open": "~11.0.0",
"rimraf": "~6.1.3",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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();
}
}
84 changes: 84 additions & 0 deletions src/commands/mcp/install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
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 { resolveApifyToken } from '../../lib/mcp/auth.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';

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 config entry. Pass --yes to overwrite without prompting.';

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 auth token'.`,
}),
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 });
}
}
23 changes: 23 additions & 0 deletions src/lib/mcp/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import process from 'node:process';

import { CommandExitCodes } from '../consts.js';
import { error } from '../outputs.js';
import { getLocalUserInfo } from '../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.
*/
export 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;
}
Loading
Loading