@quarri/claude-data-tools 1.1.1 → 1.2.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 CHANGED
@@ -1,98 +1,163 @@
1
- # Quarri Claude Code Plugin
1
+ # Quarri Claude Plugin
2
2
 
3
3
  Natural language data analysis with Quarri. Query databases, create visualizations, and get insights using plain English.
4
4
 
5
5
  ## Installation
6
6
 
7
- ### Via Claude Code Marketplace
7
+ ### Claude Code
8
8
 
9
9
  ```bash
10
10
  claude /install quarri
11
11
  ```
12
12
 
13
- ### Manual Installation
13
+ Or manually add to your Claude Code configuration:
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "quarri": {
19
+ "command": "npx",
20
+ "args": ["@quarri/claude-data-tools"]
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ ### Claude Desktop
27
+
28
+ 1. **Authenticate first** (one-time setup):
14
29
 
15
30
  ```bash
16
- npm install -g @quarri/claude-data-tools
31
+ # New users - create a free trial account
32
+ npx @quarri/claude-data-tools signup
33
+
34
+ # Existing users - log in
35
+ npx @quarri/claude-data-tools auth
17
36
  ```
18
37
 
19
- Then add to your Claude Code configuration:
38
+ 2. **Add to Claude Desktop config**:
39
+
40
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
41
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
20
42
 
21
43
  ```json
22
44
  {
23
45
  "mcpServers": {
24
46
  "quarri": {
25
- "command": "quarri-mcp"
47
+ "command": "npx",
48
+ "args": ["@quarri/claude-data-tools"]
26
49
  }
27
50
  }
28
51
  }
29
52
  ```
30
53
 
54
+ 3. **Restart Claude Desktop**
55
+
31
56
  ## Authentication
32
57
 
33
- On first use, you'll be prompted to authenticate:
58
+ ### New Users
59
+
60
+ Create a free trial account:
61
+
62
+ ```bash
63
+ npx @quarri/claude-data-tools signup
64
+ ```
34
65
 
35
- ### Existing Quarri Users
66
+ You'll receive a verification email to complete signup.
67
+
68
+ ### Existing Users
69
+
70
+ ```bash
71
+ npx @quarri/claude-data-tools auth
72
+ ```
36
73
 
37
74
  1. Enter your email address
38
75
  2. Check your email for a 6-digit verification code
39
76
  3. Enter the code to complete authentication
40
77
 
41
- ### New Users (with invitation)
42
-
43
- 1. Select "invite" when prompted
44
- 2. Enter your email address
45
- 3. Enter the invitation token from your admin's email
46
- 4. Your account will be created automatically
78
+ Credentials are stored securely in `~/.quarri/credentials`.
47
79
 
48
80
  ## Features
49
81
 
50
- ### 47 Tools for Data Analysis
51
-
52
- **Sub-Agents (12):**
53
- - `quarri_query_agent` - Generate SQL from natural language
54
- - `quarri_explain_agent` - Explain SQL queries
55
- - `quarri_chart_agent` - Generate visualizations
56
- - `quarri_metric_builder_agent` - Define metrics interactively
57
- - `quarri_planning_agent` - Create analysis plans
58
- - `quarri_insight_agent` - Generate actionable insights
59
- - `quarri_stats_agent` - Statistical analysis
60
- - `quarri_staging_agent` - Stage raw data
61
- - `quarri_modeling_agent` - Design data models
62
- - `quarri_transformers_agent` - Generate transformation SQL
63
- - `quarri_extraction_agent` - Create data pipelines
64
- - `quarri_query_with_analysis` - Full analysis pipeline
65
-
66
- **Data Tools (8):**
67
- - `quarri_execute_sql` - Run SQL queries
68
- - `quarri_get_schema` - View database schema
69
- - `quarri_search_values` - Semantic search
70
- - `quarri_get_metrics`, `quarri_create_metric`, `quarri_approve_metric`
71
- - `quarri_get_metric_detail`, `quarri_search_metrics`
72
-
73
- **Configuration (8):**
74
- - `quarri_list_agent_prompts`, `quarri_update_agent_prompt`
75
- - `quarri_list_rules`, `quarri_create_rule`, `quarri_update_rule`, `quarri_delete_rule`
76
- - `quarri_vectorize_column_values`, `quarri_list_searchable_columns`
77
-
78
- **Canvas (5):**
79
- - `quarri_list_canvases`, `quarri_get_canvas`
80
- - `quarri_create_chart_panel`, `quarri_update_chart_panel`
81
- - `quarri_export_canvas`
82
-
83
- **Team (3):**
84
- - `quarri_list_teams`, `quarri_get_team_filters`, `quarri_get_team_restrictions`
85
-
86
- **Extraction (7):**
87
- - `quarri_list_extraction_sources`, `quarri_configure_extraction`
88
- - `quarri_discover_tables`, `quarri_propose_transformation`
89
- - `quarri_upload_csv`, `quarri_generate_quarri_schema`, `quarri_list_raw_tables`
90
-
91
- **Debug (3):**
92
- - `quarri_read_server_logs`, `quarri_query_repl_activity`, `quarri_read_fly_logs`
93
-
94
- **Session (2):**
95
- - `quarri_list_databases`, `quarri_select_database`
82
+ ### Interactive UI Components (MCP Apps)
83
+
84
+ Quarri returns rich, interactive UI components for data display:
85
+
86
+ - **Data Tables** - Sortable, paginated query results
87
+ - **Charts** - Interactive Plotly visualizations
88
+ - **Schema Explorer** - Visual database structure
89
+ - **Metrics Cards** - KPI displays with status badges
90
+ - **Logs View** - Syntax-highlighted log entries
91
+ - **Code View** - Syntax-highlighted source code
92
+
93
+ These render automatically in Claude Desktop and Claude Code when using supported tools.
94
+
95
+ ### 39 Tools for Data Analysis
96
+
97
+ **Data Tools:**
98
+ - `quarri_execute_sql` - Run SQL queries → interactive data table
99
+ - `quarri_get_schema` - View database schema → schema explorer
100
+ - `quarri_search_values` - Semantic search → search results
101
+ - `quarri_get_metrics` - List defined metrics → metrics list
102
+ - `quarri_create_metric` - Define new metrics
103
+ - `quarri_approve_metric` - Approve pending metrics
104
+ - `quarri_get_metric_detail` - Metric details → metric card
105
+ - `quarri_search_metrics` - Search metrics → metrics list
106
+
107
+ **Configuration:**
108
+ - `quarri_list_agent_prompts` - View agent prompts → prompts list
109
+ - `quarri_update_agent_prompt` - Update prompts
110
+ - `quarri_list_rules` - Query generation rules → rules list
111
+ - `quarri_create_rule`, `quarri_update_rule`, `quarri_delete_rule`
112
+ - `quarri_vectorize_column_values` - Enable semantic search
113
+ - `quarri_list_searchable_columns` - View searchable columns → columns list
114
+
115
+ **Team & Security:**
116
+ - `quarri_list_teams` - Organization teams → teams list
117
+ - `quarri_get_team_filters` - Row-level security → filters view
118
+ - `quarri_get_team_restrictions` - Column restrictions → restrictions view
119
+
120
+ **Data Extraction:**
121
+ - `quarri_list_extraction_sources` - Data sources → sources list
122
+ - `quarri_configure_extraction` - Configure sources
123
+ - `quarri_discover_tables` - Discover tables → tables list
124
+ - `quarri_propose_transformation` - Propose transforms
125
+ - `quarri_upload_csv` - Upload CSV files
126
+ - `quarri_generate_quarri_schema` - Generate schema config
127
+ - `quarri_list_raw_tables` - Raw tables → tables list
128
+
129
+ **Connectors:**
130
+ - `quarri_get_connector_code` - View connector code → code view
131
+ - `quarri_get_connector_logs` - Connector logs → logs view
132
+ - `quarri_log_analysis_run` - Log analysis runs
133
+ - `quarri_schedule_extraction` - Schedule extractions
134
+ - `quarri_store_generated_code` - Save connector code
135
+ - `quarri_update_connector_code` - Update connector code
136
+
137
+ **Debug:**
138
+ - `quarri_read_server_logs` - Server logs → logs view
139
+ - `quarri_query_repl_activity` - Activity history → activity list
140
+ - `quarri_read_fly_logs` - Production logs → logs view
141
+
142
+ **Session:**
143
+ - `quarri_auth_status` - Check auth → auth status card
144
+ - `quarri_trial_status` - Trial info → trial status card
145
+ - `quarri_list_databases` - Available databases
146
+ - `quarri_select_database` - Switch database
147
+
148
+ ### Skills (Slash Commands)
149
+
150
+ Quarri includes intelligent skills for common workflows:
151
+
152
+ - `/quarri-query` - Natural language to SQL
153
+ - `/quarri-analyze` - Full analysis pipeline
154
+ - `/quarri-chart` - Generate visualizations
155
+ - `/quarri-insights` - Statistical insights
156
+ - `/quarri-metric` - Define metrics interactively
157
+ - `/quarri-explain` - Explain SQL queries
158
+ - `/quarri-extract` - Create data pipelines
159
+ - `/quarri-diagnose` - Debug connector issues
160
+ - `/quarri-guide` - Quarri usage guide
96
161
 
97
162
  ## Usage Examples
98
163
 
@@ -102,64 +167,58 @@ On first use, you'll be prompted to authenticate:
102
167
  What were our top 10 products by revenue last month?
103
168
  ```
104
169
 
105
- Claude will use `quarri_query_with_analysis` to:
106
- 1. Generate SQL from your question
107
- 2. Execute the query
108
- 3. Perform statistical analysis
109
- 4. Generate a chart
110
- 5. Provide key insights
170
+ Claude will generate SQL, execute it, and return results in an interactive data table.
111
171
 
112
- ### Create a Metric
172
+ ### Create a Visualization
113
173
 
114
174
  ```
115
- Help me define a metric for customer lifetime value
175
+ /quarri-chart Show monthly revenue trend for 2024
116
176
  ```
117
177
 
118
- Claude will guide you through defining the metric using `quarri_metric_builder_agent`.
178
+ Returns an interactive Plotly chart rendered in the conversation.
119
179
 
120
- ### Configure Semantic Search
180
+ ### Define a Metric
121
181
 
122
182
  ```
123
- Enable semantic search on the product_name column
183
+ /quarri-metric Help me define customer lifetime value
124
184
  ```
125
185
 
126
- Claude will use `quarri_vectorize_column_values` to enable natural language search.
186
+ Guides you through defining a reusable metric with SQL template.
187
+
188
+ ### Configure Semantic Search
127
189
 
128
- ## Configuration
190
+ ```
191
+ Enable semantic search on the product_name column
192
+ ```
129
193
 
130
- Credentials are stored in `~/.quarri/credentials` with secure permissions (0600).
194
+ Uses `quarri_vectorize_column_values` to enable natural language search on column values.
131
195
 
132
- ### Environment Variables
196
+ ## Environment Variables
133
197
 
134
198
  - `QUARRI_API_URL` - API endpoint (default: https://app.quarri.ai)
135
199
 
136
200
  ## Development
137
201
 
138
- ### Building
139
-
140
202
  ```bash
203
+ # Install dependencies
141
204
  npm install
142
- npm run build
143
- ```
144
205
 
145
- ### Testing
206
+ # Build
207
+ npm run build
146
208
 
147
- ```bash
209
+ # Run tests
148
210
  npm test
149
- ```
150
-
151
- ### Local Development
152
211
 
153
- ```bash
212
+ # Local development
154
213
  npm run dev
155
214
  ```
156
215
 
157
- ## License
158
-
159
- MIT
160
-
161
216
  ## Support
162
217
 
163
218
  - GitHub Issues: https://github.com/djayatillake/quarri-claude-plugin/issues
164
219
  - Documentation: https://docs.quarri.ai
165
220
  - Email: support@quarri.ai
221
+
222
+ ## License
223
+
224
+ MIT
package/dist/index.js CHANGED
@@ -13,15 +13,17 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
13
13
  const client_js_1 = require("./api/client.js");
14
14
  const token_store_js_1 = require("./auth/token-store.js");
15
15
  const definitions_js_1 = require("./tools/definitions.js");
16
+ const resources_js_1 = require("./ui/resources.js");
16
17
  // Initialize API client
17
18
  const client = new client_js_1.QuarriApiClient();
18
- // Initialize MCP server
19
+ // Initialize MCP server with resources capability
19
20
  const server = new index_js_1.Server({
20
21
  name: 'quarri',
21
22
  version: '1.0.0',
22
23
  }, {
23
24
  capabilities: {
24
25
  tools: {},
26
+ resources: {},
25
27
  },
26
28
  });
27
29
  /**
@@ -56,6 +58,40 @@ Not authenticated with Quarri.
56
58
  After authenticating, restart Claude Code to pick up the credentials.
57
59
  `.trim();
58
60
  }
