🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@labwired/mcp

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@labwired/mcp - npm Package Compare versions

Comparing version
0.6.0
to
0.7.0
+1
-1
dist/boards.js
/**
* On-disk example-lab catalog for the high-level `labwired_list_boards` /
* On-disk example-lab catalog for the high-level `labwired_list` /
* `labwired_run` (a.k.a. the deprecated `labwired_run_lab`) tools.

@@ -4,0 +4,0 @@ *

@@ -195,2 +195,55 @@ import { execFile } from 'node:child_process';

}
export async function compileViaBuilder(opts) {
const base = builderBaseUrl();
if (!base) {
return {
ok: false,
diagnostics: [],
error: 'No builder URL configured. Set LABWIRED_COMPILE_URL (or LABWIRED_BUILDER_URL) ' +
'to the labwired-builder base URL so firmware can compile server-side.',
};
}
const secret = process.env.LABWIRED_BUILDER_SECRET ?? process.env.BUILDER_SECRET ?? '';
const headers = { 'content-type': 'application/json' };
if (secret)
headers['x-builder-secret'] = secret;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 600_000);
try {
const resp = await fetch(`${base}/compile`, {
method: 'POST',
headers,
body: JSON.stringify({
source: opts.source,
board: opts.board,
language: opts.language,
files: opts.files,
lib_deps: opts.lib_deps,
}),
signal: controller.signal,
});
const data = (await resp.json());
return {
ok: Boolean(data.ok),
...(data.ok && data.elfBase64 ? { elf_base64: data.elfBase64 } : {}),
runnable: data.runnable ?? false,
...(data.ok && data.flashImages?.length
? { flash_images: data.flashImages, flash_chip: data.flashChip }
: {}),
diagnostics: data.diagnostics ?? [],
...(data.error ? { error: data.error } : {}),
...(data.ok ? {} : { detail: 'Firmware did not compile. Fix the diagnostics below and retry.' }),
};
}
catch (e) {
return {
ok: false,
diagnostics: [],
error: `builder /compile unreachable: ${e instanceof Error ? e.message : String(e)}`,
};
}
finally {
clearTimeout(timer);
}
}
export async function validateSystem(systemYaml) {

@@ -197,0 +250,0 @@ const work = await mkdtemp(join(tmpdir(), 'labwired-mcp-validate-'));

@@ -9,7 +9,7 @@ #!/usr/bin/env node

import { join, resolve } from 'node:path';
import { listChips, validateSystem, runLab, fuzzFirmware, runCli, runDeviceCapture, runBuildViaBuilder } from './cli.js';
import { validateSystem, runLab, fuzzFirmware, runCli, runDeviceCapture, runBuildViaBuilder, compileViaBuilder } from './cli.js';
import { listBoards, getBoard, readBoardYamls, boardSystemYamlPath, boardChipYamlPath, } from './boards.js';
import { putSnapshot, getSnapshot } from './snapshots.js';
import { compile, composeDiagnostics, describeBoard, evaluateOracle, oracleClauseCount } from '@labwired/board-config';
import { AGENT_HARDWARE_LOOP_GUIDE_URI, LOCAL_AGENT_WORKFLOW, SEARCH_TOOLS_TOOL, SEARCH_TOOLS_TOOL_NAME, rankTools, } from './search-tools.js';
import { composeDiagnostics, describeBoard, describeComponent, componentPinCount, evaluateOracle, oracleClauseCount, toolsForSurface, CATALOG, } from '@labwired/board-config';
import { AGENT_HARDWARE_LOOP_GUIDE_URI, LOCAL_AGENT_WORKFLOW, SEARCH_TOOLS_TOOL_NAME, rankTools, } from './search-tools.js';
import { decorateTools } from './tool-metadata.js';

@@ -19,15 +19,3 @@ import { RESOURCES, getResource } from './resources.js';

const SERVER_NAME = '@labwired/mcp';
const SERVER_VERSION = '0.5.0';
const CatalogInput = z.object({
filter: z
.string()
.optional()
.describe('Optional substring filter applied to chip/board names.'),
});
const ValidateInput = z.object({
system_yaml: z.string().describe('Full contents of the System Manifest YAML to validate.'),
});
const ListBoardsInput = z.object({
filter: z.string().optional().describe('Substring filter on id / name / chip family.'),
});
const SERVER_VERSION = '0.7.0';
const RunLabInput = z.object({

@@ -37,7 +25,7 @@ target: z

.optional()
.describe('Board id / target from labwired_list_boards, e.g. "stm32f103-blinky". Canonical name for board_id.'),
.describe('Board id / target from labwired_list, e.g. "stm32f103-blinky". Canonical name for board_id.'),
board_id: z
.string()
.optional()
.describe('Deprecated alias for `target` (board id from labwired_list_boards).'),
.describe('Deprecated alias for `target` (board id from labwired_list).'),
elf_base64: z

@@ -50,3 +38,3 @@ .string()

.optional()
.describe('Content-addressed firmware ref (`sha256:<hex>` from labwired_compile_firmware). Resolved from ' +
.describe('Content-addressed firmware ref (`sha256:<hex>` from labwired_compile). Resolved from ' +
'the hosted blob store — no base64 to carry. Used when elf_base64 is omitted.'),

@@ -87,9 +75,4 @@ max_cycles: z

}).refine((v) => Boolean(v.target || v.board_id), {
message: 'Provide `target` (or the legacy `board_id`) — a board id from labwired_list_boards.',
message: 'Provide `target` (or the legacy `board_id`) — a board id from labwired_list.',
});
const DescribeBoardInput = z.object({
board: z
.string()
.describe('Any accepted board/chip spelling (canonical board id, playground id, or chip family key).'),
});
// ─── Oracle / unmodeled-access helpers (mcp-unification §c) ─────────────────

@@ -132,3 +115,3 @@ // A minimal "execute and capture UART" test script for the oracle-block verify

.string()
.describe('Board id from labwired_list_boards (resolves chip + system YAML).'),
.describe('Board id from labwired_list (resolves chip + system YAML).'),
elf_base64: z

@@ -174,16 +157,2 @@ .string()

});
const ValidateDiagramInput = z.object({
diagram: z
.object({
board: z.string(),
parts: z.array(z.object({ id: z.string(), type: z.string() }).passthrough()),
// wires is optional: v2 diagrams may use only connections/nets
wires: z.array(z.object({
from: z.object({ part: z.string(), pin: z.string() }),
to: z.object({ part: z.string(), pin: z.string() }),
})).optional(),
})
.passthrough()
.describe('Diagram JSON: { board, parts: [{id, type, ...}], wires?: [{from, to}] }.'),
});
// ─── Workspace root (mirrors boards.ts resolution logic) ──────────────────

@@ -218,359 +187,19 @@ // Checked in order: LABWIRED_REPO_ROOT env, then walk up from __dirname.

const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {}, resources: {} } });
function localTools() {
return [
SEARCH_TOOLS_TOOL,
{
name: 'labwired_catalog',
description: "List supported chips/boards in the LabWired catalog. Returns name, family, " +
'architecture, pass rate, and verification status. Use this to discover what targets ' +
'are available before running a simulation. Free; no API key required.',
inputSchema: {
type: 'object',
properties: {
filter: {
type: 'string',
description: 'Optional substring filter applied to chip/board names.',
},
},
},
},
{
name: 'labwired_run_device',
description: 'Boot an Arduino-ESP32 firmware ELF on simulated ESP32 hardware and report what the attached ' +
'device actually did. The peripherals (e.g. an SSD1680/UC8151D e-paper panel and its CS/DC ' +
'pins) are attached from the system_yaml manifest. Works for ANY symbol-bearing Arduino-ESP32 ' +
'build (agent-compiled firmware always has symbols). Use this for real firmware driving a ' +
'display — labwired_verify runs the bare test path which does NOT boot Arduino-ESP32 and will ' +
'fault. Returns whether the panel painted, its refresh generation, and how much ink reached it ' +
'(the deterministic hardware oracle, byte-exact to silicon).',
inputSchema: {
type: 'object',
required: ['firmware_base64', 'system_yaml'],
properties: {
firmware_base64: { type: 'string', description: 'Base64-encoded Arduino-ESP32 (Xtensa) ELF.' },
system_yaml: { type: 'string', description: 'System Manifest YAML — chip + connected peripherals (e.g. an e-paper panel on spi3 with CS/DC pins).' },
steps: { type: 'integer', description: 'Cycles to run (default 45,000,000; max 50,000,000).' },
},
},
},
{
name: 'labwired_validate_system',
description: 'Validate a System Manifest YAML against the LabWired schema and confirm referenced ' +
'chip descriptors exist. Returns the validator stdout / stderr. ' +
'Use this before simulate() to catch schema errors fast.',
inputSchema: {
type: 'object',
required: ['system_yaml'],
properties: {
system_yaml: {
type: 'string',
description: 'Full contents of the System Manifest YAML to validate.',
},
},
},
},
{
name: 'labwired_list_boards',
description: 'List supported boards (chip + pre-wired peripherals + demo firmware). Higher-level ' +
'than labwired_catalog: each board has a complete simulation config you can run with ' +
'labwired_run. Returns id, name, chip family, arch, and description for each.',
inputSchema: {
type: 'object',
properties: {
filter: {
type: 'string',
description: 'Substring filter on id / name / chip family.',
},
},
},
},
{
name: 'labwired_describe_board',
description: 'Describe a board/chip: its canonical id, chip family, runnable flag, toolchains, the full ' +
'pin table, per-bus (I2C/SPI/UART) default pins, and a starter LED pin. Use this FIRST for ' +
'discovery — it kills target-name guessing and tells you which GPIO to wire each bus to ' +
'before building a diagram or firmware. Accepts any spelling (canonical id, playground id, ' +
'or bare chip key); resolution is normalized. Free; no run required.',
inputSchema: {
type: 'object',
required: ['board'],
properties: {
board: {
type: 'string',
description: 'Any accepted board/chip spelling, e.g. "esp32c3-supermini" or "esp32c3".',
},
},
},
},
{
name: 'labwired_run',
description: 'Run a firmware ELF against a pre-configured board and OBSERVE what it did (serial, exit ' +
'reason, cycles). THE canonical execution tool. Pick a board with labwired_list_boards, ' +
"compile locally (we don't compile for you), then upload the ELF. Observational: it reports " +
'behaviour but never mints a "verified" verdict — use labwired_verify with an oracle for a ' +
'gated pass. Returns a snapshot_id for labwired_inspect_run. Deterministic. `output` selects ' +
'the response shape; `stimuli` drives a sensor mid-run.',
inputSchema: {
type: 'object',
properties: {
target: {
type: 'string',
description: 'Board id / target from labwired_list_boards (e.g. "stm32f103-blinky").',
},
board_id: {
type: 'string',
description: 'Deprecated alias for `target`.',
},
elf_base64: {
type: 'string',
description: 'Base64-encoded ELF binary. Agent compiles locally. Optional if firmware_ref is given.',
},
firmware_ref: {
type: 'string',
description: 'Content-addressed firmware ref (`sha256:<hex>` from labwired_compile_firmware); resolved ' +
'from the hosted blob store, so there is no base64 to carry. Used when elf_base64 is omitted.',
},
max_cycles: {
type: 'integer',
description: 'Cycle budget (default 10M, hard cap 100M).',
},
output: {
type: 'string',
enum: ['summary', 'serial', 'peripherals', 'full'],
description: 'Response-shape selector. Default full.',
},
stimuli: {
type: 'array',
description: 'Declarative input stimuli — drive a sensor mid-run, then keep going.',
items: {
type: 'object',
required: ['channel', 'value'],
properties: {
channel: { type: 'string' },
value: { type: 'number' },
after_cycles: { type: 'integer' },
component: { type: 'string' },
},
},
},
},
},
},
{
name: 'labwired_fuzz',
description: 'Coverage-guided fuzz a firmware ELF in the silicon-validated simulator and return the ' +
'crashing inputs. AFL-style edge coverage drives mutation; a crash is a CPU fault or the ' +
"firmware's FAULT marker. Because the sim is silicon-validated, crashes found here are " +
'replayable on real hardware (HIL-confirm) — silicon-true findings, not emulation false ' +
'positives. The target firmware must follow the fuzz contract (RAM length+data input ' +
'buffer, a verdict word with DONE/FAULT markers). Deterministic for a fixed seed. Returns ' +
'the distinct crashing inputs (as byte arrays) you can replay or minimize.',
inputSchema: {
type: 'object',
required: ['board_id', 'elf_base64'],
properties: {
board_id: {
type: 'string',
description: 'Board id from labwired_list_boards (resolves chip + system YAML).',
},
elf_base64: {
type: 'string',
description: 'Base64-encoded fuzz-target ELF following the fuzz contract. Agent compiles locally.',
},
max_iters: {
type: 'integer',
description: 'Max fuzzing iterations (default 200000).',
},
seed: {
type: 'integer',
description: 'RNG seed — fuzzing is deterministic for a fixed seed.',
},
collect: {
type: 'integer',
description: 'Collect up to N distinct crashes (default 8).',
},
seed_inputs_hex: {
type: 'array',
items: { type: 'string' },
description: 'Seed inputs as hex byte strings, e.g. ["5000"].',
},
contract: {
type: 'object',
description: 'Override the fuzz contract (hex strings). Defaults match the F103 fuzz target: ' +
'input_len_addr 0x20002800, input_data_addr 0x20002804, verdict_addr 0x20003000, ' +
'done_magic 0xC0DEF022, fault_magic 0xDEADFA17.',
properties: {
input_len_addr: { type: 'string' },
input_data_addr: { type: 'string' },
verdict_addr: { type: 'string' },
done_magic: { type: 'string' },
fault_magic: { type: 'string' },
},
},
},
},
},
{
name: 'labwired_inspect_run',
description: 'Retrieve a deeper slice of state from a prior labwired_run snapshot. Use scope=summary ' +
'for cycles + PC + exit reason, scope=serial for full UART transcript, scope=gpio for pin ' +
'transitions (when CLI emits them), scope=raw for the underlying result.json. Snapshots ' +
'expire after 10 min.',
inputSchema: {
type: 'object',
required: ['snapshot_id'],
properties: {
snapshot_id: {
type: 'string',
description: 'snapshot_id from labwired_run output.',
},
scope: {
type: 'string',
enum: ['summary', 'serial', 'gpio', 'raw'],
description: 'Slice to return. Default summary.',
},
},
},
},
{
name: 'labwired_validate_diagram',
description: 'Structurally validate a wired diagram BEFORE attempting to run it. Returns an array of ' +
'machine-readable diagnostics (severity, code, message, location, suggested fix) — much ' +
'friendlier than waiting for the simulator to fail at run time. Common codes: ' +
'PIN_NOT_ON_CHIP, PIN_LACKS_I2C, BOARDIO_NOT_TO_MCU, NO_MCU, COMPONENT_DANGLING. Use this ' +
'when building circuits programmatically; iterate until you get back an empty array.',
inputSchema: {
type: 'object',
required: ['diagram'],
properties: {
diagram: {
type: 'object',
description: 'Diagram JSON: { board: "stm32f103", parts: [{id, type}], wires: [{from:{part,pin}, to:{part,pin}}] }',
},
},
},
},
{
name: 'labwired_define_component',
description: 'Define a new off-chip device from a declarative IR spec (YAML). Validates the spec ' +
'(stable ICOMP_* diagnostic codes with hints), persists it under ' +
'.labwired/components/<name>.yaml, and returns the exact manifest external_devices ' +
'entry (type: ir, spec_path) needed to wire the device into a system manifest. ' +
'Use this to model any custom sensor, driver, or expander before simulation. ' +
'Keywords: define component, ir spec, custom device, sensor model.',
inputSchema: {
type: 'object',
required: ['spec_yaml'],
properties: {
spec_yaml: {
type: 'string',
description: 'IrComponent spec as YAML',
},
name: {
type: 'string',
description: 'Override file name (defaults to spec name, kebab-cased)',
},
},
},
},
{
name: 'labwired_compile_diagram',
description: 'Compile a wired diagram into a runnable LabWired System Manifest YAML. ' +
'Runs electrical rule checks (ERC) first — any error aborts compilation. ' +
'On success, persists the manifest to <repoRoot>/.labwired/boards/<name>.yaml ' +
'and returns the board_path, system_yaml, and any warnings. ' +
'Use this after labwired_validate_diagram to produce the manifest for labwired_verify. ' +
'Keywords: compile diagram, diagram to manifest, build board.',
inputSchema: {
type: 'object',
required: ['diagram'],
properties: {
diagram: {
type: 'object',
description: 'Diagram JSON: { board, parts: [{id, type, ...}], wires: [{from, to}] }',
},
name: {
type: 'string',
description: 'Optional name for the output file (defaults to board name). Kebab-cased.',
},
},
},
},
{
name: 'labwired_verify',
description: 'THE mandatory-oracle gate: run YOUR OWN build server-side and PROVE it worked. You SUPPLY ' +
'the compiled firmware ELF (base64) + the LabWired system manifest (YAML), plus a passed ' +
'behavioral proof — EITHER a raw `test_yaml` (schema "1.0" assertions, the reference power ' +
'path, evaluated by `labwired test` inside the container) OR an `oracle` block with at least ' +
'one clause (serial / display / registers / gpio), which is evaluated by the shared ' +
'evaluateOracle. Deterministic oracle run on the modeled hardware, not a self-reported claim. ' +
'A missing oracle AND missing test_yaml → ORACLE_REQUIRED (refuses to verify). ' +
'On the stdio path only `serial` (UART) and `display` clauses are observable; register/gpio ' +
'clauses need the hosted /run peripherals path and are flagged unevaluable. ' +
'Returns { ok, proven, verification, status, oracle_results[], assertions[], uart_excerpt }.',
inputSchema: {
type: 'object',
required: ['system_yaml'],
properties: {
firmware_base64: {
type: 'string',
description: 'Base64-encoded compiled firmware ELF for your build. Optional if firmware_ref is given.',
},
firmware_ref: {
type: 'string',
description: 'Content-addressed firmware ref (`sha256:<hex>` from labwired_compile_firmware); resolved ' +
'from the hosted blob store, so there is no base64 to carry. Used when firmware_base64 is omitted.',
},
system_yaml: {
type: 'string',
description: 'LabWired system manifest (YAML) describing the modeled hardware your firmware runs on.',
},
test_yaml: {
type: 'string',
description: 'Optional reference power path: LabWired test script (YAML, schema_version "1.0") with limits + assertions. ' +
'Its inputs.firmware / inputs.system are overridden by the supplied firmware + manifest.',
},
oracle: {
type: 'object',
description: 'Behavioral proof (at least one clause). { serial: [{contains|matches}], ' +
'display: {painted, min_ink_bytes, min_refresh_generation, panel}, ' +
'registers: [{peripheral, register, field?, equals, mask?}], gpio: [{pin, state}] }.',
properties: {
serial: { type: 'array', items: { type: 'object' } },
display: { type: 'object' },
registers: { type: 'array', items: { type: 'object' } },
gpio: { type: 'array', items: { type: 'object' } },
},
},
},
},
},
{
name: 'labwired_ingest_svd',
description: 'Ingest a CMSIS-SVD file into runnable declarative PeripheralDescriptor YAML — the ' +
'one-step path from a silicon-vendor SVD to a working chip, with no codegen and no ' +
'recompile. Writes descriptors under .labwired/peripherals/<name>.yaml and returns each ' +
'peripheral with its descriptor YAML, base address, and register count, plus a ' +
'paste-ready chip-yaml peripherals block (type: declarative) to drop straight into a ' +
'chip descriptor. Use this to model any MCU peripheral from its vendor SVD. ' +
'Keywords: svd import, declarative peripheral, chip from svd, register map.',
inputSchema: {
type: 'object',
required: ['svd_content'],
properties: {
svd_content: {
type: 'string',
description: 'Full CMSIS-SVD XML contents.',
},
filter: {
type: 'string',
description: 'Comma-separated peripheral names to ingest (default: all).',
},
},
},
},
];
// The advertised stdio surface is built from the single MCP tool registry
// (@labwired/board-config), decorated with stdio titles/annotations — so the
// advertised names/schemas can never drift from dispatch or from the hosted
// surface. A drift-guard test asserts tools/list == toolsForSurface('stdio').
const STDIO_TOOLS = decorateTools(toolsForSurface('stdio'));
/** Component library listing (mirrors the hosted labwired_list): every catalog
* part as { type, class, pin_count }, optionally substring-filtered on type. */
function listComponents(filter) {
return Object.keys(CATALOG)
.filter((type) => !filter || type.includes(filter))
.map((type) => {
const descriptor = describeComponent(type);
return { type, class: descriptor.class, pin_count: componentPinCount(descriptor) };
});
}
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: decorateTools(localTools()),
tools: STDIO_TOOLS,
}));

