metabase-ai-assistant 3.4.2 → 3.4.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metabase-ai-assistant",
3
- "version": "3.4.2",
3
+ "version": "3.4.3",
4
4
  "mcpName": "io.github.enessari/metabase-ai-assistant",
5
5
  "description": "The most powerful MCP Server for Metabase - 111+ tools for AI-powered SQL generation, dashboard automation, user management & enterprise BI. Works with Claude, Cursor, and any MCP-compatible AI.",
6
6
  "main": "src/index.js",
package/src/mcp/server.js CHANGED
@@ -4198,11 +4198,39 @@ class MetabaseMCPServer {
4198
4198
 
4199
4199
  async handleAddCardToDashboard(args) {
4200
4200
  try {
4201
+ // Normalize position parameters (support both flat and nested structure)
4202
+ // AI sometimes sends flat: { row: 0, col: 0, size_x: 4 }
4203
+ // Or nested: { position: { row: 0, col: 0, sizeX: 4 } }
4204
+ let position = args.position || {};
4205
+
4206
+ // If args has direct position props, merge them
4207
+ if (args.row !== undefined) position.row = args.row;
4208
+ if (args.col !== undefined) position.col = args.col;
4209
+
4210
+ // Handle size_x vs sizeX and size_y vs sizeY
4211
+ if (args.size_x !== undefined) position.sizeX = args.size_x;
4212
+ if (args.size_y !== undefined) position.sizeY = args.size_y;
4213
+ if (args.sizeX !== undefined) position.sizeX = args.sizeX;
4214
+ if (args.sizeY !== undefined) position.sizeY = args.sizeY;
4215
+
4216
+ // Map back to format expected by client
4217
+ // The client expects: Options object with optional row, col, sizeX, sizeY
4218
+ // But we need to make sure we pass the right keys to client.addCardToDashboard
4219
+
4220
+ // Create a normalized options object for the client
4221
+ const options = {
4222
+ row: position.row,
4223
+ col: position.col,
4224
+ sizeX: position.sizeX || position.size_x,
4225
+ sizeY: position.sizeY || position.size_y,
4226
+ parameter_mappings: args.parameter_mappings || []
4227
+ };
4228
+
4201
4229
  const result = await this.metabaseClient.addCardToDashboard(
4202
4230
  args.dashboard_id,
4203
4231
  args.question_id,
4204
- args.position,
4205
- args.parameter_mappings
4232
+ options, // Pass normalized options instead of raw args
4233
+ args.parameter_mappings // Double pass, just in case (client signature check needed)
4206
4234
  );
4207
4235
 
4208
4236
  // VERIFICATION: Check if card was actually added
@@ -60,6 +60,39 @@ export class MetabaseClient {
60
60
  }
61
61
  }
62
62
 
63
+ /**
64
+ * Generic request wrapper for Metabase API
65
+ * Used by MCP handlers that need arbitrary API access
66
+ */
67
+ async request(method, endpoint, data = null, config = {}) {
68
+ await this.ensureAuthenticated();
69
+
70
+ // Normalize method
71
+ const methodUpper = method.toUpperCase();
72
+
73
+ const requestConfig = {
74
+ ...config,
75
+ method: methodUpper,
76
+ url: endpoint,
77
+ data: data
78
+ };
79
+
80
+ // For GET requests with data, move to params if not already set
81
+ if (methodUpper === 'GET' && data && !requestConfig.params) {
82
+ requestConfig.params = data;
83
+ delete requestConfig.data;
84
+ }
85
+
86
+ try {
87
+ const response = await this.client.request(requestConfig);
88
+ return response.data;
89
+ } catch (error) {
90
+ const errorMsg = error.response?.data?.message || error.message;
91
+ logger.error(`API Request Failed: ${methodUpper} ${endpoint}`, { error: errorMsg });
92
+ throw new Error(`Metabase API Error: ${errorMsg}`);
93
+ }
94
+ }
95
+
63
96
  // Database Operations
64
97
  async getDatabases() {
65
98
  await this.ensureAuthenticated();