mcp-server-commands 0.3.0 → 0.4.0
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 +14 -0
- package/build/index.js +66 -5
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -74,6 +74,16 @@ On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
|
74
74
|
}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
### Logging
|
|
78
|
+
|
|
79
|
+
Claude Desktop app writes logs to `~/Library/Logs/Claude/mcp-server-mcp-server-commands.log`
|
|
80
|
+
|
|
81
|
+
By default, only important messages are logged (i.e. errors).
|
|
82
|
+
If you want to see more messages, add `--verbose` to the `args` when configuring the server.
|
|
83
|
+
|
|
84
|
+
By the way, logs are written to `STDERR` because that is what Claude Desktop routes to the log files.
|
|
85
|
+
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).
|
|
86
|
+
|
|
77
87
|
### Debugging
|
|
78
88
|
|
|
79
89
|
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:
|
|
@@ -83,3 +93,7 @@ npm run inspector
|
|
|
83
93
|
```
|
|
84
94
|
|
|
85
95
|
The Inspector will provide a URL to access debugging tools in your browser.
|
|
96
|
+
|
|
97
|
+
## TODOs
|
|
98
|
+
|
|
99
|
+
- Add some mechanism (likely in a new MCP server) to retain memory of past command failures, i.e. to use `python3` and not `python` and tie it to a machine or some context?
|
package/build/index.js
CHANGED
|
@@ -5,19 +5,72 @@ 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
|
-
// TODO use .promises?
|
|
8
|
+
// TODO use .promises? in node api
|
|
9
9
|
const execAsync = promisify(exec);
|
|
10
|
+
let verbose = false;
|
|
11
|
+
// check CLI args:
|
|
12
|
+
if (process.argv.includes("--verbose")) {
|
|
13
|
+
verbose = true;
|
|
14
|
+
}
|
|
10
15
|
const server = new Server({
|
|
11
16
|
name: "mcp-server-commands",
|
|
12
|
-
version: "0.
|
|
17
|
+
version: "0.4.0",
|
|
13
18
|
}, {
|
|
14
19
|
capabilities: {
|
|
15
20
|
//resources: {},
|
|
16
21
|
tools: {},
|
|
17
22
|
prompts: {},
|
|
23
|
+
//logging: {}, // for logging messages that don't seem to work yet or I am doing them wrong
|
|
18
24
|
},
|
|
19
25
|
});
|
|
26
|
+
function always_log(message, data) {
|
|
27
|
+
if (data) {
|
|
28
|
+
console.error(message + ": " + JSON.stringify(data));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.error(message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (verbose) {
|
|
35
|
+
always_log("INFO: verbose logging enabled");
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
always_log("INFO: verbose logging disabled, enable it with --verbose");
|
|
39
|
+
}
|
|
40
|
+
function verbose_log(message, data) {
|
|
41
|
+
// https://modelcontextprotocol.io/docs/tools/debugging - mentions various ways to debug/troubleshoot (including dev tools)
|
|
42
|
+
//
|
|
43
|
+
// remember STDIO transport means can't log over STDOUT (client expects JSON messages per the spec)
|
|
44
|
+
// https://modelcontextprotocol.io/docs/tools/debugging#implementing-logging
|
|
45
|
+
// mentions STDERR is captured by the host app (i.e. Claude Desktop app)
|
|
46
|
+
// server.sendLoggingMessage is captured by MCP client (not Claude Desktop app)
|
|
47
|
+
// SO, IIUC use STDERR for logging into Claude Desktop app logs in:
|
|
48
|
+
// '~/Library/Logs/Claude/mcp.log'
|
|
49
|
+
if (verbose) {
|
|
50
|
+
always_log(message, data);
|
|
51
|
+
}
|
|
52
|
+
// inspector, catches these logs and shows them on left hand side of screen (sidebar)
|
|
53
|
+
// TODO add verbose parameter (CLI arg?)
|
|
54
|
+
// IF I wanted to log via MCP client logs (not sure what those are/do):
|
|
55
|
+
// I do not see inspector catching these logs :(, there is a server notifications section and it remains empty
|
|
56
|
+
//server.sendLoggingMessage({
|
|
57
|
+
// level: "info",
|
|
58
|
+
// data: message,
|
|
59
|
+
//});
|
|
60
|
+
// which results in something like:
|
|
61
|
+
//server.notification({
|
|
62
|
+
// method: "notifications/message",
|
|
63
|
+
// params: {
|
|
64
|
+
// level: "warning",
|
|
65
|
+
// logger: "mcp-server-commands",
|
|
66
|
+
// data: "ListToolsRequest2",
|
|
67
|
+
// },
|
|
68
|
+
//});
|
|
69
|
+
//
|
|
70
|
+
// FYI client should also requets a log level from the server, so that needs to be here at some point too
|
|
71
|
+
}
|
|
20
72
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
73
|
+
verbose_log("INFO: ListTools");
|
|
21
74
|
return {
|
|
22
75
|
tools: [
|
|
23
76
|
{
|
|
@@ -74,6 +127,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
74
127
|
};
|
|
75
128
|
});
|
|
76
129
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
130
|
+
verbose_log("INFO: ToolRequest", request);
|
|
77
131
|
switch (request.params.name) {
|
|
78
132
|
case "run_command": {
|
|
79
133
|
return {
|
|
@@ -108,11 +162,13 @@ async function runCommand(args) {
|
|
|
108
162
|
}
|
|
109
163
|
catch (error) {
|
|
110
164
|
// TODO catch for other errors, not just ExecException
|
|
111
|
-
|
|
165
|
+
// 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?
|
|
166
|
+
const response = {
|
|
112
167
|
isError: true,
|
|
113
168
|
content: messagesFor(error),
|
|
114
|
-
//content: [{ type: "text", text: JSON.stringify(error) }],
|
|
115
169
|
};
|
|
170
|
+
always_log("WARN: run_command failed", response);
|
|
171
|
+
return response;
|
|
116
172
|
}
|
|
117
173
|
}
|
|
118
174
|
async function runScript(args) {
|
|
@@ -141,10 +197,12 @@ async function runScript(args) {
|
|
|
141
197
|
};
|
|
142
198
|
}
|
|
143
199
|
catch (error) {
|
|
144
|
-
|
|
200
|
+
const response = {
|
|
145
201
|
isError: true,
|
|
146
202
|
content: messagesFor(error),
|
|
147
203
|
};
|
|
204
|
+
always_log("WARN: run_script failed", response);
|
|
205
|
+
return response;
|
|
148
206
|
}
|
|
149
207
|
}
|
|
150
208
|
function messagesFor(result) {
|
|
@@ -174,6 +232,7 @@ function messagesFor(result) {
|
|
|
174
232
|
return messages;
|
|
175
233
|
}
|
|
176
234
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
235
|
+
verbose_log("INFO: ListPrompts");
|
|
177
236
|
return {
|
|
178
237
|
prompts: [
|
|
179
238
|
{
|
|
@@ -193,6 +252,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
193
252
|
if (request.params.name !== "run_command") {
|
|
194
253
|
throw new Error("Unknown prompt");
|
|
195
254
|
}
|
|
255
|
+
verbose_log("INFO: PromptRequest", request);
|
|
196
256
|
const command = String(request.params.arguments?.command);
|
|
197
257
|
if (!command) {
|
|
198
258
|
throw new Error("Command is required");
|
|
@@ -231,6 +291,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
231
291
|
},
|
|
232
292
|
});
|
|
233
293
|
}
|
|
294
|
+
verbose_log("INFO: PromptResponse", messages);
|
|
234
295
|
return { messages };
|
|
235
296
|
});
|
|
236
297
|
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.0",
|
|
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
|
}
|