@@ -600,63 +229,102 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => ({

workflow: LOCAL_AGENT_WORKFLOW,
tools: rankTools(query, decorateTools(localTools()), limit),
tools: rankTools(query, STDIO_TOOLS, limit),
}) }],
};
}
if (name === 'labwired_catalog') {
const { filter } = CatalogInput.parse(args ?? {});
const chips = await listChips(filter);
return {
content: [{ type: 'text', text: JSON.stringify(chips, null, 2) }],
};
// Folds the old labwired_catalog + labwired_list. `kind` narrows the
// listing (board | mcu | component); omit for everything.
if (name === 'labwired_list') {
const input = (args ?? {});
const kind = input.kind === 'board' || input.kind === 'mcu' || input.kind === 'component'
? input.kind
: undefined;
const filter = typeof input.filter === 'string' && input.filter ? input.filter : undefined;
const out = {};
if (!kind || kind === 'board') {
out.boards = listBoards(filter);
}
if (!kind || kind === 'component' || kind === 'mcu') {
const components = listComponents(filter);
out.components = kind === 'mcu' ? components.filter((c) => c.class === 'mcu') : components;
}
return { content: [{ type: 'text', text: JSON.stringify(out, null, 2) }] };
}
if (name === 'labwired_run_device') {
// Renamed from labwired_describe. Accepts `id` (registry schema) or the
// legacy `board` key. A board/MCU/chip spelling → the rich board descriptor;
// a peripheral/passive type → the flat component descriptor.
if (name === 'labwired_describe') {
const a = (args ?? {});
if (!a.firmware_base64 || !a.system_yaml) {
return { content: [{ type: 'text', text: JSON.stringify({ error: 'firmware_base64 and system_yaml are required' }) }], isError: true };
const id = (typeof a.id === 'string' ? a.id : typeof a.board === 'string' ? a.board : '').trim();
if (!id) {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'INVALID_ARGS', message: 'id is required.' }, null, 2) }],
isError: true,
};
}
const run = await runDeviceCapture({ firmwareBase64: a.firmware_base64, systemYaml: a.system_yaml, steps: a.steps });
const descriptor = describeBoard(id);
if (descriptor) {
return { content: [{ type: 'text', text: JSON.stringify(descriptor, null, 2) }] };
}
const component = describeComponent(id);
if (component) {
return { content: [{ type: 'text', text: JSON.stringify(component, null, 2) }] };
}
return {
content: [{ type: 'text', text: JSON.stringify(run, null, 2) }],
isError: !run.painted,
};
}
if (name === 'labwired_validate_system') {
const { system_yaml } = ValidateInput.parse(args ?? {});
const result = await validateSystem(system_yaml);
return {
content: [
{
type: 'text',
text: JSON.stringify({ exit_code: result.exitCode, stdout: result.stdout, stderr: result.stderr }, null, 2),
text: JSON.stringify({
error: 'UNKNOWN_BOARD',
message: `No board/chip/component matches "${id}". Call labwired_list for valid ids.`,
}, null, 2),
},
],
isError: result.exitCode !== 0,
isError: true,
};
}
if (name === 'labwired_list_boards') {
const { filter } = ListBoardsInput.parse(args ?? {});
const boards = listBoards(filter);
return {
content: [{ type: 'text', text: JSON.stringify({ boards }, null, 2) }],
};
}
if (name === 'labwired_describe_board') {
const { board } = DescribeBoardInput.parse(args ?? {});
const descriptor = describeBoard(board);
if (!descriptor) {
// Folds the old labwired_validate_system + labwired_validate_diagram. A
// `diagram` runs the ERC/pin diagnostics; a `system_yaml` runs the schema
// validator. Diagram wins if both are present.
if (name === 'labwired_validate') {
const a = (args ?? {});
if (a.diagram && typeof a.diagram === 'object' && !Array.isArray(a.diagram)) {
const result = composeDiagnostics(a.diagram);
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
isError: result.error_count > 0,
};
}
if (typeof a.system_yaml === 'string' && a.system_yaml.trim()) {
const result = await validateSystem(a.system_yaml);
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: 'UNKNOWN_BOARD',
message: `No board/chip matches "${board}". Call labwired_list_boards for valid ids.`,
}, null, 2),
text: JSON.stringify({ exit_code: result.exitCode, stdout: result.stdout, stderr: result.stderr }, null, 2),
},
],
isError: true,
isError: result.exitCode !== 0,
};
}
return { content: [{ type: 'text', text: JSON.stringify(descriptor, null, 2) }] };
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'INVALID_ARGS', message: 'Provide a diagram (JSON) or a system_yaml (string) to validate.' }, null, 2) }],
isError: true,
};
}
// Compile C/C++ firmware → ELF on the hosted PlatformIO toolchain (builder
// /compile). Same standardized tool as the hosted + builder surfaces.
if (name === 'labwired_compile') {
const a = (args ?? {});
const result = await compileViaBuilder({
source: typeof a.source === 'string' ? a.source : undefined,
board: typeof a.board === 'string' ? a.board : undefined,
language: typeof a.language === 'string' ? a.language : undefined,
files: a.files,
lib_deps: a.lib_deps,
});
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
isError: !result.ok,
};
}
if (name === 'labwired_run') {

@@ -674,3 +342,3 @@ const input = RunLabInput.parse(args ?? {});

error: 'INVALID_BOARD',
message: `Unknown target "${boardId}". Call labwired_list_boards to see available ids.`,
message: `Unknown target "${boardId}". Call labwired_list to see available ids.`,
}, null, 2),

