feishu-user-plugin 1.3.0 → 1.3.2
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/.claude-plugin/plugin.json +2 -2
- package/README.md +103 -39
- package/package.json +5 -3
- package/proto/lark.proto +27 -0
- package/scripts/confirm-version.js +28 -0
- package/scripts/mcp_stdio_bridge.js +97 -0
- package/skills/feishu-user-plugin/references/CLAUDE.md +105 -41
- package/src/cli.js +12 -7
- package/src/client.js +81 -10
- package/src/config.js +202 -27
- package/src/index.js +128 -246
- package/src/oauth.js +2 -1
- package/src/official.js +274 -209
- package/src/setup.js +19 -3
package/src/index.js
CHANGED
|
@@ -144,12 +144,17 @@ const TOOLS = [
|
|
|
144
144
|
// ========== User Identity — Send Messages ==========
|
|
145
145
|
{
|
|
146
146
|
name: 'send_as_user',
|
|
147
|
-
description: '[User Identity] Send a text message as the logged-in Feishu user. Supports reply threading.',
|
|
147
|
+
description: '[User Identity] Send a text message as the logged-in Feishu user. Supports reply threading and real @-mentions (triggers push notifications).',
|
|
148
148
|
inputSchema: {
|
|
149
149
|
type: 'object',
|
|
150
150
|
properties: {
|
|
151
151
|
chat_id: { type: 'string', description: 'Target chat ID (numeric)' },
|
|
152
|
-
text: { type: 'string', description: 'Message text' },
|
|
152
|
+
text: { type: 'string', description: 'Message text. If `ats` is provided, include the display marker for each @ in this text (default marker is `@<name>`).' },
|
|
153
|
+
ats: {
|
|
154
|
+
type: 'array',
|
|
155
|
+
description: 'Optional @-mentions. Each entry: {userId: "ou_xxx", name: "DisplayName"}. The text must contain each @<name> marker in order — it gets spliced into a real AT element so the mentioned user receives a notification.',
|
|
156
|
+
items: { type: 'object', properties: { userId: { type: 'string' }, name: { type: 'string' }, marker: { type: 'string' } } },
|
|
157
|
+
},
|
|
153
158
|
root_id: { type: 'string', description: 'Thread root message ID (for reply, optional)' },
|
|
154
159
|
parent_id: { type: 'string', description: 'Parent message ID (for nested reply, optional)' },
|
|
155
160
|
},
|
|
@@ -164,6 +169,11 @@ const TOOLS = [
|
|
|
164
169
|
properties: {
|
|
165
170
|
user_name: { type: 'string', description: 'Recipient name (Chinese or English)' },
|
|
166
171
|
text: { type: 'string', description: 'Message text' },
|
|
172
|
+
ats: {
|
|
173
|
+
type: 'array',
|
|
174
|
+
description: 'Optional @-mentions. Same format as send_as_user.ats: [{userId, name}]. Text must contain the `@<name>` marker for each entry.',
|
|
175
|
+
items: { type: 'object', properties: { userId: { type: 'string' }, name: { type: 'string' }, marker: { type: 'string' } } },
|
|
176
|
+
},
|
|
167
177
|
},
|
|
168
178
|
required: ['user_name', 'text'],
|
|
169
179
|
},
|
|
@@ -176,6 +186,11 @@ const TOOLS = [
|
|
|
176
186
|
properties: {
|
|
177
187
|
group_name: { type: 'string', description: 'Group chat name' },
|
|
178
188
|
text: { type: 'string', description: 'Message text' },
|
|
189
|
+
ats: {
|
|
190
|
+
type: 'array',
|
|
191
|
+
description: 'Optional @-mentions that trigger real notifications. Each entry: {userId, name}. Text must contain `@<name>` marker for each entry.',
|
|
192
|
+
items: { type: 'object', properties: { userId: { type: 'string' }, name: { type: 'string' }, marker: { type: 'string' } } },
|
|
193
|
+
},
|
|
179
194
|
},
|
|
180
195
|
required: ['group_name', 'text'],
|
|
181
196
|
},
|
|
@@ -222,7 +237,7 @@ const TOOLS = [
|
|
|
222
237
|
},
|
|
223
238
|
{
|
|
224
239
|
name: 'send_post_as_user',
|
|
225
|
-
description: '[User Identity] Send a rich text (POST) message with title and formatted paragraphs.',
|
|
240
|
+
description: '[User Identity] Send a rich text (POST) message with title and formatted paragraphs. Supports real @-mentions that trigger notifications.',
|
|
226
241
|
inputSchema: {
|
|
227
242
|
type: 'object',
|
|
228
243
|
properties: {
|
|
@@ -230,7 +245,7 @@ const TOOLS = [
|
|
|
230
245
|
title: { type: 'string', description: 'Post title (optional)' },
|
|
231
246
|
paragraphs: {
|
|
232
247
|
type: 'array',
|
|
233
|
-
description: 'Array of paragraphs. Each paragraph is an array of elements
|
|
248
|
+
description: 'Array of paragraphs. Each paragraph is an array of elements:\n• {tag:"text",text:"..."} — plain text\n• {tag:"a",href:"https://...",text:"display"} — hyperlink\n• {tag:"at",userId:"ou_xxx",name:"Display Name"} — real @-mention (triggers notification)',
|
|
234
249
|
items: { type: 'array', items: { type: 'object' } },
|
|
235
250
|
},
|
|
236
251
|
root_id: { type: 'string', description: 'Thread root message ID (optional)' },
|
|
@@ -541,49 +556,9 @@ const TOOLS = [
|
|
|
541
556
|
required: ['app_token', 'table_id'],
|
|
542
557
|
},
|
|
543
558
|
},
|
|
544
|
-
{
|
|
545
|
-
name: 'create_bitable_record',
|
|
546
|
-
description: '[Official API] Create a new record (row) in a Bitable table.',
|
|
547
|
-
inputSchema: {
|
|
548
|
-
type: 'object',
|
|
549
|
-
properties: {
|
|
550
|
-
app_token: { type: 'string', description: 'Bitable app token' },
|
|
551
|
-
table_id: { type: 'string', description: 'Table ID' },
|
|
552
|
-
fields: { type: 'object', description: 'Field name → value mapping' },
|
|
553
|
-
},
|
|
554
|
-
required: ['app_token', 'table_id', 'fields'],
|
|
555
|
-
},
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
name: 'update_bitable_record',
|
|
559
|
-
description: '[Official API] Update an existing record in a Bitable table.',
|
|
560
|
-
inputSchema: {
|
|
561
|
-
type: 'object',
|
|
562
|
-
properties: {
|
|
563
|
-
app_token: { type: 'string', description: 'Bitable app token' },
|
|
564
|
-
table_id: { type: 'string', description: 'Table ID' },
|
|
565
|
-
record_id: { type: 'string', description: 'Record ID to update' },
|
|
566
|
-
fields: { type: 'object', description: 'Field name → new value mapping' },
|
|
567
|
-
},
|
|
568
|
-
required: ['app_token', 'table_id', 'record_id', 'fields'],
|
|
569
|
-
},
|
|
570
|
-
},
|
|
571
|
-
{
|
|
572
|
-
name: 'delete_bitable_record',
|
|
573
|
-
description: '[Official API] Delete a record (row) from a Bitable table.',
|
|
574
|
-
inputSchema: {
|
|
575
|
-
type: 'object',
|
|
576
|
-
properties: {
|
|
577
|
-
app_token: { type: 'string', description: 'Bitable app token' },
|
|
578
|
-
table_id: { type: 'string', description: 'Table ID' },
|
|
579
|
-
record_id: { type: 'string', description: 'Record ID to delete' },
|
|
580
|
-
},
|
|
581
|
-
required: ['app_token', 'table_id', 'record_id'],
|
|
582
|
-
},
|
|
583
|
-
},
|
|
584
559
|
{
|
|
585
560
|
name: 'batch_create_bitable_records',
|
|
586
|
-
description: '[Official API]
|
|
561
|
+
description: '[Official API] Create one or more records (rows) in a Bitable table. Pass a single record or up to 500.',
|
|
587
562
|
inputSchema: {
|
|
588
563
|
type: 'object',
|
|
589
564
|
properties: {
|
|
@@ -596,7 +571,7 @@ const TOOLS = [
|
|
|
596
571
|
},
|
|
597
572
|
{
|
|
598
573
|
name: 'batch_update_bitable_records',
|
|
599
|
-
description: '[Official API]
|
|
574
|
+
description: '[Official API] Update one or more records in a Bitable table. Pass a single record or up to 500.',
|
|
600
575
|
inputSchema: {
|
|
601
576
|
type: 'object',
|
|
602
577
|
properties: {
|
|
@@ -609,7 +584,7 @@ const TOOLS = [
|
|
|
609
584
|
},
|
|
610
585
|
{
|
|
611
586
|
name: 'batch_delete_bitable_records',
|
|
612
|
-
description: '[Official API]
|
|
587
|
+
description: '[Official API] Delete one or more records from a Bitable table. Pass a single ID or up to 500.',
|
|
613
588
|
inputSchema: {
|
|
614
589
|
type: 'object',
|
|
615
590
|
properties: {
|
|
@@ -714,13 +689,13 @@ const TOOLS = [
|
|
|
714
689
|
// ========== IM — Bot Send / Edit / Delete ==========
|
|
715
690
|
{
|
|
716
691
|
name: 'send_message_as_bot',
|
|
717
|
-
description: '[Official API] Send a message as the bot to any chat. Supports text, post, interactive, etc.',
|
|
692
|
+
description: '[Official API] Send a message as the bot to any chat. Supports text, post, interactive, etc. This is the reliable path for @-mentions: include `<at user_id="ou_xxx">Name</at>` inline in text content and Feishu resolves it to a real @-notification.',
|
|
718
693
|
inputSchema: {
|
|
719
694
|
type: 'object',
|
|
720
695
|
properties: {
|
|
721
696
|
chat_id: { type: 'string', description: 'Target chat_id (oc_xxx) or open_id' },
|
|
722
697
|
msg_type: { type: 'string', description: 'Message type: text, post, image, interactive, etc.', enum: ['text', 'post', 'image', 'interactive', 'share_chat', 'share_user', 'audio', 'media', 'file', 'sticker'] },
|
|
723
|
-
content: { description: 'Message content (string or object, auto-serialized).
|
|
698
|
+
content: { description: 'Message content (string or object, auto-serialized). Plain text: {"text":"hello"}. Text with @-mention: {"text":"<at user_id=\\"ou_xxx\\">Alice</at> hi"} — the inline tag becomes a real @-notification.' },
|
|
724
699
|
},
|
|
725
700
|
required: ['chat_id', 'msg_type', 'content'],
|
|
726
701
|
},
|
|
@@ -777,19 +752,13 @@ const TOOLS = [
|
|
|
777
752
|
// ========== IM — Pin Messages ==========
|
|
778
753
|
{
|
|
779
754
|
name: 'pin_message',
|
|
780
|
-
description: '[Official API] Pin a message in a chat.',
|
|
755
|
+
description: '[Official API] Pin or unpin a message in a chat.',
|
|
781
756
|
inputSchema: {
|
|
782
757
|
type: 'object',
|
|
783
|
-
properties: {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
{
|
|
788
|
-
name: 'unpin_message',
|
|
789
|
-
description: '[Official API] Unpin a message from a chat.',
|
|
790
|
-
inputSchema: {
|
|
791
|
-
type: 'object',
|
|
792
|
-
properties: { message_id: { type: 'string', description: 'Message ID to unpin' } },
|
|
758
|
+
properties: {
|
|
759
|
+
message_id: { type: 'string', description: 'Message ID' },
|
|
760
|
+
pinned: { type: 'boolean', description: 'true to pin, false to unpin', default: true },
|
|
761
|
+
},
|
|
793
762
|
required: ['message_id'],
|
|
794
763
|
},
|
|
795
764
|
},
|
|
@@ -835,27 +804,16 @@ const TOOLS = [
|
|
|
835
804
|
},
|
|
836
805
|
},
|
|
837
806
|
{
|
|
838
|
-
name: '
|
|
839
|
-
description: '[Official API] Add
|
|
807
|
+
name: 'manage_members',
|
|
808
|
+
description: '[Official API] Add or remove members from a group chat.',
|
|
840
809
|
inputSchema: {
|
|
841
810
|
type: 'object',
|
|
842
811
|
properties: {
|
|
843
|
-
chat_id: { type: 'string', description: '
|
|
844
|
-
|
|
812
|
+
chat_id: { type: 'string', description: 'Group chat ID (oc_xxx)' },
|
|
813
|
+
member_ids: { type: 'array', items: { type: 'string' }, description: 'Array of user open_ids' },
|
|
814
|
+
action: { type: 'string', enum: ['add', 'remove'], description: 'Action to perform' },
|
|
845
815
|
},
|
|
846
|
-
required: ['chat_id', '
|
|
847
|
-
},
|
|
848
|
-
},
|
|
849
|
-
{
|
|
850
|
-
name: 'remove_members',
|
|
851
|
-
description: '[Official API] Remove users from a group chat.',
|
|
852
|
-
inputSchema: {
|
|
853
|
-
type: 'object',
|
|
854
|
-
properties: {
|
|
855
|
-
chat_id: { type: 'string', description: 'Chat ID (oc_xxx)' },
|
|
856
|
-
user_ids: { type: 'array', items: { type: 'string' }, description: 'Array of user open_ids to remove' },
|
|
857
|
-
},
|
|
858
|
-
required: ['chat_id', 'user_ids'],
|
|
816
|
+
required: ['chat_id', 'member_ids', 'action'],
|
|
859
817
|
},
|
|
860
818
|
},
|
|
861
819
|
|
|
@@ -929,165 +887,111 @@ const TOOLS = [
|
|
|
929
887
|
},
|
|
930
888
|
},
|
|
931
889
|
|
|
932
|
-
// ========== Drive — File Operations ==========
|
|
933
890
|
{
|
|
934
|
-
name: '
|
|
935
|
-
description: '[Official API]
|
|
891
|
+
name: 'get_bitable_meta',
|
|
892
|
+
description: '[Official API] Get metadata of a Bitable app (name, revision, etc.).',
|
|
936
893
|
inputSchema: {
|
|
937
894
|
type: 'object',
|
|
938
895
|
properties: {
|
|
939
|
-
|
|
940
|
-
name: { type: 'string', description: 'New file name' },
|
|
941
|
-
folder_token: { type: 'string', description: 'Destination folder token (optional)' },
|
|
942
|
-
type: { type: 'string', description: 'File type: file, doc, sheet, bitable, docx, mindnote, slides (optional)' },
|
|
943
|
-
},
|
|
944
|
-
required: ['file_token', 'name'],
|
|
945
|
-
},
|
|
946
|
-
},
|
|
947
|
-
{
|
|
948
|
-
name: 'move_file',
|
|
949
|
-
description: '[Official API] Move a file to another folder in Drive.',
|
|
950
|
-
inputSchema: {
|
|
951
|
-
type: 'object',
|
|
952
|
-
properties: {
|
|
953
|
-
file_token: { type: 'string', description: 'File token to move' },
|
|
954
|
-
folder_token: { type: 'string', description: 'Destination folder token' },
|
|
955
|
-
},
|
|
956
|
-
required: ['file_token', 'folder_token'],
|
|
957
|
-
},
|
|
958
|
-
},
|
|
959
|
-
{
|
|
960
|
-
name: 'delete_file',
|
|
961
|
-
description: '[Official API] Delete a file/folder from Drive.',
|
|
962
|
-
inputSchema: {
|
|
963
|
-
type: 'object',
|
|
964
|
-
properties: {
|
|
965
|
-
file_token: { type: 'string', description: 'File token to delete' },
|
|
966
|
-
type: { type: 'string', description: 'Type: file, folder, doc, sheet, bitable, docx, mindnote, slides' },
|
|
896
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
967
897
|
},
|
|
968
|
-
required: ['
|
|
898
|
+
required: ['app_token'],
|
|
969
899
|
},
|
|
970
900
|
},
|
|
971
|
-
|
|
972
|
-
// ========== Calendar ==========
|
|
973
901
|
{
|
|
974
|
-
name: '
|
|
975
|
-
description: '[Official API]
|
|
976
|
-
inputSchema: { type: 'object', properties: {} },
|
|
977
|
-
},
|
|
978
|
-
{
|
|
979
|
-
name: 'create_calendar_event',
|
|
980
|
-
description: '[Official API] Create a calendar event.',
|
|
902
|
+
name: 'update_bitable_table',
|
|
903
|
+
description: '[Official API] Rename a data table in a Bitable app.',
|
|
981
904
|
inputSchema: {
|
|
982
905
|
type: 'object',
|
|
983
906
|
properties: {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
end_time: { type: 'string', description: 'End time (RFC3339 or Unix timestamp string)' },
|
|
988
|
-
description: { type: 'string', description: 'Event description (optional)' },
|
|
989
|
-
location: { type: 'string', description: 'Event location (optional)' },
|
|
907
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
908
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
909
|
+
name: { type: 'string', description: 'New table name' },
|
|
990
910
|
},
|
|
991
|
-
required: ['
|
|
911
|
+
required: ['app_token', 'table_id', 'name'],
|
|
992
912
|
},
|
|
993
913
|
},
|
|
994
914
|
{
|
|
995
|
-
name: '
|
|
996
|
-
description: '[Official API]
|
|
915
|
+
name: 'create_bitable_view',
|
|
916
|
+
description: '[Official API] Create a new view in a Bitable table.',
|
|
997
917
|
inputSchema: {
|
|
998
918
|
type: 'object',
|
|
999
919
|
properties: {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
920
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
921
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
922
|
+
view_name: { type: 'string', description: 'View name' },
|
|
923
|
+
view_type: { type: 'string', description: 'View type: grid (default), kanban, gallery, form, gantt, calendar', default: 'grid' },
|
|
1004
924
|
},
|
|
1005
|
-
required: ['
|
|
925
|
+
required: ['app_token', 'table_id', 'view_name'],
|
|
1006
926
|
},
|
|
1007
927
|
},
|
|
1008
928
|
{
|
|
1009
|
-
name: '
|
|
1010
|
-
description: '[Official API] Delete a
|
|
929
|
+
name: 'delete_bitable_view',
|
|
930
|
+
description: '[Official API] Delete a view from a Bitable table.',
|
|
1011
931
|
inputSchema: {
|
|
1012
932
|
type: 'object',
|
|
1013
933
|
properties: {
|
|
1014
|
-
|
|
1015
|
-
|
|
934
|
+
app_token: { type: 'string', description: 'Bitable app token' },
|
|
935
|
+
table_id: { type: 'string', description: 'Table ID' },
|
|
936
|
+
view_id: { type: 'string', description: 'View ID to delete' },
|
|
1016
937
|
},
|
|
1017
|
-
required: ['
|
|
938
|
+
required: ['app_token', 'table_id', 'view_id'],
|
|
1018
939
|
},
|
|
1019
940
|
},
|
|
1020
941
|
{
|
|
1021
|
-
name: '
|
|
1022
|
-
description: '[Official API]
|
|
942
|
+
name: 'copy_bitable',
|
|
943
|
+
description: '[Official API] Copy a Bitable app to create a new one.',
|
|
1023
944
|
inputSchema: {
|
|
1024
945
|
type: 'object',
|
|
1025
946
|
properties: {
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
947
|
+
app_token: { type: 'string', description: 'Bitable app token to copy' },
|
|
948
|
+
name: { type: 'string', description: 'New Bitable name' },
|
|
949
|
+
folder_id: { type: 'string', description: 'Destination folder token (optional)' },
|
|
1029
950
|
},
|
|
1030
|
-
required: ['
|
|
951
|
+
required: ['app_token', 'name'],
|
|
1031
952
|
},
|
|
1032
953
|
},
|
|
1033
954
|
|
|
1034
|
-
// ==========
|
|
955
|
+
// ========== Drive — File Operations ==========
|
|
1035
956
|
{
|
|
1036
|
-
name: '
|
|
1037
|
-
description: '[Official API]
|
|
957
|
+
name: 'copy_file',
|
|
958
|
+
description: '[Official API] Copy a file/doc in Drive.',
|
|
1038
959
|
inputSchema: {
|
|
1039
960
|
type: 'object',
|
|
1040
961
|
properties: {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
962
|
+
file_token: { type: 'string', description: 'File token to copy' },
|
|
963
|
+
name: { type: 'string', description: 'New file name' },
|
|
964
|
+
folder_token: { type: 'string', description: 'Destination folder token (optional)' },
|
|
965
|
+
type: { type: 'string', description: 'File type: file, doc, sheet, bitable, docx, mindnote, slides (optional)' },
|
|
1044
966
|
},
|
|
1045
|
-
required: ['
|
|
1046
|
-
},
|
|
1047
|
-
},
|
|
1048
|
-
{
|
|
1049
|
-
name: 'get_task',
|
|
1050
|
-
description: '[Official API] Get task details by ID.',
|
|
1051
|
-
inputSchema: {
|
|
1052
|
-
type: 'object',
|
|
1053
|
-
properties: { task_id: { type: 'string', description: 'Task ID' } },
|
|
1054
|
-
required: ['task_id'],
|
|
967
|
+
required: ['file_token', 'name'],
|
|
1055
968
|
},
|
|
1056
969
|
},
|
|
1057
970
|
{
|
|
1058
|
-
name: '
|
|
1059
|
-
description: '[Official API]
|
|
971
|
+
name: 'move_file',
|
|
972
|
+
description: '[Official API] Move a file to another folder in Drive.',
|
|
1060
973
|
inputSchema: {
|
|
1061
974
|
type: 'object',
|
|
1062
975
|
properties: {
|
|
1063
|
-
|
|
1064
|
-
|
|
976
|
+
file_token: { type: 'string', description: 'File token to move' },
|
|
977
|
+
folder_token: { type: 'string', description: 'Destination folder token' },
|
|
1065
978
|
},
|
|
979
|
+
required: ['file_token', 'folder_token'],
|
|
1066
980
|
},
|
|
1067
981
|
},
|
|
1068
982
|
{
|
|
1069
|
-
name: '
|
|
1070
|
-
description: '[Official API]
|
|
983
|
+
name: 'delete_file',
|
|
984
|
+
description: '[Official API] Delete a file/folder from Drive.',
|
|
1071
985
|
inputSchema: {
|
|
1072
986
|
type: 'object',
|
|
1073
987
|
properties: {
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
description: { type: 'string', description: 'New description (optional)' },
|
|
1077
|
-
due: { type: 'string', description: 'New due date (optional)' },
|
|
988
|
+
file_token: { type: 'string', description: 'File token to delete' },
|
|
989
|
+
type: { type: 'string', description: 'Type: file, folder, doc, sheet, bitable, docx, mindnote, slides' },
|
|
1078
990
|
},
|
|
1079
|
-
required: ['
|
|
1080
|
-
},
|
|
1081
|
-
},
|
|
1082
|
-
{
|
|
1083
|
-
name: 'complete_task',
|
|
1084
|
-
description: '[Official API] Mark a task as complete.',
|
|
1085
|
-
inputSchema: {
|
|
1086
|
-
type: 'object',
|
|
1087
|
-
properties: { task_id: { type: 'string', description: 'Task ID to complete' } },
|
|
1088
|
-
required: ['task_id'],
|
|
991
|
+
required: ['file_token'],
|
|
1089
992
|
},
|
|
1090
993
|
},
|
|
994
|
+
|
|
1091
995
|
];
|
|
1092
996
|
|
|
1093
997
|
// --- Server ---
|
|
@@ -1119,7 +1023,7 @@ async function handleTool(name, args) {
|
|
|
1119
1023
|
|
|
1120
1024
|
case 'send_as_user': {
|
|
1121
1025
|
const c = await getUserClient();
|
|
1122
|
-
const r = await c.sendMessage(args.chat_id, args.text, { rootId: args.root_id, parentId: args.parent_id });
|
|
1026
|
+
const r = await c.sendMessage(args.chat_id, args.text, { rootId: args.root_id, parentId: args.parent_id, ats: args.ats });
|
|
1123
1027
|
return sendResult(r, `Text sent as user to ${args.chat_id}`);
|
|
1124
1028
|
}
|
|
1125
1029
|
case 'send_to_user': {
|
|
@@ -1134,7 +1038,7 @@ async function handleTool(name, args) {
|
|
|
1134
1038
|
const user = users[0];
|
|
1135
1039
|
const chatId = await c.createChat(user.id);
|
|
1136
1040
|
if (!chatId) return text(`Failed to create chat with ${user.title}`);
|
|
1137
|
-
const r = await c.sendMessage(chatId, args.text);
|
|
1041
|
+
const r = await c.sendMessage(chatId, args.text, { ats: args.ats });
|
|
1138
1042
|
return sendResult(r, `Text sent to ${user.title} (chat: ${chatId})`);
|
|
1139
1043
|
}
|
|
1140
1044
|
case 'send_to_group': {
|
|
@@ -1147,7 +1051,7 @@ async function handleTool(name, args) {
|
|
|
1147
1051
|
return text(`Multiple groups match "${args.group_name}":\n${candidates}\nUse search_contacts to find the exact group, then send_as_user with the ID.`);
|
|
1148
1052
|
}
|
|
1149
1053
|
const group = groups[0];
|
|
1150
|
-
const r = await c.sendMessage(group.id, args.text);
|
|
1054
|
+
const r = await c.sendMessage(group.id, args.text, { ats: args.ats });
|
|
1151
1055
|
return sendResult(r, `Text sent to group "${group.title}" (${group.id})`);
|
|
1152
1056
|
}
|
|
1153
1057
|
|
|
@@ -1331,14 +1235,19 @@ async function handleTool(name, args) {
|
|
|
1331
1235
|
return json(await getOfficialClient().readDoc(args.document_id));
|
|
1332
1236
|
case 'get_doc_blocks':
|
|
1333
1237
|
return json(await getOfficialClient().getDocBlocks(args.document_id));
|
|
1334
|
-
case 'create_doc':
|
|
1335
|
-
|
|
1238
|
+
case 'create_doc': {
|
|
1239
|
+
const official = getOfficialClient();
|
|
1240
|
+
const ownership = official.hasUAT ? ' (as user)' : '';
|
|
1241
|
+
return text(`Document created${ownership}: ${(await official.createDoc(args.title, args.folder_id)).documentId}`);
|
|
1242
|
+
}
|
|
1336
1243
|
|
|
1337
1244
|
// --- Official API: Bitable ---
|
|
1338
1245
|
|
|
1339
1246
|
case 'create_bitable': {
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1247
|
+
const official = getOfficialClient();
|
|
1248
|
+
const ownership = official.hasUAT ? ' (as user)' : '';
|
|
1249
|
+
const r = await official.createBitable(args.name, args.folder_id);
|
|
1250
|
+
return text(`Bitable created${ownership}: ${r.appToken}\nURL: ${r.url || ''}`);
|
|
1342
1251
|
}
|
|
1343
1252
|
case 'list_bitable_tables':
|
|
1344
1253
|
return json(await getOfficialClient().listBitableTables(args.app_token));
|
|
@@ -1368,12 +1277,6 @@ async function handleTool(name, args) {
|
|
|
1368
1277
|
return json(await getOfficialClient().searchBitableRecords(args.app_token, args.table_id, {
|
|
1369
1278
|
filter: args.filter, sort: args.sort, pageSize: args.page_size,
|
|
1370
1279
|
}));
|
|
1371
|
-
case 'create_bitable_record':
|
|
1372
|
-
return text(`Record created: ${(await getOfficialClient().createBitableRecord(args.app_token, args.table_id, args.fields)).recordId}`);
|
|
1373
|
-
case 'update_bitable_record':
|
|
1374
|
-
return text(`Record updated: ${(await getOfficialClient().updateBitableRecord(args.app_token, args.table_id, args.record_id, args.fields)).recordId}`);
|
|
1375
|
-
case 'delete_bitable_record':
|
|
1376
|
-
return text(`Record deleted: ${(await getOfficialClient().deleteBitableRecord(args.app_token, args.table_id, args.record_id)).deleted}`);
|
|
1377
1280
|
case 'batch_create_bitable_records':
|
|
1378
1281
|
return json(await getOfficialClient().batchCreateBitableRecords(args.app_token, args.table_id, args.records));
|
|
1379
1282
|
case 'batch_update_bitable_records':
|
|
@@ -1394,8 +1297,11 @@ async function handleTool(name, args) {
|
|
|
1394
1297
|
|
|
1395
1298
|
case 'list_files':
|
|
1396
1299
|
return json(await getOfficialClient().listFiles(args.folder_token));
|
|
1397
|
-
case 'create_folder':
|
|
1398
|
-
|
|
1300
|
+
case 'create_folder': {
|
|
1301
|
+
const official = getOfficialClient();
|
|
1302
|
+
const ownership = official.hasUAT ? ' (as user)' : '';
|
|
1303
|
+
return text(`Folder created${ownership}: ${(await official.createFolder(args.name, args.parent_token)).token}`);
|
|
1304
|
+
}
|
|
1399
1305
|
|
|
1400
1306
|
// --- Official API: Contact ---
|
|
1401
1307
|
|
|
@@ -1434,9 +1340,7 @@ async function handleTool(name, args) {
|
|
|
1434
1340
|
// --- Official API: Pins ---
|
|
1435
1341
|
|
|
1436
1342
|
case 'pin_message':
|
|
1437
|
-
return json(await getOfficialClient().pinMessage(args.message_id));
|
|
1438
|
-
case 'unpin_message':
|
|
1439
|
-
return text(`Unpinned: ${(await getOfficialClient().unpinMessage(args.message_id)).deleted}`);
|
|
1343
|
+
return json(await getOfficialClient().pinMessage(args.message_id, args.pinned !== false));
|
|
1440
1344
|
|
|
1441
1345
|
// --- Official API: Chat Management ---
|
|
1442
1346
|
|
|
@@ -1446,13 +1350,12 @@ async function handleTool(name, args) {
|
|
|
1446
1350
|
return text(`Group updated: ${(await getOfficialClient().updateChat(args.chat_id, { name: args.name, description: args.description })).updated}`);
|
|
1447
1351
|
case 'list_members':
|
|
1448
1352
|
return json(await getOfficialClient().listChatMembers(args.chat_id, { pageSize: args.page_size, pageToken: args.page_token }));
|
|
1449
|
-
case '
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
return text(r.invalidIds.length ? `Removed (invalid IDs: ${r.invalidIds.join(', ')})` : 'Members removed');
|
|
1353
|
+
case 'manage_members': {
|
|
1354
|
+
const official = getOfficialClient();
|
|
1355
|
+
if (args.action === 'remove') {
|
|
1356
|
+
return json(await official.removeChatMembers(args.chat_id, args.member_ids));
|
|
1357
|
+
}
|
|
1358
|
+
return json(await official.addChatMembers(args.chat_id, args.member_ids));
|
|
1456
1359
|
}
|
|
1457
1360
|
|
|
1458
1361
|
// --- Official API: Doc Block Editing ---
|
|
@@ -1470,6 +1373,16 @@ async function handleTool(name, args) {
|
|
|
1470
1373
|
return json(await getOfficialClient().getBitableRecord(args.app_token, args.table_id, args.record_id));
|
|
1471
1374
|
case 'delete_bitable_table':
|
|
1472
1375
|
return text(`Table deleted: ${(await getOfficialClient().deleteBitableTable(args.app_token, args.table_id)).deleted}`);
|
|
1376
|
+
case 'get_bitable_meta':
|
|
1377
|
+
return json(await getOfficialClient().getBitableMeta(args.app_token));
|
|
1378
|
+
case 'update_bitable_table':
|
|
1379
|
+
return text(`Table renamed: ${(await getOfficialClient().updateBitableTable(args.app_token, args.table_id, args.name)).name}`);
|
|
1380
|
+
case 'create_bitable_view':
|
|
1381
|
+
return json(await getOfficialClient().createBitableView(args.app_token, args.table_id, args.view_name, args.view_type));
|
|
1382
|
+
case 'delete_bitable_view':
|
|
1383
|
+
return text(`View deleted: ${(await getOfficialClient().deleteBitableView(args.app_token, args.table_id, args.view_id)).deleted}`);
|
|
1384
|
+
case 'copy_bitable':
|
|
1385
|
+
return json(await getOfficialClient().copyBitable(args.app_token, args.name, args.folder_id));
|
|
1473
1386
|
|
|
1474
1387
|
// --- Official API: Drive File Operations ---
|
|
1475
1388
|
|
|
@@ -1480,53 +1393,22 @@ async function handleTool(name, args) {
|
|
|
1480
1393
|
case 'delete_file':
|
|
1481
1394
|
return text(`File deleted: task=${(await getOfficialClient().deleteFile(args.file_token, args.type)).taskId}`);
|
|
1482
1395
|
|
|
1483
|
-
// --- Official API: Calendar ---
|
|
1484
|
-
|
|
1485
|
-
case 'list_calendars':
|
|
1486
|
-
return json(await getOfficialClient().listCalendars());
|
|
1487
|
-
case 'create_calendar_event': {
|
|
1488
|
-
const event = { summary: args.summary, description: args.description || '' };
|
|
1489
|
-
event.start_time = { timestamp: args.start_time };
|
|
1490
|
-
event.end_time = { timestamp: args.end_time };
|
|
1491
|
-
if (args.location) event.location = { name: args.location };
|
|
1492
|
-
return json(await getOfficialClient().createCalendarEvent(args.calendar_id, event));
|
|
1493
|
-
}
|
|
1494
|
-
case 'list_calendar_events':
|
|
1495
|
-
return json(await getOfficialClient().listCalendarEvents(args.calendar_id, {
|
|
1496
|
-
startTime: args.start_time, endTime: args.end_time, pageSize: args.page_size,
|
|
1497
|
-
}));
|
|
1498
|
-
case 'delete_calendar_event':
|
|
1499
|
-
return text(`Event deleted: ${(await getOfficialClient().deleteCalendarEvent(args.calendar_id, args.event_id)).deleted}`);
|
|
1500
|
-
case 'get_freebusy':
|
|
1501
|
-
return json(await getOfficialClient().getFreeBusy(args.user_ids, args.start_time, args.end_time));
|
|
1502
|
-
|
|
1503
|
-
// --- Official API: Tasks ---
|
|
1504
|
-
|
|
1505
|
-
case 'create_task': {
|
|
1506
|
-
const task = { summary: args.summary };
|
|
1507
|
-
if (args.description) task.description = args.description;
|
|
1508
|
-
if (args.due) task.due = { timestamp: args.due };
|
|
1509
|
-
return json(await getOfficialClient().createTask(task));
|
|
1510
|
-
}
|
|
1511
|
-
case 'get_task':
|
|
1512
|
-
return json(await getOfficialClient().getTask(args.task_id));
|
|
1513
|
-
case 'list_tasks':
|
|
1514
|
-
return json(await getOfficialClient().listTasks({ pageSize: args.page_size, pageToken: args.page_token }));
|
|
1515
|
-
case 'update_task': {
|
|
1516
|
-
const task = {};
|
|
1517
|
-
if (args.summary) task.summary = args.summary;
|
|
1518
|
-
if (args.description) task.description = args.description;
|
|
1519
|
-
if (args.due) task.due = { timestamp: args.due };
|
|
1520
|
-
return json(await getOfficialClient().updateTask(args.task_id, task));
|
|
1521
|
-
}
|
|
1522
|
-
case 'complete_task':
|
|
1523
|
-
return text(`Task completed: ${(await getOfficialClient().completeTask(args.task_id)).completed}`);
|
|
1524
|
-
|
|
1525
1396
|
default:
|
|
1526
1397
|
return text(`Unknown tool: ${name}`);
|
|
1527
1398
|
}
|
|
1528
1399
|
}
|
|
1529
1400
|
|
|
1401
|
+
// --- Process-level error handlers ---
|
|
1402
|
+
// Prevent stray promise rejections or uncaught exceptions from killing the MCP server.
|
|
1403
|
+
process.on('uncaughtException', (err) => {
|
|
1404
|
+
console.error('[feishu-user-plugin] Uncaught exception:', err.message);
|
|
1405
|
+
console.error(err.stack);
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
process.on('unhandledRejection', (reason) => {
|
|
1409
|
+
console.error('[feishu-user-plugin] Unhandled rejection:', reason);
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1530
1412
|
async function main() {
|
|
1531
1413
|
const transport = new StdioServerTransport();
|
|
1532
1414
|
await server.connect(transport);
|
package/src/oauth.js
CHANGED
|
@@ -22,7 +22,8 @@ const APP_SECRET = creds.LARK_APP_SECRET;
|
|
|
22
22
|
const PORT = 9997;
|
|
23
23
|
const REDIRECT_URI = `http://127.0.0.1:${PORT}/callback`;
|
|
24
24
|
// offline_access is required to get refresh_token for auto-renewal
|
|
25
|
-
|
|
25
|
+
// Write scopes (docx:document, drive:drive, bitable:app) allow creating resources as the user, not the app
|
|
26
|
+
const SCOPES = 'offline_access auth:user.id:read im:message im:message:readonly im:chat im:chat:readonly contact:user.base:readonly contact:user.id:readonly docx:document drive:drive bitable:app wiki:wiki:readonly';
|
|
26
27
|
|
|
27
28
|
if (!APP_ID || !APP_SECRET) {
|
|
28
29
|
console.error('Missing LARK_APP_ID or LARK_APP_SECRET.');
|