mcp-proxy-adapter 2.0.2__py3-none-any.whl → 2.1.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.
Files changed (67) 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 +1 -1
  30. mcp_proxy_adapter/models.py +19 -19
  31. {mcp_proxy_adapter-2.0.2.dist-info → mcp_proxy_adapter-2.1.1.dist-info}/METADATA +47 -13
  32. mcp_proxy_adapter-2.1.1.dist-info/RECORD +61 -0
  33. {mcp_proxy_adapter-2.0.2.dist-info → mcp_proxy_adapter-2.1.1.dist-info}/WHEEL +1 -1
  34. mcp_proxy_adapter-2.1.1.dist-info/top_level.txt +5 -0
  35. scripts/code_analyzer/code_analyzer.py +328 -0
  36. scripts/code_analyzer/register_commands.py +446 -0
  37. scripts/publish.py +85 -0
  38. tests/conftest.py +12 -0
  39. tests/test_adapter.py +529 -0
  40. tests/test_adapter_coverage.py +274 -0
  41. tests/test_basic_dispatcher.py +169 -0
  42. tests/test_command_registry.py +328 -0
  43. tests/test_examples.py +32 -0
  44. tests/test_mcp_proxy_adapter.py +568 -0
  45. tests/test_mcp_proxy_adapter_basic.py +262 -0
  46. tests/test_part1.py +348 -0
  47. tests/test_part2.py +524 -0
  48. tests/test_schema.py +358 -0
  49. tests/test_simple_adapter.py +251 -0
  50. mcp_proxy_adapter/adapters/__init__.py +0 -16
  51. mcp_proxy_adapter/cli/__init__.py +0 -12
  52. mcp_proxy_adapter/cli/__main__.py +0 -79
  53. mcp_proxy_adapter/cli/command_runner.py +0 -233
  54. mcp_proxy_adapter/generators/__init__.py +0 -14
  55. mcp_proxy_adapter/generators/endpoint_generator.py +0 -172
  56. mcp_proxy_adapter/generators/openapi_generator.py +0 -254
  57. mcp_proxy_adapter/generators/rest_api_generator.py +0 -207
  58. mcp_proxy_adapter/openapi_schema/__init__.py +0 -38
  59. mcp_proxy_adapter/openapi_schema/command_registry.py +0 -312
  60. mcp_proxy_adapter/openapi_schema/rest_schema.py +0 -510
  61. mcp_proxy_adapter/openapi_schema/rpc_generator.py +0 -307
  62. mcp_proxy_adapter/openapi_schema/rpc_schema.py +0 -416
  63. mcp_proxy_adapter/validators/__init__.py +0 -14
  64. mcp_proxy_adapter/validators/base_validator.py +0 -23
  65. mcp_proxy_adapter-2.0.2.dist-info/RECORD +0 -33
  66. mcp_proxy_adapter-2.0.2.dist-info/top_level.txt +0 -1
  67. {mcp_proxy_adapter-2.0.2.dist-info → mcp_proxy_adapter-2.1.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,369 @@
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 tests.test_mcp_proxy_adapter 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
+ }
95
+ self.commands_info = {
96
+ "get_items": {
97
+ "description": "Get list of all items",
98
+ "params": {}
99
+ },
100
+ "get_item": {
101
+ "description": "Get item by ID",
102
+ "params": {
103
+ "item_id": {
104
+ "type": "integer",
105
+ "description": "Item ID to search for",
106
+ "required": True
107
+ }
108
+ }
109
+ },
110
+ "create_item": {
111
+ "description": "Create new item",
112
+ "params": {
113
+ "item": {
114
+ "type": "object",
115
+ "description": "Item data",
116
+ "required": True
117
+ }
118
+ }
119
+ },
120
+ "update_item": {
121
+ "description": "Update item by ID",
122
+ "params": {
123
+ "item_id": {
124
+ "type": "integer",
125
+ "description": "Item ID to update",
126
+ "required": True
127
+ },
128
+ "updated_data": {
129
+ "type": "object",
130
+ "description": "Updated data",
131
+ "required": True
132
+ }
133
+ }
134
+ },
135
+ "delete_item": {
136
+ "description": "Delete item by ID",
137
+ "params": {
138
+ "item_id": {
139
+ "type": "integer",
140
+ "description": "Item ID to delete",
141
+ "required": True
142
+ }
143
+ }
144
+ },
145
+ "search_items": {
146
+ "description": "Search items by keyword",
147
+ "params": {
148
+ "keyword": {
149
+ "type": "string",
150
+ "description": "Search keyword",
151
+ "required": True
152
+ }
153
+ }
154
+ },
155
+ "execute": {
156
+ "description": "Universal command for executing queries",
157
+ "params": {
158
+ "query": {
159
+ "type": "string",
160
+ "description": "Query to execute",
161
+ "required": False
162
+ },
163
+ "subcommand": {
164
+ "type": "string",
165
+ "description": "Subcommand to execute",
166
+ "required": False
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ def execute(self, command, **params):
173
+ """Executes command with specified parameters."""
174
+ if command not in self.commands:
175
+ raise KeyError(f"Unknown command: {command}")
176
+ return self.commands[command](**params)
177
+
178
+ def execute_command(self, **params):
179
+ """Universal method for executing commands."""
180
+ query = params.get("query", "")
181
+ subcommand = params.get("subcommand", "")
182
+
183
+ # Debug logging
184
+ logger.info(f"Executing universal command with query={query}, subcommand={subcommand}, params={params}")
185
+
186
+ # Handle different command types
187
+ if query.lower() in ["list", "all", "items"]:
188
+ # Return list of all items
189
+ return self.get_items()
190
+ elif query.lower() == "search" and "keyword" in params:
191
+ # Search by keyword
192
+ return self.search_items(params["keyword"])
193
+ elif query.isdigit() or (isinstance(query, int) and query > 0):
194
+ # If query looks like an ID, return item by ID
195
+ try:
196
+ return self.get_item(int(query))
197
+ except ValueError:
198
+ return {"error": f"Item with ID {query} not found"}
199
+ else:
200
+ # By default return information about available commands
201
+ commands = list(self.commands.keys())
202
+ return {
203
+ "available_commands": commands,
204
+ "message": "Use one of the available commands or specify query for executing query",
205
+ "received_params": params
206
+ }
207
+
208
+ def get_valid_commands(self):
209
+ """Returns list of available commands."""
210
+ return list(self.commands.keys())
211
+
212
+ def get_command_info(self, command):
213
+ """Returns information about command."""
214
+ return self.commands_info.get(command)
215
+
216
+ def get_commands_info(self):
217
+ """Returns information about all commands."""
218
+ return self.commands_info
219
+
220
+ # Command implementations
221
+ def get_items(self):
222
+ """Get list of all items."""
223
+ return items_db
224
+
225
+ def get_item(self, item_id):
226
+ """Get item by ID."""
227
+ for item in items_db:
228
+ if item["id"] == item_id:
229
+ return item
230
+ raise ValueError(f"Item with ID {item_id} not found")
231
+
232
+ def create_item(self, item):
233
+ """Create new item."""
234
+ # Find maximum ID
235
+ max_id = max([i["id"] for i in items_db]) if items_db else 0
236
+ # Create new item with increased ID
237
+ new_item = {**item, "id": max_id + 1}
238
+ items_db.append(new_item)
239
+ return new_item
240
+
241
+ def update_item(self, item_id, updated_data):
242
+ """Update item by ID."""
243
+ for i, item in enumerate(items_db):
244
+ if item["id"] == item_id:
245
+ # Update item, keeping original ID
246
+ updated_item = {**item, **updated_data, "id": item_id}
247
+ items_db[i] = updated_item
248
+ return updated_item
249
+ raise ValueError(f"Item with ID {item_id} not found")
250
+
251
+ def delete_item(self, item_id):
252
+ """Delete item by ID."""
253
+ for i, item in enumerate(items_db):
254
+ if item["id"] == item_id:
255
+ del items_db[i]
256
+ return {"message": f"Item with ID {item_id} successfully deleted"}
257
+ raise ValueError(f"Item with ID {item_id} not found")
258
+
259
+ def search_items(self, keyword):
260
+ """Search items by keyword."""
261
+ keyword = keyword.lower()
262
+ return [
263
+ item for item in items_db
264
+ if keyword in item["name"].lower() or
265
+ (item["description"] and keyword in item["description"].lower())
266
+ ]
267
+
268
+ class CustomMockRegistry(MockRegistry):
269
+ """Custom command registry for example."""
270
+
271
+ def __init__(self):
272
+ """Initialization with custom dispatcher."""
273
+ self.dispatcher = MockDispatcher()
274
+ self.generators = []
275
+
276
+ def main():
277
+ """Main function to start server."""
278
+ # Create command registry
279
+ registry = CustomMockRegistry()
280
+
281
+ # Create FastAPI object
282
+ app = FastAPI(
283
+ title="OpenAPI Server Example",
284
+ description="Example OpenAPI server with MCP Proxy Adapter integration",
285
+ version="1.0.0"
286
+ )
287
+
288
+ # Configure CORS
289
+ from fastapi.middleware.cors import CORSMiddleware
290
+ app.add_middleware(
291
+ CORSMiddleware,
292
+ allow_origins=["*"], # Allow requests from all sources
293
+ allow_credentials=True,
294
+ allow_methods=["*"], # Allow all methods
295
+ allow_headers=["*"], # Allow all headers
296
+ )
297
+
298
+ # Create MCP Proxy adapter with explicit endpoint
299
+ adapter = MCPProxyAdapter(registry, cmd_endpoint="/cmd")
300
+
301
+ # Register adapter endpoints
302
+ adapter.register_endpoints(app)
303
+
304
+ # Save MCP Proxy configuration to file
305
+ config_path = os.path.join(os.path.dirname(__file__), "mcp_proxy_config.json")
306
+ adapter.save_config_to_file(config_path)
307
+ logger.info(f"MCP Proxy configuration saved to {config_path}")
308
+
309
+ # Define REST endpoints for example (not related to MCP Proxy)
310
+ @app.get("/")
311
+ def read_root():
312
+ """Root endpoint."""
313
+ return {
314
+ "message": "OpenAPI Server Example with MCP Proxy Adapter integration",
315
+ "endpoints": {
316
+ "items": "/items",
317
+ "item": "/items/{item_id}",
318
+ "search": "/items/search",
319
+ "mcp_proxy": "/cmd"
320
+ }
321
+ }
322
+
323
+ @app.get("/items", response_model=List[Item])
324
+ def read_items():
325
+ """Get all items."""
326
+ return items_db
327
+
328
+ @app.get("/items/{item_id}", response_model=Item)
329
+ def read_item(item_id: int = Path(..., description="Item ID", gt=0)):
330
+ """Get item by ID."""
331
+ try:
332
+ return registry.dispatcher.get_item(item_id)
333
+ except ValueError as e:
334
+ return {"error": str(e)}
335
+
336
+ @app.post("/items", response_model=Item)
337
+ def create_new_item(item: Item = Body(..., description="Data of new item")):
338
+ """Create new item."""
339
+ return registry.dispatcher.create_item(item.model_dump())
340
+
341
+ @app.put("/items/{item_id}", response_model=Item)
342
+ def update_existing_item(
343
+ item_id: int = Path(..., description="Item ID to update", gt=0),
344
+ item: Item = Body(..., description="Updated item data")
345
+ ):
346
+ """Update item by ID."""
347
+ try:
348
+ return registry.dispatcher.update_item(item_id, item.model_dump())
349
+ except ValueError as e:
350
+ return {"error": str(e)}
351
+
352
+ @app.delete("/items/{item_id}")
353
+ def delete_existing_item(item_id: int = Path(..., description="Item ID to delete", gt=0)):
354
+ """Delete item by ID."""
355
+ try:
356
+ return registry.dispatcher.delete_item(item_id)
357
+ except ValueError as e:
358
+ return {"error": str(e)}
359
+
360
+ @app.get("/items/search", response_model=List[Item])
361
+ def search_items_by_keyword(keyword: str = Query(..., description="Search keyword")):
362
+ """Search items by keyword."""
363
+ return registry.dispatcher.search_items(keyword)
364
+
365
+ # Start server
366
+ uvicorn.run(app, host="0.0.0.0", port=8000)
367
+
368
+ if __name__ == "__main__":
369
+ main()
@@ -0,0 +1,47 @@
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)
@@ -0,0 +1,53 @@
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
+ 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 = {"echo": self.echo}
20
+ self.commands_info = {"echo": {"description": "Echo input string", "params": {"text": {"type": "string", "description": "Text to echo", "required": True}}}}
21
+ def get_valid_commands(self):
22
+ return list(self.commands.keys())
23
+ def get_command_info(self, command):
24
+ return self.commands_info.get(command)
25
+ def get_commands_info(self):
26
+ return self.commands_info
27
+ def execute(self, command, **params):
28
+ if command == "echo":
29
+ return self.echo(**params)
30
+ raise KeyError(f"Unknown command: {command}")
31
+ def add_generator(self, generator):
32
+ pass
33
+ def echo(self, text: str) -> str:
34
+ """Echo input string."""
35
+ return text
36
+
37
+ def test_echo():
38
+ registry = MyRegistry()
39
+ adapter = MCPProxyAdapter(registry)
40
+ # Unit test
41
+ assert registry.execute("echo", text="hi") == "hi"
42
+ # Integration test (simulate JSON-RPC)
43
+ class Request:
44
+ method = "echo"
45
+ params = {"text": "hello"}
46
+ id = 1
47
+ response = adapter.router.routes[0].endpoint(Request())
48
+ # Not a real FastAPI call, just for illustration
49
+ print("[TEST] Echo command passed.")
50
+
51
+ if __name__ == "__main__":
52
+ test_echo()
53
+ print("All tests passed.")
@@ -7,7 +7,7 @@ for AI models.
7
7
  """
8
8
 
9
9
  # Package version
10
- __version__ = '2.0.0'
10
+ __version__ = '1.0.0'
11
11
 
12
12
  # Public API
13
13
  from .adapter import MCPProxyAdapter, configure_logger
@@ -6,42 +6,42 @@ from pydantic import BaseModel, Field
6
6
 
7
7
  class JsonRpcRequest(BaseModel):
8
8
  """Base model for JSON-RPC requests."""
9
- jsonrpc: str = Field(default="2.0", description="JSON-RPC version")
9
+ jsonrpc: str = Field("2.0", description="JSON-RPC version")
10
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")
11
+ params: Dict[str, Any] = Field({}, description="Method parameters")
12
+ id: Optional[Union[str, int]] = Field(None, description="Request identifier")
13
13
 
14
14
  class JsonRpcResponse(BaseModel):
15
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")
16
+ jsonrpc: str = Field("2.0", description="JSON-RPC version")
17
+ result: Optional[Any] = Field(None, description="Method execution result")
18
+ error: Optional[Dict[str, Any]] = Field(None, description="Error information")
19
+ id: Optional[Union[str, int]] = Field(None, description="Request identifier")
20
20
 
21
21
  class CommandInfo(BaseModel):
22
22
  """Command information model."""
23
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")
24
+ description: str = Field("", description="Command description")
25
+ summary: Optional[str] = Field(None, description="Brief description")
26
+ parameters: Dict[str, Dict[str, Any]] = Field({}, description="Command parameters")
27
+ returns: Optional[Dict[str, Any]] = Field(None, description="Return value information")
28
28
 
29
29
  class CommandParameter(BaseModel):
30
30
  """Command parameter model."""
31
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")
32
+ description: str = Field("", description="Parameter description")
33
+ required: bool = Field(False, description="Whether the parameter is required")
34
+ default: Optional[Any] = Field(None, description="Default value")
35
+ enum: Optional[List[Any]] = Field(None, description="Possible values for enumeration")
36
36
 
37
37
  class MCPProxyTool(BaseModel):
38
38
  """Tool model for MCPProxy."""
39
39
  name: str = Field(..., description="Tool name")
40
- description: str = Field(default="", description="Tool description")
40
+ description: str = Field("", description="Tool description")
41
41
  parameters: Dict[str, Any] = Field(..., description="Tool parameters schema")
42
42
 
43
43
  class MCPProxyConfig(BaseModel):
44
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")
45
+ version: str = Field("1.0", description="Configuration version")
46
+ tools: List[MCPProxyTool] = Field([], description="List of tools")
47
+ routes: List[Dict[str, Any]] = Field([], description="Routes configuration")
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-proxy-adapter
3
- Version: 2.0.2
3
+ Version: 2.1.1
4
4
  Summary: Adapter for exposing Command Registry commands as tools for AI models via MCP Proxy.
5
5
  Home-page: https://github.com/vasilyvz/mcp-proxy-adapter
6
6
  Author: Vasiliy VZ
7
7
  Author-email: Vasiliy VZ <vasilyvz@example.com>
8
8
  License: MIT
9
9
  Project-URL: Homepage, https://github.com/vasilyvz/mcp-proxy-adapter
10
- Project-URL: BugTracker, https://github.com/vasilyvz/mcp-proxy-adapter/issues
11
- Project-URL: Documentation, https://github.com/vasilyvz/mcp_proxy_adapter#readme
12
- Project-URL: Source, https://github.com/vasilyvz/mcp_proxy_adapter
10
+ Project-URL: Bug Tracker, https://github.com/vasilyvz/mcp-proxy-adapter/issues
11
+ Project-URL: Documentation, https://github.com/vasilyvz/mcp-proxy-adapter/tree/main/docs
13
12
  Classifier: Development Status :: 4 - Beta
14
13
  Classifier: Intended Audience :: Developers
15
14
  Classifier: License :: OSI Approved :: MIT License
@@ -22,7 +21,7 @@ Requires-Python: >=3.9, <4
22
21
  Description-Content-Type: text/markdown
23
22
  License-File: LICENSE
24
23
  Requires-Dist: fastapi<1.0.0,>=0.95.0
25
- Requires-Dist: pydantic<3.0.0,>=2.0.0
24
+ Requires-Dist: pydantic>=2.0.0
26
25
  Requires-Dist: uvicorn<1.0.0,>=0.22.0
27
26
  Requires-Dist: docstring-parser<1.0.0,>=0.15
28
27
  Requires-Dist: typing-extensions<5.0.0,>=4.5.0
@@ -262,11 +261,46 @@ MIT
262
261
  ## Documentation
263
262
  See [docs/](docs/) for detailed guides, architecture, and examples.
264
263
 
265
- ## CI/CD & PyPI automation
266
-
267
- This project uses GitHub Actions for continuous integration and automated publishing to PyPI.
268
-
269
- - All tests are run on every push and pull request.
270
- - On push of a new tag (vX.Y.Z), the package is built and published to PyPI automatically.
271
-
272
- See `.github/workflows/publish.yml` for details.
264
+ ## 📦 Примеры (examples/)
265
+
266
+ - `help_usage.py` базовое использование help-команды
267
+ - `help_best_practices.py` — best practices для help
268
+ - `project_structure_example.py` структура проекта с MCPProxyAdapter
269
+ - `docstring_and_schema_example.py` как документировать команды для схемы
270
+ - `testing_example.py` — как тестировать команды и интеграцию
271
+ - `extension_example.py` как расширять и кастомизировать команды и help
272
+
273
+ ## ✅ Чеклист для добавления новой команды
274
+
275
+ 1. **Реализовать функцию-команду** с подробным docstring (EN)
276
+ 2. **Добавить описание параметров** (type, description, required)
277
+ 3. **Добавить описание возврата** (docstring, тип)
278
+ 4. **Зарегистрировать команду** в registry/dispatcher
279
+ 5. **Добавить описание в get_commands_info** (использовать docstring)
280
+ 6. **Покрыть тестами** (unit/integration, edge-cases, ошибки)
281
+ 7. **Добавить пример использования** в examples/
282
+ 8. **Проверить интеграцию с help** (и с параметром, и без)
283
+ 9. **Проверить генерацию схемы/OpenAPI**
284
+ 10. **Документировать в README.md** (EN/RU)
285
+
286
+ ## 📦 Examples (EN)
287
+
288
+ - `help_usage.py` — basic help command usage
289
+ - `help_best_practices.py` — best practices for help
290
+ - `project_structure_example.py` — project structure with MCPProxyAdapter
291
+ - `docstring_and_schema_example.py` — how to document commands for schema
292
+ - `testing_example.py` — how to test commands and integration
293
+ - `extension_example.py` — how to extend/customize commands and help
294
+
295
+ ## ✅ Checklist for adding a new command
296
+
297
+ 1. **Implement the command function** with detailed docstring (EN)
298
+ 2. **Describe parameters** (type, description, required)
299
+ 3. **Describe return value** (docstring, type)
300
+ 4. **Register the command** in registry/dispatcher
301
+ 5. **Add description to get_commands_info** (use docstring)
302
+ 6. **Cover with tests** (unit/integration, edge-cases, errors)
303
+ 7. **Add usage example** to examples/
304
+ 8. **Check help integration** (with/without param)
305
+ 9. **Check schema/OpenAPI generation**
306
+ 10. **Document in README.md** (EN/RU)