Skip to content

Commit 99b962d

Browse files
committed
Parse tool args from CLI as JsonValue instead of just strings
1 parent cfc17e5 commit 99b962d

File tree

3 files changed

+79
-15
lines changed

3 files changed

+79
-15
lines changed

cli/src/client/prompts.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
22
import { McpResponse } from "./types.js";
33

4+
// JSON value type matching the client utils
5+
type JsonValue =
6+
| string
7+
| number
8+
| boolean
9+
| null
10+
| undefined
11+
| JsonValue[]
12+
| { [key: string]: JsonValue };
13+
414
// List available prompts
515
export async function listPrompts(client: Client): Promise<McpResponse> {
616
try {
@@ -17,12 +27,26 @@ export async function listPrompts(client: Client): Promise<McpResponse> {
1727
export async function getPrompt(
1828
client: Client,
1929
name: string,
20-
args?: Record<string, string>,
30+
args?: Record<string, JsonValue>,
2131
): Promise<McpResponse> {
2232
try {
33+
// Convert all arguments to strings for prompt arguments
34+
const stringArgs: Record<string, string> = {};
35+
if (args) {
36+
for (const [key, value] of Object.entries(args)) {
37+
if (typeof value === 'string') {
38+
stringArgs[key] = value;
39+
} else if (value === null || value === undefined) {
40+
stringArgs[key] = String(value);
41+
} else {
42+
stringArgs[key] = JSON.stringify(value);
43+
}
44+
}
45+
}
46+
2347
const response = await client.getPrompt({
2448
name,
25-
arguments: args || {},
49+
arguments: stringArgs,
2650
});
2751

2852
return response;

cli/src/client/tools.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
22
import { Tool } from "@modelcontextprotocol/sdk/types.js";
33
import { McpResponse } from "./types.js";
44

5+
// JSON value type matching the client utils
6+
type JsonValue =
7+
| string
8+
| number
9+
| boolean
10+
| null
11+
| undefined
12+
| JsonValue[]
13+
| { [key: string]: JsonValue };
14+
515
type JsonSchemaType = {
616
type: "string" | "number" | "integer" | "boolean" | "array" | "object";
717
description?: string;
@@ -20,7 +30,7 @@ export async function listTools(client: Client): Promise<McpResponse> {
2030
}
2131
}
2232

23-
function convertParameterValue(value: string, schema: JsonSchemaType): unknown {
33+
function convertParameterValue(value: string, schema: JsonSchemaType): JsonValue {
2434
if (!value) {
2535
return value;
2636
}
@@ -35,7 +45,7 @@ function convertParameterValue(value: string, schema: JsonSchemaType): unknown {
3545

3646
if (schema.type === "object" || schema.type === "array") {
3747
try {
38-
return JSON.parse(value);
48+
return JSON.parse(value) as JsonValue;
3949
} catch (error) {
4050
return value;
4151
}
@@ -47,8 +57,8 @@ function convertParameterValue(value: string, schema: JsonSchemaType): unknown {
4757
function convertParameters(
4858
tool: Tool,
4959
params: Record<string, string>,
50-
): Record<string, unknown> {
51-
const result: Record<string, unknown> = {};
60+
): Record<string, JsonValue> {
61+
const result: Record<string, JsonValue> = {};
5262
const properties = tool.inputSchema.properties || {};
5363

5464
for (const [key, value] of Object.entries(params)) {
@@ -68,18 +78,29 @@ function convertParameters(
6878
export async function callTool(
6979
client: Client,
7080
name: string,
71-
args: Record<string, string>,
81+
args: Record<string, JsonValue>,
7282
): Promise<McpResponse> {
7383
try {
7484
const toolsResponse = await listTools(client);
7585
const tools = toolsResponse.tools as Tool[];
7686
const tool = tools.find((t) => t.name === name);
7787

78-
let convertedArgs: Record<string, unknown> = args;
88+
let convertedArgs: Record<string, JsonValue> = args;
7989

8090
if (tool) {
81-
// Convert parameters based on the tool's schema
82-
convertedArgs = convertParameters(tool, args);
91+
// Convert parameters based on the tool's schema, but only for string values
92+
// since we now accept pre-parsed values from the CLI
93+
const stringArgs: Record<string, string> = {};
94+
for (const [key, value] of Object.entries(args)) {
95+
if (typeof value === 'string') {
96+
stringArgs[key] = value;
97+
}
98+
}
99+
100+
if (Object.keys(stringArgs).length > 0) {
101+
const convertedStringArgs = convertParameters(tool, stringArgs);
102+
convertedArgs = { ...args, ...convertedStringArgs };
103+
}
83104
}
84105

85106
const response = await client.callTool({

cli/src/index.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,25 @@ import {
2020
import { handleError } from "./error-handler.js";
2121
import { createTransport, TransportOptions } from "./transport.js";
2222

23+
// JSON value type for CLI arguments
24+
type JsonValue =
25+
| string
26+
| number
27+
| boolean
28+
| null
29+
| undefined
30+
| JsonValue[]
31+
| { [key: string]: JsonValue };
32+
2333
type Args = {
2434
target: string[];
2535
method?: string;
2636
promptName?: string;
27-
promptArgs?: Record<string, string>;
37+
promptArgs?: Record<string, JsonValue>;
2838
uri?: string;
2939
logLevel?: LogLevel;
3040
toolName?: string;
31-
toolArg?: Record<string, string>;
41+
toolArg?: Record<string, JsonValue>;
3242
transport?: "sse" | "stdio" | "http";
3343
};
3444

@@ -162,8 +172,8 @@ async function callMethod(args: Args): Promise<void> {
162172

163173
function parseKeyValuePair(
164174
value: string,
165-
previous: Record<string, string> = {},
166-
): Record<string, string> {
175+
previous: Record<string, JsonValue> = {},
176+
): Record<string, JsonValue> {
167177
const parts = value.split("=");
168178
const key = parts[0];
169179
const val = parts.slice(1).join("=");
@@ -174,7 +184,16 @@ function parseKeyValuePair(
174184
);
175185
}
176186

177-
return { ...previous, [key as string]: val };
187+
// Try to parse as JSON first
188+
let parsedValue: JsonValue;
189+
try {
190+
parsedValue = JSON.parse(val) as JsonValue;
191+
} catch {
192+
// If JSON parsing fails, keep as string
193+
parsedValue = val;
194+
}
195+
196+
return { ...previous, [key as string]: parsedValue };
178197
}
179198

180199
function parseArgs(): Args {

0 commit comments

Comments
 (0)