返回 AiToEarn
content.mcp.controller.ts
根目录 / project / aitoearn-backend / apps / aitoearn-server / src / core / content / content.mcp.controller.ts
1 import { Controller, Injectable } from '@nestjs/common'
2 import { getUser, toTextResult, toYamlTextResult, UserType } from '@yikart/common'
3 import { MaterialStatus, MaterialType, MediaType } from '@yikart/mongodb'
4 import { Tool } from '@yikart/nest-mcp'
5 import { z } from 'zod'
6 import { MaterialGroupService } from './material-group.service'
7 import { MaterialService } from './material.service'
8 import { MediaGroupService } from './media-group.service'
9 import { MediaService } from './media.service'
10
11 const GetGroupInfoByNameSchema = z.object({
12 title: z.string().describe('Group title'),
13 })
14
15 const CreateMediaSchema = z.object({
16 groupId: z.string().describe('Media group ID'),
17 draftId: z.string().optional().describe('Draft ID'),
18 type: z.enum(MediaType).describe('Media type'),
19 url: z.string().describe('Media URL'),
20 thumbUrl: z.string().optional().describe('Media thumbnail URL'),
21 title: z.string().optional().describe('Media title'),
22 desc: z.string().optional().describe('Media description'),
23 })
24
25 const MaterialMediaSchema = z.object({
26 id: z.string().optional().describe('Media ID'),
27 url: z.string(),
28 type: z.enum(MediaType).describe('Media type'),
29 thumbUrl: z.string().optional().describe('Media thumbnail URL'),
30 content: z.string().optional().describe('Media content'),
31 mediaId: z.string().optional().describe('Media ID'),
32 })
33
34 const CreateMaterialSchema = z.object({
35 groupId: z.string().describe('Group ID'),
36 coverUrl: z.string().optional().describe('Cover URL'),
37 mediaList: z.array(MaterialMediaSchema).describe('Media list'),
38 title: z.string().describe('Draft title'),
39 desc: z.string().optional().describe('Draft description'),
40 topics: z.array(z.string()).optional().default([]).describe('Draft topics'),
41 option: z.any().optional().describe('Draft options'),
42 type: z.enum(MaterialType).describe('Draft type'),
43 })
44
45 const ListDraftsSchema = z.object({
46 pageNo: z.number().int().min(1).default(1).describe('Page number'),
47 pageSize: z.number().int().min(1).max(100).default(20).describe('Page size'),
48 groupId: z.string().optional().describe('Draft group ID'),
49 title: z.string().optional().describe('Search by title'),
50 })
51
52 const GetDraftDetailSchema = z.object({
53 draftId: z.string().describe('Draft ID'),
54 })
55
56 const DeleteDraftSchema = z.object({
57 draftId: z.string().describe('Draft ID'),
58 })
59
60 const ListMediaSchema = z.object({
61 pageNo: z.number().int().min(1).default(1).describe('Page number'),
62 pageSize: z.number().int().min(1).max(100).default(20).describe('Page size'),
63 groupId: z.string().optional().describe('Media group ID'),
64 })
65
66 const ListGroupsSchema = z.object({
67 pageNo: z.number().int().min(1).default(1).describe('Page number'),
68 pageSize: z.number().int().min(1).max(100).default(100).describe('Page size'),
69 })
70
71 @Injectable()
72 @Controller()
73 export class ContentMcpController {
74 constructor(
75 private readonly mediaService: MediaService,
76 private readonly mediaGroupService: MediaGroupService,
77 private readonly materialService: MaterialService,
78 private readonly materialGroupService: MaterialGroupService,
79 ) {}
80
81 @Tool({
82 name: 'getMediaGroupInfoByName',
83 description: 'Get the authenticated user\'s media group by title. Returns matching group details, or default group if not found.',
84 parameters: GetGroupInfoByNameSchema,
85 })
86 async getMediaGroupByName(params: z.infer<typeof GetGroupInfoByNameSchema>) {
87 const user = getUser()
88 const result = await this.mediaGroupService.getInfoByName(user.id, params.title)
89 if (result) {
90 return toYamlTextResult(result)
91 }
92
93 const defaultGroup = await this.mediaGroupService.getDefaultGroup(user.id)
94 if (defaultGroup) {
95 return toYamlTextResult({
96 ...defaultGroup,
97 isDefault: true,
98 })
99 }
100
101 return toTextResult('Failed to get media group by title', true)
102 }
103
104 @Tool({
105 name: 'createMedia',
106 description: 'Create a new media resource for the authenticated user. Provide group ID, type, media URL, and optional thumbnail, title, and description.',
107 parameters: CreateMediaSchema,
108 })
109 async createMedia(params: z.infer<typeof CreateMediaSchema>) {
110 const user = getUser()
111 const result = await this.mediaService.create(user.id, {
112 groupId: params.groupId,
113 materialId: params.draftId,
114 type: params.type,
115 url: params.url,
116 thumbUrl: params.thumbUrl,
117 title: params.title,
118 desc: params.desc,
119 })
120 return toTextResult(`Media created successfully, ID: ${result.id}`)
121 }
122
123 @Tool({
124 name: 'getDraftGroupInfoByName',
125 description: 'Get the authenticated user\'s draft group by title. Returns matching group details, or default group if not found.',
126 parameters: GetGroupInfoByNameSchema,
127 })
128 async getMaterialGroupByName(params: z.infer<typeof GetGroupInfoByNameSchema>) {
129 const user = getUser()
130 const result = await this.materialGroupService.getInfoByName(user.id, params.title)
131 if (result) {
132 return toYamlTextResult(result)
133 }
134
135 const defaultGroup = await this.materialGroupService.getDefaultGroup(user.id)
136 if (defaultGroup) {
137 return toYamlTextResult({
138 ...defaultGroup,
139 isDefault: true,
140 })
141 }
142
143 return toTextResult('Failed to get Draft group by title', true)
144 }
145
146 @Tool({
147 name: 'createDraft',
148 description: 'Create a new draft for the authenticated user. Provide group ID, title, description, cover URL, media list, type, and options.',
149 parameters: CreateMaterialSchema,
150 })
151 async createMaterial(params: z.infer<typeof CreateMaterialSchema>) {
152 const user = getUser()
153 const result = await this.materialService.create({
154 userId: user.id,
155 userType: UserType.User,
156 type: params.type,
157 groupId: params.groupId,
158 coverUrl: params.coverUrl,
159 mediaList: params.mediaList,
160 title: params.title,
161 desc: params.desc,
162 topics: params.topics,
163 option: params.option,
164 autoDeleteMedia: false,
165 status: MaterialStatus.SUCCESS,
166 })
167 return toTextResult(`Draft created successfully, ID: ${result.id}`)
168 }
169
170 @Tool({
171 name: 'listDrafts',
172 description: 'List the authenticated user\'s drafts with pagination. Supports filtering by group ID and title.',
173 parameters: ListDraftsSchema,
174 })
175 async listDrafts(params: z.infer<typeof ListDraftsSchema>) {
176 const user = getUser()
177 const { list, total } = await this.materialService.getList(
178 { pageNo: params.pageNo, pageSize: params.pageSize },
179 { userId: user.id, userType: UserType.User, groupId: params.groupId, title: params.title },
180 )
181 return toYamlTextResult({
182 pageNo: params.pageNo,
183 pageSize: params.pageSize,
184 total,
185 list,
186 })
187 }
188
189 @Tool({
190 name: 'getDraftDetail',
191 description: 'Get detailed information of a single draft, including title, description, topics, and media list.',
192 parameters: GetDraftDetailSchema,
193 })
194 async getDraftDetail(params: z.infer<typeof GetDraftDetailSchema>) {
195 const user = getUser()
196 const material = await this.materialService.getInfo(params.draftId)
197 if (!material || material.userId !== user.id) {
198 return toTextResult('Draft not found.', true)
199 }
200 return toYamlTextResult(material)
201 }
202
203 @Tool({
204 name: 'deleteDraft',
205 description: 'Delete a single draft for the authenticated user.',
206 parameters: DeleteDraftSchema,
207 })
208 async deleteDraft(params: z.infer<typeof DeleteDraftSchema>) {
209 const user = getUser()
210 const material = await this.materialService.getInfo(params.draftId)
211 if (!material || material.userId !== user.id) {
212 return toTextResult('Draft not found.', true)
213 }
214 await this.materialService.del(params.draftId)
215 return toTextResult(`Draft deleted successfully, ID: ${params.draftId}`)
216 }
217
218 @Tool({
219 name: 'listMedia',
220 description: 'List the authenticated user\'s media resources with pagination. Supports filtering by group ID.',
221 parameters: ListMediaSchema,
222 })
223 async listMedia(params: z.infer<typeof ListMediaSchema>) {
224 const user = getUser()
225 const { list, total } = await this.mediaService.getList(
226 { pageNo: params.pageNo, pageSize: params.pageSize },
227 { userId: user.id, groupId: params.groupId },
228 )
229 return toYamlTextResult({
230 pageNo: params.pageNo,
231 pageSize: params.pageSize,
232 total,
233 list,
234 })
235 }
236
237 @Tool({
238 name: 'listDraftGroups',
239 description: 'List all draft groups for the authenticated user.',
240 parameters: ListGroupsSchema,
241 })
242 async listDraftGroups(params: z.infer<typeof ListGroupsSchema>) {
243 const user = getUser()
244 const { list, total } = await this.materialGroupService.getGroupList(
245 { pageNo: params.pageNo, pageSize: params.pageSize },
246 { userId: user.id },
247 )
248 return toYamlTextResult({
249 pageNo: params.pageNo,
250 pageSize: params.pageSize,
251 total,
252 list,
253 })
254 }
255
256 @Tool({
257 name: 'listMediaGroups',
258 description: 'List all media groups for the authenticated user.',
259 parameters: ListGroupsSchema,
260 })
261 async listMediaGroups(params: z.infer<typeof ListGroupsSchema>) {
262 const user = getUser()
263 const { list, total } = await this.mediaGroupService.getList(
264 { pageNo: params.pageNo, pageSize: params.pageSize },
265 { userId: user.id },
266 )
267 return toYamlTextResult({
268 pageNo: params.pageNo,
269 pageSize: params.pageSize,
270 total,
271 list,
272 })
273 }
274 }
275
275 lines TYPESCRIPT