feat: port CLI, TUI, and launcher from v1.5 into v2 (#1452)
Ports the CLI, TUI, and launcher from v1.5 into v2 behind a single `mcp-inspector` entrypoint (launcher → web/cli/tui). Includes the `mcp-inspector --web --dev` launcher fix (Vite alias resolution + prod-server double-start), the root `postinstall` install cascade with simplified CI, and spec/AGENTS docs (no-workspaces rationale, Development setup). Closes #1246. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
53dc4549be
commit
2488ca5e38
1 changed files with 25594 additions and 236 deletions
137
clients/tui/src/utils/schemaToForm.ts
Normal file
137
clients/tui/src/utils/schemaToForm.ts
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* Converts JSON Schema to ink-form format
|
||||
*/
|
||||
|
||||
import type { FormStructure, FormSection, FormField } from "ink-form";
|
||||
|
||||
/** Minimal JSON Schema property shape used when building tool parameter forms */
|
||||
interface JsonSchemaProperty {
|
||||
type?: string;
|
||||
title?: string;
|
||||
enum?: unknown[];
|
||||
items?: { enum?: unknown[] };
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
default?: unknown;
|
||||
}
|
||||
|
||||
/** Minimal JSON Schema object shape (properties + required) */
|
||||
interface JsonSchemaObject {
|
||||
properties?: Record<string, JsonSchemaProperty>;
|
||||
required?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON Schema to ink-form structure
|
||||
*/
|
||||
export function schemaToForm(
|
||||
schema: JsonSchemaObject | null | undefined,
|
||||
toolName: string,
|
||||
): FormStructure {
|
||||
const fields: FormField[] = [];
|
||||
|
||||
if (!schema || !schema.properties) {
|
||||
return {
|
||||
title: `Test Tool: ${toolName}`,
|
||||
sections: [{ title: "Parameters", fields: [] }],
|
||||
};
|
||||
}
|
||||
|
||||
const properties = schema.properties || {};
|
||||
const required = schema.required || [];
|
||||
|
||||
for (const [key, prop] of Object.entries(properties)) {
|
||||
const property = prop as JsonSchemaProperty;
|
||||
const baseField = {
|
||||
name: key,
|
||||
label: property.title || key,
|
||||
required: required.includes(key),
|
||||
};
|
||||
|
||||
let field: FormField;
|
||||
|
||||
// Handle enum -> select
|
||||
if (property.enum) {
|
||||
if (property.type === "array" && property.items?.enum) {
|
||||
// For array of enums, we'll use select but handle it differently
|
||||
// Note: ink-form doesn't have multiselect, so we'll use select
|
||||
field = {
|
||||
type: "select",
|
||||
...baseField,
|
||||
options: property.items.enum.map((val: unknown) => ({
|
||||
label: String(val),
|
||||
value: String(val),
|
||||
})),
|
||||
} as FormField;
|
||||
} else {
|
||||
// Single select
|
||||
field = {
|
||||
type: "select",
|
||||
...baseField,
|
||||
options: property.enum.map((val: unknown) => ({
|
||||
label: String(val),
|
||||
value: String(val),
|
||||
})),
|
||||
} as FormField;
|
||||
}
|
||||
} else {
|
||||
// Map JSON Schema types to ink-form types
|
||||
switch (property.type) {
|
||||
case "string":
|
||||
field = {
|
||||
type: "string",
|
||||
...baseField,
|
||||
} as FormField;
|
||||
break;
|
||||
case "integer":
|
||||
field = {
|
||||
type: "integer",
|
||||
...baseField,
|
||||
...(property.minimum !== undefined && { min: property.minimum }),
|
||||
...(property.maximum !== undefined && { max: property.maximum }),
|
||||
} as FormField;
|
||||
break;
|
||||
case "number":
|
||||
field = {
|
||||
type: "float",
|
||||
...baseField,
|
||||
...(property.minimum !== undefined && { min: property.minimum }),
|
||||
...(property.maximum !== undefined && { max: property.maximum }),
|
||||
} as FormField;
|
||||
break;
|
||||
case "boolean":
|
||||
field = {
|
||||
type: "boolean",
|
||||
...baseField,
|
||||
} as FormField;
|
||||
break;
|
||||
default:
|
||||
// Default to string for unknown types
|
||||
field = {
|
||||
type: "string",
|
||||
...baseField,
|
||||
} as FormField;
|
||||
}
|
||||
}
|
||||
|
||||
// Set initial value from default (ink-form FormField allows initialValue for some types)
|
||||
if (property.default !== undefined) {
|
||||
(field as FormField & { initialValue?: unknown }).initialValue =
|
||||
property.default;
|
||||
}
|
||||
|
||||
fields.push(field);
|
||||
}
|
||||
|
||||
const sections: FormSection[] = [
|
||||
{
|
||||
title: "Parameters",
|
||||
fields,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
title: `Test Tool: ${toolName}`,
|
||||
sections,
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue