Skip to content

feat(mcp): add 'apify mcp install <client>' command#1145

Open
MQ37 wants to merge 10 commits into
masterfrom
feat/mcp-install
Open

feat(mcp): add 'apify mcp install <client>' command#1145
MQ37 wants to merge 10 commits into
masterfrom
feat/mcp-install

Conversation

@MQ37
Copy link
Copy Markdown
Contributor

@MQ37 MQ37 commented May 18, 2026

Context

Implements the "configuration only" half of apify/ai-team#147 — a new apify mcp install <client> command that configures the Apify MCP server in six AI clients (Claude Code, Cursor, VS Code, Codex CLI, Kiro, Antigravity).

Closes apify/ai-team#175
Part of apify/ai-team#147

Solution

Per-client handler dispatched by the client argument, writing the documented config shape to each client's user-global location, or shelling out to the client's own CLI (claude mcp add --scope user, codex mcp add). Token resolves from --tokenAPIFY_TOKEN env → stored apify login. JSON files merge the apify entry without clobbering other entries; an existing entry triggers a --yes/prompt overwrite gate (matches the YesFlag() convention used by actors rm, builds rm, etc.).

Worth your attention

  • Token never reaches the terminal. claude mcp add echoes the Authorization header on success, so we use stdio: ['ignore', 'ignore', 'inherit'] to discard child stdout and keep live errors. The overwrite-confirm prompt no longer prints the existing entry either. Two expect(stderr).not.toContain(TEST_TOKEN) tests guard against regression.
  • Claude Desktop intentionally not included — its MCP support currently relies on the mcp-remote local stdio bridge, which is on a deprecation track as Claude Desktop adds native remote-MCP support via Connectors. Easy to add back later.
  • claude-code passes --scope user so the server lands in user-wide config, matching every other client's default user-global location.
  • Codex uses bearer_token_env_var = "APIFY_TOKEN" — codex's runtime rejects literal bearer_token (Config schema exposes unsupported MCP bearer_token field openai/codex#19275), so env var indirection is the only HTTP-transport auth it accepts. The next-steps tell the user to export APIFY_TOKEN=<your-token> in their shell rc.
  • userHomeDir helper extracted to src/lib/utils.ts and reused from cli-management/install.ts (removes the second copy of the same idiom).

Follow-up

  • apify/ai-team#176 — built-in agent (apify <prompt>) bundling mcpc + OpenRouter Actor.

Configures the Apify MCP server in six AI clients (claude-code, cursor, vscode, codex, kiro, antigravity) by writing the client's canonical config or shelling out to its own 'mcp add' CLI.
@MQ37 MQ37 requested review from szaganek and vladfrangu as code owners May 18, 2026 07:17
@github-actions github-actions Bot added t-ai Issues owned by the AI team. tested Temporary label used only programatically for some analytics. labels May 18, 2026
@vladfrangu
Copy link
Copy Markdown
Member

VS Code, and possibly it's forks (I only have Cursor that I checked), have a CLI interface for adding MCP servers

Model Context Protocol
  --add-mcp <json> Adds a Model Context Protocol server definition to the user profile. Accepts JSON input in the form '{"name":"server-name","command":...}'

let's avoid editing files wherever possible. And if we cannot avoid editing files, we must preserve everything in them at all costs (not necessarily indentation between tabs and spaces but definitely comments [so read the file via I think JSON5])

@vladfrangu
Copy link
Copy Markdown
Member

Let's also add in vscode-insiders please (identical to vscode except binary is code-insiders)

Comment thread docs/reference.md Outdated
Comment thread docs/reference.md Outdated
Comment thread docs/reference.md Outdated
Comment thread docs/reference.md Outdated
Comment thread docs/reference.md Outdated
Comment thread docs/reference.md Outdated
Comment thread docs/reference.md Outdated
@vladfrangu
Copy link
Copy Markdown
Member

Don't forget reference docs changes need to be applied in code, not the markdown file!

MQ37 added 2 commits May 18, 2026 11:18
Switch the vscode handler to shell out to 'code --add-mcp <json>' so VS Code itself owns the config write (preserves JSONC comments, format, and overwrite semantics). Add vscode-insiders as a sibling client using 'code-insiders --add-mcp'. Other file-edit clients (cursor, kiro, antigravity) keep their current handlers — their CLIs either don't expose '--add-mcp' (cursor's wrapper double-evals args breaking JSON quoting) or their --add-mcp behavior isn't suitable for our use case.
After a successful install, a link to the third-party client's MCP docs is noise. The user just configured the server — they don't need to look up how the client handles MCP.
@MQ37
Copy link
Copy Markdown
Contributor Author

