mcp-proxy-adapter 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,416 @@
1
+ """
2
+ Модуль для создания RPC части OpenAPI схемы.
3
+ Содержит описание единого RPC эндпоинта для вызова команд.
4
+ """
5
+ from typing import Dict, Any
6
+
7
+ def get_rpc_schema() -> Dict[str, Any]:
8
+ """
9
+ Возвращает RPC часть OpenAPI схемы.
10
+
11
+ Returns:
12
+ dict: RPC часть OpenAPI схемы
13
+ """
14
+ return {
15
+ "paths": {
16
+ "/cmd": {
17
+ "post": {
18
+ "summary": "Универсальный RPC эндпоинт для выполнения команд",
19
+ "description": "**Основной интерфейс взаимодействия с системой**. Эндпоинт принимает структурированный JSON-RPC 2.0 запрос с командой и параметрами, выполняет указанную операцию и возвращает результат. Поддерживает работу только с векторами размерности 384.",
20
+ "operationId": "executeCommand",
21
+ "requestBody": {
22
+ "required": True,
23
+ "content": {
24
+ "application/json": {
25
+ "schema": {
26
+ "$ref": "#/components/schemas/CommandRequest"
27
+ }
28
+ }
29
+ }
30
+ },
31
+ "responses": {
32
+ "200": {
33
+ "description": "Результат выполнения команды (независимо от успеха или ошибки). API всегда возвращает код 200 и использует поле 'success' для индикации успеха операции.",
34
+ "content": {
35
+ "application/json": {
36
+ "schema": {
37
+ "$ref": "#/components/schemas/ApiResponse"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ },
46
+ "components": {
47
+ "schemas": {
48
+ "CommandRequest": {
49
+ "properties": {
50
+ "command": {
51
+ "type": "string",
52
+ "title": "Command",
53
+ "description": "Название команды для выполнения",
54
+ "enum": [
55
+ "health", "status", "help", "get_metadata", "get_text",
56
+ "create_record", "add_vector", "create_text_record", "add_text",
57
+ "search_records", "search_by_vector", "search_text_records",
58
+ "search_by_text", "filter_records", "filter", "delete_records",
59
+ "delete", "get_by_session_message"
60
+ ]
61
+ },
62
+ "params": {
63
+ "title": "Params",
64
+ "description": "Параметры команды",
65
+ "type": "object",
66
+ "additionalProperties": True
67
+ },
68
+ "jsonrpc": {
69
+ "type": "string",
70
+ "description": "Версия JSON-RPC протокола",
71
+ "enum": ["2.0"],
72
+ "default": "2.0"
73
+ },
74
+ "id": {
75
+ "type": "string",
76
+ "description": "Идентификатор запроса (опционально)"
77
+ }
78
+ },
79
+ "type": "object",
80
+ "required": ["command"],
81
+ "title": "CommandRequest",
82
+ "description": "Модель для запроса выполнения команды векторного хранилища"
83
+ },
84
+ "JsonRpcResponse": {
85
+ "type": "object",
86
+ "required": ["jsonrpc", "success"],
87
+ "properties": {
88
+ "jsonrpc": {
89
+ "type": "string",
90
+ "description": "Версия JSON-RPC протокола",
91
+ "enum": ["2.0"],
92
+ "default": "2.0"
93
+ },
94
+ "success": {
95
+ "type": "boolean",
96
+ "description": "Индикатор успешности операции",
97
+ "default": False
98
+ },
99
+ "result": {
100
+ "description": "Результат операции. Присутствует только при успешном выполнении (success=True). Формат результата зависит от выполненной команды."
101
+ },
102
+ "error": {
103
+ "description": "Информация об ошибке. Присутствует только при возникновении ошибки (success=\"false\").",
104
+ "type": "object",
105
+ "required": ["code", "message"],
106
+ "properties": {
107
+ "code": {
108
+ "type": "integer",
109
+ "description": "Код ошибки (внутренний код, не HTTP-статус)",
110
+ "example": 400
111
+ },
112
+ "message": {
113
+ "type": "string",
114
+ "description": "Текстовое описание ошибки",
115
+ "example": "Запись не существует: ID 12345"
116
+ }
117
+ }
118
+ },
119
+ "id": {
120
+ "type": "string",
121
+ "description": "Идентификатор запроса (если был указан в запросе)"
122
+ }
123
+ },
124
+ "example": {
125
+ "jsonrpc": "2.0",
126
+ "success": True,
127
+ "result": {"id": "550e8400-e29b-41d4-a716-446655440000"}
128
+ }
129
+ },
130
+ "HealthParams": {
131
+ "type": "object",
132
+ "properties": {},
133
+ "title": "HealthParams",
134
+ "description": "Параметры для команды health"
135
+ },
136
+ "StatusParams": {
137
+ "type": "object",
138
+ "properties": {},
139
+ "title": "StatusParams",
140
+ "description": "Параметры для команды status"
141
+ },
142
+ "HelpParams": {
143
+ "type": "object",
144
+ "properties": {},
145
+ "title": "HelpParams",
146
+ "description": "Параметры для команды help"
147
+ },
148
+ "GetMetadataParams": {
149
+ "type": "object",
150
+ "properties": {
151
+ "id": {
152
+ "type": "string",
153
+ "format": "uuid",
154
+ "description": "UUID4 идентификатор записи"
155
+ }
156
+ },
157
+ "required": ["id"],
158
+ "title": "GetMetadataParams",
159
+ "description": "Параметры для команды get_metadata"
160
+ },
161
+ "GetTextParams": {
162
+ "type": "object",
163
+ "properties": {
164
+ "id": {
165
+ "type": "string",
166
+ "format": "uuid",
167
+ "description": "UUID4 идентификатор записи"
168
+ }
169
+ },
170
+ "required": ["id"],
171
+ "title": "GetTextParams",
172
+ "description": "Параметры для команды get_text"
173
+ },
174
+ "CreateRecordParams": {
175
+ "type": "object",
176
+ "properties": {
177
+ "vector": {
178
+ "$ref": "#/components/schemas/Vector"
179
+ },
180
+ "metadata": {
181
+ "type": "object",
182
+ "description": "Метаданные записи"
183
+ },
184
+ "session_id": {
185
+ "type": "string",
186
+ "format": "uuid",
187
+ "description": "UUID4 идентификатор сессии (опционально)"
188
+ },
189
+ "message_id": {
190
+ "type": "string",
191
+ "format": "uuid",
192
+ "description": "UUID4 идентификатор сообщения (опционально)"
193
+ },
194
+ "timestamp": {
195
+ "type": "string",
196
+ "format": "date-time",
197
+ "description": "Временная метка в формате ISO 8601 (опционально)"
198
+ }
199
+ },
200
+ "required": ["vector"],
201
+ "title": "CreateRecordParams",
202
+ "description": "Параметры для создания новой векторной записи"
203
+ },
204
+ "CreateTextRecordParams": {
205
+ "type": "object",
206
+ "properties": {
207
+ "text": {
208
+ "type": "string",
209
+ "description": "Текст для векторизации"
210
+ },
211
+ "metadata": {
212
+ "type": "object",
213
+ "description": "Метаданные записи"
214
+ },
215
+ "model": {
216
+ "type": "string",
217
+ "description": "Модель для векторизации (опционально)"
218
+ },
219
+ "session_id": {
220
+ "type": "string",
221
+ "format": "uuid",
222
+ "description": "UUID4 идентификатор сессии (опционально)"
223
+ },
224
+ "message_id": {
225
+ "type": "string",
226
+ "format": "uuid",
227
+ "description": "UUID4 идентификатор сообщения (опционально)"
228
+ },
229
+ "timestamp": {
230
+ "type": "string",
231
+ "format": "date-time",
232
+ "description": "Временная метка в формате ISO 8601 (опционально)"
233
+ }
234
+ },
235
+ "required": ["text"],
236
+ "title": "CreateTextRecordParams",
237
+ "description": "Параметры для создания новой текстовой записи"
238
+ },
239
+ "SearchRecordsParams": {
240
+ "type": "object",
241
+ "properties": {
242
+ "vector": {
243
+ "$ref": "#/components/schemas/Vector"
244
+ },
245
+ "limit": {
246
+ "type": "integer",
247
+ "description": "Максимальное количество результатов",
248
+ "default": 10
249
+ },
250
+ "threshold": {
251
+ "type": "number",
252
+ "description": "Порог схожести (от 0 до 1)",
253
+ "default": 0.7
254
+ }
255
+ },
256
+ "required": ["vector"],
257
+ "title": "SearchRecordsParams",
258
+ "description": "Параметры для поиска векторных записей"
259
+ },
260
+ "SearchTextRecordsParams": {
261
+ "type": "object",
262
+ "properties": {
263
+ "text": {
264
+ "type": "string",
265
+ "description": "Текст для поиска"
266
+ },
267
+ "limit": {
268
+ "type": "integer",
269
+ "description": "Максимальное количество результатов",
270
+ "default": 10
271
+ },
272
+ "threshold": {
273
+ "type": "number",
274
+ "description": "Порог схожести (от 0 до 1)",
275
+ "default": 0.7
276
+ }
277
+ },
278
+ "required": ["text"],
279
+ "title": "SearchTextRecordsParams",
280
+ "description": "Параметры для поиска текстовых записей"
281
+ },
282
+ "FilterRecordsParams": {
283
+ "type": "object",
284
+ "properties": {
285
+ "metadata": {
286
+ "type": "object",
287
+ "description": "Критерии фильтрации по метаданным"
288
+ },
289
+ "limit": {
290
+ "type": "integer",
291
+ "description": "Максимальное количество результатов",
292
+ "default": 10
293
+ }
294
+ },
295
+ "required": ["metadata"],
296
+ "title": "FilterRecordsParams",
297
+ "description": "Параметры для фильтрации записей"
298
+ },
299
+ "DeleteRecordsParams": {
300
+ "type": "object",
301
+ "properties": {
302
+ "ids": {
303
+ "type": "array",
304
+ "items": {
305
+ "type": "string",
306
+ "format": "uuid"
307
+ },
308
+ "description": "Список UUID4 идентификаторов записей для удаления"
309
+ }
310
+ },
311
+ "required": ["ids"],
312
+ "title": "DeleteRecordsParams",
313
+ "description": "Параметры для удаления записей"
314
+ },
315
+ "GetBySessionMessageParams": {
316
+ "type": "object",
317
+ "properties": {
318
+ "session_id": {
319
+ "type": "string",
320
+ "format": "uuid",
321
+ "description": "UUID4 идентификатор сессии"
322
+ },
323
+ "message_id": {
324
+ "type": "string",
325
+ "format": "uuid",
326
+ "description": "UUID4 идентификатор сообщения"
327
+ }
328
+ },
329
+ "required": ["session_id", "message_id"],
330
+ "title": "GetBySessionMessageParams",
331
+ "description": "Параметры для получения записи по идентификаторам сессии и сообщения"
332
+ }
333
+ },
334
+ "examples": {
335
+ "health_check": {
336
+ "summary": "Проверка доступности сервиса",
337
+ "value": {
338
+ "jsonrpc": "2.0",
339
+ "command": "health",
340
+ "id": "1"
341
+ }
342
+ },
343
+ "create_vector": {
344
+ "summary": "Создание векторной записи",
345
+ "value": {
346
+ "jsonrpc": "2.0",
347
+ "command": "create_record",
348
+ "params": {
349
+ "vector": [0.1] * 384,
350
+ "metadata": {"source": "test"}
351
+ },
352
+ "id": "2"
353
+ }
354
+ },
355
+ "search_by_vector": {
356
+ "summary": "Поиск по вектору",
357
+ "value": {
358
+ "jsonrpc": "2.0",
359
+ "command": "search_records",
360
+ "params": {
361
+ "vector": [0.1] * 384,
362
+ "limit": 5
363
+ },
364
+ "id": "3"
365
+ }
366
+ },
367
+ "search_by_text": {
368
+ "summary": "Поиск по тексту",
369
+ "value": {
370
+ "jsonrpc": "2.0",
371
+ "command": "search_text_records",
372
+ "params": {
373
+ "text": "пример поиска",
374
+ "limit": 5
375
+ },
376
+ "id": "4"
377
+ }
378
+ },
379
+ "filter_records": {
380
+ "summary": "Фильтрация записей",
381
+ "value": {
382
+ "jsonrpc": "2.0",
383
+ "command": "filter_records",
384
+ "params": {
385
+ "metadata": {"source": "test"},
386
+ "limit": 10
387
+ },
388
+ "id": "5"
389
+ }
390
+ },
391
+ "delete_records": {
392
+ "summary": "Удаление записей",
393
+ "value": {
394
+ "jsonrpc": "2.0",
395
+ "command": "delete_records",
396
+ "params": {
397
+ "ids": ["550e8400-e29b-41d4-a716-446655440000"]
398
+ },
399
+ "id": "6"
400
+ }
401
+ },
402
+ "get_by_session_message": {
403
+ "summary": "Получение записи по идентификаторам",
404
+ "value": {
405
+ "jsonrpc": "2.0",
406
+ "command": "get_by_session_message",
407
+ "params": {
408
+ "session_id": "550e8400-e29b-41d4-a716-446655440000",
409
+ "message_id": "660e8400-e29b-41d4-a716-446655440000"
410
+ },
411
+ "id": "7"
412
+ }
413
+ }
414
+ }
415
+ }
416
+ }
validators/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """
2
+ Validators for checking the correspondence between metadata and function signatures.
3
+
4
+ This module contains classes for validating the correspondence between command metadata
5
+ and signatures and docstrings of handler functions.
6
+ """
7
+
8
+ from .docstring_validator import DocstringValidator
9
+ from .metadata_validator import MetadataValidator
10
+
11
+ __all__ = [
12
+ 'DocstringValidator',
13
+ 'MetadataValidator',
14
+ ]
@@ -0,0 +1,23 @@
1
+ """
2
+ Base class for command validators.
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import Any, Callable, Dict, List, Tuple
6
+
7
+ class BaseValidator(ABC):
8
+ """
9
+ Base class for all command validators.
10
+
11
+ Defines a common interface for validating command handler functions,
12
+ their docstrings, metadata, and other aspects.
13
+ """
14
+
15
+ @abstractmethod
16
+ def validate(self, *args, **kwargs) -> Tuple[bool, List[str]]:
17
+ """
18
+ Main validation method.
19
+
20
+ Returns:
21
+ Tuple[bool, List[str]]: Validity flag and list of errors
22
+ """
23
+ pass
@@ -0,0 +1,75 @@
1
+ """
2
+ Validator for checking the correspondence between docstrings and function signatures.
3
+ """
4
+ import inspect
5
+ from typing import Dict, Any, Optional, Callable, List, Tuple, get_type_hints
6
+ import docstring_parser
7
+
8
+ class DocstringValidator:
9
+ """
10
+ Validator for checking the correspondence between docstrings and handler functions.
11
+
12
+ This class verifies that function docstrings match their signatures,
13
+ contain all necessary sections, and describe all parameters.
14
+ """
15
+
16
+ def validate(self, handler: Callable, command_name: str, metadata: Dict[str, Any]) -> Tuple[bool, List[str]]:
17
+ """
18
+ Validates the function's docstring against its formal parameters.
19
+
20
+ Args:
21
+ handler: Command handler function
22
+ command_name: Command name
23
+ metadata: Command metadata
24
+
25
+ Returns:
26
+ Tuple[bool, List[str]]: Validity flag and list of errors
27
+ """
28
+ errors = []
29
+
30
+ # Get formal parameters of the function
31
+ sig = inspect.signature(handler)
32
+ formal_params = list(sig.parameters.keys())
33
+
34
+ # Skip self parameter for methods
35
+ if formal_params and formal_params[0] == 'self':
36
+ formal_params = formal_params[1:]
37
+
38
+ # Parse docstring
39
+ docstring = handler.__doc__ or ""
40
+ parsed_doc = docstring_parser.parse(docstring)
41
+
42
+ # Check for function description
43
+ if not parsed_doc.short_description and not parsed_doc.long_description:
44
+ errors.append(f"Missing function description")
45
+
46
+ # Get parameters from docstring
47
+ doc_params = {param.arg_name: param for param in parsed_doc.params}
48
+
49
+ # Check that all formal parameters are described in the docstring
50
+ for param in formal_params:
51
+ # Skip special parameters
52
+ if param in ('params', 'kwargs'):
53
+ continue
54
+
55
+ if param not in doc_params:
56
+ errors.append(f"Parameter '{param}' is not described in the function docstring")
57
+
58
+ # Check for returns in docstring
59
+ if not parsed_doc.returns and not any(t.type_name == 'Returns' for t in parsed_doc.meta):
60
+ errors.append(f"Missing return value description in the function docstring")
61
+
62
+ # Check for type annotations
63
+ try:
64
+ type_hints = get_type_hints(handler)
65
+ for param in formal_params:
66
+ # Skip special parameters
67
+ if param in ('params', 'kwargs'):
68
+ continue
69
+
70
+ if param not in type_hints:
71
+ errors.append(f"Missing type annotation for parameter '{param}' in function {command_name}")
72
+ except Exception as e:
73
+ errors.append(f"Error getting type hints: {str(e)}")
74
+
75
+ return len(errors) == 0, errors
@@ -0,0 +1,76 @@
1
+ """
2
+ Validator for checking command metadata against function signatures.
3
+ """
4
+ import inspect
5
+ from typing import Dict, Any, Optional, Callable, List, Tuple
6
+
7
+ class MetadataValidator:
8
+ """
9
+ Validator for checking handler function metadata.
10
+
11
+ This class verifies that command metadata matches function signatures,
12
+ and all parameters are correctly described.
13
+ """
14
+
15
+ def validate(self, handler: Callable, command_name: str, metadata: Dict[str, Any]) -> Tuple[bool, List[str]]:
16
+ """
17
+ Checks if metadata matches function's formal parameters.
18
+
19
+ Args:
20
+ handler: Command handler function
21
+ command_name: Command name
22
+ metadata: Command metadata
23
+
24
+ Returns:
25
+ Tuple[bool, List[str]]: Validity flag and list of errors
26
+ """
27
+ errors = []
28
+
29
+ # Check presence of main fields in metadata
30
+ if not metadata.get('description'):
31
+ errors.append(f"Missing description for command '{command_name}'")
32
+
33
+ # Get function's formal parameters
34
+ sig = inspect.signature(handler)
35
+ formal_params = list(sig.parameters.keys())
36
+
37
+ # Skip self parameter for methods
38
+ if formal_params and formal_params[0] == 'self':
39
+ formal_params = formal_params[1:]
40
+
41
+ # Check presence of parameters in metadata
42
+ if 'parameters' not in metadata or not isinstance(metadata['parameters'], dict):
43
+ errors.append(f"Parameters are missing or incorrectly defined in metadata for command '{command_name}'")
44
+ return False, errors
45
+
46
+ meta_params = metadata['parameters']
47
+
48
+ # Check that all formal parameters are in metadata
49
+ for param in formal_params:
50
+ # Skip special parameters
51
+ if param in ('params', 'kwargs'):
52
+ continue
53
+
54
+ if param not in meta_params:
55
+ errors.append(f"Parameter '{param}' is not described in metadata for command '{command_name}'")
56
+ continue
57
+
58
+ param_info = meta_params[param]
59
+
60
+ # Check presence of required fields for parameter
61
+ if not isinstance(param_info, dict):
62
+ errors.append(f"Incorrect format for parameter '{param}' description in metadata for command '{command_name}'")
63
+ continue
64
+
65
+ if 'type' not in param_info:
66
+ errors.append(f"Type is not specified for parameter '{param}' in metadata for command '{command_name}'")
67
+
68
+ if 'description' not in param_info:
69
+ errors.append(f"Description is not specified for parameter '{param}' in metadata for command '{command_name}'")
70
+
71
+ # Check that there are no extra parameters in metadata
72
+ for param in meta_params:
73
+ if param not in formal_params and param not in ('params', 'kwargs'):
74
+ errors.append(f"Extra parameter '{param}' in metadata for command '{command_name}'")
75
+
76
+ return len(errors) == 0, errors