npm-run-mcp-server 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +308 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +197 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# npm-run-mcp-server
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
*A Model Context Protocol (MCP) server that exposes your project's `package.json` scripts as tools for AI agents.*
|
|
6
|
+
|
|
7
|
+
[](https://github.com/fstubner/npm-run-mcp-server/actions/workflows/test.yml)
|
|
8
|
+
[](https://github.com/fstubner/npm-run-mcp-server/actions/workflows/build-and-publish.yml)
|
|
9
|
+
[](https://www.npmjs.com/package/npm-run-mcp-server)
|
|
10
|
+
[](https://www.npmjs.com/package/npm-run-mcp-server)
|
|
11
|
+
[](https://opensource.org/licenses/MIT)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
|
|
19
|
+
- [Install](#install)
|
|
20
|
+
- [Usage](#usage)
|
|
21
|
+
- [Configuration](#configuration)
|
|
22
|
+
- [GitHub Copilot Chat (VS Code)](#github-copilot-chat-vs-code)
|
|
23
|
+
- [Claude Code (VS Code extension)](#claude-code-vs-code-extension)
|
|
24
|
+
- [Claude Code (terminal / standalone)](#claude-code-terminal--standalone)
|
|
25
|
+
- [Cursor](#cursor)
|
|
26
|
+
- [Install from source](#install-from-source)
|
|
27
|
+
- [Testing with MCP Inspector](#testing-with-mcp-inspector)
|
|
28
|
+
- [CLI Options](#cli-options)
|
|
29
|
+
- [Contributing](#contributing)
|
|
30
|
+
- [License](#license)
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm i -D npm-run-mcp-server
|
|
36
|
+
# or globally
|
|
37
|
+
npm i -g npm-run-mcp-server
|
|
38
|
+
# ad-hoc
|
|
39
|
+
npx npm-run-mcp-server
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### As an MCP Server
|
|
45
|
+
|
|
46
|
+
Add this server to your MCP host configuration. It uses stdio and dynamically exposes each script from the closest `package.json` (walking up from `process.cwd()`).
|
|
47
|
+
|
|
48
|
+
The tool names match your script names. Each tool accepts an optional `args` string that is appended after `--` when running the script. The server detects your package manager (npm, pnpm, yarn, bun).
|
|
49
|
+
|
|
50
|
+
### As a CLI Tool
|
|
51
|
+
|
|
52
|
+
You can also use this package directly from the command line:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# List available scripts in current directory
|
|
56
|
+
npx npm-run-mcp-server --list-scripts
|
|
57
|
+
|
|
58
|
+
# Run with verbose output
|
|
59
|
+
npx npm-run-mcp-server --verbose
|
|
60
|
+
|
|
61
|
+
# Specify a different working directory
|
|
62
|
+
npx npm-run-mcp-server --cwd /path/to/project --list-scripts
|
|
63
|
+
|
|
64
|
+
# Override package manager detection
|
|
65
|
+
npx npm-run-mcp-server --pm yarn --list-scripts
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
### Install in GitHub Copilot Chat (VS Code)
|
|
71
|
+
|
|
72
|
+
Option A — per-workspace via `.vscode/mcp.json` (recommended for multi-project use):
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"servers": {
|
|
77
|
+
"npm-scripts": {
|
|
78
|
+
"command": "npx",
|
|
79
|
+
"args": ["-y", "npm-run-mcp-server"]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Option B — user settings (`settings.json`):
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"mcp.servers": {
|
|
90
|
+
"npm-scripts": {
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["-y", "npm-run-mcp-server"]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Note**: The server automatically detects the current project's `package.json` using `process.cwd()`, so no hardcoded paths are needed. It works seamlessly across all your projects.
|
|
99
|
+
|
|
100
|
+
Then open Copilot Chat, switch to Agent mode, and start the `npm-scripts` server from the tools panel.
|
|
101
|
+
|
|
102
|
+
### Multi-Project Workflow
|
|
103
|
+
|
|
104
|
+
The MCP server is designed to work seamlessly across multiple projects without configuration changes:
|
|
105
|
+
|
|
106
|
+
- **VS Code/Cursor**: Use workspace settings (`.vscode/mcp.json` or `.vscode/settings.json`) - the server automatically targets the current project
|
|
107
|
+
- **Claude Desktop**: The server uses the working directory where Claude is launched
|
|
108
|
+
- **No Hardcoded Paths**: All examples use `npx npm-run-mcp-server` without `--cwd` flags
|
|
109
|
+
- **Automatic Detection**: The server walks up the directory tree to find the nearest `package.json`
|
|
110
|
+
|
|
111
|
+
This means you can use the same MCP configuration across all your projects, and the server will automatically target the correct project based on where your MCP client is running.
|
|
112
|
+
|
|
113
|
+
### Install in Claude Code (VS Code extension)
|
|
114
|
+
|
|
115
|
+
Add to VS Code user/workspace settings (`settings.json`):
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"claude.mcpServers": {
|
|
120
|
+
"npm-scripts": {
|
|
121
|
+
"command": "npx",
|
|
122
|
+
"args": ["-y", "npm-run-mcp-server"]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Note**: Workspace settings (`.vscode/settings.json`) are recommended for multi-project use, as they automatically target the current project.
|
|
129
|
+
|
|
130
|
+
Restart the extension and confirm the server/tools appear.
|
|
131
|
+
|
|
132
|
+
### Install in Claude Code (terminal / standalone)
|
|
133
|
+
|
|
134
|
+
Add this server to Claude's global config file (paths vary by OS). Create the file if it doesn't exist.
|
|
135
|
+
|
|
136
|
+
- Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
137
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
138
|
+
- Linux: `~/.config/Claude/claude_desktop_config.json`
|
|
139
|
+
|
|
140
|
+
**Recommended approach** - Using npx (works across all projects):
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"mcpServers": {
|
|
145
|
+
"npm-scripts": {
|
|
146
|
+
"command": "npx",
|
|
147
|
+
"args": ["-y", "npm-run-mcp-server"]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Alternative** - Using a local build (requires absolute path):
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"mcpServers": {
|
|
158
|
+
"npm-scripts": {
|
|
159
|
+
"command": "node",
|
|
160
|
+
"args": ["/absolute/path/to/npm-run-mcp-server/dist/index.js"]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Note**: The npx approach is recommended as it automatically targets the current working directory where Claude is launched.
|
|
167
|
+
|
|
168
|
+
Optional: include environment variables
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"mcpServers": {
|
|
173
|
+
"npm-scripts": {
|
|
174
|
+
"command": "npx",
|
|
175
|
+
"args": ["-y", "npm-run-mcp-server"],
|
|
176
|
+
"env": {
|
|
177
|
+
"NODE_ENV": "production"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Restart Claude after editing the config so it picks up the new server.
|
|
185
|
+
|
|
186
|
+
### Install in Cursor
|
|
187
|
+
|
|
188
|
+
- Open Settings → MCP Servers → Add MCP Server
|
|
189
|
+
- Type: NPX Package
|
|
190
|
+
- Command: `npx`
|
|
191
|
+
- Arguments: `-y npm-run-mcp-server`
|
|
192
|
+
- Save and start the server from the tools list
|
|
193
|
+
|
|
194
|
+
**Note**: This configuration automatically works across all your projects. The server will target the current project's `package.json` wherever Cursor is opened.
|
|
195
|
+
|
|
196
|
+
### Install from source (for testing in another project)
|
|
197
|
+
|
|
198
|
+
Clone, build, and link globally:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
git clone https://github.com/your-org-or-user/npm-run-mcp-server.git
|
|
202
|
+
cd npm-run-mcp-server
|
|
203
|
+
npm install
|
|
204
|
+
npm run build
|
|
205
|
+
npm link
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
In your other project, either reference the global binary or the built file directly:
|
|
209
|
+
|
|
210
|
+
- Using the linked binary:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"servers": {
|
|
215
|
+
"npm-scripts": {
|
|
216
|
+
"command": "npm-run-mcp-server"
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- Using an explicit Node command (no global link needed):
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"servers": {
|
|
227
|
+
"npm-scripts": {
|
|
228
|
+
"command": "node",
|
|
229
|
+
"args": ["/absolute/path/to/npm-run-mcp-server/dist/index.js"]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Optional CLI flags you can pass in `args`:
|
|
236
|
+
- `--cwd /path/to/project` to choose which project to read `package.json` from (rarely needed - server auto-detects by default)
|
|
237
|
+
- `--pm npm|pnpm|yarn|bun` to override package manager detection
|
|
238
|
+
|
|
239
|
+
## Testing with MCP Inspector
|
|
240
|
+
|
|
241
|
+
Test the server locally before integrating with AI agents:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Start MCP Inspector
|
|
245
|
+
npx @modelcontextprotocol/inspector
|
|
246
|
+
|
|
247
|
+
# In the Inspector UI:
|
|
248
|
+
# 1. Transport Type: STDIO
|
|
249
|
+
# 2. Command: npx
|
|
250
|
+
# 3. Arguments: npm-run-mcp-server --cwd /path/to/your/project --verbose
|
|
251
|
+
# 4. Click "Connect"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
You should see your package.json scripts listed as available tools. Try running one - it executes the script and returns the output.
|
|
255
|
+
|
|
256
|
+
## CLI Options
|
|
257
|
+
|
|
258
|
+
Available command-line flags:
|
|
259
|
+
|
|
260
|
+
- `--cwd <path>` - Specify working directory (defaults to current directory)
|
|
261
|
+
- `--pm <manager>` - Override package manager detection (npm|pnpm|yarn|bun)
|
|
262
|
+
- `--verbose` - Enable detailed logging to stderr
|
|
263
|
+
- `--list-scripts` - List available scripts and exit
|
|
264
|
+
|
|
265
|
+
## Contributing
|
|
266
|
+
|
|
267
|
+
We welcome contributions! Here's how you can help:
|
|
268
|
+
|
|
269
|
+
### Reporting Issues
|
|
270
|
+
|
|
271
|
+
- Use the [issue tracker](https://github.com/fstubner/npm-run-mcp-server/issues) to report bugs
|
|
272
|
+
- Include your Node.js version, package manager, and operating system
|
|
273
|
+
- Provide a minimal reproduction case when possible
|
|
274
|
+
|
|
275
|
+
### Submitting Changes
|
|
276
|
+
|
|
277
|
+
1. **Fork** the repository
|
|
278
|
+
2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
|
|
279
|
+
3. **Make** your changes and add tests if applicable
|
|
280
|
+
4. **Test** your changes: `npm run build && npm run test`
|
|
281
|
+
5. **Commit** your changes: `git commit -m 'Add amazing feature'`
|
|
282
|
+
6. **Push** to the branch: `git push origin feature/amazing-feature`
|
|
283
|
+
7. **Submit** a pull request
|
|
284
|
+
|
|
285
|
+
### Development Setup
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
git clone https://github.com/fstubner/npm-run-mcp-server.git
|
|
289
|
+
cd npm-run-mcp-server
|
|
290
|
+
npm install
|
|
291
|
+
npm run build
|
|
292
|
+
npm run test
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The project uses a custom build script located in `scripts/build.cjs` that handles TypeScript compilation and shebang injection for the executable.
|
|
296
|
+
|
|
297
|
+
### Guidelines
|
|
298
|
+
|
|
299
|
+
- Follow the existing code style
|
|
300
|
+
- Add tests for new features
|
|
301
|
+
- Update documentation as needed
|
|
302
|
+
- Keep commits focused and descriptive
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT
|
|
307
|
+
|
|
308
|
+
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync, existsSync } from 'fs';
|
|
3
|
+
import { promises as fsp } from 'fs';
|
|
4
|
+
import { dirname, resolve } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { exec as nodeExec } from 'child_process';
|
|
7
|
+
import { promisify } from 'util';
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
const exec = promisify(nodeExec);
|
|
11
|
+
function parseCliArgs(argv) {
|
|
12
|
+
const args = {};
|
|
13
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
14
|
+
const token = argv[i];
|
|
15
|
+
if (!token)
|
|
16
|
+
continue;
|
|
17
|
+
if (token.startsWith('--')) {
|
|
18
|
+
const key = token.slice(2);
|
|
19
|
+
const next = argv[i + 1];
|
|
20
|
+
if (next && !next.startsWith('--')) {
|
|
21
|
+
args[key] = next;
|
|
22
|
+
i += 1;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
args[key] = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return args;
|
|
30
|
+
}
|
|
31
|
+
async function findNearestPackageJson(startDir) {
|
|
32
|
+
let current = resolve(startDir);
|
|
33
|
+
while (true) {
|
|
34
|
+
const candidate = resolve(current, 'package.json');
|
|
35
|
+
if (existsSync(candidate))
|
|
36
|
+
return candidate;
|
|
37
|
+
const parent = dirname(current);
|
|
38
|
+
if (parent === current)
|
|
39
|
+
break;
|
|
40
|
+
current = parent;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
async function readPackageJson(pathToPackageJson) {
|
|
45
|
+
const raw = await fsp.readFile(pathToPackageJson, 'utf8');
|
|
46
|
+
return JSON.parse(raw);
|
|
47
|
+
}
|
|
48
|
+
function detectPackageManager(projectDir, pkg, override) {
|
|
49
|
+
if (override)
|
|
50
|
+
return override;
|
|
51
|
+
// Prefer explicit packageManager field if present
|
|
52
|
+
if (pkg.packageManager) {
|
|
53
|
+
const pm = pkg.packageManager.split('@')[0];
|
|
54
|
+
if (pm === 'npm' || pm === 'pnpm' || pm === 'yarn' || pm === 'bun')
|
|
55
|
+
return pm;
|
|
56
|
+
}
|
|
57
|
+
// Lockfile heuristic
|
|
58
|
+
if (existsSync(resolve(projectDir, 'pnpm-lock.yaml')))
|
|
59
|
+
return 'pnpm';
|
|
60
|
+
if (existsSync(resolve(projectDir, 'yarn.lock')))
|
|
61
|
+
return 'yarn';
|
|
62
|
+
if (existsSync(resolve(projectDir, 'bun.lockb')) || existsSync(resolve(projectDir, 'bun.lock')))
|
|
63
|
+
return 'bun';
|
|
64
|
+
return 'npm';
|
|
65
|
+
}
|
|
66
|
+
function buildRunCommand(pm, scriptName, extraArgs) {
|
|
67
|
+
const quoted = scriptName.replace(/"/g, '\\"');
|
|
68
|
+
const suffix = extraArgs && extraArgs.trim().length > 0 ? ` -- ${extraArgs}` : '';
|
|
69
|
+
switch (pm) {
|
|
70
|
+
case 'pnpm':
|
|
71
|
+
return `pnpm run "${quoted}"${suffix}`;
|
|
72
|
+
case 'yarn':
|
|
73
|
+
return `yarn run "${quoted}"${suffix}`;
|
|
74
|
+
case 'bun':
|
|
75
|
+
return `bun run "${quoted}"${suffix}`;
|
|
76
|
+
case 'npm':
|
|
77
|
+
default:
|
|
78
|
+
return `npm run "${quoted}"${suffix}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function trimOutput(out, limit = 12000) {
|
|
82
|
+
if (out.length <= limit)
|
|
83
|
+
return { text: out, truncated: false };
|
|
84
|
+
return { text: out.slice(0, limit) + `\n...[truncated ${out.length - limit} chars]`, truncated: true };
|
|
85
|
+
}
|
|
86
|
+
async function main() {
|
|
87
|
+
const args = parseCliArgs(process.argv);
|
|
88
|
+
const startCwd = args.cwd ? resolve(String(args.cwd)) : process.cwd();
|
|
89
|
+
const pkgJsonPath = await findNearestPackageJson(startCwd);
|
|
90
|
+
if (!pkgJsonPath) {
|
|
91
|
+
console.error(`npm-run-mcp-server: No package.json found starting from ${startCwd}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const projectDir = dirname(pkgJsonPath);
|
|
95
|
+
const projectPkg = await readPackageJson(pkgJsonPath);
|
|
96
|
+
const verbose = Boolean(args.verbose ||
|
|
97
|
+
process.env.MCP_VERBOSE ||
|
|
98
|
+
(process.env.DEBUG && process.env.DEBUG.toLowerCase().includes('mcp')));
|
|
99
|
+
if (verbose) {
|
|
100
|
+
console.error(`[mcp] server starting: cwd=${startCwd}`);
|
|
101
|
+
console.error(`[mcp] using package.json: ${pkgJsonPath}`);
|
|
102
|
+
}
|
|
103
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
104
|
+
const __dirname = dirname(__filename);
|
|
105
|
+
const selfPkgPath = resolve(__dirname, '..', 'package.json');
|
|
106
|
+
let serverName = 'npm-run-mcp-server';
|
|
107
|
+
let serverVersion = '0.0.0';
|
|
108
|
+
try {
|
|
109
|
+
if (existsSync(selfPkgPath)) {
|
|
110
|
+
const selfPkg = JSON.parse(readFileSync(selfPkgPath, 'utf8'));
|
|
111
|
+
if (selfPkg.name)
|
|
112
|
+
serverName = selfPkg.name;
|
|
113
|
+
if (selfPkg.version)
|
|
114
|
+
serverVersion = selfPkg.version;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
const pm = detectPackageManager(projectDir, projectPkg, args.pm);
|
|
119
|
+
if (verbose) {
|
|
120
|
+
console.error(`[mcp] detected package manager: ${pm}`);
|
|
121
|
+
}
|
|
122
|
+
const server = new McpServer({ name: serverName, version: serverVersion });
|
|
123
|
+
const scripts = projectPkg.scripts ?? {};
|
|
124
|
+
const scriptNames = Object.keys(scripts);
|
|
125
|
+
if (scriptNames.length === 0) {
|
|
126
|
+
console.error(`npm-run-mcp-server: No scripts found in ${pkgJsonPath}`);
|
|
127
|
+
}
|
|
128
|
+
if (args['list-scripts']) {
|
|
129
|
+
for (const name of scriptNames) {
|
|
130
|
+
console.error(`${name}: ${scripts[name]}`);
|
|
131
|
+
}
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
// Register a tool per script
|
|
135
|
+
for (const scriptName of scriptNames) {
|
|
136
|
+
server.tool(scriptName, {
|
|
137
|
+
description: `Run package script '${scriptName}' via ${pm} in ${projectDir}`,
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
properties: {
|
|
141
|
+
args: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
description: 'Optional arguments appended after -- to the script'
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
}, async ({ args: extraArgs }) => {
|
|
148
|
+
const command = buildRunCommand(pm, scriptName, extraArgs);
|
|
149
|
+
try {
|
|
150
|
+
const { stdout, stderr } = await exec(command, {
|
|
151
|
+
cwd: projectDir,
|
|
152
|
+
env: process.env,
|
|
153
|
+
maxBuffer: 16 * 1024 * 1024, // 16MB
|
|
154
|
+
windowsHide: true,
|
|
155
|
+
});
|
|
156
|
+
const combined = stdout && stderr ? `${stdout}\n${stderr}` : stdout || stderr || '';
|
|
157
|
+
const { text } = trimOutput(combined);
|
|
158
|
+
return {
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: 'text',
|
|
162
|
+
text,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
const stdout = error?.stdout ?? '';
|
|
169
|
+
const stderr = error?.stderr ?? '';
|
|
170
|
+
const message = error?.message ? String(error.message) : 'Script failed';
|
|
171
|
+
const combined = [message, stdout, stderr].filter(Boolean).join('\n');
|
|
172
|
+
const { text } = trimOutput(combined);
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: 'text',
|
|
177
|
+
text,
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const transport = new StdioServerTransport();
|
|
185
|
+
if (verbose) {
|
|
186
|
+
console.error(`[mcp] registered ${scriptNames.length} tools; awaiting stdio client...`);
|
|
187
|
+
}
|
|
188
|
+
await server.connect(transport);
|
|
189
|
+
if (verbose) {
|
|
190
|
+
console.error(`[mcp] stdio transport connected (waiting for initialize)`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Run
|
|
194
|
+
main().catch((err) => {
|
|
195
|
+
console.error(err);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "npm-run-mcp-server",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "An MCP server that exposes package.json scripts as tools for agents.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"npm-run-mcp-server": "dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/index.js",
|
|
14
|
+
"dist/index.d.ts"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node scripts/build.cjs",
|
|
18
|
+
"start": "node ./dist/index.js",
|
|
19
|
+
"test": "node dist/index.js --list-scripts && echo 'MCP server test completed successfully'",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18.18.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
27
|
+
"zod": "^3.23.8"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^5.4.0",
|
|
31
|
+
"@types/node": "^20.14.9"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"mcp",
|
|
35
|
+
"model-context-protocol",
|
|
36
|
+
"agent",
|
|
37
|
+
"npm",
|
|
38
|
+
"scripts",
|
|
39
|
+
"ai",
|
|
40
|
+
"claude",
|
|
41
|
+
"cursor",
|
|
42
|
+
"copilot",
|
|
43
|
+
"automation"
|
|
44
|
+
],
|
|
45
|
+
"author": "fstubner",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/fstubner/npm-run-mcp-server.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/fstubner/npm-run-mcp-server/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/fstubner/npm-run-mcp-server#readme",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public",
|
|
57
|
+
"registry": "https://registry.npmjs.org"
|
|
58
|
+
}
|
|
59
|
+
}
|