在 Corsair 中接入 Google Sheets:`@corsair-dev/googlesheets` 插件完整使用与实现解析
2026/9/16 19:23:33 网站建设 项目流程

在 Corsair 中接入 Google Sheets:@corsair-dev/googlesheets插件完整使用与实现解析

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

导读@corsair-dev/googlesheets是 Corsair 官方提供的 Google Sheets 连接插件,让 AI Agent 能够代表终端用户安全地读取、写入和清空 Google 表格数据。本文以该插件的 README 为主体,结合 endpoints、client、webhooks 等源码实现,系统讲解插件的安装、12 个内置端点(含参数与默认值)、OAuth 2.0 认证流程、Webhook 事件以及权限风险模型,帮助你直接在自己的 Corsair 应用中落地"让 Agent 操作 Google Sheets"的能力。

一、插件概览与安装

@corsair-dev/googlesheets是一个面向 Corsair 平台的 Google Sheets 插件,采用 OAuth 2.0 认证:首次使用时,Corsair 会向你的租户(tenant)提示授权凭证,之后所有 API 调用都会自动携带访问令牌。该插件封装了 Google Sheets API v4 与 Google Drive API v3,并以统一的端点树形式暴露给 Agent。

安装方式

在项目中使用 pnpm 安装即可:

pnpm add @corsair-dev/googlesheets

从 package.json 可以看到,该包以 ESM 模块发布("type": "module"),入口为dist/index.js,类型声明为dist/index.d.ts,并额外导出./types子路径(types.ts中定义与 Google API 对应的基础类型)。它的运行时依赖要求如下:

  • peerDependenciescorsair >= 0.1.120(核心框架)与zod ^4.1.13(输入输出校验);
  • 构建命令为pnpm build(tsc + tsup),测试命令为pnpm test(Jest)。

因此,使用该插件前请确保你的项目已经安装了满足版本要求的corsairzod

二、端点总览:一张表看懂 12 个操作

插件将功能划分为spreadsheets(电子表格级)与sheets(工作表级)两组,共 12 个端点。下表完整列出各操作的调用名(Operation)、唯一操作 ID、风险级别与说明:

OperationOperation IDRiskDescription
sheets.appendOrUpdateRowgooglesheets.api.sheets.appendOrUpdateRowwriteAppend a new row or update an existing one
sheets.appendRowgooglesheets.api.sheets.appendRowwriteAppend a new row to a sheet
sheets.clearSheetgooglesheets.api.sheets.clearSheetdestructiveClear all data from a sheet [DESTRUCTIVE]
sheets.createSheetgooglesheets.api.sheets.createSheetwriteAdd a new sheet tab to a spreadsheet
sheets.deleteRowsOrColumnsgooglesheets.api.sheets.deleteRowsOrColumnsdestructiveDelete rows or columns from a sheet [DESTRUCTIVE]
sheets.deleteSheetgooglesheets.api.sheets.deleteSheetdestructiveDelete a sheet tab and all its data [DESTRUCTIVE · IRREVERSIBLE]
sheets.getRowsgooglesheets.api.sheets.getRowsreadRead rows from a sheet
sheets.listSheetsInSpreadsheetgooglesheets.api.sheets.listSheetsInSpreadsheetreadList all sheet tabs in a spreadsheet
sheets.updateRowgooglesheets.api.sheets.updateRowwriteUpdate an existing row in a sheet
spreadsheets.creategooglesheets.api.spreadsheets.createwriteCreate a new spreadsheet
spreadsheets.deletegooglesheets.api.spreadsheets.deletedestructivePermanently delete a spreadsheet [DESTRUCTIVE · IRREVERSIBLE]
spreadsheets.listgooglesheets.api.spreadsheets.listreadList all spreadsheets in Google Drive

从源码 index.ts 可以看到端点树的实际绑定结构:

const googleSheetsEndpointsNested = { spreadsheets: { create, delete, list, }, sheets: { appendRow, appendOrUpdateRow, getRows, updateRow, clearSheet, createSheet, deleteSheet, deleteRowsOrColumns, listSheetsInSpreadsheet, }, } as const;

