Skip to content

[Med] fix(openapi): add shebang and chmod +x to gen-mcp generated index.js#715

Open
katnisscalls99 wants to merge 1 commit into
profullstack:masterfrom
katnisscalls99:fix/gen-mcp-shebang-bin
Open

[Med] fix(openapi): add shebang and chmod +x to gen-mcp generated index.js#715
katnisscalls99 wants to merge 1 commit into
profullstack:masterfrom
katnisscalls99:fix/gen-mcp-shebang-bin

Conversation

@katnisscalls99
Copy link
Copy Markdown

generateMcpServer() emits a package.json with a bin entry pointing to index.js, but the generated index.js starts with a comment line — no shebang. When the user runs npm i -g @generated/foo-mcp and then invokes foo-mcp on the shell, the OS tries to exec index.js as a native script. Without #!/usr/bin/env node it sees import as the first token and fails with ENOEXEC on Linux or a cryptic syntax error on macOS, neither of which suggests the real problem.

Two fixes:

  1. Prepend #!/usr/bin/env node to the generated index template so the file is self-describing as a Node.js script.

  2. chmod(dest, 0o755) the emitted index.js at write time (only that file, not package.json) so npm link / global install symlinks can exec it. Uses .catch(() => {}) so the write step doesn't fail on Windows or read-only filesystems where the mode change is irrelevant.

The bug affects every MCP server generated by gen-mcp when installed globally, which is the documented primary use case per the emitted README.

Severity: Medium

generateMcpServer() emits a package.json with a bin entry pointing to
index.js, but the generated index.js starts with a comment line — no
shebang. When the user runs 'npm i -g @generated/foo-mcp' and then
invokes 'foo-mcp' on the shell, the OS tries to exec index.js as a
native script. Without #!/usr/bin/env node it sees 'import' as the first
token and fails with ENOEXEC on Linux or a cryptic syntax error on macOS,
neither of which suggests the real problem.

Two fixes:

1. Prepend '#!/usr/bin/env node' to the generated index template so the
   file is self-describing as a Node.js script.

2. chmod(dest, 0o755) the emitted index.js at write time (only that file,
   not package.json) so 'npm link' / global install symlinks can exec it.
   Uses .catch(() => {}) so the write step doesn't fail on Windows or
   read-only filesystems where the mode change is irrelevant.

The bug affects every MCP server generated by gen-mcp when installed
globally, which is the documented primary use case per the emitted README.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 6, 2026

Greptile Summary

This PR fixes two related gaps in the MCP server code generator: the generated index.js now starts with #!/usr/bin/env node so the OS knows how to exec it, and the generator chmods the emitted file to 0755 immediately after writing so that npm i -g / npm link installs a runnable symlink.

  • Shebang#!/usr/bin/env node is prepended to the index template string inside render(); without it every global-install invocation of the generated binary fails with a confusing ENOEXEC or SyntaxError before Node.js is even involved.
  • Executable bitchmod(dest, 0o755) is called after writeFile for the index.js file only; errors are silently swallowed (.catch(() => {})) to avoid breaking Windows users where chmod is a no-op.
  • Scope — change is limited to packages/openapi/src/gen-mcp/index.ts; no schema, API, or runtime changes are involved.

Confidence Score: 4/5

Safe to merge; the two changes are narrowly scoped to the MCP generator and fix a real usability gap for global installs.

Both fixes are correct and solve the documented bug. The only notable rough edge is that .catch(() => {}) silently swallows every chmod error rather than just the expected platform-level ones (ENOSYS, EPERM, ENOTSUP), which could mask a genuine failure in unusual environments. The hardcoded 'index.js' string appearing independently in the chmod guard and in the bin field of the emitted package.json is a minor coupling risk if the entry-point path ever changes. Neither issue affects correctness on the primary Linux/macOS targets today.

packages/openapi/src/gen-mcp/index.ts — the chmod error handling and the duplicated entry-point path literal are both worth a second look.

Important Files Changed

Filename Overview
packages/openapi/src/gen-mcp/index.ts Adds shebang line to the generated index.js template and chmod 0755 the emitted file; two minor concerns: all chmod errors are silently swallowed and the entry-point path is hardcoded in two separate places.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[generateMcpServer called] --> B[render IR → GeneratedFile list]
    B --> C{for each file}
    C --> D[mkdir outDir/dirname]
    D --> E[writeFile dest contents]
    E --> F{f.path === 'index.js'?}
    F -- Yes --> G[chmod dest 0o755]
    G --> H{chmod error?}
    H -- ENOSYS / EPERM / ENOTSUP --> I[swallow silently]
    H -- unexpected error --> I
    H -- success --> C
    I --> C
    F -- No --> C
    C -- done --> J[return GeneratedFile list]
Loading

Comments Outside Diff (1)

  1. packages/openapi/src/gen-mcp/index.ts, line 41-46 (link)

    P2 The chmod guard and the bin entry in package.json both independently hard-code 'index.js'. If a future change renames the entry-point but only updates pkgJson, the chmod will silently skip the new file — the generated package will be broken without any warning. Deriving both from a single constant keeps them in sync automatically.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (1): Last reviewed commit: "fix(openapi): add shebang and chmod +x t..." | Re-trigger Greptile

Comment on lines +32 to +34
if (f.path === 'index.js') {
await chmod(dest, 0o755).catch(() => {});
}
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.

P2 The .catch(() => {}) swallows every possible chmod error, not only the platform-specific ones that are expected (e.g. ENOSYS on Windows, EPERM on a read-only volume). If chmod fails for an unexpected reason after a successful writeFile — for instance a race with another process renaming the file — the generator returns silently, the file is left non-executable, and the caller has no idea. Filtering to known "not applicable" codes preserves the intent while letting real errors bubble.

Suggested change
if (f.path === 'index.js') {
await chmod(dest, 0o755).catch(() => {});
}
if (f.path === 'index.js') {
await chmod(dest, 0o755).catch((e: NodeJS.ErrnoException) => {
// chmod is a no-op on Windows (ENOSYS) and may be refused on
// read-only volumes (EPERM/ENOTSUP). Swallow only those cases
// so that unexpected errors (e.g. the file was removed) still
// surface to the caller.
if (!['ENOSYS', 'EPERM', 'ENOTSUP'].includes(e.code ?? '')) throw e;
});
}

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

8 similar comments
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: git fetch upstream master && git rebase upstream/master.

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.

1 participant