61
+ /**
62
+ * Handle list resources request
63
+ */
64
+ server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => {
65
+ return {
66
+ resources: resources_js_1.UI_RESOURCES.map((resource) => ({
67
+ uri: resource.uri,
68
+ name: resource.name,
69
+ description: resource.description,
70
+ mimeType: resource.mimeType,
71
+ })),
72
+ };
73
+ });
74
+ /**
75
+ * Handle read resource request
76
+ */
77
+ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
78
+ const { uri } = request.params;
79
+ const resource = resources_js_1.UI_RESOURCES.find((r) => r.uri === uri);
80
+ if (!resource) {
81
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, `Unknown resource: ${uri}`);
82
+ }
83
+ // Resources are populated dynamically by tool responses
84
+ // This handler returns an empty template
85
+ return {
86
+ contents: [
87
+ {
88
+ uri: resource.uri,
89
+ mimeType: resource.mimeType,
90
+ text: JSON.stringify({ type: resource.name, data: null }),
91
+ },
92
+ ],
93
+ };
94
+ });
59
95
  /**
60
96
  * Handle list tools request
61
97
  */
@@ -65,6 +101,7 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
65
101
  name: tool.name,
66
102
  description: tool.description,
67
103
  inputSchema: tool.inputSchema,
104
+ ...(tool._meta && { _meta: tool._meta }),
68
105
  })),
