mcp-proxy-adapter 2.1.9__tar.gz → 2.1.11__tar.gz

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 (50) hide show
  1. {mcp_proxy_adapter-2.1.9/mcp_proxy_adapter.egg-info → mcp_proxy_adapter-2.1.11}/PKG-INFO +1 -1
  2. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/adapter.py +3 -13
  3. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +12 -6
  4. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/help_best_practices.py +2 -2
  5. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/help_usage.py +12 -12
  6. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/openapi_server.py +86 -100
  7. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11/mcp_proxy_adapter.egg-info}/PKG-INFO +1 -1
  8. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/pyproject.toml +1 -1
  9. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/setup.py +1 -1
  10. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_adapter.py +6 -102
  11. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_examples.py +1 -1
  12. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/LICENSE +0 -0
  13. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/MANIFEST.in +0 -0
  14. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/README.md +0 -0
  15. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/code_index.yaml +0 -0
  16. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/__init__.py +0 -0
  17. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/analyzers/__init__.py +0 -0
  18. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -0
  19. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/analyzers/type_analyzer.py +0 -0
  20. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/dispatchers/__init__.py +0 -0
  21. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -0
  22. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/analyze_config.py +0 -0
  23. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/basic_integration.py +0 -0
  24. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -0
  25. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/extension_example.py +0 -0
  26. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -0
  27. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/project_structure_example.py +0 -0
  28. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/examples/testing_example.py +0 -0
  29. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/models.py +0 -0
  30. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/registry.py +0 -0
  31. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/schema.py +0 -0
  32. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/testing_utils.py +0 -0
  33. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/validators/docstring_validator.py +0 -0
  34. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter/validators/metadata_validator.py +0 -0
  35. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter.egg-info/SOURCES.txt +0 -0
  36. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter.egg-info/dependency_links.txt +0 -0
  37. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter.egg-info/requires.txt +0 -0
  38. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/mcp_proxy_adapter.egg-info/top_level.txt +0 -0
  39. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/requirements.txt +0 -0
  40. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/setup.cfg +0 -0
  41. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/conftest.py +0 -0
  42. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_adapter_coverage.py +0 -0
  43. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_basic_dispatcher.py +0 -0
  44. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_command_registry.py +0 -0
  45. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_mcp_proxy_adapter.py +0 -0
  46. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_mcp_proxy_adapter_basic.py +0 -0
  47. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_part1.py +0 -0
  48. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_part2.py +0 -0
  49. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_schema.py +0 -0
  50. {mcp_proxy_adapter-2.1.9 → mcp_proxy_adapter-2.1.11}/tests/test_simple_adapter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-proxy-adapter
3
- Version: 2.1.9
3
+ Version: 2.1.11
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
@@ -493,13 +493,6 @@ class MCPProxyAdapter:
493
493
  "details": "Request requires 'command', 'method' or 'params' field"
494
494
  }
495
495
  }
496
- # Подмена help -> __help
497
- if command == "help":
498
- command = "__help"
499
- # Переименовываем параметр внутри params
500
- if "command" in params:
501
- params["cmdname"] = params.pop("command")
502
- logger.info(f"[DEBUG] MCP CMD: command={command}, params={params}")
503
496
 
504
497
  # Check if command exists
505
498
  if command not in self.registry.dispatcher.get_valid_commands():
@@ -508,14 +501,12 @@ class MCPProxyAdapter:
508
501
  "error": {
509
502
  "code": 404,
510
503
  "message": f"Unknown command: {command}",
511
- "details": f"Unknown command: {command}. Available commands: {', '.join(self.registry.dispatcher.get_valid_commands())}"
504
+ "details": f"Command '{command}' not found in registry. Available commands: {', '.join(self.registry.dispatcher.get_valid_commands())}"
512
505
  }
513
506
  }
514
507
 
515
- logger.info(f"[DEBUG] MCP CMD: command={command}, params={params}")
516
508
  # Check for required parameters
517
509
  command_info = self.registry.dispatcher.get_command_info(command)
