@squadbase/vite-server 0.1.6 → 0.1.7-dev.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.
@@ -42,63 +42,11 @@ function createClient(_params, fetchFn = fetch) {
42
42
  }
43
43
  return await response.json();
44
44
  }
45
- async function updateValues(spreadsheetId, range, values) {
46
- const url = `${SHEETS_BASE_URL}/${spreadsheetId}/values/${encodeURIComponent(range)}?valueInputOption=USER_ENTERED`;
47
- const response = await fetchFn(url, {
48
- method: "PUT",
49
- headers: { "Content-Type": "application/json" },
50
- body: JSON.stringify({ range, majorDimension: "ROWS", values })
51
- });
52
- if (!response.ok) {
53
- const body = await response.text();
54
- throw new Error(
55
- `google-sheets: updateValues failed (${response.status}): ${body}`
56
- );
57
- }
58
- return await response.json();
59
- }
60
- async function appendValues(spreadsheetId, range, values) {
61
- const url = `${SHEETS_BASE_URL}/${spreadsheetId}/values/${encodeURIComponent(range)}:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS`;
62
- const response = await fetchFn(url, {
63
- method: "POST",
64
- headers: { "Content-Type": "application/json" },
65
- body: JSON.stringify({ range, majorDimension: "ROWS", values })
66
- });
67
- if (!response.ok) {
68
- const body = await response.text();
69
- throw new Error(
70
- `google-sheets: appendValues failed (${response.status}): ${body}`
71
- );
72
- }
73
- return await response.json();
74
- }
75
- async function createSpreadsheet(title, sheetTitles) {
76
- const sheets = sheetTitles && sheetTitles.length > 0 ? sheetTitles.map((t) => ({ properties: { title: t } })) : void 0;
77
- const url = `${SHEETS_BASE_URL}`;
78
- const response = await fetchFn(url, {
79
- method: "POST",
80
- headers: { "Content-Type": "application/json" },
81
- body: JSON.stringify({
82
- properties: { title },
83
- ...sheets ? { sheets } : {}
84
- })
85
- });
86
- if (!response.ok) {
87
- const body = await response.text();
88
- throw new Error(
89
- `google-sheets: createSpreadsheet failed (${response.status}): ${body}`
90
- );
91
- }
92
- return await response.json();
93
- }
94
45
  return {
95
46
  request,
96
47
  getSpreadsheet,
97
48
  getValues,
98
- batchGetValues,
99
- updateValues,
100
- appendValues,
101
- createSpreadsheet
49
+ batchGetValues
102
50
  };
103
51
  }
104
52
 
@@ -249,19 +197,6 @@ var AUTH_TYPES = {
249
197
  USER_PASSWORD: "user-password"
250
198
  };
251
199
 
252
- // ../connectors/src/connectors/google-sheets/setup.ts
253
- var googleSheetsOnboarding = new ConnectorOnboarding({
254
- dataOverviewInstructions: {
255
- en: `1. Create a new spreadsheet with google-sheets-oauth_request (POST with body { properties: { title: "..." } }) or use an existing spreadsheet ID.
256
- 2. Call google-sheets-oauth_request with GET /{spreadsheetId} to fetch spreadsheet metadata (sheet names and properties).`,
257
- ja: `1. google-sheets-oauth_request \u3092 POST\uFF08Body: { properties: { title: "..." } }\uFF09\u3067\u547C\u3073\u51FA\u3057\u3066\u65B0\u3057\u3044\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u3092\u4F5C\u6210\u3059\u308B\u304B\u3001\u65E2\u5B58\u306E\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID\u3092\u5229\u7528\u3057\u307E\u3059\u3002
258
- 2. google-sheets-oauth_request \u3067 GET /{spreadsheetId} \u3092\u547C\u3073\u51FA\u3057\u3001\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\uFF08\u30B7\u30FC\u30C8\u540D\u3068\u30D7\u30ED\u30D1\u30C6\u30A3\uFF09\u3092\u53D6\u5F97\u3057\u307E\u3059\u3002`
259
- }
260
- });
261
-
262
- // ../connectors/src/connectors/google-sheets/parameters.ts
263
- var parameters = {};
264
-
265
200
  // ../connectors/src/connectors/google-sheets/tools/request.ts
266
201
  import { z } from "zod";
267
202
  var SHEETS_BASE_URL2 = "https://sheets.googleapis.com/v4/spreadsheets";
@@ -302,11 +237,10 @@ var inputSchema = z.object({
302
237
  "Brief description of what you intend to accomplish with this tool call"
303
238
  ),
304
239
  connectionId: z.string().describe("ID of the Google Sheets connection to use"),
305
- method: z.enum(["GET", "POST", "PUT"]).describe("HTTP method"),
240
+ method: z.enum(["GET"]).describe("HTTP method. Only GET is supported (read-only analysis)."),
306
241
  path: z.string().describe(
307
- "API path appended to https://sheets.googleapis.com/v4/spreadsheets (e.g., '', '/{spreadsheetId}', '/{spreadsheetId}/values/Sheet1!A1:D10')."
242
+ "API path appended to https://sheets.googleapis.com/v4/spreadsheets (e.g., '/{spreadsheetId}', '/{spreadsheetId}/values/Sheet1!A1:D10'). The `{spreadsheetId}` placeholder is automatically replaced with the connection's configured spreadsheet ID."
308
243
  ),
309
- body: z.record(z.string(), z.unknown()).optional().describe("JSON request body for POST/PUT requests"),
310
244
  queryParams: z.record(z.string(), z.string()).optional().describe("Query parameters to append to the URL")
311
245
  });
