mcp-caller 0.0.3 → 0.0.5

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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/index.js +120 -14
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -48,6 +48,24 @@ Read JSON from files instead of inline values:
48
48
  npx -y mcp-caller --server-file server.json --call-file call.json
49
49
  ```
50
50
 
51
+ Short call form:
52
+
53
+ ```bash
54
+ npx -y mcp-caller --server-file server.json --tool say_hello --params-json '{"name":"world"}'
55
+ ```
56
+
57
+ List callable tools:
58
+
59
+ ```bash
60
+ npx -y mcp-caller --server-file server.json --list-tools
61
+ ```
62
+
63
+ Inspect tools, prompts, and resources:
64
+
65
+ ```bash
66
+ npx -y mcp-caller --server-file server.json --inspect --pretty
67
+ ```
68
+
51
69
  ## JSON Shapes
52
70
 
53
71
  Server JSON:
package/index.js CHANGED
@@ -7,6 +7,9 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
7
7
 
8
8
  function printUsage() {
9
9
  console.error(`Usage:
10
+ mcp-caller --server-json <json> --list-tools
11
+ mcp-caller --server-json <json> --inspect
12
+ mcp-caller --server-json <json> --tool <name> [--params-json <json>]
10
13
  mcp-caller --server-json <json> --call-json <json>
11
14
  mcp-caller --server-file <path> --call-file <path>
12
15
 
@@ -57,6 +60,16 @@ function readSource(options, jsonKey, fileKey, label) {
57
60
  throw new Error(`Missing ${label}`);
58
61
  }
59
62
 
