-
Notifications
You must be signed in to change notification settings - Fork 48
feat(mcp): add 'apify mcp install <client>' command #1145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MQ37
wants to merge
26
commits into
master
Choose a base branch
from
feat/mcp-install
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
25f6250
feat(mcp): add 'apify mcp install <client>' command
MQ37 43dbc16
refactor(mcp): use 'code --add-mcp' for vscode + add vscode-insiders
MQ37 a34d8d9
refactor(mcp): drop 'Docs:' line from install success output
MQ37 e9e1916
feat(mcp): preserve JSONC comments and formatting on file edits
MQ37 a433090
refactor(mcp): drop 'Next steps' from install success output
MQ37 d7cc312
docs(mcp): apply szaganek's copy tweaks from PR review
MQ37 b980ad0
Merge branch 'master' into feat/mcp-install
MQ37 bd3acb8
Update src/commands/mcp/install.ts
MQ37 c1e65e9
test(mcp): move install JSONC fixture to test/__setup__/fixtures/
MQ37 e3e1ff1
Merge branch 'master' into feat/mcp-install
MQ37 4693f7c
Merge branch 'master' into feat/mcp-install
MQ37 d108cde
test(mcp): move cursor JSONC fixture into a real .jsonc file
MQ37 364270d
Merge branch 'master' into feat/mcp-install
MQ37 be72483
fix(mcp): drop redundant unstubAllEnvs that wipes ~/.apify
MQ37 f24826b
feat(mcp): print APIFY_TOKEN next-step on codex; tighten install copy
MQ37 d4febae
refactor(mcp): replace jsonc-parser with existing jju dep
MQ37 f6a8070
docs(api): regen reference.md for --describe/--search help drift
MQ37 772e042
refactor(mcp): dedupe --add-mcp server JSON into one builder
MQ37 c9ea062
Merge branch 'master' into feat/mcp-install
MQ37 80ea43a
refactor(mcp): collapse per-client handlers into two strategies
MQ37 6d3452d
Merge branch 'master' into feat/mcp-install
MQ37 5ba6935
fix(mcp): address PR #1145 review bugs for home dir, root object, URL…
MQ37 87f2a7d
Merge branch 'master' into feat/mcp-install
MQ37 5f68d87
Merge branch 'master' into feat/mcp-install
MQ37 0624631
fix(mcp): normalize tildify output to forward slashes for cross-platf…
MQ37 25f8051
fix(mcp): prevent token leaks on CLI failure and drop codex token req…
MQ37 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 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 }); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.