feishu-user-plugin 1.3.0 → 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: {
@@ -777,19 +737,13 @@ const TOOLS = [
777
737
  // ========== IM — Pin Messages ==========
778
738
  {
779
739
  name: 'pin_message',
780
- description: '[Official API] Pin a message in a chat.',
781
- inputSchema: {
782
- type: 'object',
783
- properties: { message_id: { type: 'string', description: 'Message ID to pin' } },
784
- required: ['message_id'],
785
- },
786
- },
787
- {
788
- name: 'unpin_message',
789
- description: '[Official API] Unpin a message from a chat.',
740
+ description: '[Official API] Pin or unpin a message in a chat.',
790
741
  inputSchema: {
791
742
  type: 'object',
792
- properties: { message_id: { type: 'string', description: 'Message ID to unpin' } },
743
+ properties: {
744
+ message_id: { type: 'string', description: 'Message ID' },
745
+ pinned: { type: 'boolean', description: 'true to pin, false to unpin', default: true },
746
+ },
793
747
  required: ['message_id'],
794
748
  },
795
749
  },
@@ -835,27 +789,16 @@ const TOOLS = [
835
789
  },
836
790
  },
837
791
  {
838
- name: 'add_members',
839
- description: '[Official API] Add users to a group chat.',
792
+ name: 'manage_members',
793
+ description: '[Official API] Add or remove members from a group chat.',
840
794
  inputSchema: {
841
795
  type: 'object',
842
796
  properties: {
843
- chat_id: { type: 'string', description: 'Chat ID (oc_xxx)' },
844
- user_ids: { type: 'array', items: { type: 'string' }, description: 'Array of user open_ids to add' },
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' },
845
800
  },
846
- required: ['chat_id', 'user_ids'],
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'],
801
+ required: ['chat_id', 'member_ids', 'action'],
859
802
  },
860
803
  },
861
804
 
@@ -929,165 +872,111 @@ const TOOLS = [
929
872
  },
930
873
  },
931
874
 
932
- // ========== Drive — File Operations ==========
933
875
  {
934
- name: 'copy_file',
935
- description: '[Official API] Copy a file/doc in Drive.',
876
+ name: 'get_bitable_meta',
877
+ description: '[Official API] Get metadata of a Bitable app (name, revision, etc.).',
936
878
  inputSchema: {
937
879
  type: 'object',
938
880
  properties: {
939
- file_token: { type: 'string', description: 'File token to copy' },
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' },
881
+ app_token: { type: 'string', description: 'Bitable app token' },
967
882
  },
968
- required: ['file_token'],
883
+ required: ['app_token'],
969
884
  },
970
885
  },
971
-
972
- // ========== Calendar ==========
973
- {
974
- name: 'list_calendars',
975
- description: '[Official API] List all calendars accessible by the bot.',
976
- inputSchema: { type: 'object', properties: {} },
977
- },
978
886
  {
979
- name: 'create_calendar_event',
980
- description: '[Official API] Create a calendar event.',
887
+ name: 'update_bitable_table',
888
+ description: '[Official API] Rename a data table in a Bitable app.',
981
889
  inputSchema: {
982
890
  type: 'object',
983
891
  properties: {
984
- calendar_id: { type: 'string', description: 'Calendar ID (from list_calendars)' },
985
- summary: { type: 'string', description: 'Event title' },
986
- start_time: { type: 'string', description: 'Start time (RFC3339 or Unix timestamp string)' },
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)' },
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' },
990
895
  },
991
- required: ['calendar_id', 'summary', 'start_time', 'end_time'],
896
+ required: ['app_token', 'table_id', 'name'],
992
897
  },
993
898
  },
994
899
  {
995
- name: 'list_calendar_events',
996
- description: '[Official API] List events in a calendar.',
900
+ name: 'create_bitable_view',
901
+ description: '[Official API] Create a new view in a Bitable table.',
997
902
  inputSchema: {
998
903
  type: 'object',
999
904
  properties: {
1000
- calendar_id: { type: 'string', description: 'Calendar ID' },
1001
- start_time: { type: 'string', description: 'Start time filter (Unix timestamp string, optional)' },
1002
- end_time: { type: 'string', description: 'End time filter (Unix timestamp string, optional)' },
1003
- page_size: { type: 'number', description: 'Items per page (default 50)' },
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' },
1004
909
  },
1005
- required: ['calendar_id'],
910
+ required: ['app_token', 'table_id', 'view_name'],
1006
911
  },
1007
912
  },
1008
913
  {
1009
- name: 'delete_calendar_event',
1010
- description: '[Official API] Delete a calendar event.',
914
+ name: 'delete_bitable_view',
915
+ description: '[Official API] Delete a view from a Bitable table.',
1011
916
  inputSchema: {
1012
917
  type: 'object',
1013
918
  properties: {
1014
- calendar_id: { type: 'string', description: 'Calendar ID' },
1015
- event_id: { type: 'string', description: 'Event ID to delete' },
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' },
1016
922
  },
1017
- required: ['calendar_id', 'event_id'],
923
+ required: ['app_token', 'table_id', 'view_id'],
1018
924
  },
1019
925
  },
1020
926
  {
1021
- name: 'get_freebusy',
1022
- description: '[Official API] Check free/busy status of users for a time range.',
927
+ name: 'copy_bitable',
928
+ description: '[Official API] Copy a Bitable app to create a new one.',
1023
929
  inputSchema: {
1024
930
  type: 'object',
1025
931
  properties: {
1026
- user_ids: { type: 'array', items: { type: 'string' }, description: 'Array of user open_ids to check' },
1027
- start_time: { type: 'string', description: 'Range start (RFC3339)' },
1028
- end_time: { type: 'string', description: 'Range end (RFC3339)' },
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)' },
1029
935
  },
1030
- required: ['user_ids', 'start_time', 'end_time'],
936
+ required: ['app_token', 'name'],
1031
937
  },
1032
938
  },
1033
939
 
1034
- // ========== Tasks ==========
940
+ // ========== Drive — File Operations ==========
1035
941
  {
1036
- name: 'create_task',
1037
- description: '[Official API] Create a new task in Feishu Tasks.',
942
+ name: 'copy_file',
943
+ description: '[Official API] Copy a file/doc in Drive.',
1038
944
  inputSchema: {
1039
945
  type: 'object',
1040
946
  properties: {
1041
- summary: { type: 'string', description: 'Task title/summary' },
1042
- description: { type: 'string', description: 'Task description (optional)' },
1043
- due: { type: 'string', description: 'Due date/time (RFC3339 or Unix timestamp string, optional)' },
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)' },
1044
951
  },
1045
- required: ['summary'],
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'],
952
+ required: ['file_token', 'name'],
1055
953
  },
1056
954
  },
1057
955
  {
1058
- name: 'list_tasks',
1059
- description: '[Official API] List tasks.',
956
+ name: 'move_file',
957
+ description: '[Official API] Move a file to another folder in Drive.',
1060
958
  inputSchema: {
1061
959
  type: 'object',
1062
960
  properties: {
1063
- page_size: { type: 'number', description: 'Items per page (default 50)' },
1064
- page_token: { type: 'string', description: 'Pagination token' },
961
+ file_token: { type: 'string', description: 'File token to move' },
962
+ folder_token: { type: 'string', description: 'Destination folder token' },
1065
963
  },
964
+ required: ['file_token', 'folder_token'],
1066
965
  },
1067
966
  },
1068
967
  {
1069
- name: 'update_task',
1070
- description: '[Official API] Update a task (title, description, due date, etc.).',
968
+ name: 'delete_file',
969
+ description: '[Official API] Delete a file/folder from Drive.',
1071
970
  inputSchema: {
1072
971
  type: 'object',
1073
972
  properties: {
1074
- task_id: { type: 'string', description: 'Task ID' },
1075
- summary: { type: 'string', description: 'New title (optional)' },
1076
- description: { type: 'string', description: 'New description (optional)' },
1077
- due: { type: 'string', description: 'New due date (optional)' },
973
+ file_token: { type: 'string', description: 'File token to delete' },
974
+ type: { type: 'string', description: 'Type: file, folder, doc, sheet, bitable, docx, mindnote, slides' },
1078
975
  },
1079
- required: ['task_id'],
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'],
976
+ required: ['file_token'],
1089
977
  },
1090
978
  },
979
+
1091
980
  ];
1092
981
 
1093
982
  // --- Server ---
@@ -1331,14 +1220,33 @@ async function handleTool(name, args) {
1331
1220
  return json(await getOfficialClient().readDoc(args.document_id));
1332
1221
  case 'get_doc_blocks':
1333
1222
  return json(await getOfficialClient().getDocBlocks(args.document_id));
1334
- case 'create_doc':
1335
- 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
+ }
1336
1235
 
1337
1236
  // --- Official API: Bitable ---
1338
1237
 
1339
1238
  case 'create_bitable': {
1340
- const r = await getOfficialClient().createBitable(args.name, args.folder_id);
1341
- 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 || ''}`);
1342
1250
  }
1343
1251
  case 'list_bitable_tables':
1344
1252
  return json(await getOfficialClient().listBitableTables(args.app_token));
@@ -1368,12 +1276,6 @@ async function handleTool(name, args) {
1368
1276
  return json(await getOfficialClient().searchBitableRecords(args.app_token, args.table_id, {
1369
1277
  filter: args.filter, sort: args.sort, pageSize: args.page_size,
1370
1278
  }));
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
1279
  case 'batch_create_bitable_records':
1378
1280
  return json(await getOfficialClient().batchCreateBitableRecords(args.app_token, args.table_id, args.records));
1379
1281
  case 'batch_update_bitable_records':
@@ -1394,8 +1296,17 @@ async function handleTool(name, args) {
1394
1296
 
1395
1297
  case 'list_files':
1396
1298
  return json(await getOfficialClient().listFiles(args.folder_token));
1397
- case 'create_folder':
1398
- 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
+ }
1399
1310
 
1400
1311
  // --- Official API: Contact ---
1401
1312
 
@@ -1434,9 +1345,7 @@ async function handleTool(name, args) {
1434
1345
  // --- Official API: Pins ---
1435
1346
 
1436
1347
  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}`);
1348
+ return json(await getOfficialClient().pinMessage(args.message_id, args.pinned !== false));
1440
1349
 
1441
1350
  // --- Official API: Chat Management ---
1442
1351
 
@@ -1446,13 +1355,12 @@ async function handleTool(name, args) {
1446
1355
  return text(`Group updated: ${(await getOfficialClient().updateChat(args.chat_id, { name: args.name, description: args.description })).updated}`);
1447
1356
  case 'list_members':
1448
1357
  return json(await getOfficialClient().listChatMembers(args.chat_id, { pageSize: args.page_size, pageToken: args.page_token }));
1449
- case 'add_members': {
1450
- const r = await getOfficialClient().addChatMembers(args.chat_id, args.user_ids);
1451
- return text(r.invalidIds.length ? `Added (invalid IDs: ${r.invalidIds.join(', ')})` : 'Members added');
1452
- }
1453
- case 'remove_members': {
1454
- const r = await getOfficialClient().removeChatMembers(args.chat_id, args.user_ids);
1455
- return text(r.invalidIds.length ? `Removed (invalid IDs: ${r.invalidIds.join(', ')})` : 'Members removed');
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));
1456
1364
  }
1457
1365
 
1458
1366
  // --- Official API: Doc Block Editing ---
@@ -1470,6 +1378,16 @@ async function handleTool(name, args) {
1470
1378
  return json(await getOfficialClient().getBitableRecord(args.app_token, args.table_id, args.record_id));
1471
1379
  case 'delete_bitable_table':
1472
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));
1473
1391
 
1474
1392
  // --- Official API: Drive File Operations ---
1475
1393
 
@@ -1480,53 +1398,22 @@ async function handleTool(name, args) {
1480
1398
  case 'delete_file':
1481
1399
  return text(`File deleted: task=${(await getOfficialClient().deleteFile(args.file_token, args.type)).taskId}`);
1482
1400
 
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
1401
  default:
1526
1402
  return text(`Unknown tool: ${name}`);
1527
1403
  }
1528
1404
  }
1529
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
+
1530
1417
  async function main() {
1531
1418
  const transport = new StdioServerTransport();
1532
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.');