Skip to content

fix(ai-mcp): add shims to bin bundle for CJS dynamic-require compat#754

Open
citizen204 wants to merge 1 commit into
TanStack:mainfrom
citizen204:fix-753-mcp-cli-dynamic-require-shims
Open

fix(ai-mcp): add shims to bin bundle for CJS dynamic-require compat#754
citizen204 wants to merge 1 commit into
TanStack:mainfrom
citizen204:fix-753-mcp-cli-dynamic-require-shims

Conversation

@citizen204

@citizen204 citizen204 commented Jun 13, 2026

Copy link
Copy Markdown

Summary

@tanstack/ai-mcp 0.1.0 ships its CLI as an ESM bundle with json-schema-to-typescript inlined via noExternal. That library uses require() internally, which is not available in ESM-output bundles — so the CLI crashes on startup with:

Error: Dynamic require of "fs" is not supported
    at .../node_modules/@tanstack/ai-mcp/dist/bin/chunk-LMMQX4CK.js
    at .../json-schema-to-typescript/dist/src/index.js

before it can print help or run codegen.

Changes

  • packages/ai-mcp/tsup.bin.config.ts: add shims: true

tsup's shims option injects a createRequire(import.meta.url) shim at the top of the ESM bundle, making CJS dependencies' require() calls work correctly. This is the standard fix for CJS-in-ESM bundling with tsup.

Fixes #753

Changes

  • packages/ai-mcp/tsup.bin.config.ts: add shims: true

Summary by CodeRabbit

  • Chores
    • Updated build configuration to ensure proper module bundling and compatibility across different environments.

json-schema-to-typescript (inlined via noExternal) uses dynamic require()
calls internally. When bundled into an ESM output the global require is
absent, causing the CLI to crash on startup with
"Dynamic require of 'fs' is not supported" before it can parse any args.

shims: true makes tsup inject a createRequire-based shim at the top of the
bundle so CJS dependencies can call require() safely from within the ESM
output.

Fixes TanStack#753
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The AI-MCP CLI bundling configuration is updated to enable CommonJS shim injection in the tsup bundler, allowing ESM-bundled dependencies that use dynamic require() calls to function correctly at runtime without "Dynamic require is not supported" errors.

Changes

Bundle configuration for ESM compatibility

