在 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 对应的基础类型)。它的运行时依赖要求如下:
- peerDependencies:
corsair >= 0.1.120(核心框架)与zod ^4.1.13(输入输出校验); - 构建命令为
pnpm build(tsc + tsup),测试命令为pnpm test(Jest)。
因此,使用该插件前请确保你的项目已经安装了满足版本要求的corsair与zod。
二、端点总览:一张表看懂 12 个操作
插件将功能划分为spreadsheets(电子表格级)与sheets(工作表级)两组,共 12 个端点。下表完整列出各操作的调用名(Operation)、唯一操作 ID、风险级别与说明:
| Operation | Operation ID | Risk | Description |
|---|---|---|---|
sheets.appendOrUpdateRow | googlesheets.api.sheets.appendOrUpdateRow | write | Append a new row or update an existing one |
sheets.appendRow | googlesheets.api.sheets.appendRow | write | Append a new row to a sheet |
sheets.clearSheet | googlesheets.api.sheets.clearSheet | destructive | Clear all data from a sheet [DESTRUCTIVE] |
sheets.createSheet | googlesheets.api.sheets.createSheet | write | Add a new sheet tab to a spreadsheet |
sheets.deleteRowsOrColumns | googlesheets.api.sheets.deleteRowsOrColumns | destructive | Delete rows or columns from a sheet [DESTRUCTIVE] |
sheets.deleteSheet | googlesheets.api.sheets.deleteSheet | destructive | Delete a sheet tab and all its data [DESTRUCTIVE · IRREVERSIBLE] |
sheets.getRows | googlesheets.api.sheets.getRows | read | Read rows from a sheet |
sheets.listSheetsInSpreadsheet | googlesheets.api.sheets.listSheetsInSpreadsheet | read | List all sheet tabs in a spreadsheet |
sheets.updateRow | googlesheets.api.sheets.updateRow | write | Update an existing row in a sheet |
spreadsheets.create | googlesheets.api.spreadsheets.create | write | Create a new spreadsheet |
spreadsheets.delete | googlesheets.api.spreadsheets.delete | destructive | Permanently delete a spreadsheet [DESTRUCTIVE · IRREVERSIBLE] |
spreadsheets.list | googlesheets.api.spreadsheets.list | read | List 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.getRows、spreadsheets.list的点分路径即可,而每个端点对应的唯一 Operation ID(如googlesheets.api.sheets.appendRow)则用于权限系统与审计日志的精确标记。三个destructive且irreversible: true的端点(spreadsheets.delete、sheets.deleteSheet、sheets.deleteRowsOrColumns与sheets.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:时区,可选。
创建成功后,源码会将返回的spreadsheetId、title、spreadsheetUrl写入本地数据库(ctx.db.spreadsheets.upsertByEntityId),并通过logEventFromContext记录事件googlesheets.spreadsheets.create为completed。输出为SpreadsheetSchema(spreadsheetId、properties、spreadsheetUrl)。
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数组(每项含id、name、createdTime、modifiedTime、webViewLink)与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),供后续查询或审计使用。输出为ValueRange(range、majorDimension、values)。
4.2sheets.appendRow:追加新行
向工作表末尾追加一行,底层调用POST /spreadsheets/{id}/values/{range}:append。输入参数:
spreadsheetId(必填);sheetName:默认Sheet1;range:默认${sheetName}!A:Z;values:一维数组,元素类型为string | number | boolean | null,作为一行写入;valueInputOption:RAW/USER_ENTERED(默认,按用户输入解析,可识别公式与日期);insertDataOption:OVERWRITE/INSERT_ROWS(默认,插入新行而非覆盖)。
请求体统一为{ values: [input.values], majorDimension: 'ROWS' },即把传入的一维数组包装为单行二维数组。响应为BatchUpdateValuesResponse(含totalUpdatedRows、totalUpdatedCells、responses等统计字段)。
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:待写入的一行数据;valueInputOption、insertDataOption:同appendRow。
其执行流程可以拆解为三步:
GET读取sheetName!keyColumn:keyColumn整列,逐行比对values[i][0] === String(keyValue)找到目标行号rowIndex;- 若命中(
rowIndex >= 0),调用PUT /spreadsheets/{id}/values/{range}更新sheetName!{rowIndex+1}:{rowIndex+1}这一行,日志记录action: 'update'; - 若未命中,调用
: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。输入为spreadsheetId、sheetName(默认Sheet1)、range(默认取sheetName本身)。风险级别destructive——清空操作不可恢复,且源码会同步删除本地数据库中对应clearedRange的行记录(ctx.db.rows.deleteByEntityId)。输出为ClearValuesResponse(含clearedRange)。
4.6sheets.createSheet:新建工作表标签
在已有电子表格内新增一个 sheet tab,底层调用POST /spreadsheets/{id}:batchUpdate,请求体为addSheet请求。输入参数:spreadsheetId(必填)、title(默认Sheet1)。创建成功后会把sheetId、title、index写入ctx.db.sheets。输出为BatchUpdateSpreadsheetResponse(replies中包含新 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:删除行列(破坏性)
按索引区间删除行或列,底层调用:batchUpdate的deleteDimension请求。输入参数:
spreadsheetId(必填)、sheetId(必填,数字);dimension:ROWS(默认)/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[] },每个SheetProperties含sheetId、title、index、sheetType(GRID/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 实现了两层关键逻辑:
- 统一请求封装:
makeSheetsRequest基于corsair/http的request,BASE 固定为https://sheets.googleapis.com/v4;Drive 请求则使用原生fetch直连https://www.googleapis.com/drive/v3; - 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)允许spreadsheetId、sheetName、range、values、eventType、timestamp、event等字段,其中eventType固定为字面量'rangeUpdated'。
6.2 匹配与处理流程
Webhook 处理器实现在 webhooks/rows.ts:
- 匹配器:
createGoogleSheetsWebhookMatcher('rangeUpdated')解析请求体并校验body.eventType === 'rangeUpdated'(请求体为字符串时会先JSON.parse); - 校验:缺少
spreadsheetId或values时返回{ success: false, error: 'Missing required fields: spreadsheetId or values' }; - 落库:将事件写入
ctx.db.rows(键为spreadsheetId_sheetName_range),补全默认值(sheetName默认Sheet1,range默认A:Z,timestamp默认当前时间); - 日志:记录事件
googlesheets.webhook.rangeUpdated为completed; - 返回:
{ success: true, data: event }。
6.3 租户路由
除事件级匹配外,插件还通过pluginWebhookMatcher(检查负载中是否存在spreadsheetId字符串或eventType === 'rangeUpdated')与pluginTenantWebhookMatcher(见 webhooks/tenant-matcher.ts)实现多租户路由:Corsair 会依据负载中的spreadsheetId将请求分发到拥有该表格凭证的租户,确保数据不会跨租户错配。
七、数据库模型:插件的本地状态
插件通过 Corsair 的数据库能力维护三张实体表(定义于 schema/database.ts):
| 实体 | 关键字段 | 维护时机 |
|---|---|---|
spreadsheets | spreadsheetId(主键)、title、spreadsheetUrl、createdAt | spreadsheets.create/spreadsheets.delete |
sheets | sheetId(主键)、spreadsheetId、title、index、createdAt | sheets.createSheet/sheets.deleteSheet |
rows | rowId(主键,spreadsheetId_sheetName_range)、spreadsheetId、sheetName、range、values、createdAt | 所有行读写端点及rangeUpdatedWebhook |
从源码可以看到,所有写操作都遵循"先调 Google API,再同步本地缓存"的模式,并且落库失败只打印console.warn而不中断主流程(如 sheets.ts),保证了远端操作与本地缓存的最终一致。这套本地模型让 Agent 可以基于缓存快速检索"哪些行已经存在",支撑appendOrUpdateRow的去重语义,也方便审计追溯。
八、权限与风险模型
插件在 index.ts 中为每个端点声明了风险元数据,供 Corsair 的 MCP 权限系统(allow / deny / require_approval)决策:
read级:spreadsheets.list、sheets.getRows、sheets.listSheetsInSpreadsheet——只读操作,通常直接放行;write级:spreadsheets.create、sheets.appendRow、sheets.appendOrUpdateRow、sheets.updateRow、sheets.createSheet——会修改数据,建议授权或要求确认;destructive级:spreadsheets.delete、sheets.deleteSheet(均标记irreversible: true)、sheets.clearSheet、sheets.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 目录还提供了overview、api、database、webhooks、get-credentials五个专题文档(对应插件文档源文件overview.mdx、api.mdx、database.mdx、webhooks.mdx、get-credentials.mdx),包含完整的参考类型与示例,可作为接入时的补充资料。
十、许可证
@corsair-dev/googlesheets以Apache-2.0协议开源(见 package.json 与 README 的 License 一节),可放心用于商业项目。
总结:最小接入三步走
- 安装:
pnpm add @corsair-dev/googlesheets,确保corsair >= 0.1.120与zod ^4.1.13就绪; - 注册插件:在 Corsair 应用中调用
googlesheets({ ... })工厂函数,按需配置permissions与webhookHooks; - 体验能力:租户完成一次 OAuth 授权后,Agent 即可使用
spreadsheets.list→sheets.getRows/sheets.appendOrUpdateRow/sheets.appendRow完成"查找 → 读取 → 写入"的完整数据闭环,并通过rangeUpdatedWebhook 感知表格的后续外部变更。
【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考