CopilotKit / CopilotKit/CopilotKit

@ag-ui/mcp-middleware@0.0.1 pins @ag-ui/client 0.0.54, nesting a 7.3 MB duplicate tree in every runtime install

Open Beginner friendly
#6,921 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dependencies-core
Dominant language
TypeScript
Stars
37.4k
Forks
4.6k
Avg merge
1d 6h
Merged PRs (30d)
444

Description

Every @copilotkit/runtime install ships a second, older copy of the @ag-ui client tree nested under @ag-ui/mcp-middleware.

What a real install looks like

Installing the published @copilotkit/runtime@1.70.1 + @copilotkit/react-core@1.70.1 into an empty project:

node_modules/@ag-ui/core: 0.0.59                                     <- what react-core and runtime resolve
node_modules/@ag-ui/client: 0.0.59
node_modules/@ag-ui/mcp-middleware/node_modules/@ag-ui/client: 0.0.54
node_modules/@ag-ui/mcp-middleware/node_modules/@ag-ui/core: 0.0.54
node_modules/@ag-ui/mcp-middleware/node_modules/@ag-ui/proto: 0.0.54

The nested tree is 7.3 MB, out of 22 MB total for node_modules/@ag-ui.

Cause

@ag-ui/mcp-middleware@0.0.1 declares an exact pin:

{ "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", "@ag-ui/client": "0.0.54" } }

0.0.1 is the only version ever published, so the pin cannot be satisfied by dedupe no matter what the host resolves. It is a hard dependency, not a peer.

The middleware needs @ag-ui/client for exactly one thing. Instrumenting the module load shows it reads a single property off the package:

exports of mcp-middleware: DEFAULT_MAX_ITERATIONS, MAX_TOOL_NAME_LENGTH, MCPMiddleware, MCP_TOOL_NAME_PREFIX
properties read off @ag-ui/client: Middleware

Middleware is the abstract base class it extends. Pulling that one class in as a hard exact dependency drags @ag-ui/client -> @ag-ui/core -> @ag-ui/proto along with it.

This is not a correctness bug today

Stating this explicitly so nobody escalates it. The nested copy is loaded at runtime, and MCPMiddleware is constructed from copy 0.0.54 before being attached in attachIntelligenceEnterpriseLearning (packages/runtime/src/v2/runtime/handlers/shared/agent-utils.ts) to an agent built from copy 0.0.59. That cross-copy attachment works, for two reasons I verified:

  1. AbstractAgent.use() duck-types. It does no instanceof check, it just pushes the object:
    use(...e){ let t = e.map(e => typeof e === "function" ? new B(e) : e); return this.middlewares.push(...t), this }
    
  2. The Middleware abstract class declaration is byte-identical between @ag-ui/client 0.0.54 and 0.0.59.

The nested copy's Zod event schemas are never used for validation either, so this does not reintroduce the null-parentMessageId class of failure from #2788.

Why it is still worth fixing

  • 7.3 MB of duplicate install weight on every runtime consumer.
  • The contract identity is a coincidence, not a guarantee. Point 2 above holds for the current pair of versions only. The pin drifts one release further behind every time ag-ui ships. Whenever Middleware does change, enterprise-learning MCP attachment breaks across the copy boundary, and it breaks silently, because nothing type-checks the boundary at runtime.
  • Duplicate-dependency and stale-transitive scanners flag it continuously.

Two fixes, in different repos

1. Upstream, ag-ui (the real fix). Republish @ag-ui/mcp-middleware with @ag-ui/client as a peerDependency (or at minimum a range rather than an exact pin), so it dedupes onto the host's copy. This is the same shape @ag-ui/mcp-apps-middleware@0.0.3 already uses, which is why that package produces no nested copy.

2. Here, the stale override. The root package.json carries:

"@ag-ui/mcp-middleware>@ag-ui/client": "0.0.53"

Added in c3f7961242 (2026-06-04) alongside adopting @ag-ui/mcp-middleware@0.0.1, when the repo itself was on 0.0.53 and the override collapsed the tree to one copy. The repo has since moved to 0.0.59 and the override never followed. It now pins our workspace copy to 0.0.53, which is older than even the upstream 0.0.54, and it dedupes nothing. Note that a pnpm override is workspace-only, so it has never affected published consumers at all.

Bump it in lockstep with the rest of the @ag-ui/* pins, or delete it once fix 1 lands.

Verification

Reproduce the copy list with:

mkdir /tmp/dupe && cd /tmp/dupe && npm init -y >/dev/null
npm install @copilotkit/runtime@1.70.1 @copilotkit/react-core@1.70.1
find node_modules -path "*@ag-ui/*/package.json" -maxdepth 5 \
  -exec sh -c 'echo "$(dirname {}): $(node -p "require(\"./{}\").version")"' \;
du -sh node_modules/@ag-ui/mcp-middleware/node_modules

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the root package.json override for @ag-ui/mcp-middleware>@ag-ui/client and compare it with the other @ag-ui pins, then inspect the lockfile and the commit that introduced it. Reproduce the dependency tree with the provided npm install and find commands. Done means the workspace no longer retains the stale override, or it is aligned with the current @ag-ui version; the upstream peer-dependency fix is a separate repository task.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
build-system, tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.