chore(mcp): add js tool verification
This commit is contained in:
parent
d688006958
commit
a4a098e923
51
Justfile
51
Justfile
@ -6,7 +6,7 @@ default:
|
||||
@just --list
|
||||
|
||||
# Install local development tooling.
|
||||
install: install-git-hooks check-install generate-env
|
||||
install: install-git-hooks check-install npm-install generate-env
|
||||
|
||||
# Remove generated local environment files.
|
||||
clean:
|
||||
@ -112,6 +112,33 @@ generate-env:
|
||||
done < .env.test > .env
|
||||
printf '%s\n' 'Generated .env'
|
||||
|
||||
# Install Node dependencies for repository tools.
|
||||
npm-install:
|
||||
npm install --prefix tools/mcp-bridge
|
||||
|
||||
# Build Node-based repository tools.
|
||||
npm-build:
|
||||
npm run build --prefix tools/mcp-bridge
|
||||
|
||||
# Check Node-based repository tools.
|
||||
npm-check:
|
||||
npm run check --prefix tools/mcp-bridge
|
||||
|
||||
# Run Node-based tool tests.
|
||||
npm-test:
|
||||
npm test --prefix tools/mcp-bridge
|
||||
|
||||
# Run Node-based tool integration tests.
|
||||
npm-test-integration:
|
||||
npm run test-integration --prefix tools/mcp-bridge
|
||||
|
||||
# Run Node-based tool CI.
|
||||
npm-ci:
|
||||
npm run ci --prefix tools/mcp-bridge
|
||||
|
||||
# Build generated artifacts.
|
||||
build: npm-build
|
||||
|
||||
# Pass args through to `craftos`. Prefer `just trapos-exec '<lua>'` for
|
||||
# automated probes that must not hang the terminal.
|
||||
# Launch the TrapOS dev environment in CraftOS-PC with repo-local data
|
||||
@ -392,14 +419,22 @@ opencode-attach *args:
|
||||
fi
|
||||
exec opencode attach "$target" "$@"
|
||||
|
||||
# Local CI entry point used by Git hooks. Pass args through to `test`.
|
||||
ci *args: check-craftos check check-packages
|
||||
@just test {{args}}
|
||||
@just test-timeout
|
||||
# Local CI entry point used by Git hooks. Pass args through to CraftOS tests.
|
||||
ci *args: check-craftos check check-packages npm-ci
|
||||
@just _craftos-test {{args}}
|
||||
@just test-integration
|
||||
|
||||
# Run all standard tests. Pass `--pretty` for grouped CraftOS output.
|
||||
[positional-arguments]
|
||||
test *args: build npm-test
|
||||
@just _craftos-test {{args}}
|
||||
|
||||
# Run integration and harness tests that are too broad for unit test targets.
|
||||
test-integration: npm-test-integration test-timeout
|
||||
|
||||
# Run CraftOS-PC headless integration tests. Pass `--pretty` for grouped output.
|
||||
[positional-arguments]
|
||||
test *args:
|
||||
_craftos-test *args:
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
|
||||
@ -541,8 +576,8 @@ test-timeout-shell: (_timeout-fixture "/tests/harness/slow-case.lua" "${TRAP_CCL
|
||||
# Fast regression guard for both timeout layers. Wired into `ci`.
|
||||
test-timeout: test-timeout-lua test-timeout-shell
|
||||
|
||||
# Lint all Lua source with luacheck and validate markdown links.
|
||||
check: check-luacheck check-lychee
|
||||
# Lint Lua/TypeScript source and validate markdown links.
|
||||
check: check-luacheck check-lychee npm-check
|
||||
luacheck --quiet .
|
||||
@just lint-markdown
|
||||
|
||||
|
||||
@ -14,5 +14,6 @@ Future ADRs can reuse the shape of the existing files when it is useful.
|
||||
- [`adr-0007-test-framework.md`](adr-0007-test-framework.md) — `libtest` per-case helper, `runtest` suite orchestration, and the two-layer timeout (libtest + shell watchdog).
|
||||
- [`adr-0010-ccpm-package-manager.md`](adr-0010-ccpm-package-manager.md) — `ccpm` package manager (packages, registries, package-aware bootstrap).
|
||||
- [`adr-0011-repo-conventions.md`](adr-0011-repo-conventions.md) — Git hooks own commit/push verification; markdown link syntax for cross-references.
|
||||
- [`adr-0016-js-tool-verification.md`](adr-0016-js-tool-verification.md) — JavaScript/TypeScript tool build, check, test, CI, and future integration-test split.
|
||||
|
||||
Gaps in numbering (0003, 0004, 0006, 0008, 0009, 0012, 0013, 0014, 0015) are records that were either superseded by later decisions or consolidated into the surviving ADRs above.
|
||||
|
||||
45
docs/adrs/adr-0016-js-tool-verification.md
Normal file
45
docs/adrs/adr-0016-js-tool-verification.md
Normal file
@ -0,0 +1,45 @@
|
||||
# ADR 0016: JavaScript Tool Verification
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-06-10
|
||||
|
||||
## Context
|
||||
|
||||
The repository now contains `tools/mcp-bridge`, a TypeScript Node.js tool that sits next to the ComputerCraft Lua code. It has a different build and test lifecycle than TrapOS packages, but it still participates in the same local developer workflow and Git hooks described by [ADR-0011](adr-0011-repo-conventions.md).
|
||||
|
||||
The bridge also needs future end-to-end coverage that spans both runtimes: a host Node process and a headless CraftOS-PC computer. That is broader and slower than normal unit tests, and it needs the same kind of timeout discipline as the Lua harness in [ADR-0007](adr-0007-test-framework.md).
|
||||
|
||||
## Decision
|
||||
|
||||
Keep the Node package scripts simple and one-purpose:
|
||||
|
||||
- `npm run build` emits TypeScript into `dist/`.
|
||||
- `npm run test` runs only the already-built Node unit tests.
|
||||
- `npm run check` runs ESLint. TypeScript compilation is covered by `npm run build` and `npm run ci` to avoid duplicate compiler runs in repository CI.
|
||||
- `npm run ci` runs `npm run build && npm run test`.
|
||||
- `npm run test-integration` is reserved for bridge-to-CraftOS integration coverage and currently prints an explicit TODO.
|
||||
|
||||
Expose matching repository recipes for the Node lifecycle:
|
||||
|
||||
- `just build` delegates to `npm run build` for the bridge.
|
||||
- `just test` depends on `just build`, runs `npm run test`, then runs the existing CraftOS-PC test suite.
|
||||
- `just check` includes `npm run check` alongside Lua and Markdown checks.
|
||||
- `just ci` uses `npm run ci`, then runs CraftOS-PC tests and the broader integration/harness target.
|
||||
- `just test-integration` runs the placeholder Node integration target and the Lua timeout harness checks.
|
||||
|
||||
## Consequences
|
||||
|
||||
- `npm run test` no longer hides a build step. Callers that need a fresh build must use `npm run ci` or `just test`.
|
||||
- `just ci` avoids duplicating Node unit tests by calling `npm run ci` directly and then invoking only the CraftOS-side test body.
|
||||
- ESLint failures are part of `just check`, so they are covered by the same pre-commit and pre-push hooks as Lua and Markdown checks. TypeScript compiler failures are covered by `just build`, `just test`, and `just ci`.
|
||||
- Future bridge integration tests have a named home before they exist, reducing the chance that slow end-to-end behavior is mixed into fast unit tests.
|
||||
|
||||
## Future Work
|
||||
|
||||
- Replace the `npm run test-integration` placeholder with a harness that launches the bridge and CraftOS-PC headless, connects a websocket client from CraftOS, probes through the MCP surface, and tears down both processes reliably.
|
||||
- Give the bridge integration harness host-level timeout handling and readable diagnostics modelled on the CraftOS-PC test recipes.
|
||||
26
tools/mcp-bridge/eslint.config.js
Normal file
26
tools/mcp-bridge/eslint.config.js
Normal file
@ -0,0 +1,26 @@
|
||||
import js from "@eslint/js";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ["dist/**"] },
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: globals.node,
|
||||
parserOptions: {
|
||||
projectService: {
|
||||
allowDefaultProject: ["eslint.config.js"],
|
||||
},
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["test/**/*.ts"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-floating-promises": "off",
|
||||
},
|
||||
},
|
||||
);
|
||||
1231
tools/mcp-bridge/package-lock.json
generated
1231
tools/mcp-bridge/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,18 +5,25 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit false",
|
||||
"check": "eslint .",
|
||||
"ci": "npm run build && npm run test",
|
||||
"dev": "tsx src/index.ts",
|
||||
"start": "node dist/src/index.js",
|
||||
"test": "npm run build && node --test dist/test/*.test.js"
|
||||
"test": "node --test dist/test/*.test.js",
|
||||
"test-integration": "node -e \"console.log('TODO: add mcp-bridge integration tests that launch CraftOS-PC headless, start the bridge, connect a ComputerCraft websocket client from inside CraftOS, and exercise the MCP probe path end-to-end. The intended harness should verify startup ordering, websocket hello/response framing, timeout behavior when a computer is silent, and cleanup of both the CraftOS process and Node bridge server. Keep this separate from unit tests because it will need host ports, a CraftOS watchdog, and careful failure diagnostics similar to the existing Lua timeout fixtures.')\""
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": "^8.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@types/node": "^20.14.10",
|
||||
"@types/ws": "^8.5.10",
|
||||
"eslint": "^10.4.1",
|
||||
"globals": "^17.6.0",
|
||||
"tsx": "^4.16.2",
|
||||
"typescript": "^5.5.3"
|
||||
"typescript": "^5.5.3",
|
||||
"typescript-eslint": "^8.61.0"
|
||||
},
|
||||
"allowScripts": {
|
||||
"esbuild@0.28.0": true,
|
||||
|
||||
@ -117,7 +117,7 @@ function writeJson(res: ServerResponse, statusCode: number, body: unknown): void
|
||||
async function readBody(req: IncomingMessage): Promise<string> {
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of req) {
|
||||
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
||||
}
|
||||
return Buffer.concat(chunks).toString("utf8");
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ export type ResponseMessage = {
|
||||
|
||||
export type LinkMessage = HelloMessage | ResponseMessage;
|
||||
|
||||
export function parseJsonFrame(data: unknown): unknown | null {
|
||||
export function parseJsonFrame(data: unknown): unknown {
|
||||
if (typeof data === "string") {
|
||||
return parseJson(data);
|
||||
}
|
||||
@ -76,7 +76,7 @@ export function formatComputer(computerId: number, label: string | null): string
|
||||
return `${computerId} (Label: ${label ?? "null"})`;
|
||||
}
|
||||
|
||||
function parseJson(text: string): unknown | null {
|
||||
function parseJson(text: string): unknown {
|
||||
try {
|
||||
return JSON.parse(text) as unknown;
|
||||
} catch {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user