这意味着 Agent 调用时使用形如sheets.getRowsspreadsheets.list的点分路径即可,而每个端点对应的唯一 Operation ID(如googlesheets.api.sheets.appendRow)则用于权限系统与审计日志的精确标记。三个destructiveirreversible: true的端点(spreadsheets.deletesheets.deleteSheetsheets.deleteRowsOrColumnssheets.clearSheet等)在 index.ts 的 endpointMeta 中被显式标注,供 MCP 权限系统做 allow / deny / require_approval 决策。

三、spreadsheets组:电子表格生命周期管理

spreadsheets组处理"整张电子表格"级别的操作,实现在 endpoints/spreadsheets.ts。

3.1spreadsheets.create:创建新表格

创建新的 Google Sheets 电子表格,调用 Sheets API 的POST /spreadsheets。输入参数(定义于 endpoints/types.ts):

  • properties.title:表格标题,可选;
  • properties.locale:区域设置,可选;
  • properties.timeZone:时区,可选。

创建成功后,源码会将返回的spreadsheetIdtitlespreadsheetUrl写入本地数据库(ctx.db.spreadsheets.upsertByEntityId),并通过logEventFromContext记录事件googlesheets.spreadsheets.createcompleted输出SpreadsheetSchemaspreadsheetIdpropertiesspreadsheetUrl)。

3.2spreadsheets.list:列出 Drive 中的表格

列出当前用户 Google Drive 中所有电子表格,底层调用 Google Drive API 的GET /files,并通过 MIME 类型过滤:

const mimeFilter = "mimeType='application/vnd.google-apps.spreadsheet'";

输入参数:

  • pageSize:每页条数,可选;
  • pageToken:分页令牌,可选;
  • query:额外的 Drive 查询条件,可选,会与 MIME 过滤条件以AND组合。

输出为ListSpreadsheetsResponseSchema,包含files数组(每项含idnamecreatedTimemodifiedTimewebViewLink)与nextPageToken,天然支持分页遍历用户的所有表格。

3.3spreadsheets.delete:永久删除表格

永久删除一张电子表格,调用 Drive API 的DELETE /files/{spreadsheetId}。这是不可逆操作,风险级别为destructive。源码在删除时做了容错:如果响应状态不是 404(文件已不存在)则抛出错误,404 视为删除成功;同时会清理本地数据库中的对应记录(ctx.db.spreadsheets.deleteByEntityId)。输入仅需spreadsheetId,输出为void

四、sheets组:行级数据操作

sheets组处理"工作表内部"的数据读写,实现在 endpoints/sheets.ts。这一组是 Agent 最常使用的能力,下面逐一展开。

4.1sheets.getRows:读取行数据

读取工作表中的行,底层调用 Sheets APIGET /spreadsheets/{id}/values/{range}。输入参数:

  • spreadsheetId(必填);
  • sheetName:工作表名,默认Sheet1
  • range:A1 范围,默认${sheetName}!A:Z
  • valueRenderOption:取值FORMATTED_VALUE(默认)/UNFORMATTED_VALUE/FORMULA
  • dateTimeRenderOption:取值SERIAL_NUMBER/FORMATTED_STRING(默认)。

读取到数据后,源码会把每一行以spreadsheetId_sheetName_range为唯一键 upsert 到本地数据库(ctx.db.rows),供后续查询或审计使用。输出为ValueRangerangemajorDimensionvalues)。

4.2sheets.appendRow:追加新行

向工作表末尾追加一行,底层调用POST /spreadsheets/{id}/values/{range}:append。输入参数:

  • spreadsheetId(必填);
  • sheetName:默认Sheet1range:默认${sheetName}!A:Z
  • values:一维数组,元素类型为string | number | boolean | null,作为一行写入;
  • valueInputOptionRAW/USER_ENTERED(默认,按用户输入解析,可识别公式与日期);
  • insertDataOptionOVERWRITE/INSERT_ROWS(默认,插入新行而非覆盖)。

