mcp-proxy-adapter 2.1.17__py3-none-any.whl → 3.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.
- examples/__init__.py +19 -0
- examples/anti_patterns/README.md +51 -0
- examples/anti_patterns/__init__.py +9 -0
- examples/anti_patterns/bad_design/README.md +72 -0
- examples/anti_patterns/bad_design/global_state.py +170 -0
- examples/anti_patterns/bad_design/monolithic_command.py +272 -0
- examples/basic_example/README.md +131 -0
- examples/basic_example/__init__.py +8 -0
- examples/basic_example/commands/__init__.py +5 -0
- examples/basic_example/commands/echo_command.py +95 -0
- examples/basic_example/commands/math_command.py +151 -0
- examples/basic_example/commands/time_command.py +152 -0
- examples/basic_example/config.json +21 -0
- examples/basic_example/config.yaml +20 -0
- examples/basic_example/docs/EN/README.md +136 -0
- examples/basic_example/docs/RU/README.md +136 -0
- examples/basic_example/main.py +50 -0
- examples/basic_example/server.py +45 -0
- examples/basic_example/tests/conftest.py +243 -0
- examples/commands/echo_command.py +52 -0
- examples/commands/echo_result.py +65 -0
- examples/commands/get_date_command.py +98 -0
- examples/commands/new_uuid4_command.py +91 -0
- examples/complete_example/Dockerfile +24 -0
- examples/complete_example/README.md +92 -0
- examples/complete_example/__init__.py +8 -0
- examples/complete_example/commands/__init__.py +5 -0
- examples/complete_example/commands/system_command.py +327 -0
- examples/complete_example/config.json +41 -0
- examples/complete_example/configs/config.dev.yaml +40 -0
- examples/complete_example/configs/config.docker.yaml +40 -0
- examples/complete_example/docker-compose.yml +35 -0
- examples/complete_example/main.py +67 -0
- examples/complete_example/requirements.txt +20 -0
- examples/complete_example/server.py +85 -0
- examples/minimal_example/README.md +51 -0
- examples/minimal_example/__init__.py +8 -0
- examples/minimal_example/config.json +21 -0
- examples/minimal_example/config.yaml +26 -0
- examples/minimal_example/main.py +67 -0
- examples/minimal_example/simple_server.py +124 -0
- examples/minimal_example/tests/conftest.py +171 -0
- examples/minimal_example/tests/test_hello_command.py +111 -0
- examples/minimal_example/tests/test_integration.py +183 -0
- examples/server.py +69 -0
- examples/simple_server.py +137 -0
- examples/test_server.py +126 -0
- mcp_proxy_adapter/__init__.py +33 -1
- mcp_proxy_adapter/config.py +186 -0
- mcp_proxy_adapter/custom_openapi.py +125 -0
- mcp_proxy_adapter/framework.py +109 -0
- mcp_proxy_adapter/openapi.py +403 -0
- mcp_proxy_adapter/version.py +3 -0
- mcp_proxy_adapter-3.0.0.dist-info/METADATA +200 -0
- mcp_proxy_adapter-3.0.0.dist-info/RECORD +58 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/top_level.txt +1 -0
- mcp_proxy_adapter/adapter.py +0 -697
- mcp_proxy_adapter/analyzers/__init__.py +0 -1
- mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
- mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
- mcp_proxy_adapter/dispatchers/__init__.py +0 -1
- mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
- mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -262
- mcp_proxy_adapter/examples/analyze_config.py +0 -141
- mcp_proxy_adapter/examples/basic_integration.py +0 -155
- mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
- mcp_proxy_adapter/examples/extension_example.py +0 -72
- mcp_proxy_adapter/examples/help_best_practices.py +0 -67
- mcp_proxy_adapter/examples/help_usage.py +0 -64
- mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
- mcp_proxy_adapter/examples/openapi_server.py +0 -383
- mcp_proxy_adapter/examples/project_structure_example.py +0 -47
- mcp_proxy_adapter/examples/testing_example.py +0 -64
- mcp_proxy_adapter/models.py +0 -47
- mcp_proxy_adapter/registry.py +0 -439
- mcp_proxy_adapter/schema.py +0 -257
- mcp_proxy_adapter/testing_utils.py +0 -112
- mcp_proxy_adapter/validators/__init__.py +0 -1
- mcp_proxy_adapter/validators/docstring_validator.py +0 -75
- mcp_proxy_adapter/validators/metadata_validator.py +0 -76
- mcp_proxy_adapter-2.1.17.dist-info/METADATA +0 -376
- mcp_proxy_adapter-2.1.17.dist-info/RECORD +0 -30
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
"""
|
2
|
+
OpenAPI schema generator for MCP Microservice
|
3
|
+
"""
|
4
|
+
from typing import Dict, Any, List, Optional
|
5
|
+
from dataclasses import dataclass
|
6
|
+
import inspect
|
7
|
+
import json
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
from .registry import CommandRegistry, Command
|
11
|
+
from .exceptions import SchemaValidationError
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class TypeInfo:
|
16
|
+
"""Information about a type for OpenAPI schema"""
|
17
|
+
openapi_type: str
|
18
|
+
format: Optional[str] = None
|
19
|
+
items: Optional[Dict[str, Any]] = None
|
20
|
+
properties: Optional[Dict[str, Any]] = None
|
21
|
+
required: Optional[List[str]] = None
|
22
|
+
|
23
|
+
|
24
|
+
class OpenApiGenerator:
|
25
|
+
"""Generates OpenAPI schema for MCP Microservice"""
|
26
|
+
|
27
|
+
PYTHON_TO_OPENAPI_TYPES = {
|
28
|
+
str: TypeInfo("string"),
|
29
|
+
int: TypeInfo("integer", "int64"),
|
30
|
+
float: TypeInfo("number", "float"),
|
31
|
+
bool: TypeInfo("boolean"),
|
32
|
+
list: TypeInfo("array"),
|
33
|
+
dict: TypeInfo("object"),
|
34
|
+
None: TypeInfo("null"),
|
35
|
+
}
|
36
|
+
|
37
|
+
def __init__(self, registry: CommandRegistry):
|
38
|
+
"""
|
39
|
+
Initialize generator
|
40
|
+
|
41
|
+
Args:
|
42
|
+
registry: Command registry instance
|
43
|
+
"""
|
44
|
+
self.registry = registry
|
45
|
+
self._base_schema = self._load_base_schema()
|
46
|
+
|
47
|
+
def _load_base_schema(self) -> Dict[str, Any]:
|
48
|
+
"""Load base schema from file"""
|
49
|
+
schema_path = Path(__file__).parent / "schemas" / "base_schema.json"
|
50
|
+
with open(schema_path) as f:
|
51
|
+
return json.load(f)
|
52
|
+
|
53
|
+
def _get_type_info(self, python_type: Any) -> TypeInfo:
|
54
|
+
"""
|
55
|
+
Get OpenAPI type info for Python type
|
56
|
+
|
57
|
+
Args:
|
58
|
+
python_type: Python type annotation
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
TypeInfo object with OpenAPI type information
|
62
|
+
"""
|
63
|
+
# Handle Optional types
|
64
|
+
origin = getattr(python_type, "__origin__", None)
|
65
|
+
if origin is Optional:
|
66
|
+
return self._get_type_info(python_type.__args__[0])
|
67
|
+
|
68
|
+
# Handle List and Dict
|
69
|
+
if origin is list:
|
70
|
+
item_type = self._get_type_info(python_type.__args__[0])
|
71
|
+
return TypeInfo("array", items={"type": item_type.openapi_type})
|
72
|
+
|
73
|
+
if origin is dict:
|
74
|
+
return TypeInfo("object", additionalProperties=True)
|
75
|
+
|
76
|
+
# Handle basic types
|
77
|
+
if python_type in self.PYTHON_TO_OPENAPI_TYPES:
|
78
|
+
return self.PYTHON_TO_OPENAPI_TYPES[python_type]
|
79
|
+
|
80
|
+
# Handle custom classes
|
81
|
+
if inspect.isclass(python_type):
|
82
|
+
properties = {}
|
83
|
+
required = []
|
84
|
+
|
85
|
+
for name, field in inspect.get_annotations(python_type).items():
|
86
|
+
field_info = self._get_type_info(field)
|
87
|
+
properties[name] = {
|
88
|
+
"type": field_info.openapi_type
|
89
|
+
}
|
90
|
+
if field_info.format:
|
91
|
+
properties[name]["format"] = field_info.format
|
92
|
+
required.append(name)
|
93
|
+
|
94
|
+
return TypeInfo(
|
95
|
+
"object",
|
96
|
+
properties=properties,
|
97
|
+
required=required
|
98
|
+
)
|
99
|
+
|
100
|
+
raise ValueError(f"Unsupported type: {python_type}")
|
101
|
+
|
102
|
+
def _add_command_params(self, schema: Dict[str, Any], command: Command):
|
103
|
+
"""
|
104
|
+
Add command parameters to schema
|
105
|
+
|
106
|
+
Args:
|
107
|
+
schema: OpenAPI schema
|
108
|
+
command: Command instance
|
109
|
+
"""
|
110
|
+
params = {}
|
111
|
+
required = []
|
112
|
+
|
113
|
+
# Get parameters from function signature
|
114
|
+
sig = inspect.signature(command.func)
|
115
|
+
for name, param in sig.parameters.items():
|
116
|
+
param_schema = {}
|
117
|
+
|
118
|
+
# Get type info
|
119
|
+
type_info = self._get_type_info(param.annotation)
|
120
|
+
param_schema["type"] = type_info.openapi_type
|
121
|
+
|
122
|
+
if type_info.format:
|
123
|
+
param_schema["format"] = type_info.format
|
124
|
+
|
125
|
+
if type_info.items:
|
126
|
+
param_schema["items"] = type_info.items
|
127
|
+
|
128
|
+
# Get description from docstring
|
129
|
+
if command.doc and command.doc.params:
|
130
|
+
for doc_param in command.doc.params:
|
131
|
+
if doc_param.arg_name == name:
|
132
|
+
param_schema["description"] = doc_param.description
|
133
|
+
break
|
134
|
+
|
135
|
+
# Handle default value
|
136
|
+
if param.default is not param.empty:
|
137
|
+
param_schema["default"] = param.default
|
138
|
+
else:
|
139
|
+
required.append(name)
|
140
|
+
|
141
|
+
params[name] = param_schema
|
142
|
+
|
143
|
+
# Add to schema
|
144
|
+
method_schema = {
|
145
|
+
"type": "object",
|
146
|
+
"properties": params
|
147
|
+
}
|
148
|
+
if required:
|
149
|
+
method_schema["required"] = required
|
150
|
+
|
151
|
+
schema["components"]["schemas"][f"Params{command.name}"] = method_schema
|
152
|
+
|
153
|
+
def _add_commands_to_schema(self, schema: Dict[str, Any]):
|
154
|
+
"""
|
155
|
+
Add all commands to schema
|
156
|
+
|
157
|
+
Args:
|
158
|
+
schema: OpenAPI schema
|
159
|
+
"""
|
160
|
+
for command in self.registry.get_commands():
|
161
|
+
self._add_command_params(schema, command)
|
162
|
+
|
163
|
+
def _add_cmd_endpoint(self, schema: Dict[str, Any]) -> None:
|
164
|
+
"""
|
165
|
+
Add /cmd endpoint to OpenAPI schema.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
schema: OpenAPI schema to update
|
169
|
+
"""
|
170
|
+
schema["paths"]["/cmd"] = {
|
171
|
+
"post": {
|
172
|
+
"summary": "Execute command",
|
173
|
+
"description": "Universal endpoint for executing any command",
|
174
|
+
"operationId": "execute_command",
|
175
|
+
"requestBody": {
|
176
|
+
"content": {
|
177
|
+
"application/json": {
|
178
|
+
"schema": {"$ref": "#/components/schemas/CommandRequest"}
|
179
|
+
}
|
180
|
+
},
|
181
|
+
"required": True
|
182
|
+
},
|
183
|
+
"responses": {
|
184
|
+
"200": {
|
185
|
+
"description": "Command execution result",
|
186
|
+
"content": {
|
187
|
+
"application/json": {
|
188
|
+
"schema": {
|
189
|
+
"oneOf": [
|
190
|
+
{"$ref": "#/components/schemas/CommandSuccessResponse"},
|
191
|
+
{"$ref": "#/components/schemas/CommandErrorResponse"}
|
192
|
+
]
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
def _add_cmd_models(self, schema: Dict[str, Any]) -> None:
|
202
|
+
"""
|
203
|
+
Add models for /cmd endpoint to OpenAPI schema.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
schema: OpenAPI schema to update
|
207
|
+
"""
|
208
|
+
# Add command request model
|
209
|
+
schema["components"]["schemas"]["CommandRequest"] = {
|
210
|
+
"type": "object",
|
211
|
+
"required": ["command"],
|
212
|
+
"properties": {
|
213
|
+
"command": {
|
214
|
+
"type": "string",
|
215
|
+
"description": "Command name to execute"
|
216
|
+
},
|
217
|
+
"params": {
|
218
|
+
"type": "object",
|
219
|
+
"description": "Command parameters (specific to command)",
|
220
|
+
"additionalProperties": True
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
# Add command success response model
|
226
|
+
schema["components"]["schemas"]["CommandSuccessResponse"] = {
|
227
|
+
"type": "object",
|
228
|
+
"required": ["result"],
|
229
|
+
"properties": {
|
230
|
+
"result": {
|
231
|
+
"type": "object",
|
232
|
+
"description": "Command execution result",
|
233
|
+
"additionalProperties": True
|
234
|
+
}
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
# Add command error response model
|
239
|
+
schema["components"]["schemas"]["CommandErrorResponse"] = {
|
240
|
+
"type": "object",
|
241
|
+
"required": ["error"],
|
242
|
+
"properties": {
|
243
|
+
"error": {
|
244
|
+
"type": "object",
|
245
|
+
"required": ["code", "message"],
|
246
|
+
"properties": {
|
247
|
+
"code": {
|
248
|
+
"type": "integer",
|
249
|
+
"description": "Error code"
|
250
|
+
},
|
251
|
+
"message": {
|
252
|
+
"type": "string",
|
253
|
+
"description": "Error message"
|
254
|
+
},
|
255
|
+
"data": {
|
256
|
+
"type": "object",
|
257
|
+
"description": "Additional error data",
|
258
|
+
"additionalProperties": True
|
259
|
+
}
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
def _add_cmd_examples(self, schema: Dict[str, Any]) -> None:
|
266
|
+
"""
|
267
|
+
Add examples for /cmd endpoint to OpenAPI schema.
|
268
|
+
|
269
|
+
Args:
|
270
|
+
schema: OpenAPI schema to update
|
271
|
+
"""
|
272
|
+
# Create examples section if it doesn't exist
|
273
|
+
if "examples" not in schema["components"]:
|
274
|
+
schema["components"]["examples"] = {}
|
275
|
+
|
276
|
+
# Add help command example request
|
277
|
+
schema["components"]["examples"]["help_request"] = {
|
278
|
+
"summary": "Get list of commands",
|
279
|
+
"value": {
|
280
|
+
"command": "help"
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
# Add help command example response
|
285
|
+
schema["components"]["examples"]["help_response"] = {
|
286
|
+
"summary": "Response with list of commands",
|
287
|
+
"value": {
|
288
|
+
"result": {
|
289
|
+
"commands": {
|
290
|
+
"help": {
|
291
|
+
"description": "Get help information about available commands"
|
292
|
+
},
|
293
|
+
"health": {
|
294
|
+
"description": "Check server health"
|
295
|
+
}
|
296
|
+
}
|
297
|
+
}
|
298
|
+
}
|
299
|
+
}
|
300
|
+
|
301
|
+
# Add specific command help example request
|
302
|
+
schema["components"]["examples"]["help_specific_request"] = {
|
303
|
+
"summary": "Get information about specific command",
|
304
|
+
"value": {
|
305
|
+
"command": "help",
|
306
|
+
"params": {
|
307
|
+
"cmdname": "health"
|
308
|
+
}
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
# Add error example
|
313
|
+
schema["components"]["examples"]["command_error"] = {
|
314
|
+
"summary": "Command not found error",
|
315
|
+
"value": {
|
316
|
+
"error": {
|
317
|
+
"code": -32601,
|
318
|
+
"message": "Command 'unknown_command' not found"
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
# Link examples to endpoint
|
324
|
+
schema["paths"]["/cmd"]["post"]["requestBody"]["content"]["application/json"]["examples"] = {
|
325
|
+
"help": {"$ref": "#/components/examples/help_request"},
|
326
|
+
"help_specific": {"$ref": "#/components/examples/help_specific_request"}
|
327
|
+
}
|
328
|
+
|
329
|
+
schema["paths"]["/cmd"]["post"]["responses"]["200"]["content"]["application/json"]["examples"] = {
|
330
|
+
"help": {"$ref": "#/components/examples/help_response"},
|
331
|
+
"error": {"$ref": "#/components/examples/command_error"}
|
332
|
+
}
|
333
|
+
|
334
|
+
def _validate_required_paths(self, schema: Dict[str, Any]) -> None:
|
335
|
+
"""
|
336
|
+
Validate that required paths exist in schema.
|
337
|
+
|
338
|
+
Args:
|
339
|
+
schema: OpenAPI schema to validate
|
340
|
+
|
341
|
+
Raises:
|
342
|
+
SchemaValidationError: If required paths are missing
|
343
|
+
"""
|
344
|
+
required_paths = ['/cmd', '/api/commands']
|
345
|
+
|
346
|
+
for path in required_paths:
|
347
|
+
if path not in schema['paths']:
|
348
|
+
raise SchemaValidationError(f"Missing required path: {path}")
|
349
|
+
|
350
|
+
def generate(self) -> Dict[str, Any]:
|
351
|
+
"""
|
352
|
+
Generate complete OpenAPI schema
|
353
|
+
|
354
|
+
Returns:
|
355
|
+
OpenAPI schema as dictionary
|
356
|
+
"""
|
357
|
+
schema = self._base_schema.copy()
|
358
|
+
|
359
|
+
# Add commands to schema
|
360
|
+
self._add_commands_to_schema(schema)
|
361
|
+
|
362
|
+
# Add /cmd endpoint regardless of commands
|
363
|
+
self._add_cmd_endpoint(schema)
|
364
|
+
self._add_cmd_models(schema)
|
365
|
+
self._add_cmd_examples(schema)
|
366
|
+
|
367
|
+
# Validate required paths
|
368
|
+
self._validate_required_paths(schema)
|
369
|
+
|
370
|
+
self.validate_schema(schema)
|
371
|
+
return schema
|
372
|
+
|
373
|
+
def validate_schema(self, schema: Dict[str, Any]):
|
374
|
+
"""
|
375
|
+
Validate generated schema
|
376
|
+
|
377
|
+
Args:
|
378
|
+
schema: OpenAPI schema to validate
|
379
|
+
|
380
|
+
Raises:
|
381
|
+
SchemaValidationError: If schema is invalid
|
382
|
+
"""
|
383
|
+
try:
|
384
|
+
# Check that required components exist
|
385
|
+
required_components = ['CommandRequest', 'CommandSuccessResponse', 'CommandErrorResponse']
|
386
|
+
for component in required_components:
|
387
|
+
if component not in schema['components']['schemas']:
|
388
|
+
raise SchemaValidationError(f"Missing required component: {component}")
|
389
|
+
|
390
|
+
# Validate that all paths return 200 status
|
391
|
+
for path in schema['paths'].values():
|
392
|
+
for method in path.values():
|
393
|
+
if '200' not in method['responses']:
|
394
|
+
raise SchemaValidationError("All endpoints must return 200 status code")
|
395
|
+
|
396
|
+
response = method['responses']['200']
|
397
|
+
if 'application/json' not in response['content']:
|
398
|
+
raise SchemaValidationError("All responses must be application/json")
|
399
|
+
except Exception as e:
|
400
|
+
raise SchemaValidationError(f"Schema validation failed: {str(e)}")
|
401
|
+
|
402
|
+
# Here we would normally use a library like openapi-spec-validator
|
403
|
+
# to validate the schema against the OpenAPI 3.0 specification
|
@@ -0,0 +1,200 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: mcp_proxy_adapter
|
3
|
+
Version: 3.0.0
|
4
|
+
Summary: Reliable microservice with unified JSON-RPC endpoint
|
5
|
+
Home-page: https://github.com/yourusername/mcp-proxy-adapter
|
6
|
+
Author: MCP Team
|
7
|
+
Author-email: Vasiliy Zubarev <vasiliy.zubarev@example.com>
|
8
|
+
License: MIT License
|
9
|
+
|
10
|
+
Copyright (c) 2023-2024 Vasiliy VZ
|
11
|
+
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
14
|
+
in the Software without restriction, including without limitation the rights
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
17
|
+
furnished to do so, subject to the following conditions:
|
18
|
+
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
20
|
+
copies or substantial portions of the Software.
|
21
|
+
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
28
|
+
SOFTWARE.
|
29
|
+
Project-URL: Documentation, https://github.com/maverikod/vvz-mcp-proxy-adapter/tree/main/docs/RU/README.md
|
30
|
+
Project-URL: Source, https://github.com/maverikod/vvz-mcp-proxy-adapter
|
31
|
+
Project-URL: Bug Reports, https://github.com/maverikod/vvz-mcp-proxy-adapter/issues
|
32
|
+
Classifier: Programming Language :: Python :: 3
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
34
|
+
Classifier: Operating System :: OS Independent
|
35
|
+
Classifier: Framework :: FastAPI
|
36
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
37
|
+
Classifier: Intended Audience :: Developers
|
38
|
+
Requires-Python: >=3.9
|
39
|
+
Description-Content-Type: text/markdown
|
40
|
+
License-File: LICENSE
|
41
|
+
Requires-Dist: fastapi<1.0.0,>=0.95.0
|
42
|
+
Requires-Dist: pydantic>=2.0.0
|
43
|
+
Requires-Dist: uvicorn<1.0.0,>=0.22.0
|
44
|
+
Requires-Dist: docstring-parser<1.0.0,>=0.15
|
45
|
+
Requires-Dist: typing-extensions<5.0.0,>=4.5.0
|
46
|
+
Requires-Dist: jsonrpc>=1.2.0
|
47
|
+
Requires-Dist: psutil>=5.9.0
|
48
|
+
Dynamic: author
|
49
|
+
Dynamic: home-page
|
50
|
+
Dynamic: license-file
|
51
|
+
Dynamic: requires-python
|
52
|
+
|
53
|
+
# MCP Proxy Adapter
|
54
|
+
|
55
|
+
**MCP Proxy Adapter** - это фреймворк для создания микросервисов на основе JSON-RPC. Он предоставляет базовую инфраструктуру для создания команд, обработки запросов и возвращения ответов через JSON-RPC API.
|
56
|
+
|
57
|
+
**MCP Proxy Adapter** - это фреймворк для создания микросервисов на основе JSON-RPC. Он предоставляет базовую инфраструктуру для создания команд, обработки запросов и возвращения ответов через JSON-RPC API.
|
58
|
+
|
59
|
+
## Установка
|
60
|
+
|
61
|
+
```bash
|
62
|
+
pip install mcp-proxy-adapter
|
63
|
+
```
|
64
|
+
|
65
|
+
## Использование
|
66
|
+
|
67
|
+
1. Создайте свой проект и установите зависимость:
|
68
|
+
|
69
|
+
```bash
|
70
|
+
pip install mcp-proxy-adapter
|
71
|
+
```
|
72
|
+
|
73
|
+
2. Создайте свои команды:
|
74
|
+
|
75
|
+
```python
|
76
|
+
from mcp_proxy_adapter.commands.base import Command
|
77
|
+
from mcp_proxy_adapter.commands.result import SuccessResult
|
78
|
+
|
79
|
+
class YourCommand(Command):
|
80
|
+
name = "your_command"
|
81
|
+
|
82
|
+
async def execute(self, param1: str, param2: int = 0) -> SuccessResult:
|
83
|
+
# Ваша логика
|
84
|
+
result_data = {"param1": param1, "param2": param2}
|
85
|
+
return SuccessResult(data=result_data)
|
86
|
+
```
|
87
|
+
|
88
|
+
3. Запустите сервер:
|
89
|
+
|
90
|
+
```python
|
91
|
+
import uvicorn
|
92
|
+
from mcp_proxy_adapter.api.app import create_app
|
93
|
+
|
94
|
+
# Регистрация ваших команд происходит автоматически
|
95
|
+
app = create_app()
|
96
|
+
|
97
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
98
|
+
```
|
99
|
+
|
100
|
+
## Структура проекта
|
101
|
+
|
102
|
+
Проект представляет собой фреймворк с базовой инфраструктурой:
|
103
|
+
|
104
|
+
* **mcp_proxy_adapter/** - основной модуль фреймворка
|
105
|
+
* **api/** - модуль API
|
106
|
+
* **commands/** - базовые классы команд
|
107
|
+
* **core/** - ядро фреймворка
|
108
|
+
* **schemas/** - JSON-схемы
|
109
|
+
* **examples/** - примеры использования фреймворка
|
110
|
+
* **basic_example/** - базовый пример
|
111
|
+
* **minimal_example/** - минимальный пример
|
112
|
+
* **complete_example/** - полный пример с Docker
|
113
|
+
|
114
|
+
## Базовые команды
|
115
|
+
|
116
|
+
Фреймворк включает следующие базовые команды:
|
117
|
+
|
118
|
+
- `help` - получение справки по доступным командам
|
119
|
+
- `health` - проверка состояния сервиса
|
120
|
+
|
121
|
+
## API
|
122
|
+
|
123
|
+
Фреймворк предоставляет следующие эндпоинты:
|
124
|
+
|
125
|
+
- `POST /api/jsonrpc` - основной JSON-RPC эндпоинт для выполнения команд
|
126
|
+
- `POST /api/command/{command_name}` - REST эндпоинт для выполнения конкретной команды
|
127
|
+
- `GET /api/commands` - получение списка доступных команд
|
128
|
+
- `GET /api/commands/{command_name}` - получение информации о конкретной команде
|
129
|
+
- `GET /health` - проверка состояния сервиса
|
130
|
+
|
131
|
+
## Запуск примеров
|
132
|
+
|
133
|
+
```bash
|
134
|
+
# Базовый пример
|
135
|
+
cd examples/basic_example
|
136
|
+
python main.py
|
137
|
+
|
138
|
+
# Минимальный пример
|
139
|
+
cd examples/minimal_example
|
140
|
+
python main.py
|
141
|
+
|
142
|
+
# Полный пример с Docker
|
143
|
+
cd examples/complete_example
|
144
|
+
docker-compose up -d
|
145
|
+
```
|
146
|
+
|
147
|
+
## Создание новой команды
|
148
|
+
|
149
|
+
Пример создания новой команды:
|
150
|
+
|
151
|
+
```python
|
152
|
+
from typing import Dict, Any, ClassVar, Type
|
153
|
+
from mcp_proxy_adapter.commands.base import Command
|
154
|
+
from mcp_proxy_adapter.commands.result import SuccessResult
|
155
|
+
|
156
|
+
class CustomResult(SuccessResult):
|
157
|
+
"""
|
158
|
+
Пользовательский класс результата.
|
159
|
+
"""
|
160
|
+
|
161
|
+
def __init__(self, value: str):
|
162
|
+
super().__init__(data={"value": value})
|
163
|
+
|
164
|
+
@classmethod
|
165
|
+
def get_schema(cls) -> Dict[str, Any]:
|
166
|
+
return {
|
167
|
+
"type": "object",
|
168
|
+
"properties": {
|
169
|
+
"data": {
|
170
|
+
"type": "object",
|
171
|
+
"properties": {
|
172
|
+
"value": {"type": "string"}
|
173
|
+
},
|
174
|
+
"required": ["value"]
|
175
|
+
}
|
176
|
+
},
|
177
|
+
"required": ["data"]
|
178
|
+
}
|
179
|
+
|
180
|
+
class CustomCommand(Command):
|
181
|
+
"""
|
182
|
+
Пользовательская команда.
|
183
|
+
"""
|
184
|
+
|
185
|
+
name: ClassVar[str] = "custom"
|
186
|
+
result_class: ClassVar[Type[SuccessResult]] = CustomResult
|
187
|
+
|
188
|
+
async def execute(self, input_text: str) -> CustomResult:
|
189
|
+
return CustomResult(value=f"Processed: {input_text}")
|
190
|
+
|
191
|
+
@classmethod
|
192
|
+
def get_schema(cls) -> Dict[str, Any]:
|
193
|
+
return {
|
194
|
+
"type": "object",
|
195
|
+
"properties": {
|
196
|
+
"input_text": {"type": "string"}
|
197
|
+
},
|
198
|
+
"required": ["input_text"],
|
199
|
+
"additionalProperties": False
|
200
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
examples/__init__.py,sha256=sLYNpeoiE-X5q7fmJb7NFMmhiIn0543mgJj16q1qmk0,593
|
2
|
+
examples/server.py,sha256=gnRTE_k7C0A255dLyaJWyA4YU0H6Elc7osr_JQvsQhQ,2286
|
3
|
+
examples/simple_server.py,sha256=WzsX66Nq8pC3G2yEZ1FeXV5mW5gmeSKoQn4_yZkcF20,4104
|
4
|
+
examples/test_server.py,sha256=Uj6aNKQRmhTfsou6Rbye8ZU6wwKZCDZbatNvb5B1X1E,3485
|
5
|
+
examples/anti_patterns/README.md,sha256=1-Hby6Wf3kAC0XOV_jOvuHL-kmTypWOUfE_rEU3Knu8,2045
|
6
|
+
examples/anti_patterns/__init__.py,sha256=xbgoTIMM2LNt4P8h0jTaenUxXkA_l9I7JL_9d4BhoDU,261
|
7
|
+
examples/anti_patterns/bad_design/README.md,sha256=SJDMXZfvNMajN0JyvPBm5GtzxyiZZdtMfHslW3-LOII,2422
|
8
|
+
examples/anti_patterns/bad_design/global_state.py,sha256=UnpMCWDfl-DaZSPhWl8C9fGifXkG_sYKhjx3_HvTiSI,5161
|
9
|
+
examples/anti_patterns/bad_design/monolithic_command.py,sha256=gdp1Ok4h4dKto7nfJmhjoWTtjdCfwI-MtcKpn6il7Yo,8815
|
10
|
+
examples/basic_example/README.md,sha256=8MuXrDPawcPhot4srYzoMCBG2NPOzaWbSTpqfIFvf_I,4207
|
11
|
+
examples/basic_example/__init__.py,sha256=SXYCM5mPlVpEMdahhukXSYVepNiPNddhgypwAlhJjDU,191
|
12
|
+
examples/basic_example/config.json,sha256=ImV2B2X2r0jvchVvGDineO9GXPL0I3FBmp1hUhu6hfw,356
|
13
|
+
examples/basic_example/config.yaml,sha256=6Ct93uj5helKQW5z4D6Z5ur83PBz6c_LOO_UHMQM3f4,600
|
14
|
+
examples/basic_example/main.py,sha256=1ipCAgbLBCxSKBloW9AmEgBb_Gmq06_o2cUsMB4_3ec,1574
|
15
|
+
examples/basic_example/server.py,sha256=HuokZRD0kP1yvjtizQbBsFVXKCUIw61jpGjzsajy7TI,1200
|
16
|
+
examples/basic_example/commands/__init__.py,sha256=jD5tBWpkxH1Yk9kBHS_V3bBIMxwqqY4skr7RGuRru9s,115
|
17
|
+
examples/basic_example/commands/echo_command.py,sha256=EoXMHEf5ouKjvxvZAEiNH6hqyD6ktyyEDWYP-6Oa2D4,2128
|
18
|
+
examples/basic_example/commands/math_command.py,sha256=apJuU5EXlR965rGmAd2h8zcPWgLUSROtPeZ7Ml0H6nw,4423
|
19
|
+
examples/basic_example/commands/time_command.py,sha256=REw-8RCgq9iVUdQxnIFPmeYO_9_6-7QVLzLKLGq0Lb0,4345
|
20
|
+
examples/basic_example/docs/EN/README.md,sha256=yVn9WDJ5kk894WV-HSetSTslP98e1sZpfx2vdP2PXUk,3401
|
21
|
+
examples/basic_example/docs/RU/README.md,sha256=Ad3S2z6E8NbYVThFOmCywyHwhT1U0nY7ahtD-IhlEqE,4680
|
22
|
+
examples/basic_example/tests/conftest.py,sha256=6e85QRE50P9PMV1RXXVgtBQxy49hVLbevZ6dHFBrxRU,5917
|
23
|
+
examples/commands/echo_command.py,sha256=7RuUZfP0YlKl9gks02NiuiYEiiNPa_8lk9CAZmzijyo,1569
|
24
|
+
examples/commands/echo_result.py,sha256=q5IUEeFoi4PunzOhYD0zju2ZwETdGl35yj4DVXZfX68,1635
|
25
|
+
examples/commands/get_date_command.py,sha256=ePQzZCV-n3tLf4IYv6Gn75t0AXXplXtikCDmrIMD2Q4,2537
|
26
|
+
examples/commands/new_uuid4_command.py,sha256=lJQiPuVHtKCnOEUQukji6dwe0VOkddJ7sZXZ7XUsY6Y,2240
|
27
|
+
examples/complete_example/Dockerfile,sha256=MpwyUpA2wMeLtEVQ2ay98z-l8EX5aEFvwUSxZCCxg64,499
|
28
|
+
examples/complete_example/README.md,sha256=EupIC8DhC7dVsObbXjuERpwheVzfLR2i0y3LzCRHLYs,2784
|
29
|
+
examples/complete_example/__init__.py,sha256=JQDNMNOrQAvbQyEV7tV3XrVKBxZRYy3ODMCVyOxU404,216
|
30
|
+
examples/complete_example/config.json,sha256=KbfZnu4SMYH6B7ENQaZ_sZXrWdkmCdMcWgy47nAHh_Q,814
|
31
|
+
examples/complete_example/docker-compose.yml,sha256=kBXjXFeswZCZ4-UhI0vC0OMR_nMRRoDt3PltCi-RGb8,924
|
32
|
+
examples/complete_example/main.py,sha256=cVXHRuk_qGlnzne2_qix43PmnyM1GUb10NPSNqPgxpA,2239
|
33
|
+
examples/complete_example/requirements.txt,sha256=QHeMbXMHgkISmFoXfO09kiaXAMkg0nNBdCfwThKiHDc,403
|
34
|
+
examples/complete_example/server.py,sha256=dChcKg_-4BHkrXCocc8GD90Qpb2gC7JK5MBO9UFtuk8,2195
|
35
|
+
examples/complete_example/commands/__init__.py,sha256=cZDiwz3nLaeSCwR4oK7xlGgVsXt2WyAlIEMWCxSJPAE,121
|
36
|
+
examples/complete_example/commands/system_command.py,sha256=BUu4HxJka77BR5WgxTdkbaLM2NStctgSAesjQPz5_Z8,10703
|
37
|
+
examples/complete_example/configs/config.dev.yaml,sha256=ASIwU8xzB-kinAsLGghv8-LCER1SUC1ed6K8BktOYPQ,1282
|
38
|
+
examples/complete_example/configs/config.docker.yaml,sha256=DZWxauz5fcaiVFaphvf7zpvLI3oXjUUiXdX_bGlcBds,1389
|
39
|
+
examples/minimal_example/README.md,sha256=Ar70b3BO-UyiYxe0hDcJQ5rWRo06zOOda6e2l8adC0c,1888
|
40
|
+
examples/minimal_example/__init__.py,sha256=EloOqdgVLp7YA2DJOWMw0ZxFbVUCH2LqvZVq5RSfSbg,165
|
41
|
+
examples/minimal_example/config.json,sha256=LvxtN5nxcYT2hPyS_RiGx7NsaXHYU70rsYMIQ1e_24w,360
|
42
|
+
examples/minimal_example/config.yaml,sha256=uLf2GyY64PbmRnT_BUYnkUiUffb-Pb48lkMY9MNFaDw,795
|
43
|
+
examples/minimal_example/main.py,sha256=cVXHRuk_qGlnzne2_qix43PmnyM1GUb10NPSNqPgxpA,2239
|
44
|
+
examples/minimal_example/simple_server.py,sha256=t_Bzh-EbBk20GeZ0wXJEcom4w_YYRC2oRTyktKqL_YE,3903
|
45
|
+
examples/minimal_example/tests/conftest.py,sha256=e-ICRJNV37UaUPlevIlXAhniDcbiPyMpQJr_GQWKCgc,4166
|
46
|
+
examples/minimal_example/tests/test_hello_command.py,sha256=C48o7Hh_gy2NlLqb2KKm2HX37df8eIop0IQfOxWPVS0,3485
|
47
|
+
examples/minimal_example/tests/test_integration.py,sha256=uvu4uuwMSHUsYaerw6-2eY7_rwrftw_lxUl41MsaJF4,6179
|
48
|
+
mcp_proxy_adapter/__init__.py,sha256=B7m1YWyv_Wb87-Q-JqVpHQgwajnfIgDyZ_iIxzdTbBY,1021
|
49
|
+
mcp_proxy_adapter/config.py,sha256=IswC2aoUfqArCS6jAydn7-rg5dVODy76d0pLCeOsRyg,5534
|
50
|
+
mcp_proxy_adapter/custom_openapi.py,sha256=EFEjeQ3DudgTfb4870PWkF1upWM5RaY1PY9jxTSOwU8,4142
|
51
|
+
mcp_proxy_adapter/framework.py,sha256=T4zpn-NOEkPkiJfA5T-zmsGSyJRFd3sIUCNIyw7uUWM,3191
|
52
|
+
mcp_proxy_adapter/openapi.py,sha256=jyl5EPXcFhzFKEEMXxHeqF1U-SsYvtdlaKGU2QrekpU,13889
|
53
|
+
mcp_proxy_adapter/version.py,sha256=lhzscbXtI4rVuLccUMXqfLAo7liihKAC6f7DOJt9siI,71
|
54
|
+
mcp_proxy_adapter-3.0.0.dist-info/licenses/LICENSE,sha256=OkApFEwdgMCt_mbvUI-eIwKMSTe38K3XnU2DT5ub-wI,1072
|
55
|
+
mcp_proxy_adapter-3.0.0.dist-info/METADATA,sha256=YguOw7HmpwOlzI9TTvZPlj59zvYPt-kIwn_lTF3XpoU,7537
|
56
|
+
mcp_proxy_adapter-3.0.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
57
|
+
mcp_proxy_adapter-3.0.0.dist-info/top_level.txt,sha256=kxq3OC7vBtsFdy9dDVse4cOl-SV_QlvcTeSkuw_jw3I,27
|
58
|
+
mcp_proxy_adapter-3.0.0.dist-info/RECORD,,
|