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
@@ -1,131 +0,0 @@
1
- """
2
- Client for testing interaction with OpenAPI server through MCP Proxy.
3
-
4
- This script demonstrates how a client can send JSON-RPC requests
5
- to MCP Proxy, which forwards them to the OpenAPI server.
6
-
7
- Usage:
8
- python examples/mcp_proxy_client.py
9
- """
10
- import os
11
- import sys
12
- import json
13
- import requests
14
- from typing import Dict, Any, Optional
15
-
16
- def send_jsonrpc_request(
17
- endpoint: str,
18
- method: str,
19
- params: Optional[Dict[str, Any]] = None,
20
- request_id: int = 1
21
- ) -> Dict[str, Any]:
22
- """
23
- Sends JSON-RPC request to specified endpoint.
24
-
25
- Args:
26
- endpoint (str): Endpoint URL
27
- method (str): Method name to call
28
- params (Optional[Dict[str, Any]], optional): Method parameters. Defaults to None.
29
- request_id (int, optional): Request ID. Defaults to 1.
30
-
31
- Returns:
32
- Dict[str, Any]: Server response
33
- """
34
- # Form JSON-RPC request
35
- payload = {
36
- "jsonrpc": "2.0",
37
- "method": method,
38
- "id": request_id
39
- }
40
-
41
- # Add parameters if they exist
42
- if params:
43
- payload["params"] = params
44
-
45
- # Send request
46
- response = requests.post(endpoint, json=payload)
47
-
48
- # Return response in JSON format
49
- return response.json()
50
-
51
- def print_response(response: Dict[str, Any]) -> None:
52
- """
53
- Prints response in formatted view.
54
-
55
- Args:
56
- response (Dict[str, Any]): Server response
57
- """
58
- print("Response:")
59
- print(json.dumps(response, indent=2, ensure_ascii=False))
60
- print("-" * 50)
61
-
62
- def main():
63
- """Main function for demonstrating work with MCP Proxy."""
64
- # Base server URL
65
- server_url = "http://localhost:8000"
66
-
67
- # JSON-RPC endpoint (should match cmd_endpoint in MCPProxyAdapter)
68
- jsonrpc_endpoint = f"{server_url}/api/command"
69
-
70
- print("=== Testing JSON-RPC requests through MCP Proxy ===\n")
71
-
72
- # Example 1: Get all items
73
- print("1. Getting all items:")
74
- response = send_jsonrpc_request(jsonrpc_endpoint, "get_items")
75
- print_response(response)
76
-
77
- # Example 2: Get item by ID
78
- print("2. Getting item by ID:")
79
- response = send_jsonrpc_request(jsonrpc_endpoint, "get_item", {"item_id": 1})
80
- print_response(response)
81
-
82
- # Example 3: Create new item
83
- print("3. Creating new item:")
84
- new_item = {
85
- "name": "Test Item",
86
- "description": "Item for API testing",
87
- "price": 123.45,
88
- "is_available": True
89
- }
90
- response = send_jsonrpc_request(jsonrpc_endpoint, "create_item", {"item": new_item})
91
- print_response(response)
92
-
93
- # Get created item ID for further operations
94
- if "result" in response:
95
- created_item_id = response["result"]["id"]
96
- else:
97
- # Use fixed ID in case of error
98
- created_item_id = 4
99
-
100
- # Example 4: Update item
101
- print(f"4. Updating item with ID {created_item_id}:")
102
- updated_data = {
103
- "name": "Updated Item",
104
- "price": 199.99
105
- }
106
- response = send_jsonrpc_request(
107
- jsonrpc_endpoint,
108
- "update_item",
109
- {"item_id": created_item_id, "updated_data": updated_data}
110
- )
111
- print_response(response)
112
-
113
- # Example 5: Search items
114
- print("5. Searching items by keyword:")
115
- response = send_jsonrpc_request(jsonrpc_endpoint, "search_items", {"keyword": "updated"})
116
- print_response(response)
117
-
118
- # Example 6: Delete item
119
- print(f"6. Deleting item with ID {created_item_id}:")
120
- response = send_jsonrpc_request(jsonrpc_endpoint, "delete_item", {"item_id": created_item_id})
121
- print_response(response)
122
-
123
- # Example 7: Check after deletion
124
- print("7. Checking items list after deletion:")
125
- response = send_jsonrpc_request(jsonrpc_endpoint, "get_items")
126
- print_response(response)
127
-
128
- print("Testing completed.")
129
-
130
- if __name__ == "__main__":
131
- main()
@@ -1,383 +0,0 @@
1
- """
2
- Example of creating an OpenAPI server using MCP Proxy Adapter.
3
-
4
- Usage:
5
- python examples/openapi_server.py
6
-
7
- Server will be available at: http://localhost:8000
8
- """
9
- import os
10
- import sys
11
- import logging
12
- from typing import Dict, List, Any
13
-
14
- # Add project root directory to sys.path
15
- sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
16
-
17
- from fastapi import FastAPI, Query, Path, Body, Request
18
- from pydantic import BaseModel, Field
19
- import uvicorn
20
-
21
- # Import MCP Proxy Adapter
22
- from mcp_proxy_adapter.adapter import MCPProxyAdapter
23
-
24
- # Import models for JSON-RPC
25
- from mcp_proxy_adapter.models import JsonRpcRequest, JsonRpcResponse
26
-
27
- # Import MockRegistry from tests for example
28
- # (in a real project, CommandRegistry would be used)
29
- from mcp_proxy_adapter.testing_utils import MockRegistry
30
-
31
- # Configure logging
32
- logging.basicConfig(level=logging.DEBUG)
33
- logger = logging.getLogger(__name__)
34
-
35
- # Data model definitions
36
- class Item(BaseModel):
37
- """Data model for an item."""
38
- id: int = Field(..., description="Unique item identifier")
39
- name: str = Field(..., description="Item name")
40
- description: str = Field(None, description="Item description")
41
- price: float = Field(..., description="Item price")
42
- is_available: bool = Field(True, description="Item availability")
43
-
44
- model_config = {
45
- "json_schema_extra": {
46
- "example": {
47
- "id": 1,
48
- "name": "Super Product",
49
- "description": "Best product on the market",
50
- "price": 99.99,
51
- "is_available": True
52
- }
53
- }
54
- }
55
-
56
- # Example data
57
- items_db = [
58
- {
59
- "id": 1,
60
- "name": "Smartphone X",
61
- "description": "Latest smartphone with cutting-edge technology",
62
- "price": 999.99,
63
- "is_available": True
64
- },
65
- {
66
- "id": 2,
67
- "name": "Laptop Y",
68
- "description": "Powerful laptop for professionals",
69
- "price": 1499.99,
70
- "is_available": True
71
- },
72
- {
73
- "id": 3,
74
- "name": "Tablet Z",
75
- "description": "Compact tablet for creativity",
76
- "price": 599.99,
77
- "is_available": False
78
- }
79
- ]
80
-
81
- # Define commands for MockRegistry
82
- class MockDispatcher:
83
- """Mock for command dispatcher in example."""
84
-
85
- def __init__(self):
86
- self.commands = {
87
- "get_items": self.get_items,
88
- "get_item": self.get_item,
89
- "create_item": self.create_item,
90
- "update_item": self.update_item,
91
- "delete_item": self.delete_item,
92
- "search_items": self.search_items,
93
- "execute": self.execute_command,
94
- "help": self.help_command
95
- }
96
- self.commands_info = {
97
- "get_items": {
98
- "description": "Get list of all items",
99
- "params": {}
100
- },
101
- "get_item": {
102
- "description": "Get item by ID",
103
- "params": {
104
- "item_id": {
105
- "type": "integer",
106
- "description": "Item ID to search for",
107
- "required": True
108
- }
109
- }
110
- },
111
- "create_item": {
112
- "description": "Create new item",
113
- "params": {
114
- "item": {
115
- "type": "object",
116
- "description": "Item data",
117
- "required": True
118
- }
119
- }
120
- },
121
- "update_item": {
122
- "description": "Update item by ID",
123
- "params": {
124
- "item_id": {
125
- "type": "integer",
126
- "description": "Item ID to update",
127
- "required": True
128
- },
129
- "updated_data": {
130
- "type": "object",
131
- "description": "Updated data",
132
- "required": True
133
- }
134
- }
135
- },
136
- "delete_item": {
137
- "description": "Delete item by ID",
138
- "params": {
139
- "item_id": {
140
- "type": "integer",
141
- "description": "Item ID to delete",
142
- "required": True
143
- }
144
- }
145
- },
146
- "search_items": {
147
- "description": "Search items by keyword",
148
- "params": {
149
- "keyword": {
150
- "type": "string",
151
- "description": "Search keyword",
152
- "required": True
153
- }
154
- }
155
- },
156
- "execute": {
157
- "description": "Universal command for executing queries",
158
- "params": {
159
- "query": {
160
- "type": "string",
161
- "description": "Query to execute",
162
- "required": False
163
- },
164
- "subcommand": {
165
- "type": "string",
166
- "description": "Subcommand to execute",
167
- "required": False
168
- }
169
- }
170
- },
171
- "help": {
172
- "description": "Show information about available commands or a specific command.",
173
- "params": {
174
- "cmdname": {
175
- "type": "string",
176
- "description": "Command name for detailed info",
177
- "required": False
178
- }
179
- }
180
- }
181
- }
182
-
183
- def execute(self, command_name, **params):
184
- """Executes command with specified parameters."""
185
- if command_name not in self.commands:
186
- raise KeyError(f"Unknown command: {command_name}")
187
- return self.commands[command_name](**params)
188
-
189
- def execute_command(self, **params):
190
- """Universal method for executing commands."""
191
- query = params.get("query", "")
192
- subcommand = params.get("subcommand", "")
193
-
194
- # Debug logging
195
- logger.info(f"Executing universal command with query={query}, subcommand={subcommand}, params={params}")
196
-
197
- # Handle different command types
198
- if query.lower() in ["list", "all", "items"]:
199
- # Return list of all items
200
- return self.get_items()
201
- elif query.lower() == "search" and "keyword" in params:
202
- # Search by keyword
203
- return self.search_items(params["keyword"])
204
- elif query.isdigit() or (isinstance(query, int) and query > 0):
205
- # If query looks like an ID, return item by ID
206
- try:
207
- return self.get_item(int(query))
208
- except ValueError:
209
- return {"error": f"Item with ID {query} not found"}
210
- else:
211
- # By default return information about available commands
212
- commands = list(self.commands.keys())
213
- return {
214
- "available_commands": commands,
215
- "message": "Use one of the available commands or specify query for executing query",
216
- "received_params": params
217
- }
218
-
219
- def get_valid_commands(self):
220
- """Returns list of available commands."""
221
- return list(self.commands.keys())
222
-
223
- def get_command_info(self, command_name):
224
- """Returns information about command."""
225
- return self.commands_info.get(command_name)
226
-
227
- def get_commands_info(self):
228
- """Returns information about all commands."""
229
- return self.commands_info
230
-
231
- # Command implementations
232
- def get_items(self):
233
- """Get list of all items."""
234
- return items_db
235
-
236
- def get_item(self, item_id):
237
- """Get item by ID."""
238
- for item in items_db:
239
- if item["id"] == item_id:
240
- return item
241
- raise ValueError(f"Item with ID {item_id} not found")
242
-
243
- def create_item(self, item):
244
- """Create new item."""
245
- # Find maximum ID
246
- max_id = max([i["id"] for i in items_db]) if items_db else 0
247
- # Create new item with increased ID
248
- new_item = {**item, "id": max_id + 1}
249
- items_db.append(new_item)
250
- return new_item
251
-
252
- def update_item(self, item_id, updated_data):
253
- """Update item by ID."""
254
- for i, item in enumerate(items_db):
255
- if item["id"] == item_id:
256
- # Update item, keeping original ID
257
- updated_item = {**item, **updated_data, "id": item_id}
258
- items_db[i] = updated_item
259
- return updated_item
260
- raise ValueError(f"Item with ID {item_id} not found")
261
-
262
- def delete_item(self, item_id):
263
- """Delete item by ID."""
264
- for i, item in enumerate(items_db):
265
- if item["id"] == item_id:
266
- del items_db[i]
267
- return {"message": f"Item with ID {item_id} successfully deleted"}
268
- raise ValueError(f"Item with ID {item_id} not found")
269
-
270
- def search_items(self, keyword):
271
- """Search items by keyword."""
272
- keyword = keyword.lower()
273
- return [
274
- item for item in items_db
275
- if keyword in item["name"].lower() or
276
- (item["description"] and keyword in item["description"].lower())
277
- ]
278
-
279
- def help_command(self, **params):
280
- """Return info about all commands or a specific command."""
281
- cmdname = params.get("cmdname")
282
- if cmdname:
283
- info = self.commands_info.get(cmdname)
284
- if info:
285
- return {"command": cmdname, "info": info}
286
- else:
287
- return {"error": f"Command '{cmdname}' not found", "available_commands": list(self.commands_info.keys())}
288
- # Если параметр cmdname не указан, возвращаем краткую информацию обо всех
289
- return {
290
- "commands": {cmd: {"description": info["description"], "params": info["params"]} for cmd, info in self.commands_info.items()},
291
- "total": len(self.commands_info),
292
- "note": "Use the 'cmdname' parameter to get detailed information about a specific command"
293
- }
294
-
295
- # --- Создание registry и FastAPI-приложения на верхнем уровне ---
296
- class CustomMockRegistry(MockRegistry):
297
- """Custom command registry for example."""
298
- def __init__(self):
299
- self.dispatcher = MockDispatcher()
300
- self.generators = []
301
-
302
- registry = CustomMockRegistry()
303
- app = FastAPI(
304
- title="OpenAPI Server Example",
305
- description="Example OpenAPI server with MCP Proxy Adapter integration",
306
- version="1.0.0"
307
- )
308
-
309
- # Configure CORS
310
- from fastapi.middleware.cors import CORSMiddleware
311
- app.add_middleware(
312
- CORSMiddleware,
313
- allow_origins=["*"],
314
- allow_credentials=True,
315
- allow_methods=["*"],
316
- allow_headers=["*"],
317
- )
318
-
319
- # Create MCP Proxy adapter with explicit endpoint
320
- adapter = MCPProxyAdapter(registry, cmd_endpoint="/cmd")
321
- # Register adapter endpoints
322
- adapter.register_endpoints(app)
323
- # Save MCP Proxy configuration to file
324
- config_path = os.path.join(os.path.dirname(__file__), "mcp_proxy_config.json")
325
- adapter.save_config_to_file(config_path)
326
- logger.info(f"MCP Proxy configuration saved to {config_path}")
327
-
328
- # --- REST endpoints ---
329
- @app.get("/")
330
- def read_root():
331
- return {
332
- "message": "OpenAPI Server Example with MCP Proxy Adapter integration",
333
- "endpoints": {
334
- "items": "/items",
335
- "item": "/items/{item_id}",
336
- "search": "/items/search",
337
- "mcp_proxy": "/cmd"
338
- }
339
- }
340
-
341
- @app.get("/items", response_model=List[Item])
342
- def read_items():
343
- return items_db
344
-
345
- @app.get("/items/{item_id}", response_model=Item)
346
- def read_item(item_id: int = Path(..., description="Item ID", gt=0)):
347
- try:
348
- return registry.dispatcher.get_item(item_id)
349
- except ValueError as e:
350
- return {"error": str(e)}
351
-
352
- @app.post("/items", response_model=Item)
353
- def create_new_item(item: Item = Body(..., description="Data of new item")):
354
- return registry.dispatcher.create_item(item.model_dump())
355
-
356
- @app.put("/items/{item_id}", response_model=Item)
357
- def update_existing_item(
358
- item_id: int = Path(..., description="Item ID to update", gt=0),
359
- item: Item = Body(..., description="Updated item data")
360
- ):
361
- try:
362
- return registry.dispatcher.update_item(item_id, item.model_dump())
363
- except ValueError as e:
364
- return {"error": str(e)}
365
-
366
- @app.delete("/items/{item_id}")
367
- def delete_existing_item(item_id: int = Path(..., description="Item ID to delete", gt=0)):
368
- try:
369
- return registry.dispatcher.delete_item(item_id)
370
- except ValueError as e:
371
- return {"error": str(e)}
372
-
373
- @app.get("/items/search", response_model=List[Item])
374
- def search_items_by_keyword(keyword: str = Query(..., description="Search keyword")):
375
- return registry.dispatcher.search_items(keyword)
376
-
377
- # --- main() только для запуска через python ---
378
- def main():
379
- import uvicorn
380
- uvicorn.run(app, host="0.0.0.0", port=8000)
381
-
382
- if __name__ == "__main__":
383
- main()
@@ -1,47 +0,0 @@
1
- """
2
- Project Structure Example for MCPProxyAdapter
3
-
4
- - How to organize your project for clean integration
5
- - Where to place registry, commands, adapter
6
- - How to register endpoints in FastAPI
7
-
8
- Run:
9
- python examples/project_structure_example.py
10
- """
11
- import os
12
- import sys
13
- sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
14
- from fastapi import FastAPI
15
- from mcp_proxy_adapter.adapter import MCPProxyAdapter
16
-
17
- # --- Command registry and commands ---
18
- class MyRegistry:
19
- def __init__(self):
20
- self.dispatcher = self
21
- self.commands = {"hello": self.hello}
22
- self.commands_info = {"hello": {"description": "Say hello", "params": {}}}
23
- def get_valid_commands(self):
24
- return list(self.commands.keys())
25
- def get_command_info(self, command):
26
- return self.commands_info.get(command)
27
- def get_commands_info(self):
28
- return self.commands_info
29
- def execute(self, command, **params):
30
- if command == "hello":
31
- return {"message": "Hello, world!"}
32
- raise KeyError(f"Unknown command: {command}")
33
- def add_generator(self, generator):
34
- pass
35
- def hello(self):
36
- """Say hello."""
37
- return {"message": "Hello, world!"}
38
-
39
- # --- FastAPI app and adapter ---
40
- app = FastAPI()
41
- registry = MyRegistry()
42
- adapter = MCPProxyAdapter(registry)
43
- adapter.register_endpoints(app)
44
-
45
- if __name__ == "__main__":
46
- import uvicorn
47
- uvicorn.run(app, host="0.0.0.0", port=8000)
@@ -1,64 +0,0 @@
1
- """
2
- Testing Example for MCPProxyAdapter
3
-
4
- - How to write unit and integration tests for commands
5
- - How to test help and error handling
6
- - Best practices for test structure
7
-
8
- Run:
9
- python examples/testing_example.py
10
- """
11
- import os
12
- import sys
13
- import asyncio
14
- sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
15
- from mcp_proxy_adapter.adapter import MCPProxyAdapter
16
-
17
- class MyRegistry:
18
- def __init__(self):
19
- self.dispatcher = self
20
- self.commands = {"echo": self.echo}
21
- self.commands_info = {"echo": {"description": "Echo input string", "params": {"text": {"type": "string", "description": "Text to echo", "required": True}}}}
22
- def get_valid_commands(self):
23
- return list(self.commands.keys())
24
- def get_command_info(self, command):
25
- return self.commands_info.get(command)
26
- def get_commands_info(self):
27
- return self.commands_info
28
- def execute(self, command, **params):
29
- if command == "echo":
30
- return self.echo(**params)
31
- raise KeyError(f"Unknown command: {command}")
32
- def add_generator(self, generator):
33
- pass
34
- def echo(self, text: str) -> str:
35
- """Echo input string."""
36
- return text
37
-
38
- def test_echo():
39
- registry = MyRegistry()
40
- adapter = MCPProxyAdapter(registry)
41
- # Unit test
42
- assert registry.execute("echo", text="hi") == "hi"
43
- # Integration test (simulate JSON-RPC)
44
- class Request:
45
- method = "echo"
46
- params = {"text": "hello"}
47
- id = 1
48
- response = adapter.router.routes[0].endpoint(Request())
49
- # Not a real FastAPI call, just for illustration
50
- print("[TEST] Echo command passed.")
51
-
52
- # Call sync handler
53
- registry = MyRegistry()
54
- adapter = MCPProxyAdapter(registry)
55
- result_sync = registry.execute('echo', text='hi')
56
- print(result_sync) # hi
57
-
58
- # Call async handler
59
- result_async = asyncio.run(registry.execute('async', x=10))
60
- print(result_async) # 20
61
-
62
- if __name__ == "__main__":
63
- test_echo()
64
- print("All tests passed.")
@@ -1,47 +0,0 @@
1
- """
2
- Data models for MCP Proxy Adapter.
3
- """
4
- from typing import Dict, Any, List, Optional, Union
5
- from pydantic import BaseModel, Field
6
-
7
- class JsonRpcRequest(BaseModel):
8
- """Base model for JSON-RPC requests."""
9
- jsonrpc: str = Field(default="2.0", description="JSON-RPC version")
10
- method: str = Field(..., description="Method name to call")
11
- params: Dict[str, Any] = Field(default_factory=dict, description="Method parameters")
12
- id: Optional[Union[str, int]] = Field(default=None, description="Request identifier")
13
-
14
- class JsonRpcResponse(BaseModel):
15
- """Base model for JSON-RPC responses."""
16
- jsonrpc: str = Field(default="2.0", description="JSON-RPC version")
17
- result: Optional[Any] = Field(default=None, description="Method execution result")
18
- error: Optional[Dict[str, Any]] = Field(default=None, description="Error information")
19
- id: Optional[Union[str, int]] = Field(default=None, description="Request identifier")
20
-
21
- class CommandInfo(BaseModel):
22
- """Command information model."""
23
- name: str = Field(..., description="Command name")
24
- description: str = Field(default="", description="Command description")
25
- summary: Optional[str] = Field(default=None, description="Brief description")
26
- parameters: Dict[str, Dict[str, Any]] = Field(default_factory=dict, description="Command parameters")
27
- returns: Optional[Dict[str, Any]] = Field(default=None, description="Return value information")
28
-
29
- class CommandParameter(BaseModel):
30
- """Command parameter model."""
31
- type: str = Field(..., description="Parameter type")
32
- description: str = Field(default="", description="Parameter description")
33
- required: bool = Field(default=False, description="Whether the parameter is required")
34
- default: Optional[Any] = Field(default=None, description="Default value")
35
- enum: Optional[List[Any]] = Field(default=None, description="Possible values for enumeration")
36
-
37
- class MCPProxyTool(BaseModel):
38
- """Tool model for MCPProxy."""
39
- name: str = Field(..., description="Tool name")
40
- description: str = Field(default="", description="Tool description")
41
- parameters: Dict[str, Any] = Field(..., description="Tool parameters schema")
42
-
43
- class MCPProxyConfig(BaseModel):
44
- """Configuration model for MCPProxy."""
45
- version: str = Field(default="1.0", description="Configuration version")
46
- tools: List[MCPProxyTool] = Field(default_factory=list, description="List of tools")
47
- routes: List[Dict[str, Any]] = Field(default_factory=list, description="Routes configuration")