jui-tools-mcp-server 2.5.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/.github/workflows/test.yml +35 -0
- package/README.md +148 -0
- package/data/attribute_definitions.json +4248 -0
- package/data/attribute_semantics.json +137 -0
- package/data/binding_semantics.json +115 -0
- package/data/component_metadata.json +769 -0
- package/data/coverage.json +114 -0
- package/data/screen_identity.json +350 -0
- package/dist/cli_runner.d.ts +10 -0
- package/dist/cli_runner.js +33 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +67 -0
- package/dist/data/component_metadata.d.ts +30 -0
- package/dist/data/component_metadata.js +498 -0
- package/dist/data/derived.d.ts +198 -0
- package/dist/data/derived.js +293 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +130 -0
- package/dist/spec_loader.d.ts +110 -0
- package/dist/spec_loader.js +445 -0
- package/dist/tools/api/list_api_models.d.ts +10 -0
- package/dist/tools/api/list_api_models.js +37 -0
- package/dist/tools/api/list_api_specs.d.ts +19 -0
- package/dist/tools/api/list_api_specs.js +42 -0
- package/dist/tools/api/preview_api_model_sync.d.ts +11 -0
- package/dist/tools/api/preview_api_model_sync.js +34 -0
- package/dist/tools/context/get_project_config.d.ts +3 -0
- package/dist/tools/context/get_project_config.js +20 -0
- package/dist/tools/context/list_component_specs.d.ts +3 -0
- package/dist/tools/context/list_component_specs.js +35 -0
- package/dist/tools/context/list_layouts.d.ts +3 -0
- package/dist/tools/context/list_layouts.js +118 -0
- package/dist/tools/context/list_screen_specs.d.ts +3 -0
- package/dist/tools/context/list_screen_specs.js +37 -0
- package/dist/tools/context/read_layout_file.d.ts +3 -0
- package/dist/tools/context/read_layout_file.js +25 -0
- package/dist/tools/context/read_spec_file.d.ts +3 -0
- package/dist/tools/context/read_spec_file.js +27 -0
- package/dist/tools/doc/doc_generate_component.d.ts +3 -0
- package/dist/tools/doc/doc_generate_component.js +38 -0
- package/dist/tools/doc/doc_generate_html.d.ts +3 -0
- package/dist/tools/doc/doc_generate_html.js +46 -0
- package/dist/tools/doc/doc_generate_spec.d.ts +3 -0
- package/dist/tools/doc/doc_generate_spec.js +38 -0
- package/dist/tools/doc/doc_init_component.d.ts +3 -0
- package/dist/tools/doc/doc_init_component.js +30 -0
- package/dist/tools/doc/doc_init_spec.d.ts +3 -0
- package/dist/tools/doc/doc_init_spec.js +33 -0
- package/dist/tools/doc/doc_rules_init.d.ts +3 -0
- package/dist/tools/doc/doc_rules_init.js +25 -0
- package/dist/tools/doc/doc_rules_show.d.ts +3 -0
- package/dist/tools/doc/doc_rules_show.js +21 -0
- package/dist/tools/doc/doc_validate_component.d.ts +3 -0
- package/dist/tools/doc/doc_validate_component.js +39 -0
- package/dist/tools/doc/doc_validate_spec.d.ts +3 -0
- package/dist/tools/doc/doc_validate_spec.js +40 -0
- package/dist/tools/jui/jui_build.d.ts +3 -0
- package/dist/tools/jui/jui_build.js +31 -0
- package/dist/tools/jui/jui_generate_converter.d.ts +3 -0
- package/dist/tools/jui/jui_generate_converter.js +43 -0
- package/dist/tools/jui/jui_generate_project.d.ts +3 -0
- package/dist/tools/jui/jui_generate_project.js +43 -0
- package/dist/tools/jui/jui_generate_screen.d.ts +3 -0
- package/dist/tools/jui/jui_generate_screen.js +22 -0
- package/dist/tools/jui/jui_init.d.ts +3 -0
- package/dist/tools/jui/jui_init.js +42 -0
- package/dist/tools/jui/jui_migrate_layouts.d.ts +3 -0
- package/dist/tools/jui/jui_migrate_layouts.js +25 -0
- package/dist/tools/jui/jui_sync_tool.d.ts +3 -0
- package/dist/tools/jui/jui_sync_tool.js +33 -0
- package/dist/tools/jui/jui_verify.d.ts +3 -0
- package/dist/tools/jui/jui_verify.js +43 -0
- package/dist/tools/spec/get_binding_rules.d.ts +3 -0
- package/dist/tools/spec/get_binding_rules.js +8 -0
- package/dist/tools/spec/get_data_source.d.ts +3 -0
- package/dist/tools/spec/get_data_source.js +41 -0
- package/dist/tools/spec/get_modifier_order.d.ts +3 -0
- package/dist/tools/spec/get_modifier_order.js +14 -0
- package/dist/tools/spec/get_platform_mapping.d.ts +3 -0
- package/dist/tools/spec/get_platform_mapping.js +20 -0
- package/dist/tools/spec/get_screen_identity.d.ts +3 -0
- package/dist/tools/spec/get_screen_identity.js +22 -0
- package/dist/tools/spec/lookup_attribute.d.ts +3 -0
- package/dist/tools/spec/lookup_attribute.js +14 -0
- package/dist/tools/spec/lookup_component.d.ts +3 -0
- package/dist/tools/spec/lookup_component.js +15 -0
- package/dist/tools/spec/search_components.d.ts +3 -0
- package/dist/tools/spec/search_components.js +14 -0
- package/dist/tools/test/test_artifacts_pull.d.ts +3 -0
- package/dist/tools/test/test_artifacts_pull.js +48 -0
- package/dist/tools/test/test_artifacts_status.d.ts +3 -0
- package/dist/tools/test/test_artifacts_status.js +28 -0
- package/dist/tools/test/test_generate_description.d.ts +3 -0
- package/dist/tools/test/test_generate_description.js +27 -0
- package/dist/tools/test/test_generate_flow.d.ts +3 -0
- package/dist/tools/test/test_generate_flow.js +29 -0
- package/dist/tools/test/test_generate_screen.d.ts +3 -0
- package/dist/tools/test/test_generate_screen.js +29 -0
- package/dist/tools/test/test_mock_generate.d.ts +3 -0
- package/dist/tools/test/test_mock_generate.js +64 -0
- package/dist/tools/test/test_report.d.ts +3 -0
- package/dist/tools/test/test_report.js +26 -0
- package/dist/tools/test/test_validate.d.ts +3 -0
- package/dist/tools/test/test_validate.js +43 -0
- package/docs/design.md +227 -0
- package/install.sh +90 -0
- package/package.json +24 -0
- package/scripts/fetch-definitions.js +101 -0
- package/scripts/fetch-manifest.js +60 -0
- package/src/cli_runner.ts +55 -0
- package/src/config.ts +88 -0
- package/src/data/derived.ts +305 -0
- package/src/index.ts +157 -0
- package/src/spec_loader.ts +591 -0
- package/src/tools/api/list_api_models.ts +44 -0
- package/src/tools/api/list_api_specs.ts +53 -0
- package/src/tools/api/preview_api_model_sync.ts +41 -0
- package/src/tools/context/get_project_config.ts +27 -0
- package/src/tools/context/list_component_specs.ts +44 -0
- package/src/tools/context/list_layouts.ts +131 -0
- package/src/tools/context/list_screen_specs.ts +46 -0
- package/src/tools/context/read_layout_file.ts +35 -0
- package/src/tools/context/read_spec_file.ts +38 -0
- package/src/tools/doc/doc_generate_component.ts +43 -0
- package/src/tools/doc/doc_generate_html.ts +40 -0
- package/src/tools/doc/doc_generate_spec.ts +43 -0
- package/src/tools/doc/doc_init_component.ts +32 -0
- package/src/tools/doc/doc_init_spec.ts +37 -0
- package/src/tools/doc/doc_rules_init.ts +29 -0
- package/src/tools/doc/doc_rules_show.ts +27 -0
- package/src/tools/doc/doc_validate_component.ts +47 -0
- package/src/tools/doc/doc_validate_spec.ts +48 -0
- package/src/tools/jui/jui_build.ts +31 -0
- package/src/tools/jui/jui_generate_converter.ts +41 -0
- package/src/tools/jui/jui_generate_project.ts +37 -0
- package/src/tools/jui/jui_generate_screen.ts +28 -0
- package/src/tools/jui/jui_init.ts +38 -0
- package/src/tools/jui/jui_migrate_layouts.ts +29 -0
- package/src/tools/jui/jui_sync_tool.ts +33 -0
- package/src/tools/jui/jui_verify.ts +43 -0
- package/src/tools/spec/get_binding_rules.ts +16 -0
- package/src/tools/spec/get_data_source.ts +53 -0
- package/src/tools/spec/get_modifier_order.ts +22 -0
- package/src/tools/spec/get_platform_mapping.ts +28 -0
- package/src/tools/spec/get_screen_identity.ts +31 -0
- package/src/tools/spec/lookup_attribute.ts +22 -0
- package/src/tools/spec/lookup_component.ts +23 -0
- package/src/tools/spec/search_components.ts +22 -0
- package/src/tools/test/test_artifacts_pull.ts +47 -0
- package/src/tools/test/test_artifacts_status.ts +34 -0
- package/src/tools/test/test_generate_description.ts +33 -0
- package/src/tools/test/test_generate_flow.ts +33 -0
- package/src/tools/test/test_generate_screen.ts +33 -0
- package/src/tools/test/test_mock_generate.ts +58 -0
- package/src/tools/test/test_report.ts +32 -0
- package/src/tools/test/test_validate.ts +43 -0
- package/tests/__snapshots__/derived.test.ts.snap +279 -0
- package/tests/bundled_snapshot.test.ts +155 -0
- package/tests/cli_runner.test.ts +120 -0
- package/tests/config.test.ts +212 -0
- package/tests/derived.test.ts +114 -0
- package/tests/helpers.ts +272 -0
- package/tests/setup.ts +27 -0
- package/tests/snapshot_completeness.test.ts +152 -0
- package/tests/spec_loader.test.ts +488 -0
- package/tests/tools_cli.test.ts +687 -0
- package/tests/tools_context.test.ts +274 -0
- package/tests/tools_spec.test.ts +225 -0
- package/tests/tools_test.test.ts +284 -0
- package/tsconfig.json +15 -0
- package/uninstall.sh +46 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_artifacts_pull", "Pull test artifacts (screenshots/recordings) from the latest run (iOS xcresult / Android device / web Playwright output) into the project's configured artifacts directory, returning generated file paths", {
|
|
5
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
6
|
+
platform: z.enum(["ios", "android", "web", "all"]).optional().describe("Restrict pulling to one platform (default: all configured)"),
|
|
7
|
+
xcresult: z.string().optional().describe("Explicit .xcresult bundle path (iOS)"),
|
|
8
|
+
serial: z.string().optional().describe("adb device serial (Android)"),
|
|
9
|
+
clean: z.boolean().optional().describe("Remove artifacts from the device after pulling (Android)"),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
13
|
+
const args = ["artifacts", "pull", "--json"];
|
|
14
|
+
if (params.platform) {
|
|
15
|
+
args.push("--platform", params.platform);
|
|
16
|
+
}
|
|
17
|
+
if (params.xcresult) {
|
|
18
|
+
args.push("--xcresult", params.xcresult);
|
|
19
|
+
}
|
|
20
|
+
if (params.serial) {
|
|
21
|
+
args.push("--serial", params.serial);
|
|
22
|
+
}
|
|
23
|
+
if (params.clean) {
|
|
24
|
+
args.push("--clean");
|
|
25
|
+
}
|
|
26
|
+
// xcresult export can exceed the default 60s timeout.
|
|
27
|
+
const result = await runCli("jsonui-test", args, {
|
|
28
|
+
cwd: projectDir,
|
|
29
|
+
timeout: 180_000,
|
|
30
|
+
});
|
|
31
|
+
if (result.exitCode === 0) {
|
|
32
|
+
try {
|
|
33
|
+
JSON.parse(result.stdout);
|
|
34
|
+
// The CLI already emits a JSON document ({outputDir, files, skipped})
|
|
35
|
+
// — pass it through verbatim so callers don't peel a wrapper envelope.
|
|
36
|
+
return { content: [{ type: "text", text: result.stdout.trim() }] };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Not JSON — fall through to the standard envelope.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_artifacts_status", "Show the resolved test-artifacts configuration (output dir, iOS xcresult discovery, Android appId) and list already-pulled artifact files", {
|
|
5
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
6
|
+
}, async (params) => {
|
|
7
|
+
try {
|
|
8
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
9
|
+
const result = await runCli("jsonui-test", ["artifacts", "status", "--json"], {
|
|
10
|
+
cwd: projectDir,
|
|
11
|
+
});
|
|
12
|
+
if (result.exitCode === 0) {
|
|
13
|
+
try {
|
|
14
|
+
JSON.parse(result.stdout);
|
|
15
|
+
// The CLI already emits a JSON document — pass it through verbatim.
|
|
16
|
+
return { content: [{ type: "text", text: result.stdout.trim() }] };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Not JSON — fall through to the standard envelope.
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_generate_description", "Generate a description JSON file for a screen or flow test case via the jsonui-test CLI", {
|
|
5
|
+
test_type: z.enum(["screen", "flow"]).describe("Whether the case belongs to a screen or flow test"),
|
|
6
|
+
name: z.string().describe("Screen/flow name (e.g., login, checkout)"),
|
|
7
|
+
case_name: z.string().describe("Test case name (e.g., initial_display, happy_path)"),
|
|
8
|
+
path: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Output file path (default: tests/<type>s/<name>/descriptions/<case_name>.json)"),
|
|
12
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
13
|
+
}, async (params) => {
|
|
14
|
+
try {
|
|
15
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
16
|
+
const args = ["generate", "description", params.test_type, params.name, params.case_name];
|
|
17
|
+
if (params.path) {
|
|
18
|
+
args.push("--path", params.path);
|
|
19
|
+
}
|
|
20
|
+
const result = await runCli("jsonui-test", args, { cwd: projectDir });
|
|
21
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_generate_flow", "Generate a flow test file template via the jsonui-test CLI", {
|
|
5
|
+
name: z.string().describe("Flow name (e.g., login, checkout)"),
|
|
6
|
+
path: z.string().optional().describe("Output test file path (default: tests/flows/<name>/<name>.test.json)"),
|
|
7
|
+
platform: z
|
|
8
|
+
.enum(["ios", "ios-swiftui", "ios-uikit", "android", "web", "all"])
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Target platform"),
|
|
11
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
12
|
+
}, async (params) => {
|
|
13
|
+
try {
|
|
14
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
15
|
+
const args = ["generate", "test", "flow", params.name];
|
|
16
|
+
if (params.path) {
|
|
17
|
+
args.push("--path", params.path);
|
|
18
|
+
}
|
|
19
|
+
if (params.platform) {
|
|
20
|
+
args.push("--platform", params.platform);
|
|
21
|
+
}
|
|
22
|
+
const result = await runCli("jsonui-test", args, { cwd: projectDir });
|
|
23
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_generate_screen", "Generate a screen test file template via the jsonui-test CLI", {
|
|
5
|
+
name: z.string().describe("Screen name (e.g., login, home)"),
|
|
6
|
+
path: z.string().optional().describe("Output test file path (default: tests/screens/<name>/<name>.test.json)"),
|
|
7
|
+
platform: z
|
|
8
|
+
.enum(["ios", "ios-swiftui", "ios-uikit", "android", "web", "all"])
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Target platform"),
|
|
11
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
12
|
+
}, async (params) => {
|
|
13
|
+
try {
|
|
14
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
15
|
+
const args = ["generate", "test", "screen", params.name];
|
|
16
|
+
if (params.path) {
|
|
17
|
+
args.push("--path", params.path);
|
|
18
|
+
}
|
|
19
|
+
if (params.platform) {
|
|
20
|
+
args.push("--platform", params.platform);
|
|
21
|
+
}
|
|
22
|
+
const result = await runCli("jsonui-test", args, { cwd: projectDir });
|
|
23
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_mock_generate", "Manage API mock files against an OpenAPI/Swagger spec via the jsonui-test CLI. Writes ONLY into <mockDir>/generated/, which it wipes and rewrites on each run — anything outside that directory is hand-written and never touched. Mocks are matched to operations by their source method+path, not by filename, so a project's own naming keeps working. Only the endpoints the project declares it consumes are scaffolded and checked: api.schemas.include_paths / exclude_paths (the same keys the DTO codegen filters on), or mock.includePaths / mock.excludePaths when the mock scope differs. A shared swagger's other realms are therefore NOT reported as mocks this project is missing — if a check reports many [MISSING] entries, read the paths before writing any, because endpoints from a realm the app cannot reach mean the scope is undeclared and the fix is one config key. A mock file serving an out-of-scope route is reported as [SCOPE] (an unused file, safe to delete) and does not fail; [ORPHAN] still means the swagger has no such endpoint at all, and still fails. check=true reports drift (response bodies are compared against the schema: types, required, enum) without writing. Findings under generated/ are warnings, findings outside it are errors, and a scenario that merely omits OPTIONAL fields is a note that does not fail — omitting an optional field is a valid instance, and failing on it buries the real violations (set strict=true to demand full coverage). Do NOT fill optional fields in to silence a note: a mechanical merge puts null into non-nullable slots and manufactures violations. update_default=true REPAIRS each existing mock's default scenario: it adds the required fields the contract has and the body lacks, refreshes the source route, and changes nothing else — no existing value is overwritten, nothing is removed, other scenarios are untouched. The default scenario is where a project keeps the data its tests assert on (scaffolding creates default and nothing else), so never replace a body wholesale to satisfy the check. Violations a merge cannot decide — wrong type, undeclared field — are reported for a person. dry_run=true previews. The 'mock serve' subcommand is intentionally NOT exposed over MCP (it is a long-running local HTTP server that executes run-targets).", {
|
|
5
|
+
swagger: z
|
|
6
|
+
.array(z.string())
|
|
7
|
+
.optional()
|
|
8
|
+
.describe("Paths to OpenAPI files (repeatable). Defaults to the project mock config if omitted."),
|
|
9
|
+
out: z.string().optional().describe("Output mock dir (default: mock.mockDir or tests/mocks)"),
|
|
10
|
+
config_file: z.string().optional().describe("Config file (default: jui.config.json)"),
|
|
11
|
+
check: z
|
|
12
|
+
.boolean()
|
|
13
|
+
.optional()
|
|
14
|
+
.default(false)
|
|
15
|
+
.describe("When true, report drift vs swagger and do NOT write (adds --check). Default false = regenerate <mockDir>/generated/."),
|
|
16
|
+
strict: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.optional()
|
|
19
|
+
.default(false)
|
|
20
|
+
.describe("With check=true, treat a missing OPTIONAL field as drift too (adds --strict). Off by default."),
|
|
21
|
+
update_default: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.default(false)
|
|
25
|
+
.describe("Repair each existing mock's default scenario by ADDING missing required fields (adds --update-default). Never overwrites a value or removes a field."),
|
|
26
|
+
dry_run: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.optional()
|
|
29
|
+
.default(false)
|
|
30
|
+
.describe("With update_default, report what would be added without writing (adds --dry-run)."),
|
|
31
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
32
|
+
}, async (params) => {
|
|
33
|
+
try {
|
|
34
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
35
|
+
const args = ["mock", "generate"];
|
|
36
|
+
for (const s of params.swagger ?? []) {
|
|
37
|
+
args.push("--swagger", s);
|
|
38
|
+
}
|
|
39
|
+
if (params.out) {
|
|
40
|
+
args.push("--out", params.out);
|
|
41
|
+
}
|
|
42
|
+
if (params.config_file) {
|
|
43
|
+
args.push("--config", params.config_file);
|
|
44
|
+
}
|
|
45
|
+
if (params.check) {
|
|
46
|
+
args.push("--check");
|
|
47
|
+
}
|
|
48
|
+
if (params.strict) {
|
|
49
|
+
args.push("--strict");
|
|
50
|
+
}
|
|
51
|
+
if (params.update_default) {
|
|
52
|
+
args.push("--update-default");
|
|
53
|
+
}
|
|
54
|
+
if (params.dry_run) {
|
|
55
|
+
args.push("--dry-run");
|
|
56
|
+
}
|
|
57
|
+
const result = await runCli("jsonui-test", args, { cwd: projectDir });
|
|
58
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_report", "Convert one or more results JSON files (results.schema.json format) into a JUnit XML or HTML report via the jsonui-test CLI", {
|
|
5
|
+
files: z
|
|
6
|
+
.array(z.string())
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe("Results JSON files to convert (multiple inputs merge into one report)"),
|
|
9
|
+
format: z.enum(["junit", "html"]).describe("Report format"),
|
|
10
|
+
output: z.string().optional().describe("Output file path (default: report.xml / report.html)"),
|
|
11
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
12
|
+
}, async (params) => {
|
|
13
|
+
try {
|
|
14
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
15
|
+
const args = ["report", ...params.files, "--format", params.format];
|
|
16
|
+
if (params.output) {
|
|
17
|
+
args.push("--output", params.output);
|
|
18
|
+
}
|
|
19
|
+
const result = await runCli("jsonui-test", args, { cwd: projectDir });
|
|
20
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCli, formatResult } from "../../cli_runner.js";
|
|
3
|
+
export function register(server, config) {
|
|
4
|
+
server.tool("test_validate", "Validate JsonUI test files (screen/flow tests + descriptions) against the schema via the jsonui-test CLI. When the project config declares mock.swagger + mock.mockDir this ALSO regenerates <mockDir>/generated/ if it is missing or older than the swagger, and fails on mock contract drift — so a non-zero result here is not necessarily about the test files. A mock that merely omits optional fields does not fail this gate (set mock.checkOptionalFields=true in the project config to demand full coverage). Only the endpoints the project declares it consumes are checked (api.schemas.include_paths / exclude_paths, the same keys the DTO codegen filters on), so a shared swagger's other realms are not reported as missing mocks. Pass no_mock_check to validate the test files alone. Like the CLI, this installs (flatten-copies) the tests into the configured test.install destinations as a side effect — pass no_install to validate without consuming the files.", {
|
|
5
|
+
files: z
|
|
6
|
+
.array(z.string())
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe("Files or directories to validate (e.g., ['tests/screens'] or ['login.test.json'])"),
|
|
9
|
+
verbose: z.boolean().optional().describe("Show all files, including valid ones"),
|
|
10
|
+
quiet: z.boolean().optional().describe("Hide warnings, show only errors"),
|
|
11
|
+
no_mock_check: z
|
|
12
|
+
.boolean()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Skip the mock-vs-swagger contract check that otherwise runs on the same gate"),
|
|
15
|
+
no_install: z
|
|
16
|
+
.boolean()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Validate only; skip the flatten-install side effect even if test.install is configured (maps to CLI --no-install; default false, same behavior as the CLI)"),
|
|
19
|
+
project_dir: z.string().optional().describe("Project directory (overrides JUI_PROJECT_DIR env)"),
|
|
20
|
+
}, async (params) => {
|
|
21
|
+
try {
|
|
22
|
+
const projectDir = config.resolveProjectDir(params.project_dir);
|
|
23
|
+
const args = ["validate", ...params.files];
|
|
24
|
+
if (params.verbose) {
|
|
25
|
+
args.push("--verbose");
|
|
26
|
+
}
|
|
27
|
+
if (params.quiet) {
|
|
28
|
+
args.push("--quiet");
|
|
29
|
+
}
|
|
30
|
+
if (params.no_mock_check) {
|
|
31
|
+
args.push("--no-mock-check");
|
|
32
|
+
}
|
|
33
|
+
if (params.no_install) {
|
|
34
|
+
args.push("--no-install");
|
|
35
|
+
}
|
|
36
|
+
const result = await runCli("jsonui-test", args, { cwd: projectDir });
|
|
37
|
+
return { content: [{ type: "text", text: formatResult(result) }] };
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
package/docs/design.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# jsonui-mcp-server (`jui-tools`) — Design
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
An MCP server that lets an AI agent drive JsonUI projects without memorising
|
|
6
|
+
CLI syntax or re-reading source files. It exposes five groups of tools:
|
|
7
|
+
|
|
8
|
+
1. **Spec lookup** — answers "what is this component / attribute, and how
|
|
9
|
+
does it bind?" from the canonical jsonui-cli data files.
|
|
10
|
+
2. **Project context** — reads `jui.config.json`, screen specs, layouts,
|
|
11
|
+
and component specs on the agent's behalf.
|
|
12
|
+
3. **`jui` CLI wrappers** — `init` / `generate project` / `generate screen`
|
|
13
|
+
/ `generate converter` / `build` / `verify` / `migrate-layouts`.
|
|
14
|
+
4. **`jsonui-doc` CLI wrappers** — `init` / `validate` / `generate` /
|
|
15
|
+
`rules` for specs, components, and HTML output.
|
|
16
|
+
5. **API model discovery** — read-only wrappers over `jui ls api-specs` /
|
|
17
|
+
`jui ls api-models` / `jui g api --dry-run` for the swagger→DTO/Domain
|
|
18
|
+
pipeline.
|
|
19
|
+
|
|
20
|
+
The server is published as `jui-tools` (version 2.x), currently exposing
|
|
21
|
+
41 tools.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Data pipeline
|
|
26
|
+
|
|
27
|
+
The spec-lookup tools (Group A) never hand-maintain component data. They
|
|
28
|
+
derive everything at runtime from two files that live in jsonui-cli:
|
|
29
|
+
|
|
30
|
+
| File | Owner | Role |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `shared/core/attribute_definitions.json` | jsonui-cli | Raw schema: every component, every attribute, their `type`s, descriptions, enums, and — for attributes whose `type` includes `"binding"` — an optional `binding_direction: "two-way"` flag. |
|
|
33
|
+
| `shared/core/component_metadata.json` | jsonui-cli | Presentation metadata: per-component description, aliases, which platforms generate the component, platform-specific implementation notes, and agent-facing prose rules. |
|
|
34
|
+
|
|
35
|
+
`SpecLoader` joins these two at startup to produce the in-memory
|
|
36
|
+
`ComponentSpec` map that the tools read.
|
|
37
|
+
|
|
38
|
+
### 4-layer resolution
|
|
39
|
+
|
|
40
|
+
Both files are located via the same fallback chain:
|
|
41
|
+
|
|
42
|
+
1. `$JSONUI_CLI_PATH/shared/core/<file>` — env override for monorepos or
|
|
43
|
+
custom checkouts.
|
|
44
|
+
2. `./.jsonui-cli/shared/core/<file>` — per-project override inside the
|
|
45
|
+
current working directory.
|
|
46
|
+
3. `~/.jsonui-cli/shared/core/<file>` — default location of the
|
|
47
|
+
jsonui-cli bootstrap installer.
|
|
48
|
+
4. `<mcp-root>/data/<file>` — bundled snapshot, populated at install
|
|
49
|
+
time by `scripts/fetch-definitions.js` (hit against
|
|
50
|
+
`raw.githubusercontent.com/Tai-Kimura/jsonui-cli/main/...`).
|
|
51
|
+
|
|
52
|
+
The highest-priority existing file wins. If all four are missing,
|
|
53
|
+
`SpecLoader` throws at startup with a diagnostic that lists every path
|
|
54
|
+
it tried.
|
|
55
|
+
|
|
56
|
+
The `get_data_source` tool exposes this information at runtime: which
|
|
57
|
+
layer each file came from, its `mtime`, and a freshness bucket
|
|
58
|
+
(`fresh` ≤30d / `aging` ≤90d / `stale` >90d). Agents that need
|
|
59
|
+
up-to-date schema call this to verify the data they're working against.
|
|
60
|
+
|
|
61
|
+
### Derived (non-file) content
|
|
62
|
+
|
|
63
|
+
`src/data/derived.ts` holds content that does not depend on the two
|
|
64
|
+
jsonui-cli files — it encodes fixed cross-platform conventions:
|
|
65
|
+
|
|
66
|
+
- `MODIFIER_ORDER` (Swift / Kotlin / React application order + critical
|
|
67
|
+
rules like "background must come after padding").
|
|
68
|
+
- `BINDING_RULES` (`@{}` syntax, two-way vs. read-only summary, direction
|
|
69
|
+
rules across platforms).
|
|
70
|
+
- `PLATFORM_MAPPING` (`matchParent` / `wrapContent`, `contentMode`,
|
|
71
|
+
`textAlign`, `fontWeight`, `orientation`, `gravity`, type mappings).
|
|
72
|
+
- `categorizeCommonAttributes(…)` — groups `common.*` attributes into
|
|
73
|
+
sizing / spacing / visual / visibility / interaction / layout /
|
|
74
|
+
lifecycle / accessibility / binding buckets for the lookup tools.
|
|
75
|
+
|
|
76
|
+
These are served directly by the `get_modifier_order`,
|
|
77
|
+
`get_binding_rules`, and `get_platform_mapping` tools.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Tool inventory (33)
|
|
82
|
+
|
|
83
|
+
### Group A — Spec lookup (7)
|
|
84
|
+
|
|
85
|
+
| Tool | What it returns |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `lookup_component` | Full spec for a component (attributes, binding behaviour, platform notes, rules). |
|
|
88
|
+
| `lookup_attribute` | Definition + scope (common vs. component) for a single attribute name. |
|
|
89
|
+
| `search_components` | Ranked matches across component names, aliases, descriptions, attribute names, and rules. |
|
|
90
|
+
| `get_modifier_order` | Platform-specific modifier / class ordering. |
|
|
91
|
+
| `get_binding_rules` | Full `@{}` binding reference. |
|
|
92
|
+
| `get_platform_mapping` | Value/enum mapping across Swift/Kotlin/React. |
|
|
93
|
+
| `get_data_source` | Per-file layer, path, `mtime`, freshness bucket. |
|
|
94
|
+
|
|
95
|
+
### Group B — Project context (6)
|
|
96
|
+
|
|
97
|
+
| Tool | Reads |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `get_project_config` | `jui.config.json`, resolves relative paths to absolute. |
|
|
100
|
+
| `list_screen_specs` | Screen-spec index with metadata. |
|
|
101
|
+
| `list_component_specs` | Component-spec index with metadata. |
|
|
102
|
+
| `list_layouts` | Layout JSON inventory. |
|
|
103
|
+
| `read_spec_file` | One spec file (no parsing beyond `JSON.parse`). |
|
|
104
|
+
| `read_layout_file` | One layout JSON file. |
|
|
105
|
+
|
|
106
|
+
### Group C — `jui` CLI wrappers (8)
|
|
107
|
+
|
|
108
|
+
`jui_init`, `jui_generate_project`, `jui_generate_screen`,
|
|
109
|
+
`jui_generate_converter`, `jui_build`, `jui_verify`,
|
|
110
|
+
`jui_migrate_layouts`, `jui_sync_tool`.
|
|
111
|
+
|
|
112
|
+
`jui_sync_tool` mirrors the home `~/.jsonui-cli/` platform tools into each
|
|
113
|
+
project-local `<platform_root>/<tool>_tools/`, preserving every
|
|
114
|
+
`extensions/` directory and propagating each tool's `.ruby-version` to
|
|
115
|
+
the platform root. Agents call it after a jsonui-cli bump or when
|
|
116
|
+
bootstrapping a freshly scaffolded platform.
|
|
117
|
+
|
|
118
|
+
### Group D — `jsonui-doc` CLI wrappers (9)
|
|
119
|
+
|
|
120
|
+
`doc_init_spec`, `doc_init_component`, `doc_validate_spec`,
|
|
121
|
+
`doc_validate_component`, `doc_generate_spec`, `doc_generate_component`,
|
|
122
|
+
`doc_generate_html`, `doc_rules_init`, `doc_rules_show`.
|
|
123
|
+
|
|
124
|
+
`figma fetch/images` and `generate mermaid/adapter` are intentionally
|
|
125
|
+
excluded (token management / low priority).
|
|
126
|
+
|
|
127
|
+
### Group E — API model discovery (3)
|
|
128
|
+
|
|
129
|
+
`list_api_specs`, `list_api_models`, `preview_api_model_sync`.
|
|
130
|
+
|
|
131
|
+
All three shell out to `jui` with `--json` output (`jui ls api-specs`,
|
|
132
|
+
`jui ls api-models`, `jui g api --dry-run --json`). `list_api_specs` also
|
|
133
|
+
surfaces the swagger halt conditions before a build is attempted;
|
|
134
|
+
`preview_api_model_sync` is a pure dry-run — it reports kept/filtered
|
|
135
|
+
schemas and the would-be file list without writing anything.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Source layout
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
src/
|
|
143
|
+
index.ts # registers all 41 tools
|
|
144
|
+
config.ts # ServerConfig: JUI_PROJECT_DIR, jui.config.json
|
|
145
|
+
cli_runner.ts # execFile wrapper (no shell), timeout defaults
|
|
146
|
+
spec_loader.ts # 4-layer fallback + merge pipeline, public API
|
|
147
|
+
data/
|
|
148
|
+
derived.ts # MODIFIER_ORDER / BINDING_RULES / PLATFORM_MAPPING + categorizer
|
|
149
|
+
tools/
|
|
150
|
+
spec/ (7 files) # Group A
|
|
151
|
+
context/ (6 files) # Group B
|
|
152
|
+
jui/ (8 files) # Group C
|
|
153
|
+
doc/ (9 files) # Group D
|
|
154
|
+
api/ (3 files) # Group E
|
|
155
|
+
data/
|
|
156
|
+
attribute_definitions.json # bundled snapshot (4th-layer fallback)
|
|
157
|
+
component_metadata.json # bundled snapshot (4th-layer fallback)
|
|
158
|
+
scripts/
|
|
159
|
+
fetch-definitions.js # npm postinstall hook — fetches both files
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Configuration
|
|
165
|
+
|
|
166
|
+
### `project_dir` resolution
|
|
167
|
+
|
|
168
|
+
Each tool in Groups B/C/D accepts an optional `project_dir`. Resolution:
|
|
169
|
+
|
|
170
|
+
1. Tool parameter, if passed.
|
|
171
|
+
2. `$JUI_PROJECT_DIR` environment variable.
|
|
172
|
+
3. Otherwise the tool returns an actionable error.
|
|
173
|
+
|
|
174
|
+
### Environment variables
|
|
175
|
+
|
|
176
|
+
| Variable | Purpose |
|
|
177
|
+
|---|---|
|
|
178
|
+
| `JUI_PROJECT_DIR` | Default project directory for Groups B/C/D. |
|
|
179
|
+
| `JSONUI_CLI_PATH` | Override for layer 1 of the spec-data fallback. |
|
|
180
|
+
| `JSONUI_CLI_RAW_BASE` | Override the GitHub raw base URL used by the postinstall fetch script (defaults to `raw.githubusercontent.com/Tai-Kimura/jsonui-cli/main`). |
|
|
181
|
+
| `JSONUI_ATTR_DEFINITIONS_URL` | Direct URL override for `attribute_definitions.json` in the fetch script. |
|
|
182
|
+
| `JSONUI_COMPONENT_METADATA_URL` | Direct URL override for `component_metadata.json` in the fetch script. |
|
|
183
|
+
|
|
184
|
+
### Claude Code integration
|
|
185
|
+
|
|
186
|
+
`~/.claude/settings.json`:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"mcpServers": {
|
|
191
|
+
"jui-tools": {
|
|
192
|
+
"command": "node",
|
|
193
|
+
"args": ["<install_path>/dist/index.js"],
|
|
194
|
+
"env": {
|
|
195
|
+
"JUI_PROJECT_DIR": "/path/to/project"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`install.sh` writes this entry (preserving any other servers); `uninstall.sh`
|
|
203
|
+
removes it.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Security
|
|
208
|
+
|
|
209
|
+
- Group C/D tools invoke CLIs through `child_process.execFile`, never
|
|
210
|
+
`exec`, so no shell parses arguments.
|
|
211
|
+
- File-read tools validate that targets stay inside `project_dir`.
|
|
212
|
+
- `figma` CLIs are excluded to avoid handling tokens in this server.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Install flow
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
git clone https://github.com/Tai-Kimura/jsonui-mcp-server.git
|
|
220
|
+
cd jsonui-mcp-server
|
|
221
|
+
./install.sh # npm install → npm run build → postinstall fetch
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`npm install` triggers `scripts/fetch-definitions.js` automatically. If
|
|
225
|
+
the network is unavailable and no prior bundled snapshot exists, the
|
|
226
|
+
script exits 0 and the MCP surfaces the misconfiguration at startup
|
|
227
|
+
instead of silently running on stale data.
|
package/install.sh
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
REPO_URL="${JSONUI_MCP_REPO:-https://github.com/Tai-Kimura/jsonui-mcp-server.git}"
|
|
5
|
+
INSTALL_DIR="${JSONUI_MCP_DIR:-$HOME/.jsonui-mcp-server}"
|
|
6
|
+
CLAUDE_JSON="${CLAUDE_JSON:-$HOME/.claude.json}"
|
|
7
|
+
NODE_BIN="$(which node)"
|
|
8
|
+
|
|
9
|
+
if [ -z "$NODE_BIN" ]; then
|
|
10
|
+
echo "Error: node not found in PATH. Install Node.js first." >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
echo "=== JUI Tools MCP Server Installer ==="
|
|
15
|
+
echo " Repo: $REPO_URL"
|
|
16
|
+
echo " Install: $INSTALL_DIR"
|
|
17
|
+
echo " Node: $NODE_BIN"
|
|
18
|
+
|
|
19
|
+
# 1. Clone or update the repository
|
|
20
|
+
if [ -d "$INSTALL_DIR/.git" ]; then
|
|
21
|
+
echo "Updating existing installation..."
|
|
22
|
+
cd "$INSTALL_DIR"
|
|
23
|
+
git fetch origin
|
|
24
|
+
git reset --hard origin/main
|
|
25
|
+
elif [ -d "$INSTALL_DIR" ]; then
|
|
26
|
+
echo "Re-installing (previous directory had no .git)..."
|
|
27
|
+
rm -rf "$INSTALL_DIR"
|
|
28
|
+
git clone "$REPO_URL" "$INSTALL_DIR"
|
|
29
|
+
cd "$INSTALL_DIR"
|
|
30
|
+
else
|
|
31
|
+
echo "Cloning repository..."
|
|
32
|
+
git clone "$REPO_URL" "$INSTALL_DIR"
|
|
33
|
+
cd "$INSTALL_DIR"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# 2. Install dependencies
|
|
37
|
+
echo "Installing dependencies..."
|
|
38
|
+
npm install
|
|
39
|
+
|
|
40
|
+
# 3. Build
|
|
41
|
+
echo "Building..."
|
|
42
|
+
npm run build
|
|
43
|
+
|
|
44
|
+
# 4. Register MCP server in ~/.claude.json
|
|
45
|
+
echo "Configuring Claude Code..."
|
|
46
|
+
|
|
47
|
+
if [ ! -f "$CLAUDE_JSON" ]; then
|
|
48
|
+
cat > "$CLAUDE_JSON" << EOF
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"jui-tools": {
|
|
52
|
+
"command": "$NODE_BIN",
|
|
53
|
+
"args": ["$INSTALL_DIR/dist/index.js"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
EOF
|
|
58
|
+
else
|
|
59
|
+
node -e "
|
|
60
|
+
const fs = require('fs');
|
|
61
|
+
const data = JSON.parse(fs.readFileSync('$CLAUDE_JSON', 'utf8'));
|
|
62
|
+
if (!data.mcpServers) data.mcpServers = {};
|
|
63
|
+
// Remove old entry if exists
|
|
64
|
+
if (data.mcpServers['jsonui-spec']) {
|
|
65
|
+
delete data.mcpServers['jsonui-spec'];
|
|
66
|
+
console.log('Removed old jsonui-spec entry.');
|
|
67
|
+
}
|
|
68
|
+
data.mcpServers['jui-tools'] = {
|
|
69
|
+
command: '$NODE_BIN',
|
|
70
|
+
args: ['$INSTALL_DIR/dist/index.js']
|
|
71
|
+
};
|
|
72
|
+
fs.writeFileSync('$CLAUDE_JSON', JSON.stringify(data, null, 2));
|
|
73
|
+
"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
echo ""
|
|
77
|
+
echo "Installation complete!"
|
|
78
|
+
echo " Installed to: $INSTALL_DIR"
|
|
79
|
+
echo " Server path: $INSTALL_DIR/dist/index.js"
|
|
80
|
+
echo " Node: $NODE_BIN"
|
|
81
|
+
echo " Config: $CLAUDE_JSON"
|
|
82
|
+
echo ""
|
|
83
|
+
echo "To set a default project directory, add env to your MCP config:"
|
|
84
|
+
echo ' "jui-tools": {'
|
|
85
|
+
echo ' "command": "'$NODE_BIN'",'
|
|
86
|
+
echo ' "args": ["'$INSTALL_DIR'/dist/index.js"],'
|
|
87
|
+
echo ' "env": { "JUI_PROJECT_DIR": "/path/to/your/project" }'
|
|
88
|
+
echo ' }'
|
|
89
|
+
echo ""
|
|
90
|
+
echo "Restart Claude Code to activate the MCP server."
|