@@ -714,3 +382,3 @@ },

error: 'FIRMWARE_REQUIRED',
message: 'Provide elf_base64 or firmware_ref (sha256:<hex> from labwired_compile_firmware).',
message: 'Provide elf_base64 or firmware_ref (sha256:<hex> from labwired_compile).',
}, null, 2),

@@ -813,3 +481,3 @@ },

note: 'The stdio local run path does not emit a peripheral inspect block. ' +
'Use labwired_inspect_run (scope=gpio|serial) on the snapshot_id for state, ' +
'Use labwired_inspect (scope=gpio|serial) on the snapshot_id for state, ' +
'or the hosted /run peripherals path for a full inspect.',

@@ -837,3 +505,3 @@ ...(unmodeled_access.length ? { unmodeled_access } : {}),

error: 'INVALID_BOARD',
message: `Unknown board_id "${input.board_id}". Call labwired_list_boards to see available ids.`,
message: `Unknown board_id "${input.board_id}". Call labwired_list to see available ids.`,
}, null, 2),

@@ -913,57 +581,2 @@ },

}
if (name === 'labwired_validate_diagram') {
const { diagram } = ValidateDiagramInput.parse(args ?? {});
const result = composeDiagnostics(diagram);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
isError: result.error_count > 0,
};
}
if (name === 'labwired_compile_diagram') {
const input = (args ?? {});
const diagram = input.diagram;
const nameOverride = typeof input.name === 'string' ? input.name : undefined;
if (!diagram || typeof diagram !== 'object') {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'INVALID_ARGS', detail: 'diagram is required' }, null, 2) }],
isError: true,
};
}
const result = compile(diagram);
if (!result.ok) {
return {
content: [{ type: 'text', text: JSON.stringify({ ok: false, diagnostics: result.diagnostics }, null, 2) }],
isError: true,
};
}
// Persist to .labwired/boards/<name>.yaml
const diagramAny = diagram;
const boardName = typeof diagramAny.board === 'string' ? diagramAny.board : 'board';
const stem = nameOverride ? toKebabCase(nameOverride) : toKebabCase(boardName);
const fileName = `${stem}.yaml`;
const workspaceRoot = resolveWorkspaceRoot();
const boardsDir = join(workspaceRoot, '.labwired', 'boards');
await mkdir(boardsDir, { recursive: true });
const boardPath = join(boardsDir, fileName);
await writeFile(boardPath, result.systemYaml ?? '');
const absBoardPath = resolve(boardPath);
return {
content: [
{
type: 'text',
text: JSON.stringify({
ok: true,
board_path: absBoardPath,
system_yaml: result.systemYaml,
diagnostics: result.diagnostics,
}, null, 2),
},
],
};
}
if (name === 'labwired_define_component') {

@@ -1131,3 +744,3 @@ const { spec_yaml, name: nameOverride } = DefineComponentInput.parse(args ?? {});

error: 'FIRMWARE_REQUIRED',
detail: 'Provide firmware_base64 or firmware_ref (sha256:<hex> from labwired_compile_firmware).',
detail: 'Provide firmware_base64 or firmware_ref (sha256:<hex> from labwired_compile).',
}, null, 2),

