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