518
- logger.info(f"[DEBUG] MCP CMD: command_info={command_info}")
519
510
  if command_info and "params" in command_info:
520
511
  missing_params = []
521
512
  for param_name, param_info in command_info["params"].items():
@@ -531,7 +522,7 @@ class MCPProxyAdapter:
531
522
  "details": f"Command '{command}' requires following parameters: {', '.join(missing_params)}"
532
523
  }
533
524
  }
534
- logger.info(f"[DEBUG] MCP CMD: command={command}, params={params}")
525
+
535
526
  # Check parameter types
536
527
  type_errors = self._validate_param_types(command, params)
537
528
  if type_errors:
@@ -543,10 +534,9 @@ class MCPProxyAdapter:
543
534
  "details": "Check parameter types and try again"
544
535
  }
545
536
  }
546
- logger.info(f"[DEBUG] MCP CMD: command={command}, params={params}")
537
+
547
538
  # Execute the command
548
539
  try:
549
- logger.info(f"[DEBUG] MCP CMD: executing command={command}, params={params}")
550
540
  result = self.registry.dispatcher.execute(command, **params)
551
541
 
552
542
  # Return result in MCP Proxy format
@@ -157,26 +157,31 @@ class JsonRpcDispatcher(BaseDispatcher):
157
157
  def _help_command(self, params: Dict[str, Any] = None) -> Dict[str, Any]:
158
158
  """
159
159
  Built-in help command for getting command information.
160
+
160
161
  Args:
161
- params: Command parameters (dict)
162
+ params: Command parameters
162
163
  cmdname: Command name for detailed information
164
+
163
165
  Returns:
164
166
  Dict[str, Any]: Command help information
165
167
  """
166
- if params is None:
168
+ if not params:
167
169
  params = {}
168
- # If specific command is specified, return information only about it
169
- cmdname = params.get("cmdname")
170
- if cmdname:
170
+
171
+ # Only support 'cmdname' parameter
172
+ if "cmdname" in params and params["cmdname"]:
173
+ cmdname = params["cmdname"]
171
174
  if cmdname not in self._metadata:
172
175
  return {
173
176
  "error": f"Command '{cmdname}' not found",
174
177
  "available_commands": list(self._metadata.keys())
175
178
  }
179
+
176
180
  return {
177
181
  "cmdname": cmdname,
178
182
  "info": self._metadata[cmdname]
179
183
  }
184
+
180
185
  # Otherwise return brief information about all commands
181
186
  commands_info = {}
182
187
  for cmd, info in self._metadata.items():
@@ -185,8 +190,9 @@ class JsonRpcDispatcher(BaseDispatcher):
185
190
  "description": info["description"],
186
191
  "params_count": len(info["params"])
187
192
  }
193
+
188
194
  return {
189
195
  "commands": commands_info,
190
196
  "total": len(commands_info),
191
- "note": "Use the 'cmdname' parameter to get detailed information about a specific command"
197
+ "note": "To get info about a specific command, call help with parameter: POST /cmd {\"command\": \"help\", \"params\": {\"cmdname\": \"<command_name>\"}}. Only the 'cmdname' parameter is supported. Calling 'help <command>' (with space) is NOT supported."
192
198
  }
@@ -12,10 +12,10 @@ Run:
12
12
  """
13
13
  import os
14
14
  import sys
15
- sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
15
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
16
16
  from typing import Any, Dict
17
17
  from mcp_proxy_adapter.adapter import MCPProxyAdapter
18
- from tests.test_mcp_proxy_adapter import MockRegistry
18
+ from mcp_proxy_adapter.testing_utils import MockRegistry
19
19
 
20
20
  # --- Setup registry and adapter ---
21
21
  registry = MockRegistry()
@@ -12,41 +12,41 @@ Run:
12
12
  """
13
13
  import os
14
14
  import sys
15
- sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(__file__))))
15
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
16
16
  from typing import Any, Dict
17
17
 
18
18
  # Assume MCPProxyAdapter and MockRegistry are available from src and tests
19
19
  from mcp_proxy_adapter.adapter import MCPProxyAdapter
