Spaces:
Configuration error
Configuration error
Delete FastMCPToolGenerator
Browse files- FastMCPToolGenerator/.gitattributes +0 -35
- FastMCPToolGenerator/.gitignore +0 -63
- FastMCPToolGenerator/App.tsx +0 -283
- FastMCPToolGenerator/Dockerfile +0 -77
- FastMCPToolGenerator/GEMINI.md +0 -50
- FastMCPToolGenerator/README0.md +0 -20
- FastMCPToolGenerator/backend/__pycache__/main.cpython-313.pyc +0 -0
- FastMCPToolGenerator/backend/main.py +0 -164
- FastMCPToolGenerator/components/AiPrompt.tsx +0 -52
- FastMCPToolGenerator/components/CodeOutput.tsx +0 -340
- FastMCPToolGenerator/components/FunctionEditor.tsx +0 -146
- FastMCPToolGenerator/components/ToolForm.tsx +0 -115
- FastMCPToolGenerator/components/ui/Button.tsx +0 -28
- FastMCPToolGenerator/components/ui/Card.tsx +0 -21
- FastMCPToolGenerator/components/ui/Input.tsx +0 -19
- FastMCPToolGenerator/components/ui/Label.tsx +0 -16
- FastMCPToolGenerator/components/ui/Select.tsx +0 -28
- FastMCPToolGenerator/components/ui/Textarea.tsx +0 -20
- FastMCPToolGenerator/constants.ts +0 -18
- FastMCPToolGenerator/dist/assets/index-CjZgBbLF.js +0 -0
- FastMCPToolGenerator/dist/index.html +0 -46
- FastMCPToolGenerator/examples.ts +0 -60
- FastMCPToolGenerator/index.html +0 -45
- FastMCPToolGenerator/index.tsx +0 -20
- FastMCPToolGenerator/metadata.json +0 -7
- FastMCPToolGenerator/package-lock.json +0 -0
- FastMCPToolGenerator/package.json +0 -42
- FastMCPToolGenerator/requirements.txt +0 -11
- FastMCPToolGenerator/services/aiService.ts +0 -71
- FastMCPToolGenerator/services/codeGenerator.ts +0 -428
- FastMCPToolGenerator/style.css +0 -28
- FastMCPToolGenerator/tsconfig.json +0 -32
- FastMCPToolGenerator/types.ts +0 -72
- FastMCPToolGenerator/vite.config.ts +0 -72
FastMCPToolGenerator/.gitattributes
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/.gitignore
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
# Logs
|
| 3 |
-
|
| 4 |
-
logs
|
| 5 |
-
|
| 6 |
-
*.log
|
| 7 |
-
|
| 8 |
-
npm-debug.log*
|
| 9 |
-
|
| 10 |
-
yarn-debug.log*
|
| 11 |
-
|
| 12 |
-
yarn-error.log*
|
| 13 |
-
|
| 14 |
-
pnpm-debug.log*
|
| 15 |
-
|
| 16 |
-
lerna-debug.log*
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
node_modules
|
| 21 |
-
|
| 22 |
-
dist
|
| 23 |
-
|
| 24 |
-
dist-ssr
|
| 25 |
-
|
| 26 |
-
*.local
|
| 27 |
-
|
| 28 |
-
.env
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
# Editor directories and files
|
| 33 |
-
|
| 34 |
-
.vscode/*
|
| 35 |
-
|
| 36 |
-
!.vscode/extensions.json
|
| 37 |
-
|
| 38 |
-
.idea
|
| 39 |
-
|
| 40 |
-
.DS_Store
|
| 41 |
-
|
| 42 |
-
*.suo
|
| 43 |
-
|
| 44 |
-
*.ntvs*
|
| 45 |
-
|
| 46 |
-
*.njsproj
|
| 47 |
-
|
| 48 |
-
*.sln
|
| 49 |
-
|
| 50 |
-
*.sw?
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
# Python
|
| 55 |
-
|
| 56 |
-
__pycache__/
|
| 57 |
-
|
| 58 |
-
*.pyc
|
| 59 |
-
|
| 60 |
-
.venv
|
| 61 |
-
|
| 62 |
-
venv/
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/App.tsx
DELETED
|
@@ -1,283 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React, { useState, useMemo } from 'react';
|
| 3 |
-
|
| 4 |
-
import { ToolDefinition, GeneratedCode, ToolType, FunctionDefinition, ParameterType } from './types';
|
| 5 |
-
|
| 6 |
-
import ToolForm from './components/ToolForm';
|
| 7 |
-
|
| 8 |
-
import CodeOutput from './components/CodeOutput';
|
| 9 |
-
|
| 10 |
-
import { generateAllCode } from './services/codeGenerator';
|
| 11 |
-
|
| 12 |
-
import { nmapScannerExample } from './examples';
|
| 13 |
-
|
| 14 |
-
import Button from './components/ui/Button';
|
| 15 |
-
|
| 16 |
-
import AiPrompt from './components/AiPrompt';
|
| 17 |
-
|
| 18 |
-
import { generateToolDefinitionFromPrompt } from './services/aiService';
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
const initialFunction: FunctionDefinition = {
|
| 23 |
-
|
| 24 |
-
id: crypto.randomUUID(),
|
| 25 |
-
|
| 26 |
-
name: 'example_function',
|
| 27 |
-
|
| 28 |
-
description: 'Краткое описание того, что делает эта функция.',
|
| 29 |
-
|
| 30 |
-
parameters: [
|
| 31 |
-
|
| 32 |
-
{
|
| 33 |
-
|
| 34 |
-
id: crypto.randomUUID(),
|
| 35 |
-
|
| 36 |
-
name: 'example_param',
|
| 37 |
-
|
| 38 |
-
type: ParameterType.STRING, // Исправлено с 'str' as any
|
| 39 |
-
|
| 40 |
-
description: 'Описание параметра.',
|
| 41 |
-
|
| 42 |
-
required: true,
|
| 43 |
-
|
| 44 |
-
},
|
| 45 |
-
|
| 46 |
-
],
|
| 47 |
-
|
| 48 |
-
returnType: ParameterType.STRING, // Исправлено с 'str' as any
|
| 49 |
-
|
| 50 |
-
returnDescription: 'Описание возвращаемого значения.',
|
| 51 |
-
|
| 52 |
-
};
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
const initialToolDefinition: ToolDefinition = {
|
| 57 |
-
|
| 58 |
-
name: 'my_tool',
|
| 59 |
-
|
| 60 |
-
type: ToolType.UTILITY,
|
| 61 |
-
|
| 62 |
-
dependencies: '',
|
| 63 |
-
|
| 64 |
-
functions: [initialFunction],
|
| 65 |
-
|
| 66 |
-
};
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
const App: React.FC = () => {
|
| 71 |
-
|
| 72 |
-
const [toolDefinition, setToolDefinition] = useState<ToolDefinition>(initialToolDefinition);
|
| 73 |
-
|
| 74 |
-
const [aiPrompt, setAiPrompt] = useState<string>('');
|
| 75 |
-
|
| 76 |
-
const [isGenerating, setIsGenerating] = useState<boolean>(false);
|
| 77 |
-
|
| 78 |
-
const [aiError, setAiError] = useState<string | null>(null);
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
const generatedCode = useMemo<GeneratedCode | null>(() => {
|
| 83 |
-
|
| 84 |
-
try {
|
| 85 |
-
|
| 86 |
-
if (toolDefinition.name && toolDefinition.functions.length > 0 && toolDefinition.functions.every(f => f.name)) {
|
| 87 |
-
|
| 88 |
-
return generateAllCode(toolDefinition);
|
| 89 |
-
|
| 90 |
-
}
|
| 91 |
-
|
| 92 |
-
return null;
|
| 93 |
-
|
| 94 |
-
} catch (error) {
|
| 95 |
-
|
| 96 |
-
console.error("Error generating code:", error);
|
| 97 |
-
|
| 98 |
-
return null;
|
| 99 |
-
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
}, [toolDefinition]);
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
const loadNmapExample = () => {
|
| 107 |
-
|
| 108 |
-
setToolDefinition(nmapScannerExample);
|
| 109 |
-
|
| 110 |
-
};
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
const handleAiGenerate = async () => {
|
| 115 |
-
|
| 116 |
-
if (!aiPrompt.trim()) {
|
| 117 |
-
|
| 118 |
-
setAiError("Пожалуйста, введите описание инструмента.");
|
| 119 |
-
|
| 120 |
-
return;
|
| 121 |
-
|
| 122 |
-
}
|
| 123 |
-
|
| 124 |
-
setIsGenerating(true);
|
| 125 |
-
|
| 126 |
-
setAiError(null);
|
| 127 |
-
|
| 128 |
-
try {
|
| 129 |
-
|
| 130 |
-
const jsonString = await generateToolDefinitionFromPrompt(aiPrompt);
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
// Бэкенд уже должен был очистить markdown, но на всякий случай
|
| 135 |
-
|
| 136 |
-
const cleanedJsonString = jsonString.replace(/^```json\s*|\s*```$/g, '').trim();
|
| 137 |
-
|
| 138 |
-
const newToolDefinition = JSON.parse(cleanedJsonString);
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
// Добавим уникальные ID, так как AI их не генерирует
|
| 143 |
-
|
| 144 |
-
if (newToolDefinition.functions) {
|
| 145 |
-
|
| 146 |
-
newToolDefinition.functions.forEach((func: FunctionDefinition) => {
|
| 147 |
-
|
| 148 |
-
func.id = crypto.randomUUID();
|
| 149 |
-
|
| 150 |
-
if (func.parameters) {
|
| 151 |
-
|
| 152 |
-
func.parameters.forEach((param: any) => {
|
| 153 |
-
|
| 154 |
-
param.id = crypto.randomUUID();
|
| 155 |
-
|
| 156 |
-
});
|
| 157 |
-
|
| 158 |
-
} else {
|
| 159 |
-
|
| 160 |
-
func.parameters = []; // Убедимся, что параметры существуют
|
| 161 |
-
|
| 162 |
-
}
|
| 163 |
-
|
| 164 |
-
});
|
| 165 |
-
|
| 166 |
-
} else {
|
| 167 |
-
|
| 168 |
-
newToolDefinition.functions = []; // Убедимся, что функции существуют
|
| 169 |
-
|
| 170 |
-
}
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
setToolDefinition(newToolDefinition);
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
} catch (error) {
|
| 179 |
-
|
| 180 |
-
console.error("AI generation failed:", error);
|
| 181 |
-
|
| 182 |
-
setAiError(`Не удалось сгенерировать инструмент: ${error.message}. Проверьте консоль.`);
|
| 183 |
-
|
| 184 |
-
} finally {
|
| 185 |
-
|
| 186 |
-
setIsGenerating(false);
|
| 187 |
-
|
| 188 |
-
}
|
| 189 |
-
|
| 190 |
-
};
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
return (
|
| 195 |
-
|
| 196 |
-
<div className="min-h-screen bg-gray-900 text-gray-100 p-4 sm:p-6 lg:p-8">
|
| 197 |
-
|
| 198 |
-
<div className="max-w-screen-2xl mx-auto">
|
| 199 |
-
|
| 200 |
-
<header className="mb-8">
|
| 201 |
-
|
| 202 |
-
<div className="text-center">
|
| 203 |
-
|
| 204 |
-
<h1 className="text-4xl sm:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-sky-400 to-blue-600">
|
| 205 |
-
|
| 206 |
-
Gemini CLI Tool Generator
|
| 207 |
-
|
| 208 |
-
</h1>
|
| 209 |
-
|
| 210 |
-
<p className="mt-2 text-lg text-gray-400">
|
| 211 |
-
|
| 212 |
-
Создайте кастомные расширения, которые Gemini сможет вызывать для решения ваших задач.
|
| 213 |
-
|
| 214 |
-
</p>
|
| 215 |
-
|
| 216 |
-
</div>
|
| 217 |
-
|
| 218 |
-
<div className="text-center mt-4">
|
| 219 |
-
|
| 220 |
-
<Button onClick={loadNmapExample} variant="secondary">
|
| 221 |
-
|
| 222 |
-
Загрузить пример: Nmap Scanner
|
| 223 |
-
|
| 224 |
-
</Button>
|
| 225 |
-
|
| 226 |
-
</div>
|
| 227 |
-
|
| 228 |
-
</header>
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
<AiPrompt
|
| 233 |
-
|
| 234 |
-
prompt={aiPrompt}
|
| 235 |
-
|
| 236 |
-
setPrompt={setAiPrompt}
|
| 237 |
-
|
| 238 |
-
onGenerate={handleAiGenerate}
|
| 239 |
-
|
| 240 |
-
isLoading={isGenerating}
|
| 241 |
-
|
| 242 |
-
error={aiError}
|
| 243 |
-
|
| 244 |
-
/>
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
<main className="grid grid-cols-1 xl:grid-cols-2 gap-8 mt-8">
|
| 249 |
-
|
| 250 |
-
<ToolForm toolDefinition={toolDefinition} setToolDefinition={setToolDefinition} />
|
| 251 |
-
|
| 252 |
-
<CodeOutput generatedCode={generatedCode} toolName={toolDefinition.name} />
|
| 253 |
-
|
| 254 |
-
</main>
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
<footer className="text-center mt-12 py-4 border-t border-gray-700">
|
| 259 |
-
|
| 260 |
-
<p className="text-gray-500">
|
| 261 |
-
|
| 262 |
-
Разработано с использованием React, Tailwind CSS и Gemini.
|
| 263 |
-
|
| 264 |
-
</p>
|
| 265 |
-
|
| 266 |
-
</footer>
|
| 267 |
-
|
| 268 |
-
</div>
|
| 269 |
-
|
| 270 |
-
</div>
|
| 271 |
-
|
| 272 |
-
);
|
| 273 |
-
|
| 274 |
-
};
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
export default App;
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/Dockerfile
DELETED
|
@@ -1,77 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
# Этап 1: Сборка React-фронтенда
|
| 3 |
-
|
| 4 |
-
FROM node:18-slim AS build
|
| 5 |
-
|
| 6 |
-
WORKDIR /app
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
# Копируем package.json и устанавливаем npm зависимости
|
| 11 |
-
|
| 12 |
-
COPY package.json package-lock.json ./
|
| 13 |
-
|
| 14 |
-
RUN npm install
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
# Копируем остальной код фронтенда
|
| 19 |
-
|
| 20 |
-
COPY . .
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
# Собираем production build (создает папку 'dist')
|
| 25 |
-
|
| 26 |
-
RUN npm run build
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
# Этап 2: Настройка Python-сервера (FastAPI)
|
| 31 |
-
|
| 32 |
-
FROM python:3.10-slim
|
| 33 |
-
|
| 34 |
-
WORKDIR /app
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
# Установка переменных окружения
|
| 39 |
-
|
| 40 |
-
ENV PYTHONUNBUFFERED 1
|
| 41 |
-
|
| 42 |
-
ENV PYTHONDONTWRITEBYTECODE 1
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
# Копируем Python-зависимости и устанавливаем их
|
| 47 |
-
|
| 48 |
-
COPY requirements.txt .
|
| 49 |
-
|
| 50 |
-
RUN pip install --no-cache-dir -r requirements.txt
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
# Копируем код бэкенда (FastAPI)
|
| 55 |
-
|
| 56 |
-
COPY ./backend ./backend
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
# Копируем собранный фронтенд из Этапа 1
|
| 61 |
-
|
| 62 |
-
COPY --from=build /app/dist ./dist
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
# Порт 7860 - стандартный для HF Spaces
|
| 67 |
-
|
| 68 |
-
EXPOSE 7860
|
| 69 |
-
|
| 70 |
-
ENV PORT=7860
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
# Команда для запуска Uvicorn (FastAPI)
|
| 75 |
-
|
| 76 |
-
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/GEMINI.md
DELETED
|
@@ -1,50 +0,0 @@
|
|
| 1 |
-
# Инструкция для Агента: Инструмент "nmap_scanner"
|
| 2 |
-
|
| 3 |
-
## Описание
|
| 4 |
-
|
| 5 |
-
Этот инструмент предоставляет прямой доступ к сетевому сканеру Nmap для проведения разведки и обнаружения хостов в сети. Он относится к типу **UTILITY**. Используй его для анализа сетевой безопасности, инвентаризации устройств и проверки открытых портов.
|
| 6 |
-
|
| 7 |
-
## Функции
|
| 8 |
-
|
| 9 |
-
### `nmap_scanner:run_scan`
|
| 10 |
-
|
| 11 |
-
Запускает детальное сканирование Nmap на указанные цели. Позволяет гибко настраивать тип сканирования, определять службы и версии ПО.
|
| 12 |
-
|
| 13 |
-
**Параметры:**
|
| 14 |
-
- `targets` (*str*): Цели для сканирования (IP-адрес, домен, CIDR-диапазон). Например: '192.168.1.1', 'scanme.nmap.org', '10.0.0.0/24'. (обязательный)
|
| 15 |
-
- `ports` (*str*): Порты для сканирования. Например: '21-25,80,443', '1-1024'. (обязательный)
|
| 16 |
-
- `arguments` (*str*): Дополнительные аргументы командной строки Nmap. Например: '-sV -sC' для определения версий и запуска скриптов, или '-A' для агрессивного сканирования. (обязательный)
|
| 17 |
-
|
| 18 |
-
**Возвращает:**
|
| 19 |
-
*dict* - Результат сканирования в формате JSON, содержащий информацию о хостах, их состоянии, открытых портах, службах и версиях.
|
| 20 |
-
|
| 21 |
-
---
|
| 22 |
-
|
| 23 |
-
### `nmap_scanner:host_discovery`
|
| 24 |
-
|
| 25 |
-
Быстро обнаруживает активные (включенные) хосты в указанной сети с помощью 'ping scan' (`-sn`), не проводя полного сканирования портов.
|
| 26 |
-
|
| 27 |
-
**Параметры:**
|
| 28 |
-
- `network_cidr` (*str*): Сетевой диапазон в формате CIDR для обнаружения хостов. Например: '192.168.1.0/24'. (обязательный)
|
| 29 |
-
|
| 30 |
-
**Возвращает:**
|
| 31 |
-
*list* - Список IP-адресов активных хостов, обнаруженных в сети.
|
| 32 |
-
|
| 33 |
-
## Примеры использования
|
| 34 |
-
|
| 35 |
-
**ВАЖНО:** Всегда используй формат `nmap_scanner:имя_функции()`.
|
| 36 |
-
|
| 37 |
-
**Пример 1: Обнаружение всех активных устройств в локальной сети**
|
| 38 |
-
```
|
| 39 |
-
nmap_scanner:host_discovery(network_cidr="192.168.0.0/24")
|
| 40 |
-
```
|
| 41 |
-
|
| 42 |
-
**Пример 2: Быстрое сканирование популярных портов на конкретном хосте**
|
| 43 |
-
```
|
| 44 |
-
nmap_scanner:run_scan(targets="192.168.1.1", ports="1-1024", arguments="-T4")
|
| 45 |
-
```
|
| 46 |
-
|
| 47 |
-
**Пример 3: Агрессивное сканирование веб-сервера для определения ОС, служб и уязвимостей**
|
| 48 |
-
```
|
| 49 |
-
nmap_scanner:run_scan(targets="scanme.nmap.org", ports="80,443", arguments="-A -v")
|
| 50 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/README0.md
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
<div align="center">
|
| 2 |
-
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
| 3 |
-
</div>
|
| 4 |
-
|
| 5 |
-
# Run and deploy your AI Studio app
|
| 6 |
-
|
| 7 |
-
This contains everything you need to run your app locally.
|
| 8 |
-
|
| 9 |
-
View your app in AI Studio: https://ai.studio/apps/drive/1DWLDNsbZ8bO_lQPzuDVQB3cpW92HgYCC
|
| 10 |
-
|
| 11 |
-
## Run Locally
|
| 12 |
-
|
| 13 |
-
**Prerequisites:** Node.js
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
1. Install dependencies:
|
| 17 |
-
`npm install`
|
| 18 |
-
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
| 19 |
-
3. Run the app:
|
| 20 |
-
`npm run dev`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/backend/__pycache__/main.cpython-313.pyc
DELETED
|
Binary file (6.89 kB)
|
|
|
FastMCPToolGenerator/backend/main.py
DELETED
|
@@ -1,164 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import re
|
| 3 |
-
from fastapi import FastAPI, HTTPException
|
| 4 |
-
from fastapi.responses import FileResponse
|
| 5 |
-
from fastapi.staticfiles import StaticFiles
|
| 6 |
-
from pydantic import BaseModel
|
| 7 |
-
import google.generativeai as genai
|
| 8 |
-
from google.generativeai.types import HarmCategory, HarmBlockThreshold
|
| 9 |
-
|
| 10 |
-
# Получаем ключ из переменных окружения (HF Secrets)
|
| 11 |
-
API_KEY = os.environ.get("GEMINI_API_KEY")
|
| 12 |
-
|
| 13 |
-
if not API_KEY:
|
| 14 |
-
print("ВНИМАНИЕ: GEMINI_API_KEY не установлен в переменных окружения.")
|
| 15 |
-
else:
|
| 16 |
-
genai.configure(api_key=API_KEY)
|
| 17 |
-
|
| 18 |
-
app = FastAPI()
|
| 19 |
-
|
| 20 |
-
# Модель для входящего запроса от фронтенда
|
| 21 |
-
class PromptRequest(BaseModel):
|
| 22 |
-
prompt: str
|
| 23 |
-
|
| 24 |
-
def get_ai_prompt(user_prompt: str):
|
| 25 |
-
return f"""
|
| 26 |
-
Вы — эксперт по генерации JSON-определений инструментов (ToolDefinition) для FastMCP (Gemini CLI).
|
| 27 |
-
Ваша задача — принять текстовое описание инструмента от пользователя и вернуть ТОЛЬКО JSON-структуру, соответствующую интерфейсу ToolDefinition.
|
| 28 |
-
|
| 29 |
-
Интерфейс ToolDefinition (TypeScript):
|
| 30 |
-
export enum ToolType {{
|
| 31 |
-
REST_API = 'REST_API',
|
| 32 |
-
CLI = 'CLI',
|
| 33 |
-
DATABASE = 'DATABASE',
|
| 34 |
-
CUSTOM = 'CUSTOM',
|
| 35 |
-
UTILITY = 'UTILITY',
|
| 36 |
-
CALCULATOR = 'CALCULATOR',
|
| 37 |
-
CONVERTER = 'CONVERTER'
|
| 38 |
-
}}
|
| 39 |
-
export enum ParameterType {{
|
| 40 |
-
STRING = 'str',
|
| 41 |
-
INTEGER = 'int',
|
| 42 |
-
FLOAT = 'float',
|
| 43 |
-
BOOLEAN = 'bool',
|
| 44 |
-
LIST = 'list',
|
| 45 |
-
DICTIONARY = 'dict'
|
| 46 |
-
}}
|
| 47 |
-
export interface ParameterDefinition {{
|
| 48 |
-
name: string;
|
| 49 |
-
type: ParameterType;
|
| 50 |
-
description: string;
|
| 51 |
-
required: boolean;
|
| 52 |
-
}}
|
| 53 |
-
export interface FunctionDefinition {{
|
| 54 |
-
name: string;
|
| 55 |
-
description: string;
|
| 56 |
-
parameters: ParameterDefinition[];
|
| 57 |
-
returnType: ParameterType;
|
| 58 |
-
returnDescription: string;
|
| 59 |
-
}}
|
| 60 |
-
export interface ToolDefinition {{
|
| 61 |
-
name: string;
|
| 62 |
-
type: ToolType;
|
| 63 |
-
dependencies: string;
|
| 64 |
-
functions: FunctionDefinition[];
|
| 65 |
-
}}
|
| 66 |
-
|
| 67 |
-
Правила:
|
| 68 |
-
1. НЕ добавляйте ID. Фронтенд сделает это.
|
| 69 |
-
2. Возвращайте ТОЛЬКО JSON-код. Без markdown, без "вот ваш JSON".
|
| 70 |
-
3. Имена функций и параметров должны быть в snake_case.
|
| 71 |
-
4. Тип (ToolType) должен быть наиболее подходящим (часто это UTILITY или CLI).
|
| 72 |
-
5. Dependencies (зависимости) должны быть пустыми (""), если не указано иное (например, для 'nmap' нужна 'python-nmap').
|
| 73 |
-
|
| 74 |
-
Пример запроса пользователя:
|
| 75 |
-
"Инструмент для получения текущей погоды в городе."
|
| 76 |
-
|
| 77 |
-
Пример вашего ИДЕАЛЬНОГО ответа (ТОЛЬКО JSON):
|
| 78 |
-
{{
|
| 79 |
-
"name": "weather_tool",
|
| 80 |
-
"type": "REST_API",
|
| 81 |
-
"dependencies": "requests",
|
| 82 |
-
"functions": [
|
| 83 |
-
{{
|
| 84 |
-
"name": "get_current_weather",
|
| 85 |
-
"description": "Получает текущую погоду для указанного города.",
|
| 86 |
-
"parameters": [
|
| 87 |
-
{{
|
| 88 |
-
"name": "city_name",
|
| 89 |
-
"type": "str",
|
| 90 |
-
"description": "Название города, для которого нужно получить погоду.",
|
| 91 |
-
"required": true
|
| 92 |
-
}}
|
| 93 |
-
],
|
| 94 |
-
"returnType": "dict",
|
| 95 |
-
"returnDescription": "Словарь с данными о погоде (температура, влажность и т.д.)."
|
| 96 |
-
}}
|
| 97 |
-
]
|
| 98 |
-
}}
|
| 99 |
-
---
|
| 100 |
-
ЗАПРОС ПОЛЬЗОВАТЕЛЯ:
|
| 101 |
-
"{user_prompt}"
|
| 102 |
-
|
| 103 |
-
ВАШ ОТВЕТ (ТОЛЬКО JSON):
|
| 104 |
-
"""
|
| 105 |
-
|
| 106 |
-
generation_config = {
|
| 107 |
-
"temperature": 0.7,
|
| 108 |
-
"top_p": 1.0,
|
| 109 |
-
"top_k": 32,
|
| 110 |
-
"max_output_tokens": 4096,
|
| 111 |
-
}
|
| 112 |
-
|
| 113 |
-
safety_settings = [
|
| 114 |
-
{"category": HarmCategory.HARM_CATEGORY_HARASSMENT, "threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE},
|
| 115 |
-
{"category": HarmCategory.HARM_CATEGORY_HATE_SPEECH, "threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE},
|
| 116 |
-
{"category": HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, "threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE},
|
| 117 |
-
{"category": HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, "threshold": HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE},
|
| 118 |
-
]
|
| 119 |
-
|
| 120 |
-
@app.post("/api/generate")
|
| 121 |
-
async def handle_generate(request: PromptRequest):
|
| 122 |
-
if not API_KEY:
|
| 123 |
-
raise HTTPException(status_code=500, detail="GEMINI_API_KEY не настроен на сервере.")
|
| 124 |
-
|
| 125 |
-
try:
|
| 126 |
-
model = genai.GenerativeModel(
|
| 127 |
-
model_name="gemini-1.5-pro-latest",
|
| 128 |
-
generation_config=generation_config,
|
| 129 |
-
safety_settings=safety_settings
|
| 130 |
-
)
|
| 131 |
-
|
| 132 |
-
prompt_parts = [get_ai_prompt(request.prompt)]
|
| 133 |
-
response = model.generate_content(prompt_parts)
|
| 134 |
-
|
| 135 |
-
json_string = response.text
|
| 136 |
-
|
| 137 |
-
cleaned_json_string = re.sub(r'^```json\s*|\s*```$', '', json_string, flags=re.MULTILINE).strip()
|
| 138 |
-
|
| 139 |
-
return {"jsonString": cleaned_json_string}
|
| 140 |
-
|
| 141 |
-
except Exception as e:
|
| 142 |
-
print(f"Ошибка вызова Gemini API: {e}")
|
| 143 |
-
raise HTTPException(status_code=500, detail=f"Ошибка генерации AI: {str(e)}")
|
| 144 |
-
|
| 145 |
-
# --- Настройки для развертывания на Hugging Face Spaces/Docker ---
|
| 146 |
-
# PORT=7860 устанавливается Hugging Face при запуске контейнера
|
| 147 |
-
IS_HUGGINGFACE_SPACE = os.environ.get("PORT") == "7860"
|
| 148 |
-
|
| 149 |
-
if IS_HUGGINGFACE_SPACE:
|
| 150 |
-
|
| 151 |
-
# Монтируем статические файлы (React build)
|
| 152 |
-
app.mount("/assets", StaticFiles(directory="/app/dist/assets"), name="assets")
|
| 153 |
-
|
| 154 |
-
@app.get("/{full_path:path}")
|
| 155 |
-
async def serve_react_app(full_path: str):
|
| 156 |
-
# Обслуживаем index.html для всех non-API маршрутов
|
| 157 |
-
return FileResponse("/app/dist/index.html")
|
| 158 |
-
|
| 159 |
-
else:
|
| 160 |
-
# При локальной разработке (Vite) FastAPI отвечает ТОЛЬКО за API.
|
| 161 |
-
# Статику раздает Vite (порт 3000).
|
| 162 |
-
print("ℹ️ Режим локальной разработки. Статические файлы обслуживаются Vite.")
|
| 163 |
-
|
| 164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/AiPrompt.tsx
DELETED
|
@@ -1,52 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
import Card from './ui/Card';
|
| 4 |
-
import Textarea from './ui/Textarea';
|
| 5 |
-
import Button from './ui/Button';
|
| 6 |
-
import Label from './ui/Label';
|
| 7 |
-
|
| 8 |
-
interface AiPromptProps {
|
| 9 |
-
prompt: string;
|
| 10 |
-
setPrompt: (value: string) => void;
|
| 11 |
-
onGenerate: () => void;
|
| 12 |
-
isLoading: boolean;
|
| 13 |
-
error: string | null;
|
| 14 |
-
}
|
| 15 |
-
|
| 16 |
-
const AiPrompt: React.FC<AiPromptProps> = ({ prompt, setPrompt, onGenerate, isLoading, error }) => {
|
| 17 |
-
return (
|
| 18 |
-
<Card className="mb-8 border-sky-500 border-2 shadow-lg shadow-sky-500/10">
|
| 19 |
-
<h2 className="text-2xl font-bold mb-4 text-sky-400">
|
| 20 |
-
✨ Создание с помощью AI
|
| 21 |
-
</h2>
|
| 22 |
-
<p className="text-gray-400 mb-4 text-sm">
|
| 23 |
-
Опишите инструмент, который вы хотите создать, на естественном языке. AI проанализирует ваш запрос и автоматически заполнит форму ниже.
|
| 24 |
-
</p>
|
| 25 |
-
|
| 26 |
-
<div>
|
| 27 |
-
<Label htmlFor="ai-prompt">Ваш запрос:</Label>
|
| 28 |
-
<Textarea
|
| 29 |
-
id="ai-prompt"
|
| 30 |
-
value={prompt}
|
| 31 |
-
onChange={(e) => setPrompt(e.target.value)}
|
| 32 |
-
placeholder="Например: 'Рнструмент для получения текущей РїРѕРіРѕРґС‹ РІ РіРѕСЂРѕРґРµ' или 'Утилита для работы СЃ файлами: чтение, запись Рё удаление'"
|
| 33 |
-
rows={3}
|
| 34 |
-
disabled={isLoading}
|
| 35 |
-
/>
|
| 36 |
-
</div>
|
| 37 |
-
|
| 38 |
-
<div className="mt-4 flex items-center justify-between">
|
| 39 |
-
<Button onClick={onGenerate} disabled={isLoading}>
|
| 40 |
-
{isLoading ? 'Генерация...' : 'Сгенерировать'}
|
| 41 |
-
</Button>
|
| 42 |
-
{error && <p className="text-red-400 text-sm ml-4">{error}</p>}
|
| 43 |
-
</div>
|
| 44 |
-
</Card>
|
| 45 |
-
);
|
| 46 |
-
};
|
| 47 |
-
|
| 48 |
-
export default AiPrompt;
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/CodeOutput.tsx
DELETED
|
@@ -1,340 +0,0 @@
|
|
| 1 |
-
import React, { useState } from 'react';
|
| 2 |
-
|
| 3 |
-
import { GeneratedCode } from '../types';
|
| 4 |
-
|
| 5 |
-
import JSZip from 'jszip'; // Импортируем JSZip из npm
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
interface CodeOutputProps {
|
| 10 |
-
|
| 11 |
-
generatedCode: GeneratedCode | null;
|
| 12 |
-
|
| 13 |
-
toolName: string;
|
| 14 |
-
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
type Tab = 'setup.py' | 'server.py' | '.gemini/settings.json' | 'GEMINI.md' | 'Инструкция' | 'Примеры';
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
const CodeOutput: React.FC<CodeOutputProps> = ({ generatedCode, toolName }) => {
|
| 24 |
-
|
| 25 |
-
const [activeTab, setActiveTab] = useState<Tab>('Примеры');
|
| 26 |
-
|
| 27 |
-
const [copiedTab, setCopiedTab] = useState<Tab | null>(null);
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
const handleCopy = (content: string, tab: Tab) => {
|
| 32 |
-
|
| 33 |
-
navigator.clipboard.writeText(content);
|
| 34 |
-
|
| 35 |
-
setCopiedTab(tab);
|
| 36 |
-
|
| 37 |
-
setTimeout(() => setCopiedTab(null), 2000);
|
| 38 |
-
|
| 39 |
-
};
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
const handleDownloadZip = () => {
|
| 44 |
-
|
| 45 |
-
if (!generatedCode || !toolName) return;
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
const zip = new JSZip(); // Теперь JSZip импортирован
|
| 50 |
-
|
| 51 |
-
const rootFolder = zip.folder(toolName);
|
| 52 |
-
|
| 53 |
-
if (!rootFolder) return;
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
rootFolder.file('setup.py', generatedCode.setupPy);
|
| 58 |
-
|
| 59 |
-
rootFolder.file('server.py', generatedCode.serverPy);
|
| 60 |
-
|
| 61 |
-
rootFolder.file('GEMINI.md', generatedCode.geminiMd);
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
const geminiFolder = rootFolder.folder('.gemini');
|
| 66 |
-
|
| 67 |
-
if (geminiFolder) {
|
| 68 |
-
|
| 69 |
-
geminiFolder.file('settings.json', generatedCode.settingsJson);
|
| 70 |
-
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
zip.generateAsync({ type: 'blob' }).then(content => {
|
| 76 |
-
|
| 77 |
-
const link = document.createElement('a');
|
| 78 |
-
|
| 79 |
-
link.href = URL.createObjectURL(content);
|
| 80 |
-
|
| 81 |
-
link.download = `${toolName}.zip`;
|
| 82 |
-
|
| 83 |
-
document.body.appendChild(link);
|
| 84 |
-
|
| 85 |
-
link.click();
|
| 86 |
-
|
| 87 |
-
document.body.removeChild(link);
|
| 88 |
-
|
| 89 |
-
});
|
| 90 |
-
|
| 91 |
-
};
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
const tabs: Tab[] = ['Примеры', 'GEMINI.md', 'server.py', 'setup.py', '.gemini/settings.json', 'Инструкция'];
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
const getContentForTab = (tab: Tab): string => {
|
| 100 |
-
|
| 101 |
-
if (!generatedCode) return "Сгенерируйте код, заполнив форму слева.";
|
| 102 |
-
|
| 103 |
-
switch (tab) {
|
| 104 |
-
|
| 105 |
-
case 'setup.py': return generatedCode.setupPy;
|
| 106 |
-
|
| 107 |
-
case 'server.py': return generatedCode.serverPy;
|
| 108 |
-
|
| 109 |
-
case '.gemini/settings.json': return generatedCode.settingsJson;
|
| 110 |
-
|
| 111 |
-
case 'GEMINI.md': return generatedCode.geminiMd;
|
| 112 |
-
|
| 113 |
-
case 'Примеры': return generatedCode.examplesMd;
|
| 114 |
-
|
| 115 |
-
case 'Инструкция': return getInstructions(toolName, generatedCode);
|
| 116 |
-
|
| 117 |
-
default: return '';
|
| 118 |
-
|
| 119 |
-
}
|
| 120 |
-
|
| 121 |
-
};
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
const getLanguageForTab = (tab: Tab): string => {
|
| 126 |
-
|
| 127 |
-
switch(tab) {
|
| 128 |
-
|
| 129 |
-
case 'setup.py':
|
| 130 |
-
|
| 131 |
-
case 'server.py':
|
| 132 |
-
|
| 133 |
-
return 'python';
|
| 134 |
-
|
| 135 |
-
case '.gemini/settings.json':
|
| 136 |
-
|
| 137 |
-
return 'json';
|
| 138 |
-
|
| 139 |
-
case 'GEMINI.md':
|
| 140 |
-
|
| 141 |
-
case 'Инструкция':
|
| 142 |
-
|
| 143 |
-
case 'Примеры':
|
| 144 |
-
|
| 145 |
-
return 'markdown';
|
| 146 |
-
|
| 147 |
-
default:
|
| 148 |
-
|
| 149 |
-
return 'plaintext';
|
| 150 |
-
|
| 151 |
-
}
|
| 152 |
-
|
| 153 |
-
}
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
const getInstructions = (toolName: string, code: GeneratedCode): string => {
|
| 158 |
-
|
| 159 |
-
const primaryFunctionMatch = code.serverPy?.match(/def\s+([a-zA-Z0-9_]+)\(/);
|
| 160 |
-
|
| 161 |
-
const primaryFunction = primaryFunctionMatch && primaryFunctionMatch[1] ? primaryFunctionMatch[1] : 'your_function';
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
return `
|
| 166 |
-
|
| 167 |
-
# Набор Инструкций для Создания Нового Инструмента
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
## 1. 📂 Структура проекта
|
| 172 |
-
|
| 173 |
-
Создайте следующую структуру папок и файлов (или просто нажмите "Скачать .zip"):
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
${toolName}/
|
| 178 |
-
|
| 179 |
-
├── .gemini/
|
| 180 |
-
|
| 181 |
-
│ └── settings.json
|
| 182 |
-
|
| 183 |
-
├── GEMINI.md
|
| 184 |
-
|
| 185 |
-
├── setup.py
|
| 186 |
-
|
| 187 |
-
└── server.py
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
## 2. 🐍 Содержимое файлов
|
| 192 |
-
|
| 193 |
-
Скопируйте содержимое из соответствующих вкладок в созданные файлы.
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
## 3. 📦 Установка и запуск
|
| 198 |
-
|
| 199 |
-
Откройте терминал в папке \`${toolName}\` и выполните:
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
\`\`\`bash
|
| 204 |
-
|
| 205 |
-
# Установка зависимостей и инструмента в режиме разработки
|
| 206 |
-
|
| 207 |
-
pip install -e .
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
# Запуск инструмента (Gemini CLI сделает это автоматически)
|
| 212 |
-
|
| 213 |
-
# Для ручной проверки можно запустить:
|
| 214 |
-
|
| 215 |
-
python server.py
|
| 216 |
-
|
| 217 |
-
\`\`\`
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
## 4. 🚀 Использование в Gemini CLI
|
| 222 |
-
|
| 223 |
-
После установки Gemini CLI автоматически обнаружит ваш инструмент.
|
| 224 |
-
|
| 225 |
-
См. вкладку "Примеры", чтобы увидеть, как ваши запросы на естественном языке будут превращаться в вызовы функций.
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
### ⚠️ Правило вызова
|
| 230 |
-
|
| 231 |
-
\`\`\`
|
| 232 |
-
|
| 233 |
-
${toolName}:${primaryFunction}
|
| 234 |
-
|
| 235 |
-
\`\`\`
|
| 236 |
-
|
| 237 |
-
`;
|
| 238 |
-
|
| 239 |
-
};
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
return (
|
| 244 |
-
|
| 245 |
-
<div className="bg-gray-800/50 border border-gray-700 rounded-lg overflow-hidden h-full flex flex-col">
|
| 246 |
-
|
| 247 |
-
<div className="flex-shrink-0 bg-gray-900/70 border-b border-gray-700 flex justify-between items-center pr-2">
|
| 248 |
-
|
| 249 |
-
<nav className="flex space-x-1 p-1" aria-label="Tabs">
|
| 250 |
-
|
| 251 |
-
{tabs.map(tab => (
|
| 252 |
-
|
| 253 |
-
<button
|
| 254 |
-
|
| 255 |
-
key={tab}
|
| 256 |
-
|
| 257 |
-
onClick={() => setActiveTab(tab)}
|
| 258 |
-
|
| 259 |
-
className={`whitespace-nowrap px-3 py-2 text-sm font-medium rounded-md transition ${
|
| 260 |
-
|
| 261 |
-
activeTab === tab
|
| 262 |
-
|
| 263 |
-
? 'bg-sky-600 text-white'
|
| 264 |
-
|
| 265 |
-
: 'text-gray-400 hover:bg-gray-700 hover:text-gray-200'
|
| 266 |
-
|
| 267 |
-
}`}
|
| 268 |
-
|
| 269 |
-
>
|
| 270 |
-
|
| 271 |
-
{tab}
|
| 272 |
-
|
| 273 |
-
</button>
|
| 274 |
-
|
| 275 |
-
))}
|
| 276 |
-
|
| 277 |
-
</nav>
|
| 278 |
-
|
| 279 |
-
{generatedCode && (
|
| 280 |
-
|
| 281 |
-
<button
|
| 282 |
-
|
| 283 |
-
onClick={handleDownloadZip}
|
| 284 |
-
|
| 285 |
-
className="bg-sky-600 hover:bg-sky-500 text-white px-3 py-1.5 rounded-md text-xs font-semibold transition disabled:opacity-50"
|
| 286 |
-
|
| 287 |
-
disabled={!generatedCode}
|
| 288 |
-
|
| 289 |
-
>
|
| 290 |
-
|
| 291 |
-
Скачать .zip
|
| 292 |
-
|
| 293 |
-
</button>
|
| 294 |
-
|
| 295 |
-
)}
|
| 296 |
-
|
| 297 |
-
</div>
|
| 298 |
-
|
| 299 |
-
<div className="relative flex-grow min-h-0">
|
| 300 |
-
|
| 301 |
-
<pre className="h-full overflow-auto p-4 text-sm font-mono bg-gray-800">
|
| 302 |
-
|
| 303 |
-
<code className={`language-${getLanguageForTab(activeTab)}`}>
|
| 304 |
-
|
| 305 |
-
{getContentForTab(activeTab)}
|
| 306 |
-
|
| 307 |
-
</code>
|
| 308 |
-
|
| 309 |
-
</pre>
|
| 310 |
-
|
| 311 |
-
{activeTab !== 'Инструкция' && generatedCode && (
|
| 312 |
-
|
| 313 |
-
<button
|
| 314 |
-
|
| 315 |
-
onClick={() => handleCopy(getContentForTab(activeTab), activeTab)}
|
| 316 |
-
|
| 317 |
-
className="absolute top-4 right-4 bg-gray-700 hover:bg-gray-600 text-gray-200 px-3 py-1 rounded-md text-xs transition"
|
| 318 |
-
|
| 319 |
-
>
|
| 320 |
-
|
| 321 |
-
{copiedTab === activeTab ? 'Скопировано!' : 'Копировать'}
|
| 322 |
-
|
| 323 |
-
</button>
|
| 324 |
-
|
| 325 |
-
)}
|
| 326 |
-
|
| 327 |
-
</div>
|
| 328 |
-
|
| 329 |
-
</div>
|
| 330 |
-
|
| 331 |
-
);
|
| 332 |
-
|
| 333 |
-
};
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
export default CodeOutput;
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/FunctionEditor.tsx
DELETED
|
@@ -1,146 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
import { FunctionDefinition, ParameterDefinition, ParameterType } from '../types';
|
| 4 |
-
import { PARAMETER_TYPES, PARAMETER_TYPE_DESCRIPTIONS } from '../constants';
|
| 5 |
-
import Input from './ui/Input';
|
| 6 |
-
import Textarea from './ui/Textarea';
|
| 7 |
-
import Select from './ui/Select';
|
| 8 |
-
import Label from './ui/Label';
|
| 9 |
-
import Button from './ui/Button';
|
| 10 |
-
import Card from './ui/Card';
|
| 11 |
-
|
| 12 |
-
interface FunctionEditorProps {
|
| 13 |
-
func: FunctionDefinition;
|
| 14 |
-
updateFunction: (func: FunctionDefinition) => void;
|
| 15 |
-
removeFunction: () => void;
|
| 16 |
-
isOnlyFunction: boolean;
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
const ParameterRow: React.FC<{
|
| 20 |
-
param: ParameterDefinition;
|
| 21 |
-
updateParameter: (param: ParameterDefinition) => void;
|
| 22 |
-
removeParameter: () => void;
|
| 23 |
-
}> = ({ param, updateParameter, removeParameter }) => {
|
| 24 |
-
|
| 25 |
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
| 26 |
-
const { name, value, type } = e.target;
|
| 27 |
-
const isCheckbox = type === 'checkbox';
|
| 28 |
-
const checked = isCheckbox ? (e.target as HTMLInputElement).checked : undefined;
|
| 29 |
-
|
| 30 |
-
updateParameter({
|
| 31 |
-
...param,
|
| 32 |
-
[name]: isCheckbox ? checked : value
|
| 33 |
-
});
|
| 34 |
-
};
|
| 35 |
-
|
| 36 |
-
return (
|
| 37 |
-
<div className="grid grid-cols-1 md:grid-cols-[1fr,1fr,2fr,auto,auto] gap-2 items-center p-2 bg-gray-900/50 rounded">
|
| 38 |
-
<Input name="name" value={param.name} onChange={handleChange} placeholder="РРјСЏ параметра" />
|
| 39 |
-
<div>
|
| 40 |
-
<Select name="type" value={param.type} onChange={handleChange}>
|
| 41 |
-
{PARAMETER_TYPES.map(t => <option key={t} value={t} title={PARAMETER_TYPE_DESCRIPTIONS[t]}>{t}</option>)}
|
| 42 |
-
</Select>
|
| 43 |
-
<p className="text-xs text-gray-400 mt-1 pl-1">{PARAMETER_TYPE_DESCRIPTIONS[param.type]}</p>
|
| 44 |
-
</div>
|
| 45 |
-
<Input name="description" value={param.description} onChange={handleChange} placeholder="Описание" />
|
| 46 |
-
<label className="flex items-center space-x-2 text-sm text-gray-300 justify-center">
|
| 47 |
-
<input type="checkbox" name="required" checked={param.required} onChange={handleChange} className="rounded bg-gray-700 border-gray-600 text-sky-500 focus:ring-sky-500"/>
|
| 48 |
-
<span>Req.</span>
|
| 49 |
-
</label>
|
| 50 |
-
<Button onClick={removeParameter} variant="danger" className="px-2 py-1 text-xs">X</Button>
|
| 51 |
-
</div>
|
| 52 |
-
);
|
| 53 |
-
};
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
const FunctionEditor: React.FC<FunctionEditorProps> = ({ func, updateFunction, removeFunction, isOnlyFunction }) => {
|
| 57 |
-
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
| 58 |
-
updateFunction({ ...func, [e.target.name]: e.target.value });
|
| 59 |
-
};
|
| 60 |
-
|
| 61 |
-
const addParameter = () => {
|
| 62 |
-
const newParam: ParameterDefinition = {
|
| 63 |
-
id: crypto.randomUUID(),
|
| 64 |
-
name: `param_${func.parameters.length + 1}`,
|
| 65 |
-
type: ParameterType.STRING,
|
| 66 |
-
description: '',
|
| 67 |
-
required: true
|
| 68 |
-
};
|
| 69 |
-
updateFunction({ ...func, parameters: [...func.parameters, newParam] });
|
| 70 |
-
};
|
| 71 |
-
|
| 72 |
-
const updateParameter = (updatedParam: ParameterDefinition) => {
|
| 73 |
-
updateFunction({
|
| 74 |
-
...func,
|
| 75 |
-
parameters: func.parameters.map(p => p.id === updatedParam.id ? updatedParam : p)
|
| 76 |
-
});
|
| 77 |
-
};
|
| 78 |
-
|
| 79 |
-
const removeParameter = (id: string) => {
|
| 80 |
-
updateFunction({
|
| 81 |
-
...func,
|
| 82 |
-
parameters: func.parameters.filter(p => p.id !== id)
|
| 83 |
-
});
|
| 84 |
-
};
|
| 85 |
-
|
| 86 |
-
return (
|
| 87 |
-
<Card>
|
| 88 |
-
<div className="flex justify-between items-start mb-4">
|
| 89 |
-
<div className="flex-grow">
|
| 90 |
-
<Label htmlFor={`func-name-${func.id}`}>РРјСЏ функции</Label>
|
| 91 |
-
<Input id={`func-name-${func.id}`} name="name" value={func.name} onChange={handleInputChange} />
|
| 92 |
-
</div>
|
| 93 |
-
<Button onClick={removeFunction} variant="danger" disabled={isOnlyFunction} className="ml-4 mt-6">
|
| 94 |
-
Удалить функцию
|
| 95 |
-
</Button>
|
| 96 |
-
</div>
|
| 97 |
-
|
| 98 |
-
<div>
|
| 99 |
-
<Label htmlFor={`func-desc-${func.id}`}>Описание функции</Label>
|
| 100 |
-
<Textarea id={`func-desc-${func.id}`} name="description" value={func.description} onChange={handleInputChange} />
|
| 101 |
-
</div>
|
| 102 |
-
|
| 103 |
-
<div className="mt-4">
|
| 104 |
-
<h4 className="font-semibold text-gray-300 mb-2">Параметры</h4>
|
| 105 |
-
<div className="flex flex-col gap-2">
|
| 106 |
-
{func.parameters.length > 0 && (
|
| 107 |
-
<div className="hidden md:grid md:grid-cols-[1fr,1fr,2fr,auto,auto] gap-2 text-xs text-gray-400 px-2">
|
| 108 |
-
<span>РРјСЏ</span>
|
| 109 |
-
<span>РўРёРї</span>
|
| 110 |
-
<span>Описание</span>
|
| 111 |
-
<span className="text-center">РћР±СЏР·.</span>
|
| 112 |
-
</div>
|
| 113 |
-
)}
|
| 114 |
-
{func.parameters.map((param) => (
|
| 115 |
-
<ParameterRow
|
| 116 |
-
key={param.id}
|
| 117 |
-
param={param}
|
| 118 |
-
updateParameter={updateParameter}
|
| 119 |
-
removeParameter={() => removeParameter(param.id)}
|
| 120 |
-
/>
|
| 121 |
-
))}
|
| 122 |
-
</div>
|
| 123 |
-
<Button onClick={addParameter} variant="secondary" className="mt-3 text-xs">+ Добавить параметр</Button>
|
| 124 |
-
</div>
|
| 125 |
-
|
| 126 |
-
<div className="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 127 |
-
<div>
|
| 128 |
-
<Label htmlFor={`func-return-type-${func.id}`}>Тип возвращаемого значения</Label>
|
| 129 |
-
<Select id={`func-return-type-${func.id}`} name="returnType" value={func.returnType} onChange={handleInputChange}>
|
| 130 |
-
{PARAMETER_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
|
| 131 |
-
</Select>
|
| 132 |
-
</div>
|
| 133 |
-
<div>
|
| 134 |
-
<Label htmlFor={`func-return-desc-${func.id}`}>Описание возвращаемого значения</Label>
|
| 135 |
-
<Input id={`func-return-desc-${func.id}`} name="returnDescription" value={func.returnDescription} onChange={handleInputChange} />
|
| 136 |
-
</div>
|
| 137 |
-
</div>
|
| 138 |
-
</Card>
|
| 139 |
-
);
|
| 140 |
-
};
|
| 141 |
-
|
| 142 |
-
export default FunctionEditor;
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/ToolForm.tsx
DELETED
|
@@ -1,115 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
import { ToolDefinition, FunctionDefinition, ToolType, ParameterType } from '../types';
|
| 4 |
-
import { TOOL_TYPES } from '../constants';
|
| 5 |
-
import FunctionEditor from './FunctionEditor';
|
| 6 |
-
import Input from './ui/Input';
|
| 7 |
-
import Select from './ui/Select';
|
| 8 |
-
import Label from './ui/Label';
|
| 9 |
-
import Button from './ui/Button';
|
| 10 |
-
import Card from './ui/Card';
|
| 11 |
-
|
| 12 |
-
interface ToolFormProps {
|
| 13 |
-
toolDefinition: ToolDefinition;
|
| 14 |
-
setToolDefinition: React.Dispatch<React.SetStateAction<ToolDefinition>>;
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
-
const ToolForm: React.FC<ToolFormProps> = ({ toolDefinition, setToolDefinition }) => {
|
| 18 |
-
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
| 19 |
-
const { name, value } = e.target;
|
| 20 |
-
setToolDefinition(prev => ({ ...prev, [name]: value }));
|
| 21 |
-
};
|
| 22 |
-
|
| 23 |
-
const addFunction = () => {
|
| 24 |
-
const newFunction: FunctionDefinition = {
|
| 25 |
-
id: crypto.randomUUID(),
|
| 26 |
-
name: `new_function_${toolDefinition.functions.length + 1}`,
|
| 27 |
-
description: 'Описание новой функции.',
|
| 28 |
-
parameters: [],
|
| 29 |
-
returnType: ParameterType.STRING,
|
| 30 |
-
returnDescription: 'Описание возвращаемого значения.'
|
| 31 |
-
};
|
| 32 |
-
setToolDefinition(prev => ({
|
| 33 |
-
...prev,
|
| 34 |
-
functions: [...prev.functions, newFunction]
|
| 35 |
-
}));
|
| 36 |
-
};
|
| 37 |
-
|
| 38 |
-
const updateFunction = (updatedFunction: FunctionDefinition) => {
|
| 39 |
-
setToolDefinition(prev => ({
|
| 40 |
-
...prev,
|
| 41 |
-
functions: prev.functions.map(fn => fn.id === updatedFunction.id ? updatedFunction : fn)
|
| 42 |
-
}));
|
| 43 |
-
};
|
| 44 |
-
|
| 45 |
-
const removeFunction = (id: string) => {
|
| 46 |
-
setToolDefinition(prev => ({
|
| 47 |
-
...prev,
|
| 48 |
-
functions: prev.functions.filter(fn => fn.id !== id)
|
| 49 |
-
}));
|
| 50 |
-
};
|
| 51 |
-
|
| 52 |
-
return (
|
| 53 |
-
<div className="flex flex-col gap-6">
|
| 54 |
-
<Card>
|
| 55 |
-
<h2 className="text-2xl font-bold mb-4 text-sky-400">1. Конфигурация инструмента</h2>
|
| 56 |
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 57 |
-
<div>
|
| 58 |
-
<Label htmlFor="name">Название инструмента (Tool Name)</Label>
|
| 59 |
-
<Input
|
| 60 |
-
id="name"
|
| 61 |
-
name="name"
|
| 62 |
-
value={toolDefinition.name}
|
| 63 |
-
onChange={handleInputChange}
|
| 64 |
-
placeholder="e.g., web_scraper"
|
| 65 |
-
/>
|
| 66 |
-
</div>
|
| 67 |
-
<div>
|
| 68 |
-
<Label htmlFor="type">Тип инструмента (Tool Type)</Label>
|
| 69 |
-
<Select id="type" name="type" value={toolDefinition.type} onChange={handleInputChange}>
|
| 70 |
-
{TOOL_TYPES.map(type => (
|
| 71 |
-
<option key={type} value={type}>{type}</option>
|
| 72 |
-
))}
|
| 73 |
-
</Select>
|
| 74 |
-
</div>
|
| 75 |
-
<div className="md:col-span-2">
|
| 76 |
-
<Label htmlFor="dependencies">Зависимости (Dependencies)</Label>
|
| 77 |
-
<Input
|
| 78 |
-
id="dependencies"
|
| 79 |
-
name="dependencies"
|
| 80 |
-
value={toolDefinition.dependencies}
|
| 81 |
-
onChange={handleInputChange}
|
| 82 |
-
placeholder="e.g., requests, beautifulsoup4"
|
| 83 |
-
/>
|
| 84 |
-
<p className="text-xs text-gray-500 mt-1">Перечислите через запятую или пробел.</p>
|
| 85 |
-
</div>
|
| 86 |
-
</div>
|
| 87 |
-
</Card>
|
| 88 |
-
|
| 89 |
-
<div className="flex flex-col gap-6">
|
| 90 |
-
<h2 className="text-2xl font-bold text-sky-400">2. Функции</h2>
|
| 91 |
-
{toolDefinition.functions.map((func, index) => (
|
| 92 |
-
<FunctionEditor
|
| 93 |
-
key={func.id}
|
| 94 |
-
func={func}
|
| 95 |
-
updateFunction={updateFunction}
|
| 96 |
-
removeFunction={() => removeFunction(func.id)}
|
| 97 |
-
isOnlyFunction={toolDefinition.functions.length === 1}
|
| 98 |
-
/>
|
| 99 |
-
))}
|
| 100 |
-
</div>
|
| 101 |
-
|
| 102 |
-
<div className="mt-2">
|
| 103 |
-
<Button onClick={addFunction} variant="secondary">
|
| 104 |
-
+ Добавить функцию
|
| 105 |
-
</Button>
|
| 106 |
-
</div>
|
| 107 |
-
</div>
|
| 108 |
-
);
|
| 109 |
-
};
|
| 110 |
-
|
| 111 |
-
export default ToolForm;
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/ui/Button.tsx
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
|
| 4 |
-
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
| 5 |
-
variant?: 'primary' | 'secondary' | 'danger';
|
| 6 |
-
}
|
| 7 |
-
|
| 8 |
-
const Button: React.FC<ButtonProps> = ({ variant = 'primary', className, children, ...props }) => {
|
| 9 |
-
const baseClasses = "px-4 py-2 rounded-md font-semibold text-sm transition-all focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed";
|
| 10 |
-
|
| 11 |
-
const variantClasses = {
|
| 12 |
-
primary: 'bg-sky-600 text-white hover:bg-sky-500 focus:ring-sky-500',
|
| 13 |
-
secondary: 'bg-gray-700 text-gray-200 hover:bg-gray-600 focus:ring-gray-500',
|
| 14 |
-
danger: 'bg-red-600 text-white hover:bg-red-500 focus:ring-red-500',
|
| 15 |
-
};
|
| 16 |
-
|
| 17 |
-
return (
|
| 18 |
-
<button className={`${baseClasses} ${variantClasses[variant]} ${className}`} {...props}>
|
| 19 |
-
{children}
|
| 20 |
-
</button>
|
| 21 |
-
);
|
| 22 |
-
};
|
| 23 |
-
|
| 24 |
-
export default Button;
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/ui/Card.tsx
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
|
| 4 |
-
interface CardProps {
|
| 5 |
-
children: React.ReactNode;
|
| 6 |
-
className?: string;
|
| 7 |
-
}
|
| 8 |
-
|
| 9 |
-
const Card: React.FC<CardProps> = ({ children, className }) => {
|
| 10 |
-
return (
|
| 11 |
-
<div className={`bg-gray-800/50 border border-gray-700 rounded-lg p-6 ${className}`}>
|
| 12 |
-
{children}
|
| 13 |
-
</div>
|
| 14 |
-
);
|
| 15 |
-
};
|
| 16 |
-
|
| 17 |
-
export default Card;
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/ui/Input.tsx
DELETED
|
@@ -1,19 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
|
| 4 |
-
type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
| 5 |
-
|
| 6 |
-
const Input: React.FC<InputProps> = (props) => {
|
| 7 |
-
return (
|
| 8 |
-
<input
|
| 9 |
-
{...props}
|
| 10 |
-
className="w-full bg-gray-800 border border-gray-600 rounded-md px-3 py-2 text-gray-200 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-sky-500 transition"
|
| 11 |
-
/>
|
| 12 |
-
);
|
| 13 |
-
};
|
| 14 |
-
|
| 15 |
-
export default Input;
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/ui/Label.tsx
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
|
| 4 |
-
type LabelProps = React.LabelHTMLAttributes<HTMLLabelElement>;
|
| 5 |
-
|
| 6 |
-
const Label: React.FC<LabelProps> = (props) => {
|
| 7 |
-
return (
|
| 8 |
-
<label {...props} className="block text-sm font-medium text-gray-300 mb-1" />
|
| 9 |
-
);
|
| 10 |
-
};
|
| 11 |
-
|
| 12 |
-
export default Label;
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/ui/Select.tsx
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
|
| 4 |
-
type SelectProps = React.SelectHTMLAttributes<HTMLSelectElement>;
|
| 5 |
-
|
| 6 |
-
const Select: React.FC<SelectProps> = (props) => {
|
| 7 |
-
return (
|
| 8 |
-
<select
|
| 9 |
-
{...props}
|
| 10 |
-
className="w-full bg-gray-800 border border-gray-600 rounded-md px-3 py-2 text-gray-200 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-sky-500 transition appearance-none"
|
| 11 |
-
style={{
|
| 12 |
-
backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`,
|
| 13 |
-
backgroundPosition: 'right 0.5rem center',
|
| 14 |
-
backgroundRepeat: 'no-repeat',
|
| 15 |
-
backgroundSize: '1.5em 1.5em',
|
| 16 |
-
paddingRight: '2.5rem',
|
| 17 |
-
}}
|
| 18 |
-
>
|
| 19 |
-
{props.children}
|
| 20 |
-
</select>
|
| 21 |
-
);
|
| 22 |
-
};
|
| 23 |
-
|
| 24 |
-
export default Select;
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/components/ui/Textarea.tsx
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
|
| 4 |
-
type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
| 5 |
-
|
| 6 |
-
const Textarea: React.FC<TextareaProps> = (props) => {
|
| 7 |
-
return (
|
| 8 |
-
<textarea
|
| 9 |
-
{...props}
|
| 10 |
-
className="w-full bg-gray-800 border border-gray-600 rounded-md px-3 py-2 text-gray-200 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-sky-500 transition"
|
| 11 |
-
rows={2}
|
| 12 |
-
/>
|
| 13 |
-
);
|
| 14 |
-
};
|
| 15 |
-
|
| 16 |
-
export default Textarea;
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/constants.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import { ToolType, ParameterType } from './types';
|
| 3 |
-
|
| 4 |
-
export const TOOL_TYPES: ToolType[] = Object.values(ToolType);
|
| 5 |
-
export const PARAMETER_TYPES: ParameterType[] = Object.values(ParameterType);
|
| 6 |
-
|
| 7 |
-
export const PARAMETER_TYPE_DESCRIPTIONS: Record<ParameterType, string> = {
|
| 8 |
-
[ParameterType.STRING]: 'Текстовая строка (e.g., "hello world").',
|
| 9 |
-
[ParameterType.INTEGER]: 'Целое число (e.g., 10, -5).',
|
| 10 |
-
[ParameterType.FLOAT]: 'Число с плавающей точкой (e.g., 3.14).',
|
| 11 |
-
[ParameterType.BOOLEAN]: 'Логическое значение (True или False).',
|
| 12 |
-
[ParameterType.LIST]: 'Упорядоченный список элементов (e.g., ["a", "b"]).',
|
| 13 |
-
[ParameterType.DICTIONARY]: 'Словарь пар ключ-значение (e.g., {"key": "value"}).'
|
| 14 |
-
};
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/dist/assets/index-CjZgBbLF.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
FastMCPToolGenerator/dist/index.html
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
<!DOCTYPE html>
|
| 3 |
-
<html lang="en">
|
| 4 |
-
<head>
|
| 5 |
-
<meta charset="UTF-8" />
|
| 6 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
-
<title>FastMCP Tool Generator</title>
|
| 8 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
| 10 |
-
<style>
|
| 11 |
-
/* Custom scrollbar for webkit browsers */
|
| 12 |
-
::-webkit-scrollbar {
|
| 13 |
-
width: 8px;
|
| 14 |
-
height: 8px;
|
| 15 |
-
}
|
| 16 |
-
::-webkit-scrollbar-track {
|
| 17 |
-
background: #1f2937; /* bg-gray-800 */
|
| 18 |
-
}
|
| 19 |
-
::-webkit-scrollbar-thumb {
|
| 20 |
-
background: #4b5563; /* bg-gray-600 */
|
| 21 |
-
border-radius: 4px;
|
| 22 |
-
}
|
| 23 |
-
::-webkit-scrollbar-thumb:hover {
|
| 24 |
-
background: #6b7280; /* bg-gray-500 */
|
| 25 |
-
}
|
| 26 |
-
</style>
|
| 27 |
-
|
| 28 |
-
<link rel="stylesheet" href="/index.css">
|
| 29 |
-
<script type="importmap">
|
| 30 |
-
{
|
| 31 |
-
"imports": {
|
| 32 |
-
"react": "https://aistudiocdn.com/react@^19.2.0",
|
| 33 |
-
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/",
|
| 34 |
-
"react/": "https://aistudiocdn.com/react@^19.2.0/",
|
| 35 |
-
"@google/genai": "https://aistudiocdn.com/@google/genai@^1.29.0"
|
| 36 |
-
}
|
| 37 |
-
}
|
| 38 |
-
</script>
|
| 39 |
-
<script type="module" crossorigin src="/assets/index-CjZgBbLF.js"></script>
|
| 40 |
-
</head>
|
| 41 |
-
<body class="bg-gray-900 text-gray-100">
|
| 42 |
-
<div id="root"></div>
|
| 43 |
-
</body>
|
| 44 |
-
</html>
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/examples.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import { ToolDefinition, ToolType, ParameterType } from './types';
|
| 3 |
-
|
| 4 |
-
export const nmapScannerExample: ToolDefinition = {
|
| 5 |
-
name: 'nmap_scanner',
|
| 6 |
-
type: ToolType.UTILITY,
|
| 7 |
-
dependencies: 'python-nmap',
|
| 8 |
-
functions: [
|
| 9 |
-
{
|
| 10 |
-
id: 'nmap-func-1',
|
| 11 |
-
name: 'run_scan',
|
| 12 |
-
description: 'Запускает сканирование Nmap на указанные цели с заданными аргументами. Позволяет выполнять гибкую разведку сети.',
|
| 13 |
-
parameters: [
|
| 14 |
-
{
|
| 15 |
-
id: 'nmap-param-1',
|
| 16 |
-
name: 'targets',
|
| 17 |
-
type: ParameterType.STRING,
|
| 18 |
-
description: "Цели для сканирования (IP, домен, CIDR). Например: '192.168.1.1', 'scanme.nmap.org', '10.0.0.0/24'.",
|
| 19 |
-
required: true,
|
| 20 |
-
},
|
| 21 |
-
{
|
| 22 |
-
id: 'nmap-param-2',
|
| 23 |
-
name: 'ports',
|
| 24 |
-
type: ParameterType.STRING,
|
| 25 |
-
description: "Порты для сканирования. Например: '21-25,80,443,8080'.",
|
| 26 |
-
required: true,
|
| 27 |
-
},
|
| 28 |
-
{
|
| 29 |
-
id: 'nmap-param-3',
|
| 30 |
-
name: 'arguments',
|
| 31 |
-
type: ParameterType.STRING,
|
| 32 |
-
description: "Дополнительные аргументы Nmap для тонкой настройки сканирования. Например: '-sV -sC -O'.",
|
| 33 |
-
required: true,
|
| 34 |
-
},
|
| 35 |
-
],
|
| 36 |
-
returnType: ParameterType.DICTIONARY,
|
| 37 |
-
returnDescription: 'Результат сканирования в формате JSON, содержащий информацию о хостах, портах, службах и их версиях.',
|
| 38 |
-
},
|
| 39 |
-
{
|
| 40 |
-
id: 'nmap-func-2',
|
| 41 |
-
name: 'host_discovery',
|
| 42 |
-
description: 'Обнаруживает активные хосты в указанной сети (ping scan), не проводя сканирование портов.',
|
| 43 |
-
parameters: [
|
| 44 |
-
{
|
| 45 |
-
id: 'nmap-param-4',
|
| 46 |
-
name: 'network_cidr',
|
| 47 |
-
type: ParameterType.STRING,
|
| 48 |
-
description: "Сетевой диапазон в формате CIDR для обнаружения хостов. Например: '192.168.1.0/24'.",
|
| 49 |
-
required: true,
|
| 50 |
-
},
|
| 51 |
-
],
|
| 52 |
-
returnType: ParameterType.LIST,
|
| 53 |
-
returnDescription: 'Список IP-адресов активных хостов, обнаруженных в сети.',
|
| 54 |
-
},
|
| 55 |
-
],
|
| 56 |
-
};
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/index.html
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
<!DOCTYPE html>
|
| 3 |
-
<html lang="en">
|
| 4 |
-
<head>
|
| 5 |
-
<meta charset="UTF-8" />
|
| 6 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
-
<title>FastMCP Tool Generator</title>
|
| 8 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
| 10 |
-
<style>
|
| 11 |
-
/* Custom scrollbar for webkit browsers */
|
| 12 |
-
::-webkit-scrollbar {
|
| 13 |
-
width: 8px;
|
| 14 |
-
height: 8px;
|
| 15 |
-
}
|
| 16 |
-
::-webkit-scrollbar-track {
|
| 17 |
-
background: #1f2937; /* bg-gray-800 */
|
| 18 |
-
}
|
| 19 |
-
::-webkit-scrollbar-thumb {
|
| 20 |
-
background: #4b5563; /* bg-gray-600 */
|
| 21 |
-
border-radius: 4px;
|
| 22 |
-
}
|
| 23 |
-
::-webkit-scrollbar-thumb:hover {
|
| 24 |
-
background: #6b7280; /* bg-gray-500 */
|
| 25 |
-
}
|
| 26 |
-
</style>
|
| 27 |
-
<script type="importmap">
|
| 28 |
-
{
|
| 29 |
-
"imports": {
|
| 30 |
-
"react": "https://aistudiocdn.com/react@^19.2.0",
|
| 31 |
-
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/",
|
| 32 |
-
"react/": "https://aistudiocdn.com/react@^19.2.0/",
|
| 33 |
-
"@google/genai": "https://aistudiocdn.com/@google/genai@^1.29.0"
|
| 34 |
-
}
|
| 35 |
-
}
|
| 36 |
-
</script>
|
| 37 |
-
<link rel="stylesheet" href="/index.css">
|
| 38 |
-
</head>
|
| 39 |
-
<body class="bg-gray-900 text-gray-100">
|
| 40 |
-
<div id="root"></div>
|
| 41 |
-
<script type="module" src="/index.tsx"></script>
|
| 42 |
-
</body>
|
| 43 |
-
</html>
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/index.tsx
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import React from 'react';
|
| 3 |
-
import ReactDOM from 'react-dom/client';
|
| 4 |
-
import App from './App';
|
| 5 |
-
|
| 6 |
-
const rootElement = document.getElementById('root');
|
| 7 |
-
if (!rootElement) {
|
| 8 |
-
throw new Error("Could not find root element to mount to");
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
const root = ReactDOM.createRoot(rootElement);
|
| 12 |
-
root.render(
|
| 13 |
-
<React.StrictMode>
|
| 14 |
-
<App />
|
| 15 |
-
</React.StrictMode>
|
| 16 |
-
);
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/metadata.json
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "Copy of Copy of Copy of FastMCP Tool Generator",
|
| 3 |
-
"description": "A web application to generate complete, self-contained, and deployable FastMCP tool boilerplates for Gemini CLI based on user's technical requirements. It creates all necessary files including server logic, setup configuration, and agent instructions.",
|
| 4 |
-
"requestFramePermissions": []
|
| 5 |
-
}
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/package-lock.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
FastMCPToolGenerator/package.json
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
{
|
| 3 |
-
"name": "fastmcp-tool-generator-hf",
|
| 4 |
-
"version": "0.0.0",
|
| 5 |
-
"private": true,
|
| 6 |
-
"scripts": {
|
| 7 |
-
"dev": "vite",
|
| 8 |
-
"build": "tsc && vite build",
|
| 9 |
-
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
| 10 |
-
"preview": "vite preview"
|
| 11 |
-
},
|
| 12 |
-
"dependencies": {
|
| 13 |
-
"autoprefixer": "^10.4.19",
|
| 14 |
-
"axios": "^1.7.2",
|
| 15 |
-
"highlight.js": "^11.9.0",
|
| 16 |
-
"jszip": "^3.10.1",
|
| 17 |
-
"postcss": "^8.4.38",
|
| 18 |
-
"react": "^18.2.0",
|
| 19 |
-
"react-dom": "^18.2.0",
|
| 20 |
-
"react-hot-toast": "^2.4.1",
|
| 21 |
-
"tailwindcss": "^3.4.4"
|
| 22 |
-
},
|
| 23 |
-
"devDependencies": {
|
| 24 |
-
"@types/highlight.js": "^10.1.0",
|
| 25 |
-
"@types/jszip": "^3.10.1",
|
| 26 |
-
"@types/react": "^18.2.66",
|
| 27 |
-
"@types/react-dom": "^18.2.22",
|
| 28 |
-
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
| 29 |
-
"@typescript-eslint/parser": "^7.2.0",
|
| 30 |
-
"@vitejs/plugin-react": "^4.2.1",
|
| 31 |
-
"eslint": "^8.57.0",
|
| 32 |
-
"eslint-plugin-react-hooks": "^4.6.0",
|
| 33 |
-
"eslint-plugin-react-refresh": "^0.4.6",
|
| 34 |
-
"typescript": "^5.2.2",
|
| 35 |
-
"vite": "^5.2.0"
|
| 36 |
-
}
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/requirements.txt
DELETED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
fastapi
|
| 3 |
-
|
| 4 |
-
uvicorn[standard]
|
| 5 |
-
|
| 6 |
-
google-generativeai
|
| 7 |
-
|
| 8 |
-
pydantic
|
| 9 |
-
|
| 10 |
-
python-dotenv
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/services/aiService.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import { ToolDefinition, FunctionDefinition } from '../types';
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
export async function generateToolDefinitionFromPrompt(prompt: string): Promise<string> {
|
| 7 |
-
|
| 8 |
-
console.log("Отправка запроса на бэкенд /api/generate...");
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
try {
|
| 13 |
-
|
| 14 |
-
const response = await fetch('/api/generate', {
|
| 15 |
-
|
| 16 |
-
method: 'POST',
|
| 17 |
-
|
| 18 |
-
headers: {
|
| 19 |
-
|
| 20 |
-
'Content-Type': 'application/json',
|
| 21 |
-
|
| 22 |
-
},
|
| 23 |
-
|
| 24 |
-
body: JSON.stringify({ prompt: prompt }),
|
| 25 |
-
|
| 26 |
-
});
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
if (!response.ok) {
|
| 31 |
-
|
| 32 |
-
const errorData = await response.json();
|
| 33 |
-
|
| 34 |
-
throw new Error(errorData.detail || `HTTP error! status: ${response.status}`);
|
| 35 |
-
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
const data = await response.json();
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
// Бэкенд теперь возвращает объект { jsonString: "..." }
|
| 45 |
-
|
| 46 |
-
if (typeof data.jsonString !== 'string') {
|
| 47 |
-
|
| 48 |
-
throw new Error("Бэкенд вернул неверный формат ответа.");
|
| 49 |
-
|
| 50 |
-
}
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
return data.jsonString;
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
} catch (error) {
|
| 59 |
-
|
| 60 |
-
console.error("Ошибка при вызове бэкенда /api/generate:", error);
|
| 61 |
-
|
| 62 |
-
throw new Error(`Не удалось связаться с сервером: ${error.message}`);
|
| 63 |
-
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/services/codeGenerator.ts
DELETED
|
@@ -1,428 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
import { ToolDefinition, FunctionDefinition, ParameterDefinition, ParameterType, GeneratedCode } from '../types';
|
| 3 |
-
|
| 4 |
-
const toPythonType = (type: ParameterType): string => {
|
| 5 |
-
const mapping: Record<ParameterType, string> = {
|
| 6 |
-
[ParameterType.STRING]: 'str',
|
| 7 |
-
[ParameterType.INTEGER]: 'int',
|
| 8 |
-
[ParameterType.FLOAT]: 'float',
|
| 9 |
-
[ParameterType.BOOLEAN]: 'bool',
|
| 10 |
-
[ParameterType.LIST]: 'list',
|
| 11 |
-
[ParameterType.DICTIONARY]: 'dict',
|
| 12 |
-
};
|
| 13 |
-
return mapping[type] || 'Any';
|
| 14 |
-
};
|
| 15 |
-
|
| 16 |
-
const generateSetupPy = (def: ToolDefinition): string => {
|
| 17 |
-
const dependencies = def.dependencies.split(/[\s,]+/).filter(Boolean);
|
| 18 |
-
const installRequires = dependencies.length > 0
|
| 19 |
-
? `install_requires=${JSON.stringify(dependencies)},`
|
| 20 |
-
: '';
|
| 21 |
-
|
| 22 |
-
return `
|
| 23 |
-
from setuptools import setup, find_packages
|
| 24 |
-
|
| 25 |
-
setup(
|
| 26 |
-
name='${def.name}',
|
| 27 |
-
version='0.1.0',
|
| 28 |
-
packages=find_packages(),
|
| 29 |
-
${installRequires}
|
| 30 |
-
entry_points={
|
| 31 |
-
'gemini_tools': [
|
| 32 |
-
'${def.name} = server:main',
|
| 33 |
-
],
|
| 34 |
-
},
|
| 35 |
-
)
|
| 36 |
-
`.trim();
|
| 37 |
-
};
|
| 38 |
-
|
| 39 |
-
const generateServerPy = (def: ToolDefinition): string => {
|
| 40 |
-
const functionStrings = def.functions.map(func => generateFunction(def, func)).join('\n\n');
|
| 41 |
-
const imports = def.name === 'nmap_scanner' ? 'import nmap' : '# TODO: Добавьте необходимые импорты';
|
| 42 |
-
|
| 43 |
-
return `
|
| 44 |
-
# -*- coding: utf-8 -*-
|
| 45 |
-
import sys
|
| 46 |
-
import json
|
| 47 |
-
from typing import Any, Dict, List
|
| 48 |
-
|
| 49 |
-
# =====================================================================================
|
| 50 |
-
# вљ пёЏ Важно: Ртот РєРѕРґ сгенерирован автоматически.
|
| 51 |
-
# Внесите свои изменения в секцию "Реализация функций"
|
| 52 |
-
# =====================================================================================
|
| 53 |
-
|
| 54 |
-
# =====================================================================================
|
| 55 |
-
# Секция: Рмпорты
|
| 56 |
-
# =====================================================================================
|
| 57 |
-
${imports}
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
# =====================================================================================
|
| 61 |
-
# Секция: Реализация функций
|
| 62 |
-
# =====================================================================================
|
| 63 |
-
# TODO: Добавьте здесь вашу бизнес-логику для каждой функции.
|
| 64 |
-
|
| 65 |
-
${functionStrings}
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
# =====================================================================================
|
| 69 |
-
# Секция: FastMCP/STDIO Транспорт (Не изменять)
|
| 70 |
-
# =====================================================================================
|
| 71 |
-
|
| 72 |
-
def _get_function_schema(func) -> Dict[str, Any]:
|
| 73 |
-
"""Генерирует OpenAPI-совместимую схему из докстринга функции."""
|
| 74 |
-
if not func.__doc__:
|
| 75 |
-
return {}
|
| 76 |
-
|
| 77 |
-
lines = func.__doc__.strip().split('\\n')
|
| 78 |
-
description = lines[0].strip()
|
| 79 |
-
|
| 80 |
-
properties = {}
|
| 81 |
-
required = []
|
| 82 |
-
|
| 83 |
-
args_section = False
|
| 84 |
-
for line in lines:
|
| 85 |
-
line = line.strip()
|
| 86 |
-
if line.startswith('Args:'):
|
| 87 |
-
args_section = True
|
| 88 |
-
continue
|
| 89 |
-
if line.startswith('Returns:'):
|
| 90 |
-
args_section = False
|
| 91 |
-
continue
|
| 92 |
-
|
| 93 |
-
if args_section and ':' in line:
|
| 94 |
-
# Обработка необязательных параметров, обозначенных (*), например "аргумент (*str*):"
|
| 95 |
-
is_required = not line.strip().startswith('(')
|
| 96 |
-
|
| 97 |
-
arg_name_part, arg_desc = line.split(':', 1)
|
| 98 |
-
arg_name = arg_name_part.split('(')[0].strip()
|
| 99 |
-
|
| 100 |
-
# Предполагаем тип из аннотации
|
| 101 |
-
param_type_hint = func.__annotations__.get(arg_name, 'string')
|
| 102 |
-
if hasattr(param_type_hint, '__name__'):
|
| 103 |
-
param_type_hint = param_type_hint.__name__
|
| 104 |
-
|
| 105 |
-
openapi_type = {
|
| 106 |
-
'str': 'string',
|
| 107 |
-
'int': 'integer',
|
| 108 |
-
'float': 'number',
|
| 109 |
-
'bool': 'boolean',
|
| 110 |
-
'list': 'array',
|
| 111 |
-
'dict': 'object'
|
| 112 |
-
}.get(param_type_hint, 'string')
|
| 113 |
-
|
| 114 |
-
properties[arg_name] = {
|
| 115 |
-
'type': openapi_type,
|
| 116 |
-
'description': arg_desc.strip()
|
| 117 |
-
}
|
| 118 |
-
if is_required:
|
| 119 |
-
required.append(arg_name)
|
| 120 |
-
|
| 121 |
-
return {
|
| 122 |
-
"name": func.__name__,
|
| 123 |
-
"description": description,
|
| 124 |
-
"parameters": {
|
| 125 |
-
"type": "object",
|
| 126 |
-
"properties": properties,
|
| 127 |
-
"required": required
|
| 128 |
-
}
|
| 129 |
-
}
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
def main():
|
| 133 |
-
"""Главная функция для обработки stdio."""
|
| 134 |
-
functions = {
|
| 135 |
-
${def.functions.map(f => `"${f.name}": ${f.name}`).join(',\n ')}
|
| 136 |
-
}
|
| 137 |
-
|
| 138 |
-
if len(sys.argv) > 1 and sys.argv[1] == 'discover':
|
| 139 |
-
tool_schema = {
|
| 140 |
-
"name": "${def.name}",
|
| 141 |
-
"tool_spec": {
|
| 142 |
-
"function_declarations": [
|
| 143 |
-
_get_function_schema(func) for func in functions.values()
|
| 144 |
-
]
|
| 145 |
-
}
|
| 146 |
-
}
|
| 147 |
-
print(json.dumps(tool_schema, ensure_ascii=False))
|
| 148 |
-
return
|
| 149 |
-
|
| 150 |
-
for line in sys.stdin:
|
| 151 |
-
try:
|
| 152 |
-
call = json.loads(line)
|
| 153 |
-
function_name = call['function_call']['name']
|
| 154 |
-
args = call['function_call']['args']
|
| 155 |
-
|
| 156 |
-
if function_name in functions:
|
| 157 |
-
result = functions[function_name](**args)
|
| 158 |
-
response = {
|
| 159 |
-
"tool_response": {
|
| 160 |
-
"name": function_name,
|
| 161 |
-
"content": json.dumps(result, ensure_ascii=False)
|
| 162 |
-
}
|
| 163 |
-
}
|
| 164 |
-
print(json.dumps(response, ensure_ascii=False))
|
| 165 |
-
else:
|
| 166 |
-
# Обработка ошибки: функция не найдена
|
| 167 |
-
error_response = {"tool_response": {"name": function_name, "content": json.dumps({"error": f"Function {function_name} not found."})}}
|
| 168 |
-
print(json.dumps(error_response, ensure_ascii=False))
|
| 169 |
-
except (json.JSONDecodeError, KeyError) as e:
|
| 170 |
-
# Обработка ошибок парсинга или структуры JSON
|
| 171 |
-
error_response = {"tool_response": {"name": "unknown", "content": json.dumps({"error": f"Invalid input JSON: {e}"})}}
|
| 172 |
-
print(json.dumps(error_response, ensure_ascii=False))
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
if __name__ == "__main__":
|
| 176 |
-
main()
|
| 177 |
-
`.trim();
|
| 178 |
-
};
|
| 179 |
-
|
| 180 |
-
const generateFunctionBody = (def: ToolDefinition, func: FunctionDefinition): string => {
|
| 181 |
-
// Generate real, working code for the nmap_scanner example
|
| 182 |
-
if (def.name === 'nmap_scanner') {
|
| 183 |
-
if (func.name === 'run_scan') {
|
| 184 |
-
return `
|
| 185 |
-
try:
|
| 186 |
-
nm = nmap.PortScanner()
|
| 187 |
-
# nmap.scan() возвращает результат сканирования
|
| 188 |
-
scan_result = nm.scan(hosts=targets, ports=ports, arguments=arguments)
|
| 189 |
-
return scan_result
|
| 190 |
-
except nmap.PortScannerError as e:
|
| 191 |
-
return {"error": f"Ошибка сканирования Nmap: {e}. Убедитесь, что nmap установлен и доступен в системном PATH."}
|
| 192 |
-
except Exception as e:
|
| 193 |
-
return {"error": f"Произошла непредвиденная ошибка: {e}"}
|
| 194 |
-
`.trim();
|
| 195 |
-
}
|
| 196 |
-
if (func.name === 'host_discovery') {
|
| 197 |
-
return `
|
| 198 |
-
try:
|
| 199 |
-
nm = nmap.PortScanner()
|
| 200 |
-
# Аргумент -sn выполняет "ping scan" для обнаружения хостов без сканирования портов
|
| 201 |
-
nm.scan(hosts=network_cidr, arguments='-sn')
|
| 202 |
-
# nm.all_hosts() возвращает список хостов, которые находятся в состоянии 'up'
|
| 203 |
-
return {"active_hosts": nm.all_hosts()}
|
| 204 |
-
except nmap.PortScannerError as e:
|
| 205 |
-
return {"error": f"Ошибка сканирования Nmap: {e}. Убедитесь, что nmap установлен и доступен в системном PATH."}
|
| 206 |
-
except Exception as e:
|
| 207 |
-
return {"error": f"Произошла непредвиденная ошибка: {e}"}
|
| 208 |
-
`.trim();
|
| 209 |
-
}
|
| 210 |
-
}
|
| 211 |
-
|
| 212 |
-
// Default placeholder for other tools
|
| 213 |
-
const mockReturnValue = () => {
|
| 214 |
-
switch(func.returnType) {
|
| 215 |
-
case ParameterType.STRING: return '"Результат выполнен��ёСЏ функции"';
|
| 216 |
-
case ParameterType.INTEGER: return '123';
|
| 217 |
-
case ParameterType.FLOAT: return '123.45';
|
| 218 |
-
case ParameterType.BOOLEAN: return 'True';
|
| 219 |
-
case ParameterType.LIST: return '["элемент1", "элемент2"]';
|
| 220 |
-
case ParameterType.DICTIONARY: return '{"ключ": "значение"}';
|
| 221 |
-
default: return 'None';
|
| 222 |
-
}
|
| 223 |
-
};
|
| 224 |
-
|
| 225 |
-
return `
|
| 226 |
-
# TODO: Реализуйте логику функции здесь
|
| 227 |
-
# Рто пример. Замените его своей реализацией.
|
| 228 |
-
print(f"Функция '${func.name}' вызвана со следующими аргументами:")
|
| 229 |
-
${func.parameters.map(p => ` print(f"- ${p.name}: {${p.name}}")`).join('\n')}
|
| 230 |
-
|
| 231 |
-
return ${mockReturnValue()}
|
| 232 |
-
`.trim();
|
| 233 |
-
};
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
const generateFunction = (def: ToolDefinition, func: FunctionDefinition): string => {
|
| 237 |
-
const paramsString = func.parameters.map(p => {
|
| 238 |
-
const typeHint = toPythonType(p.type);
|
| 239 |
-
const defaultValue = p.required ? '' : ' = None';
|
| 240 |
-
return `${p.name}: ${typeHint}${defaultValue}`;
|
| 241 |
-
}).join(', ');
|
| 242 |
-
const returnTypeString = toPythonType(func.returnType);
|
| 243 |
-
|
| 244 |
-
const docstringParams = func.parameters.map(p =>
|
| 245 |
-
` ${p.name} (${toPythonType(p.type)}): ${p.description}`
|
| 246 |
-
).join('\n');
|
| 247 |
-
|
| 248 |
-
const docstring = `
|
| 249 |
-
"""${func.description}
|
| 250 |
-
|
| 251 |
-
Args:
|
| 252 |
-
${docstringParams || ' Нет.'}
|
| 253 |
-
|
| 254 |
-
Returns:
|
| 255 |
-
${returnTypeString}: ${func.returnDescription}
|
| 256 |
-
"""
|
| 257 |
-
`.trim();
|
| 258 |
-
|
| 259 |
-
const body = generateFunctionBody(def, func);
|
| 260 |
-
|
| 261 |
-
return `
|
| 262 |
-
def ${func.name}(${paramsString}) -> ${returnTypeString}:
|
| 263 |
-
${docstring}
|
| 264 |
-
${body}
|
| 265 |
-
`.trim();
|
| 266 |
-
};
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
const generateSettingsJson = (def: ToolDefinition): string => {
|
| 270 |
-
return JSON.stringify({
|
| 271 |
-
servers: [
|
| 272 |
-
{
|
| 273 |
-
name: def.name,
|
| 274 |
-
command: ['python', 'server.py'],
|
| 275 |
-
transport: 'stdio',
|
| 276 |
-
type: def.type
|
| 277 |
-
}
|
| 278 |
-
]
|
| 279 |
-
}, null, 2);
|
| 280 |
-
};
|
| 281 |
-
|
| 282 |
-
const generateGeminiMd = (def: ToolDefinition): string => {
|
| 283 |
-
const functionDocs = def.functions.map(func => {
|
| 284 |
-
const paramsList = func.parameters.map(p => `- \`${p.name}\` (*${toPythonType(p.type)}*): ${p.description}${p.required ? ' (обязательный)' : ''}`).join('\n');
|
| 285 |
-
return `
|
| 286 |
-
### \`${def.name}:${func.name}\`
|
| 287 |
-
|
| 288 |
-
${func.description}
|
| 289 |
-
|
| 290 |
-
**Параметры:**
|
| 291 |
-
${paramsList || 'Нет параметров.'}
|
| 292 |
-
|
| 293 |
-
**Возвращает:**
|
| 294 |
-
*${toPythonType(func.returnType)}* - ${func.returnDescription}
|
| 295 |
-
`.trim();
|
| 296 |
-
}).join('\n\n---\n\n');
|
| 297 |
-
|
| 298 |
-
const nmapExamples = `
|
| 299 |
-
## Правила Рё Примеры Рспользования
|
| 300 |
-
|
| 301 |
-
**ВАЖНО:** Всегда используй формат \`nmap_scanner:имя_функции()\`.
|
| 302 |
-
|
| 303 |
-
Если пользователь просит **обнаружить активные устройства в сети**, используй \`host_discovery\`.
|
| 304 |
-
*Запрос пользователя:* "Найди все работающие компьютеры в сети 192.168.0.0/24"
|
| 305 |
-
*Твой вызов:* \`nmap_scanner:host_discovery(network_cidr="192.168.0.0/24")\`
|
| 306 |
-
|
| 307 |
-
Если пользователь просит **просканировать порты или найти уязвимости**, используй \`run_scan\`.
|
| 308 |
-
*Запрос пользователя:* "Просканируй хост 192.168.1.1 на наличие открытых портов и определи запущенные службы"
|
| 309 |
-
*Твой вызов:* \`nmap_scanner:run_scan(targets="192.168.1.1", ports="1-1024", arguments="-sV")\`
|
| 310 |
-
|
| 311 |
-
*Запрос пользователя:* "Проведи агрессивное сканирование scanme.nmap.org, чтобы найти возможные векторы ата��и"
|
| 312 |
-
*Твой вызов:* \`nmap_scanner:run_scan(targets="scanme.nmap.org", ports="1-65535", arguments="-A -v")\`
|
| 313 |
-
`.trim();
|
| 314 |
-
|
| 315 |
-
const genericExample = `
|
| 316 |
-
## Пример использования
|
| 317 |
-
|
| 318 |
-
**Простой вызов:**
|
| 319 |
-
\`\`\`
|
| 320 |
-
${def.name}:${def.functions[0]?.name}(${def.functions[0]?.parameters.filter(p => p.required).map(p => `${p.name}="значение"`).join(', ')})
|
| 321 |
-
\`\`\`
|
| 322 |
-
`.trim();
|
| 323 |
-
|
| 324 |
-
return `
|
| 325 |
-
# Рнструкция для Агента: Рнструмент "${def.name}"
|
| 326 |
-
|
| 327 |
-
## Описание
|
| 328 |
-
|
| 329 |
-
Ртот инструмент предоставляет набор утилит для [опишите общую цель инструмента, например: сетевой разведки СЃ помощью Nmap]. РћРЅ относится Рє типу **${def.type}**.
|
| 330 |
-
|
| 331 |
-
## Функции
|
| 332 |
-
|
| 333 |
-
${functionDocs}
|
| 334 |
-
|
| 335 |
-
${def.name === 'nmap_scanner' ? nmapExamples : genericExample}
|
| 336 |
-
`.trim();
|
| 337 |
-
};
|
| 338 |
-
|
| 339 |
-
const getExampleValue = (param: ParameterDefinition): string => {
|
| 340 |
-
const { name, type } = param;
|
| 341 |
-
const lowerParamName = name.toLowerCase();
|
| 342 |
-
|
| 343 |
-
if (type === ParameterType.STRING) {
|
| 344 |
-
if (lowerParamName.includes('target') || lowerParamName.includes('host') || lowerParamName.includes('domain')) return "'scanme.nmap.org'";
|
| 345 |
-
if (lowerParamName.includes('port')) return "'80,443,8080'";
|
| 346 |
-
if (lowerParamName.includes('cidr') || lowerParamName.includes('network')) return "'192.168.1.0/24'";
|
| 347 |
-
if (lowerParamName.includes('argument')) return "'-A -v'";
|
| 348 |
-
if (lowerParamName.includes('file') || lowerParamName.includes('path')) return "'/path/to/file.txt'";
|
| 349 |
-
if (lowerParamName.includes('url')) return "'https://example.com'";
|
| 350 |
-
if (lowerParamName.includes('name')) return "'John Doe'";
|
| 351 |
-
return "'example_value'";
|
| 352 |
-
}
|
| 353 |
-
|
| 354 |
-
switch (type) {
|
| 355 |
-
case ParameterType.INTEGER: return '1024';
|
| 356 |
-
case ParameterType.FLOAT: return '3.14';
|
| 357 |
-
case ParameterType.BOOLEAN: return 'True';
|
| 358 |
-
case ParameterType.LIST: return '["item1", "item2"]';
|
| 359 |
-
case ParameterType.DICTIONARY: return '{"key": "value"}';
|
| 360 |
-
default: return 'None';
|
| 361 |
-
}
|
| 362 |
-
};
|
| 363 |
-
|
| 364 |
-
const getNaturalLanguageExample = (defName: string, func: FunctionDefinition): string => {
|
| 365 |
-
if (defName === 'nmap_scanner') {
|
| 366 |
-
if (func.name === 'run_scan') {
|
| 367 |
-
return '> "Просканируй scanme.nmap.org, чтобы найти возможные векторы атаки."';
|
| 368 |
-
}
|
| 369 |
-
if (func.name === 'host_discovery') {
|
| 370 |
-
return '> "Найди все активные устройства в моей локальной сети 192.168.1.0/24."';
|
| 371 |
-
}
|
| 372 |
-
}
|
| 373 |
-
return `> "Выполни функцию ${func.name} с тестовыми параметрами."`;
|
| 374 |
-
}
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
const generateExamplesMd = (def: ToolDefinition): string => {
|
| 378 |
-
if (!def.functions || def.functions.length === 0) {
|
| 379 |
-
return '# Примеры использования\n\nРнструмент РЅРµ содержит функций для генерации примеров.';
|
| 380 |
-
}
|
| 381 |
-
|
| 382 |
-
const exampleSnippets = def.functions.map(func => {
|
| 383 |
-
const paramsString = func.parameters
|
| 384 |
-
.map(p => `${p.name}=${getExampleValue(p)}`)
|
| 385 |
-
.join(', ');
|
| 386 |
-
|
| 387 |
-
const exampleCall = `${def.name}:${func.name}(${paramsString})`;
|
| 388 |
-
const naturalLanguageQuery = getNaturalLanguageExample(def.name, func);
|
| 389 |
-
|
| 390 |
-
return `
|
| 391 |
-
### Функция: \`${func.name}\`
|
| 392 |
-
|
| 393 |
-
${func.description}
|
| 394 |
-
|
| 395 |
-
**Пример запроса на естественном языке:**
|
| 396 |
-
${naturalLanguageQuery}
|
| 397 |
-
|
| 398 |
-
**В ответ Gemini CLI сгенерирует следующий вызов:**
|
| 399 |
-
\`\`\`bash
|
| 400 |
-
${exampleCall}
|
| 401 |
-
\`\`\`
|
| 402 |
-
`.trim();
|
| 403 |
-
}).join('\n\n---\n\n');
|
| 404 |
-
|
| 405 |
-
return `
|
| 406 |
-
# Примеры использования для инструмента "${def.name}"
|
| 407 |
-
|
| 408 |
-
Gemini CLI понимает естественный язык. Он анализирует ваш запрос и вызывает соответствующую функцию инструмента. Ниже показано, как ваши запросы преобразуются в конкретные вызовы.
|
| 409 |
-
|
| 410 |
-
---
|
| 411 |
-
|
| 412 |
-
${exampleSnippets}
|
| 413 |
-
`.trim();
|
| 414 |
-
};
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
export const generateAllCode = (def: ToolDefinition): GeneratedCode => {
|
| 418 |
-
return {
|
| 419 |
-
setupPy: generateSetupPy(def),
|
| 420 |
-
serverPy: generateServerPy(def),
|
| 421 |
-
settingsJson: generateSettingsJson(def),
|
| 422 |
-
geminiMd: generateGeminiMd(def),
|
| 423 |
-
examplesMd: generateExamplesMd(def),
|
| 424 |
-
};
|
| 425 |
-
};
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/style.css
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
body {
|
| 2 |
-
padding: 2rem;
|
| 3 |
-
font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
-
h1 {
|
| 7 |
-
font-size: 16px;
|
| 8 |
-
margin-top: 0;
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
p {
|
| 12 |
-
color: rgb(107, 114, 128);
|
| 13 |
-
font-size: 15px;
|
| 14 |
-
margin-bottom: 10px;
|
| 15 |
-
margin-top: 5px;
|
| 16 |
-
}
|
| 17 |
-
|
| 18 |
-
.card {
|
| 19 |
-
max-width: 620px;
|
| 20 |
-
margin: 0 auto;
|
| 21 |
-
padding: 16px;
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
.card p:last-child {
|
| 27 |
-
margin-bottom: 0;
|
| 28 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/tsconfig.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"compilerOptions": {
|
| 3 |
-
"target": "ES2022",
|
| 4 |
-
"experimentalDecorators": true,
|
| 5 |
-
"useDefineForClassFields": false,
|
| 6 |
-
"module": "ESNext",
|
| 7 |
-
"lib": [
|
| 8 |
-
"ES2022",
|
| 9 |
-
"DOM",
|
| 10 |
-
"DOM.Iterable"
|
| 11 |
-
],
|
| 12 |
-
"skipLibCheck": true,
|
| 13 |
-
"types": [
|
| 14 |
-
"node"
|
| 15 |
-
],
|
| 16 |
-
"moduleResolution": "bundler",
|
| 17 |
-
"isolatedModules": true,
|
| 18 |
-
"moduleDetection": "force",
|
| 19 |
-
"allowJs": true,
|
| 20 |
-
"jsx": "react-jsx",
|
| 21 |
-
"paths": {
|
| 22 |
-
"@/*": [
|
| 23 |
-
"./*"
|
| 24 |
-
]
|
| 25 |
-
},
|
| 26 |
-
"allowImportingTsExtensions": true,
|
| 27 |
-
"noEmit": true
|
| 28 |
-
}
|
| 29 |
-
}
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/types.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
export enum ToolType {
|
| 3 |
-
REST_API = 'REST_API',
|
| 4 |
-
CLI = 'CLI',
|
| 5 |
-
DATABASE = 'DATABASE',
|
| 6 |
-
CUSTOM = 'CUSTOM',
|
| 7 |
-
UTILITY = 'UTILITY',
|
| 8 |
-
CALCULATOR = 'CALCULATOR',
|
| 9 |
-
CONVERTER = 'CONVERTER'
|
| 10 |
-
}
|
| 11 |
-
|
| 12 |
-
export enum ParameterType {
|
| 13 |
-
STRING = 'str',
|
| 14 |
-
INTEGER = 'int',
|
| 15 |
-
FLOAT = 'float',
|
| 16 |
-
BOOLEAN = 'bool',
|
| 17 |
-
LIST = 'list',
|
| 18 |
-
DICTIONARY = 'dict'
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
export interface ParameterDefinition {
|
| 22 |
-
id: string;
|
| 23 |
-
name: string;
|
| 24 |
-
type: ParameterType;
|
| 25 |
-
description: string;
|
| 26 |
-
required: boolean;
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
export interface FunctionDefinition {
|
| 30 |
-
id: string;
|
| 31 |
-
name: string;
|
| 32 |
-
description: string;
|
| 33 |
-
parameters: ParameterDefinition[];
|
| 34 |
-
returnType: ParameterType;
|
| 35 |
-
returnDescription: string;
|
| 36 |
-
}
|
| 37 |
-
|
| 38 |
-
export interface ToolDefinition {
|
| 39 |
-
name: string;
|
| 40 |
-
type: ToolType;
|
| 41 |
-
dependencies: string;
|
| 42 |
-
functions: FunctionDefinition[];
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
export interface GeneratedCode {
|
| 46 |
-
setupPy: string;
|
| 47 |
-
serverPy: string;
|
| 48 |
-
settingsJson: string;
|
| 49 |
-
geminiMd: string;
|
| 50 |
-
examplesMd: string;
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
// Ркспортируем для использования РІ AI промпте
|
| 54 |
-
export const initialFunctionForPrompt: Omit<FunctionDefinition, 'id'> = {
|
| 55 |
-
name: 'example_function',
|
| 56 |
-
description: 'Краткое описание того, что делает эта функция.',
|
| 57 |
-
parameters: [
|
| 58 |
-
{
|
| 59 |
-
id: 'param-id-1', // id здесь для примера, в реальности он генерируется
|
| 60 |
-
name: 'example_param',
|
| 61 |
-
type: ParameterType.STRING,
|
| 62 |
-
description: 'Описание параметра.',
|
| 63 |
-
required: true,
|
| 64 |
-
},
|
| 65 |
-
],
|
| 66 |
-
returnType: ParameterType.STRING,
|
| 67 |
-
returnDescription: 'Описание возвращаемого значения.',
|
| 68 |
-
};
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FastMCPToolGenerator/vite.config.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
| 1 |
-
import path from 'path';
|
| 2 |
-
|
| 3 |
-
import { defineConfig, loadEnv } from 'vite';
|
| 4 |
-
|
| 5 |
-
import react from '@vitejs/plugin-react';
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
export default defineConfig(({ mode }) => {
|
| 10 |
-
|
| 11 |
-
const env = loadEnv(mode, '.', '');
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
// Порт для FastAPI (бэкенда) при локальной разработке
|
| 16 |
-
|
| 17 |
-
const PYTHON_BACKEND_PORT = 8000;
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
return {
|
| 22 |
-
|
| 23 |
-
server: {
|
| 24 |
-
|
| 25 |
-
// Используем порт 3000 для Vite (фронтенда)
|
| 26 |
-
|
| 27 |
-
port: 3000,
|
| 28 |
-
|
| 29 |
-
host: '0.0.0.0',
|
| 30 |
-
|
| 31 |
-
proxy: {
|
| 32 |
-
|
| 33 |
-
// Перенаправляем все /api запросы на наш Python бэкенд
|
| 34 |
-
|
| 35 |
-
'/api': {
|
| 36 |
-
|
| 37 |
-
target: `http://127.0.0.1:${PYTHON_BACKEND_PORT}`, // ИСПРАВЛЕНО
|
| 38 |
-
|
| 39 |
-
changeOrigin: true,
|
| 40 |
-
|
| 41 |
-
secure: false,
|
| 42 |
-
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
},
|
| 48 |
-
|
| 49 |
-
plugins: [react()],
|
| 50 |
-
|
| 51 |
-
define: {
|
| 52 |
-
|
| 53 |
-
// Ключ API БОЛЬШЕ НЕ НУЖЕН в define!
|
| 54 |
-
|
| 55 |
-
},
|
| 56 |
-
|
| 57 |
-
resolve: {
|
| 58 |
-
|
| 59 |
-
alias: {
|
| 60 |
-
|
| 61 |
-
'@': path.resolve(__dirname, '.'),
|
| 62 |
-
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
};
|
| 68 |
-
|
| 69 |
-
});
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|