@tensor-cad/mcp 0.1.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/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # @tensor-cad/mcp
2
+
3
+ An MCP server that lets an assistant design transformer language models and find out what they would cost, before anyone writes a training script.
4
+
5
+ It wraps [`@tensor-cad/engine`](../engine), the analysis compiled from Go to WebAssembly: a typed block graph, symbolic shape inference, a design-rule check, the parameter/FLOPs/memory/cost model, and a PyTorch emitter. The server is headless — it works on `.tensorcad.json` files and the built-in reference architectures, with no editor running.
6
+
7
+ Built on the official TypeScript SDK v2 (`@modelcontextprotocol/server` 2.0.0), served over stdio.
8
+
9
+ ## Install
10
+
11
+ ### Claude Code
12
+
13
+ ```bash
14
+ # from a checkout, during development
15
+ claude mcp add --transport stdio tensorcad -- bun packages/mcp/src/stdio.ts
16
+
17
+ # published
18
+ claude mcp add --transport stdio tensorcad -- npx -y @tensor-cad/mcp
19
+ ```
20
+
21
+ Add `--scope project` to write a committed `.mcp.json` for everyone on the repo. Resources then surface as `@tensorcad:tensorcad://...` mentions and prompts as `/mcp__tensorcad__design_model`.
22
+
23
+ ### Cursor
24
+
25
+ `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for every project:
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "tensorcad": {
31
+ "command": "npx",
32
+ "args": ["-y", "@tensor-cad/mcp"]
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ Cursor keeps a limited number of tools active across all servers, which is why this one ships eighteen rather than forty.
39
+
40
+ ### Claude Desktop, in one click
41
+
42
+ Download `tensorcad.mcpb` from a release and open it. Claude Desktop installs it
43
+ as an extension and asks once for a folder to keep designs in.
44
+
45
+ The bundle is the server built for Node — which Claude Desktop ships and this
46
+ repository does not use — with the WebAssembly engine beside it. `bun run
47
+ build:mcpb` builds one and starts it under Node before it is done, because the
48
+ two things that have broken it were both differences between Bun and Node rather
49
+ than anything in the server.
50
+
51
+ ### Claude Desktop, by hand
52
+
53
+ `claude_desktop_config.json` (Settings, Developer, Edit Config):
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "tensorcad": {
59
+ "command": "npx",
60
+ "args": ["-y", "@tensor-cad/mcp"],
61
+ "env": { "TENSORCAD_ROOT": "/absolute/path/to/your/designs" }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Any client
68
+
69
+ [`mcp.example.json`](./mcp.example.json) is the repo-relative development form: `bun packages/mcp/src/stdio.ts`, run from the repository root.
70
+
71
+ `TENSORCAD_ROOT` sets the directory `tensorcad_list_designs` scans for `.tensorcad.json` files and that relative paths resolve against. It defaults to the process working directory.
72
+
73
+ ## Tools
74
+
75
+ Eighteen, all namespaced `tensorcad_`. Every one declares an `inputSchema` and an `outputSchema` and returns `structuredContent` alongside a readable text mirror; reads are annotated `readOnlyHint` and `idempotentHint`, and the two that overwrite something are annotated `destructiveHint`.
76
+
77
+ | Tool | What it does |
78
+ |---|---|
79
+ | `tensorcad_list_designs` | Open designs, the built-in reference architectures, and `.tensorcad.json` files on disk |
80
+ | `tensorcad_new_design` | Start from a preset or from nothing; returns a `design_id` |
81
+ | `tensorcad_open_design` | Load a `.tensorcad.json`; opening the same path twice returns the same handle |
82
+ | `tensorcad_save_design` | Write a design back to disk |
83
+ | `tensorcad_get_design` | `format: "outline"` (default) or `"full"` |
84
+ | `tensorcad_get_block` | One block: parameters as written and as resolved, port shapes, wiring, parameter count |
85
+ | `tensorcad_search_catalog` | The block catalog with parameter schemas, port patterns and docs |
86
+ | `tensorcad_apply_ops` | Batched edits with optimistic concurrency |
87
+ | `tensorcad_validate` | Every design rule, each finding with a fix hint |
88
+ | `tensorcad_analyze` | Parameters, FLOPs, KV cache, memory, throughput, cost, Chinchilla |
89
+ | `tensorcad_generate_code` | PyTorch module and config, inline or written to a directory |
90
+ | `tensorcad_checkpoint` | Named snapshot |
91
+ | `tensorcad_restore` | Back to a checkpoint, or undo the last batch |
92
+ | `tensorcad_explain` | One block: what it contributes, and why it is the size it is |
93
+ | `tensorcad_scale` | Shrink a design to a parameter budget, keeping its proportions |
94
+ | `tensorcad_plan` | Every way to split the training across a cluster, and which of them fit |
95
+ | `tensorcad_diff` | What changed between two designs, structurally and numerically |
96
+ | `tensorcad_import_hf` | Read a Hugging Face `config.json` into a design |
97
+
98
+ ### Five that answer questions the others cannot
99
+
100
+ `analyze` says what a design costs; it does not say *why*. `explain` takes one
101
+ path and answers that — the parameters as written and as evaluated, the shape on
102
+ every port, the share of the model's weights and compute — without the agent
103
+ reading the whole document to work it out.
104
+
105
+ `diff` is the other half of editing. An agent that has just applied four
106
+ operations can ask what they moved, structurally and numerically, against the
107
+ design it started from. `plan` answers the question that decides whether any of
108
+ it matters: would this train on the machines available, and how would it have to
109
+ be split.
110
+
111
+ `scale` and `import_hf` both produce a design rather than a report, and both save
112
+ it in the session, so the result can be analysed, diffed and generated from like
113
+ any other. That is why the store gained `adopt`: `create` builds a design from a
114
+ preset or from nothing, and these arrive whole.
115
+
116
+ ### Handles, not sessions
117
+
118
+ `tensorcad_new_design` and `tensorcad_open_design` mint a `design_id`; everything else takes it as an argument. That is what the 2026-07-28 revision asks for — cross-call state travels as an explicit handle rather than as connection state — and it means a client can restart the server mid-conversation without losing the thread of *which* design is meant.
119
+
120
+ Each design carries a `revision` that increments on every change. Pass `expected_revision` to `tensorcad_apply_ops` and a concurrent edit becomes a clear error naming the current revision, instead of a silent overwrite.
121
+
122
+ ### One patch tool, not thirty setters
123
+
124
+ `tensorcad_apply_ops` takes a list of operations and applies them in order to a copy. The first rejected operation aborts the batch and the design is left exactly as it was, so a half-applied edit is never observable.
125
+
126
+ | Op | Fields |
127
+ |---|---|
128
+ | `add_node` | `parent?`, `id`, `type`, `params?`, `label?` |
129
+ | `remove_node` | `path` (its edges go with it) |
130
+ | `set_param` | `path`, `key`, `value` |
131
+ | `connect` | `graph?`, `from`, `to` |
132
+ | `disconnect` | `graph?`, `from`, `to` |
133
+ | `set_symbol` | `name`, `value` (number, expression, or `null` to delete), `doc?`, `runtime?` |
134
+ | `rename` | `path`, `id` (edges are rewritten) |
135
+ | `set_label` | `path`, `label` |
136
+
137
+ A `path` is slash-separated (`layers/block`), and endpoints are `blockId:port` local to their own graph. Most designs are parameterised by symbols — `L` layers, `D` width, `H` heads, `Hkv` key/value heads, `dh` head dimension, `F` feed-forward width, `V` vocabulary — so `set_symbol` is usually the right edit rather than touching individual blocks.
138
+
139
+ ## Resources
140
+
141
+ | URI | Contents |
142
+ |---|---|
143
+ | `tensorcad://designs/{id}` | The document plus its outline |
144
+ | `tensorcad://designs/{id}/validation` | Every finding |
145
+ | `tensorcad://designs/{id}/analysis` | Every number, at the document's own defaults |
146
+ | `tensorcad://catalog` | Every block type with parameter schemas and port patterns |
147
+ | `tensorcad://catalog/{type}` | One block type |
148
+ | `tensorcad://schema/design` | JSON Schema for the `.tensorcad.json` format |
149
+
150
+ The templated ones support argument completion, and the design and catalog templates enumerate their instances, so a client can offer them without guessing an id.
151
+
152
+ ## Prompts
153
+
154
+ `design_model` (target parameter count, context length, family), `review_design`, `scale_design`, `explain_costs`.
155
+
156
+ ## A typical session
157
+
158
+ ```
159
+ tensorcad_new_design { preset: "llama-3-8b" } -> dsn_1, revision 1
160
+ tensorcad_get_design { design_id: "dsn_1" } -> the outline: symbols, blocks, edge shapes
161
+ tensorcad_checkpoint { design_id: "dsn_1" } -> ckpt_1
162
+ tensorcad_apply_ops { design_id: "dsn_1", expected_revision: 1,
163
+ ops: [{ op: "set_symbol", name: "D", value: 5120 },
164
+ { op: "set_symbol", name: "L", value: 40 }] }
165
+ -> revision 2, parameters and a validation summary
166
+ tensorcad_validate { design_id: "dsn_1" } -> findings with fix hints
167
+ tensorcad_analyze { design_id: "dsn_1", T: 8192, hardware: "h100-sxm", gpus: 8, zero: 3 }
168
+ tensorcad_generate_code { design_id: "dsn_1", out_dir: "out/my-model" }
169
+ tensorcad_restore { design_id: "dsn_1", checkpoint_id: "ckpt_1" } # if it did not work out
170
+ ```
171
+
172
+ ## The live editor bridge
173
+
174
+ Start the server with `TENSORCAD_BRIDGE=1` and a running TensorCAD editor attaches to it. The agent's edits appear on the canvas as it makes them, and the human's edits come back the other way.
175
+
176
+ ```bash
177
+ TENSORCAD_BRIDGE=1 bun run packages/mcp/src/stdio.ts
178
+ ```
179
+
180
+ Then open the editor (`bun run --cwd packages/ui dev`, or the desktop build). The status bar's rightmost cell says `agent`, and pressing it detaches.
181
+
182
+ What happens, in order: the editor finds the bridge, publishes the design on screen — so the agent works on *that*, not on a file that resembles it — and the agent sees it in `tensorcad_list_designs`. From then on `tensorcad_apply_ops` lands on the canvas, and what the human does lands in the agent's store, at which point the client is told through `notifications/resources/updated` that the design's resources moved. An edit that arrives from the agent goes onto the editor's undo stack, so the person watching can take it back.
183
+
184
+ There is no second document. An earlier design had a `LiveStore` beside the `FileStore`, both behind `DocumentStore`; that gives a design two homes and no rule for which one is right when they differ. The bridge observes the one store the tools already write to, and an editor's edit goes through the same `apply` a tool call does — same revision check, same undo log.
185
+
186
+ **Off unless asked for.** Without the environment variable the server opens no port and is exactly as headless as it was, which is what keeps it usable in CI.
187
+
188
+ **Who can connect.** It binds 127.0.0.1, so nothing off the machine reaches it. It refuses an upgrade whose `Origin` is not a localhost one, because the same-origin policy does *not* stop a page on the internet opening a WebSocket to your loopback address. And it requires a token, written to `~/.tensorcad/session.json` with owner-only permissions and served to loopback callers of `GET /session` — a browser cannot read the file, which is the only reason that endpoint exists. Anything already running on this machine as you can read the file anyway; the token is not a defence against that and is not meant to be.
189
+
190
+ `TENSORCAD_BRIDGE_PORT` moves it off 7357. The editor probes that port and the three above it, which is also the range the bridge falls back through when one is taken.
191
+
192
+ Also deliberately absent: `tensorcad_render_preview` (a canvas image needs the editor) and `tensorcad_run_script` (a scripting escape hatch, which wants a sandbox and an opt-in environment variable before it is worth shipping). Both are in the research notes as future tools; leaving them out keeps the count where clients are happy.
193
+
194
+ ## Publishing
195
+
196
+ `server.json` beside this README is the [MCP registry][registry] manifest:
197
+ the namespace (`io.github.filip-pajalic/tensorcad`, which matches `mcpName` in
198
+ `package.json`), the npm package it points at, and the one environment variable
199
+ the server reads. It is checked against the registry's own schema when it is
200
+ written and against `package.json` on every test run, because what drifts is not
201
+ the schema but the version in two files.
202
+
203
+ ```bash
204
+ mcp-publisher login github # the namespace is verified as the GitHub user
205
+ mcp-publisher publish --file server.json
206
+ ```
207
+
208
+ [registry]: https://github.com/modelcontextprotocol/registry
209
+
210
+ ## Development
211
+
212
+ ```bash
213
+ bun run packages/mcp/src/stdio.ts # serve on stdio
214
+ bun test packages/mcp/test # contract tests: spin the server up and call every tool
215
+ ```
216
+
217
+ The contract tests connect with the SDK's own client over a real stdio pipe and validate every tool's `structuredContent` against the `outputSchema` that tool advertised, so the declared contract and the actual answer cannot drift apart.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * What the agent and the editor say to each other.
3
+ *
4
+ * The bridge carries *documents*, not just operations. Forwarding the op
5
+ * stream alone would make the canvas replay every edit the agent makes, which
6
+ * sounds elegant until the two ends disagree about what an op means and
7
+ * nothing notices: the editor would drift, silently, and the drawing would
8
+ * stop being the design. So every change carries the whole document as the
9
+ * truth, with the operations beside it as the *account* of what changed —
10
+ * which is what a person watching wants to read.
11
+ *
12
+ * A document is not large next to what a browser already holds, and the engine
13
+ * that would have to be asked to reconcile a divergence is the expensive part.
14
+ */
15
+ import type { Doc } from "@tensor-cad/engine";
16
+ import type { Op } from "../ops.js";
17
+ import type { DesignSummary } from "../store/types.js";
18
+ /**
19
+ * Bumped when a message changes shape. The editor refuses a bridge whose
20
+ * protocol it does not know rather than half-understanding it, because a
21
+ * canvas that mirrors *some* of an edit is worse than one that mirrors none.
22
+ */
23
+ export declare const BRIDGE_PROTOCOL = 1;
24
+ /** Why a design arrived. `requested` answers an `attach`; `published` answers a `publish`. */
25
+ export type ChangeReason = "registered" | "applied" | "replaced" | "saved" | "restored" | "published" | "requested";
26
+ export interface HelloMessage {
27
+ type: "hello";
28
+ protocol: number;
29
+ server: string;
30
+ version: string;
31
+ /** The directory the agent's relative paths resolve against, for the editor to show. */
32
+ root: string;
33
+ designs: DesignSummary[];
34
+ }
35
+ /** A design as it now stands. The one message that carries a document. */
36
+ export interface DesignMessage {
37
+ type: "design";
38
+ reason: ChangeReason;
39
+ design: DesignSummary;
40
+ doc: Doc;
41
+ /** Present when `reason` is `applied`: what the agent did, in its own words. */
42
+ ops?: Op[];
43
+ }
44
+ export interface ErrorMessage {
45
+ type: "error";
46
+ message: string;
47
+ /** The message this is a reply to, when it was a reply to one. */
48
+ about?: ClientMessage["type"];
49
+ }
50
+ export type ServerMessage = HelloMessage | DesignMessage | ErrorMessage;
51
+ /** "This is what I have open." The bridge takes it as a new design. */
52
+ export interface PublishMessage {
53
+ type: "publish";
54
+ doc: Doc;
55
+ }
56
+ /**
57
+ * "I had this open before." Sent instead of `publish` after a reconnect, so a
58
+ * dropped socket does not leave the agent looking at two copies of one design.
59
+ * Answered with an error when the id is unknown — a restarted server has
60
+ * forgotten it — and the editor then publishes.
61
+ */
62
+ export interface AttachMessage {
63
+ type: "attach";
64
+ design_id: string;
65
+ }
66
+ /** The human edited, in a way that is one of the agent's own operations. */
67
+ export interface OpsMessage {
68
+ type: "ops";
69
+ design_id: string;
70
+ /** Rejected when it is not the current revision, exactly as `tensorcad_apply_ops` is. */
71
+ revision?: number;
72
+ ops: Op[];
73
+ }
74
+ /**
75
+ * The human edited, generally.
76
+ *
77
+ * Most of what an editor does is not one of the eight operations — a block
78
+ * moved on the sheet, a definition written, a configuration switched — so the
79
+ * editor's ordinary way of saying what it has is to say the whole thing. It is
80
+ * an `ops` message in every other respect: revision-checked, undoable, and
81
+ * announced to the agent.
82
+ */
83
+ export interface ReplaceMessage {
84
+ type: "replace";
85
+ design_id: string;
86
+ revision?: number;
87
+ doc: Doc;
88
+ }
89
+ export type ClientMessage = PublishMessage | AttachMessage | OpsMessage | ReplaceMessage;
90
+ /** What `~/.tensorcad/session.json` holds while a bridge is listening. */
91
+ export interface SessionFile {
92
+ version: number;
93
+ protocol: number;
94
+ port: number;
95
+ token: string;
96
+ pid: number;
97
+ root: string;
98
+ started_at: string;
99
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The live editor bridge: a loopback socket the running editor attaches to, so
3
+ * an agent's edits appear on the canvas as it makes them and the human's edits
4
+ * reach the agent.
5
+ *
6
+ * It is *not* a second document store. It watches the one store the tools
7
+ * already write to and mirrors every change outward; an editor's own edit goes
8
+ * through the same `apply` a tool call does, revision check and all. There is
9
+ * one document, one revision counter and one undo log, which is what makes
10
+ * "who is right" a question that never has to be answered.
11
+ *
12
+ * Off unless asked for. An MCP server that opened a port nobody requested
13
+ * would be a surprise in every CI job that runs one, so `TENSORCAD_BRIDGE=1`
14
+ * turns it on and the server is otherwise exactly as headless as it was.
15
+ *
16
+ * ## Who is allowed to connect
17
+ *
18
+ * Three things, in order of how much they actually do:
19
+ *
20
+ * 1. **The bind.** 127.0.0.1 only, so nothing off this machine can reach it.
21
+ * 2. **The origin.** A page on the internet can open a WebSocket to your
22
+ * loopback address — the same-origin policy does not stop it — so the
23
+ * upgrade is refused unless the `Origin` header is absent (a native client)
24
+ * or names a localhost origin. This is the check that matters for a browser.
25
+ * 3. **The token.** A random 32 bytes, in `~/.tensorcad/session.json` with
26
+ * owner-only permissions, and handed to loopback callers of `/session`.
27
+ * It guards against another program on this machine that guessed the port.
28
+ */
29
+ import type { DocumentStore, StoreChange } from "../store/types.js";
30
+ /**
31
+ * Where the editor looks first. Fixed rather than ephemeral because a browser
32
+ * cannot read the session file to find out — it probes this and the three
33
+ * ports above it, which is also the range this server falls back through when
34
+ * one is taken.
35
+ */
36
+ export declare const DEFAULT_BRIDGE_PORT = 7357;
37
+ export declare const PORT_ATTEMPTS = 4;
38
+ export interface BridgeOptions {
39
+ store: DocumentStore;
40
+ /** What `hello` reports as the agent's working directory. */
41
+ root: string;
42
+ name: string;
43
+ version: string;
44
+ port?: number;
45
+ /** Supplied by tests; otherwise 32 random bytes. */
46
+ token?: string;
47
+ /** Where to write the session file. Omitted means the real one. */
48
+ sessionFile?: string;
49
+ /** Somewhere to say what happened. Defaults to stderr, which is where a stdio server's diagnostics go. */
50
+ log?: (line: string) => void;
51
+ }
52
+ /**
53
+ * A design changed; something outside the bridge may want to know.
54
+ *
55
+ * `from` is the half the store cannot say. The MCP server needs it: telling an
56
+ * agent that a resource changed because the agent changed it is noise, and
57
+ * telling it when the *human* changed it is the whole point.
58
+ */
59
+ export type BridgeWatcher = (change: StoreChange, from: "agent" | "editor") => void;
60
+ export declare class BridgeServer {
61
+ readonly token: string;
62
+ private readonly http;
63
+ private readonly wss;
64
+ private readonly options;
65
+ private readonly log;
66
+ private unsubscribe?;
67
+ private readonly watchers;
68
+ private port_;
69
+ /**
70
+ * The connection whose message is being handled right now.
71
+ *
72
+ * The store calls its listeners synchronously, inside `apply`, so this is
73
+ * set for exactly the duration of that call and is how a change is
74
+ * attributed to the socket that caused it. An editor that has already drawn
75
+ * its own edit does not want it sent back.
76
+ */
77
+ private acting?;
78
+ constructor(options: BridgeOptions);
79
+ get port(): number;
80
+ /** `ws://127.0.0.1:<port>/bridge?token=…`, which is what the editor opens. */
81
+ get url(): string;
82
+ get connections(): number;
83
+ /** Called for every store change, after the mirror has gone out. */
84
+ watch(watcher: BridgeWatcher): () => void;
85
+ start(): Promise<void>;
86
+ stop(): Promise<void>;
87
+ /**
88
+ * One endpoint, and it exists for one reason: a browser cannot read the
89
+ * session file. Everything else is a 404, because this is not a web server.
90
+ */
91
+ private serveHttp;
92
+ /** The reason to refuse, as an HTTP status line, or undefined to allow. */
93
+ private refuse;
94
+ private attach;
95
+ private handle;
96
+ private mirror;
97
+ private during;
98
+ private send;
99
+ }
100
+ /**
101
+ * A localhost page, the desktop shell, or a file:// document.
102
+ *
103
+ * `*.localhost` is here for Wails, which serves the desktop build from
104
+ * `http://wails.localhost` on Windows.
105
+ */
106
+ export declare function isLocalOrigin(origin: string): boolean;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `~/.tensorcad/session.json` — how a program on this machine finds the bridge.
3
+ *
4
+ * A file rather than a fixed port alone, because the port is only half the
5
+ * answer: the other half is the token, and a token that lives in a file with
6
+ * owner-only permissions is a token that a page you happened to visit cannot
7
+ * read. The desktop shell reads this file. A browser cannot, and gets the
8
+ * token from the bridge's own `/session` endpoint instead, which answers only
9
+ * a loopback caller — see `server.ts`.
10
+ *
11
+ * It is written when the bridge starts listening and removed when it stops,
12
+ * including on a clean exit. A file left behind by a crash names a port that
13
+ * answers nothing, which is why every reader checks the port rather than
14
+ * trusting the file.
15
+ */
16
+ import { type SessionFile } from "./protocol.js";
17
+ export declare const SESSION_VERSION = 1;
18
+ /** Overridable so a test does not write into the developer's home directory. */
19
+ export declare function sessionPath(): string;
20
+ export declare function writeSession(session: Omit<SessionFile, "version" | "protocol">, path?: string): Promise<void>;
21
+ export declare function readSession(path?: string): Promise<SessionFile | undefined>;
22
+ export declare function clearSession(path?: string): Promise<void>;
23
+ /** The same, for `process.on("exit")`, which cannot wait for a promise. */
24
+ export declare function clearSessionSync(path?: string): void;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * JSON Schema for the design document, served as `tensorcad://schema/design`.
3
+ *
4
+ * Hand-written rather than derived, because the engine describes the
5
+ * document with TypeScript types and a per-block parameter catalog rather than
6
+ * one monolithic runtime schema. Block parameters are therefore `object` here;
7
+ * `tensorcad_search_catalog` is where the per-type parameter schemas live.
8
+ */
9
+ export declare const DESIGN_JSON_SCHEMA: {
10
+ readonly $schema: "https://json-schema.org/draft/2020-12/schema";
11
+ readonly $id: "tensorcad://schema/design";
12
+ readonly title: "TensorCAD design document";
13
+ readonly description: "The `.tensorcad.json` format. The graph is the source of truth; the editor's node positions are a view of it.";
14
+ readonly type: "object";
15
+ readonly required: readonly ["version", "meta", "symbols", "graph"];
16
+ readonly additionalProperties: false;
17
+ readonly properties: {
18
+ readonly version: {
19
+ readonly const: 1;
20
+ readonly description: "Document format version.";
21
+ };
22
+ readonly meta: {
23
+ readonly type: "object";
24
+ readonly required: readonly ["name"];
25
+ readonly additionalProperties: false;
26
+ readonly properties: {
27
+ readonly name: {
28
+ readonly type: "string";
29
+ };
30
+ readonly family: {
31
+ readonly type: "string";
32
+ readonly description: "Architecture family, e.g. llama, qwen, gpt2.";
33
+ };
34
+ readonly notes: {
35
+ readonly type: "string";
36
+ };
37
+ readonly published: {
38
+ readonly type: "object";
39
+ readonly description: "Reference numbers from the model card or paper, asserted by the regression suite.";
40
+ readonly additionalProperties: false;
41
+ readonly properties: {
42
+ readonly params: {
43
+ readonly type: "number";
44
+ };
45
+ readonly activeParams: {
46
+ readonly type: "number";
47
+ };
48
+ readonly kvBytesPerToken: {
49
+ readonly type: "number";
50
+ };
51
+ readonly source: {
52
+ readonly type: "string";
53
+ };
54
+ readonly tolerance: {
55
+ readonly type: "number";
56
+ readonly description: "Allowed relative difference. Defaults to 0.5%.";
57
+ };
58
+ };
59
+ };
60
+ };
61
+ };
62
+ readonly symbols: {
63
+ readonly type: "object";
64
+ readonly description: string;
65
+ readonly additionalProperties: {
66
+ readonly anyOf: readonly [{
67
+ readonly type: "number";
68
+ }, {
69
+ readonly type: "string";
70
+ readonly description: "Expression over earlier symbols, e.g. \"ceil_mult(1.3*8/3*D, 1024)\".";
71
+ }, {
72
+ readonly type: "object";
73
+ readonly required: readonly ["kind", "default"];
74
+ readonly additionalProperties: false;
75
+ readonly properties: {
76
+ readonly kind: {
77
+ readonly const: "runtime";
78
+ };
79
+ readonly default: {
80
+ readonly type: "number";
81
+ };
82
+ readonly doc: {
83
+ readonly type: "string";
84
+ };
85
+ };
86
+ }, {
87
+ readonly type: "object";
88
+ readonly required: readonly ["kind", "value"];
89
+ readonly additionalProperties: false;
90
+ readonly properties: {
91
+ readonly kind: {
92
+ readonly const: "design";
93
+ };
94
+ readonly value: {
95
+ readonly anyOf: readonly [{
96
+ readonly type: "number";
97
+ }, {
98
+ readonly type: "string";
99
+ }];
100
+ };
101
+ readonly doc: {
102
+ readonly type: "string";
103
+ };
104
+ };
105
+ }];
106
+ };
107
+ };
108
+ readonly graph: {
109
+ readonly $ref: "#/$defs/graph";
110
+ };
111
+ readonly ui: {
112
+ readonly type: "object";
113
+ readonly description: "Editor state. Ignored by analysis and codegen.";
114
+ readonly additionalProperties: false;
115
+ readonly properties: {
116
+ readonly positions: {
117
+ readonly type: "object";
118
+ readonly additionalProperties: {
119
+ readonly type: "array";
120
+ readonly items: {
121
+ readonly type: "number";
122
+ };
123
+ readonly minItems: 2;
124
+ readonly maxItems: 2;
125
+ };
126
+ };
127
+ readonly collapsed: {
128
+ readonly type: "array";
129
+ readonly items: {
130
+ readonly type: "string";
131
+ };
132
+ };
133
+ };
134
+ };
135
+ };
136
+ readonly $defs: {
137
+ readonly graph: {
138
+ readonly type: "object";
139
+ readonly required: readonly ["nodes", "edges"];
140
+ readonly additionalProperties: false;
141
+ readonly properties: {
142
+ readonly nodes: {
143
+ readonly type: "array";
144
+ readonly items: {
145
+ readonly $ref: "#/$defs/node";
146
+ };
147
+ };
148
+ readonly edges: {
149
+ readonly type: "array";
150
+ readonly description: "Each edge is [\"fromBlock:port\", \"toBlock:port\"], with ids local to this graph.";
151
+ readonly items: {
152
+ readonly type: "array";
153
+ readonly items: {
154
+ readonly type: "string";
155
+ };
156
+ readonly minItems: 2;
157
+ readonly maxItems: 2;
158
+ };
159
+ };
160
+ };
161
+ };
162
+ readonly node: {
163
+ readonly type: "object";
164
+ readonly required: readonly ["id", "type"];
165
+ readonly additionalProperties: false;
166
+ readonly properties: {
167
+ readonly id: {
168
+ readonly type: "string";
169
+ readonly description: "Unique within its graph.";
170
+ };
171
+ readonly type: {
172
+ readonly type: "string";
173
+ readonly description: "A catalog block type; see tensorcad://catalog.";
174
+ };
175
+ readonly params: {
176
+ readonly type: "object";
177
+ readonly description: string;
178
+ readonly additionalProperties: true;
179
+ };
180
+ readonly graph: {
181
+ readonly $ref: "#/$defs/graph";
182
+ readonly description: "Subgraph, for container blocks such as repeat.";
183
+ };
184
+ readonly variants: {
185
+ readonly type: "object";
186
+ readonly description: "Named subgraph variants, for hybrid repeat patterns.";
187
+ readonly additionalProperties: {
188
+ readonly $ref: "#/$defs/graph";
189
+ };
190
+ };
191
+ readonly label: {
192
+ readonly type: "string";
193
+ readonly description: "Shown on the canvas instead of the id.";
194
+ };
195
+ };
196
+ };
197
+ };
198
+ };
package/index.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The library: everything this package exposes to code that imports it.
3
+ *
4
+ * Starting a server is `stdio.ts`, and the split matters. This used to be both,
5
+ * with an `if (import.meta.main)` at the bottom — which works under Bun and
6
+ * does not survive a bundler, because `import.meta.main` compiles to a
7
+ * CommonJS check that is not defined in an ESM output. The desktop bundle
8
+ * crashed before its first line ran.
9
+ */
10
+ export { serve } from "./serve.js";
11
+ export { createServer, SERVER_NAME, SERVER_VERSION } from "./server.js";
12
+ export { FileStore } from "./store/file-store.js";
13
+ export type { DocumentStore, DesignRecord, DesignSummary } from "./store/types.js";
14
+ export { TOOL_NAMES } from "./tools.js";
15
+ export { PROMPT_NAMES } from "./prompts.js";
16
+ export { applyOps, type Op } from "./ops.js";