spreadsheet_read_mcp/
model.rs

1use crate::caps::BackendCaps;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, Default)]
7#[serde(transparent)]
8pub struct WorkbookId(pub String);
9
10impl WorkbookId {
11    pub fn as_str(&self) -> &str {
12        &self.0
13    }
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
17pub struct WorkbookDescriptor {
18    pub workbook_id: WorkbookId,
19    pub short_id: String,
20    pub slug: String,
21    pub folder: Option<String>,
22    pub path: String,
23    pub bytes: u64,
24    pub last_modified: Option<String>,
25    pub caps: BackendCaps,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
29pub struct WorkbookListResponse {
30    pub workbooks: Vec<WorkbookDescriptor>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
34pub struct WorkbookDescription {
35    pub workbook_id: WorkbookId,
36    pub short_id: String,
37    pub slug: String,
38    pub path: String,
39    pub bytes: u64,
40    pub sheet_count: usize,
41    pub defined_names: usize,
42    pub tables: usize,
43    pub macros_present: bool,
44    pub last_modified: Option<String>,
45    pub caps: BackendCaps,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
49pub struct WorkbookSummaryResponse {
50    pub workbook_id: WorkbookId,
51    pub workbook_short_id: String,
52    pub slug: String,
53    pub sheet_count: usize,
54    pub total_cells: u64,
55    pub total_formulas: u64,
56    pub breakdown: WorkbookBreakdown,
57    pub region_counts: RegionCountSummary,
58    pub key_named_ranges: Vec<NamedRangeDescriptor>,
59    pub suggested_entry_points: Vec<EntryPoint>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
63pub struct WorkbookBreakdown {
64    pub data_sheets: u32,
65    pub calculator_sheets: u32,
66    pub parameter_sheets: u32,
67    pub metadata_sheets: u32,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
71pub struct RegionCountSummary {
72    pub data: u32,
73    pub parameters: u32,
74    pub outputs: u32,
75    pub calculator: u32,
76    pub metadata: u32,
77    pub other: u32,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
81pub struct EntryPoint {
82    pub sheet_name: String,
83    pub region_id: Option<u32>,
84    pub bounds: Option<String>,
85    pub rationale: String,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
89pub struct SheetSummary {
90    pub name: String,
91    pub visible: bool,
92    pub row_count: u32,
93    pub column_count: u32,
94    pub non_empty_cells: u32,
95    pub formula_cells: u32,
96    pub cached_values: u32,
97    pub classification: SheetClassification,
98    pub style_tags: Vec<String>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
102#[serde(rename_all = "snake_case")]
103pub enum SheetClassification {
104    Data,
105    Calculator,
106    Mixed,
107    Metadata,
108    Empty,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
112pub struct SheetListResponse {
113    pub workbook_id: WorkbookId,
114    pub workbook_short_id: String,
115    pub sheets: Vec<SheetSummary>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
119pub struct SheetOverviewResponse {
120    pub workbook_id: WorkbookId,
121    pub workbook_short_id: String,
122    pub sheet_name: String,
123    pub narrative: String,
124    pub regions: Vec<SheetRegion>,
125    pub detected_regions: Vec<DetectedRegion>,
126    pub key_ranges: Vec<String>,
127    pub formula_ratio: f32,
128    pub notable_features: Vec<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
132pub struct SheetRegion {
133    pub kind: RegionKind,
134    pub address: String,
135    pub description: String,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
139#[serde(rename_all = "snake_case")]
140pub enum RegionKind {
141    Table,
142    Data,
143    Parameters,
144    Outputs,
145    Calculator,
146    Metadata,
147    Styles,
148    Comments,
149    Other,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
153pub struct DetectedRegion {
154    pub id: u32,
155    pub bounds: String,
156    pub header_row: Option<u32>,
157    pub headers: Vec<String>,
158    pub row_count: u32,
159    pub classification: RegionKind,
160    pub region_kind: Option<RegionKind>,
161    pub confidence: f32,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
165pub struct SheetPageResponse {
166    pub workbook_id: WorkbookId,
167    pub workbook_short_id: String,
168    pub sheet_name: String,
169    pub rows: Vec<RowSnapshot>,
170    pub has_more: bool,
171    pub next_start_row: Option<u32>,
172    pub header_row: Option<RowSnapshot>,
173    pub compact: Option<SheetPageCompact>,
174    pub values_only: Option<SheetPageValues>,
175    pub format: SheetPageFormat,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
179pub struct RowSnapshot {
180    pub row_index: u32,
181    pub cells: Vec<CellSnapshot>,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
185pub struct CellSnapshot {
186    pub address: String,
187    pub value: Option<CellValue>,
188    pub formula: Option<String>,
189    pub cached_value: Option<CellValue>,
190    pub number_format: Option<String>,
191    pub style_tags: Vec<String>,
192    pub notes: Vec<String>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
196#[serde(tag = "kind", content = "value")]
197pub enum CellValue {
198    Text(String),
199    Number(f64),
200    Bool(bool),
201    Error(String),
202    Date(String),
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
206#[serde(rename_all = "snake_case")]
207#[derive(Default)]
208pub enum SheetPageFormat {
209    #[default]
210    Full,
211    Compact,
212    ValuesOnly,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
216pub struct SheetPageCompact {
217    pub headers: Vec<String>,
218    pub header_row: Vec<Option<CellValue>>,
219    pub rows: Vec<Vec<Option<CellValue>>>,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
223pub struct SheetPageValues {
224    pub rows: Vec<Vec<Option<CellValue>>>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
228pub struct SheetStatisticsResponse {
229    pub workbook_id: WorkbookId,
230    pub workbook_short_id: String,
231    pub sheet_name: String,
232    pub row_count: u32,
233    pub column_count: u32,
234    pub density: f32,
235    pub numeric_columns: Vec<ColumnSummary>,
236    pub text_columns: Vec<ColumnSummary>,
237    pub null_counts: BTreeMap<String, u32>,
238    pub duplicate_warnings: Vec<String>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
242pub struct ColumnSummary {
243    pub header: Option<String>,
244    pub column: String,
245    pub samples: Vec<CellValue>,
246    pub min: Option<f64>,
247    pub max: Option<f64>,
248    pub mean: Option<f64>,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
252pub struct SheetFormulaMapResponse {
253    pub workbook_id: WorkbookId,
254    pub workbook_short_id: String,
255    pub sheet_name: String,
256    pub groups: Vec<FormulaGroup>,
257    pub truncated: bool,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
261pub struct FormulaGroup {
262    pub fingerprint: String,
263    pub addresses: Vec<String>,
264    pub formula: String,
265    pub is_array: bool,
266    pub is_shared: bool,
267    pub is_volatile: bool,
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
271pub struct FormulaTraceResponse {
272    pub workbook_id: WorkbookId,
273    pub workbook_short_id: String,
274    pub sheet_name: String,
275    pub origin: String,
276    pub direction: TraceDirection,
277    pub layers: Vec<TraceLayer>,
278    pub next_cursor: Option<TraceCursor>,
279    pub notes: Vec<String>,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
283pub struct FormulaTraceEdge {
284    pub from: String,
285    pub to: String,
286    pub formula: Option<String>,
287    pub note: Option<String>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
291pub struct TraceLayer {
292    pub depth: u32,
293    pub summary: TraceLayerSummary,
294    pub highlights: TraceLayerHighlights,
295    pub edges: Vec<FormulaTraceEdge>,
296    pub has_more: bool,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
300pub struct TraceLayerSummary {
301    pub total_nodes: usize,
302    pub formula_nodes: usize,
303    pub value_nodes: usize,
304    pub blank_nodes: usize,
305    pub external_nodes: usize,
306    pub unique_formula_groups: usize,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
310pub struct TraceLayerHighlights {
311    pub top_ranges: Vec<TraceRangeHighlight>,
312    pub top_formula_groups: Vec<TraceFormulaGroupHighlight>,
313    pub notable_cells: Vec<TraceCellHighlight>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
317pub struct TraceRangeHighlight {
318    pub start: String,
319    pub end: String,
320    pub count: usize,
321    pub literals: usize,
322    pub formulas: usize,
323    pub blanks: usize,
324    pub sample_values: Vec<CellValue>,
325    pub sample_formulas: Vec<String>,
326    pub sample_addresses: Vec<String>,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
330pub struct TraceFormulaGroupHighlight {
331    pub fingerprint: String,
332    pub formula: String,
333    pub count: usize,
334    pub sample_addresses: Vec<String>,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
338pub struct TraceCellHighlight {
339    pub address: String,
340    pub kind: TraceCellKind,
341    pub value: Option<CellValue>,
342    pub formula: Option<String>,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
346#[serde(rename_all = "snake_case")]
347pub enum TraceCellKind {
348    Formula,
349    Literal,
350    Blank,
351    External,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
355pub struct TraceCursor {
356    pub depth: u32,
357    pub offset: usize,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
361#[serde(rename_all = "snake_case")]
362pub enum TraceDirection {
363    Precedents,
364    Dependents,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
368pub struct NamedRangeDescriptor {
369    pub name: String,
370    pub scope: Option<String>,
371    pub refers_to: String,
372    pub kind: NamedItemKind,
373    pub sheet_name: Option<String>,
374    pub comment: Option<String>,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
378#[serde(rename_all = "snake_case")]
379pub enum NamedItemKind {
380    NamedRange,
381    Table,
382    Formula,
383    Unknown,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
387pub struct NamedRangesResponse {
388    pub workbook_id: WorkbookId,
389    pub workbook_short_id: String,
390    pub items: Vec<NamedRangeDescriptor>,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
394pub struct FindFormulaMatch {
395    pub address: String,
396    pub sheet_name: String,
397    pub formula: String,
398    pub cached_value: Option<CellValue>,
399    pub context: Vec<RowSnapshot>,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
403pub struct FindFormulaResponse {
404    pub workbook_id: WorkbookId,
405    pub workbook_short_id: String,
406    pub matches: Vec<FindFormulaMatch>,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
410pub struct VolatileScanEntry {
411    pub address: String,
412    pub sheet_name: String,
413    pub function: String,
414    pub note: Option<String>,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
418pub struct VolatileScanResponse {
419    pub workbook_id: WorkbookId,
420    pub workbook_short_id: String,
421    pub items: Vec<VolatileScanEntry>,
422    pub truncated: bool,
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
426pub struct SheetStylesResponse {
427    pub workbook_id: WorkbookId,
428    pub workbook_short_id: String,
429    pub sheet_name: String,
430    pub styles: Vec<StyleSummary>,
431    pub conditional_rules: Vec<String>,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
435pub struct StyleSummary {
436    pub style_id: String,
437    pub occurrences: u32,
438    pub tags: Vec<String>,
439    pub example_cells: Vec<String>,
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
443pub struct ManifestStubResponse {
444    pub workbook_id: WorkbookId,
445    pub workbook_short_id: String,
446    pub slug: String,
447    pub sheets: Vec<ManifestSheetStub>,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
451pub struct ManifestSheetStub {
452    pub sheet_name: String,
453    pub classification: SheetClassification,
454    pub candidate_expectations: Vec<String>,
455    pub notes: Vec<String>,
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
459#[serde(rename_all = "snake_case")]
460pub enum FindMode {
461    #[default]
462    Value,
463    Label,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
467#[serde(rename_all = "snake_case")]
468pub enum LabelDirection {
469    Right,
470    Below,
471    Any,
472}
473
474#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
475pub struct FindValueMatch {
476    pub address: String,
477    pub sheet_name: String,
478    pub value: Option<CellValue>,
479    pub row_context: Option<RowContext>,
480    pub neighbors: Option<NeighborValues>,
481    pub label_hit: Option<LabelHit>,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
485pub struct RowContext {
486    pub headers: Vec<String>,
487    pub values: Vec<Option<CellValue>>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
491pub struct NeighborValues {
492    pub left: Option<CellValue>,
493    pub right: Option<CellValue>,
494    pub up: Option<CellValue>,
495    pub down: Option<CellValue>,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
499pub struct LabelHit {
500    pub label_address: String,
501    pub label: String,
502}
503
504#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
505pub struct FindValueResponse {
506    pub workbook_id: WorkbookId,
507    pub workbook_short_id: String,
508    pub matches: Vec<FindValueMatch>,
509    pub truncated: bool,
510}
511
512pub type TableRow = BTreeMap<String, Option<CellValue>>;
513
514#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
515pub struct ReadTableResponse {
516    pub workbook_id: WorkbookId,
517    pub workbook_short_id: String,
518    pub sheet_name: String,
519    pub table_name: Option<String>,
520    pub headers: Vec<String>,
521    pub rows: Vec<TableRow>,
522    pub total_rows: u32,
523    pub has_more: bool,
524}
525
526#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
527pub struct ColumnTypeSummary {
528    pub name: String,
529    pub inferred_type: String,
530    pub nulls: u32,
531    pub distinct: u32,
532    pub top_values: Vec<String>,
533    pub min: Option<f64>,
534    pub max: Option<f64>,
535    pub mean: Option<f64>,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
539pub struct TableProfileResponse {
540    pub workbook_id: WorkbookId,
541    pub workbook_short_id: String,
542    pub sheet_name: String,
543    pub table_name: Option<String>,
544    pub headers: Vec<String>,
545    pub column_types: Vec<ColumnTypeSummary>,
546    pub row_count: u32,
547    pub samples: Vec<TableRow>,
548    pub notes: Vec<String>,
549}
550
551#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
552pub struct RangeValuesResponse {
553    pub workbook_id: WorkbookId,
554    pub workbook_short_id: String,
555    pub sheet_name: String,
556    pub values: Vec<RangeValuesEntry>,
557}
558
559#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
560pub struct RangeValuesEntry {
561    pub range: String,
562    pub rows: Vec<Vec<Option<CellValue>>>,
563}
564
565#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
566pub struct CloseWorkbookResponse {
567    pub workbook_id: WorkbookId,
568    pub message: String,
569}