feishu-user-plugin 1.2.1 → 1.3.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/src/index.js CHANGED
@@ -541,49 +541,9 @@ const TOOLS = [
541
541
  required: ['app_token', 'table_id'],
542
542
  },
543
543
  },
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
544
  {
585
545
  name: 'batch_create_bitable_records',
586
- description: '[Official API] Batch create multiple records (rows) in a Bitable table. Max 500 per call.',
546
+ description: '[Official API] Create one or more records (rows) in a Bitable table. Pass a single record or up to 500.',
587
547
  inputSchema: {
588
548
  type: 'object',
589
549
  properties: {
@@ -596,7 +556,7 @@ const TOOLS = [
596
556
  },
597
557
  {
598
558
  name: 'batch_update_bitable_records',
599
- description: '[Official API] Batch update multiple records in a Bitable table. Max 500 per call.',
559
+ description: '[Official API] Update one or more records in a Bitable table. Pass a single record or up to 500.',
600
560
  inputSchema: {
601
561
  type: 'object',
602
562
  properties: {
@@ -609,7 +569,7 @@ const TOOLS = [
609
569
  },
610
570
  {
611
571
  name: 'batch_delete_bitable_records',
612
- description: '[Official API] Batch delete multiple records from a Bitable table. Max 500 per call.',
572
+ description: '[Official API] Delete one or more records from a Bitable table. Pass a single ID or up to 500.',
613
573
  inputSchema: {
614
574
  type: 'object',
615
575
  properties: {
@@ -710,6 +670,313 @@ const TOOLS = [
710
670
  },
711
671
  },
712
672
  },
673
+
674
+ // ========== IM — Bot Send / Edit / Delete ==========
675
+ {
676
+ name: 'send_message_as_bot',
677
+ description: '[Official API] Send a message as the bot to any chat. Supports text, post, interactive, etc.',
678
+ inputSchema: {
679
+ type: 'object',
680
+ properties: {
681
+ chat_id: { type: 'string', description: 'Target chat_id (oc_xxx) or open_id' },
682
+ 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'] },
683
+ content: { description: 'Message content (string or object, auto-serialized). For text: {"text":"hello"}' },
684
+ },
685
+ required: ['chat_id', 'msg_type', 'content'],
686
+ },
687
+ },
688
+ {
689
+ name: 'delete_message',
690
+ description: '[Official API] Recall/delete a message (bot can only delete its own messages).',
691
+ inputSchema: {
692
+ type: 'object',
693
+ properties: { message_id: { type: 'string', description: 'Message ID (om_xxx)' } },
694
+ required: ['message_id'],
695
+ },
696
+ },
697
+ {
698
+ name: 'update_message',
699
+ description: '[Official API] Edit a sent message (bot can only edit its own messages). Supports text and post.',
700
+ inputSchema: {
701
+ type: 'object',
702
+ properties: {
703
+ message_id: { type: 'string', description: 'Message ID (om_xxx)' },
704
+ msg_type: { type: 'string', description: 'Message type: text or post' },
705
+ content: { description: 'New content. For text: {"text":"updated text"}' },
706
+ },
707
+ required: ['message_id', 'msg_type', 'content'],
708
+ },
709
+ },
710
+
711
+ // ========== IM — Reactions ==========
712
+ {
713
+ name: 'add_reaction',
714
+ description: '[Official API] Add an emoji reaction to a message.',
715
+ inputSchema: {
716
+ type: 'object',
717
+ properties: {
718
+ message_id: { type: 'string', description: 'Message ID (om_xxx)' },
719
+ emoji_type: { type: 'string', description: 'Emoji type string, e.g. "THUMBSUP", "SMILE", "HEART"' },
720
+ },
721
+ required: ['message_id', 'emoji_type'],
722
+ },
723
+ },
724
+ {
725
+ name: 'delete_reaction',
726
+ description: '[Official API] Remove an emoji reaction from a message.',
727
+ inputSchema: {
728
+ type: 'object',
729
+ properties: {
730
+ message_id: { type: 'string', description: 'Message ID' },
731
+ reaction_id: { type: 'string', description: 'Reaction ID (from add_reaction response)' },
732
+ },
733
+ required: ['message_id', 'reaction_id'],
734
+ },
735
+ },
736
+
737
+ // ========== IM — Pin Messages ==========
738
+ {
739
+ name: 'pin_message',
740
+ description: '[Official API] Pin or unpin a message in a chat.',
741
+ inputSchema: {
742
+ type: 'object',
743
+ properties: {
744
+ message_id: { type: 'string', description: 'Message ID' },
745
+ pinned: { type: 'boolean', description: 'true to pin, false to unpin', default: true },
746
+ },
747
+ required: ['message_id'],
748
+ },
749
+ },
750
+
751
+ // ========== IM — Chat Management ==========
752
+ {
753
+ name: 'create_group',
754
+ description: '[Official API] Create a new group chat (as bot). Can add initial members.',
755
+ inputSchema: {
756
+ type: 'object',
757
+ properties: {
758
+ name: { type: 'string', description: 'Group name' },
759
+ description: { type: 'string', description: 'Group description (optional)' },
760
+ user_ids: { type: 'array', items: { type: 'string' }, description: 'Initial member open_ids (optional)' },
761
+ },
762
+ required: ['name'],
763
+ },
764
+ },
765
+ {
766
+ name: 'update_group',
767
+ description: '[Official API] Update group chat name or description.',
768
+ inputSchema: {
769
+ type: 'object',
770
+ properties: {
771
+ chat_id: { type: 'string', description: 'Chat ID (oc_xxx)' },
772
+ name: { type: 'string', description: 'New group name (optional)' },
773
+ description: { type: 'string', description: 'New description (optional)' },
774
+ },
775
+ required: ['chat_id'],
776
+ },
777
+ },
778
+ {
779
+ name: 'list_members',
780
+ description: '[Official API] List all members in a group chat.',
781
+ inputSchema: {
782
+ type: 'object',
783
+ properties: {
784
+ chat_id: { type: 'string', description: 'Chat ID (oc_xxx)' },
785
+ page_size: { type: 'number', description: 'Items per page (default 50)' },
786
+ page_token: { type: 'string', description: 'Pagination token' },
787
+ },
788
+ required: ['chat_id'],
789
+ },
790
+ },
791
+ {
792
+ name: 'manage_members',
793
+ description: '[Official API] Add or remove members from a group chat.',
794
+ inputSchema: {
795
+ type: 'object',
796
+ properties: {
797
+ chat_id: { type: 'string', description: 'Group chat ID (oc_xxx)' },
798
+ member_ids: { type: 'array', items: { type: 'string' }, description: 'Array of user open_ids' },
799
+ action: { type: 'string', enum: ['add', 'remove'], description: 'Action to perform' },
800
+ },
801
+ required: ['chat_id', 'member_ids', 'action'],
802
+ },
803
+ },
804
+
805
+ // ========== Docs — Block Editing ==========
806
+ {
807
+ name: 'create_doc_block',
808
+ description: '[Official API] Insert content blocks into a document. Add text, headings, lists, etc. after create_doc.',
809
+ inputSchema: {
810
+ type: 'object',
811
+ properties: {
812
+ document_id: { type: 'string', description: 'Document ID' },
813
+ parent_block_id: { type: 'string', description: 'Parent block ID (use document_id for root)' },
814
+ children: { type: 'array', description: 'Array of block objects to insert. E.g. [{block_type:2, text:{elements:[{text_run:{content:"Hello"}}]}}]', items: { type: 'object' } },
815
+ index: { type: 'number', description: 'Insert position (optional, appends to end if omitted)' },
816
+ },
817
+ required: ['document_id', 'parent_block_id', 'children'],
818
+ },
819
+ },
820
+ {
821
+ name: 'update_doc_block',
822
+ description: '[Official API] Update a specific block in a document (change text content, style, etc.).',
823
+ inputSchema: {
824
+ type: 'object',
825
+ properties: {
826
+ document_id: { type: 'string', description: 'Document ID' },
827
+ block_id: { type: 'string', description: 'Block ID to update' },
828
+ update_body: { type: 'object', description: 'Update payload. E.g. {update_text_elements:{elements:[{text_run:{content:"new text"}}]}}' },
829
+ },
830
+ required: ['document_id', 'block_id', 'update_body'],
831
+ },
832
+ },
833
+ {
834
+ name: 'delete_doc_blocks',
835
+ description: '[Official API] Delete a range of blocks from a document.',
836
+ inputSchema: {
837
+ type: 'object',
838
+ properties: {
839
+ document_id: { type: 'string', description: 'Document ID' },
840
+ parent_block_id: { type: 'string', description: 'Parent block ID containing the blocks to delete' },
841
+ start_index: { type: 'number', description: 'Start index (inclusive)' },
842
+ end_index: { type: 'number', description: 'End index (exclusive)' },
843
+ },
844
+ required: ['document_id', 'parent_block_id', 'start_index', 'end_index'],
845
+ },
846
+ },
847
+
848
+ // ========== Bitable — Additional ==========
849
+ {
850
+ name: 'get_bitable_record',
851
+ description: '[Official API] Get a single record by ID from a Bitable table.',
852
+ inputSchema: {
853
+ type: 'object',
854
+ properties: {
855
+ app_token: { type: 'string', description: 'Bitable app token' },
856
+ table_id: { type: 'string', description: 'Table ID' },
857
+ record_id: { type: 'string', description: 'Record ID' },
858
+ },
859
+ required: ['app_token', 'table_id', 'record_id'],
860
+ },
861
+ },
862
+ {
863
+ name: 'delete_bitable_table',
864
+ description: '[Official API] Delete a data table from a Bitable app.',
865
+ inputSchema: {
866
+ type: 'object',
867
+ properties: {
868
+ app_token: { type: 'string', description: 'Bitable app token' },
869
+ table_id: { type: 'string', description: 'Table ID to delete' },
870
+ },
871
+ required: ['app_token', 'table_id'],
872
+ },
873
+ },
874
+
875
+ {
876
+ name: 'get_bitable_meta',
877
+ description: '[Official API] Get metadata of a Bitable app (name, revision, etc.).',
878
+ inputSchema: {
879
+ type: 'object',
880
+ properties: {
881
+ app_token: { type: 'string', description: 'Bitable app token' },
882
+ },
883
+ required: ['app_token'],
884
+ },
885
+ },
886
+ {
887
+ name: 'update_bitable_table',
888
+ description: '[Official API] Rename a data table in a Bitable app.',
889
+ inputSchema: {
890
+ type: 'object',
891
+ properties: {
892
+ app_token: { type: 'string', description: 'Bitable app token' },
893
+ table_id: { type: 'string', description: 'Table ID' },
894
+ name: { type: 'string', description: 'New table name' },
895
+ },
896
+ required: ['app_token', 'table_id', 'name'],
897
+ },
898
+ },
899
+ {
900
+ name: 'create_bitable_view',
901
+ description: '[Official API] Create a new view in a Bitable table.',
902
+ inputSchema: {
903
+ type: 'object',
904
+ properties: {
905
+ app_token: { type: 'string', description: 'Bitable app token' },
906
+ table_id: { type: 'string', description: 'Table ID' },
907
+ view_name: { type: 'string', description: 'View name' },
908
+ view_type: { type: 'string', description: 'View type: grid (default), kanban, gallery, form, gantt, calendar', default: 'grid' },
909
+ },
910
+ required: ['app_token', 'table_id', 'view_name'],
911
+ },
912
+ },
913
+ {
914
+ name: 'delete_bitable_view',
915
+ description: '[Official API] Delete a view from a Bitable table.',
916
+ inputSchema: {
917
+ type: 'object',
918
+ properties: {
919
+ app_token: { type: 'string', description: 'Bitable app token' },
920
+ table_id: { type: 'string', description: 'Table ID' },
921
+ view_id: { type: 'string', description: 'View ID to delete' },
922
+ },
923
+ required: ['app_token', 'table_id', 'view_id'],
924
+ },
925
+ },
926
+ {
927
+ name: 'copy_bitable',
928
+ description: '[Official API] Copy a Bitable app to create a new one.',
929
+ inputSchema: {
930
+ type: 'object',
931
+ properties: {
932
+ app_token: { type: 'string', description: 'Bitable app token to copy' },
933
+ name: { type: 'string', description: 'New Bitable name' },
934
+ folder_id: { type: 'string', description: 'Destination folder token (optional)' },
935
+ },
936
+ required: ['app_token', 'name'],
937
+ },
938
+ },
939
+
940
+ // ========== Drive — File Operations ==========
941
+ {
942
+ name: 'copy_file',
943
+ description: '[Official API] Copy a file/doc in Drive.',
944
+ inputSchema: {
945
+ type: 'object',
946
+ properties: {
947
+ file_token: { type: 'string', description: 'File token to copy' },
948
+ name: { type: 'string', description: 'New file name' },
949
+ folder_token: { type: 'string', description: 'Destination folder token (optional)' },
950
+ type: { type: 'string', description: 'File type: file, doc, sheet, bitable, docx, mindnote, slides (optional)' },
951
+ },
952
+ required: ['file_token', 'name'],
953
+ },
954
+ },
955
+ {
956
+ name: 'move_file',
957
+ description: '[Official API] Move a file to another folder in Drive.',
958
+ inputSchema: {
959
+ type: 'object',
960
+ properties: {
961
+ file_token: { type: 'string', description: 'File token to move' },
962
+ folder_token: { type: 'string', description: 'Destination folder token' },
963
+ },
964
+ required: ['file_token', 'folder_token'],
965
+ },
966
+ },
967
+ {
968
+ name: 'delete_file',
969
+ description: '[Official API] Delete a file/folder from Drive.',
970
+ inputSchema: {
971
+ type: 'object',
972
+ properties: {
973
+ file_token: { type: 'string', description: 'File token to delete' },
974
+ type: { type: 'string', description: 'Type: file, folder, doc, sheet, bitable, docx, mindnote, slides' },
975
+ },
976
+ required: ['file_token'],
977
+ },
978
+ },
979
+
713
980
  ];
714
981
 
715
982
  // --- Server ---
@@ -953,14 +1220,33 @@ async function handleTool(name, args) {
953
1220
  return json(await getOfficialClient().readDoc(args.document_id));
954
1221
  case 'get_doc_blocks':
955
1222
  return json(await getOfficialClient().getDocBlocks(args.document_id));
956
- case 'create_doc':
957
- return text(`Document created: ${(await getOfficialClient().createDoc(args.title, args.folder_id)).documentId}`);
1223
+ case 'create_doc': {
1224
+ const official = getOfficialClient();
1225
+ if (official.hasUAT) {
1226
+ try {
1227
+ const result = await official.createDocAsUser(args.title, args.folder_id);
1228
+ return text(`Document created (as user): ${result.documentId}`);
1229
+ } catch (e) {
1230
+ console.error(`[feishu-user-plugin] UAT createDoc failed, falling back to app: ${e.message}`);
1231
+ }
1232
+ }
1233
+ return text(`Document created: ${(await official.createDoc(args.title, args.folder_id)).documentId}`);
1234
+ }
958
1235
 
959
1236
  // --- Official API: Bitable ---
960
1237
 
961
1238
  case 'create_bitable': {
962
- const r = await getOfficialClient().createBitable(args.name, args.folder_id);
963
- return text(`Bitable created: ${r.appToken}${r.url ? `\nURL: ${r.url}` : ''}`);
1239
+ const official = getOfficialClient();
1240
+ if (official.hasUAT) {
1241
+ try {
1242
+ const r = await official.createBitableAsUser(args.name, args.folder_id);
1243
+ return text(`Bitable created (as user): ${r.appToken}\nURL: ${r.url || ''}`);
1244
+ } catch (e) {
1245
+ console.error(`[feishu-user-plugin] UAT createBitable failed, falling back to app: ${e.message}`);
1246
+ }
1247
+ }
1248
+ const r = await official.createBitable(args.name, args.folder_id);
1249
+ return text(`Bitable created: ${r.appToken}\nURL: ${r.url || ''}`);
964
1250
  }
965
1251
  case 'list_bitable_tables':
966
1252
  return json(await getOfficialClient().listBitableTables(args.app_token));
@@ -990,12 +1276,6 @@ async function handleTool(name, args) {
990
1276
  return json(await getOfficialClient().searchBitableRecords(args.app_token, args.table_id, {
991
1277
  filter: args.filter, sort: args.sort, pageSize: args.page_size,
992
1278
  }));
993
- case 'create_bitable_record':
994
- return text(`Record created: ${(await getOfficialClient().createBitableRecord(args.app_token, args.table_id, args.fields)).recordId}`);
995
- case 'update_bitable_record':
996
- return text(`Record updated: ${(await getOfficialClient().updateBitableRecord(args.app_token, args.table_id, args.record_id, args.fields)).recordId}`);
997
- case 'delete_bitable_record':
998
- return text(`Record deleted: ${(await getOfficialClient().deleteBitableRecord(args.app_token, args.table_id, args.record_id)).deleted}`);
999
1279
  case 'batch_create_bitable_records':
1000
1280
  return json(await getOfficialClient().batchCreateBitableRecords(args.app_token, args.table_id, args.records));
1001
1281
  case 'batch_update_bitable_records':
@@ -1016,8 +1296,17 @@ async function handleTool(name, args) {
1016
1296
 
1017
1297
  case 'list_files':
1018
1298
  return json(await getOfficialClient().listFiles(args.folder_token));
1019
- case 'create_folder':
1020
- return text(`Folder created: ${(await getOfficialClient().createFolder(args.name, args.parent_token)).token}`);
1299
+ case 'create_folder': {
1300
+ const official = getOfficialClient();
1301
+ if (official.hasUAT) {
1302
+ try {
1303
+ return text(`Folder created (as user): ${(await official.createFolderAsUser(args.name, args.parent_token)).token}`);
1304
+ } catch (e) {
1305
+ console.error(`[feishu-user-plugin] UAT createFolder failed, falling back to app: ${e.message}`);
1306
+ }
1307
+ }
1308
+ return text(`Folder created: ${(await official.createFolder(args.name, args.parent_token)).token}`);
1309
+ }
1021
1310
 
1022
1311
  // --- Official API: Contact ---
1023
1312
 
@@ -1035,11 +1324,96 @@ async function handleTool(name, args) {
1035
1324
  return text(`File uploaded: ${r.fileKey}\nUse this file_key with send_file_as_user to send it.`);
1036
1325
  }
1037
1326
 
1327
+ // --- Official API: Bot Send / Edit / Delete ---
1328
+
1329
+ case 'send_message_as_bot': {
1330
+ const r = await getOfficialClient().sendMessageAsBot(args.chat_id, args.msg_type, args.content);
1331
+ return text(`Message sent (bot): ${r.messageId}`);
1332
+ }
1333
+ case 'delete_message':
1334
+ return text(`Message deleted: ${(await getOfficialClient().deleteMessage(args.message_id)).deleted}`);
1335
+ case 'update_message':
1336
+ return text(`Message updated: ${(await getOfficialClient().updateMessage(args.message_id, args.msg_type, args.content)).messageId}`);
1337
+
1338
+ // --- Official API: Reactions ---
1339
+
1340
+ case 'add_reaction':
1341
+ return text(`Reaction added: ${(await getOfficialClient().addReaction(args.message_id, args.emoji_type)).reactionId}`);
1342
+ case 'delete_reaction':
1343
+ return text(`Reaction removed: ${(await getOfficialClient().deleteReaction(args.message_id, args.reaction_id)).deleted}`);
1344
+
1345
+ // --- Official API: Pins ---
1346
+
1347
+ case 'pin_message':
1348
+ return json(await getOfficialClient().pinMessage(args.message_id, args.pinned !== false));
1349
+
1350
+ // --- Official API: Chat Management ---
1351
+
1352
+ case 'create_group':
1353
+ return text(`Group created: ${(await getOfficialClient().createChat({ name: args.name, description: args.description, userIds: args.user_ids })).chatId}`);
1354
+ case 'update_group':
1355
+ return text(`Group updated: ${(await getOfficialClient().updateChat(args.chat_id, { name: args.name, description: args.description })).updated}`);
1356
+ case 'list_members':
1357
+ return json(await getOfficialClient().listChatMembers(args.chat_id, { pageSize: args.page_size, pageToken: args.page_token }));
1358
+ case 'manage_members': {
1359
+ const official = getOfficialClient();
1360
+ if (args.action === 'remove') {
1361
+ return json(await official.removeChatMembers(args.chat_id, args.member_ids));
1362
+ }
1363
+ return json(await official.addChatMembers(args.chat_id, args.member_ids));
1364
+ }
1365
+
1366
+ // --- Official API: Doc Block Editing ---
1367
+
1368
+ case 'create_doc_block':
1369
+ return json(await getOfficialClient().createDocBlock(args.document_id, args.parent_block_id, args.children, args.index));
1370
+ case 'update_doc_block':
1371
+ return json(await getOfficialClient().updateDocBlock(args.document_id, args.block_id, args.update_body));
1372
+ case 'delete_doc_blocks':
1373
+ return text(`Blocks deleted: ${(await getOfficialClient().deleteDocBlocks(args.document_id, args.parent_block_id, args.start_index, args.end_index)).deleted}`);
1374
+
1375
+ // --- Official API: Bitable Additional ---
1376
+
1377
+ case 'get_bitable_record':
1378
+ return json(await getOfficialClient().getBitableRecord(args.app_token, args.table_id, args.record_id));
1379
+ case 'delete_bitable_table':
1380
+ return text(`Table deleted: ${(await getOfficialClient().deleteBitableTable(args.app_token, args.table_id)).deleted}`);
1381
+ case 'get_bitable_meta':
1382
+ return json(await getOfficialClient().getBitableMeta(args.app_token));
1383
+ case 'update_bitable_table':
1384
+ return text(`Table renamed: ${(await getOfficialClient().updateBitableTable(args.app_token, args.table_id, args.name)).name}`);
1385
+ case 'create_bitable_view':
1386
+ return json(await getOfficialClient().createBitableView(args.app_token, args.table_id, args.view_name, args.view_type));
1387
+ case 'delete_bitable_view':
1388
+ return text(`View deleted: ${(await getOfficialClient().deleteBitableView(args.app_token, args.table_id, args.view_id)).deleted}`);
1389
+ case 'copy_bitable':
1390
+ return json(await getOfficialClient().copyBitable(args.app_token, args.name, args.folder_id));
1391
+
1392
+ // --- Official API: Drive File Operations ---
1393
+
1394
+ case 'copy_file':
1395
+ return json(await getOfficialClient().copyFile(args.file_token, args.name, args.folder_token, args.type));
1396
+ case 'move_file':
1397
+ return text(`File moved: task=${(await getOfficialClient().moveFile(args.file_token, args.folder_token)).taskId}`);
1398
+ case 'delete_file':
1399
+ return text(`File deleted: task=${(await getOfficialClient().deleteFile(args.file_token, args.type)).taskId}`);
1400
+
1038
1401
  default:
1039
1402
  return text(`Unknown tool: ${name}`);
1040
1403
  }
1041
1404
  }
1042
1405
 
1406
+ // --- Process-level error handlers ---
1407
+ // Prevent stray promise rejections or uncaught exceptions from killing the MCP server.
1408
+ process.on('uncaughtException', (err) => {
1409
+ console.error('[feishu-user-plugin] Uncaught exception:', err.message);
1410
+ console.error(err.stack);
1411
+ });
1412
+
1413
+ process.on('unhandledRejection', (reason) => {
1414
+ console.error('[feishu-user-plugin] Unhandled rejection:', reason);
1415
+ });
1416
+
1043
1417
  async function main() {
1044
1418
  const transport = new StdioServerTransport();
1045
1419
  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
- const SCOPES = 'offline_access im:message im:message:readonly im:chat:readonly contact:user.base:readonly';
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.');