mcp-server-commands 0.3.0 → 0.4.2
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/README.md +13 -1
- package/build/index.js +74 -6
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -10,13 +10,15 @@ An MCP server to run commands.
|
|
|
10
10
|
|
|
11
11
|
## Tools
|
|
12
12
|
|
|
13
|
-
Tools are for LLMs to request, i.e. Claude Desktop app
|
|
13
|
+
Tools are for LLMs to request, i.e. Claude Desktop app. Claude Sonnet 3.5 intelligently uses both tools, I was pleasantly surprised.
|
|
14
14
|
|
|
15
15
|
- `run_command` - run a command, i.e. `hostname` or `ls -al` or `echo "hello world"` etc
|
|
16
16
|
- Returns STDOUT and STDERR as text
|
|
17
17
|
- `run_script` - run a script! (i.e. `fish`, `bash`, `zsh`, `python`)
|
|
18
18
|
- Let your LLM run the code it writes!
|
|
19
19
|
- script is passed over STDIN
|
|
20
|
+
- `run_script` == `run_command` + script over STDIN
|
|
21
|
+
- Claude has been pretty creative with this, i.e. using `cat` as the interpreter to create new files!
|
|
20
22
|
|
|
21
23
|
## Prompts
|
|
22
24
|
|
|
@@ -74,6 +76,16 @@ On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
|
74
76
|
}
|
|
75
77
|
```
|
|
76
78
|
|
|
79
|
+
### Logging
|
|
80
|
+
|
|
81
|
+
Claude Desktop app writes logs to `~/Library/Logs/Claude/mcp-server-mcp-server-commands.log`
|
|
82
|
+
|
|
83
|
+
By default, only important messages are logged (i.e. errors).
|
|
84
|
+
If you want to see more messages, add `--verbose` to the `args` when configuring the server.
|
|
85
|
+
|
|
86
|
+
By the way, logs are written to `STDERR` because that is what Claude Desktop routes to the log files.
|
|
87
|
+
In the future, I expect well formatted log messages to be written over the `STDIO` transport to the MCP client (note: not Claude Desktop app).
|
|
88
|
+
|
|
77
89
|
### Debugging
|
|
78
90
|
|
|
79
91
|
Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script:
|
package/build/index.js
CHANGED
|
@@ -5,23 +5,81 @@ import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema
|
|
|
5
5
|
import { exec } from "node:child_process";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { execFileWithInput } from "./exec-utils.js";
|
|
8
|
-
|
|
8
|
+
import { createRequire } from "module";
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const { name: package_name, version: package_version } = require("../package.json");
|
|
11
|
+
// TODO use .promises? in node api
|
|
9
12
|
const execAsync = promisify(exec);
|
|
13
|
+
let verbose = false;
|
|
14
|
+
// check CLI args:
|
|
15
|
+
if (process.argv.includes("--verbose")) {
|
|
16
|
+
verbose = true;
|
|
17
|
+
}
|
|
10
18
|
const server = new Server({
|
|
11
|
-
name:
|
|
12
|
-
version:
|
|
19
|
+
name: package_name,
|
|
20
|
+
version: package_version,
|
|
21
|
+
//description: "Run commands on this " + os.platform() + " machine",
|
|
13
22
|
}, {
|
|
14
23
|
capabilities: {
|
|
15
24
|
//resources: {},
|
|
16
25
|
tools: {},
|
|
17
26
|
prompts: {},
|
|
27
|
+
//logging: {}, // for logging messages that don't seem to work yet or I am doing them wrong
|
|
18
28
|
},
|
|
19
29
|
});
|
|
30
|
+
function always_log(message, data) {
|
|
31
|
+
if (data) {
|
|
32
|
+
console.error(message + ": " + JSON.stringify(data));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.error(message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (verbose) {
|
|
39
|
+
always_log("INFO: verbose logging enabled");
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
always_log("INFO: verbose logging disabled, enable it with --verbose");
|
|
43
|
+
}
|
|
44
|
+
function verbose_log(message, data) {
|
|
45
|
+
// https://modelcontextprotocol.io/docs/tools/debugging - mentions various ways to debug/troubleshoot (including dev tools)
|
|
46
|
+
//
|
|
47
|
+
// remember STDIO transport means can't log over STDOUT (client expects JSON messages per the spec)
|
|
48
|
+
// https://modelcontextprotocol.io/docs/tools/debugging#implementing-logging
|
|
49
|
+
// mentions STDERR is captured by the host app (i.e. Claude Desktop app)
|
|
50
|
+
// server.sendLoggingMessage is captured by MCP client (not Claude Desktop app)
|
|
51
|
+
// SO, IIUC use STDERR for logging into Claude Desktop app logs in:
|
|
52
|
+
// '~/Library/Logs/Claude/mcp.log'
|
|
53
|
+
if (verbose) {
|
|
54
|
+
always_log(message, data);
|
|
55
|
+
}
|
|
56
|
+
// inspector, catches these logs and shows them on left hand side of screen (sidebar)
|
|
57
|
+
// TODO add verbose parameter (CLI arg?)
|
|
58
|
+
// IF I wanted to log via MCP client logs (not sure what those are/do):
|
|
59
|
+
// I do not see inspector catching these logs :(, there is a server notifications section and it remains empty
|
|
60
|
+
//server.sendLoggingMessage({
|
|
61
|
+
// level: "info",
|
|
62
|
+
// data: message,
|
|
63
|
+
//});
|
|
64
|
+
// which results in something like:
|
|
65
|
+
//server.notification({
|
|
66
|
+
// method: "notifications/message",
|
|
67
|
+
// params: {
|
|
68
|
+
// level: "warning",
|
|
69
|
+
// logger: "mcp-server-commands",
|
|
70
|
+
// data: "ListToolsRequest2",
|
|
71
|
+
// },
|
|
72
|
+
//});
|
|
73
|
+
//
|
|
74
|
+
// FYI client should also requets a log level from the server, so that needs to be here at some point too
|
|
75
|
+
}
|
|
20
76
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
77
|
+
verbose_log("INFO: ListTools");
|
|
21
78
|
return {
|
|
22
79
|
tools: [
|
|
23
80
|
{
|
|
24
81
|
name: "run_command",
|
|
82
|
+
//description: "Run a command on this " + os.platform() + " machine",
|
|
25
83
|
inputSchema: {
|
|
26
84
|
type: "object",
|
|
27
85
|
properties: {
|
|
@@ -50,6 +108,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
50
108
|
{
|
|
51
109
|
// TODO is run_script even needed if I were to add STDIN support to run_command above?
|
|
52
110
|
name: "run_script",
|
|
111
|
+
// TODO is it useful to include OS type? I need to test this on a windows machine and see how Claude does w/ and w/o this os hint:
|
|
112
|
+
//description: "Run a script on this " + os.platform() + " machine",
|
|
53
113
|
inputSchema: {
|
|
54
114
|
type: "object",
|
|
55
115
|
properties: {
|
|
@@ -74,6 +134,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
74
134
|
};
|
|
75
135
|
});
|
|
76
136
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
137
|
+
verbose_log("INFO: ToolRequest", request);
|
|
77
138
|
switch (request.params.name) {
|
|
78
139
|
case "run_command": {
|
|
79
140
|
return {
|
|
@@ -108,11 +169,13 @@ async function runCommand(args) {
|
|
|
108
169
|
}
|
|
109
170
|
catch (error) {
|
|
110
171
|
// TODO catch for other errors, not just ExecException
|
|
111
|
-
|
|
172
|
+
// FYI failure may not always be a bad thing if for example checking for a file to exist so just keep that in mind in terms of logging?
|
|
173
|
+
const response = {
|
|
112
174
|
isError: true,
|
|
113
175
|
content: messagesFor(error),
|
|
114
|
-
//content: [{ type: "text", text: JSON.stringify(error) }],
|
|
115
176
|
};
|
|
177
|
+
always_log("WARN: run_command failed", response);
|
|
178
|
+
return response;
|
|
116
179
|
}
|
|
117
180
|
}
|
|
118
181
|
async function runScript(args) {
|
|
@@ -141,10 +204,12 @@ async function runScript(args) {
|
|
|
141
204
|
};
|
|
142
205
|
}
|
|
143
206
|
catch (error) {
|
|
144
|
-
|
|
207
|
+
const response = {
|
|
145
208
|
isError: true,
|
|
146
209
|
content: messagesFor(error),
|
|
147
210
|
};
|
|
211
|
+
always_log("WARN: run_script failed", response);
|
|
212
|
+
return response;
|
|
148
213
|
}
|
|
149
214
|
}
|
|
150
215
|
function messagesFor(result) {
|
|
@@ -174,6 +239,7 @@ function messagesFor(result) {
|
|
|
174
239
|
return messages;
|
|
175
240
|
}
|
|
176
241
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
242
|
+
verbose_log("INFO: ListPrompts");
|
|
177
243
|
return {
|
|
178
244
|
prompts: [
|
|
179
245
|
{
|
|
@@ -193,6 +259,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
193
259
|
if (request.params.name !== "run_command") {
|
|
194
260
|
throw new Error("Unknown prompt");
|
|
195
261
|
}
|
|
262
|
+
verbose_log("INFO: PromptRequest", request);
|
|
196
263
|
const command = String(request.params.arguments?.command);
|
|
197
264
|
if (!command) {
|
|
198
265
|
throw new Error("Command is required");
|
|
@@ -231,6 +298,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
231
298
|
},
|
|
232
299
|
});
|
|
233
300
|
}
|
|
301
|
+
verbose_log("INFO: PromptResponse", messages);
|
|
234
302
|
return { messages };
|
|
235
303
|
});
|
|
236
304
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-server-commands",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "An MCP server to run arbitrary commands",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -15,13 +15,19 @@
|
|
|
15
15
|
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
16
16
|
"prepare": "npm run build",
|
|
17
17
|
"watch": "npm run build && tsc --watch",
|
|
18
|
-
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
|
|
18
|
+
"inspector": "npx @modelcontextprotocol/inspector build/index.js",
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"test:watch": "jest --watch",
|
|
21
|
+
"test:integration": "jest tests/integration"
|
|
19
22
|
},
|
|
20
23
|
"dependencies": {
|
|
21
24
|
"@modelcontextprotocol/sdk": "0.6.0"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
27
|
+
"@types/jest": "^29.5.11",
|
|
24
28
|
"@types/node": "^20.11.24",
|
|
29
|
+
"jest": "^29.7.0",
|
|
30
|
+
"ts-jest": "^29.1.1",
|
|
25
31
|
"typescript": "^5.3.3"
|
|
26
32
|
}
|
|
27
33
|
}
|