请求体统一为{ values: [input.values], majorDimension: 'ROWS' },即把传入的一维数组包装为单行二维数组。响应为BatchUpdateValuesResponse(含totalUpdatedRowstotalUpdatedCellsresponses等统计字段)。

4.3sheets.appendOrUpdateRow:追加或更新(upsert)

这是最实用的"按唯一键写入"端点:先查找指定列中是否已存在匹配值,存在则更新该行,不存在则追加新行。逻辑位于 sheets.ts 的 appendOrUpdateRow。

输入参数:

  • spreadsheetId(必填);
  • sheetName:默认Sheet1
  • keyColumn列字母(如"A"),不是列头名称(如"Company Name"——这是 schema 中通过.describe()明确强调的约定,可选,默认A
  • keyValue:用于匹配的唯一键值,string | number必填(缺失时源码直接抛出Error('keyValue is required for appendOrUpdateRow'));
  • values:待写入的一行数据;
  • valueInputOptioninsertDataOption:同appendRow

其执行流程可以拆解为三步:

  1. GET读取sheetName!keyColumn:keyColumn整列,逐行比对values[i][0] === String(keyValue)找到目标行号rowIndex
  2. 若命中(rowIndex >= 0),调用PUT /spreadsheets/{id}/values/{range}更新sheetName!{rowIndex+1}:{rowIndex+1}这一行,日志记录action: 'update'
  3. 若未命中,调用:append追加到sheetName!A:Z,日志记录action: 'append'

返回值类型为UpdateValuesResponse | AppendValuesResponse。这一端点非常适合"按客户 ID 同步数据"等幂等写入场景。

4.4sheets.updateRow:更新指定行

按行号更新一行,底层调用PUT /spreadsheets/{id}/values/{range}。输入参数:

  • spreadsheetId(必填);
  • sheetName:默认Sheet1
  • rowIndex:行号,默认 1(与sheetName组合为默认 range);
  • range:A1 范围,可选,优先于sheetName + rowIndex
  • values:新行数据;
  • valueInputOption:默认USER_ENTERED

4.5sheets.clearSheet:清空工作表(破坏性)

清空工作表全部数据,底层调用POST /spreadsheets/{id}/values/{range}:clear。输入为spreadsheetIdsheetName(默认Sheet1)、range(默认取sheetName本身)。风险级别destructive——清空操作不可恢复,且源码会同步删除本地数据库中对应clearedRange的行记录(ctx.db.rows.deleteByEntityId)。输出为ClearValuesResponse(含clearedRange)。

4.6sheets.createSheet:新建工作表标签

在已有电子表格内新增一个 sheet tab,底层调用POST /spreadsheets/{id}:batchUpdate,请求体为addSheet请求。输入参数:spreadsheetId(必填)、title默认Sheet1)。创建成功后会把sheetIdtitleindex写入ctx.db.sheets。输出为BatchUpdateSpreadsheetResponsereplies中包含新 sheet 的properties)。

4.7sheets.deleteSheet:删除工作表标签(不可逆)

删除一个 sheet tab 及其全部数据,同样走:batchUpdate,请求体为deleteSheet请求。风险级别destructive · irreversible。输入参数:spreadsheetId(必填)、sheetId数字类型,注意与字符串形式的spreadsheetId区分——这是 Google Sheets API 中 sheet tab 的数字 ID)。删除后同步清理本地ctx.db.sheets记录。

4.8sheets.deleteRowsOrColumns:删除行列(破坏性)

按索引区间删除行或列,底层调用:batchUpdatedeleteDimension请求。输入参数:

  • spreadsheetId(必填)、sheetId(必填,数字);
  • dimensionROWS(默认)/COLUMNS
  • startIndex:起始索引,默认 0(0 基);
  • endIndex:结束索引,默认startIndex + 1

若删除的是行(dimension === 'ROWS'),源码会遍历该区间并清理本地数据库中的行记录。风险级别destructive,需谨慎授权。

4.9sheets.listSheetsInSpreadsheet:列出所有工作表标签

列出电子表格内所有 sheet tab,底层调用GET /spreadsheets/{id}并只请求fields=spreadsheetId,sheets.properties(最小化网络传输)。输入仅需spreadsheetId。输出为ListSheetsResponse{ spreadsheetId, sheets: SheetProperties[] },每个SheetPropertiessheetIdtitleindexsheetTypeGRID/OBJECT/DATA_SOURCE)、hidden等字段。

五、认证机制:OAuth 2.0 与令牌自动刷新

README 明确说明:Auth: OAuth 2.0. Corsair prompts your tenant for credentials on first use(首次使用时 Corsair 向租户提示授权)。源码 index.ts 给出了完整的 OAuth 配置:

oauthConfig: { providerName: 'Google', authUrl: 'https://accounts.google.com/o/oauth2/v2/auth', tokenUrl: 'https://oauth2.googleapis.com/token', scopes: [ 'https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive.readonly', ], authParams: { access_type: 'offline', prompt: 'consent' }, },

要点解读:

  • 申请了两个 scope:spreadsheets(读写表格数据)与drive.readonly(只读 Drive 以支持spreadsheets.list);
  • access_type=offline+prompt=consent用于获取可长期使用的刷新令牌,是服务端 Agent 场景的标准配置;
  • authConfig声明了 OAuth 账户元数据为account: ['spreadsheet_id'],即一个 Google 账户(spreadsheet_id)对应一套凭证,天然支持多租户隔离。

在令牌使用层面,client.ts 实现了两层关键逻辑:

  1. 统一请求封装makeSheetsRequest基于corsair/httprequest,BASE 固定为https://sheets.googleapis.com/v4;Drive 请求则使用原生fetch直连https://www.googleapis.com/drive/v3
  2. 401 自动刷新makeAuthenticatedSheetsRequest捕获 401 后,若上下文中存在_refreshAuth,则调用它获取新令牌并重放请求;Drive 请求的makeAuthenticatedDriveRequest也有同样的逻辑。这意味着 Agent 会话期间令牌过期时,插件会在运行时透明续期,无需人工干预。

令牌的获取入口是插件工厂函数googlesheets()中的keyBuilder:优先使用用户传入的静态options.key,否则通过getOAuthAccessToken(ctx, { plugin: 'googlesheets', tokenUrl })从 Corsair 的 OAuth 存储中换取访问令牌;当没有可用凭证时抛出AuthMissingError('googlesheets', 'oauth_2'),提示租户去完成授权。

六、Webhooks:监听表格数据变化

README 指出插件处理1 个 Webhook 事件,即rangeUpdated。它设计为与Google Apps Script集成:你在表格中部署一个 Apps Script,把onEdit等触发器捕获的变更以 JSON POST 到 Corsair 提供的 Webhook 地址,插件即可把变更同步进来。

6.1 事件结构

rangeUpdated事件的规范化结构定义于 webhooks/types.ts:

RangeUpdatedEvent = { eventType: 'rangeUpdated', spreadsheetId: string, sheetName: string, range: string, values: (string | number | boolean | null)[], timestamp: string, }

传入的原始负载(GoogleAppsScriptWebhookPayloadSchema)允许spreadsheetIdsheetNamerangevalueseventTypetimestampevent等字段,其中eventType固定为字面量'rangeUpdated'

6.2 匹配与处理流程

Webhook 处理器实现在 webhooks/rows.ts:

  1. 匹配器createGoogleSheetsWebhookMatcher('rangeUpdated')解析请求体并校验body.eventType === 'rangeUpdated'(请求体为字符串时会先JSON.parse);
  2. 校验:缺少spreadsheetIdvalues时返回{ success: false, error: 'Missing required fields: spreadsheetId or values' }
  3. 落库:将事件写入ctx.db.rows(键为spreadsheetId_sheetName_range),补全默认值(sheetName默认Sheet1range默认A:Ztimestamp默认当前时间);
  4. 日志:记录事件googlesheets.webhook.rangeUpdatedcompleted
  5. 返回{ success: true, data: event }