Layer / File(s) Summary
Enable CJS dynamic require shims in tsup config
packages/ai-mcp/tsup.bin.config.ts
The tsup configuration option shims: true is added with explanatory comments. This injects createRequire polyfills into the ESM bundle, allowing json-schema-to-typescript and other CJS dependencies to use dynamic require() calls when the binary runs.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰 A CLI that wouldn't start, now hops with cheer—
shims inject require, and the path is clear!
ESM and CJS, once at odds, now play,
bundled together, the rabbit's way. 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding shims to the bin bundle to fix CJS dynamic-require compatibility issues.
Description check ✅ Passed The description covers the problem, solution, and links the fix to issue #753, but is missing the checklist section required by the template.
Linked Issues check ✅ Passed The PR fully addresses issue #753 by adding the shims: true option to prevent ESM bundling errors when CJS dependencies use dynamic require().
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the CLI startup crash by adding the shims configuration; no out-of-scope modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ai-mcp/tsup.bin.config.ts`:
- Around line 13-15: The comment claiming "shims: true injects createRequire" is
incorrect; update the inline comment in tsup config to explain that shims only
provide ESM/Node global compatibility and do not polyfill dynamic require, and
then add a tsup injection/banner that defines a require using Node's
createRequire (e.g., use createRequire(import.meta.url) to set a global require
helper) so json-schema-to-typescript's dynamic require() calls will work in the
ESM bundle; specifically modify the tsup config where "shims: true" is set to
replace the comment and add an "inject" or "banner" entry that imports
createRequire from "module" and assigns const require =
createRequire(import.meta.url) (or attaches it to globalThis), ensuring the
bundle provides a working require at runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3852e1cb-0669-4731-9370-6d95a4e67a37

📥 Commits

Reviewing files that changed from the base of the PR and between 984ac3c and 7a6f1d2.

📒 Files selected for processing (1)
  • packages/ai-mcp/tsup.bin.config.ts

Comment on lines +13 to +15
// json-schema-to-typescript uses CJS dynamic require(); shims injects
// createRequire so those calls work inside this ESM bundle.
shims: true,

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify tsup shims option documentation and behavior

# Check tsup documentation for the shims option
echo "=== Checking tsup package.json for version ==="
cat package.json | jq -r '.devDependencies.tsup // .dependencies.tsup // "not found"'

echo -e "\n=== Searching for tsup shims usage in the codebase ==="
rg -n --type=ts 'shims.*true' -g '!node_modules' -C2

Repository: TanStack/ai

Length of output: 553


🌐 Web query:

What does the tsup shims option do for ESM bundles with dynamic require calls?

💡 Result:

The shims option in tsup is designed to provide compatibility polyfills when building code that assumes the presence of Node.js-specific global variables or module system features in formats where they are not natively available [1][2]. Specifically, it injects code to support dirname and filename in ESM bundles (which natively only provide import.meta.url) and provides import.meta.url in CJS bundles [1][2]. While the shims option is intended to bridge these gaps, it is not a comprehensive solution for "dynamic require" errors in ESM bundles [3][4]. 1. Purpose of Shims: When enabled (shims: true), tsup automatically injects polyfills via esbuild's inject configuration [1]. These polyfills define dirname and filename using node:path and node:url utilities within ESM [1]. 2. Limitations with Dynamic Require: Enabling shims does not automatically provide a global require function in ESM [5]. If your code performs dynamic require() calls (a CJS-specific feature), it will fail in an ESM environment because ESM does not natively support require [3][4]. 3. Recommended Workarounds: - Manual Shim Injection: To support dynamic require in ESM, developers must often manually create a require function using createRequire from the node:module package [5][3]. - Configuration Pattern: A common approach involves using tsup's inject option to point to a custom shim file that defines globalThis.require = createRequire(import.meta.url) [5]. Alternatively, some developers use the banner option to prepend this definition to the generated ESM output [3]. In summary, the shims option handles specific variable compatibility (dirname/filename) but does not resolve dynamic require limitations in ESM [1][2]. Resolving dynamic require calls typically requires manual injection of a createRequire polyfill [5][3].

Citations:


Fix tsup shims explanation/mechanism: shims: true usually doesn’t provide createRequire for ESM dynamic require()

packages/ai-mcp/tsup.bin.config.ts comments claim shims: true “inject[s] createRequire” so CJS require() calls from json-schema-to-typescript work in the ESM bundle, but tsup’s shims option is primarily for ESM/Node global compatibility (e.g., dirname/filename) and is not generally a solution for ESM “Dynamic require” failures. To address that crash, use tsup’s inject/banner to define a require via createRequire(import.meta.url), and update the inline comments to match the actual mechanism.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-mcp/tsup.bin.config.ts` around lines 13 - 15, The comment
claiming "shims: true injects createRequire" is incorrect; update the inline
comment in tsup config to explain that shims only provide ESM/Node global
compatibility and do not polyfill dynamic require, and then add a tsup
injection/banner that defines a require using Node's createRequire (e.g., use
createRequire(import.meta.url) to set a global require helper) so
json-schema-to-typescript's dynamic require() calls will work in the ESM bundle;
specifically modify the tsup config where "shims: true" is set to replace the
comment and add an "inject" or "banner" entry that imports createRequire from
"module" and assigns const require = createRequire(import.meta.url) (or attaches
it to globalThis), ensuring the bundle provides a working require at runtime.

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.

bug(ai-mcp): CLI fails on startup

1 participant