Skip to content

perf(cli): start the MCP server in-process before the CLI loads - #1871

Open
ivanvpetrov wants to merge 6 commits into
masterfrom
ipetrov/ig-mcp-fast-path
Open

ivanvpetrov wants to merge 6 commits into
masterfrom
ipetrov/ig-mcp-fast-path

Conversation

@ivanvpetrov

@ivanvpetrov ivanvpetrov commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Description

ig mcp loaded the whole CLI before it reached the mcp command (yargs, every command, the template catalog of every framework) and then spawned node dist/index.js as a child, staying alive as its parent for the whole session. This cost about 0.7 s per start and an extra Node process (about 50 MB) per MCP session. It also made the server depend on the CLI's own start-up, e.g. every ig command crashed in a folder whose package.json has no dependencies field (#1870 fixes that crash; with this PR, ig mcp no longer runs that code at all).

  • bin/execute.js now handles ig mcp before the CLI loads: it picks the CLI copy as before (the project's local copy if there is one), finds @igniteui/mcp-server installed with that copy and imports it in the same process. The session runs in a single Node process, with no CLI parent and no child.
  • --remote <url>, --remote=<url> and --debug are passed to the server. --help, --version, any other or incomplete option (e.g. --debug=true, --no-debug), and a missing or unbuilt server fall back to the existing ig mcp command, so its help and error messages are unchanged. That fallback still loads the CLI and starts the server as a child process; the entries ig ai-config writes and the documented options never reach it.
  • The server lookup follows the CLI copy's real path, so the fast path also works in pnpm projects, where packages are linked and their dependencies sit next to the link target.
  • The "Different igniteui-cli global and local version" notice now goes to stderr. On stdout it would reach the MCP client as an invalid protocol message.

Warm start to the initialize response (Windows, Node 24): 1.04–1.13 s before, 0.42–0.49 s after; the server started directly takes 0.36–0.41 s.

Related Issue

Closes #1864

Addresses the issue's second point (ig mcp loads the CLI and keeps it alive as the parent of the server process). #1870 handles the first: the entry ig ai-config writes. Projects that list igniteui-cli keep the npx -y igniteui-cli mcp entry, and this PR makes that path about as fast as starting @igniteui/mcp-server directly. npx runs the project's own CLI copy there, so these projects get the speed-up once they update igniteui-cli to a release that includes it.

#1870 and this PR touch different files and can merge in either order.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring / code cleanup
  • Build / CI configuration change

Affected Packages

  • igniteui-cli (packages/cli)
  • @igniteui/cli-core (packages/core)
  • @igniteui/angular-templates (packages/igx-templates)
  • @igniteui/angular-schematics (packages/ng-schematics)
  • @igniteui/mcp-server (packages/igniteui-mcp)

Checklist

  • I have tested my changes locally (npm run test)
  • I have built the project successfully (npm run build)
  • I have run the linter (npm run lint)
  • I have added/updated tests as needed
  • My changes do not introduce new warnings or errors

Additional Context

New tests:

  • Unit tests for the argument mapping and the server lookup.
  • Acceptance tests that run bin/execute.js mcp and check that the server answers initialize, that stdout carries only JSON-RPC, and that the process exits when stdin closes.
  • Acceptance tests for both --remote=<url> and --remote <url>: they call list_components against a local HTTP backend and check that the backend receives the request and its response reaches the tool result. They fail if the launcher drops the flag.
  • Each acceptance run has a deadline: a hung server is killed and the spec fails with the request it didn't answer.

`ig mcp` loaded the whole CLI (yargs, every template) and then spawned
the server as a second Node process. bin/execute.js now finds the
server installed with the selected CLI copy and imports it directly,
which halves the warm start (about 1.1 s to 0.45 s) and runs one
process per session. Help, version, other options and a missing or
unbuilt server still go through the CLI.

The server lookup follows the CLI copy's real path, so it works in
pnpm projects. The global/local version notice now goes to stderr, as
stdout carries the MCP protocol.
@coveralls

coveralls commented Nov 2, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 95.365% (-0.004%) from 95.369% — ipetrov/ig-mcp-fast-path into master

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The acceptance test lacks an internal deadline to kill the child and reject, so an unresponsive MCP server can leave its process and pipes alive after Jasmine times out.

1 open issue
What changed in this PR

Speeds up ig mcp by starting the MCP server in-process before loading the full CLI. This complements #1870 for projects that retain the local CLI entry.

Changes:

  • Adds an early MCP startup path using the selected CLI copy’s server, including pnpm-aware resolution.
  • Maps supported server arguments, preserves CLI fallback behavior, and sends version notices to stderr.
  • Adds unit and acceptance coverage for argument handling, server resolution, initialization, shutdown, and help.
File Description
spec/​unit/​mcp-cli-spec.ts Tests argument mapping and server entry resolution.
spec/​acceptance/​mcp-spec.ts Tests initialization, protocol output, shutdown, and remote arguments.
spec/​acceptance/​help-spec.ts Checks MCP command help output.
packages/​cli/​lib/​commands/​mcp.ts Adds lightweight argument and server-resolution helpers.
packages/​cli/​bin/​execute.js Starts MCP before loading the CLI and routes version notices to stderr.

Review effort: Balanced

Comment thread spec/acceptance/mcp-spec.ts Resolved
The spec now has its own deadline, shorter than the Jasmine timeout,
that kills the child and rejects, so a server that never answers
initialize or never exits doesn't outlive the spec.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The acceptance test can pass even if --remote is dropped because initialization never contacts the remote backend, so it needs a fixture-backed tool call to verify argument forwarding.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Test that remote mode forwards requests to the backend

spec/​acceptance/​mcp-spec.ts:80

This spec also passes if the launcher drops --remote entirely: both local and remote modes return the same serverInfo.name, and initialization never contacts the remote backend. Please call a remote-backed tool against a small HTTP fixture and assert that the fixture receives the request, so the acceptance test actually detects failures in the new argument-forwarding path.

1 resolved since last review

Review effort: Balanced

The remote specs started the server against an unreachable URL and
only checked the initialize reply, which local mode returns too.
They now call list_components against a local HTTP fixture and
assert that the fixture received the request and that its response
reaches the tool result, for both --remote=<url> and --remote <url>.
@ivanvpetrov

Copy link
Copy Markdown
Contributor Author

🔵 Needs a closer look

The acceptance test can pass even if --remote is dropped because initialization never contacts the remote backend, so it needs a fixture-backed tool call to verify argument forwarding.

No open issues 🎉

1 previously missed issue
In code that hasn't changed since last review

Medium severity Test that remote mode forwards requests to the backend
spec/​acceptance/​mcp-spec.ts:80

This spec also passes if the launcher drops --remote entirely: both local and remote modes return the same serverInfo.name, and initialization never contacts the remote backend. Please call a remote-backed tool against a small HTTP fixture and assert that the fixture receives the request, so the acceptance test actually detects failures in the new argument-forwarding path.

1 resolved since last review

Review effort: Balanced

Addressed: the remote specs now start a local HTTP fixture, call list_components through ig mcp --remote=<url> and --remote <url>, and assert that the fixture received GET /api/docs?framework=angular&filter=grid and that its response reaches the tool result. Verified that both specs fail when the flag is not passed (the request is then served from the local database).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues remain for the in-process MCP startup change.

No open issues 🎉

Review effort: Balanced

@ivanvpetrov
ivanvpetrov marked this pull request as ready for review November 4, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP: the npx -y igniteui-cli mcp entry written by ai-config is the slowest way to start the server

3 participants