Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions MANIFEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,24 @@ The `mcp_config` object in the server configuration defines how the implementing
}
```

**Node.js Example (npm + npx):**

For packages published to npm, you can use `npx` to fetch and run the server dynamically instead of bundling `node_modules`:

```json
"mcp_config": {
"command": "npx",
"args": ["-y", "--package=@your-org/your-mcp-server", "your-mcp-server"],
"env": {
"API_KEY": "${user_config.api_key}"
}
}
```

This pattern creates smaller bundles (< 50 KB vs 50-300 MB) and automatically stays up-to-date with npm releases. The `-y` flag auto-confirms installation, and `--package=<pkg> <bin>` explicitly specifies the package when it has multiple bin entries. See `examples/npx-node/` for a complete example.

**Note:** Claude Desktop ships with Node.js/npm/npx built-in, so this works out-of-the-box. Users need internet access on first launch.

**Binary Example (Cross-Platform):**

```json
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,38 @@ bundle.mcpb (ZIP file)
- Use `npm ci` or `yarn install --frozen-lockfile` for reproducible builds
- Server entry point specified in manifest.json's `server.entry_point`

**Node.js Bundles (npm + npx):**

For packages published to npm, you can use `npx` to dynamically fetch and run the package instead of bundling `node_modules`. This creates significantly smaller bundles that automatically stay up-to-date with npm releases.

Requirements:
- Package must be published to npm with a `bin` entry in `package.json`
- Claude for macOS and Windows ships with Node.js/npm/npx built-in

Manifest configuration:
```json
"mcp_config": {
"command": "npx",
"args": ["-y", "--package=@your-org/your-mcp-server", "your-mcp-server"],
"env": {}
}
```

Trade-offs:

| Aspect | Traditional Bundling | npm + npx |
|--------|---------------------|-----------|
| Bundle size | 50-300 MB | < 50 KB |
| First launch | Instant | 30-60s (download) |
| Updates | Manual rebuild | Automatic from npm |
| Offline support | Always works | After first run |

When to use:
- **Use npx** when your package is published to npm and you want automatic updates
- **Use bundling** when your package is private, unpublished, or requires offline-first support

See `examples/npx-node/` for a complete working example.

**Binary Bundles:**

- Static linking preferred for maximum compatibility
Expand Down
34 changes: 34 additions & 0 deletions examples/npx-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# npm + npx Node.js Example

This example demonstrates using `npx` to run an npm-published MCP server instead of bundling dependencies.

## Benefits

- **Tiny bundles**: ~10 KB instead of 50-300 MB
- **Automatic updates**: Users always get the latest npm version
- **Zero maintenance**: No need to rebuild bundles for updates

## How It Works

The manifest uses `npx` as the command:

```json
"mcp_config": {
"command": "npx",
"args": ["-y", "--package=example-npx-mcp", "example-npx-mcp"]
}
```

On first launch, npx downloads the package from npm. Subsequent launches use the cached version.

## Requirements

- Package must be published to npm with a `bin` entry
- Claude for macOS and Windows (ships with npx built-in)

## Testing

```bash
mcpb validate manifest.json
mcpb pack .
```
46 changes: 46 additions & 0 deletions examples/npx-node/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"manifest_version": "0.3",
"name": "example-npx-mcp",
"display_name": "Example npx MCP Server",
"version": "1.0.0",
"description": "Demonstrates npm+npx deployment pattern for Node.js MCP servers",
"author": {
"name": "MCP Team"
},
"repository": {
"type": "git",
"url": "https://github.com/modelcontextprotocol/mcpb.git"
},
"server": {
"type": "node",
"entry_point": "dist/index.js",
"mcp_config": {
"command": "npx",
"args": ["-y", "--package=example-npx-mcp", "example-npx-mcp"],
"env": {
"API_KEY": "${user_config.api_key}"
}
}
},
"tools": [
{
"name": "hello",
"description": "Returns a greeting message"
}
],
"user_config": {
"api_key": {
"type": "string",
"title": "API Key",
"description": "Optional API key for demonstration",
"sensitive": true,
"required": false
}
},
"compatibility": {
"platforms": ["darwin", "win32", "linux"],
"runtimes": {
"node": ">=18.0.0"
}
}
}
20 changes: 20 additions & 0 deletions examples/npx-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "example-npx-mcp",
"version": "1.0.0",
"description": "Example MCP server demonstrating npx deployment pattern",
"type": "module",
"main": "dist/index.js",
"bin": {
"example-npx-mcp": "dist/index.js"
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
26 changes: 26 additions & 0 deletions examples/npx-node/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
name: "example-npx-mcp",
version: "1.0.0"
});

server.tool(
"hello",
"Returns a greeting message",
{
name: {
type: "string",
description: "Name to greet"
}
},
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name || "World"}!` }]
})
);

const transport = new StdioServerTransport();
await server.connect(transport);
11 changes: 11 additions & 0 deletions examples/npx-node/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}