63
+ function readParams(options) {
64
+ if (options.paramsFile) {
65
+ return parseJson(readFileSync(options.paramsFile, "utf8"), "--params-json/--params-file");
66
+ }
67
+ if (options.paramsJson) {
68
+ return parseJson(options.paramsJson, "--params-json/--params-file");
69
+ }
70
+ return {};
71
+ }
72
+
60
73
  function parseArgs(argv) {
61
74
  const options = {};
62
75
  for (let i = 0; i < argv.length; i += 1) {
@@ -81,6 +94,26 @@ function parseArgs(argv) {
81
94
  options.callFile = argv[++i];
82
95
  continue;
83
96
  }
97
+ if (token === "--tool") {
98
+ options.tool = argv[++i];
99
+ continue;
100
+ }
101
+ if (token === "--params-json") {
102
+ options.paramsJson = argv[++i];
103
+ continue;
104
+ }
105
+ if (token === "--params-file") {
106
+ options.paramsFile = argv[++i];
107
+ continue;
108
+ }
109
+ if (token === "--list-tools") {
110
+ options.listTools = true;
111
+ continue;
112
+ }
113
+ if (token === "--inspect") {
114
+ options.inspect = true;
115
+ continue;
116
+ }
84
117
  if (token === "--server-name") {
85
118
  options.serverName = argv[++i];
86
119
  continue;
@@ -153,6 +186,31 @@ function extractText(result) {
153
186
  return pieces.join("\n");
154
187
  }
155
188
 
189
+ function printTools(tools) {
190
+ for (const tool of tools) {
191
+ console.log(`- ${tool.name}${tool.description ? `: ${tool.description}` : ""}`);
192
+ }
193
+ }
194
+
195
+ function formatInspect(serverName, tools, prompts, resources) {
196
+ return {
197
+ server: serverName,
198
+ tools: tools.map((tool) => ({
199
+ name: tool.name,
200
+ description: tool.description ?? ""
201
+ })),
202
+ prompts: prompts.map((prompt) => ({
203
+ name: prompt.name,
204
+ description: prompt.description ?? ""
205
+ })),
206
+ resources: resources.map((resource) => ({
207
+ uri: resource.uri,
208
+ name: resource.name ?? "",
209
+ description: resource.description ?? ""
210
+ }))
211
+ };
212
+ }
213
+
156
214
  async function main() {
157
215
  const options = parseArgs(process.argv.slice(2));
158
216
  if (options.help) {
@@ -161,13 +219,30 @@ async function main() {
161
219
  }
162
220
 
163
221
  const serverSource = readSource(options, "serverJson", "serverFile", "--server-json/--server-file");
164
- const callSource = readSource(options, "callJson", "callFile", "--call-json/--call-file");
165
-
166
- if (!callSource || typeof callSource !== "object") {
167
- throw new Error("Call JSON must be an object");
222
+ if (options.tool && (options.callJson || options.callFile)) {
223
+ throw new Error("Use either --tool/--params-json or --call-json/--call-file, not both");
168
224
  }
169
- if (typeof callSource.tool !== "string" || !callSource.tool) {
170
- throw new Error('Call JSON must include a non-empty "tool" string');
225
+ if (options.tool && options.tool.trim().length === 0) {
226
+ throw new Error('Option "--tool" must be a non-empty string');
227
+ }
228
+
229
+ const shorthandCall = options.tool
230
+ ? {
231
+ tool: options.tool,
232
+ params: readParams(options)
233
+ }
234
+ : null;
235
+ const callSource = options.listTools || options.inspect
236
+ ? null
237
+ : shorthandCall ?? readSource(options, "callJson", "callFile", "--call-json/--call-file");
238
+
239
+ if (callSource) {
240
+ if (typeof callSource !== "object") {
241
+ throw new Error("Call JSON must be an object");
242
+ }
243
+ if (typeof callSource.tool !== "string" || !callSource.tool) {
244
+ throw new Error('Call JSON must include a non-empty "tool" string');
245
+ }
171
246
  }
172
247
 
173
248
  const { server } = resolveServerDefinition(serverSource, options.serverName);
@@ -177,18 +252,49 @@ async function main() {
177
252
  try {
178
253
  await client.connect(transport);
179
254
 
180
- const result = await client.callTool({
181
- name: callSource.tool,
182
- arguments: callSource.params ?? callSource.arguments ?? {}
183
- });
255
+ if (options.listTools || options.inspect) {
256
+ const [toolsResult, promptsResult, resourcesResult] = await Promise.all([
257
+ client.listTools(),
258
+ options.inspect ? client.listPrompts() : Promise.resolve({ prompts: [] }),
259
+ options.inspect ? client.listResources() : Promise.resolve({ resources: [] })
260
+ ]);
184
261
 
185
- const text = extractText(result);
186
- if (text) {
187
- console.log(text);
262
+ if (options.listTools) {
263
+ printTools(toolsResult.tools ?? []);
264
+ return;
265
+ }
266
+
267
+ console.log(JSON.stringify(
268
+ formatInspect(
269
+ options.serverName ?? (serverSource?.mcpServers ? Object.keys(serverSource.mcpServers)[0] : "server"),
270
+ toolsResult.tools ?? [],
271
+ promptsResult.prompts ?? [],
272
+ resourcesResult.resources ?? []
273
+ ),
274
+ null,
275
+ options.pretty ? 2 : 0
276
+ ));
188
277
  return;
189
278
  }
190
279
 
191
- console.log(JSON.stringify(result, null, options.pretty ? 2 : 0));
280
+ if (!options.listTools && !options.inspect && !callSource) {
281
+ throw new Error("Missing --call-json/--call-file, or use --list-tools / --inspect");
282
+ }
283
+
284
+ if (callSource) {
285
+ const result = await client.callTool({
286
+ name: callSource.tool,
287
+ arguments: callSource.params ?? callSource.arguments ?? {}
288
+ });
289
+
290
+ const text = extractText(result);
291
+ if (text) {
292
+ console.log(text);
293
+ return;
294
+ }
295
+
296
+ console.log(JSON.stringify(result, null, options.pretty ? 2 : 0));
297
+ }
192
298
  } finally {
193
299
  await client.close().catch(() => {});
194
300
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-caller",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "A small MCP client CLI that can start a server and call a tool from JSON input.",
5
5
  "type": "module",
6
6
  "main": "index.js",