cc-lark 0.1.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/.github/workflows/ci.yml +47 -0
- package/.github/workflows/release.yml +47 -0
- package/.github/workflows/sync-upstream.yml +127 -0
- package/.prettierrc.json +7 -0
- package/README.md +214 -0
- package/dist/core/api-error.d.ts +193 -0
- package/dist/core/api-error.d.ts.map +1 -0
- package/dist/core/api-error.js +263 -0
- package/dist/core/api-error.js.map +1 -0
- package/dist/core/auth-errors.d.ts +13 -0
- package/dist/core/auth-errors.d.ts.map +1 -0
- package/dist/core/auth-errors.js +14 -0
- package/dist/core/auth-errors.js.map +1 -0
- package/dist/core/config.d.ts +60 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +115 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/device-flow.d.ts +80 -0
- package/dist/core/device-flow.d.ts.map +1 -0
- package/dist/core/device-flow.js +231 -0
- package/dist/core/device-flow.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +16 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lark-client.d.ts +136 -0
- package/dist/core/lark-client.d.ts.map +1 -0
- package/dist/core/lark-client.js +315 -0
- package/dist/core/lark-client.js.map +1 -0
- package/dist/core/token-store.d.ts +67 -0
- package/dist/core/token-store.d.ts.map +1 -0
- package/dist/core/token-store.js +215 -0
- package/dist/core/token-store.js.map +1 -0
- package/dist/core/types.d.ts +286 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +11 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/uat-client.d.ts +64 -0
- package/dist/core/uat-client.d.ts.map +1 -0
- package/dist/core/uat-client.js +227 -0
- package/dist/core/uat-client.js.map +1 -0
- package/dist/core/version.d.ts +26 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +50 -0
- package/dist/core/version.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/bitable/app.d.ts +20 -0
- package/dist/tools/bitable/app.d.ts.map +1 -0
- package/dist/tools/bitable/app.js +301 -0
- package/dist/tools/bitable/app.js.map +1 -0
- package/dist/tools/bitable/field.d.ts +19 -0
- package/dist/tools/bitable/field.d.ts.map +1 -0
- package/dist/tools/bitable/field.js +315 -0
- package/dist/tools/bitable/field.js.map +1 -0
- package/dist/tools/bitable/index.d.ts +21 -0
- package/dist/tools/bitable/index.d.ts.map +1 -0
- package/dist/tools/bitable/index.js +39 -0
- package/dist/tools/bitable/index.js.map +1 -0
- package/dist/tools/bitable/record.d.ts +22 -0
- package/dist/tools/bitable/record.d.ts.map +1 -0
- package/dist/tools/bitable/record.js +434 -0
- package/dist/tools/bitable/record.js.map +1 -0
- package/dist/tools/bitable/table.d.ts +21 -0
- package/dist/tools/bitable/table.d.ts.map +1 -0
- package/dist/tools/bitable/table.js +361 -0
- package/dist/tools/bitable/table.js.map +1 -0
- package/dist/tools/calendar/calendar.d.ts +18 -0
- package/dist/tools/calendar/calendar.d.ts.map +1 -0
- package/dist/tools/calendar/calendar.js +192 -0
- package/dist/tools/calendar/calendar.js.map +1 -0
- package/dist/tools/calendar/event.d.ts +20 -0
- package/dist/tools/calendar/event.d.ts.map +1 -0
- package/dist/tools/calendar/event.js +465 -0
- package/dist/tools/calendar/event.js.map +1 -0
- package/dist/tools/calendar/index.d.ts +19 -0
- package/dist/tools/calendar/index.d.ts.map +1 -0
- package/dist/tools/calendar/index.js +37 -0
- package/dist/tools/calendar/index.js.map +1 -0
- package/dist/tools/chat/chat.d.ts +11 -0
- package/dist/tools/chat/chat.d.ts.map +1 -0
- package/dist/tools/chat/chat.js +106 -0
- package/dist/tools/chat/chat.js.map +1 -0
- package/dist/tools/chat/index.d.ts +11 -0
- package/dist/tools/chat/index.d.ts.map +1 -0
- package/dist/tools/chat/index.js +20 -0
- package/dist/tools/chat/index.js.map +1 -0
- package/dist/tools/chat/members.d.ts +9 -0
- package/dist/tools/chat/members.d.ts.map +1 -0
- package/dist/tools/chat/members.js +80 -0
- package/dist/tools/chat/members.js.map +1 -0
- package/dist/tools/common/get-user.d.ts +11 -0
- package/dist/tools/common/get-user.d.ts.map +1 -0
- package/dist/tools/common/get-user.js +112 -0
- package/dist/tools/common/get-user.js.map +1 -0
- package/dist/tools/common/index.d.ts +11 -0
- package/dist/tools/common/index.d.ts.map +1 -0
- package/dist/tools/common/index.js +20 -0
- package/dist/tools/common/index.js.map +1 -0
- package/dist/tools/common/search-user.d.ts +9 -0
- package/dist/tools/common/search-user.d.ts.map +1 -0
- package/dist/tools/common/search-user.js +88 -0
- package/dist/tools/common/search-user.js.map +1 -0
- package/dist/tools/doc/create.d.ts +17 -0
- package/dist/tools/doc/create.d.ts.map +1 -0
- package/dist/tools/doc/create.js +159 -0
- package/dist/tools/doc/create.js.map +1 -0
- package/dist/tools/doc/fetch.d.ts +17 -0
- package/dist/tools/doc/fetch.d.ts.map +1 -0
- package/dist/tools/doc/fetch.js +123 -0
- package/dist/tools/doc/fetch.js.map +1 -0
- package/dist/tools/doc/index.d.ts +21 -0
- package/dist/tools/doc/index.d.ts.map +1 -0
- package/dist/tools/doc/index.js +33 -0
- package/dist/tools/doc/index.js.map +1 -0
- package/dist/tools/doc/shared.d.ts +69 -0
- package/dist/tools/doc/shared.d.ts.map +1 -0
- package/dist/tools/doc/shared.js +172 -0
- package/dist/tools/doc/shared.js.map +1 -0
- package/dist/tools/doc/update.d.ts +25 -0
- package/dist/tools/doc/update.d.ts.map +1 -0
- package/dist/tools/doc/update.js +208 -0
- package/dist/tools/doc/update.js.map +1 -0
- package/dist/tools/drive/file.d.ts +13 -0
- package/dist/tools/drive/file.d.ts.map +1 -0
- package/dist/tools/drive/file.js +212 -0
- package/dist/tools/drive/file.js.map +1 -0
- package/dist/tools/drive/index.d.ts +12 -0
- package/dist/tools/drive/index.d.ts.map +1 -0
- package/dist/tools/drive/index.js +25 -0
- package/dist/tools/drive/index.js.map +1 -0
- package/dist/tools/im/format-messages.d.ts +99 -0
- package/dist/tools/im/format-messages.d.ts.map +1 -0
- package/dist/tools/im/format-messages.js +277 -0
- package/dist/tools/im/format-messages.js.map +1 -0
- package/dist/tools/im/helpers.d.ts +53 -0
- package/dist/tools/im/helpers.d.ts.map +1 -0
- package/dist/tools/im/helpers.js +85 -0
- package/dist/tools/im/helpers.js.map +1 -0
- package/dist/tools/im/index.d.ts +25 -0
- package/dist/tools/im/index.d.ts.map +1 -0
- package/dist/tools/im/index.js +44 -0
- package/dist/tools/im/index.js.map +1 -0
- package/dist/tools/im/message-read.d.ts +19 -0
- package/dist/tools/im/message-read.d.ts.map +1 -0
- package/dist/tools/im/message-read.js +526 -0
- package/dist/tools/im/message-read.js.map +1 -0
- package/dist/tools/im/message.d.ts +22 -0
- package/dist/tools/im/message.d.ts.map +1 -0
- package/dist/tools/im/message.js +233 -0
- package/dist/tools/im/message.js.map +1 -0
- package/dist/tools/im/resource.d.ts +19 -0
- package/dist/tools/im/resource.d.ts.map +1 -0
- package/dist/tools/im/resource.js +185 -0
- package/dist/tools/im/resource.js.map +1 -0
- package/dist/tools/im/time-utils.d.ts +70 -0
- package/dist/tools/im/time-utils.d.ts.map +1 -0
- package/dist/tools/im/time-utils.js +277 -0
- package/dist/tools/im/time-utils.js.map +1 -0
- package/dist/tools/index.d.ts +85 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +135 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/oauth.d.ts +15 -0
- package/dist/tools/oauth.d.ts.map +1 -0
- package/dist/tools/oauth.js +379 -0
- package/dist/tools/oauth.js.map +1 -0
- package/dist/tools/search/doc-search.d.ts +9 -0
- package/dist/tools/search/doc-search.d.ts.map +1 -0
- package/dist/tools/search/doc-search.js +219 -0
- package/dist/tools/search/doc-search.js.map +1 -0
- package/dist/tools/search/index.d.ts +11 -0
- package/dist/tools/search/index.d.ts.map +1 -0
- package/dist/tools/search/index.js +18 -0
- package/dist/tools/search/index.js.map +1 -0
- package/dist/tools/sheets/index.d.ts +11 -0
- package/dist/tools/sheets/index.d.ts.map +1 -0
- package/dist/tools/sheets/index.js +18 -0
- package/dist/tools/sheets/index.js.map +1 -0
- package/dist/tools/sheets/sheet.d.ts +11 -0
- package/dist/tools/sheets/sheet.d.ts.map +1 -0
- package/dist/tools/sheets/sheet.js +332 -0
- package/dist/tools/sheets/sheet.js.map +1 -0
- package/dist/tools/task/index.d.ts +12 -0
- package/dist/tools/task/index.d.ts.map +1 -0
- package/dist/tools/task/index.js +30 -0
- package/dist/tools/task/index.js.map +1 -0
- package/dist/tools/task/task.d.ts +13 -0
- package/dist/tools/task/task.d.ts.map +1 -0
- package/dist/tools/task/task.js +225 -0
- package/dist/tools/task/task.js.map +1 -0
- package/dist/tools/task/tasklist.d.ts +13 -0
- package/dist/tools/task/tasklist.d.ts.map +1 -0
- package/dist/tools/task/tasklist.js +206 -0
- package/dist/tools/task/tasklist.js.map +1 -0
- package/dist/tools/wiki/index.d.ts +11 -0
- package/dist/tools/wiki/index.d.ts.map +1 -0
- package/dist/tools/wiki/index.js +20 -0
- package/dist/tools/wiki/index.js.map +1 -0
- package/dist/tools/wiki/node.d.ts +11 -0
- package/dist/tools/wiki/node.d.ts.map +1 -0
- package/dist/tools/wiki/node.js +112 -0
- package/dist/tools/wiki/node.js.map +1 -0
- package/dist/tools/wiki/space.d.ts +11 -0
- package/dist/tools/wiki/space.d.ts.map +1 -0
- package/dist/tools/wiki/space.js +125 -0
- package/dist/tools/wiki/space.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +101 -0
- package/dist/utils/logger.js.map +1 -0
- package/eslint.config.js +13 -0
- package/package.json +54 -0
- package/skills/feishu-bitable/SKILL.md +248 -0
- package/skills/feishu-bitable/references/examples.md +813 -0
- package/skills/feishu-bitable/references/field-properties.md +763 -0
- package/skills/feishu-bitable/references/record-values.md +911 -0
- package/skills/feishu-calendar/SKILL.md +244 -0
- package/skills/feishu-channel-rules/SKILL.md +18 -0
- package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
- package/skills/feishu-create-doc/SKILL.md +719 -0
- package/skills/feishu-fetch-doc/SKILL.md +93 -0
- package/skills/feishu-im-read/SKILL.md +163 -0
- package/skills/feishu-task/SKILL.md +293 -0
- package/skills/feishu-troubleshoot/SKILL.md +70 -0
- package/skills/feishu-update-doc/SKILL.md +285 -0
- package/src/core/api-error.ts +342 -0
- package/src/core/auth-errors.ts +27 -0
- package/src/core/config.ts +134 -0
- package/src/core/device-flow.ts +314 -0
- package/src/core/index.ts +16 -0
- package/src/core/lark-client.ts +391 -0
- package/src/core/token-store.ts +249 -0
- package/src/core/types.ts +302 -0
- package/src/core/uat-client.ts +298 -0
- package/src/core/version.ts +53 -0
- package/src/index.ts +138 -0
- package/src/tools/bitable/app.ts +390 -0
- package/src/tools/bitable/field.ts +406 -0
- package/src/tools/bitable/index.ts +43 -0
- package/src/tools/bitable/record.ts +559 -0
- package/src/tools/bitable/table.ts +472 -0
- package/src/tools/calendar/calendar.ts +254 -0
- package/src/tools/calendar/event.ts +606 -0
- package/src/tools/calendar/index.ts +41 -0
- package/src/tools/chat/chat.ts +127 -0
- package/src/tools/chat/index.ts +24 -0
- package/src/tools/chat/members.ts +93 -0
- package/src/tools/common/get-user.ts +127 -0
- package/src/tools/common/index.ts +24 -0
- package/src/tools/common/search-user.ts +99 -0
- package/src/tools/doc/create.ts +184 -0
- package/src/tools/doc/fetch.ts +149 -0
- package/src/tools/doc/index.ts +38 -0
- package/src/tools/doc/shared.ts +228 -0
- package/src/tools/doc/update.ts +240 -0
- package/src/tools/drive/file.ts +265 -0
- package/src/tools/drive/index.ts +29 -0
- package/src/tools/im/format-messages.ts +391 -0
- package/src/tools/im/helpers.ts +109 -0
- package/src/tools/im/index.ts +49 -0
- package/src/tools/im/message-read.ts +676 -0
- package/src/tools/im/message.ts +303 -0
- package/src/tools/im/resource.ts +225 -0
- package/src/tools/im/time-utils.ts +347 -0
- package/src/tools/index.ts +205 -0
- package/src/tools/oauth.ts +460 -0
- package/src/tools/search/doc-search.ts +250 -0
- package/src/tools/search/index.ts +22 -0
- package/src/tools/sheets/index.ts +22 -0
- package/src/tools/sheets/sheet.ts +382 -0
- package/src/tools/task/index.ts +34 -0
- package/src/tools/task/task.ts +265 -0
- package/src/tools/task/tasklist.ts +262 -0
- package/src/tools/wiki/index.ts +24 -0
- package/src/tools/wiki/node.ts +131 -0
- package/src/tools/wiki/space.ts +152 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +132 -0
- package/tests/core/config.test.ts +238 -0
- package/tests/core/device-flow.test.ts +490 -0
- package/tests/core/lark-client.test.ts +378 -0
- package/tests/core/token-store.test.ts +438 -0
- package/tests/index.test.ts +360 -0
- package/tests/tools/doc/create.test.ts +224 -0
- package/tests/tools/doc/fetch.test.ts +182 -0
- package/tests/tools/doc/shared.test.ts +183 -0
- package/tests/tools/doc/update.test.ts +330 -0
- package/tests/tools/im/format-messages.test.ts +184 -0
- package/tests/tools/im/time-utils.test.ts +178 -0
- package/tests/utils/logger.test.ts +140 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* feishu_bitable_field tool - Manage Feishu Bitable fields (columns).
|
|
6
|
+
*
|
|
7
|
+
* Actions: create, list, update, delete
|
|
8
|
+
*
|
|
9
|
+
* Uses the Feishu Bitable v1 API:
|
|
10
|
+
* - create: POST /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields
|
|
11
|
+
* - list: GET /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields
|
|
12
|
+
* - update: PUT /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields/:field_id
|
|
13
|
+
* - delete: DELETE /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields/:field_id
|
|
14
|
+
*
|
|
15
|
+
* Adapted from openclaw-lark for MCP Server architecture.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import type { ToolRegistry } from '../index.js';
|
|
20
|
+
import { LarkClient } from '../../core/lark-client.js';
|
|
21
|
+
import { getValidAccessToken, NeedAuthorizationError } from '../../core/uat-client.js';
|
|
22
|
+
import { assertLarkOk } from '../../core/api-error.js';
|
|
23
|
+
import { json, jsonError, type ToolResult } from '../im/helpers.js';
|
|
24
|
+
import { logger } from '../../utils/logger.js';
|
|
25
|
+
|
|
26
|
+
const log = logger('tools:bitable:field');
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Input schemas
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
const createActionSchema = {
|
|
33
|
+
action: z.literal('create').describe('Create a new field in the table'),
|
|
34
|
+
app_token: z.string().describe('Bitable app token'),
|
|
35
|
+
table_id: z.string().describe('Table ID'),
|
|
36
|
+
field_name: z.string().describe('Field name'),
|
|
37
|
+
type: z.number().describe('Field type (1=text, 2=number, 3=single_select, 4=multi_select, 5=date, 7=checkbox, 11=person, 13=phone, 15=url, 17=attachment, 1001=created_time, 1002=modified_time, etc.)'),
|
|
38
|
+
property: z.any().optional().describe('Field property configuration (varies by type). IMPORTANT: URL fields (type=15) and checkbox fields (type=7) must NOT have this parameter'),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const listActionSchema = {
|
|
42
|
+
action: z.literal('list').describe('List fields in the table'),
|
|
43
|
+
app_token: z.string().describe('Bitable app token'),
|
|
44
|
+
table_id: z.string().describe('Table ID'),
|
|
45
|
+
view_id: z.string().optional().describe('View ID (optional)'),
|
|
46
|
+
page_size: z.number().min(1).max(100).optional().describe('Page size (default 50)'),
|
|
47
|
+
page_token: z.string().optional().describe('Pagination token'),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const updateActionSchema = {
|
|
51
|
+
action: z.literal('update').describe('Update a field'),
|
|
52
|
+
app_token: z.string().describe('Bitable app token'),
|
|
53
|
+
table_id: z.string().describe('Table ID'),
|
|
54
|
+
field_id: z.string().describe('Field ID'),
|
|
55
|
+
field_name: z.string().optional().describe('New field name (optional, will auto-fetch if not provided)'),
|
|
56
|
+
type: z.number().optional().describe('Field type (optional, will auto-fetch if not provided)'),
|
|
57
|
+
property: z.any().optional().describe('Field property configuration (optional)'),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const deleteActionSchema = {
|
|
61
|
+
action: z.literal('delete').describe('Delete a field'),
|
|
62
|
+
app_token: z.string().describe('Bitable app token'),
|
|
63
|
+
table_id: z.string().describe('Table ID'),
|
|
64
|
+
field_id: z.string().describe('Field ID'),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Tool registration
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
export function registerBitableFieldTool(registry: ToolRegistry): void {
|
|
72
|
+
registry.register({
|
|
73
|
+
name: 'feishu_bitable_field_create',
|
|
74
|
+
description: [
|
|
75
|
+
'Create a new field (column) in a Feishu Bitable table.',
|
|
76
|
+
'',
|
|
77
|
+
'Parameters:',
|
|
78
|
+
'- app_token: Bitable app token',
|
|
79
|
+
'- table_id: Table ID',
|
|
80
|
+
'- field_name: Field name',
|
|
81
|
+
'- type: Field type number',
|
|
82
|
+
'- property: Field property configuration (optional, varies by type)',
|
|
83
|
+
'',
|
|
84
|
+
'IMPORTANT: URL fields (type=15) and checkbox fields (type=7) must NOT have property parameter.',
|
|
85
|
+
'',
|
|
86
|
+
'Returns the created field info.',
|
|
87
|
+
'Requires OAuth authorization.',
|
|
88
|
+
].join('\n'),
|
|
89
|
+
inputSchema: createActionSchema,
|
|
90
|
+
handler: async (args, context) => handleCreate(args, context),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
registry.register({
|
|
94
|
+
name: 'feishu_bitable_field_list',
|
|
95
|
+
description: [
|
|
96
|
+
'List fields (columns) in a Feishu Bitable table.',
|
|
97
|
+
'',
|
|
98
|
+
'Parameters:',
|
|
99
|
+
'- app_token: Bitable app token',
|
|
100
|
+
'- table_id: Table ID',
|
|
101
|
+
'- view_id: View ID (optional)',
|
|
102
|
+
'- page_size: Page size (default 50)',
|
|
103
|
+
'- page_token: Pagination token',
|
|
104
|
+
'',
|
|
105
|
+
'Returns list of fields.',
|
|
106
|
+
'Requires OAuth authorization.',
|
|
107
|
+
].join('\n'),
|
|
108
|
+
inputSchema: listActionSchema,
|
|
109
|
+
handler: async (args, context) => handleList(args, context),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
registry.register({
|
|
113
|
+
name: 'feishu_bitable_field_update',
|
|
114
|
+
description: [
|
|
115
|
+
'Update a field (column) in a Feishu Bitable table.',
|
|
116
|
+
'',
|
|
117
|
+
'Parameters:',
|
|
118
|
+
'- app_token: Bitable app token',
|
|
119
|
+
'- table_id: Table ID',
|
|
120
|
+
'- field_id: Field ID',
|
|
121
|
+
'- field_name: New field name (optional)',
|
|
122
|
+
'- type: Field type (optional)',
|
|
123
|
+
'- property: Field property configuration (optional)',
|
|
124
|
+
'',
|
|
125
|
+
'If type or field_name not provided, will auto-fetch current values.',
|
|
126
|
+
'Returns the updated field info.',
|
|
127
|
+
'Requires OAuth authorization.',
|
|
128
|
+
].join('\n'),
|
|
129
|
+
inputSchema: updateActionSchema,
|
|
130
|
+
handler: async (args, context) => handleUpdate(args, context),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
registry.register({
|
|
134
|
+
name: 'feishu_bitable_field_delete',
|
|
135
|
+
description: [
|
|
136
|
+
'Delete a field (column) from a Feishu Bitable table.',
|
|
137
|
+
'',
|
|
138
|
+
'Parameters:',
|
|
139
|
+
'- app_token: Bitable app token',
|
|
140
|
+
'- table_id: Table ID',
|
|
141
|
+
'- field_id: Field ID',
|
|
142
|
+
'',
|
|
143
|
+
'Returns success status.',
|
|
144
|
+
'Requires OAuth authorization.',
|
|
145
|
+
].join('\n'),
|
|
146
|
+
inputSchema: deleteActionSchema,
|
|
147
|
+
handler: async (args, context) => handleDelete(args, context),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
log.debug('feishu_bitable_field tools registered');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Helpers
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
async function getAccessToken(context: { larkClient: LarkClient | null; config: import('../../core/types.js').FeishuConfig }): Promise<string | ToolResult> {
|
|
158
|
+
const { larkClient, config } = context;
|
|
159
|
+
if (!larkClient) {
|
|
160
|
+
return jsonError('LarkClient not initialized. Check FEISHU_APP_ID and FEISHU_APP_SECRET.');
|
|
161
|
+
}
|
|
162
|
+
const { appId, appSecret, brand } = config;
|
|
163
|
+
if (!appId || !appSecret) {
|
|
164
|
+
return jsonError('Missing FEISHU_APP_ID or FEISHU_APP_SECRET.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { listStoredTokens } = await import('../../core/token-store.js');
|
|
168
|
+
const tokens = await listStoredTokens(appId);
|
|
169
|
+
if (tokens.length === 0) {
|
|
170
|
+
return jsonError(
|
|
171
|
+
'No user authorization found. Please use the feishu_oauth tool with action="authorize" to authorize a user first.'
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const userOpenId = tokens[0].userOpenId;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
return await getValidAccessToken({
|
|
179
|
+
userOpenId,
|
|
180
|
+
appId,
|
|
181
|
+
appSecret,
|
|
182
|
+
domain: brand ?? 'feishu',
|
|
183
|
+
});
|
|
184
|
+
} catch (err) {
|
|
185
|
+
if (err instanceof NeedAuthorizationError) {
|
|
186
|
+
return jsonError(
|
|
187
|
+
`User authorization required or expired. Please use feishu_oauth tool with action="authorize" to re-authorize.`,
|
|
188
|
+
{ userOpenId }
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
throw err;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Handlers
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
async function handleCreate(
|
|
200
|
+
args: unknown,
|
|
201
|
+
context: { larkClient: LarkClient | null; config: import('../../core/types.js').FeishuConfig }
|
|
202
|
+
): Promise<ToolResult> {
|
|
203
|
+
const p = args as z.infer<ReturnType<typeof z.object<typeof createActionSchema>>>;
|
|
204
|
+
const { larkClient } = context;
|
|
205
|
+
|
|
206
|
+
const accessTokenResult = await getAccessToken(context);
|
|
207
|
+
if (typeof accessTokenResult === 'object' && 'content' in accessTokenResult) {
|
|
208
|
+
return accessTokenResult;
|
|
209
|
+
}
|
|
210
|
+
const accessToken = accessTokenResult;
|
|
211
|
+
|
|
212
|
+
log.info(`create: app_token=${p.app_token}, table_id=${p.table_id}, field_name=${p.field_name}, type=${p.type}`);
|
|
213
|
+
|
|
214
|
+
const Lark = await import('@larksuiteoapi/node-sdk');
|
|
215
|
+
const opts = Lark.withUserAccessToken(accessToken);
|
|
216
|
+
|
|
217
|
+
// Handle special case: checkbox (type=7) and URL (type=15) fields must not have property
|
|
218
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
219
|
+
let propertyToSend: any = p.property;
|
|
220
|
+
if ((p.type === 15 || p.type === 7) && p.property !== undefined) {
|
|
221
|
+
const fieldTypeName = p.type === 15 ? 'URL' : 'Checkbox';
|
|
222
|
+
log.warn(`create: ${fieldTypeName} field (type=${p.type}) detected with property. Removing property to avoid API error.`);
|
|
223
|
+
propertyToSend = undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const res = await larkClient!.sdk.bitable.appTableField.create(
|
|
227
|
+
{
|
|
228
|
+
path: { app_token: p.app_token, table_id: p.table_id },
|
|
229
|
+
data: {
|
|
230
|
+
field_name: p.field_name,
|
|
231
|
+
type: p.type,
|
|
232
|
+
property: propertyToSend,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
opts
|
|
236
|
+
);
|
|
237
|
+
assertLarkOk(res);
|
|
238
|
+
|
|
239
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
240
|
+
const data = res.data as any;
|
|
241
|
+
|
|
242
|
+
log.info(`create: created field ${data?.field?.field_id ?? 'unknown'}`);
|
|
243
|
+
|
|
244
|
+
return json({ field: data?.field ?? res.data });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function handleList(
|
|
248
|
+
args: unknown,
|
|
249
|
+
context: { larkClient: LarkClient | null; config: import('../../core/types.js').FeishuConfig }
|
|
250
|
+
): Promise<ToolResult> {
|
|
251
|
+
const p = args as z.infer<ReturnType<typeof z.object<typeof listActionSchema>>>;
|
|
252
|
+
const { larkClient } = context;
|
|
253
|
+
|
|
254
|
+
const accessTokenResult = await getAccessToken(context);
|
|
255
|
+
if (typeof accessTokenResult === 'object' && 'content' in accessTokenResult) {
|
|
256
|
+
return accessTokenResult;
|
|
257
|
+
}
|
|
258
|
+
const accessToken = accessTokenResult;
|
|
259
|
+
|
|
260
|
+
log.info(`list: app_token=${p.app_token}, table_id=${p.table_id}, view_id=${p.view_id ?? 'none'}`);
|
|
261
|
+
|
|
262
|
+
const Lark = await import('@larksuiteoapi/node-sdk');
|
|
263
|
+
const opts = Lark.withUserAccessToken(accessToken);
|
|
264
|
+
|
|
265
|
+
const res = await larkClient!.sdk.bitable.appTableField.list(
|
|
266
|
+
{
|
|
267
|
+
path: { app_token: p.app_token, table_id: p.table_id },
|
|
268
|
+
params: {
|
|
269
|
+
view_id: p.view_id,
|
|
270
|
+
page_size: p.page_size,
|
|
271
|
+
page_token: p.page_token,
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
opts
|
|
275
|
+
);
|
|
276
|
+
assertLarkOk(res);
|
|
277
|
+
|
|
278
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
279
|
+
const data = res.data as any;
|
|
280
|
+
|
|
281
|
+
log.info(`list: returned ${data?.items?.length ?? 0} fields`);
|
|
282
|
+
|
|
283
|
+
return json({
|
|
284
|
+
fields: data?.items,
|
|
285
|
+
has_more: data?.has_more ?? false,
|
|
286
|
+
page_token: data?.page_token,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function handleUpdate(
|
|
291
|
+
args: unknown,
|
|
292
|
+
context: { larkClient: LarkClient | null; config: import('../../core/types.js').FeishuConfig }
|
|
293
|
+
): Promise<ToolResult> {
|
|
294
|
+
const p = args as z.infer<ReturnType<typeof z.object<typeof updateActionSchema>>>;
|
|
295
|
+
const { larkClient } = context;
|
|
296
|
+
|
|
297
|
+
const accessTokenResult = await getAccessToken(context);
|
|
298
|
+
if (typeof accessTokenResult === 'object' && 'content' in accessTokenResult) {
|
|
299
|
+
return accessTokenResult;
|
|
300
|
+
}
|
|
301
|
+
const accessToken = accessTokenResult;
|
|
302
|
+
|
|
303
|
+
log.info(`update: app_token=${p.app_token}, table_id=${p.table_id}, field_id=${p.field_id}`);
|
|
304
|
+
|
|
305
|
+
const Lark = await import('@larksuiteoapi/node-sdk');
|
|
306
|
+
const opts = Lark.withUserAccessToken(accessToken);
|
|
307
|
+
|
|
308
|
+
// If missing type or field_name, auto-query current field info
|
|
309
|
+
let finalFieldName = p.field_name;
|
|
310
|
+
let finalType = p.type;
|
|
311
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
312
|
+
let finalProperty: any = p.property;
|
|
313
|
+
|
|
314
|
+
if (!finalType || !finalFieldName) {
|
|
315
|
+
log.info(`update: missing type or field_name, auto-querying field info`);
|
|
316
|
+
|
|
317
|
+
const listRes = await larkClient!.sdk.bitable.appTableField.list(
|
|
318
|
+
{
|
|
319
|
+
path: { app_token: p.app_token, table_id: p.table_id },
|
|
320
|
+
params: { page_size: 500 },
|
|
321
|
+
},
|
|
322
|
+
opts
|
|
323
|
+
);
|
|
324
|
+
assertLarkOk(listRes);
|
|
325
|
+
|
|
326
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
327
|
+
const listData = listRes.data as any;
|
|
328
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
329
|
+
const currentField = listData?.items?.find((f: any) => f.field_id === p.field_id);
|
|
330
|
+
|
|
331
|
+
if (!currentField) {
|
|
332
|
+
return jsonError(`field ${p.field_id} does not exist. Use list action to view all fields.`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Merge: user-provided values take precedence
|
|
336
|
+
finalFieldName = p.field_name || currentField.field_name;
|
|
337
|
+
finalType = p.type ?? currentField.type;
|
|
338
|
+
finalProperty = p.property !== undefined ? p.property : currentField.property;
|
|
339
|
+
|
|
340
|
+
log.info(`update: auto-filled type=${finalType}, field_name=${finalFieldName}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
344
|
+
const updateData: any = {
|
|
345
|
+
field_name: finalFieldName,
|
|
346
|
+
type: finalType,
|
|
347
|
+
};
|
|
348
|
+
if (finalProperty !== undefined) {
|
|
349
|
+
updateData.property = finalProperty;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const res = await larkClient!.sdk.bitable.appTableField.update(
|
|
353
|
+
{
|
|
354
|
+
path: {
|
|
355
|
+
app_token: p.app_token,
|
|
356
|
+
table_id: p.table_id,
|
|
357
|
+
field_id: p.field_id,
|
|
358
|
+
},
|
|
359
|
+
data: updateData,
|
|
360
|
+
},
|
|
361
|
+
opts
|
|
362
|
+
);
|
|
363
|
+
assertLarkOk(res);
|
|
364
|
+
|
|
365
|
+
log.info(`update: updated field ${p.field_id}`);
|
|
366
|
+
|
|
367
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
368
|
+
const data = res.data as any;
|
|
369
|
+
|
|
370
|
+
return json({ field: data?.field ?? res.data });
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function handleDelete(
|
|
374
|
+
args: unknown,
|
|
375
|
+
context: { larkClient: LarkClient | null; config: import('../../core/types.js').FeishuConfig }
|
|
376
|
+
): Promise<ToolResult> {
|
|
377
|
+
const p = args as z.infer<ReturnType<typeof z.object<typeof deleteActionSchema>>>;
|
|
378
|
+
const { larkClient } = context;
|
|
379
|
+
|
|
380
|
+
const accessTokenResult = await getAccessToken(context);
|
|
381
|
+
if (typeof accessTokenResult === 'object' && 'content' in accessTokenResult) {
|
|
382
|
+
return accessTokenResult;
|
|
383
|
+
}
|
|
384
|
+
const accessToken = accessTokenResult;
|
|
385
|
+
|
|
386
|
+
log.info(`delete: app_token=${p.app_token}, table_id=${p.table_id}, field_id=${p.field_id}`);
|
|
387
|
+
|
|
388
|
+
const Lark = await import('@larksuiteoapi/node-sdk');
|
|
389
|
+
const opts = Lark.withUserAccessToken(accessToken);
|
|
390
|
+
|
|
391
|
+
const res = await larkClient!.sdk.bitable.appTableField.delete(
|
|
392
|
+
{
|
|
393
|
+
path: {
|
|
394
|
+
app_token: p.app_token,
|
|
395
|
+
table_id: p.table_id,
|
|
396
|
+
field_id: p.field_id,
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
opts
|
|
400
|
+
);
|
|
401
|
+
assertLarkOk(res);
|
|
402
|
+
|
|
403
|
+
log.info(`delete: deleted field ${p.field_id}`);
|
|
404
|
+
|
|
405
|
+
return json({ success: true });
|
|
406
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Bitable Tools Index
|
|
6
|
+
*
|
|
7
|
+
* Bitable (multidimensional table) tools for Feishu/Lark.
|
|
8
|
+
* Adapted from openclaw-lark for MCP Server architecture.
|
|
9
|
+
*
|
|
10
|
+
* Tools:
|
|
11
|
+
* - feishu_bitable_app: Manage Bitable apps
|
|
12
|
+
* - feishu_bitable_table: Manage tables within a Bitable
|
|
13
|
+
* - feishu_bitable_record: Manage records within a table
|
|
14
|
+
* - feishu_bitable_field: Manage fields within a table
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { ToolRegistry } from '../index.js';
|
|
18
|
+
import { registerBitableAppTool } from './app.js';
|
|
19
|
+
import { registerBitableTableTool } from './table.js';
|
|
20
|
+
import { registerBitableRecordTool } from './record.js';
|
|
21
|
+
import { registerBitableFieldTool } from './field.js';
|
|
22
|
+
import { logger } from '../../utils/logger.js';
|
|
23
|
+
|
|
24
|
+
const log = logger('tools:bitable');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register all Bitable tools with the given registry.
|
|
28
|
+
*/
|
|
29
|
+
export function registerBitableTools(registry: ToolRegistry): void {
|
|
30
|
+
registerBitableAppTool(registry);
|
|
31
|
+
registerBitableTableTool(registry);
|
|
32
|
+
registerBitableRecordTool(registry);
|
|
33
|
+
registerBitableFieldTool(registry);
|
|
34
|
+
|
|
35
|
+
log.info('Bitable tools registered', {
|
|
36
|
+
tools: [
|
|
37
|
+
'feishu_bitable_app',
|
|
38
|
+
'feishu_bitable_table',
|
|
39
|
+
'feishu_bitable_record',
|
|
40
|
+
'feishu_bitable_field',
|
|
41
|
+
].join(', '),
|
|
42
|
+
});
|
|
43
|
+
}
|