Turning a web tool into a zero-dependency MCP server
I run DomainIntel. It is a small web app. I wanted to turn it into a Model Context Protocol (MCP) server.
I wanted users to run one command: npx -y @domainintel/mcp.
This means zero friction. No cloning repos. No long install times. The server must work as a single file with no runtime dependencies.
Here is how I built it using esbuild.
The Challenge: Packaging
I used esbuild to bundle everything. I had to solve several technical hurdles:
• Static Imports: esbuild cannot follow runtime require calls. I switched from createRequire to standard ESM imports. This allowed esbuild to follow the dependency graph.
• CommonJS Compatibility: One dependency used require('net') internally. I added a banner in the esbuild config. This banner injects createRequire so the shim works for built-in modules.
• Logger Conflicts: MCP uses stdout for communication. My existing logger tried to write to a local directory. This fails in a global CLI. I used an onResolve plugin to stub the logger.
• Double Shebangs: My entry file and my banner both had shebangs. This created an invalid file. I removed the shebang from the source code.
• Module Types: I removed "type": "module" from the package.json. This stopped esbuild from treating CommonJS files as ESM. The .mjs entry file stays ESM by extension.
• Version Pinning: A dependency switched to ESM and broke my build. I pinned the version to keep the bundle reproducible.
The Result
The final file is mcp/dist/server.mjs. It is about 1.7 MB. It has zero runtime dependencies.
You can add it to Claude with: claude mcp add domainintel -- npx -y @domainintel/mcp
Now your agent can run "give me a full report on stripe.com" and get instant data.
If you have a tool with a core logic, wrap it as an MCP server. It makes your tool useful for AI agents.
Source: https://dev.to/chris_morris/turning-a-web-tool-into-a-zero-dependency-mcp-server-21ca
Optional learning community: https://t.me/GyaanSetuAi