312
246
  var outputSchema = z.discriminatedUnion("success", [
@@ -322,12 +256,12 @@ var outputSchema = z.discriminatedUnion("success", [
322
256
  ]);
323
257
  var requestTool = new ConnectorTool({
324
258
  name: "request",
325
- description: `Send authenticated requests to the Google Sheets API v4.
326
- Supports GET (read), POST (create/append), and PUT (update) methods.
259
+ description: `Send authenticated GET requests to the Google Sheets API v4 for read-only analysis.
260
+ The \`{spreadsheetId}\` placeholder in the path is automatically replaced with the connection's configured spreadsheet ID.
327
261
  Authentication is handled automatically via OAuth proxy.`,
328
262
  inputSchema,
329
263
  outputSchema,
330
- async execute({ connectionId, method, path: path2, body, queryParams }, connections, config) {
264
+ async execute({ connectionId, method, path: path2, queryParams }, connections, config) {
331
265
  const connection2 = connections.find((c) => c.id === connectionId);
332
266
  if (!connection2) {
333
267
  return {
@@ -357,8 +291,7 @@ Authentication is handled automatically via OAuth proxy.`,
357
291
  },
358
292
  body: JSON.stringify({
359
293
  url,
360
- method,
361
- ...body != null ? { body: JSON.stringify(body) } : {}
294
+ method
362
295
  }),
363
296
  signal: controller.signal
364
297
  });
@@ -378,13 +311,69 @@ Authentication is handled automatically via OAuth proxy.`,
378
311
  }
379
312
  });
380
313
 
314
+ // ../connectors/src/connectors/google-sheets/setup.ts
315
+ var requestToolName = `google-sheets-oauth_${requestTool.name}`;
316
+ var googleSheetsOnboarding = new ConnectorOnboarding({
317
+ connectionSetupInstructions: {
318
+ ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Sheets (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002\u5206\u6790\u5BFE\u8C61\u306E\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306F\u30E6\u30FC\u30B6\u30FC\u304B\u3089\u53D7\u3051\u53D6\u3063\u305FURL\u3067\u6307\u5B9A\u3057\u307E\u3059\uFF08\u65B0\u3057\u3044\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306F\u4F5C\u6210\u3057\u307E\u305B\u3093\uFF09\u3002
319
+
320
+ 1. \`askUserQuestion\` \u3067\u5206\u6790\u5BFE\u8C61\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306EURL\u3092\u30D2\u30A2\u30EA\u30F3\u30B0\u3059\u308B:
321
+ - \`type\`: \`"freeText"\`
322
+ - \`question\`: \u300C\u5206\u6790\u3057\u305F\u3044Google Sheets\u306EURL\u3092\u8CBC\u308A\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044\u300D
323
+ - \`placeholder\`: \`"https://docs.google.com/spreadsheets/d/.../edit"\`
324
+ 2. \u53D7\u3051\u53D6\u3063\u305FURL\u304B\u3089\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID\u3092\u62BD\u51FA\u3059\u308B\u3002URL\u306E \`/d/\` \u3068 \`/edit\`\uFF08\u307E\u305F\u306F\u672B\u5C3E\uFF09\u306E\u9593\u306B\u3042\u308B\u6587\u5B57\u5217\u304C\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID\u3067\u3059\uFF08\u4F8B: \`https://docs.google.com/spreadsheets/d/1AbCxyz.../edit\` \u2192 \`1AbCxyz...\`\uFF09\u3002URL\u3067\u306F\u306A\u304FID\u3060\u3051\u304C\u6E21\u3055\u308C\u305F\u5834\u5408\u306F\u305D\u306E\u307E\u307E\u5229\u7528\u3057\u307E\u3059\u3002
325
+ 3. \u62BD\u51FA\u3057\u305FID\u3092 \`updateConnectionParameters\` \u3067\u4FDD\u5B58\u3059\u308B:
326
+ - \`parameterSlug\`: \`"spreadsheet-id"\`
327
+ - \`options\`: \`[{ value: <\u62BD\u51FA\u3057\u305FID>, label: <\u540C\u3058\u5024> }]\`\uFF081\u4EF6\u306E\u307F\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u306F\u81EA\u52D5\u9078\u629E\u3055\u308C\u308B\uFF09
328
+ 4. \`${requestToolName}\` \u3067\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3059\u308B:
329
+ - \`method\`: \`"GET"\`
330
+ - \`path\`: \`"/{spreadsheetId}"\`
331
+ 5. \u30A8\u30E9\u30FC\u304C\u8FD4\u3055\u308C\u305F\u5834\u5408\u3001\u4EE5\u4E0B\u3092\u78BA\u8A8D\u3059\u308B\u3088\u3046\u30E6\u30FC\u30B6\u30FC\u306B\u4F1D\u3048\u308B:
332
+ - OAuth\u3067\u63A5\u7D9A\u3057\u305FGoogle\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u305D\u306E\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306B\u95B2\u89A7\u6A29\u9650\u3092\u6301\u3063\u3066\u3044\u308B\u304B
333
+ - \u5165\u529B\u3055\u308C\u305FURL\u304C\u6B63\u3057\u3044\u304B
334
+
335
+ #### \u5236\u7D04
336
+ - **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30BB\u30EB\u5024\u3092\u5927\u91CF\u306B\u53D6\u5F97\u3057\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u306E\u307F
337
+ - \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u3002\u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
338
+ en: `Follow these steps to set up the Google Sheets (OAuth) connection. The spreadsheet to analyze is specified by the URL provided by the user (no new spreadsheet is created).
339
+
340
+ 1. Call \`askUserQuestion\` to ask for the spreadsheet URL to analyze:
341
+ - \`type\`: \`"freeText"\`
342
+ - \`question\`: "Please paste the URL of the Google Sheet you want to analyze"
343
+ - \`placeholder\`: \`"https://docs.google.com/spreadsheets/d/.../edit"\`
344
+ 2. Extract the spreadsheet ID from the URL. It is the segment between \`/d/\` and \`/edit\` (or the end of the URL), e.g. \`https://docs.google.com/spreadsheets/d/1AbCxyz.../edit\` \u2192 \`1AbCxyz...\`. If the user pasted just the ID, use it as-is.
345
+ 3. Save the extracted ID via \`updateConnectionParameters\`:
346
+ - \`parameterSlug\`: \`"spreadsheet-id"\`
347
+ - \`options\`: \`[{ value: <extracted ID>, label: <same value> }]\` (a single option is auto-selected)
348
+ 4. Verify access by calling \`${requestToolName}\`:
349
+ - \`method\`: \`"GET"\`
350
+ - \`path\`: \`"/{spreadsheetId}"\`
351
+ 5. If an error is returned, ask the user to verify:
352
+ - The OAuthed Google account has read access to the spreadsheet
353
+ - The URL they entered is correct
354
+
355
+ #### Constraints
356
+ - **Do NOT read large amounts of cell data during setup**. Only the metadata request specified above is allowed
357
+ - Write only 1 sentence between tool calls, then immediately call the next tool. Skip unnecessary explanations and proceed efficiently`
358
+ },
359
+ dataOverviewInstructions: {
360
+ en: `1. Call ${requestToolName} with GET /{spreadsheetId} to fetch spreadsheet metadata (sheet names, grid properties)
361
+ 2. For each sheet of interest, call ${requestToolName} with GET /{spreadsheetId}/values/{SheetName}!A1:Z5 to sample the first rows and understand the column layout`,
362
+ ja: `1. ${requestToolName} \u3067 GET /{spreadsheetId} \u3092\u547C\u3073\u51FA\u3057\u3001\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\uFF08\u30B7\u30FC\u30C8\u540D\u3001\u30B0\u30EA\u30C3\u30C9\u30D7\u30ED\u30D1\u30C6\u30A3\uFF09\u3092\u53D6\u5F97
363
+ 2. \u4E3B\u8981\u306A\u30B7\u30FC\u30C8\u306B\u3064\u3044\u3066 ${requestToolName} \u3067 GET /{spreadsheetId}/values/{\u30B7\u30FC\u30C8\u540D}!A1:Z5 \u3092\u547C\u3073\u51FA\u3057\u3001\u5148\u982D\u6570\u884C\u3092\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u3057\u3066\u30AB\u30E9\u30E0\u69CB\u9020\u3092\u628A\u63E1`
364
+ }
365
+ });
366
+
367
+ // ../connectors/src/connectors/google-sheets/parameters.ts
368
+ var parameters = {};
369
+
381
370
  // ../connectors/src/connectors/google-sheets/index.ts
382
371
  var tools = { request: requestTool };
383
372
  var googleSheetsConnector = new ConnectorPlugin({
384
373
  slug: "google-sheets",
385
374
  authType: AUTH_TYPES.OAUTH,
386
375
  name: "Google Sheets",
387
- description: "Connect to Google Sheets for spreadsheet data access and creation using OAuth.",
376
+ description: "Connect to an existing Google Sheets spreadsheet for read-only data analysis using OAuth.",
388
377
  iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/1UPQuggyiZmbb26CuaSr2h/032770e8739b183fa00b7625f024e536/google-sheets.svg",
389
378
  parameters,
390
379
  releaseFlag: { dev1: true, dev2: false, prod: false },
@@ -393,30 +382,25 @@ var googleSheetsConnector = new ConnectorPlugin({
393
382
  allowlist: [
394
383
  {
395
384
  host: "sheets.googleapis.com",
396
- methods: ["GET", "POST", "PUT"]
385
+ methods: ["GET"]
397
386
  }
398
387
  ]
399
388
  },
400
389
  systemPrompt: {
401
390
  en: `### Tools (setup-time only)
402
391
 
403
- - \`google-sheets-oauth_request\`: Call the Google Sheets API during setup / data overview. Supports read and write operations. Use it to get/update spreadsheet metadata, cell values, create new spreadsheets, and more. Authentication is configured automatically via OAuth.
392
+ - \`google-sheets-oauth_request\`: Call the Google Sheets API during setup / data overview. Read-only (GET only). Use it to fetch spreadsheet metadata and sample cell values. The \`{spreadsheetId}\` placeholder in the path is automatically replaced with the spreadsheet configured at setup time. Authentication is configured automatically via OAuth.
404
393
 
405
394
  > **Important**: The \`google-sheets-oauth_request\` tool is only available at setup time. Inside server-logic handlers, use the SDK (\`connection(id).getValues\`, etc.) \u2014 the SDK's fetch is already wired through the OAuth proxy. **Do NOT** hand-roll HTTP calls to \`_sqcore/connections/*/request\` from a handler.
406
395
 
407
- ### Google Sheets API Reference
396
+ > **Connection scope**: Each connection is bound to a single spreadsheet selected at setup time via URL. This connector does NOT create new spreadsheets and does NOT write to spreadsheets. The OAuth scope is \`spreadsheets.readonly\`.
397
+
398
+ ### Google Sheets API Reference (read-only)
408
399
 
409
- #### Read Endpoints
410
400
  - GET \`/{spreadsheetId}\` \u2014 Get spreadsheet metadata (title, sheets, properties)
411
401
  - GET \`/{spreadsheetId}/values/{range}\` \u2014 Get cell values for a range
412
402
  - GET \`/{spreadsheetId}/values:batchGet?ranges={range1}&ranges={range2}\` \u2014 Get values for multiple ranges
413
403
 
414
- #### Write Endpoints
415
- - POST \`\` (empty path, with body) \u2014 Create a new spreadsheet. Body: \`{ "properties": { "title": "My Sheet" }, "sheets": [{ "properties": { "title": "Sheet1" } }] }\`
416
- - PUT \`/{spreadsheetId}/values/{range}?valueInputOption=USER_ENTERED\` \u2014 Update cell values for a range. Body: \`{ "range": "Sheet1!A1:B2", "majorDimension": "ROWS", "values": [["a","b"],["c","d"]] }\`
417
- - POST \`/{spreadsheetId}/values/{range}:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS\` \u2014 Append rows after the last row. Body: \`{ "range": "Sheet1!A1", "majorDimension": "ROWS", "values": [["a","b"]] }\`
418
- - POST \`/{spreadsheetId}:batchUpdate\` \u2014 Apply multiple updates (formatting, add sheets, merge cells, etc.). Body: \`{ "requests": [...] }\`
419
-
420
404
  ### Range Notation (A1 notation)
421
405
  - \`Sheet1!A1:D10\` \u2014 Specific range on Sheet1
422
406
  - \`Sheet1!A:A\` \u2014 Entire column A on Sheet1
@@ -429,21 +413,17 @@ var googleSheetsConnector = new ConnectorPlugin({
429
413
  - Use \`valueRenderOption=FORMATTED_VALUE\` query param to get display values
430
414
  - Use \`valueRenderOption=UNFORMATTED_VALUE\` for raw numeric values
431
415
  - Use \`majorDimension=COLUMNS\` to get data organized by columns instead of rows
432
- - For write operations, always use \`valueInputOption=USER_ENTERED\` so values are parsed as if typed by a user (dates, numbers, formulas are auto-detected)
433
- - Sharing (permissions) cannot be done via this connector; only the OAuth user has access to the created spreadsheets
434
416
 
435
417
  ### Business Logic
436
418
 
437
- The business logic type for this connector is "typescript". Write handler code using the connector SDK shown below. Do NOT access credentials directly from environment variables and do NOT read \`INTERNAL_SQUADBASE_*\` env vars \u2014 the SDK takes care of OAuth.
419
+ The business logic type for this connector is "typescript". Write handler code using the connector SDK shown below. Do NOT access credentials directly from environment variables and do NOT read \`INTERNAL_SQUADBASE_*\` env vars \u2014 the SDK takes care of OAuth. The spreadsheet ID is bound to the connection at setup time; do NOT ask handler callers to pass it.
438
420
 
439
421
  SDK surface (client created via \`connection(connectionId)\`):
440
- - \`client.request(path, init?)\` \u2014 low-level authenticated fetch (\`path\` is appended to \`https://sheets.googleapis.com/v4/spreadsheets\`).
441
- - \`client.getSpreadsheet(spreadsheetId)\` \u2014 fetch spreadsheet metadata.
442
- - \`client.getValues(spreadsheetId, range)\` \u2014 read a range (A1 notation).
443
- - \`client.batchGetValues(spreadsheetId, ranges)\` \u2014 read multiple ranges.
444
- - \`client.updateValues(spreadsheetId, range, values)\` \u2014 write values to a range.
445
- - \`client.appendValues(spreadsheetId, range, values)\` \u2014 append rows after the last row.
446
- - \`client.createSpreadsheet(title, sheetNames?)\` \u2014 create a new spreadsheet.
422
+ - \`client.spreadsheetId\` \u2014 the spreadsheet ID configured for this connection.
423
+ - \`client.request(path, init?)\` \u2014 low-level authenticated fetch (\`path\` is appended to \`https://sheets.googleapis.com/v4/spreadsheets\`; \`{spreadsheetId}\` is auto-replaced).
424
+ - \`client.getSpreadsheet()\` \u2014 fetch spreadsheet metadata for the configured spreadsheet.
425
+ - \`client.getValues(range)\` \u2014 read a range (A1 notation) from the configured spreadsheet.
426
+ - \`client.batchGetValues(ranges)\` \u2014 read multiple ranges from the configured spreadsheet.
447
427
 
448
428
  If a handler test fails with \`Connection proxy is not configured\`, retry \u2014 the sandbox is still initializing. Do NOT abandon the SDK and construct OAuth proxy URLs manually.
449
429
 
@@ -454,43 +434,31 @@ import { connection } from "@squadbase/vite-server/connectors/google-sheets";
454
434
 
455
435
  const sheets = connection("<connectionId>");
456
436
 
457
- // Create a new spreadsheet
458
- const newSheet = await sheets.createSpreadsheet("Sales Report", ["Q1", "Q2", "Q3", "Q4"]);
459
- const spreadsheetId = newSheet.spreadsheetId;
460
-
461
- // Get spreadsheet metadata
462
- const metadata = await sheets.getSpreadsheet(spreadsheetId);
437
+ // Get metadata for the configured spreadsheet
438
+ const metadata = await sheets.getSpreadsheet();
463
439
  console.log(metadata.properties.title, metadata.sheets.map(s => s.properties.title));
464
440
 
465
441
  // Get cell values
466
- const values = await sheets.getValues(spreadsheetId, "Sheet1!A1:D10");
442
+ const values = await sheets.getValues("Sheet1!A1:D10");
467
443
  console.log(values.values); // 2D array
468
444
 
469
- // Update cell values
470
- await sheets.updateValues(spreadsheetId, "Sheet1!A1:B2", [["Name", "Score"], ["Alice", "100"]]);
471
-
472
- // Append rows
473
- await sheets.appendValues(spreadsheetId, "Sheet1!A1", [["Bob", "95"], ["Charlie", "88"]]);
445
+ // Batch-get multiple ranges
446
+ const batch = await sheets.batchGetValues(["Sheet1!A:A", "Sheet2!B2:C100"]);
474
447
  \`\`\``,
475
448
  ja: `### \u30C4\u30FC\u30EB\uFF08\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306E\u307F\uFF09
476
449
 
477
- - \`google-sheets-oauth_request\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3084\u30C7\u30FC\u30BF\u6982\u8981\u628A\u63E1\u6642\u306B Google Sheets API \u3092\u547C\u3073\u51FA\u3059\u30C4\u30FC\u30EB\u3067\u3059\u3002\u8AAD\u307F\u53D6\u308A\u3068\u66F8\u304D\u8FBC\u307F\u306E\u4E21\u65B9\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u307E\u3059\u3002\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u30FB\u30BB\u30EB\u5024\u306E\u53D6\u5F97/\u66F4\u65B0\u3001\u65B0\u3057\u3044\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u4F5C\u6210\u306A\u3069\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002OAuth \u7D4C\u7531\u3067\u8A8D\u8A3C\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
450
+ - \`google-sheets-oauth_request\`: \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3084\u30C7\u30FC\u30BF\u6982\u8981\u628A\u63E1\u6642\u306B Google Sheets API \u3092\u547C\u3073\u51FA\u3059\u30C4\u30FC\u30EB\u3067\u3059\u3002\u8AAD\u307F\u53D6\u308A\u5C02\u7528\uFF08GET \u306E\u307F\uFF09\u3067\u3059\u3002\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u3084\u30BB\u30EB\u5024\u306E\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u30D1\u30B9\u5185\u306E \`{spreadsheetId}\` \u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u306F\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306B\u6307\u5B9A\u3055\u308C\u305F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID\u306B\u81EA\u52D5\u7684\u306B\u7F6E\u63DB\u3055\u308C\u307E\u3059\u3002OAuth \u7D4C\u7531\u3067\u8A8D\u8A3C\u306F\u81EA\u52D5\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002
478
451
 
479
452
  > **\u91CD\u8981**: \`google-sheets-oauth_request\` \u306F\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306E\u307F\u5229\u7528\u53EF\u80FD\u3067\u3059\u3002\u30B5\u30FC\u30D0\u30FC\u30ED\u30B8\u30C3\u30AF\u306E\u30CF\u30F3\u30C9\u30E9\u5185\u3067\u306F\u5FC5\u305A SDK\uFF08\`connection(id).getValues\` \u306A\u3069\uFF09\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002SDK \u306E fetch \u306F OAuth \u30D7\u30ED\u30AD\u30B7\u7D4C\u7531\u3067\u65E2\u306B\u914D\u7DDA\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u30CF\u30F3\u30C9\u30E9\u304B\u3089 \`_sqcore/connections/*/request\` \u3092\u624B\u66F8\u304D\u3067\u547C\u3073\u51FA\u3055\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
480
453
 
481
- ### Google Sheets API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9
454
+ > **\u63A5\u7D9A\u30B9\u30B3\u30FC\u30D7**: \u5404\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306F\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306BURL\u3067\u6307\u5B9A\u3055\u308C\u305F1\u3064\u306E\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306B\u7D10\u3065\u304D\u307E\u3059\u3002\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u4F5C\u6210\u3084\u66F8\u304D\u8FBC\u307F\u306F\u884C\u3044\u307E\u305B\u3093\u3002OAuth \u30B9\u30B3\u30FC\u30D7\u306F \`spreadsheets.readonly\` \u3067\u3059\u3002
455
+
456
+ ### Google Sheets API \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\uFF08\u8AAD\u307F\u53D6\u308A\u5C02\u7528\uFF09
482
457
 
483
- #### \u8AAD\u307F\u53D6\u308A\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
484
458
  - GET \`/{spreadsheetId}\` \u2014 \u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\uFF08\u30BF\u30A4\u30C8\u30EB\u3001\u30B7\u30FC\u30C8\u3001\u30D7\u30ED\u30D1\u30C6\u30A3\uFF09
485
459
  - GET \`/{spreadsheetId}/values/{range}\` \u2014 \u7BC4\u56F2\u306E\u30BB\u30EB\u5024\u3092\u53D6\u5F97
486
460
  - GET \`/{spreadsheetId}/values:batchGet?ranges={range1}&ranges={range2}\` \u2014 \u8907\u6570\u7BC4\u56F2\u306E\u5024\u3092\u53D6\u5F97
487
461
 
488
- #### \u66F8\u304D\u8FBC\u307F\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8
489
- - POST \`\`\uFF08\u7A7A\u30D1\u30B9\u3001\u30DC\u30C7\u30A3\u4ED8\u304D\uFF09\u2014 \u65B0\u3057\u3044\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u3092\u4F5C\u6210\u3002Body: \`{ "properties": { "title": "\u30DE\u30A4\u30B7\u30FC\u30C8" }, "sheets": [{ "properties": { "title": "Sheet1" } }] }\`
490
- - PUT \`/{spreadsheetId}/values/{range}?valueInputOption=USER_ENTERED\` \u2014 \u7BC4\u56F2\u306E\u30BB\u30EB\u5024\u3092\u66F4\u65B0\u3002Body: \`{ "range": "Sheet1!A1:B2", "majorDimension": "ROWS", "values": [["a","b"],["c","d"]] }\`
491
- - POST \`/{spreadsheetId}/values/{range}:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS\` \u2014 \u6700\u7D42\u884C\u306E\u5F8C\u306B\u884C\u3092\u8FFD\u52A0\u3002Body: \`{ "range": "Sheet1!A1", "majorDimension": "ROWS", "values": [["a","b"]] }\`
492
- - POST \`/{spreadsheetId}:batchUpdate\` \u2014 \u8907\u6570\u306E\u66F4\u65B0\u3092\u9069\u7528\uFF08\u66F8\u5F0F\u8A2D\u5B9A\u3001\u30B7\u30FC\u30C8\u8FFD\u52A0\u3001\u30BB\u30EB\u7D50\u5408\u306A\u3069\uFF09\u3002Body: \`{ "requests": [...] }\`
493
-
494
462
  ### \u7BC4\u56F2\u306E\u8868\u8A18\u6CD5\uFF08A1\u8868\u8A18\u6CD5\uFF09
495
463
  - \`Sheet1!A1:D10\` \u2014 Sheet1\u4E0A\u306E\u7279\u5B9A\u7BC4\u56F2
496
464
  - \`Sheet1!A:A\` \u2014 Sheet1\u306EA\u5217\u5168\u4F53
@@ -503,21 +471,17 @@ await sheets.appendValues(spreadsheetId, "Sheet1!A1", [["Bob", "95"], ["Charlie"
503
471
  - \u8868\u793A\u5024\u3092\u53D6\u5F97\u3059\u308B\u306B\u306F \`valueRenderOption=FORMATTED_VALUE\` \u30AF\u30A8\u30EA\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u4F7F\u7528\u3057\u307E\u3059
504
472
  - \u751F\u306E\u6570\u5024\u3092\u53D6\u5F97\u3059\u308B\u306B\u306F \`valueRenderOption=UNFORMATTED_VALUE\` \u3092\u4F7F\u7528\u3057\u307E\u3059
505
473
  - \u5217\u3054\u3068\u306B\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3059\u308B\u306B\u306F \`majorDimension=COLUMNS\` \u3092\u4F7F\u7528\u3057\u307E\u3059
506
- - \u66F8\u304D\u8FBC\u307F\u64CD\u4F5C\u3067\u306F\u5E38\u306B \`valueInputOption=USER_ENTERED\` \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u5024\u304C\u30E6\u30FC\u30B6\u30FC\u5165\u529B\u306E\u3088\u3046\u306B\u89E3\u6790\u3055\u308C\u307E\u3059\uFF08\u65E5\u4ED8\u3001\u6570\u5024\u3001\u6570\u5F0F\u304C\u81EA\u52D5\u691C\u51FA\uFF09
507
- - \u5171\u6709\uFF08permissions\uFF09\u306F\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u7D4C\u7531\u3067\u306F\u884C\u3048\u307E\u305B\u3093\u3002\u4F5C\u6210\u3057\u305F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u3078\u306F\u63A5\u7D9A\u3057\u305FOAuth\u30E6\u30FC\u30B6\u30FC\u306E\u307F\u304C\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u3067\u3059
508
474
 
509
475
  ### Business Logic
510
476
 
511
- \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u4EE5\u4E0B\u306B\u793A\u3059\u30B3\u30CD\u30AF\u30BF SDK \u3092\u4F7F\u7528\u3057\u3066\u30CF\u30F3\u30C9\u30E9\u30B3\u30FC\u30C9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u76F4\u63A5\u8A8D\u8A3C\u60C5\u5831\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\`INTERNAL_SQUADBASE_*\` \u306E\u74B0\u5883\u5909\u6570\u3092\u4F7F\u3063\u3066\u624B\u52D5\u3067 OAuth \u30D7\u30ED\u30AD\u30B7\u3092\u53E9\u304F\u3053\u3068\u3082\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044 \u2014 SDK \u304C OAuth \u3092\u51E6\u7406\u3057\u307E\u3059\u3002
477
+ \u3053\u306E\u30B3\u30CD\u30AF\u30BF\u306E\u30D3\u30B8\u30CD\u30B9\u30ED\u30B8\u30C3\u30AF\u30BF\u30A4\u30D7\u306F "typescript" \u3067\u3059\u3002\u4EE5\u4E0B\u306B\u793A\u3059\u30B3\u30CD\u30AF\u30BF SDK \u3092\u4F7F\u7528\u3057\u3066\u30CF\u30F3\u30C9\u30E9\u30B3\u30FC\u30C9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u74B0\u5883\u5909\u6570\u304B\u3089\u76F4\u63A5\u8A8D\u8A3C\u60C5\u5831\u306B\u30A2\u30AF\u30BB\u30B9\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\`INTERNAL_SQUADBASE_*\` \u306E\u74B0\u5883\u5909\u6570\u3092\u4F7F\u3063\u3066\u624B\u52D5\u3067 OAuth \u30D7\u30ED\u30AD\u30B7\u3092\u53E9\u304F\u3053\u3068\u3082\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044 \u2014 SDK \u304C OAuth \u3092\u51E6\u7406\u3057\u307E\u3059\u3002\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID\u306F\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u6642\u306B\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306B\u7D10\u3065\u3051\u3089\u308C\u3066\u3044\u308B\u306E\u3067\u3001\u30CF\u30F3\u30C9\u30E9\u306E\u547C\u3073\u51FA\u3057\u5143\u306B\u6E21\u3055\u305B\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
512
478
 
513
479
  SDK\uFF08\`connection(connectionId)\` \u3067\u4F5C\u6210\u3057\u305F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\uFF09:
514
- - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304D fetch\uFF08\`path\` \u306F \`https://sheets.googleapis.com/v4/spreadsheets\` \u306B\u8FFD\u52A0\u3055\u308C\u307E\u3059\uFF09\u3002
515
- - \`client.getSpreadsheet(spreadsheetId)\` \u2014 \u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3002
516
- - \`client.getValues(spreadsheetId, range)\` \u2014 \u7BC4\u56F2\u306E\u5024\u3092\u53D6\u5F97\uFF08A1 \u8868\u8A18\uFF09\u3002
517
- - \`client.batchGetValues(spreadsheetId, ranges)\` \u2014 \u8907\u6570\u7BC4\u56F2\u306E\u5024\u3092\u53D6\u5F97\u3002
518
- - \`client.updateValues(spreadsheetId, range, values)\` \u2014 \u7BC4\u56F2\u306B\u5024\u3092\u66F8\u304D\u8FBC\u307F\u3002
519
- - \`client.appendValues(spreadsheetId, range, values)\` \u2014 \u6700\u7D42\u884C\u306E\u5F8C\u306B\u884C\u3092\u8FFD\u52A0\u3002
520
- - \`client.createSpreadsheet(title, sheetNames?)\` \u2014 \u65B0\u3057\u3044\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u3092\u4F5C\u6210\u3002
480
+ - \`client.spreadsheetId\` \u2014 \u3053\u306E\u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u308B\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8ID\u3002
481
+ - \`client.request(path, init?)\` \u2014 \u4F4E\u30EC\u30D9\u30EB\u306E\u8A8D\u8A3C\u4ED8\u304D fetch\uFF08\`path\` \u306F \`https://sheets.googleapis.com/v4/spreadsheets\` \u306B\u8FFD\u52A0\u3055\u308C\u3001\`{spreadsheetId}\` \u306F\u81EA\u52D5\u7F6E\u63DB\u3055\u308C\u307E\u3059\uFF09\u3002
482
+ - \`client.getSpreadsheet()\` \u2014 \u8A2D\u5B9A\u6E08\u307F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3002
483
+ - \`client.getValues(range)\` \u2014 \u8A2D\u5B9A\u6E08\u307F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u7BC4\u56F2\u306E\u5024\u3092\u53D6\u5F97\uFF08A1 \u8868\u8A18\uFF09\u3002
484
+ - \`client.batchGetValues(ranges)\` \u2014 \u8A2D\u5B9A\u6E08\u307F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u8907\u6570\u7BC4\u56F2\u306E\u5024\u3092\u53D6\u5F97\u3002
521
485
 
522
486
  \u30CF\u30F3\u30C9\u30E9\u306E\u30C6\u30B9\u30C8\u304C \`Connection proxy is not configured\` \u3067\u5931\u6557\u3059\u308B\u5834\u5408\u306F\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u901A\u5E38\u306F\u30B5\u30F3\u30C9\u30DC\u30C3\u30AF\u30B9\u306E\u521D\u671F\u5316\u4E2D\u306B\u8D77\u304D\u307E\u3059\u3002SDK \u3092\u8AE6\u3081\u3066 OAuth \u30D7\u30ED\u30AD\u30B7\u306E URL \u3092\u81EA\u5206\u3067\u7D44\u307F\u7ACB\u3066\u308B\u3053\u3068\u306F **\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044**\u3002
523
487
 
@@ -528,23 +492,16 @@ import { connection } from "@squadbase/vite-server/connectors/google-sheets";
528
492
 
529
493
  const sheets = connection("<connectionId>");
530
494
 
531
- // \u65B0\u3057\u3044\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u3092\u4F5C\u6210
532
- const newSheet = await sheets.createSpreadsheet("\u58F2\u4E0A\u30EC\u30DD\u30FC\u30C8", ["Q1", "Q2", "Q3", "Q4"]);
533
- const spreadsheetId = newSheet.spreadsheetId;
534
-
535
- // \u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
536
- const metadata = await sheets.getSpreadsheet(spreadsheetId);
495
+ // \u8A2D\u5B9A\u6E08\u307F\u30B9\u30D7\u30EC\u30C3\u30C9\u30B7\u30FC\u30C8\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u53D6\u5F97
496
+ const metadata = await sheets.getSpreadsheet();
537
497
  console.log(metadata.properties.title, metadata.sheets.map(s => s.properties.title));
538
498
 
539
499
  // \u30BB\u30EB\u5024\u3092\u53D6\u5F97
540
- const values = await sheets.getValues(spreadsheetId, "Sheet1!A1:D10");
500
+ const values = await sheets.getValues("Sheet1!A1:D10");
541
501
  console.log(values.values); // 2D array
542
502
 
543
- // \u30BB\u30EB\u5024\u3092\u66F4\u65B0
544
- await sheets.updateValues(spreadsheetId, "Sheet1!A1:B2", [["\u540D\u524D", "\u30B9\u30B3\u30A2"], ["Alice", "100"]]);
545
-
546
- // \u884C\u3092\u8FFD\u52A0
547
- await sheets.appendValues(spreadsheetId, "Sheet1!A1", [["Bob", "95"], ["Charlie", "88"]]);
503
+ // \u8907\u6570\u7BC4\u56F2\u3092\u30D0\u30C3\u30C1\u53D6\u5F97
504
+ const batch = await sheets.batchGetValues(["Sheet1!A:A", "Sheet2!B2:C100"]);
548
505
  \`\`\``
549
506
  },
550
507
  tools
@@ -47,7 +47,7 @@ var parameters = {
47
47
  url: new ParameterDefinition({
48
48
  slug: "url",
49
49
  name: "InfluxDB URL",
50
- description: "The base URL of your InfluxDB instance (e.g., 'https://us-east-1-1.aws.cloud2.influxdata.com' for InfluxDB Cloud). Do not include a trailing slash.",
50
+ description: "The base URL of your InfluxDB instance. Do not include a trailing slash. Works with both InfluxDB 2 (e.g., InfluxDB Cloud on '*.cloud2.influxdata.com', OSS 2.x) and InfluxDB 3 (Cloud Serverless, Enterprise, OSS 3.x).",
51
51
  envVarBaseKey: "INFLUXDB_URL",
52
52
  type: "text",
53
53
  secret: false,
@@ -74,7 +74,7 @@ var parameters = {
74
74
  org: new ParameterDefinition({
75
75
  slug: "org",
76
76
  name: "Organization",
77
- description: "The InfluxDB organization name. Required for InfluxDB 2.x Flux queries and writes; optional for InfluxDB 3.",
77
+ description: "The InfluxDB organization name. Required for InfluxDB 2 Flux queries and writes (including InfluxDB Cloud on '*.cloud2.influxdata.com'). Optional for InfluxDB 3 when only using SQL.",
78
78
  envVarBaseKey: "INFLUXDB_ORG",
79
79
  type: "text",
80
80
  secret: false,
@@ -110,12 +110,21 @@ function createClient(params) {
110
110
  return headers;
111
111
  }
112
112
  async function assertOk(res, label) {
113
- if (!res.ok) {
114
- const body = await res.text().catch(() => "(unreadable body)");
115
- throw new Error(
116
- `influxdb ${label}: ${res.status} ${res.statusText} \u2014 ${body}`
117
- );
118
- }
113
+ if (res.ok) return;
114
+ const body = await res.text().catch(() => "(unreadable body)");
115
+ const contentType = res.headers.get("content-type") ?? "";
116
+ const isHtml = contentType.includes("text/html") || body.trimStart().startsWith("<");
117
+ const v3Endpoints = /* @__PURE__ */ new Set([
118
+ "querySql",
119
+ "queryInfluxql",
120
+ "writeLineProtocol"
121
+ ]);
122
+ const looksLikeMissingV3 = (res.status === 405 || res.status === 404 || isHtml) && v3Endpoints.has(label);
123
+ const hint = looksLikeMissingV3 ? " \u2014 This InfluxDB instance does not support the v3 API at this path. It is likely InfluxDB 2 (e.g. InfluxDB Cloud on '*.cloud2.influxdata.com'). Use queryFlux() via '/api/v2/query?org={org}' instead." : isHtml ? " \u2014 Received HTML instead of a JSON/CSV API response; verify the URL points to the InfluxDB API host." : "";
124
+ const snippet = isHtml ? body.replace(/\s+/g, " ").slice(0, 200) : body;
125
+ throw new Error(
126
+ `influxdb ${label}: ${res.status} ${res.statusText}${hint} \u2014 ${snippet}`
127
+ );
119
128
  }
120
129
  return {
121
130
  database,
@@ -345,11 +354,11 @@ var influxdbOnboarding = new ConnectorOnboarding({
345
354
 
346
355
  #### Confirm the Database (or Bucket) Name
347
356
  - InfluxDB 3: use the database name
348
- - InfluxDB 2: use the bucket name (buckets act as databases in the v1/v3 compatibility endpoints)
357
+ - InfluxDB 2 (includes InfluxDB Cloud on \`*.cloud2.influxdata.com\`): use the bucket name (buckets act as databases in the v1/v3 compatibility endpoints)
349
358
 
350
- #### Organization (InfluxDB 2 only)
351
- - If you are on InfluxDB 2, set the Organization parameter to the org that owns the bucket
352
- - For InfluxDB 3 Cloud you can leave Organization blank`,
359
+ #### Organization
360
+ - InfluxDB 2: set the Organization parameter to the org that owns the bucket (required for Flux queries and writes)
361
+ - InfluxDB 3 Cloud Serverless: you can leave Organization blank when only using SQL`,
353
362
  ja: `#### API \u30C8\u30FC\u30AF\u30F3\u306E\u767A\u884C
354
363
  1. InfluxDB Cloud / OSS \u306B\u30B5\u30A4\u30F3\u30A4\u30F3
355
364
  2. Load Data \u2192 API Tokens \u2192 Generate API Token
@@ -358,19 +367,37 @@ var influxdbOnboarding = new ConnectorOnboarding({
358
367
 
359
368
  #### Database (\u307E\u305F\u306F Bucket) \u540D\u306E\u78BA\u8A8D
360
369
  - InfluxDB 3: database \u540D\u3092\u4F7F\u7528
361
- - InfluxDB 2: bucket \u540D\u3092\u4F7F\u7528\uFF08v1/v3 \u4E92\u63DB\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3067\u306F bucket \u304C database \u3068\u3057\u3066\u6271\u308F\u308C\u308B\uFF09
370
+ - InfluxDB 2\uFF08\`*.cloud2.influxdata.com\` \u306E InfluxDB Cloud \u3092\u542B\u3080\uFF09: bucket \u540D\u3092\u4F7F\u7528\uFF08v1/v3 \u4E92\u63DB\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3067\u306F bucket \u304C database \u3068\u3057\u3066\u6271\u308F\u308C\u308B\uFF09
362
371
 
363
- #### Organization\uFF08InfluxDB 2 \u306E\u307F\uFF09
364
- - InfluxDB 2 \u306E\u5834\u5408\u3001bucket \u3092\u4FDD\u6709\u3059\u308B\u7D44\u7E54\u540D\u3092 Organization \u30D1\u30E9\u30E1\u30FC\u30BF\u306B\u8A2D\u5B9A
365
- - InfluxDB 3 Cloud \u306E\u5834\u5408\u306F\u7A7A\u306E\u307E\u307E\u3067\u554F\u984C\u306A\u3044`
372
+ #### Organization
373
+ - InfluxDB 2: bucket \u3092\u4FDD\u6709\u3059\u308B\u7D44\u7E54\u540D\u3092 Organization \u30D1\u30E9\u30E1\u30FC\u30BF\u306B\u8A2D\u5B9A\uFF08Flux \u30AF\u30A8\u30EA\u3068\u66F8\u304D\u8FBC\u307F\u306B\u306F\u5FC5\u9808\uFF09
374
+ - InfluxDB 3 Cloud Serverless: SQL \u306E\u307F\u3092\u4F7F\u3046\u5834\u5408\u306F\u7A7A\u306E\u307E\u307E\u3067\u554F\u984C\u306A\u3044`
366
375
  },
367
376
  dataOverviewInstructions: {
368
- en: `1. For InfluxDB 3: call influxdb_request with POST /api/v3/query_sql, body { "db": "<database>", "q": "SHOW TABLES" } to list measurements
369
- 2. For InfluxDB 3: inspect a sample measurement with POST /api/v3/query_sql, body { "db": "<database>", "q": "SELECT * FROM <measurement> ORDER BY time DESC LIMIT 5" }
370
- 3. For InfluxDB 2: call influxdb_request with POST /api/v2/query?org=<org>, contentType 'application/vnd.flux', body 'buckets()' to list buckets, then 'from(bucket:"<bucket>") |> range(start: -1h) |> limit(n:5)' to inspect data`,
371
- ja: `1. InfluxDB 3 \u306E\u5834\u5408: influxdb_request \u3067 POST /api/v3/query_sql\u3001body { "db": "<database>", "q": "SHOW TABLES" } \u3092\u5B9F\u884C\u3057 measurement \u4E00\u89A7\u3092\u53D6\u5F97
372
- 2. InfluxDB 3 \u306E\u5834\u5408: POST /api/v3/query_sql\u3001body { "db": "<database>", "q": "SELECT * FROM <measurement> ORDER BY time DESC LIMIT 5" } \u3067\u4EE3\u8868\u7684\u306A measurement \u306E\u69CB\u9020\u3092\u78BA\u8A8D
373
- 3. InfluxDB 2 \u306E\u5834\u5408: POST /api/v2/query?org=<org>\u3001contentType 'application/vnd.flux'\u3001body 'buckets()' \u3067 bucket \u4E00\u89A7\u3092\u53D6\u5F97\u3057\u3001\u7D9A\u3044\u3066 'from(bucket:"<bucket>") |> range(start: -1h) |> limit(n:5)' \u3067\u30C7\u30FC\u30BF\u3092\u78BA\u8A8D`
377
+ en: `The instance may be either InfluxDB 3 (supports SQL) or InfluxDB 2 (Flux only; includes InfluxDB Cloud on '*.cloud2.influxdata.com'). Detect the variant first, then pick the matching endpoints.
378
+
379
+ 1. Probe for InfluxDB 3: call influxdb_request with POST /api/v3/query_sql, body { "db": "<database>", "q": "SELECT 1" }
380
+ - 200 with JSON rows \u2192 InfluxDB 3. Continue with SQL.
381
+ - 405 Method Not Allowed, or an HTML body like "<html>...405 Not Allowed..." \u2192 InfluxDB 2. Fall back to Flux (step 3).
382
+ 2. InfluxDB 3 data overview:
383
+ a. POST /api/v3/query_sql, body { "db": "<database>", "q": "SHOW TABLES" } to list measurements
384
+ b. POST /api/v3/query_sql, body { "db": "<database>", "q": "SELECT * FROM <measurement> ORDER BY time DESC LIMIT 5" } to inspect a sample
385
+ 3. InfluxDB 2 data overview (Organization parameter required):
386
+ a. POST /api/v2/query?org=<org>, contentType 'application/vnd.flux', body 'buckets()' to list buckets
387
+ b. POST /api/v2/query?org=<org>, contentType 'application/vnd.flux', body 'from(bucket:"<bucket>") |> range(start: -1h) |> limit(n:5)' to inspect data
388
+ - If the Organization parameter was not provided but v3 probing failed, ask the user to set it using updateConnectionParameters before continuing.`,
389
+ ja: `\u63A5\u7D9A\u5148\u306F InfluxDB 3\uFF08SQL \u5BFE\u5FDC\uFF09\u3068 InfluxDB 2\uFF08Flux \u306E\u307F\u3002\`*.cloud2.influxdata.com\` \u306E InfluxDB Cloud \u3092\u542B\u3080\uFF09\u306E\u3069\u3061\u3089\u306E\u53EF\u80FD\u6027\u3082\u3042\u308A\u307E\u3059\u3002\u307E\u305A\u30D0\u30EA\u30A2\u30F3\u30C8\u3092\u5224\u5225\u3057\u3001\u305D\u308C\u306B\u5408\u3046\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002
390
+
391
+ 1. InfluxDB 3 \u306E\u5224\u5225: influxdb_request \u3067 POST /api/v3/query_sql\u3001body { "db": "<database>", "q": "SELECT 1" } \u3092\u5B9F\u884C
392
+ - 200 + JSON \u884C\u304C\u8FD4\u308B \u2192 InfluxDB 3\u3002\u305D\u306E\u307E\u307E SQL \u3067\u7D9A\u884C\u3002
393
+ - 405 Method Not Allowed\u3001\u307E\u305F\u306F "<html>...405 Not Allowed..." \u306E\u3088\u3046\u306A HTML \u672C\u6587\u304C\u8FD4\u308B \u2192 InfluxDB 2\u3002Flux \u306B\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\uFF08\u624B\u9806 3\uFF09\u3002
394
+ 2. InfluxDB 3 \u306E\u5834\u5408\u306E\u30C7\u30FC\u30BF\u6982\u8981:
395
+ a. POST /api/v3/query_sql\u3001body { "db": "<database>", "q": "SHOW TABLES" } \u3067 measurement \u4E00\u89A7\u3092\u53D6\u5F97
396
+ b. POST /api/v3/query_sql\u3001body { "db": "<database>", "q": "SELECT * FROM <measurement> ORDER BY time DESC LIMIT 5" } \u3067\u4EE3\u8868\u7684\u306A measurement \u3092\u78BA\u8A8D
397
+ 3. InfluxDB 2 \u306E\u5834\u5408\u306E\u30C7\u30FC\u30BF\u6982\u8981\uFF08Organization \u30D1\u30E9\u30E1\u30FC\u30BF\u5FC5\u9808\uFF09:
398
+ a. POST /api/v2/query?org=<org>\u3001contentType 'application/vnd.flux'\u3001body 'buckets()' \u3067 bucket \u4E00\u89A7\u3092\u53D6\u5F97
399
+ b. POST /api/v2/query?org=<org>\u3001contentType 'application/vnd.flux'\u3001body 'from(bucket:"<bucket>") |> range(start: -1h) |> limit(n:5)' \u3067\u30C7\u30FC\u30BF\u3092\u78BA\u8A8D
400
+ - Organization \u30D1\u30E9\u30E1\u30FC\u30BF\u304C\u672A\u8A2D\u5B9A\u3067 v3 \u5224\u5225\u3082\u5931\u6557\u3057\u305F\u5834\u5408\u306F\u3001updateConnectionParameters \u3067\u30E6\u30FC\u30B6\u30FC\u306B\u8A2D\u5B9A\u3092\u4FC3\u3057\u3066\u304B\u3089\u7D9A\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
374
401
  }
375
402
  });
376
403
 
@@ -458,10 +485,17 @@ For read-only data exploration prefer SQL (InfluxDB 3) or InfluxQL queries \u201
458
485
  }
459
486
  if (!response.ok) {
460
487
  let errorMessage = `HTTP ${response.status} ${response.statusText}`;
488
+ const bodyText = typeof data === "string" ? data : void 0;
489
+ const isHtml = resContentType.includes("text/html") || bodyText !== void 0 && bodyText.trimStart().startsWith("<");
461
490
  if (data && typeof data === "object" && !Array.isArray(data) && typeof data.message === "string") {
462
491
  errorMessage = data.message;
463
- } else if (typeof data === "string" && data) {
464
- errorMessage = data;
492
+ } else if (bodyText) {
493
+ errorMessage = isHtml ? bodyText.replace(/\s+/g, " ").slice(0, 200) : bodyText;
494
+ }
495
+ const hitsV3Path = path2.includes("/api/v3/");
496
+ const looksLikeMissingV3 = hitsV3Path && (response.status === 405 || response.status === 404 || isHtml);
497
+ if (looksLikeMissingV3) {
498
+ errorMessage += " \u2014 This InfluxDB instance does not support the v3 API at this path. It is likely InfluxDB 2 (e.g. InfluxDB Cloud on '*.cloud2.influxdata.com'). Retry via Flux on '/api/v2/query?org={org}' with contentType 'application/vnd.flux'.";
465
499
  }
466
500
  return { success: false, error: errorMessage };
467
501
  }
@@ -483,14 +517,22 @@ var influxdbConnector = new ConnectorPlugin({
483
517
  authType: AUTH_TYPES.API_KEY,
484
518
  name: "InfluxDB",
485
519
  description: "Connect to InfluxDB (Cloud or OSS) to query time-series data with SQL, InfluxQL, or Flux and to write line protocol.",
486
- iconUrl: "https://upload.wikimedia.org/wikipedia/commons/b/b2/Influxdb_logo.svg",
520
+ iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/J1JauVRNmahSVTVrpPfQK/18350d8d3f2dc3be25e8e36ee52914a0/influxdb.png",
487
521
  parameters,
488
522
  releaseFlag: { dev1: true, dev2: false, prod: false },
489
523
  onboarding: influxdbOnboarding,
490
524
  systemPrompt: {
491
- en: `### Tools
525
+ en: `### Variant Detection
492
526
 
493
- - \`influxdb_request\`: The only way to call the InfluxDB HTTP API. Use it to run SQL / InfluxQL / Flux queries, write line protocol, and inspect buckets / databases. Authentication (\`Authorization: Token {token}\`) and the instance URL are configured automatically. For InfluxDB 3 prefer SQL (\`POST /api/v3/query_sql\`) \u2014 it returns JSON rows that are directly usable. For InfluxDB 2 use Flux (\`POST /api/v2/query?org={org}\`) \u2014 the response is annotated CSV. Writes use \`POST /api/v3/write_lp?db={db}\` (v3) or \`POST /api/v2/write?org={org}&bucket={bucket}\` (v2) with a line-protocol body and \`contentType\` set to \`text/plain; charset=utf-8\`.
527
+ The configured instance may be **InfluxDB 3** (supports SQL via \`/api/v3/query_sql\`) or **InfluxDB 2** (Flux only; includes InfluxDB Cloud on \`*.cloud2.influxdata.com\`). Always probe first, then use the matching endpoints:
528
+
529
+ 1. Call \`influxdb_request\` POST \`/api/v3/query_sql\` with body \`{ "db": "<database>", "q": "SELECT 1" }\`
530
+ 2. 200 + JSON rows \u2192 InfluxDB 3. Use SQL endpoints.
531
+ 3. 405 Method Not Allowed or HTML body (e.g. \`<html>...405 Not Allowed...</html>\`) \u2192 InfluxDB 2. Use Flux via \`/api/v2/query?org={org}\`. Require the \`org\` parameter; if missing, ask the user via \`updateConnectionParameters\`.
532
+
533
+ ### Tools
534
+
535
+ - \`influxdb_request\`: The only way to call the InfluxDB HTTP API. Use it to run SQL / InfluxQL / Flux queries, write line protocol, and inspect buckets / databases. Authentication (\`Authorization: Token {token}\`) and the instance URL are configured automatically. On InfluxDB 3 prefer SQL (\`POST /api/v3/query_sql\`) \u2014 it returns JSON rows that are directly usable. On InfluxDB 2 use Flux (\`POST /api/v2/query?org={org}\`) \u2014 the response is annotated CSV. Writes use \`POST /api/v3/write_lp?db={db}\` (v3) or \`POST /api/v2/write?org={org}&bucket={bucket}\` (v2) with a line-protocol body and \`contentType\` set to \`text/plain; charset=utf-8\`.
494
536
 
495
537
  ### Business Logic
496
538
 
@@ -551,7 +593,15 @@ export default async function handler(c: Context) {
551
593
  - Time filtering uses standard SQL \`time\` column comparisons (\`time >= now() - INTERVAL '...' \`)
552
594
  - Aggregates: \`AVG\`, \`SUM\`, \`MIN\`, \`MAX\`, \`COUNT\`; bucket time with \`date_bin('5 minutes', time)\`
553
595
  - List measurements: \`SHOW TABLES\`; list columns: \`SHOW COLUMNS FROM <measurement>\``,
554
- ja: `### \u30C4\u30FC\u30EB
596
+ ja: `### \u30D0\u30EA\u30A2\u30F3\u30C8\u5224\u5225
597
+
598
+ \u63A5\u7D9A\u5148\u306F **InfluxDB 3**\uFF08\`/api/v3/query_sql\` \u306E SQL \u5BFE\u5FDC\uFF09\u3068 **InfluxDB 2**\uFF08Flux \u306E\u307F\u3002\`*.cloud2.influxdata.com\` \u306E InfluxDB Cloud \u3092\u542B\u3080\uFF09\u306E\u3069\u3061\u3089\u306E\u53EF\u80FD\u6027\u3082\u3042\u308A\u307E\u3059\u3002\u5FC5\u305A\u6700\u521D\u306B\u30D7\u30ED\u30FC\u30D6\u3057\u3066\u304B\u3089\u3001\u5BFE\u5FDC\u3059\u308B\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044:
599
+
600
+ 1. \`influxdb_request\` \u3067 POST \`/api/v3/query_sql\`\u3001body \`{ "db": "<database>", "q": "SELECT 1" }\` \u3092\u547C\u3073\u51FA\u3059
601
+ 2. 200 + JSON \u884C \u2192 InfluxDB 3\u3002SQL \u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3092\u4F7F\u7528\u3002
602
+ 3. 405 Method Not Allowed\u3001\u307E\u305F\u306F HTML \u672C\u6587\uFF08\u4F8B: \`<html>...405 Not Allowed...</html>\`\uFF09\u2192 InfluxDB 2\u3002\`/api/v2/query?org={org}\` \u304B\u3089 Flux \u3092\u4F7F\u7528\u3002\`org\` \u30D1\u30E9\u30E1\u30FC\u30BF\u5FC5\u9808\u3002\u672A\u8A2D\u5B9A\u306A\u3089 \`updateConnectionParameters\` \u3067\u30E6\u30FC\u30B6\u30FC\u306B\u8A2D\u5B9A\u3092\u4F9D\u983C\u3059\u308B\u3002
603
+
604
+ ### \u30C4\u30FC\u30EB
555
605
 
556
606
  - \`influxdb_request\`: InfluxDB HTTP API \u3092\u547C\u3073\u51FA\u3059\u552F\u4E00\u306E\u624B\u6BB5\u3067\u3059\u3002SQL / InfluxQL / Flux \u30AF\u30A8\u30EA\u306E\u5B9F\u884C\u3001line protocol \u66F8\u304D\u8FBC\u307F\u3001bucket / database \u306E\u78BA\u8A8D\u306B\u4F7F\u7528\u3057\u307E\u3059\u3002\u8A8D\u8A3C\uFF08\`Authorization: Token {token}\`\uFF09\u3068 instance URL \u306F\u81EA\u52D5\u3067\u8A2D\u5B9A\u3055\u308C\u307E\u3059\u3002InfluxDB 3 \u3067\u306F SQL (\`POST /api/v3/query_sql\`) \u304C JSON \u884C\u3092\u8FD4\u3059\u305F\u3081\u6700\u3082\u6271\u3044\u3084\u3059\u3044\u3067\u3059\u3002InfluxDB 2 \u3067\u306F Flux (\`POST /api/v2/query?org={org}\`) \u3092\u4F7F\u7528\u3057\u3001\u30EC\u30B9\u30DD\u30F3\u30B9\u306F\u6CE8\u91C8\u4ED8\u304D CSV \u3067\u3059\u3002\u66F8\u304D\u8FBC\u307F\u306F \`POST /api/v3/write_lp?db={db}\` (v3) \u307E\u305F\u306F \`POST /api/v2/write?org={org}&bucket={bucket}\` (v2) \u306B line protocol \u3092\u9001\u308A\u307E\u3059\uFF08\`contentType\` \u306F \`text/plain; charset=utf-8\`\uFF09\u3002
557
607
 
@@ -615,7 +665,47 @@ export default async function handler(c: Context) {
615
665
  - \u96C6\u8A08: \`AVG\`, \`SUM\`, \`MIN\`, \`MAX\`, \`COUNT\`\u3002\u6642\u9593\u30D0\u30B1\u30C3\u30C8: \`date_bin('5 minutes', time)\`
616
666
  - measurement \u4E00\u89A7: \`SHOW TABLES\`\u3001\u5217\u4E00\u89A7: \`SHOW COLUMNS FROM <measurement>\``
617
667
  },
618
- tools
668
+ tools,
669
+ async checkConnection(params) {
670
+ const url = (params.url ?? "").replace(/\/$/, "");
671
+ const token = params.token;
672
+ if (!url) {
673
+ return { success: false, error: "InfluxDB URL is not configured" };
674
+ }
675
+ if (!token) {
676
+ return { success: false, error: "API Token is not configured" };
677
+ }
678
+ try {
679
+ const res = await fetch(`${url}/api/v2/orgs`, {
680
+ method: "GET",
681
+ headers: {
682
+ Authorization: `Token ${token}`,
683
+ Accept: "application/json"
684
+ }
685
+ });
686
+ if (res.status === 401 || res.status === 403) {
687
+ return {
688
+ success: false,
689
+ error: `Authentication failed (HTTP ${res.status}). Check the API token and its permissions.`
690
+ };
691
+ }
692
+ if (!res.ok) {
693
+ const body = await res.text().catch(() => res.statusText);
694
+ const snippet = body.replace(/\s+/g, " ").slice(0, 200);
695
+ return {
696
+ success: false,
697
+ error: `Connection check failed: HTTP ${res.status} ${res.statusText} \u2014 ${snippet}`
698
+ };
699
+ }
700
+ return { success: true };
701
+ } catch (error) {
702
+ const message = error instanceof Error ? error.message : String(error);
703
+ return {
704
+ success: false,
705
+ error: `Failed to reach InfluxDB at ${url}: ${message}`
706
+ };
707
+ }
708
+ }
619
709
  });
620
710
 
621
711
  // src/connectors/create-connector-sdk.ts