69
106
  };
70
107
  });
@@ -78,14 +115,23 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
78
115
  const credentials = (0, token_store_js_1.loadCredentials)();
79
116
  const selected = (0, token_store_js_1.getSelectedDatabase)();
80
117
  if (!credentials) {
118
+ const unauthData = {
119
+ authenticated: false,
120
+ message: 'Not authenticated. Run: npx @quarri/claude-data-tools auth',
121
+ };
81
122
  return {
82
123
  content: [
83
124
  {
84
125
  type: 'text',
85
- text: JSON.stringify({
86
- authenticated: false,
87
- message: 'Not authenticated. Run: npx @quarri/claude-data-tools auth',
88
- }, null, 2),
126
+ text: JSON.stringify(unauthData, null, 2),
127
+ },
128
+ {
129
+ type: 'resource',
130
+ resource: {
131
+ uri: 'ui://quarri/auth-status',
132
+ mimeType: 'application/vnd.quarri.auth-status+json',
133
+ text: JSON.stringify((0, resources_js_1.renderAuthStatus)({ authenticated: false })),
134
+ },
89
135
  },
90
136
  ],
91
137
  };
@@ -93,19 +139,34 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
93
139
  const expiresAt = new Date(credentials.expiresAt);
94
140
  const isExpired = expiresAt < new Date();
95
141
  const expiresIn = Math.round((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
142
+ const authData = {
143
+ authenticated: !isExpired,
144
+ email: credentials.email,
145
+ role: credentials.role,
146
+ databases: credentials.databases.map(d => d.display_name || d.database_name),
147
+ selectedDatabase: selected,
148
+ tokenExpires: credentials.expiresAt,
149
+ expiresInDays: isExpired ? 'EXPIRED' : expiresIn,
150
+ };
96
151
  return {
97
152
  content: [
98
153
  {
99
154
  type: 'text',
100
- text: JSON.stringify({
101
- authenticated: !isExpired,
102
- email: credentials.email,
103
- role: credentials.role,
104
- databases: credentials.databases.map(d => d.display_name || d.database_name),
105
- selectedDatabase: selected,
106
- tokenExpires: credentials.expiresAt,
107
- expiresInDays: isExpired ? 'EXPIRED' : expiresIn,
108
- }, null, 2),
155
+ text: JSON.stringify(authData, null, 2),
156
+ },
157
+ {
158
+ type: 'resource',
159
+ resource: {
160
+ uri: 'ui://quarri/auth-status',
161
+ mimeType: 'application/vnd.quarri.auth-status+json',
162
+ text: JSON.stringify((0, resources_js_1.renderAuthStatus)({
163
+ authenticated: !isExpired,
164
+ email: credentials.email,
165
+ role: credentials.role,
166
+ databases: credentials.databases.map(d => d.display_name || d.database_name),
167
+ expiresInDays: isExpired ? 'EXPIRED' : expiresIn,
168
+ })),
169
+ },
109
170
  },
110
171
  ],
111
172
  };
@@ -177,35 +238,62 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
177
238
  }
178
239
  const data = result.data;
179
240
  if (!data || !data.is_trial) {
241
+ const nonTrialData = {
242
+ is_trial: false,
243
+ message: 'This is not a trial account.',
244
+ database: data?.display_name || data?.database_name,
245
+ };
180
246
  return {
181
247
  content: [
182
248
  {
183
249
  type: 'text',
184
- text: JSON.stringify({
185
- is_trial: false,
186
- message: 'This is not a trial account.',
187
- database: data?.display_name || data?.database_name,
188
- }, null, 2),
250
+ text: JSON.stringify(nonTrialData, null, 2),
251
+ },
252
+ {
253
+ type: 'resource',
254
+ resource: {
255
+ uri: 'ui://quarri/trial-status',
256
+ mimeType: 'application/vnd.quarri.trial-status+json',
257
+ text: JSON.stringify((0, resources_js_1.renderTrialStatus)({
258
+ is_trial: false,
259
+ organization: data?.display_name || data?.database_name,
260
+ })),
261
+ },
189
262
  },
190
263
  ],
191
264
  };
192
265
  }
266
+ const trialData = {
267
+ is_trial: true,
268
+ organization: data.display_name,
269
+ days_remaining: data.days_remaining,
270
+ expires_at: data.expires_at,
271
+ data_limit_gb: data.data_limit_gb,
272
+ signup_type: data.signup_type,
273
+ upgrade_contact: data.upgrade_contact,
274
+ message: data.days_remaining && data.days_remaining <= 2
275
+ ? `⚠️ Your trial expires in ${data.days_remaining} day(s). Contact ${data.upgrade_contact} to upgrade.`
276
+ : `Trial active with ${data.days_remaining} days remaining.`,
277
+ };
193
278
  return {
194
279
  content: [
195
280
  {
196
281
  type: 'text',
197
- text: JSON.stringify({
198
- is_trial: true,
199
- organization: data.display_name,
200
- days_remaining: data.days_remaining,
201
- expires_at: data.expires_at,
202
- data_limit_gb: data.data_limit_gb,
203
- signup_type: data.signup_type,
204
- upgrade_contact: data.upgrade_contact,
205
- message: data.days_remaining && data.days_remaining <= 2
206
- ? `⚠️ Your trial expires in ${data.days_remaining} day(s). Contact ${data.upgrade_contact} to upgrade.`
207
- : `Trial active with ${data.days_remaining} days remaining.`,
208
- }, null, 2),
282
+ text: JSON.stringify(trialData, null, 2),
283
+ },
284
+ {
285
+ type: 'resource',
286
+ resource: {
287
+ uri: 'ui://quarri/trial-status',
288
+ mimeType: 'application/vnd.quarri.trial-status+json',
289
+ text: JSON.stringify((0, resources_js_1.renderTrialStatus)({
290
+ is_trial: true,
291
+ organization: data.display_name,
292
+ days_remaining: data.days_remaining,
293
+ expires_at: data.expires_at,
294
+ data_limit_gb: data.data_limit_gb,
295
+ })),
296
+ },
209
297
  },
210
298
  ],
211
299
  };
@@ -252,15 +340,127 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
252
340
  isError: true,
253
341
  };
254
342
  }