20
- from tests.test_mcp_proxy_adapter import MockRegistry
20
+ from mcp_proxy_adapter.testing_utils import MockRegistry
21
21
 
22
22
  # --- Setup registry and adapter ---
23
23
  registry = MockRegistry()
24
24
  adapter = MCPProxyAdapter(registry)
25
25
 
26
26
  # --- Best practice: always check if 'help' is in commands ---
27
- def call_help(command: str = None) -> Dict[str, Any]:
27
+ def call_help(cmdname: str = None) -> Dict[str, Any]:
28
28
  """Call help command with or without parameter."""
29
29
  dispatcher = registry.dispatcher
30
30
  if "help" in dispatcher.get_valid_commands():
31
- if command:
31
+ if cmdname:
32
32
  try:
33
- return dispatcher.help_command(command=command)
33
+ return dispatcher.help_command(cmdname=cmdname)
34
34
  except Exception as e:
35
35
  print(f"Project help failed: {e}. Fallback to adapter help.")
36
- return adapter_help(command)
36
+ return adapter_help(cmdname)
37
37
  else:
38
38
  return dispatcher.help_command()
39
39
  else:
40
- return adapter_help(command)
40
+ return adapter_help(cmdname)
41
41
 
42
- def adapter_help(command: str = None) -> Dict[str, Any]:
42
+ def adapter_help(cmdname: str = None) -> Dict[str, Any]:
43
43
  """Fallback: call adapter's help (simulate)."""
44
44
  dispatcher = registry.dispatcher
45
- if not command:
45
+ if not cmdname:
46
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()}
47
+ if cmdname in dispatcher.get_valid_commands():
48
+ return {"source": "adapter", "cmdname": cmdname, "info": {"description": "Adapter help for command"}}
49
+ return {"source": "adapter", "error": f"Command '{cmdname}' not found (adapter)", "available_commands": dispatcher.get_valid_commands()}
50
50
 
51
51
  if __name__ == "__main__":
52
52
  print("=== Project help (no param) ===")