6.3 租户路由

除事件级匹配外,插件还通过pluginWebhookMatcher(检查负载中是否存在spreadsheetId字符串或eventType === 'rangeUpdated')与pluginTenantWebhookMatcher(见 webhooks/tenant-matcher.ts)实现多租户路由:Corsair 会依据负载中的spreadsheetId将请求分发到拥有该表格凭证的租户,确保数据不会跨租户错配。

七、数据库模型:插件的本地状态

插件通过 Corsair 的数据库能力维护三张实体表(定义于 schema/database.ts):

实体关键字段维护时机
spreadsheetsspreadsheetId(主键)、titlespreadsheetUrlcreatedAtspreadsheets.create/spreadsheets.delete
sheetssheetId(主键)、spreadsheetIdtitleindexcreatedAtsheets.createSheet/sheets.deleteSheet
rowsrowId(主键,spreadsheetId_sheetName_range)、spreadsheetIdsheetNamerangevaluescreatedAt所有行读写端点及rangeUpdatedWebhook

从源码可以看到,所有写操作都遵循"先调 Google API,再同步本地缓存"的模式,并且落库失败只打印console.warn而不中断主流程(如 sheets.ts),保证了远端操作与本地缓存的最终一致。这套本地模型让 Agent 可以基于缓存快速检索"哪些行已经存在",支撑appendOrUpdateRow的去重语义,也方便审计追溯。

八、权限与风险模型

插件在 index.ts 中为每个端点声明了风险元数据,供 Corsair 的 MCP 权限系统(allow / deny / require_approval)决策:

  • readspreadsheets.listsheets.getRowssheets.listSheetsInSpreadsheet——只读操作,通常直接放行;
  • writespreadsheets.createsheets.appendRowsheets.appendOrUpdateRowsheets.updateRowsheets.createSheet——会修改数据,建议授权或要求确认;
  • destructivespreadsheets.deletesheets.deleteSheet(均标记irreversible: true)、sheets.clearSheetsheets.deleteRowsOrColumns——不可恢复,建议配置为require_approval

插件的permissions配置选项接受基于端点树的点分路径(如'sheets.deleteSheet'),非法路径会直接产生 TypeScript 类型错误:

googlesheets({ permissions: { 'sheets.deleteSheet': 'deny', // 禁止 Agent 删除工作表标签 'sheets.clearSheet': 'require_approval', // 清空需人工确认 'sheets.getRows': 'allow', }, })

九、测试与验证

插件自带完整测试,可从测试用例反推行为约定:

  • api.test.ts:验证各端点的输入输出 Schema 与端点注册(googlesheetsEndpointSchemas)是否一致;
  • integration.test.ts:对端点处理器做集成级验证(结合 jest 配置 jest.config.cjs 运行,命令pnpm test)。

此外,仓库的 docs/plugins/googlesheets 目录还提供了overviewapidatabasewebhooksget-credentials五个专题文档(对应插件文档源文件overview.mdxapi.mdxdatabase.mdxwebhooks.mdxget-credentials.mdx),包含完整的参考类型与示例,可作为接入时的补充资料。

十、许可证

@corsair-dev/googlesheetsApache-2.0协议开源(见 package.json 与 README 的 License 一节),可放心用于商业项目。

总结:最小接入三步走

  1. 安装pnpm add @corsair-dev/googlesheets,确保corsair >= 0.1.120zod ^4.1.13就绪;
  2. 注册插件:在 Corsair 应用中调用googlesheets({ ... })工厂函数,按需配置permissionswebhookHooks
  3. 体验能力:租户完成一次 OAuth 授权后,Agent 即可使用spreadsheets.listsheets.getRows/sheets.appendOrUpdateRow/sheets.appendRow完成"查找 → 读取 → 写入"的完整数据闭环,并通过rangeUpdatedWebhook 感知表格的后续外部变更。

【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询