mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-27 04:31:27 +08:00
feat(MCP): outputschema (#7881)
* feat(MCP): outputschema * fix: mark outputschema optional * fix: upgrade zod v4
This commit is contained in:
parent
eebed6d399
commit
d0649d29fb
@ -257,7 +257,8 @@
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
"word-extractor": "^1.0.4",
|
||||
"zipread": "^1.3.3"
|
||||
"zipread": "^1.3.3",
|
||||
"zod": "^3.25.74"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@cherrystudio/mac-system-ocr": "^0.2.2"
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
// inspired by https://dify.ai/blog/turn-your-dify-app-into-an-mcp-server
|
||||
import { loggerService } from '@logger'
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema } from '@modelcontextprotocol/sdk/types.js'
|
||||
import { z } from 'zod'
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema'
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
const logger = loggerService.withContext('DifyKnowledgeServer')
|
||||
|
||||
@ -39,10 +38,6 @@ interface DifySearchKnowledgeResponse {
|
||||
}>
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const ToolInputSchema = ToolSchema.shape.inputSchema
|
||||
type ToolInput = z.infer<typeof ToolInputSchema>
|
||||
|
||||
const SearchKnowledgeArgsSchema = z.object({
|
||||
id: z.string().describe('Knowledge ID'),
|
||||
query: z.string().describe('Query string'),
|
||||
@ -96,7 +91,7 @@ class DifyKnowledgeServer {
|
||||
{
|
||||
name: 'search_knowledge',
|
||||
description: 'Search knowledge by id and query',
|
||||
inputSchema: zodToJsonSchema(SearchKnowledgeArgsSchema) as ToolInput
|
||||
inputSchema: SearchKnowledgeArgsSchema
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -2,14 +2,13 @@
|
||||
|
||||
import { loggerService } from '@logger'
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema } from '@modelcontextprotocol/sdk/types.js'
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
||||
import { createTwoFilesPatch } from 'diff'
|
||||
import fs from 'fs/promises'
|
||||
import { minimatch } from 'minimatch'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { z } from 'zod'
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
const logger = loggerService.withContext('MCP:FileSystemServer')
|
||||
|
||||
@ -120,10 +119,6 @@ const GetFileInfoArgsSchema = z.object({
|
||||
path: z.string()
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const ToolInputSchema = ToolSchema.shape.inputSchema
|
||||
type ToolInput = z.infer<typeof ToolInputSchema>
|
||||
|
||||
interface FileInfo {
|
||||
size: number
|
||||
created: Date
|
||||
@ -345,7 +340,7 @@ class FileSystemServer {
|
||||
'Handles various text encodings and provides detailed error messages ' +
|
||||
'if the file cannot be read. Use this tool when you need to examine ' +
|
||||
'the contents of a single file. Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(ReadFileArgsSchema) as ToolInput
|
||||
inputSchema: ReadFileArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'read_multiple_files',
|
||||
@ -355,7 +350,7 @@ class FileSystemServer {
|
||||
"or compare multiple files. Each file's content is returned with its " +
|
||||
"path as a reference. Failed reads for individual files won't stop " +
|
||||
'the entire operation. Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema) as ToolInput
|
||||
inputSchema: ReadMultipleFilesArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'write_file',
|
||||
@ -363,7 +358,7 @@ class FileSystemServer {
|
||||
'Create a new file or completely overwrite an existing file with new content. ' +
|
||||
'Use with caution as it will overwrite existing files without warning. ' +
|
||||
'Handles text content with proper encoding. Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(WriteFileArgsSchema) as ToolInput
|
||||
inputSchema: WriteFileArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'edit_file',
|
||||
@ -371,7 +366,7 @@ class FileSystemServer {
|
||||
'Make line-based edits to a text file. Each edit replaces exact line sequences ' +
|
||||
'with new content. Returns a git-style diff showing the changes made. ' +
|
||||
'Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(EditFileArgsSchema) as ToolInput
|
||||
inputSchema: EditFileArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'create_directory',
|
||||
@ -380,7 +375,7 @@ class FileSystemServer {
|
||||
'nested directories in one operation. If the directory already exists, ' +
|
||||
'this operation will succeed silently. Perfect for setting up directory ' +
|
||||
'structures for projects or ensuring required paths exist. Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(CreateDirectoryArgsSchema) as ToolInput
|
||||
inputSchema: CreateDirectoryArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'list_directory',
|
||||
@ -389,7 +384,7 @@ class FileSystemServer {
|
||||
'Results clearly distinguish between files and directories with [FILE] and [DIR] ' +
|
||||
'prefixes. This tool is essential for understanding directory structure and ' +
|
||||
'finding specific files within a directory. Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(ListDirectoryArgsSchema) as ToolInput
|
||||
inputSchema: ListDirectoryArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'directory_tree',
|
||||
@ -398,7 +393,7 @@ class FileSystemServer {
|
||||
"Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " +
|
||||
'Files have no children array, while directories always have a children array (which may be empty). ' +
|
||||
'The output is formatted with 2-space indentation for readability. Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(DirectoryTreeArgsSchema) as ToolInput
|
||||
inputSchema: DirectoryTreeArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'move_file',
|
||||
@ -407,7 +402,7 @@ class FileSystemServer {
|
||||
'and rename them in a single operation. If the destination exists, the ' +
|
||||
'operation will fail. Works across different directories and can be used ' +
|
||||
'for simple renaming within the same directory. Both source and destination must be within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(MoveFileArgsSchema) as ToolInput
|
||||
inputSchema: MoveFileArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'search_files',
|
||||
@ -417,7 +412,7 @@ class FileSystemServer {
|
||||
'is case-insensitive and matches partial names. Returns full paths to all ' +
|
||||
"matching items. Great for finding files when you don't know their exact location. " +
|
||||
'Only searches within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(SearchFilesArgsSchema) as ToolInput
|
||||
inputSchema: SearchFilesArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'get_file_info',
|
||||
@ -426,7 +421,7 @@ class FileSystemServer {
|
||||
'information including size, creation time, last modified time, permissions, ' +
|
||||
'and type. This tool is perfect for understanding file characteristics ' +
|
||||
'without reading the actual content. Only works within allowed directories.',
|
||||
inputSchema: zodToJsonSchema(GetFileInfoArgsSchema) as ToolInput
|
||||
inputSchema: GetFileInfoArgsSchema
|
||||
},
|
||||
{
|
||||
name: 'list_allowed_directories',
|
||||
|
||||
@ -289,12 +289,6 @@
|
||||
"topics.unpinned": "Αποστέλλω",
|
||||
"translate": "Μετάφραση"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Κώδικας",
|
||||
"generating": "Δημιουργία",
|
||||
"preview": "Προεπισκόπηση",
|
||||
"split": "Διαχωρισμός"
|
||||
},
|
||||
"code_block": {
|
||||
"collapse": "συμπεριληφθείς",
|
||||
"disable_wrap": "ακύρωση αλλαγής γραμμής",
|
||||
@ -428,6 +422,12 @@
|
||||
"search.topics.empty": "Δεν βρέθηκαν σχετικά θέματα, πατήστε Enter για να αναζητήσετε όλα τα μηνύματα",
|
||||
"title": "Αναζήτηση θεμάτων"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Κώδικας",
|
||||
"generating": "Δημιουργία",
|
||||
"preview": "Προεπισκόπηση",
|
||||
"split": "Διαχωρισμός"
|
||||
},
|
||||
"knowledge": {
|
||||
"add": {
|
||||
"title": "Προσθήκη βιβλιοθήκης γνώσεων"
|
||||
@ -645,9 +645,9 @@
|
||||
"close": "Κλείσιμο της εφαρμογής",
|
||||
"devtools": "Εργαλεία προγραμματιστή",
|
||||
"minimize": "Ελαχιστοποίηση της εφαρμογής",
|
||||
"openExternal": "Άνοιγμα στον περιηγητή",
|
||||
"open_link_external_off": "Τρέχον: Άνοιγμα συνδέσμου χρησιμοποιώντας το προεπιλεγμένο παράθυρο",
|
||||
"open_link_external_on": "Τρέχον: Άνοιγμα συνδέσμου στον περιηγητή",
|
||||
"openExternal": "Άνοιγμα στον περιηγητή",
|
||||
"refresh": "Ανανέωση",
|
||||
"rightclick_copyurl": "Αντιγραφή URL με δεξί κλικ"
|
||||
},
|
||||
@ -905,9 +905,9 @@
|
||||
},
|
||||
"settings": {
|
||||
"about": "Περί μας",
|
||||
"about.checkingUpdate": "Ελέγχω ενημερώσεις...",
|
||||
"about.checkUpdate": "Έλεγχος ενημερώσεων",
|
||||
"about.checkUpdate.available": "Άμεση ενημέρωση",
|
||||
"about.checkingUpdate": "Ελέγχω ενημερώσεις...",
|
||||
"about.contact.button": "Ταχυδρομείο",
|
||||
"about.contact.title": "Επικοινωνία μέσω ταχυδρομείου",
|
||||
"about.description": "Ένα AI ασιστάντα που έχει σχεδιαστεί για δημιουργούς",
|
||||
|
||||
@ -290,12 +290,6 @@
|
||||
"topics.unpinned": "Quitar fijación",
|
||||
"translate": "Traducir"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Código",
|
||||
"generating": "Generando",
|
||||
"preview": "Vista previa",
|
||||
"split": "Dividir"
|
||||
},
|
||||
"code_block": {
|
||||
"collapse": "Replegar",
|
||||
"disable_wrap": "Deshabilitar salto de línea",
|
||||
@ -429,6 +423,12 @@
|
||||
"search.topics.empty": "No se encontraron temas relacionados, presione Enter para buscar todos los mensajes",
|
||||
"title": "Búsqueda de temas"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Código",
|
||||
"generating": "Generando",
|
||||
"preview": "Vista previa",
|
||||
"split": "Dividir"
|
||||
},
|
||||
"knowledge": {
|
||||
"add": {
|
||||
"title": "Agregar base de conocimientos"
|
||||
@ -646,9 +646,9 @@
|
||||
"close": "Cerrar la aplicación",
|
||||
"devtools": "Herramientas de desarrollo",
|
||||
"minimize": "Minimizar la aplicación",
|
||||
"openExternal": "Abrir en el navegador",
|
||||
"open_link_external_off": "Actual: Abrir enlaces en ventana predeterminada",
|
||||
"open_link_external_on": "Actual: Abrir enlaces en el navegador",
|
||||
"openExternal": "Abrir en el navegador",
|
||||
"refresh": "Actualizar",
|
||||
"rightclick_copyurl": "Copiar URL con clic derecho"
|
||||
},
|
||||
@ -906,9 +906,9 @@
|
||||
},
|
||||
"settings": {
|
||||
"about": "Acerca de nosotros",
|
||||
"about.checkingUpdate": "Verificando actualizaciones...",
|
||||
"about.checkUpdate": "Comprobar actualizaciones",
|
||||
"about.checkUpdate.available": "Actualizar ahora",
|
||||
"about.checkingUpdate": "Verificando actualizaciones...",
|
||||
"about.contact.button": "Correo electrónico",
|
||||
"about.contact.title": "Contacto por correo electrónico",
|
||||
"about.description": "Una asistente de IA creada para los creadores",
|
||||
|
||||
@ -289,12 +289,6 @@
|
||||
"topics.unpinned": "Annuler le fixage",
|
||||
"translate": "Traduire"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Code",
|
||||
"generating": "Génération",
|
||||
"preview": "Aperçu",
|
||||
"split": "Diviser"
|
||||
},
|
||||
"code_block": {
|
||||
"collapse": "Réduire",
|
||||
"disable_wrap": "Désactiver le retour à la ligne",
|
||||
@ -428,6 +422,12 @@
|
||||
"search.topics.empty": "Aucun sujet correspondant trouvé, appuyez sur Entrée pour rechercher tous les messages",
|
||||
"title": "Recherche de sujets"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Code",
|
||||
"generating": "Génération",
|
||||
"preview": "Aperçu",
|
||||
"split": "Diviser"
|
||||
},
|
||||
"knowledge": {
|
||||
"add": {
|
||||
"title": "Ajouter une base de connaissances"
|
||||
@ -645,9 +645,9 @@
|
||||
"close": "Закрыть мини-программу",
|
||||
"devtools": "Инструменты разработчика",
|
||||
"minimize": "Свернуть мини-программу",
|
||||
"openExternal": "Открыть в браузере",
|
||||
"open_link_external_off": "Текущий: открывать ссылки в окне по умолчанию",
|
||||
"open_link_external_on": "Текущий: открывать ссылки в браузере",
|
||||
"openExternal": "Открыть в браузере",
|
||||
"refresh": "Обновить",
|
||||
"rightclick_copyurl": "Скопировать URL через правую кнопку мыши"
|
||||
},
|
||||
@ -905,9 +905,9 @@
|
||||
},
|
||||
"settings": {
|
||||
"about": "À propos de nous",
|
||||
"about.checkingUpdate": "Vérification des mises à jour en cours...",
|
||||
"about.checkUpdate": "Vérifier les mises à jour",
|
||||
"about.checkUpdate.available": "Mettre à jour maintenant",
|
||||
"about.checkingUpdate": "Vérification des mises à jour en cours...",
|
||||
"about.contact.button": "Courriel",
|
||||
"about.contact.title": "Contactez-nous par courriel",
|
||||
"about.description": "Un assistant IA conçu pour les créateurs",
|
||||
|
||||
@ -291,12 +291,6 @@
|
||||
"topics.unpinned": "Desfixar",
|
||||
"translate": "Traduzir"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Código",
|
||||
"generating": "Gerando",
|
||||
"preview": "Visualizar",
|
||||
"split": "Dividir"
|
||||
},
|
||||
"code_block": {
|
||||
"collapse": "Recolher",
|
||||
"disable_wrap": "Desativar quebra de linha",
|
||||
@ -430,6 +424,12 @@
|
||||
"search.topics.empty": "Nenhum tópico relacionado encontrado, clique em Enter para procurar todas as mensagens",
|
||||
"title": "Procurar Tópicos"
|
||||
},
|
||||
"html_artifacts": {
|
||||
"code": "Código",
|
||||
"generating": "Gerando",
|
||||
"preview": "Visualizar",
|
||||
"split": "Dividir"
|
||||
},
|
||||
"knowledge": {
|
||||
"add": {
|
||||
"title": "Adicionar Base de Conhecimento"
|
||||
@ -647,9 +647,9 @@
|
||||
"close": "Fechar aplicativo",
|
||||
"devtools": "Ferramentas de Desenvolvedor",
|
||||
"minimize": "Minimizar aplicativo",
|
||||
"openExternal": "Abrir no navegador",
|
||||
"open_link_external_off": "Atual: Abrir links em janela padrão",
|
||||
"open_link_external_on": "Atual: Abrir links no navegador",
|
||||
"openExternal": "Abrir no navegador",
|
||||
"refresh": "Atualizar",
|
||||
"rightclick_copyurl": "Copiar URL com botão direito"
|
||||
},
|
||||
@ -906,9 +906,9 @@
|
||||
},
|
||||
"settings": {
|
||||
"about": "Sobre Nós",
|
||||
"about.checkingUpdate": "Verificando atualizações...",
|
||||
"about.checkUpdate": "Verificar atualizações",
|
||||
"about.checkUpdate.available": "Atualizar agora",
|
||||
"about.checkingUpdate": "Verificando atualizações...",
|
||||
"about.contact.button": "E-mail",
|
||||
"about.contact.title": "Contato por e-mail",
|
||||
"about.description": "Um assistente de IA criado para criadores",
|
||||
|
||||
@ -2,6 +2,7 @@ import type { WebSearchResultBlock } from '@anthropic-ai/sdk/resources'
|
||||
import type { GenerateImagesConfig, GroundingMetadata, PersonGeneration } from '@google/genai'
|
||||
import type OpenAI from 'openai'
|
||||
import type { CSSProperties } from 'react'
|
||||
import * as z from 'zod/v4'
|
||||
|
||||
export * from './file'
|
||||
import type { FileMetadata } from './file'
|
||||
@ -657,6 +658,12 @@ export interface MCPToolInputSchema {
|
||||
properties: Record<string, object>
|
||||
}
|
||||
|
||||
export const MCPToolOutputSchema = z.object({
|
||||
type: z.literal('object'),
|
||||
properties: z.record(z.string(), z.unknown()),
|
||||
required: z.array(z.string())
|
||||
})
|
||||
|
||||
export interface MCPTool {
|
||||
id: string
|
||||
serverId: string
|
||||
@ -664,6 +671,7 @@ export interface MCPTool {
|
||||
name: string
|
||||
description?: string
|
||||
inputSchema: MCPToolInputSchema
|
||||
outputSchema?: z.infer<typeof MCPToolOutputSchema>
|
||||
}
|
||||
|
||||
export interface MCPPromptArguments {
|
||||
|
||||
@ -7307,6 +7307,7 @@ __metadata:
|
||||
winston-daily-rotate-file: "npm:^5.0.0"
|
||||
word-extractor: "npm:^1.0.4"
|
||||
zipread: "npm:^1.3.3"
|
||||
zod: "npm:^3.25.74"
|
||||
dependenciesMeta:
|
||||
"@cherrystudio/mac-system-ocr":
|
||||
optional: true
|
||||
@ -21068,6 +21069,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.25.74":
|
||||
version: 3.25.74
|
||||
resolution: "zod@npm:3.25.74"
|
||||
checksum: 10c0/59e38b046ac333b5bd1ba325a83b6798721227cbfb1e69dfc7159bd7824b904241ab923026edb714fafefec3624265ae374a70aee9a5a45b365bd31781ffa105
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zustand@npm:^4.4.0":
|
||||
version: 4.5.6
|
||||
resolution: "zustand@npm:4.5.6"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user