| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- const API = require('../../../lib/API')
- const MCP = require('../../../lib/AIChat/AiAssistantMcp').AI_ASSISTANT_MCP
- const sender = {
- type: 'string',
- description: 'RunForge user uuid. Required. Only data owned by this user may be read or changed.'
- }
- const pageProps = {
- current: { type: 'integer', minimum: 1, description: 'Page number, default 1' },
- pagesize: { type: 'integer', minimum: 1, maximum: 20, description: 'Page size, default 10, max 20' }
- }
- const tools = [
- {
- name: 'list_lepao_accounts',
- description: 'Query the user owned Lepao account list with optional filters. Never returns internal auto-increment ids.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- student_num: { type: 'string' },
- name: { type: 'string' },
- area: { type: 'string' },
- state: { type: 'integer', description: '-1 all, 0 needs login, 1 normal, 2 abnormal' },
- auto_run: { type: 'integer', enum: [0, 1] },
- ...pageProps
- },
- required: ['sender']
- }
- },
- {
- name: 'list_lepao_records',
- description: 'Query Lepao run records owned by the user with account, name, result and time filters.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- lepao_account: { type: 'string' },
- name: { type: 'string' },
- result: { type: 'string' },
- start_time: { type: 'integer', description: 'Start timestamp in ms' },
- end_time: { type: 'integer', description: 'End timestamp in ms' },
- ...pageProps
- },
- required: ['sender']
- }
- },
- {
- name: 'list_lepao_count_ledger',
- description: 'Query Lepao count change ledger for the user. Never returns internal auto-increment ids.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- biz_type: { type: 'string' },
- remark: { type: 'string' },
- start_time: { type: 'integer', description: 'Start timestamp in ms' },
- end_time: { type: 'integer', description: 'End timestamp in ms' },
- ...pageProps
- },
- required: ['sender']
- }
- },
- {
- name: 'list_goods',
- description: 'Query active goods list with optional keyword and category filters. Never returns internal auto-increment ids, icon paths, or other unnecessary media fields.',
- inputSchema: {
- type: 'object',
- properties: {
- keyword: { type: 'string' },
- category: { type: 'string' },
- ...pageProps
- }
- }
- },
- {
- name: 'list_my_orders',
- description: 'Query the user order list and order status. Uses order_no instead of internal ids.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- state: { type: 'integer', description: '-1 all, 0 pending payment, 1 paid, 2 cancelled, 3 abnormal, 4 refunded' },
- order_id: { type: 'string', description: 'Public order number' },
- ...pageProps
- },
- required: ['sender']
- }
- },
- {
- name: 'save_lepao_account',
- description: 'Add or partially update a Lepao account owned by the user. student_num identifies the account; provide only fields that should change. Example: to change run area, provide sender, student_num, area only.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- student_num: { type: 'string' },
- email: { type: 'string' },
- area: { type: 'string', description: 'Supported examples: 兰花湖校区跑区, 主校区北跑区, 主校区南跑区, 重庆工商大学茶园校区, 随机分配.' },
- auto_time: { type: 'integer', minimum: -1, maximum: 23 },
- auto_run: { type: 'integer', enum: [0, 1] },
- target_count: { type: 'number' },
- auto_day: { type: 'array', items: { type: 'integer', minimum: 0, maximum: 6 } },
- notice_type: { type: 'string', enum: ['email', 'none', 'bot'] },
- notes: { type: 'string' }
- },
- required: ['sender', 'student_num']
- }
- },
- {
- name: 'save_power_task',
- description: 'Add or partially update a power query task owned by the user. Existing tasks can be identified by task_id or by area + building + room; provide only fields that should change.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- task_id: { type: 'integer', description: 'Existing task id for compatibility. Prefer area + building + room when possible.' },
- area: { type: 'string' },
- building: { type: 'string' },
- room: { type: 'string' },
- email: { type: 'string' },
- lowest: { type: 'number' },
- notes: { type: 'string' }
- },
- required: ['sender']
- }
- },
- {
- name: 'list_power_bills',
- description: 'Query power tasks or one task bill/change records owned by the user. Results never expose internal task or record ids.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- task_id: { type: 'integer', description: 'Compatibility id filter when caller already has it.' },
- area: { type: 'string' },
- building: { type: 'string' },
- room: { type: 'string' },
- ...pageProps
- },
- required: ['sender']
- }
- },
- {
- name: 'query_qxs_book_list',
- description: 'Query Quxuanshu textbook list with user provided Quxuanshu credentials.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- username: { type: 'string' },
- password: { type: 'string' },
- ...pageProps
- },
- required: ['sender', 'username', 'password']
- }
- },
- {
- name: 'submit_work_order',
- description: 'Submit a support ticket for the current user. Returns no internal auto-increment id; the user can view progress in the ticket list.',
- inputSchema: {
- type: 'object',
- properties: {
- sender,
- title: { type: 'string', maxLength: 80 },
- content: { type: 'string', maxLength: 4000 },
- email: { type: 'string' },
- files: { type: 'array', items: { type: 'string' }, maxItems: 6 }
- },
- required: ['sender', 'title', 'content']
- }
- }
- ]
- class McpRpc extends API {
- constructor() {
- super()
- this.noEncrypt()
- this.setPath('/aiAssistantMcp')
- this.setMethod('POST')
- }
- async onRequest(req, res) {
- const { method, params = {}, id: reqId } = req.body || {}
- try {
- let result
- if (method === 'initialize') {
- result = {
- protocolVersion: '2024-11-05',
- capabilities: { tools: {} },
- serverInfo: { name: 'runforgeAiAssistant', version: '1.0' }
- }
- } else if (method === 'tools/list') {
- result = { tools }
- } else if (method === 'tools/call') {
- const { name, arguments: args = {} } = params
- if (!MCP[name] || typeof MCP[name] !== 'function') {
- result = { content: [{ type: 'text', text: '未知工具' }] }
- } else {
- const output = await MCP[name](args)
- result = { content: [{ type: 'text', text: output }] }
- }
- } else {
- result = { error: `未知方法: ${method}` }
- }
- return res.json({ jsonrpc: '2.0', id: reqId ?? null, result })
- } catch (err) {
- return res.json({
- jsonrpc: '2.0',
- id: reqId ?? null,
- error: { code: -32000, message: err.message }
- })
- }
- }
- }
- module.exports.McpRpc = McpRpc
|