@@ -1277,4 +890,13 @@ },

}
if (name === 'labwired_inspect_run') {
const { snapshot_id, scope } = InspectRunInput.parse(args ?? {});
if (name === 'labwired_inspect') {
// Accept the registry `output` selector (summary|serial|peripherals|full)
// and the legacy `scope` (summary|serial|gpio|raw). The stdio snapshot
// exposes gpio/serial/raw; map peripherals→gpio and full→raw.
const a = (args ?? {});
const rawSel = typeof a.output === 'string' ? a.output : typeof a.scope === 'string' ? a.scope : 'summary';
const scope = rawSel === 'serial' ? 'serial'
: rawSel === 'gpio' || rawSel === 'peripherals' ? 'gpio'
: rawSel === 'raw' || rawSel === 'full' ? 'raw'
: 'summary';
const { snapshot_id } = InspectRunInput.parse({ snapshot_id: a.snapshot_id, scope });
const snap = getSnapshot(snapshot_id);

@@ -1281,0 +903,0 @@ if (!snap) {

@@ -7,8 +7,8 @@ export const AGENT_HARDWARE_LOOP_URI = 'labwired://guides/agent-hardware-loop';

1. Choose a board with \`labwired_list_boards\`.
2. Validate custom system YAML with \`labwired_validate_system\`.
3. Build or update the diagram and validate it with \`labwired_validate_diagram\`.
4. Compile firmware locally and pass the ELF as base64.
1. Choose a board with \`labwired_list\` (pass \`kind\` to narrow: board | mcu | component).
2. Validate a diagram or custom system YAML with \`labwired_validate\`.
3. Compile firmware with \`labwired_compile\` (hosted PlatformIO toolchain), or compile locally and pass the ELF as base64.
4. Describe any board/chip/component with \`labwired_describe\`.
5. Run with \`labwired_run\` for a preconfigured board, or \`labwired_verify\` with an oracle for a gated proof.
6. Inspect snapshots with \`labwired_inspect_run\`.
6. Inspect snapshots with \`labwired_inspect\`.
7. Use \`labwired_fuzz\` when firmware exposes the fuzz contract.

@@ -15,0 +15,0 @@ 8. Iterate until serial, GPIO, cycle counts, and stop reasons match the intended behavior.

import { rankTools as rankToolsShared } from '@labwired/board-config';
import { toolAnnotations, toolTitle } from './tool-metadata.js';
export const SEARCH_TOOLS_TOOL_NAME = 'labwired_search_tools';
export const SEARCH_TOOLS_TOOL_NAME = 'labwired_search';
export const AGENT_HARDWARE_LOOP_GUIDE_URI = 'labwired://guides/agent-hardware-loop';
export const LOCAL_AGENT_WORKFLOW = [
'labwired_list_boards',
'labwired_validate_diagram',
'labwired_compile_diagram',
'labwired_list',
'labwired_validate',
'labwired_compile',
'labwired_run',
'labwired_inspect_run',
'labwired_inspect',
];

@@ -12,0 +12,0 @@ export const SEARCH_TOOLS_TOOL = {

/**
* In-memory snapshot store for `labwired_inspect_run`. Lets the agent fetch
* In-memory snapshot store for `labwired_inspect`. Lets the agent fetch
* detailed state slices (registers, memory, peripheral) without re-shipping

@@ -4,0 +4,0 @@ * full state through `labwired_run`'s return.

const READ_ONLY_TOOLS = new Set([
'labwired_search_tools',
'labwired_catalog',
'labwired_validate_system',
'labwired_list_boards',
'labwired_inspect_run',
'labwired_validate_diagram',
'labwired_search',
'labwired_list',
'labwired_describe',
'labwired_validate',
'labwired_inspect',
]);
// labwired_define_component is intentionally absent from READ_ONLY_TOOLS:
// it writes a spec file to .labwired/components/.
// labwired_define_component / labwired_ingest_svd / labwired_compile are
// intentionally absent from READ_ONLY_TOOLS: they write spec/peripheral files
// or drive a server-side build.
const TITLES = {
labwired_catalog: 'Catalog',
labwired_validate_system: 'Validate System',
labwired_list_boards: 'List Boards',
labwired_run: 'Run',
labwired_verify: 'Verify',
labwired_search: 'Search Tools',
labwired_list: 'List Catalog',
labwired_describe: 'Describe Component',
labwired_validate: 'Validate Diagram',
labwired_compile: 'Compile Firmware',
labwired_run: 'Run Firmware',
labwired_inspect: 'Inspect Run',
labwired_verify: 'Verify Firmware',
labwired_fuzz: 'Fuzz Firmware',
labwired_inspect_run: 'Inspect Run',
labwired_validate_diagram: 'Validate Diagram',
labwired_search_tools: 'Search Tools',
labwired_ingest_svd: 'Ingest SVD',
labwired_define_component: 'Define Component',
labwired_compile_diagram: 'Compile Diagram',
labwired_ingest_svd: 'Ingest SVD',
};

@@ -25,0 +24,0 @@ export function toolTitle(name) {

{
"name": "@labwired/mcp",
"version": "0.6.0",
"version": "0.7.0",
"description": "Model Context Protocol server exposing the LabWired deterministic firmware simulator as agent tools.",

@@ -5,0 +5,0 @@ "license": "MIT",