mcp-proxy-adapter 2.1.0__py3-none-any.whl → 2.1.2__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 (78) hide show
  1. docs/README.md +172 -0
  2. docs/README_ru.md +172 -0
  3. docs/architecture.md +251 -0
  4. docs/architecture_ru.md +343 -0
  5. docs/command_development.md +250 -0
  6. docs/command_development_ru.md +593 -0
  7. docs/deployment.md +251 -0
  8. docs/deployment_ru.md +1298 -0
  9. docs/examples.md +254 -0
  10. docs/examples_ru.md +401 -0
  11. docs/mcp_proxy_adapter.md +251 -0
  12. docs/mcp_proxy_adapter_ru.md +405 -0
  13. docs/quickstart.md +251 -0
  14. docs/quickstart_ru.md +397 -0
  15. docs/testing.md +255 -0
  16. docs/testing_ru.md +469 -0
  17. docs/validation_ru.md +287 -0
  18. examples/analyze_config.py +141 -0
  19. examples/basic_integration.py +161 -0
  20. examples/docstring_and_schema_example.py +60 -0
  21. examples/extension_example.py +60 -0
  22. examples/help_best_practices.py +67 -0
  23. examples/help_usage.py +64 -0
  24. examples/mcp_proxy_client.py +131 -0
  25. examples/mcp_proxy_config.json +175 -0
  26. examples/openapi_server.py +369 -0
  27. examples/project_structure_example.py +47 -0
  28. examples/testing_example.py +53 -0
  29. mcp_proxy_adapter/__init__.py +17 -0
  30. mcp_proxy_adapter/adapter.py +697 -0
  31. mcp_proxy_adapter/models.py +47 -0
  32. mcp_proxy_adapter/registry.py +439 -0
  33. mcp_proxy_adapter/schema.py +257 -0
  34. {mcp_proxy_adapter-2.1.0.dist-info → mcp_proxy_adapter-2.1.2.dist-info}/METADATA +2 -2
  35. mcp_proxy_adapter-2.1.2.dist-info/RECORD +61 -0
  36. mcp_proxy_adapter-2.1.2.dist-info/top_level.txt +5 -0
  37. scripts/code_analyzer/code_analyzer.py +328 -0
  38. scripts/code_analyzer/register_commands.py +446 -0
  39. scripts/publish.py +85 -0
  40. tests/conftest.py +12 -0
  41. tests/test_adapter.py +529 -0
  42. tests/test_adapter_coverage.py +274 -0
  43. tests/test_basic_dispatcher.py +169 -0
  44. tests/test_command_registry.py +328 -0
  45. tests/test_examples.py +32 -0
  46. tests/test_mcp_proxy_adapter.py +568 -0
  47. tests/test_mcp_proxy_adapter_basic.py +262 -0
  48. tests/test_part1.py +348 -0
  49. tests/test_part2.py +524 -0
  50. tests/test_schema.py +358 -0
  51. tests/test_simple_adapter.py +251 -0
  52. adapters/__init__.py +0 -16
  53. cli/__init__.py +0 -12
  54. cli/__main__.py +0 -79
  55. cli/command_runner.py +0 -233
  56. generators/__init__.py +0 -14
  57. generators/endpoint_generator.py +0 -172
  58. generators/openapi_generator.py +0 -254
  59. generators/rest_api_generator.py +0 -207
  60. mcp_proxy_adapter-2.1.0.dist-info/RECORD +0 -28
  61. mcp_proxy_adapter-2.1.0.dist-info/top_level.txt +0 -7
  62. openapi_schema/__init__.py +0 -38
  63. openapi_schema/command_registry.py +0 -312
  64. openapi_schema/rest_schema.py +0 -510
  65. openapi_schema/rpc_generator.py +0 -307
  66. openapi_schema/rpc_schema.py +0 -416
  67. validators/__init__.py +0 -14
  68. validators/base_validator.py +0 -23
  69. {analyzers → mcp_proxy_adapter/analyzers}/__init__.py +0 -0
  70. {analyzers → mcp_proxy_adapter/analyzers}/docstring_analyzer.py +0 -0
  71. {analyzers → mcp_proxy_adapter/analyzers}/type_analyzer.py +0 -0
  72. {dispatchers → mcp_proxy_adapter/dispatchers}/__init__.py +0 -0
  73. {dispatchers → mcp_proxy_adapter/dispatchers}/base_dispatcher.py +0 -0
  74. {dispatchers → mcp_proxy_adapter/dispatchers}/json_rpc_dispatcher.py +0 -0
  75. {validators → mcp_proxy_adapter/validators}/docstring_validator.py +0 -0
  76. {validators → mcp_proxy_adapter/validators}/metadata_validator.py +0 -0
  77. {mcp_proxy_adapter-2.1.0.dist-info → mcp_proxy_adapter-2.1.2.dist-info}/WHEEL +0 -0
  78. {mcp_proxy_adapter-2.1.0.dist-info → mcp_proxy_adapter-2.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,60 @@
1
+ """
2
+ Extension Example for MCPProxyAdapter
3
+
4
+ - How to add custom commands
5
+ - How to extend help logic
6
+ - How to customize error handling
7
+
8
+ Run:
9
+ python examples/extension_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 mcp_proxy_adapter.adapter import MCPProxyAdapter
15
+
16
+ class MyRegistry:
17
+ def __init__(self):
18
+ self.dispatcher = self
19
+ self.commands = {"ping": self.ping, "help": self.help_command}
20
+ self.commands_info = {
21
+ "ping": {"description": "Ping command (returns pong)", "params": {}},
22
+ "help": {"description": "Show help for commands", "params": {"command": {"type": "string", "description": "Command name", "required": False}}}
23
+ }
24
+ def get_valid_commands(self):
25
+ return list(self.commands.keys())
26
+ def get_command_info(self, command):
27
+ return self.commands_info.get(command)
28
+ def get_commands_info(self):
29
+ return self.commands_info
30
+ def execute(self, *args, **params):
31
+ if args:
32
+ command = args[0]
33
+ params = {k: v for k, v in params.items()}
34
+ else:
35
+ command = params.pop("command", None)
36
+ if command == "ping":
37
+ return self.ping()
38
+ if command == "help":
39
+ return self.help_command(**params)
40
+ raise KeyError(f"Unknown command: {command}")
41
+ def add_generator(self, generator):
42
+ pass
43
+ def ping(self):
44
+ """Ping command."""
45
+ return {"result": "pong"}
46
+ def help_command(self, command: str = None):
47
+ """Custom help logic: returns info for command or all commands."""
48
+ if not command:
49
+ return {"commands": list(self.commands_info.keys())}
50
+ if command in self.commands_info:
51
+ return {"command": command, "info": self.commands_info[command]}
52
+ return {"error": f"Command '{command}' not found"}
53
+
54
+ if __name__ == "__main__":
55
+ registry = MyRegistry()
56
+ adapter = MCPProxyAdapter(registry)
57
+ print("[EXT] Ping:", registry.execute("ping"))
58
+ print("[EXT] Help (all):", registry.execute("help"))
59
+ print("[EXT] Help (ping):", registry.execute("help", command="ping"))
60
+ print("[EXT] Help (notfound):", registry.execute("help", command="notfound"))
@@ -0,0 +1,67 @@
1
+ """
2
+ Best Practices: Integrating and Testing 'help' Command in MCPProxyAdapter
3
+
4
+ This example demonstrates:
5
+ - How to robustly integrate a project-level 'help' command
6
+ - How to extend help with custom logic
7
+ - How to test help scenarios (including fallback and error cases)
8
+ - How to avoid common mistakes
9
+
10
+ Run:
11
+ python examples/help_best_practices.py
12
+ """
13
+ import os
14
+ import sys
15
+ sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
16
+ from typing import Any, Dict
17
+ from mcp_proxy_adapter.adapter import MCPProxyAdapter
18
+ from tests.test_mcp_proxy_adapter import MockRegistry
19
+
20
+ # --- Setup registry and adapter ---
21
+ registry = MockRegistry()
22
+ adapter = MCPProxyAdapter(registry)
23
+
24
+ def robust_help(command: str = None) -> Dict[str, Any]:
25
+ """
26
+ Best practice: always check for project help, handle errors, fallback to adapter help.
27
+ """
28
+ dispatcher = registry.dispatcher
29
+ if "help" in dispatcher.get_valid_commands():
30
+ try:
31
+ if command:
32
+ return dispatcher.help_command(command=command)
33
+ return dispatcher.help_command()
34
+ except Exception as e:
35
+ # Log error, fallback to adapter help
36
+ print(f"[WARN] Project help failed: {e}. Fallback to adapter help.")
37
+ return fallback_adapter_help(command)
38
+ else:
39
+ return fallback_adapter_help(command)
40
+
41
+ def fallback_adapter_help(command: str = None) -> Dict[str, Any]:
42
+ """
43
+ Fallback: call adapter's help (simulate REST/JSON-RPC call).
44
+ """
45
+ dispatcher = registry.dispatcher
46
+ if not command:
47
+ return {"source": "adapter", "commands": dispatcher.get_valid_commands()}
48
+ if command in dispatcher.get_valid_commands():
49
+ return {"source": "adapter", "command": command, "info": {"description": "Adapter help for command"}}
50
+ return {"source": "adapter", "error": f"Command '{command}' not found (adapter)", "available_commands": dispatcher.get_valid_commands()}
51
+
52
+ # --- Example test cases ---
53
+ def test_help():
54
+ """Test all help scenarios."""
55
+ print("[TEST] Project help (no param):", robust_help())
56
+ print("[TEST] Project help (existing command):", robust_help("success"))
57
+ print("[TEST] Project help (nonexistent command, triggers fallback):", robust_help("not_a_command"))
58
+ # Simulate registry without help
59
+ registry_no_help = MockRegistry()
60
+ if "help" in registry_no_help.dispatcher.commands:
61
+ del registry_no_help.dispatcher.commands["help"]
62
+ global registry
63
+ registry = registry_no_help
64
+ print("[TEST] Adapter help (no project help present):", robust_help("success"))
65
+
66
+ if __name__ == "__main__":
67
+ test_help()
examples/help_usage.py ADDED
@@ -0,0 +1,64 @@
1
+ """
2
+ Example: Using 'help' command with MCPProxyAdapter
3
+
4
+ This script demonstrates how to:
5
+ - Call the 'help' command (if present in the project)
6
+ - Call 'help' with a parameter (for a specific command)
7
+ - Handle errors and fallback to adapter help
8
+ - Best practices for integrating and extending help
9
+
10
+ Run:
11
+ python examples/help_usage.py
12
+ """
13
+ import os
14
+ import sys
15
+ sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
16
+ from typing import Any, Dict
17
+
18
+ # Assume MCPProxyAdapter and MockRegistry are available from src and tests
19
+ from mcp_proxy_adapter.adapter import MCPProxyAdapter
20
+ from tests.test_mcp_proxy_adapter import MockRegistry
21
+
22
+ # --- Setup registry and adapter ---
23
+ registry = MockRegistry()
24
+ adapter = MCPProxyAdapter(registry)
25
+
26
+ # --- Best practice: always check if 'help' is in commands ---
27
+ def call_help(command: str = None) -> Dict[str, Any]:
28
+ """Call help command with or without parameter."""
29
+ dispatcher = registry.dispatcher
30
+ if "help" in dispatcher.get_valid_commands():
31
+ if command:
32
+ try:
33
+ return dispatcher.help_command(command=command)
34
+ except Exception as e:
35
+ print(f"Project help failed: {e}. Fallback to adapter help.")
36
+ return adapter_help(command)
37
+ else:
38
+ return dispatcher.help_command()
39
+ else:
40
+ return adapter_help(command)
41
+
42
+ def adapter_help(command: str = None) -> Dict[str, Any]:
43
+ """Fallback: call adapter's help (simulate)."""
44
+ dispatcher = registry.dispatcher
45
+ if not command:
46
+ return {"source": "adapter", "commands": dispatcher.get_valid_commands()}
47
+ if command in dispatcher.get_valid_commands():
48
+ return {"source": "adapter", "command": command, "info": {"description": "Adapter help for command"}}
49
+ return {"source": "adapter", "error": f"Command '{command}' not found (adapter)", "available_commands": dispatcher.get_valid_commands()}
50
+
51
+ if __name__ == "__main__":
52
+ print("=== Project help (no param) ===")
53
+ print(call_help())
54
+ print("\n=== Project help (existing command) ===")
55
+ print(call_help("success"))
56
+ print("\n=== Project help (nonexistent command, triggers fallback) ===")
57
+ print(call_help("not_a_command"))
58
+ print("\n=== Adapter help (no project help present) ===")
59
+ # Simulate registry without help
60
+ registry_no_help = MockRegistry()
61
+ if "help" in registry_no_help.dispatcher.commands:
62
+ del registry_no_help.dispatcher.commands["help"]
63
+ adapter_no_help = MCPProxyAdapter(registry_no_help)
64
+ print(adapter_help("success"))
@@ -0,0 +1,131 @@
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()
@@ -0,0 +1,175 @@
1
+ {
2
+ "version": "1.0",
3
+ "tools": [
4
+ {
5
+ "name": "mcp_get_items",
6
+ "description": "Get list of all items",
7
+ "parameters": {
8
+ "type": "object",
9
+ "properties": {},
10
+ "required": []
11
+ }
12
+ },
13
+ {
14
+ "name": "mcp_get_item",
15
+ "description": "Get item by ID",
16
+ "parameters": {
17
+ "type": "object",
18
+ "properties": {
19
+ "item_id": {
20
+ "type": "integer",
21
+ "description": "Item ID to search for"
22
+ }
23
+ },
24
+ "required": [
25
+ "item_id"
26
+ ]
27
+ }
28
+ },
29
+ {
30
+ "name": "mcp_create_item",
31
+ "description": "Create new item",
32
+ "parameters": {
33
+ "type": "object",
34
+ "properties": {
35
+ "item": {
36
+ "type": "object",
37
+ "description": "Item data"
38
+ }
39
+ },
40
+ "required": [
41
+ "item"
42
+ ]
43
+ }
44
+ },
45
+ {
46
+ "name": "mcp_update_item",
47
+ "description": "Update item by ID",
48
+ "parameters": {
49
+ "type": "object",
50
+ "properties": {
51
+ "item_id": {
52
+ "type": "integer",
53
+ "description": "Item ID to update"
54
+ },
55
+ "updated_data": {
56
+ "type": "object",
57
+ "description": "Updated data"
58
+ }
59
+ },
60
+ "required": [
61
+ "item_id",
62
+ "updated_data"
63
+ ]
64
+ }
65
+ },
66
+ {
67
+ "name": "mcp_delete_item",
68
+ "description": "Delete item by ID",
69
+ "parameters": {
70
+ "type": "object",
71
+ "properties": {
72
+ "item_id": {
73
+ "type": "integer",
74
+ "description": "Item ID to delete"
75
+ }
76
+ },
77
+ "required": [
78
+ "item_id"
79
+ ]
80
+ }
81
+ },
82
+ {
83
+ "name": "mcp_search_items",
84
+ "description": "Search items by keyword",
85
+ "parameters": {
86
+ "type": "object",
87
+ "properties": {
88
+ "keyword": {
89
+ "type": "string",
90
+ "description": "Search keyword"
91
+ }
92
+ },
93
+ "required": [
94
+ "keyword"
95
+ ]
96
+ }
97
+ },
98
+ {
99
+ "name": "mcp_execute",
100
+ "description": "Universal command for executing queries",
101
+ "parameters": {
102
+ "type": "object",
103
+ "properties": {
104
+ "query": {
105
+ "type": "string",
106
+ "description": "Query to execute"
107
+ },
108
+ "subcommand": {
109
+ "type": "string",
110
+ "description": "Subcommand to execute"
111
+ }
112
+ },
113
+ "required": []
114
+ }
115
+ }
116
+ ],
117
+ "routes": [
118
+ {
119
+ "tool_name": "mcp_get_items",
120
+ "endpoint": "/cmd",
121
+ "method": "post",
122
+ "json_rpc": {
123
+ "method": "get_items"
124
+ }
125
+ },
126
+ {
127
+ "tool_name": "mcp_get_item",
128
+ "endpoint": "/cmd",
129
+ "method": "post",
130
+ "json_rpc": {
131
+ "method": "get_item"
132
+ }
133
+ },
134
+ {
135
+ "tool_name": "mcp_create_item",
136
+ "endpoint": "/cmd",
137
+ "method": "post",
138
+ "json_rpc": {
139
+ "method": "create_item"
140
+ }
141
+ },
142
+ {
143
+ "tool_name": "mcp_update_item",
144
+ "endpoint": "/cmd",
145
+ "method": "post",
146
+ "json_rpc": {
147
+ "method": "update_item"
148
+ }
149
+ },
150
+ {
151
+ "tool_name": "mcp_delete_item",
152
+ "endpoint": "/cmd",
153
+ "method": "post",
154
+ "json_rpc": {
155
+ "method": "delete_item"
156
+ }
157
+ },
158
+ {
159
+ "tool_name": "mcp_search_items",
160
+ "endpoint": "/cmd",
161
+ "method": "post",
162
+ "json_rpc": {
163
+ "method": "search_items"
164
+ }
165
+ },
166
+ {
167
+ "tool_name": "mcp_execute",
168
+ "endpoint": "/cmd",
169
+ "method": "post",
170
+ "json_rpc": {
171
+ "method": "execute"
172
+ }
173
+ }
174
+ ]
175
+ }