mcp-proxy-adapter 2.1.16__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.
Files changed (84) hide show
  1. examples/__init__.py +19 -0
  2. examples/anti_patterns/README.md +51 -0
  3. examples/anti_patterns/__init__.py +9 -0
  4. examples/anti_patterns/bad_design/README.md +72 -0
  5. examples/anti_patterns/bad_design/global_state.py +170 -0
  6. examples/anti_patterns/bad_design/monolithic_command.py +272 -0
  7. examples/basic_example/README.md +131 -0
  8. examples/basic_example/__init__.py +8 -0
  9. examples/basic_example/commands/__init__.py +5 -0
  10. examples/basic_example/commands/echo_command.py +95 -0
  11. examples/basic_example/commands/math_command.py +151 -0
  12. examples/basic_example/commands/time_command.py +152 -0
  13. examples/basic_example/config.json +21 -0
  14. examples/basic_example/config.yaml +20 -0
  15. examples/basic_example/docs/EN/README.md +136 -0
  16. examples/basic_example/docs/RU/README.md +136 -0
  17. examples/basic_example/main.py +50 -0
  18. examples/basic_example/server.py +45 -0
  19. examples/basic_example/tests/conftest.py +243 -0
  20. examples/commands/echo_command.py +52 -0
  21. examples/commands/echo_result.py +65 -0
  22. examples/commands/get_date_command.py +98 -0
  23. examples/commands/new_uuid4_command.py +91 -0
  24. examples/complete_example/Dockerfile +24 -0
  25. examples/complete_example/README.md +92 -0
  26. examples/complete_example/__init__.py +8 -0
  27. examples/complete_example/commands/__init__.py +5 -0
  28. examples/complete_example/commands/system_command.py +327 -0
  29. examples/complete_example/config.json +41 -0
  30. examples/complete_example/configs/config.dev.yaml +40 -0
  31. examples/complete_example/configs/config.docker.yaml +40 -0
  32. examples/complete_example/docker-compose.yml +35 -0
  33. examples/complete_example/main.py +67 -0
  34. examples/complete_example/requirements.txt +20 -0
  35. examples/complete_example/server.py +85 -0
  36. examples/minimal_example/README.md +51 -0
  37. examples/minimal_example/__init__.py +8 -0
  38. examples/minimal_example/config.json +21 -0
  39. examples/minimal_example/config.yaml +26 -0
  40. examples/minimal_example/main.py +67 -0
  41. examples/minimal_example/simple_server.py +124 -0
  42. examples/minimal_example/tests/conftest.py +171 -0
  43. examples/minimal_example/tests/test_hello_command.py +111 -0
  44. examples/minimal_example/tests/test_integration.py +183 -0
  45. examples/server.py +69 -0
  46. examples/simple_server.py +137 -0
  47. examples/test_server.py +126 -0
  48. mcp_proxy_adapter/__init__.py +33 -1
  49. mcp_proxy_adapter/config.py +186 -0
  50. mcp_proxy_adapter/custom_openapi.py +125 -0
  51. mcp_proxy_adapter/framework.py +109 -0
  52. mcp_proxy_adapter/openapi.py +403 -0
  53. mcp_proxy_adapter/version.py +3 -0
  54. mcp_proxy_adapter-3.0.0.dist-info/METADATA +200 -0
  55. mcp_proxy_adapter-3.0.0.dist-info/RECORD +58 -0
  56. {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/top_level.txt +1 -0
  57. mcp_proxy_adapter/adapter.py +0 -697
  58. mcp_proxy_adapter/analyzers/__init__.py +0 -1
  59. mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
  60. mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
  61. mcp_proxy_adapter/dispatchers/__init__.py +0 -1
  62. mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
  63. mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -235
  64. mcp_proxy_adapter/examples/analyze_config.py +0 -141
  65. mcp_proxy_adapter/examples/basic_integration.py +0 -155
  66. mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
  67. mcp_proxy_adapter/examples/extension_example.py +0 -72
  68. mcp_proxy_adapter/examples/help_best_practices.py +0 -67
  69. mcp_proxy_adapter/examples/help_usage.py +0 -64
  70. mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
  71. mcp_proxy_adapter/examples/openapi_server.py +0 -383
  72. mcp_proxy_adapter/examples/project_structure_example.py +0 -47
  73. mcp_proxy_adapter/examples/testing_example.py +0 -64
  74. mcp_proxy_adapter/models.py +0 -47
  75. mcp_proxy_adapter/registry.py +0 -439
  76. mcp_proxy_adapter/schema.py +0 -257
  77. mcp_proxy_adapter/testing_utils.py +0 -112
  78. mcp_proxy_adapter/validators/__init__.py +0 -1
  79. mcp_proxy_adapter/validators/docstring_validator.py +0 -75
  80. mcp_proxy_adapter/validators/metadata_validator.py +0 -76
  81. mcp_proxy_adapter-2.1.16.dist-info/METADATA +0 -341
  82. mcp_proxy_adapter-2.1.16.dist-info/RECORD +0 -30
  83. {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/WHEEL +0 -0
  84. {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,183 @@
1
+ """
2
+ Integration tests for the minimal example microservice.
3
+
4
+ These tests start a real server instance and test the API endpoints.
5
+ """
6
+
7
+ import os
8
+ import time
9
+ import pytest
10
+ import requests
11
+ from typing import Dict, Any, Callable
12
+
13
+ from conftest import ServerProcess
14
+
15
+
16
+ class TestHelloCommandIntegration:
17
+ """Integration tests for the HelloCommand API."""
18
+
19
+ def test_jsonrpc_hello_default(self, server: ServerProcess, jsonrpc_client: Callable):
20
+ """Test the hello command via JSON-RPC with default parameters."""
21
+ # Make JSON-RPC request
22
+ response = jsonrpc_client("hello", {})
23
+
24
+ # Check response
25
+ assert "jsonrpc" in response
26
+ assert response["jsonrpc"] == "2.0"
27
+ assert "id" in response
28
+ assert "result" in response
29
+ assert "message" in response["result"]
30
+ assert response["result"]["message"] == "Hello, World!"
31
+
32
+ def test_jsonrpc_hello_custom_name(self, server: ServerProcess, jsonrpc_client: Callable):
33
+ """Test the hello command via JSON-RPC with custom name parameter."""
34
+ # Make JSON-RPC request
35
+ response = jsonrpc_client("hello", {"name": "Integration Test"})
36
+
37
+ # Check response
38
+ assert "result" in response
39
+ assert "message" in response["result"]
40
+ assert response["result"]["message"] == "Hello, Integration Test!"
41
+
42
+ def test_cmd_endpoint_hello(self, server: ServerProcess, api_url: str):
43
+ """Test the hello command via the /cmd endpoint."""
44
+ # Prepare request
45
+ payload = {
46
+ "command": "hello",
47
+ "params": {"name": "CMD Endpoint"}
48
+ }
49
+
50
+ # Make request
51
+ response = requests.post(
52
+ f"{api_url}/cmd",
53
+ json=payload,
54
+ headers={"Content-Type": "application/json"}
55
+ )
56
+
57
+ # Check response
58
+ assert response.status_code == 200
59
+ data = response.json()
60
+ assert "result" in data
61
+ assert "message" in data["result"]
62
+ assert data["result"]["message"] == "Hello, CMD Endpoint!"
63
+
64
+ def test_api_command_endpoint_hello(self, server: ServerProcess, api_url: str):
65
+ """Test the hello command via the /api/command/{command_name} endpoint."""
66
+ # Prepare params
67
+ params = {"name": "API Command Endpoint"}
68
+
69
+ # Make request
70
+ response = requests.post(
71
+ f"{api_url}/api/command/hello",
72
+ json=params,
73
+ headers={"Content-Type": "application/json"}
74
+ )
75
+
76
+ # Check response
77
+ assert response.status_code == 200
78
+ data = response.json()
79
+ assert "message" in data
80
+ assert data["message"] == "Hello, API Command Endpoint!"
81
+
82
+ def test_jsonrpc_batch_request(self, server: ServerProcess, api_url: str):
83
+ """Test batch JSON-RPC requests with hello command."""
84
+ # Prepare batch request
85
+ batch = [
86
+ {
87
+ "jsonrpc": "2.0",
88
+ "method": "hello",
89
+ "params": {"name": "Batch 1"},
90
+ "id": 1
91
+ },
92
+ {
93
+ "jsonrpc": "2.0",
94
+ "method": "hello",
95
+ "params": {"name": "Batch 2"},
96
+ "id": 2
97
+ }
98
+ ]
99
+
100
+ # Make request
101
+ response = requests.post(
102
+ f"{api_url}/api/jsonrpc",
103
+ json=batch,
104
+ headers={"Content-Type": "application/json"}
105
+ )
106
+
107
+ # Check response
108
+ assert response.status_code == 200
109
+ data = response.json()
110
+ assert isinstance(data, list)
111
+ assert len(data) == 2
112
+
113
+ # Check first result
114
+ assert data[0]["id"] == 1
115
+ assert data[0]["result"]["message"] == "Hello, Batch 1!"
116
+
117
+ # Check second result
118
+ assert data[1]["id"] == 2
119
+ assert data[1]["result"]["message"] == "Hello, Batch 2!"
120
+
121
+
122
+ class TestServerIntegration:
123
+ """Integration tests for the server itself."""
124
+
125
+ def test_health_endpoint(self, server: ServerProcess, api_url: str):
126
+ """Test the /health endpoint."""
127
+ response = requests.get(f"{api_url}/health")
128
+
129
+ # Check response
130
+ assert response.status_code == 200
131
+ data = response.json()
132
+ assert "status" in data
133
+ assert data["status"] == "ok"
134
+
135
+ def test_openapi_schema(self, server: ServerProcess, api_url: str):
136
+ """Test the OpenAPI schema endpoint."""
137
+ response = requests.get(f"{api_url}/openapi.json")
138
+
139
+ # Check response
140
+ assert response.status_code == 200
141
+ schema = response.json()
142
+
143
+ # Check schema structure
144
+ assert "openapi" in schema
145
+ assert "info" in schema
146
+ assert "paths" in schema
147
+
148
+ # Check hello command paths
149
+ assert "/api/command/hello" in schema["paths"]
150
+ assert "/api/jsonrpc" in schema["paths"]
151
+
152
+ def test_docs_endpoint(self, server: ServerProcess, api_url: str):
153
+ """Test the Swagger UI endpoint."""
154
+ response = requests.get(f"{api_url}/docs")
155
+
156
+ # Check response
157
+ assert response.status_code == 200
158
+ assert "text/html" in response.headers["Content-Type"]
159
+
160
+ # Check if swagger UI is in the response
161
+ assert "swagger-ui" in response.text.lower()
162
+
163
+ def test_commands_list_endpoint(self, server: ServerProcess, api_url: str):
164
+ """Test the commands list endpoint."""
165
+ response = requests.get(f"{api_url}/api/commands")
166
+
167
+ # Check response
168
+ assert response.status_code == 200
169
+ data = response.json()
170
+
171
+ # Check data structure
172
+ assert "commands" in data
173
+ assert isinstance(data["commands"], list)
174
+
175
+ # Check if hello command is in the list
176
+ hello_commands = [cmd for cmd in data["commands"] if cmd["name"] == "hello"]
177
+ assert len(hello_commands) == 1
178
+
179
+ # Check hello command details
180
+ hello_cmd = hello_commands[0]
181
+ assert "name" in hello_cmd
182
+ assert "description" in hello_cmd
183
+ assert "parameters" in hello_cmd
examples/server.py ADDED
@@ -0,0 +1,69 @@
1
+ """
2
+ Main application module.
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import uvicorn
8
+ import logging
9
+
10
+ from mcp_proxy_adapter import create_app
11
+
12
+ app = create_app()
13
+ from mcp_proxy_adapter.config import config
14
+ from mcp_proxy_adapter.core.logging import logger, setup_logging
15
+
16
+
17
+ def main():
18
+ """
19
+ Main function to run the application.
20
+ """
21
+ # Убедимся что логирование настроено
22
+ logger.info("Initializing logging configuration")
23
+
24
+ try:
25
+ # Получаем настройки логирования
26
+ log_level = config.get("logging.level", "INFO")
27
+ log_file = config.get("logging.file")
28
+ rotation_type = config.get("logging.rotation.type", "size")
29
+
30
+ # Выводим информацию о настройках логирования
31
+ logger.info(f"Log level: {log_level}")
32
+ if log_file:
33
+ logger.info(f"Log file: {log_file}")
34
+ logger.info(f"Log rotation type: {rotation_type}")
35
+
36
+ if rotation_type.lower() == "time":
37
+ when = config.get("logging.rotation.when", "D")
38
+ interval = config.get("logging.rotation.interval", 1)
39
+ logger.info(f"Log rotation: every {interval} {when}")
40
+ else:
41
+ max_bytes = config.get("logging.rotation.max_bytes", 10 * 1024 * 1024)
42
+ logger.info(f"Log rotation: when size reaches {max_bytes / (1024*1024):.1f} MB")
43
+
44
+ backup_count = config.get("logging.rotation.backup_count", 5)
45
+ logger.info(f"Log backups: {backup_count}")
46
+ else:
47
+ logger.info("File logging is disabled")
48
+
49
+ # Get server settings
50
+ host = config.get("server.host", "0.0.0.0")
51
+ port = config.get("server.port", 8000)
52
+
53
+ logger.info(f"Starting server on {host}:{port}")
54
+
55
+ # Run server
56
+ uvicorn.run(
57
+ "mcp_proxy_adapter.api.app:app",
58
+ host=host,
59
+ port=port,
60
+ reload=True if os.environ.get("DEBUG") else False,
61
+ log_level=log_level.lower()
62
+ )
63
+ except Exception as e:
64
+ logger.exception(f"Error during application startup: {e}")
65
+ sys.exit(1)
66
+
67
+
68
+ if __name__ == "__main__":
69
+ main()
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Простой пример запуска сервера JSON-RPC с использованием MCP Proxy Adapter.
4
+
5
+ Этот скрипт демонстрирует минимальную конфигурацию для запуска сервера JSON-RPC.
6
+ Он создаёт экземпляр MicroService, регистрирует несколько команд и запускает сервер.
7
+ """
8
+
9
+ import os
10
+ import argparse
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ # Добавляем родительскую директорию в PYTHONPATH для импорта mcp_proxy_adapter
15
+ sys.path.insert(0, str(Path(__file__).parent.parent))
16
+
17
+ import mcp_proxy_adapter as mcp
18
+ from mcp_proxy_adapter.framework import MicroService
19
+ from examples.commands.echo_command import EchoCommand
20
+ from examples.commands.get_date_command import GetDateCommand
21
+ from examples.commands.new_uuid4_command import NewUuid4Command
22
+ from typing import Dict, Any, Optional
23
+ from mcp_proxy_adapter import Command, SuccessResult
24
+
25
+
26
+ class HelloResult(SuccessResult):
27
+ """Result of hello command."""
28
+
29
+ def __init__(self, message: str):
30
+ """
31
+ Initialize result.
32
+
33
+ Args:
34
+ message: Hello message
35
+ """
36
+ self.message = message
37
+
38
+ def to_dict(self) -> Dict[str, Any]:
39
+ """
40
+ Convert result to dictionary.
41
+
42
+ Returns:
43
+ Dictionary representation
44
+ """
45
+ return {"message": self.message}
46
+
47
+ @classmethod
48
+ def get_schema(cls) -> Dict[str, Any]:
49
+ """
50
+ Get JSON schema for result.
51
+
52
+ Returns:
53
+ JSON schema
54
+ """
55
+ return {
56
+ "type": "object",
57
+ "properties": {
58
+ "message": {"type": "string"}
59
+ },
60
+ "required": ["message"]
61
+ }
62
+
63
+
64
+ class HelloCommand(Command):
65
+ """Command that returns hello message."""
66
+
67
+ name = "hello"
68
+ result_class = HelloResult
69
+
70
+ async def execute(self, name: str = "World") -> HelloResult:
71
+ """
72
+ Execute command.
73
+
74
+ Args:
75
+ name: Name to greet
76
+
77
+ Returns:
78
+ Hello result
79
+ """
80
+ return HelloResult(f"Hello, {name}!")
81
+
82
+ @classmethod
83
+ def get_schema(cls) -> Dict[str, Any]:
84
+ """
85
+ Get JSON schema for command parameters.
86
+
87
+ Returns:
88
+ JSON schema
89
+ """
90
+ return {
91
+ "type": "object",
92
+ "properties": {
93
+ "name": {"type": "string"}
94
+ },
95
+ "additionalProperties": False
96
+ }
97
+
98
+
99
+ def main():
100
+ """
101
+ Основная функция для запуска сервера.
102
+ """
103
+ parser = argparse.ArgumentParser(description="JSON-RPC Microservice Server")
104
+ parser.add_argument("--host", default="0.0.0.0", help="Host to bind server")
105
+ parser.add_argument("--port", type=int, default=8000, help="Port to bind server")
106
+ parser.add_argument("--reload", action="store_true", help="Enable auto-reload")
107
+ parser.add_argument("--config", help="Path to config file")
108
+
109
+ args = parser.parse_args()
110
+
111
+ # Создаем экземпляр микросервиса
112
+ service = MicroService(
113
+ title="Simple Example Service",
114
+ description="Example microservice demonstrating basic functionality",
115
+ version="1.0.0",
116
+ config_path=args.config
117
+ )
118
+
119
+ # Регистрируем команды
120
+ service.register_command(EchoCommand)
121
+ service.register_command(GetDateCommand)
122
+ service.register_command(NewUuid4Command)
123
+
124
+ # Запускаем сервер
125
+ service.run(
126
+ host=args.host,
127
+ port=args.port,
128
+ reload=args.reload
129
+ )
130
+
131
+
132
+ if __name__ == "__main__":
133
+ # Run the simple server example
134
+ # To test, open http://localhost:8000/docs in your browser
135
+ # or use curl:
136
+ # curl -X POST http://localhost:8000/api/jsonrpc -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"hello","params":{"name":"PyPI"},"id":1}'
137
+ main()
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Тестовый скрипт для запуска сервера JSON-RPC с использованием MCP Proxy Adapter.
4
+ Использует только базовую команду для тестирования установки пакета.
5
+ """
6
+
7
+ import os
8
+ import argparse
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Dict, Any, Optional
12
+
13
+ # Добавляем родительскую директорию в PYTHONPATH для импорта mcp_proxy_adapter
14
+ sys.path.insert(0, str(Path(__file__).parent.parent))
15
+
16
+ from mcp_proxy_adapter.framework import MicroService
17
+ from mcp_proxy_adapter.commands.base import Command
18
+ from mcp_proxy_adapter.commands.result import SuccessResult
19
+
20
+
21
+ class HelloResult(SuccessResult):
22
+ """Result of hello command."""
23
+
24
+ def __init__(self, message: str):
25
+ """
26
+ Initialize result.
27
+
28
+ Args:
29
+ message: Hello message
30
+ """
31
+ self.message = message
32
+
33
+ def to_dict(self) -> Dict[str, Any]:
34
+ """
35
+ Convert result to dictionary.
36
+
37
+ Returns:
38
+ Dictionary representation
39
+ """
40
+ return {"message": self.message}
41
+
42
+ @classmethod
43
+ def get_schema(cls) -> Dict[str, Any]:
44
+ """
45
+ Get JSON schema for result.
46
+
47
+ Returns:
48
+ JSON schema
49
+ """
50
+ return {
51
+ "type": "object",
52
+ "properties": {
53
+ "message": {"type": "string"}
54
+ },
55
+ "required": ["message"]
56
+ }
57
+
58
+
59
+ class HelloCommand(Command):
60
+ """Command that returns hello message."""
61
+
62
+ name = "hello"
63
+ result_class = HelloResult
64
+
65
+ async def execute(self, name: str = "World") -> HelloResult:
66
+ """
67
+ Execute command.
68
+
69
+ Args:
70
+ name: Name to greet
71
+
72
+ Returns:
73
+ Hello result
74
+ """
75
+ return HelloResult(f"Hello, {name}!")
76
+
77
+ @classmethod
78
+ def get_schema(cls) -> Dict[str, Any]:
79
+ """
80
+ Get JSON schema for command parameters.
81
+
82
+ Returns:
83
+ JSON schema
84
+ """
85
+ return {
86
+ "type": "object",
87
+ "properties": {
88
+ "name": {"type": "string"}
89
+ },
90
+ "additionalProperties": False
91
+ }
92
+
93
+
94
+ def main():
95
+ """
96
+ Основная функция для запуска сервера.
97
+ """
98
+ parser = argparse.ArgumentParser(description="Test JSON-RPC Microservice Server")
99
+ parser.add_argument("--host", default="localhost", help="Host to bind server")
100
+ parser.add_argument("--port", type=int, default=8000, help="Port to bind server")
101
+ parser.add_argument("--reload", action="store_true", help="Enable auto-reload")
102
+ parser.add_argument("--config", help="Path to config file")
103
+
104
+ args = parser.parse_args()
105
+
106
+ # Создаем экземпляр микросервиса
107
+ service = MicroService(
108
+ title="Test Microservice",
109
+ description="Test microservice for package installation verification",
110
+ version="1.0.0",
111
+ config_path=args.config
112
+ )
113
+
114
+ # Регистрируем только одну команду для тестирования
115
+ service.register_command(HelloCommand)
116
+
117
+ # Запускаем сервер
118
+ service.run(
119
+ host=args.host,
120
+ port=args.port,
121
+ reload=args.reload
122
+ )
123
+
124
+
125
+ if __name__ == "__main__":
126
+ main()
@@ -1 +1,33 @@
1
-
1
+ """MCP Proxy API Service package.
2
+
3
+ This package provides a framework for creating JSON-RPC-enabled microservices.
4
+ """
5
+
6
+ from mcp_proxy_adapter.version import __version__
7
+ from mcp_proxy_adapter.api.app import create_app
8
+ from mcp_proxy_adapter.commands.base import Command
9
+ from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult, ErrorResult
10
+ from mcp_proxy_adapter.commands.command_registry import registry
11
+ from mcp_proxy_adapter.core.errors import (
12
+ MicroserviceError, CommandError, ValidationError,
13
+ InvalidParamsError, NotFoundError, TimeoutError,
14
+ InternalError
15
+ )
16
+
17
+ # Экспортируем основные классы и функции для удобного использования
18
+ __all__ = [
19
+ "__version__",
20
+ "create_app",
21
+ "Command",
22
+ "CommandResult",
23
+ "SuccessResult",
24
+ "ErrorResult",
25
+ "registry",
26
+ "MicroserviceError",
27
+ "CommandError",
28
+ "ValidationError",
29
+ "InvalidParamsError",
30
+ "NotFoundError",
31
+ "TimeoutError",
32
+ "InternalError"
33
+ ]