@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.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The headless document store: `.tensorcad.json` files plus the built-in presets,
3
+ * held in memory with a revision counter and an operation log.
4
+ *
5
+ * The live editor bridge watches this store through `subscribe` rather than
6
+ * replacing it. An earlier note here proposed a second `DocumentStore` that
7
+ * proxied to the editor; that would have given a design two homes and no rule
8
+ * for which one is right when they differ. See `src/bridge/`.
9
+ */
10
+ import { type Op } from "../ops.js";
11
+ import type { Doc } from "@tensor-cad/engine";
12
+ import { type ApplyOutcome, type CheckpointInfo, type DesignRecord, type DesignSummary, type DocumentStore, type NewDesignOptions, type StoreListener } from "./types.js";
13
+ export interface FileStoreOptions {
14
+ /** Directory `listFiles` scans and relative save paths resolve against. */
15
+ root?: string;
16
+ /** How deep `listFiles` walks. */
17
+ depth?: number;
18
+ }
19
+ export declare class FileStore implements DocumentStore {
20
+ private readonly entries;
21
+ private readonly listeners;
22
+ private nextDesign;
23
+ private nextCheckpoint;
24
+ readonly root: string;
25
+ private readonly depth;
26
+ constructor(options?: FileStoreOptions);
27
+ subscribe(listener: StoreListener): () => void;
28
+ /**
29
+ * A listener that throws must not take the edit down with it. The store's
30
+ * job is the document; a mirror that has fallen over is the mirror's problem,
31
+ * and it is reported where a server's diagnostics go.
32
+ */
33
+ private emit;
34
+ list(): DesignSummary[];
35
+ listFiles(): Promise<string[]>;
36
+ get(id: string): DesignRecord;
37
+ create(options: NewDesignOptions): DesignRecord;
38
+ adopt(doc: Doc): DesignRecord;
39
+ open(path: string): Promise<DesignRecord>;
40
+ private register;
41
+ apply(id: string, ops: Op[], expectedRevision?: number): ApplyOutcome;
42
+ replace(id: string, doc: Doc, expectedRevision?: number): ApplyOutcome;
43
+ save(id: string, path?: string): Promise<{
44
+ record: DesignRecord;
45
+ path: string;
46
+ bytes: number;
47
+ }>;
48
+ checkpoint(id: string, label?: string): CheckpointInfo;
49
+ checkpoints(id: string): CheckpointInfo[];
50
+ restore(id: string, checkpointId?: string): {
51
+ record: DesignRecord;
52
+ restoredFrom: string;
53
+ };
54
+ private entry;
55
+ private resolvePath;
56
+ /** Whether a path exists, used by tools that want a friendlier message. */
57
+ static exists(path: string): Promise<boolean>;
58
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The document store the tools talk to.
3
+ *
4
+ * Designs are addressed by a server-minted `design_id` rather than by a
5
+ * session, which is what the 2026-07-28 spec asks for: state crosses calls as
6
+ * an explicit handle in the arguments, never as an implicit connection.
7
+ *
8
+ * `FileStore` is the only implementation. The live editor bridge is *not* a
9
+ * second store: it observes this one through `subscribe` and mirrors it to a
10
+ * running editor. Two stores could disagree about what a design is; one store
11
+ * with a listener cannot.
12
+ */
13
+ import type { Op } from "../ops.js";
14
+ import type { Doc } from "@tensor-cad/engine";
15
+ /**
16
+ * Where a design in this session came from. `derived` is one the engine
17
+ * produced from another — scaled, or read out of a Hugging Face config — which
18
+ * has no file behind it and is not a preset.
19
+ */
20
+ export type DesignSource = "preset" | "file" | "empty" | "derived";
21
+ export interface DesignSummary {
22
+ design_id: string;
23
+ name: string;
24
+ revision: number;
25
+ /** Absolute path this design was opened from or last saved to. */
26
+ path?: string;
27
+ source: DesignSource;
28
+ /** True when there are edits that `tensorcad_save_design` has not written yet. */
29
+ dirty: boolean;
30
+ created_at: string;
31
+ updated_at: string;
32
+ }
33
+ export interface DesignRecord extends DesignSummary {
34
+ doc: Doc;
35
+ }
36
+ export interface CheckpointInfo {
37
+ checkpoint_id: string;
38
+ label: string;
39
+ revision: number;
40
+ created_at: string;
41
+ }
42
+ export interface ApplyOutcome {
43
+ record: DesignRecord;
44
+ applied: string[];
45
+ previousRevision: number;
46
+ }
47
+ export interface NewDesignOptions {
48
+ preset?: string;
49
+ name?: string;
50
+ }
51
+ /** What happened to a design, for anybody mirroring the store. */
52
+ export interface StoreChange {
53
+ kind: "registered" | "applied" | "replaced" | "saved" | "restored";
54
+ record: DesignRecord;
55
+ /** The operations, when there were any — `applied` alone carries them. */
56
+ ops?: Op[];
57
+ }
58
+ export type StoreListener = (change: StoreChange) => void;
59
+ export interface DocumentStore {
60
+ /**
61
+ * Watch every change to every design. Returns the function that stops
62
+ * watching.
63
+ *
64
+ * Listeners are called *synchronously*, inside the call that changed
65
+ * something, which is what lets the bridge attribute a change to the
66
+ * connection that caused it without threading an origin through every
67
+ * signature.
68
+ */
69
+ subscribe(listener: StoreListener): () => void;
70
+ /** Designs this server has open, newest first. */
71
+ list(): DesignSummary[];
72
+ /** `.tensorcad.json` files near the server's root that could be opened. */
73
+ listFiles(): Promise<string[]>;
74
+ create(options: NewDesignOptions): DesignRecord;
75
+ /**
76
+ * Take a document the engine produced — scaled, imported, derived — as a new
77
+ * design in this session.
78
+ *
79
+ * Separate from `create`, which builds one from a preset or from nothing:
80
+ * these arrive whole and there is nothing to build.
81
+ */
82
+ adopt(doc: Doc): DesignRecord;
83
+ open(path: string): Promise<DesignRecord>;
84
+ get(id: string): DesignRecord;
85
+ apply(id: string, ops: Op[], expectedRevision?: number): ApplyOutcome;
86
+ /**
87
+ * The whole document, in place of a list of operations.
88
+ *
89
+ * The editor's edits are not all expressible as the eight operations a tool
90
+ * call can send — a block moved on the sheet, a definition, a configuration —
91
+ * so what the running editor sends back is the document it now has. It is
92
+ * otherwise an `apply`: the revision is checked, the previous document goes
93
+ * on the undo log, and the change is announced.
94
+ */
95
+ replace(id: string, doc: Doc, expectedRevision?: number): ApplyOutcome;
96
+ save(id: string, path?: string): Promise<{
97
+ record: DesignRecord;
98
+ path: string;
99
+ bytes: number;
100
+ }>;
101
+ checkpoint(id: string, label?: string): CheckpointInfo;
102
+ checkpoints(id: string): CheckpointInfo[];
103
+ /** Without a checkpoint id this undoes the most recent `apply`. */
104
+ restore(id: string, checkpointId?: string): {
105
+ record: DesignRecord;
106
+ restoredFrom: string;
107
+ };
108
+ }
109
+ /** Thrown when a mutating call names a revision that is no longer current. */
110
+ export declare class RevisionConflictError extends Error {
111
+ readonly designId: string;
112
+ readonly expected: number;
113
+ readonly actual: number;
114
+ constructor(designId: string, expected: number, actual: number);
115
+ }
116
+ /** Thrown when a `design_id` is unknown. */
117
+ export declare class UnknownDesignError extends Error {
118
+ constructor(id: string, known: string[]);
119
+ }
package/summarize.d.ts ADDED
@@ -0,0 +1,128 @@
1
+ import type { CatalogEntry as EngineCatalogEntry } from "@tensor-cad/engine";
2
+ import type { AnalysisResult, Doc, ValidationReport } from "@tensor-cad/engine";
3
+ /**
4
+ * Projections of a design that are cheap for a model to read.
5
+ *
6
+ * `outline` is the one an assistant should reach for first: it is the whole
7
+ * shape of the design in a few hundred tokens, where the full document is a few
8
+ * thousand. The others mirror the core's results as plain JSON, because
9
+ * `AnalysisResult` carries `Map`s that do not survive serialization.
10
+ */
11
+ export interface OutlineSymbol {
12
+ name: string;
13
+ kind: "design" | "runtime";
14
+ value: string;
15
+ resolved?: number;
16
+ doc?: string;
17
+ }
18
+ export interface OutlineBlock {
19
+ path: string;
20
+ type: string;
21
+ kind: string;
22
+ depth: number;
23
+ label?: string;
24
+ /** Trainable parameters under this path, including every repeat instance. */
25
+ params: number;
26
+ /** Instance count for `repeat` containers. */
27
+ repeat?: number;
28
+ }
29
+ export interface OutlineEdge {
30
+ /** Container path the edge lives in; `""` is the root graph. */
31
+ graph: string;
32
+ from: string;
33
+ to: string;
34
+ /** Inferred shape on the wire, e.g. `"B T D"`. */
35
+ shape?: string;
36
+ }
37
+ export interface Outline {
38
+ name: string;
39
+ family?: string;
40
+ notes?: string;
41
+ symbols: OutlineSymbol[];
42
+ blocks: OutlineBlock[];
43
+ edges: OutlineEdge[];
44
+ params_total: number;
45
+ params_active: number;
46
+ issues: number;
47
+ }
48
+ export declare function outlineOf(doc: Doc): Outline;
49
+ /** The outline rendered for a human (and as the text mirror of the tool result). */
50
+ export declare function outlineText(o: Outline): string;
51
+ export interface BlockPort {
52
+ name: string;
53
+ pattern: string;
54
+ shape?: string;
55
+ /** Declared element type, when it is not inherited from the producer. */
56
+ dtype?: string;
57
+ /** True when this port may legitimately be left unwired. */
58
+ optional?: boolean;
59
+ /** The port on the other end of the wire, as a full path. */
60
+ connected_to?: string[];
61
+ }
62
+ export interface BlockDetail {
63
+ path: string;
64
+ id: string;
65
+ type: string;
66
+ kind: string;
67
+ category: string;
68
+ label?: string;
69
+ summary: string;
70
+ formula?: string;
71
+ params: Record<string, unknown>;
72
+ resolved_params: Record<string, unknown>;
73
+ param_errors: string[];
74
+ inputs: BlockPort[];
75
+ outputs: BlockPort[];
76
+ params_count: number;
77
+ /** Multiplied instances of this block, from the enclosing `repeat`s. */
78
+ instances: number;
79
+ children: string[];
80
+ }
81
+ export declare function blockDetail(doc: Doc, path: string): BlockDetail;
82
+ export declare function blockText(b: BlockDetail): string;
83
+ export interface CatalogParam {
84
+ name: string;
85
+ type: string;
86
+ default?: string;
87
+ doc?: string;
88
+ values?: string[];
89
+ }
90
+ export interface CatalogEntry {
91
+ type: string;
92
+ kind: string;
93
+ category: string;
94
+ summary: string;
95
+ formula?: string;
96
+ refs: string[];
97
+ params: CatalogParam[];
98
+ inputs: string[];
99
+ outputs: string[];
100
+ /** True when the port list depends on the block's parameters. */
101
+ dynamic_ports: boolean;
102
+ }
103
+ export declare function catalogEntry(def: EngineCatalogEntry): CatalogEntry;
104
+ export declare function allCatalogEntries(): CatalogEntry[];
105
+ export declare function catalogText(entries: CatalogEntry[]): string;
106
+ export interface FindingJson {
107
+ rule: string;
108
+ severity: "error" | "warning" | "info";
109
+ path?: string;
110
+ port?: string;
111
+ message: string;
112
+ hint?: string;
113
+ }
114
+ export declare function findingsJson(report: ValidationReport): FindingJson[];
115
+ export interface ValidationSummary {
116
+ ok: boolean;
117
+ counts: {
118
+ error: number;
119
+ warning: number;
120
+ info: number;
121
+ };
122
+ /** The first few findings, worst first. Call `tensorcad_validate` for all of them. */
123
+ top_findings: FindingJson[];
124
+ }
125
+ export declare function validationSummary(report: ValidationReport, limit?: number): ValidationSummary;
126
+ export declare function findingsText(findings: FindingJson[]): string;
127
+ export declare function analysisJson(a: AnalysisResult): Record<string, unknown>;
128
+ export declare function analysisText(a: AnalysisResult): string;
package/tools.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The tool surface: thirteen tools, namespaced `tensorcad_`.
3
+ *
4
+ * Deliberately few and coarse. Some clients cap how many tools they keep
5
+ * active, so graph editing is one batched `tensorcad_apply_ops` rather than a
6
+ * setter per property, and reads default to the compact outline.
7
+ */
8
+ import type { McpServer } from "@modelcontextprotocol/server";
9
+ import type { DocumentStore } from "./store/types.js";
10
+ export declare function registerTools(server: McpServer, store: DocumentStore): void;
11
+ /** The tools this server registers, in the order it registers them. */
12
+ export declare const TOOL_NAMES: readonly ["tensorcad_list_designs", "tensorcad_new_design", "tensorcad_open_design", "tensorcad_save_design", "tensorcad_get_design", "tensorcad_get_block", "tensorcad_search_catalog", "tensorcad_apply_ops", "tensorcad_validate", "tensorcad_analyze", "tensorcad_generate_code", "tensorcad_checkpoint", "tensorcad_restore", "tensorcad_explain", "tensorcad_scale", "tensorcad_mup", "tensorcad_plan", "tensorcad_diff", "tensorcad_import_hf"];