MQ37 commented May 18, 2026

@vladfrangu Good point! I verified and the CLI command approach works only for VSCode but not for the forks - so I am using the --add-mpc for the vscode and vscode-insiders client. Also lets use https://www.npmjs.com/package/jsonc-parser for manipulating the files so we can actually preserve the comments.

@vladfrangu
Copy link
Copy Markdown
Member

Really, it doesn't work for cursor? They DO document the flag, does it just not register correctly? 😢

@MQ37
Copy link
Copy Markdown
Contributor Author

MQ37 commented May 18, 2026

@vladfrangu For Cursor I could not pass the JSON as they have some bug in the CLI wrapper bash script. And for antigravity and kiro it did not work - the commands return "Added MCP servers: apify" but the server is not actually written to the kiro or antigravity config and thus is not available. I think they just left it there as they are forks of vscode and never actually implemented this properly.

MQ37 added 3 commits May 18, 2026 11:58
Switch file-edit clients (cursor, kiro, antigravity) from JSON.parse + JSON.stringify to jsonc-parser's surgical modify()/applyEdits() API. The library patches the source text in place: comments, trailing commas, indentation, and unrelated keys all survive untouched. Adds a regression test that pre-seeds a hand-edited JSONC file with comments and asserts they survive a re-install.
Keep only Server URL, Auth, and Config path. The per-client step lists were noise — users already know how to restart their editor.
"Use these commands to configure the Apify MCP server in your AI client." — drop "favorite", drop parenthetical client list from the section intro. Colon-separate client names in the index description, drop parenthetical '(or merges)' and '(forwarded as…)' from the install command text.
@MQ37
Copy link
Copy Markdown
Contributor Author

MQ37 commented May 18, 2026

@szaganek all you suggestions applied, thank you!

@patrikbraborec
Copy link
Copy Markdown
Contributor

Hi, @l2ysho is going to work on #1115. Maybe we will need to change implementation based on it?

@MQ37
Copy link
Copy Markdown
Contributor Author

MQ37 commented May 19, 2026

@patrikbraborec in the current implementation we are using the getLocalUserInfo helper func, that should be properly updated once we move to the keyring, so I think there are no changes needed in this PR regarding getting the local token.

@vladfrangu what is your take on this?

Copy link
Copy Markdown
Member

@vladfrangu vladfrangu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nits

Comment thread src/commands/mcp/install.ts Outdated
Comment thread src/lib/mcp/clients.ts
printResult({
clientLabel: 'Codex CLI',
serverUrl: url,
authDescription: `Bearer token from APIFY_TOKEN environment variable`,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have no way to tell MCP clients or w/e they need to run a cli command to get the token?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean? Like we should try to detect client installed on the system? I would honestly no do that, but it is my personal preference - I like explicit control about what I install and what does it do.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nono, to tell these mcp clients like codex "hey to get this token please run apify auth token"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on openai docs this is the only way https://developers.openai.com/codex/mcp#streamable-http-servers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is so stupid...can we propose that to them? Maybe even clanker pr it 😂

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can try but from my experience with this approach we never actually ship this feature as pushing PR there will be super painful - but maybe they are fast and responsive 😄 I would honestly merge this is we want to ever ship this and then we can negotiate with them and iterate on this Apify CLI feature.

Comment thread test/local/commands/mcp/install.test.ts Outdated
MQ37 and others added 2 commits May 19, 2026 22:28
Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
Per PR review (#1145): hoist the inline 'originalJsonc' literal out of
install.test.ts into test/__setup__/fixtures/mcp-install-fixtures.ts so
similar fixtures land alongside the existing mock-openapi-spec.
@MQ37 MQ37 requested a review from vladfrangu May 20, 2026 08:07
@MQ37
Copy link
Copy Markdown
Contributor Author

MQ37 commented May 20, 2026

@vladfrangu some tests are failing but it should be unrelated to this PR:


 FAIL  test/api/__fixtures__/commands/python/python-scrapy-template-works.test.ts > [python] [api] scrapy template works > should run the actor
 FAIL  test/api/__fixtures__/commands/python/python-scrapy-template-works.test.ts > [python] [api] scrapy template works > should run the actor
 FAIL  test/api/__fixtures__/commands/python/python-scrapy-template-works.test.ts > [python] [api] scrapy template works > should run the actor
 FAIL  test/api/__fixtures__/commands/python/python-scrapy-template-works.test.ts > [python] [api] scrapy template works > should run the actor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-ai Issues owned by the AI team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants