file-tools.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /**
  2. * File Management Tools
  3. *
  4. * MCP tools for managing files in the admin system.
  5. * Note: Only metadata management is supported, binary upload is not available.
  6. */
  7. import { getApiClient } from '../services/api-client.js';
  8. import {
  9. FileListInputSchema,
  10. FileGetInputSchema,
  11. FileDeleteInputSchema
  12. } from '../schemas/index.js';
  13. import type {
  14. FileListInput,
  15. FileGetInput,
  16. FileDeleteInput
  17. } from '../schemas/index.js';
  18. import { CHARACTER_LIMIT, ResponseFormat } from '../constants.js';
  19. import type { File, PaginatedResponse } from '../types.js';
  20. /**
  21. * Format file data for markdown output
  22. */
  23. function formatFileMarkdown(file: File): string {
  24. const lines = [
  25. `## File: ${file.fileName} (ID: ${file.id})`,
  26. '',
  27. `**File Name**: ${file.fileName}`,
  28. `**File Size**: ${(file.fileSize / 1024).toFixed(2)} KB`,
  29. `**MIME Type**: ${file.mimeType}`,
  30. `**File Path**: ${file.filePath}`,
  31. file.uploadUser ? `**Uploaded By**: ${file.uploadUser.username} (ID: ${file.uploadUserId})` : `**Uploaded By ID**: ${file.uploadUserId}`,
  32. `**Created At**: ${new Date(file.createdAt).toLocaleString('zh-CN')}`,
  33. ''
  34. ];
  35. return lines.join('\n');
  36. }
  37. /**
  38. * Format file list for markdown output
  39. */
  40. function formatFileListMarkdown(files: File[], total: number, current: number, pageSize: number): string {
  41. const lines = [
  42. `# File List`,
  43. '',
  44. `Total: ${total} files | Page: ${current} | Page Size: ${pageSize}`,
  45. ''
  46. ];
  47. for (const file of files) {
  48. lines.push(formatFileMarkdown(file));
  49. }
  50. return lines.join('\n');
  51. }
  52. /**
  53. * List files tool
  54. */
  55. export const fileListTool = async (args: FileListInput) => {
  56. const apiClient = getApiClient();
  57. try {
  58. const { page, pageSize, keyword, sortBy, sortOrder, filters, response_format } = args;
  59. const params: Record<string, unknown> = {
  60. page: page || 1,
  61. pageSize: pageSize || 20
  62. };
  63. if (keyword) params.keyword = keyword;
  64. if (sortBy) params.sortBy = sortBy;
  65. if (sortOrder) params.sortOrder = sortOrder;
  66. if (filters) params.filters = filters;
  67. const response = await apiClient.get<PaginatedResponse<File>>('/api/v1/files', params);
  68. const files = response.data || [];
  69. const total = response.pagination?.total || 0;
  70. const structuredOutput = {
  71. total,
  72. count: files.length,
  73. current: page || 1,
  74. pageSize: pageSize || 20,
  75. files: files.map((f: File) => ({
  76. id: f.id,
  77. fileName: f.fileName,
  78. fileSize: f.fileSize,
  79. mimeType: f.mimeType,
  80. filePath: f.filePath,
  81. uploadUserId: f.uploadUserId,
  82. uploadUserName: f.uploadUser?.username,
  83. createdAt: f.createdAt
  84. })),
  85. hasMore: total > (page || 1) * (pageSize || 20)
  86. };
  87. let textContent: string;
  88. if (response_format === ResponseFormat.JSON) {
  89. textContent = JSON.stringify(structuredOutput, null, 2);
  90. } else {
  91. let markdown = formatFileListMarkdown(files, total, page || 1, pageSize || 20);
  92. // Check character limit
  93. if (markdown.length > CHARACTER_LIMIT) {
  94. const halfLength = Math.floor(files.length / 2);
  95. const truncatedFiles = files.slice(0, halfLength);
  96. markdown = formatFileListMarkdown(truncatedFiles, total, page || 1, pageSize || 20);
  97. markdown += `\n---\n⚠️ **Response truncated**: Showing ${halfLength} of ${files.length} results. Use pagination to see more.\n`;
  98. }
  99. textContent = markdown;
  100. }
  101. return {
  102. content: [{ type: 'text', text: textContent }],
  103. structuredContent: structuredOutput
  104. };
  105. } catch (error) {
  106. return {
  107. content: [{
  108. type: 'text',
  109. text: `Error: ${error instanceof Error ? error.message : 'Failed to list files'}`
  110. }]
  111. };
  112. }
  113. };
  114. /**
  115. * Get single file tool
  116. */
  117. export const fileGetTool = async (args: FileGetInput) => {
  118. const apiClient = getApiClient();
  119. try {
  120. const { id } = args;
  121. const response = await apiClient.get<File>(`/api/v1/files/${id}`);
  122. const file = response;
  123. const structuredOutput = {
  124. id: file.id,
  125. fileName: file.fileName,
  126. fileSize: file.fileSize,
  127. mimeType: file.mimeType,
  128. filePath: file.filePath,
  129. uploadUserId: file.uploadUserId,
  130. uploadUserName: file.uploadUser?.username,
  131. createdAt: file.createdAt,
  132. updatedAt: file.updatedAt
  133. };
  134. const markdown = formatFileMarkdown(file);
  135. return {
  136. content: [{ type: 'text', text: markdown }],
  137. structuredContent: structuredOutput
  138. };
  139. } catch (error) {
  140. return {
  141. content: [{
  142. type: 'text',
  143. text: `Error: ${error instanceof Error ? error.message : 'Failed to get file'}`
  144. }]
  145. };
  146. }
  147. };
  148. /**
  149. * Delete file tool
  150. */
  151. export const fileDeleteTool = async (args: FileDeleteInput) => {
  152. const apiClient = getApiClient();
  153. try {
  154. const { id } = args;
  155. await apiClient.delete<{ success: boolean }>(`/api/v1/files/${id}`);
  156. const markdown = `✅ **File Deleted Successfully**\n\nFile ID ${id} has been deleted.`;
  157. const structuredOutput = {
  158. success: true,
  159. deletedFileId: id
  160. };
  161. return {
  162. content: [{ type: 'text', text: markdown }],
  163. structuredContent: structuredOutput
  164. };
  165. } catch (error) {
  166. return {
  167. content: [{
  168. type: 'text',
  169. text: `Error: ${error instanceof Error ? error.message : 'Failed to delete file'}`
  170. }]
  171. };
  172. }
  173. };
  174. // Export tool registration configs
  175. export const fileTools = {
  176. fileList: {
  177. name: 'admin_list_files',
  178. schema: FileListInputSchema,
  179. handler: fileListTool
  180. },
  181. fileGet: {
  182. name: 'admin_get_file',
  183. schema: FileGetInputSchema,
  184. handler: fileGetTool
  185. },
  186. fileDelete: {
  187. name: 'admin_delete_file',
  188. schema: FileDeleteInputSchema,
  189. handler: fileDeleteTool
  190. }
  191. };