@@ -171,7 +171,7 @@ class MockDispatcher:
171
171
  "help": {
172
172
  "description": "Show information about available commands or a specific command.",
173
173
  "params": {
174
- "command": {
174
+ "cmdname": {
175
175
  "type": "string",
176
176
  "description": "Command name for detailed info",
177
177
  "required": False
@@ -278,119 +278,105 @@ class MockDispatcher:
278
278
 
279
279
  def help_command(self, **params):
280
280
  """Return info about all commands or a specific command."""
281
- # Если в будущем появится пользовательская команда help, можно реализовать её здесь
282
- command = params.get("command")
283
- if command:
284
- info = self.commands_info.get(command)
281
+ cmdname = params.get("cmdname")
282
+ if cmdname:
283
+ info = self.commands_info.get(cmdname)
285
284
  if info:
286
- return {"command": command, "info": info}
285
+ return {"cmdname": cmdname, "info": info}
287
286
  else:
288
- return {"error": f"Command '{command}' not found", "available_commands": list(self.commands_info.keys())}
289
- # Если параметр command не указан, возвращаем краткую информацию обо всех
287
+ return {"error": f"Command '{cmdname}' not found", "available_commands": list(self.commands_info.keys())}
288
+ # Если параметр cmdname не указан, возвращаем краткую информацию обо всех
290
289
  return {
291
290
  "commands": {cmd: {"description": info["description"], "params": info["params"]} for cmd, info in self.commands_info.items()},
292
291
  "total": len(self.commands_info),
293
- "note": "Use the 'command' parameter to get detailed information about a specific command"
292
+ "note": "Use the 'cmdname' parameter to get detailed information about a specific command"
294
293
  }
295
294
 
295
+ # --- Создание registry и FastAPI-приложения на верхнем уровне ---
296
296
  class CustomMockRegistry(MockRegistry):
297
297
  """Custom command registry for example."""
298
-
299
298
  def __init__(self):
300
- """Initialization with custom dispatcher."""
301
299
  self.dispatcher = MockDispatcher()
302
300
  self.generators = []
303
301
 
304
- def main():
305
- """Main function to start server."""
306
- # Create command registry
307
- registry = CustomMockRegistry()
308
-
309
- # Create FastAPI object
310
- app = FastAPI(
311
- title="OpenAPI Server Example",
312
- description="Example OpenAPI server with MCP Proxy Adapter integration",
313
- version="1.0.0"
314
- )
315
-
316
- # Configure CORS
317
- from fastapi.middleware.cors import CORSMiddleware
318
- app.add_middleware(
319
- CORSMiddleware,
320
- allow_origins=["*"], # Allow requests from all sources
321
- allow_credentials=True,
322
- allow_methods=["*"], # Allow all methods
323
- allow_headers=["*"], # Allow all headers
324
- )
325
-
326
- # Create MCP Proxy adapter with explicit endpoint
327
- adapter = MCPProxyAdapter(registry, cmd_endpoint="/cmd")
328
-
329
- # Register adapter endpoints
330
- adapter.register_endpoints(app)
331
-
332
- # Save MCP Proxy configuration to file
333
- config_path = os.path.join(os.path.dirname(__file__), "mcp_proxy_config.json")
334
- adapter.save_config_to_file(config_path)
335
- logger.info(f"MCP Proxy configuration saved to {config_path}")
336
-
337
- # Define REST endpoints for example (not related to MCP Proxy)
338
- @app.get("/")
339
- def read_root():
340
- """Root endpoint."""
341
- return {
342
- "message": "OpenAPI Server Example with MCP Proxy Adapter integration",
343
- "endpoints": {
344
- "items": "/items",
345
- "item": "/items/{item_id}",
346
- "search": "/items/search",
347
- "mcp_proxy": "/cmd"
348
- }
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"
349
338
  }
350
-
351
- @app.get("/items", response_model=List[Item])
352
- def read_items():
353
- """Get all items."""
354
- return items_db
355
-
356
- @app.get("/items/{item_id}", response_model=Item)
357
- def read_item(item_id: int = Path(..., description="Item ID", gt=0)):
358
- """Get item by ID."""
359
- try:
360
- return registry.dispatcher.get_item(item_id)
361
- except ValueError as e:
362
- return {"error": str(e)}
363
-
364
- @app.post("/items", response_model=Item)
365
- def create_new_item(item: Item = Body(..., description="Data of new item")):
366
- """Create new item."""
367
- return registry.dispatcher.create_item(item.model_dump())
368
-
369
- @app.put("/items/{item_id}", response_model=Item)
370
- def update_existing_item(
371
- item_id: int = Path(..., description="Item ID to update", gt=0),
372
- item: Item = Body(..., description="Updated item data")
373
- ):
374
- """Update item by ID."""
375
- try:
376
- return registry.dispatcher.update_item(item_id, item.model_dump())
377
- except ValueError as e:
378
- return {"error": str(e)}
379
-
380
- @app.delete("/items/{item_id}")
381
- def delete_existing_item(item_id: int = Path(..., description="Item ID to delete", gt=0)):
382
- """Delete item by ID."""
383
- try:
384
- return registry.dispatcher.delete_item(item_id)
385
- except ValueError as e:
386
- return {"error": str(e)}
387
-
388
- @app.get("/items/search", response_model=List[Item])
389
- def search_items_by_keyword(keyword: str = Query(..., description="Search keyword")):
390
- """Search items by keyword."""
391
- return registry.dispatcher.search_items(keyword)
392
-
393
- # Start server
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
394
380
  uvicorn.run(app, host="0.0.0.0", port=8000)
395
381
 
396
382
  if __name__ == "__main__":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-proxy-adapter
3
- Version: 2.1.9
3
+ Version: 2.1.11
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mcp-proxy-adapter"
7
- version = "2.1.9"
7
+ version = "2.1.11"
8
8
  description = "Adapter for exposing Command Registry commands as tools for AI models via MCP Proxy."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -14,7 +14,7 @@ with open(os.path.join(here, 'requirements.txt'), encoding='utf-8') as f:
14
14
 
15
15
  setup(
16
16
  name="mcp-proxy-adapter",
17
- version="2.1.9",
17
+ version="2.1.11",
18
18
  description="Adapter for exposing Command Registry commands as tools for AI models via MCP Proxy.",
19
19
  long_description=long_description,
20
20
  long_description_content_type="text/markdown",
@@ -29,8 +29,6 @@ except ImportError:
29
29
  from src.adapter import MCPProxyAdapter, configure_logger
30
30
  from src.registry import CommandRegistry
31
31
 
32
- print('LOADED test_adapter.py')
33
-
34
32
  # Test command functions
35
33
  def success_command(value: int = 1) -> dict:
36
34
  """Test command that completes successfully."""
@@ -65,7 +63,7 @@ class BaseDispatcher:
65
63
  raise NotImplementedError("Method must be overridden in subclass")
66
64
 
67
65
  # Mock for command dispatcher
68
- class MockDispatcherXXX(BaseDispatcher):
66
+ class MockDispatcher(BaseDispatcher):
69
67
  """Mock for command dispatcher in tests."""
70
68
 
71
69
  def __init__(self):
@@ -73,8 +71,7 @@ class MockDispatcherXXX(BaseDispatcher):
73
71
  "success": success_command,
74
72
  "error": error_command,
75
73
  "param": param_command,
76
- "execute": self.execute_from_params,
77
- "__help": self.help_command
74
+ "execute": self.execute_from_params
78
75
  }
79
76
  self.commands_info = {
80
77
  "success": {
@@ -117,16 +114,6 @@ class MockDispatcherXXX(BaseDispatcher):
117
114
  "required": False
118
115
  }
119
116
  }
120
- },
121
- "__help": {
122
- "description": "Show help for commands",
123
- "params": {
124
- "cmdname": {
125
- "type": "string",
126
- "description": "Command name for detailed info",
127
- "required": False
128
- }
129
- }
130
117
  }
131
118
  }
132
119
 
@@ -134,8 +121,6 @@ class MockDispatcherXXX(BaseDispatcher):
134
121
  """Executes command based on parameters."""
135
122
  if "query" in params and params["query"] in self.commands:
136
123
  command = params.pop("query")
137
- if command == "help":
138
- command = "__help"
139
124
  return self.execute(command, **params)
140
125
  return {
141
126
  "available_commands": self.get_valid_commands(),
@@ -143,48 +128,25 @@ class MockDispatcherXXX(BaseDispatcher):
143
128
  }
144
129
 
145
130
  def execute(self, command, **params):
146
- print(f"[DEBUG] execute called: command={command}, params={params}")
147
- if command == "help":
148
- command = "__help"
149
131
  if command not in self.commands:
150
132
  raise KeyError(f"Unknown command: {command}")
151
- if command == "__help":
152
- return self.commands[command](params=params)
153
133
  return self.commands[command](**params)
154
134
 
155
135
  def get_valid_commands(self):
156
136
  return list(self.commands.keys())
157
137
 
158
138
  def get_command_info(self, command):
159
- if command == "help":
160
- command = "__help"
161
139
  return self.commands_info.get(command)
162
140
 
163
141
  def get_commands_info(self):
164
142
  return self.commands_info
165
143
 
166
- def help_command(self, params=None):
167
- import traceback; print(f"[DEBUG] help_command called with params: {params}"); print(f"[DEBUG] commands_info: {self.commands_info}"); traceback.print_stack()
168
- if params is None:
169
- params = {}
170
- cmdname = params.get("cmdname")
171
- if cmdname:
172
- if cmdname in self.commands_info:
173
- return {"cmdname": cmdname, "info": self.commands_info[cmdname]}
174
- else:
175
- return {"error": f"Command '{cmdname}' not found", "available_commands": list(self.commands_info.keys())}
176
- return {
177
- "commands": {cmd: {"description": info["description"], "params": info["params"]} for cmd, info in self.commands_info.items()},
178
- "total": len(self.commands_info),
179
- "note": "Use the 'cmdname' parameter to get detailed information about a specific command"
180
- }
181
-
182
144
  # Mock for CommandRegistry
183
145
  class MockRegistry:
184
146
  """Mock for CommandRegistry in tests."""
185
147
 
186
148
  def __init__(self):
187
- self.dispatcher = MockDispatcherXXX()
149
+ self.dispatcher = MockDispatcher()
188
150
  self.generators = []
189
151
 
190
152
  def get_commands_info(self):
@@ -457,9 +419,9 @@ def test_save_config_to_file(adapter, tmp_path):
457
419
 
458
420
  assert "version" in config
459
421
  assert "tools" in config
460
- assert len(config["tools"]) == 5 # Теперь 5 команд (включая help)
422
+ assert len(config["tools"]) == 4 # Corrected: now we have 4 commands
461
423
  assert "routes" in config
462
- assert len(config["routes"]) == 5 # Теперь 5 маршрутов
424
+ assert len(config["routes"]) == 4 # Corrected: now we have 4 routes, corresponding to commands
463
425
 
464
426
  def test_parameter_type_validation(test_app):
465
427
  """Test parameter type validation."""
@@ -564,62 +526,4 @@ def test_params_only_format(test_app):
564
526
  data = response.json()
565
527
  assert "result" in data
566
528
  assert "available_commands" in data["result"]
567
- assert "received_params" in data["result"]
568
-
569
- def test_jsonrpc_proxy_and_help_conflict(test_app):
570
- """Test JSON-RPC proxying and help/command param conflict."""
571
- # Обычный JSON-RPC вызов существующей команды
572
- response = test_app.post("/cmd", json={
573
- "jsonrpc": "2.0",
574
- "method": "success",
575
- "params": {"value": 3},
576
- "id": 42
577
- })
578
- assert response.status_code == 200
579
- data = response.json()
580
- assert "result" in data
581
- assert data["result"] == {"result": 6}
582
-
583
- # Вызов help по конкретной команде (не должно быть multiple values for argument 'command')
584
- response = test_app.post("/cmd", json={
585
- "jsonrpc": "2.0",
586
- "method": "help",
587
- "params": {"cmdname": "success"},
588
- "id": 43
589
- })
590
- assert response.status_code == 200
591
- data = response.json()
592
- assert "result" in data
593
- assert data["result"]["cmdname"] == "success"
594
- assert "info" in data["result"]
595
- assert "description" in data["result"]["info"]
596
-
597
- # Вызов help по несуществующей команде
598
- response = test_app.post("/cmd", json={
599
- "jsonrpc": "2.0",
600
- "method": "help",
601
- "params": {"cmdname": "not_a_command"},
602
- "id": 44
603
- })
604
- assert response.status_code == 200
605
- data = response.json()
606
- assert "result" in data
607
- # Ошибка может быть либо в data['result']['error'], либо в data['error']['message']
608
- found = False
609
- if "error" in data["result"] and "not found" in data["result"]["error"]:
610
- found = True
611
- if "error" in data and "message" in data["error"] and "not found" in data["error"]["message"]:
612
- found = True
613
- assert found, f"No 'not found' in error: {data}"
614
-
615
- # Вызов несуществующей команды
616
- response = test_app.post("/cmd", json={
617
- "jsonrpc": "2.0",
618
- "method": "not_a_command",
619
- "params": {},
620
- "id": 45
621
- })
622
- assert response.status_code == 200
623
- data = response.json()
624
- assert "error" in data
625
- assert "Unknown command" in data["error"]["message"]
529
+ assert "received_params" in data["result"]
@@ -3,7 +3,7 @@ import sys
3
3
  import os
4
4
  import pytest
5
5
 
6
- EXAMPLES_DIR = os.path.join(os.path.dirname(__file__), "..", "examples")
6
+ EXAMPLES_DIR = os.path.join(os.path.dirname(__file__), "..", "mcp_proxy_adapter", "examples")
7
7
 
8
8
  @pytest.mark.parametrize("script,expected", [
9
9
  ("help_usage.py", ["Project help", "Adapter help"]),