Skip to content

refactor: improve CLI type safety and log level consistency #565

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions cli/src/client/connection.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
import { McpResponse } from "./types.js";
import type {
LoggingLevel,
EmptyResult,
} from "@modelcontextprotocol/sdk/types.js";
import { LoggingLevelSchema } from "@modelcontextprotocol/sdk/types.js";

export const validLogLevels = [
"trace",
"debug",
"info",
"warn",
"error",
] as const;

export type LogLevel = (typeof validLogLevels)[number];
// Extract valid log levels directly from the SDK's Zod schema to avoid drift
// This ensures CLI validation stays in sync with what the SDK accepts
export const validLogLevels = LoggingLevelSchema.options;

export async function connect(
client: Client,
Expand Down Expand Up @@ -38,10 +36,10 @@ export async function disconnect(transport: Transport): Promise<void> {
// Set logging level
export async function setLoggingLevel(
client: Client,
level: LogLevel,
): Promise<McpResponse> {
level: LoggingLevel,
): Promise<EmptyResult> {
try {
const response = await client.setLoggingLevel(level as any);
const response = await client.setLoggingLevel(level);
return response;
} catch (error) {
throw new Error(
Expand Down
9 changes: 6 additions & 3 deletions cli/src/client/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { McpResponse } from "./types.js";
import {
ListPromptsResult,
GetPromptResult,
} from "@modelcontextprotocol/sdk/types.js";

// List available prompts
export async function listPrompts(client: Client): Promise<McpResponse> {
export async function listPrompts(client: Client): Promise<ListPromptsResult> {
try {
const response = await client.listPrompts();
return response;
Expand All @@ -18,7 +21,7 @@ export async function getPrompt(
client: Client,
name: string,
args?: Record<string, string>,
): Promise<McpResponse> {
): Promise<GetPromptResult> {
try {
const response = await client.getPrompt({
name,
Expand Down
14 changes: 10 additions & 4 deletions cli/src/client/resources.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { McpResponse } from "./types.js";
import {
ListResourcesResult,
ReadResourceResult,
ListResourceTemplatesResult,
} from "@modelcontextprotocol/sdk/types.js";

// List available resources
export async function listResources(client: Client): Promise<McpResponse> {
export async function listResources(
client: Client,
): Promise<ListResourcesResult> {
try {
const response = await client.listResources();
return response;
Expand All @@ -17,7 +23,7 @@ export async function listResources(client: Client): Promise<McpResponse> {
export async function readResource(
client: Client,
uri: string,
): Promise<McpResponse> {
): Promise<ReadResourceResult> {
try {
const response = await client.readResource({ uri });
return response;
Expand All @@ -31,7 +37,7 @@ export async function readResource(
// List resource templates
export async function listResourceTemplates(
client: Client,
): Promise<McpResponse> {
): Promise<ListResourceTemplatesResult> {
try {
const response = await client.listResourceTemplates();
return response;
Expand Down
14 changes: 9 additions & 5 deletions cli/src/client/tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { Tool } from "@modelcontextprotocol/sdk/types.js";
import { McpResponse } from "./types.js";
import {
Tool,
ListToolsResult,
CallToolResult,
CompatibilityCallToolResult,
} from "@modelcontextprotocol/sdk/types.js";

type JsonSchemaType = {
type: "string" | "number" | "integer" | "boolean" | "array" | "object";
Expand All @@ -9,7 +13,7 @@ type JsonSchemaType = {
items?: JsonSchemaType;
};

export async function listTools(client: Client): Promise<McpResponse> {
export async function listTools(client: Client): Promise<ListToolsResult> {
try {
const response = await client.listTools();
return response;
Expand Down Expand Up @@ -69,10 +73,10 @@ export async function callTool(
client: Client,
name: string,
args: Record<string, string>,
): Promise<McpResponse> {
): Promise<CallToolResult | CompatibilityCallToolResult> {
try {
const toolsResponse = await listTools(client);
const tools = toolsResponse.tools as Tool[];
const tools = toolsResponse.tools;
const tool = tools.find((t) => t.name === name);

let convertedArgs: Record<string, unknown> = args;
Expand Down
24 changes: 23 additions & 1 deletion cli/src/client/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
export type McpResponse = Record<string, unknown>;
import type {
ListToolsResult,
CallToolResult,
CompatibilityCallToolResult,
ListPromptsResult,
GetPromptResult,
ListResourcesResult,
ReadResourceResult,
ListResourceTemplatesResult,
EmptyResult,
} from "@modelcontextprotocol/sdk/types.js";

// Union type for all possible MCP response types
export type McpResponse =
| ListToolsResult
| CallToolResult
| CompatibilityCallToolResult
| ListPromptsResult
| GetPromptResult
| ListResourcesResult
| ReadResourceResult
| ListResourceTemplatesResult
| EmptyResult;
8 changes: 4 additions & 4 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import {
listResources,
listResourceTemplates,
listTools,
LogLevel,
McpResponse,
readResource,
setLoggingLevel,
validLogLevels,
} from "./client/index.js";
import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js";
import { handleError } from "./error-handler.js";
import { createTransport, TransportOptions } from "./transport.js";

Expand All @@ -26,7 +26,7 @@ type Args = {
promptName?: string;
promptArgs?: Record<string, string>;
uri?: string;
logLevel?: LogLevel;
logLevel?: LoggingLevel;
toolName?: string;
toolArg?: Record<string, string>;
transport?: "sse" | "stdio" | "http";
Expand Down Expand Up @@ -232,13 +232,13 @@ function parseArgs(): Args {
"--log-level <level>",
"Logging level (for logging/setLevel method)",
(value: string) => {
if (!validLogLevels.includes(value as any)) {
if (!validLogLevels.includes(value as LoggingLevel)) {
throw new Error(
`Invalid log level: ${value}. Valid levels are: ${validLogLevels.join(", ")}`,
);
}

return value as LogLevel;
return value as LoggingLevel;
},
)
//
Expand Down