Skip to content
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Konnect/
│ │ ├── sch_wiring.rs # 20 tools (incl. connect_pins, power symbol embedding)
│ │ ├── sch_analysis.rs # 15 tools (union-find net graph, connectivity)
│ │ ├── sch_batch.rs # 12 tools (single-read/single-write atomic operations)
│ │ ├── sch_export.rs # 6 tools (SVG/PDF/netlist/ERC)
│ │ ├── sch_export.rs # 7 tools (SVG/PDF/netlist/ERC/PCB sync)
│ │ ├── sch_hierarchy.rs # 12 tools (typed Sheet model, sheet CRUD + hierarchy/page queries + pin lifecycle)
│ │ ├── pcb_board.rs # 11 tools (S-expr file editing, IPC fallback, SVG logo import)
│ │ ├── pcb_components.rs # 13 tools (IPC real-time + safe headless single-placement fallback)
Expand Down Expand Up @@ -209,7 +209,7 @@ if !path.exists() {

Adding a new kind: edit `mcp/error.rs`, add the variant, add the match arm in `short_code()`, use it from the handler. The `short_code_matches_serialized_kind_field` test will fail loudly if they drift.

The dispatch-level errors (not-loaded/unknown/handler-panic) are fully structured. So are **all missing-argument errors** across all 199 tools — `tools/mod.rs::require_str` / `require_f64` emit `ToolErrorKind::InvalidArgument { field, reason }` automatically. Most in-handler errors still use `CallToolResult::error("free text")` or bubble `anyhow::Error`; migrating them is incremental. `project.rs::handle_get_project_info` demonstrates the structured `FileNotFound` pattern.
The dispatch-level errors (not-loaded/unknown/handler-panic) are fully structured. So are **all missing-argument errors** across all 200 tools — `tools/mod.rs::require_str` / `require_f64` emit `ToolErrorKind::InvalidArgument { field, reason }` automatically. Most in-handler errors still use `CallToolResult::error("free text")` or bubble `anyhow::Error`; migrating them is incremental. `project.rs::handle_get_project_info` demonstrates the structured `FileNotFound` pattern.

## Observability

Expand All @@ -230,7 +230,7 @@ Source: [`crates/konnect-core/src/observability.rs`](crates/konnect-core/src/obs

## Tool Routing (Starter Kit + On-Demand Loading)

The server does NOT expose all 199 tools (205 total with the 6 meta-tools) in `tools/list` by default — that would cost ~23K tokens of context on every listing. Instead:
The server does NOT expose all 200 tools (206 total with the 6 meta-tools) in `tools/list` by default — that would cost ~23K tokens of context on every listing. Instead:

- **Startup**: only `STARTER_KIT` toolsets are pre-loaded (see `router/registry.rs::STARTER_KIT`). Currently: `project`, `config`. Combined with the 6 meta-tools, baseline `tools/list` is ~19 tools ≈ 2K tokens.
- **On demand**: the LLM reads `list_toolboxes` → calls `load_toolset(name)` to expose a toolset's tools in subsequent `tools/list` responses. `unload_toolset(name)` prunes them when the task shifts.
Expand Down Expand Up @@ -302,9 +302,9 @@ convention for other `kicad-cli`-calling code.

## Current Stats

- **19 toolsets, 199 tools** + 6 meta-tools (4 routing + 2 observability — see `tool-directory.md`)
- **19 toolsets, 200 tools** + 6 meta-tools (4 routing + 2 observability — see `tool-directory.md`)
- Baseline `tools/list`: ~19 tools / ~2K tokens (starter kit + meta-tools)
- Full-catalog `tools/list` (all loaded): 205 tools (199 registered + 6 meta) / ~25K tokens
- Full-catalog `tools/list` (all loaded): 206 tools (200 registered + 6 meta) / ~25K tokens
- **0 IPC stubs** (all protobuf methods implemented)
- **0 unimplemented tools**
- **3 CLI commands removed in KiCAD v10** (specctra DSN/SES, pcb sync — return clear errors)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Rust binary — that lets Claude and other AI assistants design schematics and PCBs
through the [Model Context Protocol](https://modelcontextprotocol.io) (MCP).

**199 tools across 19 on-demand toolsets.** Schematic capture, PCB layout and
**200 tools across 19 on-demand toolsets.** Schematic capture, PCB layout and
routing, ERC/DRC, design-review audits, JLCPCB part search, Freerouting, reference
circuits, and a full manufacturing export pipeline — with bundled skills and agents
that teach Claude KiCAD conventions out of the box.
Expand Down
3 changes: 2 additions & 1 deletion crates/konnect-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ konnect-ipc.workspace = true
konnect-schematic-editor.workspace = true

prost-types.workspace = true
prost.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
Expand All @@ -21,6 +22,7 @@ thiserror.workspace = true
tracing.workspace = true
uuid.workspace = true
base64.workspace = true
sha2.workspace = true
reqwest.workspace = true
rusqlite.workspace = true
usvg.workspace = true
Expand All @@ -31,4 +33,3 @@ tempfile = "3"
# The pcb_components fallback tests spawn a mock KiCAD NNG endpoint to prove
# a reachable-but-rejecting KiCAD never triggers the file-editing fallback.
nng.workspace = true
prost.workspace = true
4 changes: 2 additions & 2 deletions crates/konnect-core/src/router/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ pub static ALL_TOOLSETS: &[ToolsetMeta] = &[
},
ToolsetMeta {
name: "sch_export",
description: "Export schematic to SVG/PDF/netlist, run ERC",
description: "Export schematic to SVG/PDF/netlist, run ERC, and synchronize a live PCB",
category: "schematic",
tool_count: 6,
tool_count: 7,
},
ToolsetMeta {
name: "sch_hierarchy",
Expand Down
1 change: 1 addition & 0 deletions crates/konnect-core/src/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod pcb_board;
pub mod pcb_components;
pub mod pcb_export;
pub mod pcb_routing;
pub(crate) mod pcb_sync;
pub mod project;
pub mod sch_analysis;
pub mod sch_batch;
Expand Down
10 changes: 6 additions & 4 deletions crates/konnect-core/src/tools/pcb_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ macro_rules! ipc {
/// through the project's fp-lib-table (the board's directory), then the global
/// table, then the conventional KiCad library directories — the lookup that
/// `library::resolve_footprint_path` owns.
fn resolve_footprint_source(lib_id: &str, board: &Path) -> anyhow::Result<String> {
pub(crate) fn resolve_footprint_source(lib_id: &str, board: &Path) -> anyhow::Result<String> {
let (nickname, entry) = lib_id.split_once(':').ok_or_else(|| {
anyhow::anyhow!("footprint must use Library:Footprint syntax, got '{lib_id}'")
})?;
Expand Down Expand Up @@ -197,7 +197,9 @@ fn prepare_footprint_source(
Ok(prepared)
}

fn extract_pad_definitions(source: &str) -> anyhow::Result<Vec<konnect_ipc::IpcPadDefinition>> {
pub(crate) fn extract_pad_definitions(
source: &str,
) -> anyhow::Result<Vec<konnect_ipc::IpcPadDefinition>> {
let footprint = konnect_sexp::parse_sexp(source)?;
footprint
.find_all("pad")
Expand Down Expand Up @@ -363,7 +365,7 @@ fn text_at(node: &konnect_sexp::SexpNode, kind: &str) -> anyhow::Result<((f64, f
/// Footprint-local Reference/Value text anchors from the library source, so
/// placed parts keep the library's text layout (a synthesized offset put the
/// Reference on the part's own silkscreen — silk_overlap in live DRC).
fn extract_field_placement(source: &str) -> konnect_ipc::IpcFieldPlacement {
pub(crate) fn extract_field_placement(source: &str) -> konnect_ipc::IpcFieldPlacement {
let mut placement = konnect_ipc::IpcFieldPlacement::default();
let Ok(footprint) = konnect_sexp::parse_sexp(source) else {
return placement;
Expand Down Expand Up @@ -393,7 +395,7 @@ fn extract_field_placement(source: &str) -> konnect_ipc::IpcFieldPlacement {
placement
}

fn extract_graphic_definitions(
pub(crate) fn extract_graphic_definitions(
source: &str,
) -> anyhow::Result<Vec<konnect_ipc::IpcGraphicDefinition>> {
use konnect_ipc::IpcGraphicDefinition as Graphic;
Expand Down
Loading
Loading