mcp-proxy-adapter 2.1.17__py3-none-any.whl → 3.0.1__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 +245 -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 +25 -0
- examples/basic_example/docs/EN/README.md +177 -0
- examples/basic_example/docs/RU/README.md +177 -0
- examples/basic_example/server.py +151 -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 +328 -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/requirements.txt +20 -0
- examples/complete_example/server.py +139 -0
- examples/minimal_example/README.md +65 -0
- examples/minimal_example/__init__.py +8 -0
- examples/minimal_example/config.json +14 -0
- examples/minimal_example/main.py +136 -0
- examples/minimal_example/simple_server.py +163 -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 +181 -0
- examples/server.py +69 -0
- examples/simple_server.py +128 -0
- examples/test_server.py +134 -0
- examples/tool_description_example.py +82 -0
- mcp_proxy_adapter/__init__.py +33 -1
- mcp_proxy_adapter/api/__init__.py +0 -0
- mcp_proxy_adapter/api/app.py +391 -0
- mcp_proxy_adapter/api/handlers.py +229 -0
- mcp_proxy_adapter/api/middleware/__init__.py +49 -0
- mcp_proxy_adapter/api/middleware/auth.py +146 -0
- mcp_proxy_adapter/api/middleware/base.py +79 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
- mcp_proxy_adapter/api/middleware/logging.py +96 -0
- mcp_proxy_adapter/api/middleware/performance.py +83 -0
- mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
- mcp_proxy_adapter/api/schemas.py +305 -0
- mcp_proxy_adapter/api/tool_integration.py +223 -0
- mcp_proxy_adapter/api/tools.py +198 -0
- mcp_proxy_adapter/commands/__init__.py +19 -0
- mcp_proxy_adapter/commands/base.py +301 -0
- mcp_proxy_adapter/commands/command_registry.py +231 -0
- mcp_proxy_adapter/commands/config_command.py +113 -0
- mcp_proxy_adapter/commands/health_command.py +136 -0
- mcp_proxy_adapter/commands/help_command.py +193 -0
- mcp_proxy_adapter/commands/result.py +215 -0
- mcp_proxy_adapter/config.py +195 -0
- mcp_proxy_adapter/core/__init__.py +0 -0
- mcp_proxy_adapter/core/errors.py +173 -0
- mcp_proxy_adapter/core/logging.py +205 -0
- mcp_proxy_adapter/core/utils.py +138 -0
- mcp_proxy_adapter/custom_openapi.py +125 -0
- mcp_proxy_adapter/openapi.py +403 -0
- mcp_proxy_adapter/py.typed +0 -0
- mcp_proxy_adapter/schemas/base_schema.json +114 -0
- mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +3 -0
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
- mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
- mcp_proxy_adapter/tests/commands/__init__.py +3 -0
- mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
- mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
- mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
- mcp_proxy_adapter/tests/conftest.py +131 -0
- mcp_proxy_adapter/tests/functional/__init__.py +3 -0
- mcp_proxy_adapter/tests/functional/test_api.py +235 -0
- mcp_proxy_adapter/tests/integration/__init__.py +3 -0
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
- mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
- mcp_proxy_adapter/tests/performance/__init__.py +3 -0
- mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
- mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
- mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
- mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
- mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
- mcp_proxy_adapter/tests/test_base_command.py +123 -0
- mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
- mcp_proxy_adapter/tests/test_command_registry.py +245 -0
- mcp_proxy_adapter/tests/test_config.py +127 -0
- mcp_proxy_adapter/tests/test_utils.py +65 -0
- mcp_proxy_adapter/tests/unit/__init__.py +3 -0
- mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
- mcp_proxy_adapter/tests/unit/test_config.py +217 -0
- mcp_proxy_adapter/version.py +3 -0
- mcp_proxy_adapter-3.0.1.dist-info/METADATA +200 -0
- mcp_proxy_adapter-3.0.1.dist-info/RECORD +109 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.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.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,243 @@
|
|
1
|
+
"""
|
2
|
+
Pytest configuration and fixtures for basic example tests.
|
3
|
+
|
4
|
+
This module provides fixtures for testing the basic microservice example,
|
5
|
+
including running an actual server instance for integration tests.
|
6
|
+
All commands in the microservice are implemented as asynchronous functions.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import sys
|
11
|
+
import time
|
12
|
+
import socket
|
13
|
+
import asyncio
|
14
|
+
import threading
|
15
|
+
import multiprocessing
|
16
|
+
from typing import Callable, Dict, Any, List
|
17
|
+
|
18
|
+
import pytest
|
19
|
+
import requests
|
20
|
+
|
21
|
+
# Add parent directory to path for imports
|
22
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
23
|
+
|
24
|
+
# Import the server module from the parent directory
|
25
|
+
import server as server_module
|
26
|
+
from commands.echo_command import EchoCommand
|
27
|
+
from commands.math_command import MathCommand
|
28
|
+
from commands.time_command import TimeCommand
|
29
|
+
|
30
|
+
|
31
|
+
def find_free_port() -> int:
|
32
|
+
"""
|
33
|
+
Find a free port on localhost.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
Free port number
|
37
|
+
"""
|
38
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
39
|
+
sock.bind(('localhost', 0))
|
40
|
+
return sock.getsockname()[1]
|
41
|
+
|
42
|
+
|
43
|
+
class ServerProcess:
|
44
|
+
"""Helper class to manage server process."""
|
45
|
+
|
46
|
+
def __init__(self, port: int):
|
47
|
+
"""
|
48
|
+
Initialize server process with a specified port.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
port: Port number to use
|
52
|
+
"""
|
53
|
+
self.port = port
|
54
|
+
self.process = None
|
55
|
+
|
56
|
+
def start(self) -> None:
|
57
|
+
"""Start the server in a separate process."""
|
58
|
+
def run_server():
|
59
|
+
# Mock the configuration to use the test port
|
60
|
+
os.environ["TEST_SERVER_PORT"] = str(self.port)
|
61
|
+
server_module.main()
|
62
|
+
|
63
|
+
self.process = multiprocessing.Process(target=run_server)
|
64
|
+
self.process.daemon = True
|
65
|
+
self.process.start()
|
66
|
+
|
67
|
+
# Wait for server to start
|
68
|
+
self._wait_for_server()
|
69
|
+
|
70
|
+
def stop(self) -> None:
|
71
|
+
"""Stop the server process."""
|
72
|
+
if self.process and self.process.is_alive():
|
73
|
+
self.process.terminate()
|
74
|
+
self.process.join(timeout=2)
|
75
|
+
|
76
|
+
def _wait_for_server(self, max_attempts: int = 10) -> None:
|
77
|
+
"""
|
78
|
+
Wait for the server to become available.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
max_attempts: Maximum number of connection attempts
|
82
|
+
"""
|
83
|
+
for i in range(max_attempts):
|
84
|
+
try:
|
85
|
+
response = requests.get(f"http://localhost:{self.port}/health")
|
86
|
+
if response.status_code == 200:
|
87
|
+
return
|
88
|
+
except requests.ConnectionError:
|
89
|
+
pass
|
90
|
+
|
91
|
+
time.sleep(0.5)
|
92
|
+
|
93
|
+
raise TimeoutError(f"Server did not start within {max_attempts * 0.5} seconds")
|
94
|
+
|
95
|
+
|
96
|
+
@pytest.fixture
|
97
|
+
def server_port() -> int:
|
98
|
+
"""
|
99
|
+
Fixture that provides a free port for the test server.
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Port number
|
103
|
+
"""
|
104
|
+
return find_free_port()
|
105
|
+
|
106
|
+
|
107
|
+
@pytest.fixture
|
108
|
+
def server(server_port: int) -> ServerProcess:
|
109
|
+
"""
|
110
|
+
Fixture that provides a running server instance.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
server_port: Port to run the server on
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
Server process object
|
117
|
+
"""
|
118
|
+
server_process = ServerProcess(server_port)
|
119
|
+
server_process.start()
|
120
|
+
|
121
|
+
yield server_process
|
122
|
+
|
123
|
+
server_process.stop()
|
124
|
+
|
125
|
+
|
126
|
+
@pytest.fixture
|
127
|
+
def api_url(server_port: int) -> str:
|
128
|
+
"""
|
129
|
+
Fixture that provides the base API URL.
|
130
|
+
|
131
|
+
Args:
|
132
|
+
server_port: Server port
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
Base API URL
|
136
|
+
"""
|
137
|
+
return f"http://localhost:{server_port}"
|
138
|
+
|
139
|
+
|
140
|
+
@pytest.fixture
|
141
|
+
def jsonrpc_client(api_url: str) -> Callable:
|
142
|
+
"""
|
143
|
+
Fixture that provides a JSON-RPC client function.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
api_url: Base API URL
|
147
|
+
|
148
|
+
Returns:
|
149
|
+
Function to make JSON-RPC requests
|
150
|
+
"""
|
151
|
+
def make_request(method: str, params: Dict[str, Any], request_id: int = 1) -> Dict[str, Any]:
|
152
|
+
"""
|
153
|
+
Make a JSON-RPC request.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
method: Method name
|
157
|
+
params: Method parameters
|
158
|
+
request_id: Request ID
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
JSON-RPC response
|
162
|
+
"""
|
163
|
+
payload = {
|
164
|
+
"jsonrpc": "2.0",
|
165
|
+
"method": method,
|
166
|
+
"params": params,
|
167
|
+
"id": request_id
|
168
|
+
}
|
169
|
+
|
170
|
+
response = requests.post(
|
171
|
+
f"{api_url}/api/jsonrpc",
|
172
|
+
json=payload,
|
173
|
+
headers={"Content-Type": "application/json"}
|
174
|
+
)
|
175
|
+
|
176
|
+
return response.json()
|
177
|
+
|
178
|
+
return make_request
|
179
|
+
|
180
|
+
|
181
|
+
@pytest.fixture
|
182
|
+
def batch_jsonrpc_client(api_url: str) -> Callable:
|
183
|
+
"""
|
184
|
+
Fixture that provides a batch JSON-RPC client function.
|
185
|
+
|
186
|
+
Args:
|
187
|
+
api_url: Base API URL
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
Function to make batch JSON-RPC requests
|
191
|
+
"""
|
192
|
+
def make_batch_request(requests_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
193
|
+
"""
|
194
|
+
Make a batch JSON-RPC request.
|
195
|
+
|
196
|
+
Args:
|
197
|
+
requests_data: List of request objects
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
List of JSON-RPC responses
|
201
|
+
"""
|
202
|
+
response = requests.post(
|
203
|
+
f"{api_url}/api/jsonrpc",
|
204
|
+
json=requests_data,
|
205
|
+
headers={"Content-Type": "application/json"}
|
206
|
+
)
|
207
|
+
|
208
|
+
return response.json()
|
209
|
+
|
210
|
+
return make_batch_request
|
211
|
+
|
212
|
+
|
213
|
+
@pytest.fixture
|
214
|
+
def echo_command() -> EchoCommand:
|
215
|
+
"""
|
216
|
+
Fixture that provides an instance of EchoCommand.
|
217
|
+
|
218
|
+
Returns:
|
219
|
+
EchoCommand instance
|
220
|
+
"""
|
221
|
+
return EchoCommand()
|
222
|
+
|
223
|
+
|
224
|
+
@pytest.fixture
|
225
|
+
def math_command() -> MathCommand:
|
226
|
+
"""
|
227
|
+
Fixture that provides an instance of MathCommand.
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
MathCommand instance
|
231
|
+
"""
|
232
|
+
return MathCommand()
|
233
|
+
|
234
|
+
|
235
|
+
@pytest.fixture
|
236
|
+
def time_command() -> TimeCommand:
|
237
|
+
"""
|
238
|
+
Fixture that provides an instance of TimeCommand.
|
239
|
+
|
240
|
+
Returns:
|
241
|
+
TimeCommand instance
|
242
|
+
"""
|
243
|
+
return TimeCommand()
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
Module with echo command implementation.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Any, Dict, Optional, ClassVar, Type
|
6
|
+
|
7
|
+
from pydantic import BaseModel, Field, ValidationError as PydanticValidationError
|
8
|
+
|
9
|
+
from mcp_proxy_adapter.commands.base import Command
|
10
|
+
from examples.commands.echo_result import EchoResult
|
11
|
+
from mcp_proxy_adapter.core.errors import ValidationError
|
12
|
+
from mcp_proxy_adapter.core.logging import logger
|
13
|
+
|
14
|
+
|
15
|
+
class EchoCommand(Command):
|
16
|
+
"""
|
17
|
+
Command that echoes back the parameters it receives.
|
18
|
+
|
19
|
+
This command is useful for testing parameter passing and debugging.
|
20
|
+
"""
|
21
|
+
|
22
|
+
name: ClassVar[str] = "echo"
|
23
|
+
result_class: ClassVar[Type[EchoResult]] = EchoResult
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
def get_schema(cls) -> Dict[str, Any]:
|
27
|
+
"""
|
28
|
+
Returns JSON schema for command parameters validation.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
Dictionary with JSON schema.
|
32
|
+
"""
|
33
|
+
return {
|
34
|
+
"type": "object",
|
35
|
+
"additionalProperties": True,
|
36
|
+
"description": "Any parameters will be echoed back in the response"
|
37
|
+
}
|
38
|
+
|
39
|
+
async def execute(self, **kwargs) -> EchoResult:
|
40
|
+
"""
|
41
|
+
Executes echo command and returns the parameters back.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
**kwargs: Any parameters to echo back.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
EchoResult: Command execution result with the parameters.
|
48
|
+
"""
|
49
|
+
logger.debug(f"Echo command received parameters: {kwargs}")
|
50
|
+
|
51
|
+
# Simply return the parameters that were passed
|
52
|
+
return EchoResult(params=kwargs)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
"""
|
2
|
+
Module with result class for echo command.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Any, Dict, ClassVar, Type
|
6
|
+
from pydantic import BaseModel, Field
|
7
|
+
|
8
|
+
from mcp_proxy_adapter.commands.result import CommandResult
|
9
|
+
|
10
|
+
|
11
|
+
class EchoResult(CommandResult, BaseModel):
|
12
|
+
"""
|
13
|
+
Result of echo command execution.
|
14
|
+
|
15
|
+
Attributes:
|
16
|
+
params (Dict[str, Any]): Parameters that were passed to the command.
|
17
|
+
"""
|
18
|
+
|
19
|
+
params: Dict[str, Any] = Field(..., description="Parameters that were passed to the command")
|
20
|
+
|
21
|
+
def to_dict(self) -> Dict[str, Any]:
|
22
|
+
"""
|
23
|
+
Converts result to dictionary for serialization.
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
Dictionary with result data.
|
27
|
+
"""
|
28
|
+
return {
|
29
|
+
"params": self.params
|
30
|
+
}
|
31
|
+
|
32
|
+
@classmethod
|
33
|
+
def get_schema(cls) -> Dict[str, Any]:
|
34
|
+
"""
|
35
|
+
Returns JSON schema for result validation.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Dictionary with JSON schema.
|
39
|
+
"""
|
40
|
+
return {
|
41
|
+
"type": "object",
|
42
|
+
"properties": {
|
43
|
+
"params": {
|
44
|
+
"type": "object",
|
45
|
+
"description": "Parameters that were passed to the command",
|
46
|
+
"additionalProperties": True
|
47
|
+
}
|
48
|
+
},
|
49
|
+
"required": ["params"]
|
50
|
+
}
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def from_dict(cls, data: Dict[str, Any]) -> "EchoResult":
|
54
|
+
"""
|
55
|
+
Creates result instance from dictionary.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
data: Dictionary with result data.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
EchoResult instance.
|
62
|
+
"""
|
63
|
+
return cls(
|
64
|
+
params=data.get("params", {})
|
65
|
+
)
|
@@ -0,0 +1,98 @@
|
|
1
|
+
"""
|
2
|
+
Module for the get_date command implementation.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
from typing import Any, Dict
|
7
|
+
|
8
|
+
from mcp_proxy_adapter.commands.base import Command
|
9
|
+
from mcp_proxy_adapter.commands.result import CommandResult
|
10
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
11
|
+
from mcp_proxy_adapter.core.logging import logger
|
12
|
+
|
13
|
+
|
14
|
+
class GetDateResult(CommandResult):
|
15
|
+
"""Result of getting current date"""
|
16
|
+
|
17
|
+
def __init__(self, date: str):
|
18
|
+
"""
|
19
|
+
Initialize the GetDateResult.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
date: Date in ISO 8601 format
|
23
|
+
"""
|
24
|
+
self.date = date
|
25
|
+
|
26
|
+
def to_dict(self) -> Dict[str, Any]:
|
27
|
+
"""
|
28
|
+
Convert the result to a dictionary.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
Dictionary with the date
|
32
|
+
"""
|
33
|
+
return {"date": self.date}
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def get_schema(cls) -> Dict[str, Any]:
|
37
|
+
"""
|
38
|
+
Get the JSON schema for this result.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
JSON schema
|
42
|
+
"""
|
43
|
+
return {
|
44
|
+
"type": "object",
|
45
|
+
"required": ["date"],
|
46
|
+
"properties": {
|
47
|
+
"date": {
|
48
|
+
"type": "string",
|
49
|
+
"description": "Current date and time in ISO 8601 format",
|
50
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}[+-]\\d{2}:?\\d{2}$"
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
class GetDateCommand(Command):
|
57
|
+
"""
|
58
|
+
Command that returns the current date and time in ISO 8601 format.
|
59
|
+
"""
|
60
|
+
|
61
|
+
name = "get_date"
|
62
|
+
|
63
|
+
async def execute(self) -> GetDateResult:
|
64
|
+
"""
|
65
|
+
Execute the get_date command.
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
GetDateResult: Result with date in ISO 8601 format
|
69
|
+
"""
|
70
|
+
# Get current time with timezone info
|
71
|
+
now = datetime.now().astimezone()
|
72
|
+
|
73
|
+
# Format to ISO 8601
|
74
|
+
date_str = now.strftime("%Y-%m-%dT%H:%M:%S%z")
|
75
|
+
|
76
|
+
# Insert colon in timezone offset (e.g. +0300 -> +03:00)
|
77
|
+
if len(date_str) >= 6:
|
78
|
+
date_str = date_str[:-2] + ":" + date_str[-2:]
|
79
|
+
|
80
|
+
logger.debug(f"Generated date: {date_str}")
|
81
|
+
return GetDateResult(date=date_str)
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
def get_schema(cls) -> Dict[str, Any]:
|
85
|
+
"""
|
86
|
+
Get the JSON schema for this command.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
JSON schema
|
90
|
+
"""
|
91
|
+
return {
|
92
|
+
"type": "object",
|
93
|
+
"properties": {}
|
94
|
+
}
|
95
|
+
|
96
|
+
|
97
|
+
# Register the command
|
98
|
+
registry.register(GetDateCommand)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
"""
|
2
|
+
Module for the new_uuid4 command implementation.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import uuid
|
6
|
+
from typing import Any, Dict
|
7
|
+
|
8
|
+
from mcp_proxy_adapter.commands.base import Command
|
9
|
+
from mcp_proxy_adapter.commands.result import CommandResult
|
10
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
11
|
+
from mcp_proxy_adapter.core.logging import logger
|
12
|
+
|
13
|
+
|
14
|
+
class NewUuid4Result(CommandResult):
|
15
|
+
"""Result of UUID4 generation"""
|
16
|
+
|
17
|
+
def __init__(self, uuid_str: str):
|
18
|
+
"""
|
19
|
+
Initialize the NewUuid4Result.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
uuid_str: UUID in string format
|
23
|
+
"""
|
24
|
+
self.uuid = uuid_str
|
25
|
+
|
26
|
+
def to_dict(self) -> Dict[str, Any]:
|
27
|
+
"""
|
28
|
+
Convert the result to a dictionary.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
Dictionary with the UUID
|
32
|
+
"""
|
33
|
+
return {"uuid": self.uuid}
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def get_schema(cls) -> Dict[str, Any]:
|
37
|
+
"""
|
38
|
+
Get the JSON schema for this result.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
JSON schema
|
42
|
+
"""
|
43
|
+
return {
|
44
|
+
"type": "object",
|
45
|
+
"required": ["uuid"],
|
46
|
+
"properties": {
|
47
|
+
"uuid": {
|
48
|
+
"type": "string",
|
49
|
+
"description": "Generated UUID4 in string format",
|
50
|
+
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
class NewUuid4Command(Command):
|
57
|
+
"""
|
58
|
+
Command that generates a new UUID version 4 (random).
|
59
|
+
"""
|
60
|
+
|
61
|
+
name = "new_uuid4"
|
62
|
+
|
63
|
+
async def execute(self) -> NewUuid4Result:
|
64
|
+
"""
|
65
|
+
Execute the new_uuid4 command.
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
NewUuid4Result: Result with UUID in string format
|
69
|
+
"""
|
70
|
+
# Generate a UUID4
|
71
|
+
uuid_str = str(uuid.uuid4())
|
72
|
+
|
73
|
+
logger.debug(f"Generated UUID4: {uuid_str}")
|
74
|
+
return NewUuid4Result(uuid_str=uuid_str)
|
75
|
+
|
76
|
+
@classmethod
|
77
|
+
def get_schema(cls) -> Dict[str, Any]:
|
78
|
+
"""
|
79
|
+
Get the JSON schema for this command.
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
JSON schema
|
83
|
+
"""
|
84
|
+
return {
|
85
|
+
"type": "object",
|
86
|
+
"properties": {}
|
87
|
+
}
|
88
|
+
|
89
|
+
|
90
|
+
# Register the command
|
91
|
+
registry.register(NewUuid4Command)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
FROM python:3.11-slim
|
2
|
+
|
3
|
+
# Set working directory
|
4
|
+
WORKDIR /app
|
5
|
+
|
6
|
+
# Install dependencies
|
7
|
+
COPY requirements.txt .
|
8
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
9
|
+
|
10
|
+
# Create necessary directories
|
11
|
+
RUN mkdir -p /app/logs /app/cache /app/data
|
12
|
+
|
13
|
+
# Copy application files
|
14
|
+
COPY . .
|
15
|
+
|
16
|
+
# Set environment variables
|
17
|
+
ENV PYTHONUNBUFFERED=1
|
18
|
+
ENV CONFIG_PATH=/app/configs/config.docker.yaml
|
19
|
+
|
20
|
+
# Expose API port
|
21
|
+
EXPOSE 8000
|
22
|
+
|
23
|
+
# Run the application
|
24
|
+
CMD ["python", "server.py", "--config", "/app/configs/config.docker.yaml"]
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# Complete MCP Proxy Adapter Example
|
2
|
+
|
3
|
+
This example demonstrates a complete MCP Proxy Adapter application with Docker support,
|
4
|
+
environment-specific configuration, and multiple commands.
|
5
|
+
|
6
|
+
## Structure
|
7
|
+
|
8
|
+
```
|
9
|
+
complete_example/
|
10
|
+
├── cache/ # Cache directory
|
11
|
+
├── commands/ # Commands directory
|
12
|
+
│ ├── __init__.py # Package initialization
|
13
|
+
│ ├── db_command.py # Database command
|
14
|
+
│ ├── file_command.py # File operations command
|
15
|
+
│ └── system_command.py # System information command
|
16
|
+
├── configs/ # Configuration files
|
17
|
+
│ ├── config.dev.yaml # Development config
|
18
|
+
│ └── config.docker.yaml # Docker config
|
19
|
+
├── docker-compose.yml # Docker Compose file
|
20
|
+
├── Dockerfile # Docker image definition
|
21
|
+
├── logs/ # Logs directory
|
22
|
+
├── README.md # This documentation file
|
23
|
+
├── requirements.txt # Python dependencies
|
24
|
+
└── server.py # Server startup file
|
25
|
+
```
|
26
|
+
|
27
|
+
## Features
|
28
|
+
|
29
|
+
- Multiple commands in separate files
|
30
|
+
- Environment-specific configuration
|
31
|
+
- Docker support
|
32
|
+
- Volume mounting for logs, cache, and config
|
33
|
+
- Network configuration
|
34
|
+
|
35
|
+
## Running Locally
|
36
|
+
|
37
|
+
```bash
|
38
|
+
# Navigate to the project directory
|
39
|
+
cd examples/complete_example
|
40
|
+
|
41
|
+
# Create virtual environment (optional)
|
42
|
+
python -m venv venv
|
43
|
+
source venv/bin/activate # Linux/Mac
|
44
|
+
# or
|
45
|
+
# venv\Scripts\activate # Windows
|
46
|
+
|
47
|
+
# Install dependencies
|
48
|
+
pip install -r requirements.txt
|
49
|
+
pip install -e ../.. # Install mcp_proxy_adapter from parent directory
|
50
|
+
|
51
|
+
# Run the server with development config
|
52
|
+
python server.py --config configs/config.dev.yaml
|
53
|
+
```
|
54
|
+
|
55
|
+
The server will be available at [http://localhost:8000](http://localhost:8000).
|
56
|
+
|
57
|
+
## Running with Docker
|
58
|
+
|
59
|
+
```bash
|
60
|
+
# Navigate to the project directory
|
61
|
+
cd examples/complete_example
|
62
|
+
|
63
|
+
# Build the Docker image
|
64
|
+
docker build -t mcp-proxy-adapter-example .
|
65
|
+
|
66
|
+
# Run with Docker Compose
|
67
|
+
docker-compose up
|
68
|
+
```
|
69
|
+
|
70
|
+
The server will be available at [http://localhost:8000](http://localhost:8000).
|
71
|
+
|
72
|
+
## Available Commands
|
73
|
+
|
74
|
+
The microservice includes several commands that demonstrate different features:
|
75
|
+
|
76
|
+
1. `system_info` - Returns system information
|
77
|
+
2. `file_operations` - Performs file operations
|
78
|
+
3. `db_query` - Executes database queries
|
79
|
+
|
80
|
+
For details on each command, see the API documentation at [http://localhost:8000/docs](http://localhost:8000/docs)
|
81
|
+
when the server is running.
|
82
|
+
|
83
|
+
## Docker Configuration
|
84
|
+
|
85
|
+
The Docker setup includes:
|
86
|
+
|
87
|
+
- Volume mounts for logs, cache, and configuration
|
88
|
+
- Network configuration for integration with other services
|
89
|
+
- User mapping to avoid permission issues
|
90
|
+
- Port forwarding
|
91
|
+
|
92
|
+
For details, see the `docker-compose.yml` file.
|