@cci-labs/mode-mcp 1.0.1

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,925 @@
1
+ # Mode Analytics MCP Server
2
+
3
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/AwesomeCICD/mode-mcp/blob/main/LICENSE)
4
+ [![CircleCI](https://dl.circleci.com/status-badge/img/gh/AwesomeCICD/mode-mcp/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/AwesomeCICD/mode-mcp/tree/main)
5
+ [![npm](https://img.shields.io/npm/v/%40cci-labs%2Fmode-mcp?logo=npm)](https://www.npmjs.com/package/@cci-labs/mode-mcp)
6
+ [![Docker](https://img.shields.io/badge/docker-ccilabs%2Fmode--mcp-blue?logo=docker)](https://hub.docker.com/r/ccilabs/mode-mcp)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22-brightgreen?logo=node.js)](https://nodejs.org)
8
+ [![MCP](https://img.shields.io/badge/MCP-compatible-purple)](https://modelcontextprotocol.io)
9
+
10
+ Model Context Protocol (MCP) is a [standardized protocol](https://modelcontextprotocol.io/introduction) for managing context between large language models (LLMs) and external systems. In this repository, we provide an MCP Server for [Mode Analytics](https://mode.com).
11
+
12
+ Use Cursor, Windsurf, Copilot, Claude, or any MCP-compatible client to interact with Mode Analytics using natural language — without leaving your IDE.
13
+
14
+ ## Tools
15
+
16
+ | Tool | Description |
17
+ |------|-------------|
18
+ | [`mode_list_reports`](#mode_list_reports) | List reports in the workspace, optionally filtered by space |
19
+ | [`mode_get_report`](#mode_get_report) | Get report details including queries, data sources, and last run status |
20
+ | [`mode_list_queries`](#mode_list_queries) | List all queries in a report with their SQL content |
21
+ | [`mode_run_report`](#mode_run_report) | Trigger a new report run (non-destructive) |
22
+ | [`mode_get_run_status`](#mode_get_run_status) | Check whether a report run has completed, is running, or failed |
23
+ | [`mode_get_query_results`](#mode_get_query_results) | Download query results as a markdown table, optionally save as CSV |
24
+ | [`mode_execute_and_fetch`](#mode_execute_and_fetch) | Run a report, wait for completion, and return results — all in one call |
25
+ | [`mode_list_report_runs`](#mode_list_report_runs) | List past runs for a report |
26
+ | [`mode_get_query_run`](#mode_get_query_run) | Get details on a specific query run (state, errors, timing) |
27
+ | [`mode_search_reports`](#mode_search_reports) | Search reports by name across the workspace |
28
+ | [`mode_get_report_parameters`](#mode_get_report_parameters) | Discover what parameters a report accepts before running it |
29
+ | [`mode_list_report_filters`](#mode_list_report_filters) | List interactive filters on a report |
30
+ | [`mode_list_report_charts`](#mode_list_report_charts) | List charts/visualizations in a query |
31
+ | [`mode_list_spaces`](#mode_list_spaces) | List all spaces (collections) in the workspace |
32
+ | [`mode_list_data_sources`](#mode_list_data_sources) | List all connected data sources (databases) |
33
+ | [`mode_get_data_source`](#mode_get_data_source) | Get detailed connection info for a data source |
34
+ | [`mode_list_definitions`](#mode_list_definitions) | List saved SQL definitions (reusable snippets) |
35
+ | [`mode_list_report_schedules`](#mode_list_report_schedules) | List automated schedules for a report |
36
+ | [`mode_list_report_subscriptions`](#mode_list_report_subscriptions) | List email subscriptions for a report |
37
+ | [`mode_get_audit_logs`](#mode_get_audit_logs) | Retrieve workspace audit logs |
38
+ | [`mode_list_datasets`](#mode_list_datasets) | List datasets in a space |
39
+ | [`mode_get_dataset`](#mode_get_dataset) | Get dataset details with fields and descriptions |
40
+ | [`mode_list_dataset_reports`](#mode_list_dataset_reports) | Find reports that use a dataset |
41
+ | [`mode_export_report`](#mode_export_report) | Export a report as PDF or CSV |
42
+
43
+ ## Installation
44
+
45
+ <details>
46
+ <summary><strong>Claude Code</strong></summary>
47
+
48
+ **Prerequisites:**
49
+ - [Mode API token and secret](https://app.mode.com) — generate at `app.mode.com/{workspace}/settings/api_tokens`
50
+ - Docker: [Docker](https://docs.docker.com/get-docker/)
51
+ - NPX: [Node.js >= v22](https://nodejs.org/)
52
+
53
+ #### Using Docker in a local MCP Server
54
+
55
+ ```bash
56
+ claude mcp add mode-analytics \
57
+ -e MODE_WORKSPACE=your-workspace \
58
+ -e MODE_API_TOKEN=your-token \
59
+ -e MODE_API_SECRET=your-secret \
60
+ -- docker run --rm -i \
61
+ -e MODE_WORKSPACE -e MODE_API_TOKEN -e MODE_API_SECRET \
62
+ -v "$HOME/Downloads:/output" \
63
+ ccilabs/mode-mcp
64
+ ```
65
+
66
+ > The `-v "$HOME/Downloads:/output"` mount lets tools save CSV and PDF exports to your `~/Downloads` folder. Use `/output` as the `output_dir` parameter when downloading files.
67
+
68
+ #### Using Node.js in a local MCP Server
69
+
70
+ ```bash
71
+ claude mcp add mode-analytics \
72
+ -e MODE_WORKSPACE=your-workspace \
73
+ -e MODE_API_TOKEN=your-token \
74
+ -e MODE_API_SECRET=your-secret \
75
+ -- node /path/to/mode-mcp/dist/index.js
76
+ ```
77
+
78
+ > With the Node.js setup, tools can save files directly to any path on your machine.
79
+
80
+ #### Using npx (no install)
81
+
82
+ ```bash
83
+ claude mcp add mode-analytics \
84
+ -e MODE_WORKSPACE=your-workspace \
85
+ -e MODE_API_TOKEN=your-token \
86
+ -e MODE_API_SECRET=your-secret \
87
+ -- npx -y @cci-labs/mode-mcp
88
+ ```
89
+
90
+ For more information: https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/tutorials#set-up-model-context-protocol-mcp
91
+
92
+ </details>
93
+
94
+ <details>
95
+ <summary><strong>Cursor</strong></summary>
96
+
97
+ **Prerequisites:**
98
+ - [Mode API token and secret](https://app.mode.com) — generate at `app.mode.com/{workspace}/settings/api_tokens`
99
+ - Docker: [Docker](https://docs.docker.com/get-docker/)
100
+ - NPX: [Node.js >= v22](https://nodejs.org/)
101
+
102
+ #### Using Docker in a local MCP Server
103
+
104
+ Add the following to your Cursor MCP config:
105
+
106
+ ```json
107
+ {
108
+ "mcpServers": {
109
+ "mode-analytics": {
110
+ "command": "docker",
111
+ "args": [
112
+ "run", "--rm", "-i",
113
+ "-e", "MODE_WORKSPACE",
114
+ "-e", "MODE_API_TOKEN",
115
+ "-e", "MODE_API_SECRET",
116
+ "-v", "${HOME}/Downloads:/output",
117
+ "ccilabs/mode-mcp"
118
+ ],
119
+ "env": {
120
+ "MODE_WORKSPACE": "your-workspace",
121
+ "MODE_API_TOKEN": "your-token",
122
+ "MODE_API_SECRET": "your-secret"
123
+ }
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ #### Using Node.js in a local MCP Server
130
+
131
+ ```json
132
+ {
133
+ "mcpServers": {
134
+ "mode-analytics": {
135
+ "command": "node",
136
+ "args": ["/path/to/mode-mcp/dist/index.js"],
137
+ "env": {
138
+ "MODE_WORKSPACE": "your-workspace",
139
+ "MODE_API_TOKEN": "your-token",
140
+ "MODE_API_SECRET": "your-secret"
141
+ }
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ #### Using npx (no install)
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "mode-analytics": {
153
+ "command": "npx",
154
+ "args": ["-y", "@cci-labs/mode-mcp"],
155
+ "env": {
156
+ "MODE_WORKSPACE": "your-workspace",
157
+ "MODE_API_TOKEN": "your-token",
158
+ "MODE_API_SECRET": "your-secret"
159
+ }
160
+ }
161
+ }
162
+ }
163
+ ```
164
+
165
+ </details>
166
+
167
+ <details>
168
+ <summary><strong>VS Code</strong></summary>
169
+
170
+ **Prerequisites:**
171
+ - [Mode API token and secret](https://app.mode.com) — generate at `app.mode.com/{workspace}/settings/api_tokens`
172
+ - Docker: [Docker](https://docs.docker.com/get-docker/)
173
+ - NPX: [Node.js >= v22](https://nodejs.org/)
174
+
175
+ #### Using Docker in a local MCP Server
176
+
177
+ Add the following to `.vscode/mcp.json` in your project:
178
+
179
+ ```json
180
+ {
181
+ "inputs": [
182
+ {
183
+ "type": "promptString",
184
+ "id": "mode-workspace",
185
+ "description": "Mode workspace name"
186
+ },
187
+ {
188
+ "type": "promptString",
189
+ "id": "mode-token",
190
+ "description": "Mode API Token",
191
+ "password": true
192
+ },
193
+ {
194
+ "type": "promptString",
195
+ "id": "mode-secret",
196
+ "description": "Mode API Secret",
197
+ "password": true
198
+ }
199
+ ],
200
+ "servers": {
201
+ "mode-analytics": {
202
+ "type": "stdio",
203
+ "command": "docker",
204
+ "args": [
205
+ "run", "--rm", "-i",
206
+ "-e", "MODE_WORKSPACE",
207
+ "-e", "MODE_API_TOKEN",
208
+ "-e", "MODE_API_SECRET",
209
+ "-v", "${HOME}/Downloads:/output",
210
+ "ccilabs/mode-mcp"
211
+ ],
212
+ "env": {
213
+ "MODE_WORKSPACE": "${input:mode-workspace}",
214
+ "MODE_API_TOKEN": "${input:mode-token}",
215
+ "MODE_API_SECRET": "${input:mode-secret}"
216
+ }
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ > Inputs are prompted on first server start, then stored securely by VS Code.
223
+
224
+ #### Using Node.js in a local MCP Server
225
+
226
+ ```json
227
+ {
228
+ "inputs": [
229
+ {
230
+ "type": "promptString",
231
+ "id": "mode-workspace",
232
+ "description": "Mode workspace name"
233
+ },
234
+ {
235
+ "type": "promptString",
236
+ "id": "mode-token",
237
+ "description": "Mode API Token",
238
+ "password": true
239
+ },
240
+ {
241
+ "type": "promptString",
242
+ "id": "mode-secret",
243
+ "description": "Mode API Secret",
244
+ "password": true
245
+ }
246
+ ],
247
+ "servers": {
248
+ "mode-analytics": {
249
+ "type": "stdio",
250
+ "command": "node",
251
+ "args": ["/path/to/mode-mcp/dist/index.js"],
252
+ "env": {
253
+ "MODE_WORKSPACE": "${input:mode-workspace}",
254
+ "MODE_API_TOKEN": "${input:mode-token}",
255
+ "MODE_API_SECRET": "${input:mode-secret}"
256
+ }
257
+ }
258
+ }
259
+ }
260
+ ```
261
+
262
+ #### Using npx (no install)
263
+
264
+ ```json
265
+ {
266
+ "inputs": [
267
+ {
268
+ "type": "promptString",
269
+ "id": "mode-workspace",
270
+ "description": "Mode workspace name"
271
+ },
272
+ {
273
+ "type": "promptString",
274
+ "id": "mode-token",
275
+ "description": "Mode API Token",
276
+ "password": true
277
+ },
278
+ {
279
+ "type": "promptString",
280
+ "id": "mode-secret",
281
+ "description": "Mode API Secret",
282
+ "password": true
283
+ }
284
+ ],
285
+ "servers": {
286
+ "mode-analytics": {
287
+ "type": "stdio",
288
+ "command": "npx",
289
+ "args": ["-y", "@cci-labs/mode-mcp"],
290
+ "env": {
291
+ "MODE_WORKSPACE": "${input:mode-workspace}",
292
+ "MODE_API_TOKEN": "${input:mode-token}",
293
+ "MODE_API_SECRET": "${input:mode-secret}"
294
+ }
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
300
+ </details>
301
+
302
+ <details>
303
+ <summary><strong>Claude Desktop</strong></summary>
304
+
305
+ **Prerequisites:**
306
+ - [Mode API token and secret](https://app.mode.com) — generate at `app.mode.com/{workspace}/settings/api_tokens`
307
+ - Docker: [Docker](https://docs.docker.com/get-docker/)
308
+ - NPX: [Node.js >= v22](https://nodejs.org/)
309
+
310
+ #### Using Docker in a local MCP Server
311
+
312
+ Add the following to your `claude_desktop_config.json`:
313
+
314
+ ```json
315
+ {
316
+ "mcpServers": {
317
+ "mode-analytics": {
318
+ "command": "docker",
319
+ "args": [
320
+ "run", "--rm", "-i",
321
+ "-e", "MODE_WORKSPACE",
322
+ "-e", "MODE_API_TOKEN",
323
+ "-e", "MODE_API_SECRET",
324
+ "-v", "${HOME}/Downloads:/output",
325
+ "ccilabs/mode-mcp"
326
+ ],
327
+ "env": {
328
+ "MODE_WORKSPACE": "your-workspace",
329
+ "MODE_API_TOKEN": "your-token",
330
+ "MODE_API_SECRET": "your-secret"
331
+ }
332
+ }
333
+ }
334
+ }
335
+ ```
336
+
337
+ #### Using Node.js in a local MCP Server
338
+
339
+ ```json
340
+ {
341
+ "mcpServers": {
342
+ "mode-analytics": {
343
+ "command": "node",
344
+ "args": ["/path/to/mode-mcp/dist/index.js"],
345
+ "env": {
346
+ "MODE_WORKSPACE": "your-workspace",
347
+ "MODE_API_TOKEN": "your-token",
348
+ "MODE_API_SECRET": "your-secret"
349
+ }
350
+ }
351
+ }
352
+ }
353
+ ```
354
+
355
+ #### Using npx (no install)
356
+
357
+ ```json
358
+ {
359
+ "mcpServers": {
360
+ "mode-analytics": {
361
+ "command": "npx",
362
+ "args": ["-y", "@cci-labs/mode-mcp"],
363
+ "env": {
364
+ "MODE_WORKSPACE": "your-workspace",
365
+ "MODE_API_TOKEN": "your-token",
366
+ "MODE_API_SECRET": "your-secret"
367
+ }
368
+ }
369
+ }
370
+ }
371
+ ```
372
+
373
+ To find or create your config file, open Claude Desktop settings, click **Developer** in the left sidebar, then click **Edit Config**. The config file is located at:
374
+
375
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
376
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
377
+
378
+ For more information: https://modelcontextprotocol.io/quickstart/user
379
+
380
+ </details>
381
+
382
+ <details>
383
+ <summary><strong>Windsurf</strong></summary>
384
+
385
+ **Prerequisites:**
386
+ - [Mode API token and secret](https://app.mode.com) — generate at `app.mode.com/{workspace}/settings/api_tokens`
387
+ - Docker: [Docker](https://docs.docker.com/get-docker/)
388
+ - NPX: [Node.js >= v22](https://nodejs.org/)
389
+
390
+ #### Using Docker in a local MCP Server
391
+
392
+ Add the following to your Windsurf `mcp_config.json`:
393
+
394
+ ```json
395
+ {
396
+ "mcpServers": {
397
+ "mode-analytics": {
398
+ "command": "docker",
399
+ "args": [
400
+ "run", "--rm", "-i",
401
+ "-e", "MODE_WORKSPACE",
402
+ "-e", "MODE_API_TOKEN",
403
+ "-e", "MODE_API_SECRET",
404
+ "-v", "${HOME}/Downloads:/output",
405
+ "ccilabs/mode-mcp"
406
+ ],
407
+ "env": {
408
+ "MODE_WORKSPACE": "your-workspace",
409
+ "MODE_API_TOKEN": "your-token",
410
+ "MODE_API_SECRET": "your-secret"
411
+ }
412
+ }
413
+ }
414
+ }
415
+ ```
416
+
417
+ #### Using Node.js in a local MCP Server
418
+
419
+ ```json
420
+ {
421
+ "mcpServers": {
422
+ "mode-analytics": {
423
+ "command": "node",
424
+ "args": ["/path/to/mode-mcp/dist/index.js"],
425
+ "env": {
426
+ "MODE_WORKSPACE": "your-workspace",
427
+ "MODE_API_TOKEN": "your-token",
428
+ "MODE_API_SECRET": "your-secret"
429
+ }
430
+ }
431
+ }
432
+ }
433
+ ```
434
+
435
+ #### Using npx (no install)
436
+
437
+ ```json
438
+ {
439
+ "mcpServers": {
440
+ "mode-analytics": {
441
+ "command": "npx",
442
+ "args": ["-y", "@cci-labs/mode-mcp"],
443
+ "env": {
444
+ "MODE_WORKSPACE": "your-workspace",
445
+ "MODE_API_TOKEN": "your-token",
446
+ "MODE_API_SECRET": "your-secret"
447
+ }
448
+ }
449
+ }
450
+ }
451
+ ```
452
+
453
+ For more information: https://docs.windsurf.com/windsurf/mcp
454
+
455
+ </details>
456
+
457
+ ## Tool Details
458
+
459
+ <details>
460
+ <summary id="mode_list_reports"><strong><code>mode_list_reports</code></strong></summary>
461
+
462
+ Lists reports in the Mode workspace. When no space is specified, fetches reports across all spaces.
463
+
464
+ **Parameters:**
465
+ - `space_token` (optional) — filter reports by space/collection token
466
+ - `filter` (optional) — filter string (e.g. timestamp-based)
467
+ - `order` (optional) — `asc` or `desc`
468
+ - `order_by` (optional) — `created_at` or `updated_at`
469
+
470
+ **Returns:** Numbered list of reports with names, tokens, spaces, query counts, and last run timestamps.
471
+
472
+ **Example prompts:**
473
+ - "What reports are in our Mode workspace?"
474
+ - "List all reports in the Data Science space"
475
+
476
+ </details>
477
+
478
+ <details>
479
+ <summary id="mode_get_report"><strong><code>mode_get_report</code></strong></summary>
480
+
481
+ Gets detailed information about a specific report.
482
+
483
+ **Parameters:**
484
+ - `report_token` (required) — the report token (found in Mode URLs or from `mode_list_reports`)
485
+
486
+ **Returns:** Report name, description, query count, last run timestamp and token, space, and web URL.
487
+
488
+ **Example prompts:**
489
+ - "Show me details for report 578c18655469"
490
+ - "What queries are in the Runner Dashboard report?"
491
+
492
+ </details>
493
+
494
+ <details>
495
+ <summary id="mode_list_queries"><strong><code>mode_list_queries</code></strong></summary>
496
+
497
+ Lists all queries within a report, including their full SQL content.
498
+
499
+ **Parameters:**
500
+ - `report_token` (required) — the report token
501
+
502
+ **Returns:** Query names, tokens, raw SQL previews, data source IDs, and timestamps.
503
+
504
+ **Example prompts:**
505
+ - "Show me the SQL for all queries in report 578c18655469"
506
+ - "What queries does the Runner Dashboard use?"
507
+
508
+ </details>
509
+
510
+ <details>
511
+ <summary id="mode_run_report"><strong><code>mode_run_report</code></strong></summary>
512
+
513
+ Triggers a new run of a Mode report. This is non-destructive — it executes the report's queries but does not modify the report itself.
514
+
515
+ **Parameters:**
516
+ - `report_token` (required) — the report token
517
+ - `parameters` (optional) — key/value parameters to pass to the report run
518
+
519
+ **Returns:** Run token and state. Use `mode_get_run_status` to poll for completion.
520
+
521
+ **Example prompts:**
522
+ - "Run report 578c18655469"
523
+ - "Trigger the weekly metrics report"
524
+
525
+ </details>
526
+
527
+ <details>
528
+ <summary id="mode_get_run_status"><strong><code>mode_get_run_status</code></strong></summary>
529
+
530
+ Checks the status of a report run — whether it has completed, is still running, or failed.
531
+
532
+ **Parameters:**
533
+ - `report_token` (required) — the report token
534
+ - `run_token` (required) — the run token returned by `mode_run_report`
535
+
536
+ **Returns:** Run state, creation time, and completion time.
537
+
538
+ </details>
539
+
540
+ <details>
541
+ <summary id="mode_get_query_results"><strong><code>mode_get_query_results</code></strong></summary>
542
+
543
+ Downloads the results of a completed query run. Returns data as a markdown table and optionally saves a CSV file.
544
+
545
+ **Parameters:**
546
+ - `report_token` (required) — the report token
547
+ - `run_token` (required) — the run token
548
+ - `query_run_token` (required) — the query run token
549
+ - `max_rows` (optional) — maximum rows to return (default: 100)
550
+ - `output_dir` (optional) — directory to save results as CSV. If omitted, results are returned inline only.
551
+ - `filename` (optional) — CSV filename (default: `query-results.csv`)
552
+
553
+ **Returns:** Markdown table with row data. If `output_dir` is provided and writable, also saves the full CSV to disk.
554
+
555
+ **Example prompts:**
556
+ - "Get the results from the latest run of report 578c18655469"
557
+ - "Download the query results as a CSV to /output"
558
+
559
+ </details>
560
+
561
+ <details>
562
+ <summary id="mode_execute_and_fetch"><strong><code>mode_execute_and_fetch</code></strong></summary>
563
+
564
+ The highest-value tool — runs a report, waits for completion (with progressive backoff polling), and returns the query results, all in a single call. Optionally saves results as CSV.
565
+
566
+ **Parameters:**
567
+ - `report_token` (required) — the report token
568
+ - `query_index` (optional) — which query's results to return, 0-based (default: 0)
569
+ - `parameters` (optional) — report parameters
570
+ - `max_rows` (optional) — maximum rows to return (default: 100)
571
+ - `timeout_seconds` (optional) — max seconds to wait (default: 300). Increase for reports with heavy queries.
572
+ - `output_dir` (optional) — directory to save results as CSV. If omitted, results are returned inline only.
573
+ - `filename` (optional) — CSV filename. Defaults to the report name (e.g. `runner-dashboard.csv`).
574
+
575
+ **Returns:** Report and query name, markdown table of results, total row count, and run metadata. If `output_dir` is provided and writable, also saves the full CSV to disk.
576
+
577
+ **Example prompts:**
578
+ - "Run report 578c18655469 and show me the results"
579
+ - "Execute the Runner Dashboard and save the results as CSV"
580
+
581
+ </details>
582
+
583
+ <details>
584
+ <summary id="mode_list_report_runs"><strong><code>mode_list_report_runs</code></strong></summary>
585
+
586
+ Lists past runs for a report with their status and timestamps. Useful for finding run tokens without triggering a new run.
587
+
588
+ **Parameters:**
589
+ - `report_token` (required) — the report token
590
+
591
+ **Returns:** Numbered list of runs with tokens, states (succeeded/failed), and timestamps.
592
+
593
+ **Example prompts:**
594
+ - "Show me the past runs for report 578c18655469"
595
+ - "Has the Runner Dashboard been run recently?"
596
+
597
+ </details>
598
+
599
+ <details>
600
+ <summary id="mode_get_query_run"><strong><code>mode_get_query_run</code></strong></summary>
601
+
602
+ Gets details on a specific query run, including state, timing, SQL, and error details if the query failed.
603
+
604
+ **Parameters:**
605
+ - `report_token` (required) — the report token
606
+ - `run_token` (required) — the run token
607
+ - `query_run_token` (required) — the query run token
608
+
609
+ **Returns:** Query run state, query name, data source, timestamps, SQL preview, and error message if failed.
610
+
611
+ **Example prompts:**
612
+ - "Why did this query fail?"
613
+ - "Show me the details of query run abc123"
614
+
615
+ </details>
616
+
617
+ <details>
618
+ <summary id="mode_search_reports"><strong><code>mode_search_reports</code></strong></summary>
619
+
620
+ Searches reports by name with case-insensitive substring matching. Searches across all spaces by default.
621
+
622
+ **Parameters:**
623
+ - `query` (required) — search string to match against report names
624
+ - `space_token` (optional) — limit search to a specific space
625
+
626
+ **Returns:** Numbered list of matching reports with tokens, spaces, and last run timestamps.
627
+
628
+ **Example prompts:**
629
+ - "Find reports about flaky tests"
630
+ - "Search for 'runner' in our Mode reports"
631
+
632
+ </details>
633
+
634
+ <details>
635
+ <summary id="mode_get_report_parameters"><strong><code>mode_get_report_parameters</code></strong></summary>
636
+
637
+ Discovers what parameters a report accepts before running it. Inspects the last successful run's form fields for parameter names, types, defaults, last-used values, and available dropdown options. If no prior runs exist, falls back to scanning query SQL for `{{ template_variable }}` patterns.
638
+
639
+ **Parameters:**
640
+ - `report_token` (required) — the report token
641
+
642
+ **Returns:** Parameter names, types (text, dropdown, etc.), default values, last-used values, and available options.
643
+
644
+ **Example prompts:**
645
+ - "What parameters does this report need?"
646
+ - "Check the inputs for the Cost Optimization report before running it"
647
+ - "Does report 3d3b95474b93 require any parameters?"
648
+
649
+ > **Note:** Always call this before `mode_execute_and_fetch`, `mode_run_report`, or `mode_export_report` — reports run with default parameters rarely produce the desired results.
650
+
651
+ </details>
652
+
653
+ <details>
654
+ <summary id="mode_list_spaces"><strong><code>mode_list_spaces</code></strong></summary>
655
+
656
+ Lists all spaces (collections) in the workspace. Spaces organize reports into groups.
657
+
658
+ **Parameters:**
659
+ - `filter` (optional) — `all` (default) or `custom` (non-personal spaces only)
660
+
661
+ **Returns:** Numbered list of spaces with tokens, types, and restricted status.
662
+
663
+ **Example prompts:**
664
+ - "What spaces are in our Mode workspace?"
665
+ - "List all collections"
666
+
667
+ </details>
668
+
669
+ <details>
670
+ <summary id="mode_list_data_sources"><strong><code>mode_list_data_sources</code></strong></summary>
671
+
672
+ Lists all connected data sources (databases) in the workspace.
673
+
674
+ **Parameters:** None.
675
+
676
+ **Returns:** Numbered list of data sources with adapter types (e.g. Snowflake, Postgres), hosts, and databases.
677
+
678
+ **Example prompts:**
679
+ - "What databases are connected to Mode?"
680
+ - "Show me our data sources"
681
+
682
+ </details>
683
+
684
+ <details>
685
+ <summary id="mode_list_definitions"><strong><code>mode_list_definitions</code></strong></summary>
686
+
687
+ Lists saved SQL definitions (reusable snippets/macros) available in the workspace.
688
+
689
+ **Parameters:** None.
690
+
691
+ **Returns:** Numbered list of definitions with SQL previews and associated data sources.
692
+
693
+ **Example prompts:**
694
+ - "What SQL definitions do we have?"
695
+ - "Show me our saved SQL snippets"
696
+
697
+ > **Note:** This endpoint may not be available on all Mode plan tiers.
698
+
699
+ </details>
700
+
701
+ <details>
702
+ <summary id="mode_export_report"><strong><code>mode_export_report</code></strong></summary>
703
+
704
+ Exports a report as PDF or CSV. For PDF, triggers generation with progressive backoff polling. For CSV, fetches raw query results. If `output_dir` is provided and writable, saves the file to disk.
705
+
706
+ **Parameters:**
707
+ - `report_token` (required) — the report token
708
+ - `format` (required) — `pdf` for a rendered report with charts, `csv` for raw query data
709
+ - `run_token` (optional) — the run token. If omitted, uses the last successful run.
710
+ - `query_index` (optional) — for CSV: which query to export, 0-based (default: 0). Ignored for PDF.
711
+ - `filename` (optional) — output filename (default: report name with `.pdf` or `.csv` extension)
712
+ - `output_dir` (optional) — directory to save the file. If omitted or not writable, returns a download URL (PDF) or inline preview (CSV).
713
+
714
+ **Returns:** Saved file path/size if written to disk, download URL for PDF, or inline CSV preview for small datasets.
715
+
716
+ > **Note:** Always call `mode_get_report_parameters` first to check if the report requires parameters. If it does, run it with `mode_run_report` (passing the right parameters) and use the resulting `run_token` here.
717
+
718
+ **Example prompts:**
719
+ - "Export the Runner Dashboard as PDF"
720
+ - "Download the Cost Optimization report as CSV for org infusionsoft"
721
+ - "Get a PDF of report 3d3b95474b93 for billing org common-room"
722
+
723
+ </details>
724
+
725
+ ## Configuration
726
+
727
+ | Variable | Required | Description |
728
+ |----------|----------|-------------|
729
+ | `MODE_WORKSPACE` | Yes | Your Mode workspace name (the slug in Mode URLs) |
730
+ | `MODE_API_TOKEN` | Yes | API token from Mode |
731
+ | `MODE_API_SECRET` | Yes | API secret from Mode |
732
+ | `MODE_MAX_ROWS` | No | Max rows returned per query (default: `100`) |
733
+ | `MODE_POLL_INTERVAL_MS` | No | Initial polling interval in ms (default: `2000`). Increases with progressive backoff. |
734
+ | `MODE_POLL_TIMEOUT_MS` | No | Max time to wait for a run/export in ms (default: `300000` / 5 minutes) |
735
+ | `MODE_BASE_URL` | No | Mode API base URL (default: `https://app.mode.com`) |
736
+ | `MODE_MAX_OUTPUT_LENGTH` | No | Max characters in tool output before truncation (default: `50000`) |
737
+
738
+ ## CI/CD
739
+
740
+ This project uses [CircleCI](https://circleci.com) for continuous integration and deployment.
741
+
742
+ **Every push:**
743
+ - **Lint** — TypeScript type checking + ESLint
744
+ - **Test** — full test suite with coverage reporting (JUnit for Test Insights)
745
+ - **Audit Dependencies** — npm audit + Trivy filesystem scan (vulns, secrets, misconfig, licenses) + SBOM generation
746
+ - **Build Docker Image** — multi-arch (amd64 + arm64), pushed with SHA tags
747
+ - **Test Docker Image** — dgoss container structure tests per architecture
748
+ - **Scan Docker Image** — Trivy vulnerability + license scan per architecture
749
+
750
+ **On `v*` tags (releases):**
751
+ - **Publish Docker Image** — retags SHA images, creates multi-arch manifest with version + latest tags
752
+ - **Publish npm Package** — publishes `@cci-labs/mode-mcp` to npm
753
+
754
+ ## Safety
755
+
756
+ This server is **read-only by design**. It cannot create, modify, or delete any resources in Mode. The only "write" operations are:
757
+ - **Triggering report runs** — executes existing queries but does not change the report
758
+ - **Triggering PDF exports** — generates a PDF from an existing run
759
+
760
+ ## Troubleshooting
761
+
762
+ <details>
763
+ <summary><strong>Authentication Issues</strong></summary>
764
+
765
+ - **401 Unauthorized:** Verify your `MODE_API_TOKEN` and `MODE_API_SECRET` at `app.mode.com/{workspace}/settings/api_tokens`
766
+ - **403 Forbidden:** Your token may lack permission. Workspace admin tokens are required for some endpoints.
767
+ - **"account not found":** Double-check `MODE_WORKSPACE` — it should be the slug from your Mode URL (e.g. `my-company`, not the display name).
768
+
769
+ </details>
770
+
771
+ <details>
772
+ <summary><strong>Rate Limiting</strong></summary>
773
+
774
+ Mode's API allows 40 requests per 10-second window (~4 req/sec). The server automatically:
775
+ - Throttles to stay under the limit (35 req/10s with headroom)
776
+ - Reads `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers from every response
777
+ - Proactively slows down when remaining requests < 5
778
+ - Retries on 429 responses using the server-provided reset time
779
+ - Retries transient 5xx errors up to 3 times with exponential backoff
780
+
781
+ </details>
782
+
783
+ <details>
784
+ <summary><strong>Timeouts and Long-Running Reports</strong></summary>
785
+
786
+ The server uses **progressive backoff** when polling for report runs and PDF exports:
787
+ - Starts polling every 2 seconds
788
+ - Increases interval by 1.5x each poll (2s → 3s → 4.5s → 6.75s → 10s → 15s)
789
+ - Caps at 15-second intervals
790
+ - Default maximum wait: 5 minutes (`MODE_POLL_TIMEOUT_MS=300000`)
791
+
792
+ For reports with heavy queries (e.g. large Snowflake warehouses), you can:
793
+ - Pass a higher `timeout_seconds` to `mode_execute_and_fetch` (e.g. `600` for 10 minutes)
794
+ - Increase the global default with `MODE_POLL_TIMEOUT_MS`
795
+ - Use `mode_run_report` + `mode_get_run_status` to poll manually
796
+
797
+ </details>
798
+
799
+ <details>
800
+ <summary><strong>Docker Issues</strong></summary>
801
+
802
+ - **Environment variables not reaching container:** Ensure you have both the `-e VAR_NAME` in `args` AND the value in `env`. The MCP client sets the env var, and `-e` forwards it into the container.
803
+ - **Image not found:** Build with `docker build -t ccilabs/mode-mcp .` from the project root.
804
+ - **Network errors inside container:** Docker Desktop must be running. On macOS, ensure Docker has network access.
805
+ - **File downloads not working:** When running in Docker, the `output_dir` parameter writes inside the container. To save files to your host machine, add a volume mount to your Docker args:
806
+ ```json
807
+ "args": [
808
+ "run", "--rm", "-i",
809
+ "-e", "MODE_WORKSPACE",
810
+ "-e", "MODE_API_TOKEN",
811
+ "-e", "MODE_API_SECRET",
812
+ "-v", "/Users/you/Downloads:/output",
813
+ "ccilabs/mode-mcp"
814
+ ]
815
+ ```
816
+ Then use `/output` as the `output_dir`. Without a mount, the tools still return download URLs as a fallback.
817
+
818
+ </details>
819
+
820
+ <details>
821
+ <summary><strong>Common Errors</strong></summary>
822
+
823
+ - **"Report not found" (404):** The report token may be wrong. Use `mode_list_reports` or `mode_search_reports` to find valid tokens. Report tokens are the alphanumeric strings in Mode URLs (e.g. `578c18655469`).
824
+ - **Empty results:** The report may not have been run recently. Use `mode_execute_and_fetch` to trigger a fresh run.
825
+ - **Polling timeout:** Increase `timeout_seconds` for long-running reports, or increase `MODE_POLL_TIMEOUT_MS`.
826
+ - **"PDF export is not available":** The export may have expired. Run the report again with `mode_run_report`, then export the new run.
827
+
828
+ </details>
829
+
830
+ # Development
831
+
832
+ ## Getting Started
833
+
834
+ 1. Clone the repository:
835
+
836
+ ```bash
837
+ git clone https://github.com/AwesomeCICD/mode-mcp.git
838
+ cd mode-mcp
839
+ ```
840
+
841
+ 2. Install dependencies:
842
+
843
+ ```bash
844
+ npm install
845
+ ```
846
+
847
+ 3. Build the project:
848
+
849
+ ```bash
850
+ npm run build
851
+ ```
852
+
853
+ 4. Create a `.env` file:
854
+
855
+ ```
856
+ MODE_WORKSPACE=your-workspace
857
+ MODE_API_TOKEN=your-token
858
+ MODE_API_SECRET=your-secret
859
+ ```
860
+
861
+ 5. Run linting and tests:
862
+
863
+ ```bash
864
+ npm run lint # TypeScript type check + ESLint
865
+ npm test # Run all tests
866
+ ```
867
+
868
+ ## Building Docker Container
869
+
870
+ ```bash
871
+ docker build -t ccilabs/mode-mcp .
872
+ ```
873
+
874
+ To test the container:
875
+
876
+ ```bash
877
+ export MODE_WORKSPACE=your-workspace
878
+ export MODE_API_TOKEN=your-token
879
+ export MODE_API_SECRET=your-secret
880
+ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' \
881
+ | docker run --rm -i -e MODE_WORKSPACE -e MODE_API_TOKEN -e MODE_API_SECRET ccilabs/mode-mcp
882
+ ```
883
+
884
+ ## Project Structure
885
+
886
+ ```
887
+ src/
888
+ ├── index.ts # MCP server entry point (stdio transport)
889
+ ├── config.ts # Environment variable loading and validation
890
+ ├── mode-client.ts # Mode API HTTP client (auth, rate limiting, retries, HAL+JSON)
891
+ ├── file-utils.ts # File saving utilities (slugify, trySaveFile)
892
+ ├── truncate.ts # Output truncation for LLM context limits
893
+ ├── utils.ts # Shared formatters (dates, errors, markdown tables, CSV parsing)
894
+ ├── __tests__/ # Unit tests (vitest, all mocked — no live API needed)
895
+ └── tools/
896
+ ├── analytics.ts # Report, query, run, search, filter, and chart tools
897
+ ├── management.ts # Spaces, data sources, definitions, schedules, subscriptions, audit logs
898
+ ├── datasets.ts # Dataset discovery and field inspection tools
899
+ └── distribution.ts # PDF and CSV export tools
900
+ docs/
901
+ ├── MCP_TEST_PLAYBOOK.md # Manual integration test prompts (34 tests across 9 categories)
902
+ ```
903
+
904
+ ## Testing
905
+
906
+ ```bash
907
+ npm test # Run all tests
908
+ npm run test:watch # Watch mode
909
+ npx vitest run --coverage # Run with coverage report
910
+ ```
911
+
912
+ 129+ tests covering all 24 tools, CSV parsing, Mode client (auth, rate limiting, retries, polling), config loading, file utilities, and output formatting. All tests use mocked API responses — no live credentials required.
913
+
914
+ ### Integration Testing
915
+
916
+ For manual integration testing with a live MCP connection, see the [MCP Test Playbook](docs/MCP_TEST_PLAYBOOK.md) — 34 structured prompts across 9 categories that validate tool calls, chaining, error handling, and read-only safety.
917
+
918
+ ### Docker Testing
919
+
920
+ Container structure tests use [dgoss](https://github.com/goss-org/goss/tree/master/extras/dgoss):
921
+
922
+ ```bash
923
+ # Requires goss/dgoss installed and a Linux goss binary for container exec
924
+ dgoss run --entrypoint /bin/sh ccilabs/mode-mcp:test -c "sleep 30"
925
+ ```