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.
Files changed (298) hide show
  1. package/.github/workflows/ci.yml +47 -0
  2. package/.github/workflows/release.yml +47 -0
  3. package/.github/workflows/sync-upstream.yml +127 -0
  4. package/.prettierrc.json +7 -0
  5. package/README.md +214 -0
  6. package/dist/core/api-error.d.ts +193 -0
  7. package/dist/core/api-error.d.ts.map +1 -0
  8. package/dist/core/api-error.js +263 -0
  9. package/dist/core/api-error.js.map +1 -0
  10. package/dist/core/auth-errors.d.ts +13 -0
  11. package/dist/core/auth-errors.d.ts.map +1 -0
  12. package/dist/core/auth-errors.js +14 -0
  13. package/dist/core/auth-errors.js.map +1 -0
  14. package/dist/core/config.d.ts +60 -0
  15. package/dist/core/config.d.ts.map +1 -0
  16. package/dist/core/config.js +115 -0
  17. package/dist/core/config.js.map +1 -0
  18. package/dist/core/device-flow.d.ts +80 -0
  19. package/dist/core/device-flow.d.ts.map +1 -0
  20. package/dist/core/device-flow.js +231 -0
  21. package/dist/core/device-flow.js.map +1 -0
  22. package/dist/core/index.d.ts +16 -0
  23. package/dist/core/index.d.ts.map +1 -0
  24. package/dist/core/index.js +16 -0
  25. package/dist/core/index.js.map +1 -0
  26. package/dist/core/lark-client.d.ts +136 -0
  27. package/dist/core/lark-client.d.ts.map +1 -0
  28. package/dist/core/lark-client.js +315 -0
  29. package/dist/core/lark-client.js.map +1 -0
  30. package/dist/core/token-store.d.ts +67 -0
  31. package/dist/core/token-store.d.ts.map +1 -0
  32. package/dist/core/token-store.js +215 -0
  33. package/dist/core/token-store.js.map +1 -0
  34. package/dist/core/types.d.ts +286 -0
  35. package/dist/core/types.d.ts.map +1 -0
  36. package/dist/core/types.js +11 -0
  37. package/dist/core/types.js.map +1 -0
  38. package/dist/core/uat-client.d.ts +64 -0
  39. package/dist/core/uat-client.d.ts.map +1 -0
  40. package/dist/core/uat-client.js +227 -0
  41. package/dist/core/uat-client.js.map +1 -0
  42. package/dist/core/version.d.ts +26 -0
  43. package/dist/core/version.d.ts.map +1 -0
  44. package/dist/core/version.js +50 -0
  45. package/dist/core/version.js.map +1 -0
  46. package/dist/index.d.ts +12 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +116 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/tools/bitable/app.d.ts +20 -0
  51. package/dist/tools/bitable/app.d.ts.map +1 -0
  52. package/dist/tools/bitable/app.js +301 -0
  53. package/dist/tools/bitable/app.js.map +1 -0
  54. package/dist/tools/bitable/field.d.ts +19 -0
  55. package/dist/tools/bitable/field.d.ts.map +1 -0
  56. package/dist/tools/bitable/field.js +315 -0
  57. package/dist/tools/bitable/field.js.map +1 -0
  58. package/dist/tools/bitable/index.d.ts +21 -0
  59. package/dist/tools/bitable/index.d.ts.map +1 -0
  60. package/dist/tools/bitable/index.js +39 -0
  61. package/dist/tools/bitable/index.js.map +1 -0
  62. package/dist/tools/bitable/record.d.ts +22 -0
  63. package/dist/tools/bitable/record.d.ts.map +1 -0
  64. package/dist/tools/bitable/record.js +434 -0
  65. package/dist/tools/bitable/record.js.map +1 -0
  66. package/dist/tools/bitable/table.d.ts +21 -0
  67. package/dist/tools/bitable/table.d.ts.map +1 -0
  68. package/dist/tools/bitable/table.js +361 -0
  69. package/dist/tools/bitable/table.js.map +1 -0
  70. package/dist/tools/calendar/calendar.d.ts +18 -0
  71. package/dist/tools/calendar/calendar.d.ts.map +1 -0
  72. package/dist/tools/calendar/calendar.js +192 -0
  73. package/dist/tools/calendar/calendar.js.map +1 -0
  74. package/dist/tools/calendar/event.d.ts +20 -0
  75. package/dist/tools/calendar/event.d.ts.map +1 -0
  76. package/dist/tools/calendar/event.js +465 -0
  77. package/dist/tools/calendar/event.js.map +1 -0
  78. package/dist/tools/calendar/index.d.ts +19 -0
  79. package/dist/tools/calendar/index.d.ts.map +1 -0
  80. package/dist/tools/calendar/index.js +37 -0
  81. package/dist/tools/calendar/index.js.map +1 -0
  82. package/dist/tools/chat/chat.d.ts +11 -0
  83. package/dist/tools/chat/chat.d.ts.map +1 -0
  84. package/dist/tools/chat/chat.js +106 -0
  85. package/dist/tools/chat/chat.js.map +1 -0
  86. package/dist/tools/chat/index.d.ts +11 -0
  87. package/dist/tools/chat/index.d.ts.map +1 -0
  88. package/dist/tools/chat/index.js +20 -0
  89. package/dist/tools/chat/index.js.map +1 -0
  90. package/dist/tools/chat/members.d.ts +9 -0
  91. package/dist/tools/chat/members.d.ts.map +1 -0
  92. package/dist/tools/chat/members.js +80 -0
  93. package/dist/tools/chat/members.js.map +1 -0
  94. package/dist/tools/common/get-user.d.ts +11 -0
  95. package/dist/tools/common/get-user.d.ts.map +1 -0
  96. package/dist/tools/common/get-user.js +112 -0
  97. package/dist/tools/common/get-user.js.map +1 -0
  98. package/dist/tools/common/index.d.ts +11 -0
  99. package/dist/tools/common/index.d.ts.map +1 -0
  100. package/dist/tools/common/index.js +20 -0
  101. package/dist/tools/common/index.js.map +1 -0
  102. package/dist/tools/common/search-user.d.ts +9 -0
  103. package/dist/tools/common/search-user.d.ts.map +1 -0
  104. package/dist/tools/common/search-user.js +88 -0
  105. package/dist/tools/common/search-user.js.map +1 -0
  106. package/dist/tools/doc/create.d.ts +17 -0
  107. package/dist/tools/doc/create.d.ts.map +1 -0
  108. package/dist/tools/doc/create.js +159 -0
  109. package/dist/tools/doc/create.js.map +1 -0
  110. package/dist/tools/doc/fetch.d.ts +17 -0
  111. package/dist/tools/doc/fetch.d.ts.map +1 -0
  112. package/dist/tools/doc/fetch.js +123 -0
  113. package/dist/tools/doc/fetch.js.map +1 -0
  114. package/dist/tools/doc/index.d.ts +21 -0
  115. package/dist/tools/doc/index.d.ts.map +1 -0
  116. package/dist/tools/doc/index.js +33 -0
  117. package/dist/tools/doc/index.js.map +1 -0
  118. package/dist/tools/doc/shared.d.ts +69 -0
  119. package/dist/tools/doc/shared.d.ts.map +1 -0
  120. package/dist/tools/doc/shared.js +172 -0
  121. package/dist/tools/doc/shared.js.map +1 -0
  122. package/dist/tools/doc/update.d.ts +25 -0
  123. package/dist/tools/doc/update.d.ts.map +1 -0
  124. package/dist/tools/doc/update.js +208 -0
  125. package/dist/tools/doc/update.js.map +1 -0
  126. package/dist/tools/drive/file.d.ts +13 -0
  127. package/dist/tools/drive/file.d.ts.map +1 -0
  128. package/dist/tools/drive/file.js +212 -0
  129. package/dist/tools/drive/file.js.map +1 -0
  130. package/dist/tools/drive/index.d.ts +12 -0
  131. package/dist/tools/drive/index.d.ts.map +1 -0
  132. package/dist/tools/drive/index.js +25 -0
  133. package/dist/tools/drive/index.js.map +1 -0
  134. package/dist/tools/im/format-messages.d.ts +99 -0
  135. package/dist/tools/im/format-messages.d.ts.map +1 -0
  136. package/dist/tools/im/format-messages.js +277 -0
  137. package/dist/tools/im/format-messages.js.map +1 -0
  138. package/dist/tools/im/helpers.d.ts +53 -0
  139. package/dist/tools/im/helpers.d.ts.map +1 -0
  140. package/dist/tools/im/helpers.js +85 -0
  141. package/dist/tools/im/helpers.js.map +1 -0
  142. package/dist/tools/im/index.d.ts +25 -0
  143. package/dist/tools/im/index.d.ts.map +1 -0
  144. package/dist/tools/im/index.js +44 -0
  145. package/dist/tools/im/index.js.map +1 -0
  146. package/dist/tools/im/message-read.d.ts +19 -0
  147. package/dist/tools/im/message-read.d.ts.map +1 -0
  148. package/dist/tools/im/message-read.js +526 -0
  149. package/dist/tools/im/message-read.js.map +1 -0
  150. package/dist/tools/im/message.d.ts +22 -0
  151. package/dist/tools/im/message.d.ts.map +1 -0
  152. package/dist/tools/im/message.js +233 -0
  153. package/dist/tools/im/message.js.map +1 -0
  154. package/dist/tools/im/resource.d.ts +19 -0
  155. package/dist/tools/im/resource.d.ts.map +1 -0
  156. package/dist/tools/im/resource.js +185 -0
  157. package/dist/tools/im/resource.js.map +1 -0
  158. package/dist/tools/im/time-utils.d.ts +70 -0
  159. package/dist/tools/im/time-utils.d.ts.map +1 -0
  160. package/dist/tools/im/time-utils.js +277 -0
  161. package/dist/tools/im/time-utils.js.map +1 -0
  162. package/dist/tools/index.d.ts +85 -0
  163. package/dist/tools/index.d.ts.map +1 -0
  164. package/dist/tools/index.js +135 -0
  165. package/dist/tools/index.js.map +1 -0
  166. package/dist/tools/oauth.d.ts +15 -0
  167. package/dist/tools/oauth.d.ts.map +1 -0
  168. package/dist/tools/oauth.js +379 -0
  169. package/dist/tools/oauth.js.map +1 -0
  170. package/dist/tools/search/doc-search.d.ts +9 -0
  171. package/dist/tools/search/doc-search.d.ts.map +1 -0
  172. package/dist/tools/search/doc-search.js +219 -0
  173. package/dist/tools/search/doc-search.js.map +1 -0
  174. package/dist/tools/search/index.d.ts +11 -0
  175. package/dist/tools/search/index.d.ts.map +1 -0
  176. package/dist/tools/search/index.js +18 -0
  177. package/dist/tools/search/index.js.map +1 -0
  178. package/dist/tools/sheets/index.d.ts +11 -0
  179. package/dist/tools/sheets/index.d.ts.map +1 -0
  180. package/dist/tools/sheets/index.js +18 -0
  181. package/dist/tools/sheets/index.js.map +1 -0
  182. package/dist/tools/sheets/sheet.d.ts +11 -0
  183. package/dist/tools/sheets/sheet.d.ts.map +1 -0
  184. package/dist/tools/sheets/sheet.js +332 -0
  185. package/dist/tools/sheets/sheet.js.map +1 -0
  186. package/dist/tools/task/index.d.ts +12 -0
  187. package/dist/tools/task/index.d.ts.map +1 -0
  188. package/dist/tools/task/index.js +30 -0
  189. package/dist/tools/task/index.js.map +1 -0
  190. package/dist/tools/task/task.d.ts +13 -0
  191. package/dist/tools/task/task.d.ts.map +1 -0
  192. package/dist/tools/task/task.js +225 -0
  193. package/dist/tools/task/task.js.map +1 -0
  194. package/dist/tools/task/tasklist.d.ts +13 -0
  195. package/dist/tools/task/tasklist.d.ts.map +1 -0
  196. package/dist/tools/task/tasklist.js +206 -0
  197. package/dist/tools/task/tasklist.js.map +1 -0
  198. package/dist/tools/wiki/index.d.ts +11 -0
  199. package/dist/tools/wiki/index.d.ts.map +1 -0
  200. package/dist/tools/wiki/index.js +20 -0
  201. package/dist/tools/wiki/index.js.map +1 -0
  202. package/dist/tools/wiki/node.d.ts +11 -0
  203. package/dist/tools/wiki/node.d.ts.map +1 -0
  204. package/dist/tools/wiki/node.js +112 -0
  205. package/dist/tools/wiki/node.js.map +1 -0
  206. package/dist/tools/wiki/space.d.ts +11 -0
  207. package/dist/tools/wiki/space.d.ts.map +1 -0
  208. package/dist/tools/wiki/space.js +125 -0
  209. package/dist/tools/wiki/space.js.map +1 -0
  210. package/dist/utils/index.d.ts +8 -0
  211. package/dist/utils/index.d.ts.map +1 -0
  212. package/dist/utils/index.js +8 -0
  213. package/dist/utils/index.js.map +1 -0
  214. package/dist/utils/logger.d.ts +36 -0
  215. package/dist/utils/logger.d.ts.map +1 -0
  216. package/dist/utils/logger.js +101 -0
  217. package/dist/utils/logger.js.map +1 -0
  218. package/eslint.config.js +13 -0
  219. package/package.json +54 -0
  220. package/skills/feishu-bitable/SKILL.md +248 -0
  221. package/skills/feishu-bitable/references/examples.md +813 -0
  222. package/skills/feishu-bitable/references/field-properties.md +763 -0
  223. package/skills/feishu-bitable/references/record-values.md +911 -0
  224. package/skills/feishu-calendar/SKILL.md +244 -0
  225. package/skills/feishu-channel-rules/SKILL.md +18 -0
  226. package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  227. package/skills/feishu-create-doc/SKILL.md +719 -0
  228. package/skills/feishu-fetch-doc/SKILL.md +93 -0
  229. package/skills/feishu-im-read/SKILL.md +163 -0
  230. package/skills/feishu-task/SKILL.md +293 -0
  231. package/skills/feishu-troubleshoot/SKILL.md +70 -0
  232. package/skills/feishu-update-doc/SKILL.md +285 -0
  233. package/src/core/api-error.ts +342 -0
  234. package/src/core/auth-errors.ts +27 -0
  235. package/src/core/config.ts +134 -0
  236. package/src/core/device-flow.ts +314 -0
  237. package/src/core/index.ts +16 -0
  238. package/src/core/lark-client.ts +391 -0
  239. package/src/core/token-store.ts +249 -0
  240. package/src/core/types.ts +302 -0
  241. package/src/core/uat-client.ts +298 -0
  242. package/src/core/version.ts +53 -0
  243. package/src/index.ts +138 -0
  244. package/src/tools/bitable/app.ts +390 -0
  245. package/src/tools/bitable/field.ts +406 -0
  246. package/src/tools/bitable/index.ts +43 -0
  247. package/src/tools/bitable/record.ts +559 -0
  248. package/src/tools/bitable/table.ts +472 -0
  249. package/src/tools/calendar/calendar.ts +254 -0
  250. package/src/tools/calendar/event.ts +606 -0
  251. package/src/tools/calendar/index.ts +41 -0
  252. package/src/tools/chat/chat.ts +127 -0
  253. package/src/tools/chat/index.ts +24 -0
  254. package/src/tools/chat/members.ts +93 -0
  255. package/src/tools/common/get-user.ts +127 -0
  256. package/src/tools/common/index.ts +24 -0
  257. package/src/tools/common/search-user.ts +99 -0
  258. package/src/tools/doc/create.ts +184 -0
  259. package/src/tools/doc/fetch.ts +149 -0
  260. package/src/tools/doc/index.ts +38 -0
  261. package/src/tools/doc/shared.ts +228 -0
  262. package/src/tools/doc/update.ts +240 -0
  263. package/src/tools/drive/file.ts +265 -0
  264. package/src/tools/drive/index.ts +29 -0
  265. package/src/tools/im/format-messages.ts +391 -0
  266. package/src/tools/im/helpers.ts +109 -0
  267. package/src/tools/im/index.ts +49 -0
  268. package/src/tools/im/message-read.ts +676 -0
  269. package/src/tools/im/message.ts +303 -0
  270. package/src/tools/im/resource.ts +225 -0
  271. package/src/tools/im/time-utils.ts +347 -0
  272. package/src/tools/index.ts +205 -0
  273. package/src/tools/oauth.ts +460 -0
  274. package/src/tools/search/doc-search.ts +250 -0
  275. package/src/tools/search/index.ts +22 -0
  276. package/src/tools/sheets/index.ts +22 -0
  277. package/src/tools/sheets/sheet.ts +382 -0
  278. package/src/tools/task/index.ts +34 -0
  279. package/src/tools/task/task.ts +265 -0
  280. package/src/tools/task/tasklist.ts +262 -0
  281. package/src/tools/wiki/index.ts +24 -0
  282. package/src/tools/wiki/node.ts +131 -0
  283. package/src/tools/wiki/space.ts +152 -0
  284. package/src/utils/index.ts +8 -0
  285. package/src/utils/logger.ts +132 -0
  286. package/tests/core/config.test.ts +238 -0
  287. package/tests/core/device-flow.test.ts +490 -0
  288. package/tests/core/lark-client.test.ts +378 -0
  289. package/tests/core/token-store.test.ts +438 -0
  290. package/tests/index.test.ts +360 -0
  291. package/tests/tools/doc/create.test.ts +224 -0
  292. package/tests/tools/doc/fetch.test.ts +182 -0
  293. package/tests/tools/doc/shared.test.ts +183 -0
  294. package/tests/tools/doc/update.test.ts +330 -0
  295. package/tests/tools/im/format-messages.test.ts +184 -0
  296. package/tests/tools/im/time-utils.test.ts +178 -0
  297. package/tests/utils/logger.test.ts +140 -0
  298. 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
+ }