343
+ // Build response with text and optional UI resource
344
+ const content = [
345
+ {
346
+ type: 'text',
347
+ text: formatToolResponse(name, result),
348
+ },
349
+ ];
350
+ // Add UI resource if available for this tool
351
+ const uiResourceContent = buildUIResource(name, result);
352
+ if (uiResourceContent) {
353
+ content.push(uiResourceContent);
354
+ }
355
+ return { content };
356
+ });
357
+ /**
358
+ * Build UI resource for a tool response
359
+ */
360
+ function buildUIResource(toolName, result) {
361
+ const uiResource = (0, resources_js_1.getToolUIResource)(toolName);
362
+ if (!uiResource)
363
+ return null;
364
+ let rendered = null;
365
+ switch (toolName) {
366
+ case 'quarri_execute_sql':
367
+ if (result.rows && Array.isArray(result.rows)) {
368
+ const rows = result.rows;
369
+ const columns = result.columns || (rows.length > 0 ? Object.keys(rows[0]) : []);
370
+ rendered = (0, resources_js_1.renderDataTable)(rows, columns);
371
+ }
372
+ break;
373
+ case 'quarri_get_schema':
374
+ if (result.tables && Array.isArray(result.tables)) {
375
+ rendered = (0, resources_js_1.renderSchemaExplorer)(result.tables);
376
+ }
377
+ break;
378
+ case 'quarri_search_values':
379
+ if (result.results && Array.isArray(result.results)) {
380
+ rendered = (0, resources_js_1.renderSearchResults)(result.results);
381
+ }
382
+ break;
383
+ case 'quarri_get_metrics':
384
+ case 'quarri_search_metrics':
385
+ if (result.metrics && Array.isArray(result.metrics)) {
386
+ rendered = (0, resources_js_1.renderMetricsList)(result.metrics);
387
+ }
388
+ break;
389
+ case 'quarri_get_metric_detail':
390
+ if (result.id) {
391
+ rendered = (0, resources_js_1.renderMetricDetail)(result);
392
+ }
393
+ break;
394
+ case 'quarri_list_agent_prompts':
395
+ if (result.prompts && Array.isArray(result.prompts)) {
396
+ rendered = (0, resources_js_1.renderPromptsList)(result.prompts);
397
+ }
398
+ break;
399
+ case 'quarri_list_rules':
400
+ if (result.rules && Array.isArray(result.rules)) {
401
+ rendered = (0, resources_js_1.renderRulesList)(result.rules);
402
+ }
403
+ break;
404
+ case 'quarri_list_searchable_columns':
405
+ if (result.columns && Array.isArray(result.columns)) {
406
+ rendered = (0, resources_js_1.renderColumnsList)(result.columns);
407
+ }
408
+ break;
409
+ case 'quarri_list_teams':
410
+ if (result.teams && Array.isArray(result.teams)) {
411
+ rendered = (0, resources_js_1.renderTeamsList)(result.teams);
412
+ }
413
+ break;
414
+ case 'quarri_get_team_filters':
415
+ if (result.filters && Array.isArray(result.filters)) {
416
+ rendered = (0, resources_js_1.renderTeamFilters)(result.filters);
417
+ }
418
+ break;
419
+ case 'quarri_get_team_restrictions':
420
+ if (result.restrictions && Array.isArray(result.restrictions)) {
421
+ rendered = (0, resources_js_1.renderTeamRestrictions)(result.restrictions);
422
+ }
423
+ break;
424
+ case 'quarri_list_extraction_sources':
425
+ if (result.sources && Array.isArray(result.sources)) {
426
+ rendered = (0, resources_js_1.renderSourcesList)(result.sources);
427
+ }
428
+ break;
429
+ case 'quarri_discover_tables':
430
+ case 'quarri_list_raw_tables':
431
+ if (result.tables && Array.isArray(result.tables)) {
432
+ rendered = (0, resources_js_1.renderTablesList)(result.tables);
433
+ }
434
+ break;
435
+ case 'quarri_read_server_logs':
436
+ case 'quarri_read_fly_logs':
437
+ case 'quarri_get_connector_logs':
438
+ if (result.logs && Array.isArray(result.logs)) {
439
+ rendered = (0, resources_js_1.renderLogsView)(result.logs);
440
+ }
441
+ break;
442
+ case 'quarri_query_repl_activity':
443
+ if (result.activities && Array.isArray(result.activities)) {
444
+ rendered = (0, resources_js_1.renderActivityList)(result.activities);
445
+ }
446
+ break;
447
+ case 'quarri_get_connector_code':
448
+ if (result.code && typeof result.code === 'string') {
449
+ rendered = (0, resources_js_1.renderCodeView)(result.code, result.language || 'python');
450
+ }
451
+ break;
452
+ }
453
+ if (!rendered)
454
+ return null;
255
455
  return {
256
- content: [
257
- {
258
- type: 'text',
259
- text: formatToolResponse(name, result),
260
- },
261
- ],
456
+ type: 'resource',
457
+ resource: {
458
+ uri: uiResource.uri,
459
+ mimeType: uiResource.mimeType,
460
+ text: JSON.stringify(rendered),
461
+ },
262
462
  };
263
- });
463
+ }
264
464
  /**
265
465
  * Format error response for better readability
266
466
  */