Conversation
|
Hi @Yasserelhaddar could you please provide feedback on changes to hardware component, (there is one failing test on mockplc to be fixed, as I have added ip_address to be returned in details of camera), would like to get any feedback for camera related changes. |
| from mindtrace.agents import __version__ | ||
| typer.echo(f"mindtrace version {__version__}") |
There was a problem hiding this comment.
__version__ doesn't seem to exist
| dependencies = [ | ||
| "mindtrace-core>=0.7.1", | ||
| "mindtrace-registry>=0.7.1", | ||
| "mindtrace-database>=0.7.1", | ||
| "mindtrace-services>=0.7.1", | ||
| "mindtrace-datalake>=0.7.1", | ||
| "mindtrace-models>=0.7.1", | ||
| "mindtrace-cluster>=0.7.1", | ||
| "mindtrace-jobs>=0.7.1", | ||
| "mindtrace-hardware>=0.7.1", | ||
| "fastmcp>=2.13.0", | ||
| "typer>=0.9.0", | ||
| "uvicorn>=0.25.0", | ||
| "pydantic>=2.0.0", | ||
| "rich>=13.0.0", | ||
| "httpx>=0.25.0" | ||
| ] |
There was a problem hiding this comment.
are all these dependencies required currently?
also, some like fastmcp, uvicorn, pydantic, httpx will anyway be included when importing, say, mindtrace-services
| # List all toolkits | ||
| mindtrace tools list | ||
|
|
||
| # List tools in a specific toolkit | ||
| mindtrace tools list basler_camera_tools | ||
|
|
||
| # Verbose output with descriptions | ||
| mindtrace tools list basler_camera_tools --verbose | ||
| ``` | ||
|
|
||
| #### Get Information | ||
|
|
||
| ```bash | ||
| # Get toolkit information | ||
| mindtrace tools info basler_camera_tools | ||
|
|
||
| # Get specific tool information | ||
| mindtrace tools info basler_camera_tools --tool discover_camera_parameters | ||
| ``` |
There was a problem hiding this comment.
list and info seem very similar:
$ mindtrace tools list basler_camera_tools
Toolkit: basler_camera_tools
Description: Tools for controlling and configuring Basler cameras
Version: 0.7.1
Tags: camera, hardware, basler
Tools (4):
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃ Tool Name ┃ Type ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ discover_available_cameras │ async │
│ discover_camera_parameters │ async │
│ set_camera_parameter │ async │
│ get_camera_status │ async │
└────────────────────────────┴───────┘
$ mindtrace tools info basler_camera_tools
Toolkit: basler_camera_tools
Version: 0.7.1
Tags: camera, hardware, basler
Module: mindtrace.agents.tools.basler_camera_tools
Description:
Tools for controlling and configuring Basler cameras
Tools (4):
- discover_available_cameras (async)
- discover_camera_parameters (async)
- set_camera_parameter (async)
- get_camera_status (async)
|
|
||
| ```bash | ||
| # Serve all tools from a toolkit (name is required for registry tracking) | ||
| mindtrace tools serve basler_camera_tools --name camera-server |
There was a problem hiding this comment.
serving the tools outputs an error:
$ mindtrace tools serve basler_camera_tools --name camera-server
Mindtrace Tool Service
Launching with toolkits: basler_camera_tools
Initializing LocalBackend with uri: /home/vik/.cache/mindtrace/registry
Registering default materializers...
Default materializers registered successfully.
Warmed materializer cache with 33 entries
[2026-02-09 17:38:29,909] ERROR: mindtrace.registry.core.registry.Registry: Object toolservers:camera-server version 1 does not exist.
Object toolservers:camera-server version 1 does not exist.
Launching detached process...
Verifying server is up at 0.0.0.0:8000...
⚠ Server process is running but not responding on port 8000
The server may still be starting. Check logs or try:
mindtrace tools list-servers
the service did seem to start at 8000.
| # Override logger with unique name per server instance | ||
| # This ensures each server instance logs to its own file | ||
| from mindtrace.core.logging.logger import get_logger | ||
|
|
||
| # Create unique logger name based on server_id or server_name | ||
| if server_name: | ||
| unique_logger_name = f"mindtrace.agents.server.tool_service.ToolService.{server_name}" | ||
| else: | ||
| # Use server_id to make it unique | ||
| unique_logger_name = f"mindtrace.agents.server.tool_service.ToolService.{self.id}" | ||
|
|
||
| # Set up unique logger for this instance | ||
| self.logger = get_logger( | ||
| unique_logger_name, | ||
| use_structlog=use_structlog, | ||
| structlog_bind={"tool_service_name": server_name if server_name else self.id}, | ||
| ) |
There was a problem hiding this comment.
Mindtrace.__init__ already accepts logger_kwargs but all instance-level logs go to the same file because self.unique_name happens to just stop at the class name.
Would it help to introduce a param logger_name in Mindtrace.__init__ so that deriving objects can just pass an additional param to super().__init__.
It can then be useful for any class where instance-level log files need to be spearated.
|
|
||
|
|
||
| @tools_app.command("serve") | ||
| def serve_tools( |
There was a problem hiding this comment.
this function can definitely use some helper functions. it currently does registry ops, liveness checks, http shutdown requests, subprocess launching, output formatting. this will be difficult to test.
speaking of which, let's add tests please. this is a new package being added and the process of getting tests up can help shape the library bits into better units.
| cmd = [ | ||
| sys.executable, | ||
| "-m", | ||
| "mindtrace.agents.server.tool_service_launcher", | ||
| "--host", host, | ||
| "--port", str(port), | ||
| "--workers", str(workers), | ||
| "--toolkits", ",".join(toolkits), | ||
| ] | ||
|
|
||
| if tag_set: | ||
| cmd.extend(["--tags", ",".join(tag_set)]) | ||
|
|
||
| if name: | ||
| cmd.extend(["--server-name", name]) | ||
|
|
||
| # Launch launcher module in a detached subprocess (following hardware pattern) | ||
| # Redirect stdout/stderr to /dev/null since Service handles its own logging | ||
| devnull = os.open(os.devnull, os.O_RDWR) | ||
| try: | ||
| process = subprocess.Popen( | ||
| cmd, | ||
| stdout=devnull, | ||
| stderr=devnull, | ||
| start_new_session=True, # Detach from terminal session (like hardware) | ||
| cwd=os.getcwd(), | ||
| ) | ||
| finally: | ||
| os.close(devnull) |
There was a problem hiding this comment.
there is an existing mindtrace.services.core.Launcher. Can it be used (or modified appropriately if not)?
| parser.add_argument("--host", default=os.getenv("TOOL_SERVICE_HOST", "0.0.0.0"), help="Service host") | ||
| parser.add_argument("--port", type=int, default=int(os.getenv("TOOL_SERVICE_PORT", "8000")), help="Service port") |
There was a problem hiding this comment.
os.getenvs with a TOOL_SERVICE prefix. Can mindtrace.core.Config be used here?
all packages try to use the MINDTRACE_* pattern for config items, helps to group into a single namespace.
Tools Subsystem: Toolkit Management and MCP Serving
Summary
This PR introduces a complete, production-grade tools subsystem for the Mindtrace platform that enables modular tool organization, discovery, and serving over remote MCP (Model Context Protocol) using FastMCP. The system supports both built-in and external toolkits with full lifecycle management, registry tracking, and automatic logging.
Architecture
Core Components
ToolkitLoader (
toolkit.py)MCPServer (
server/mcp_server.py)ToolService (
server/tool_service.py)track_operationCLI Interface (
cli/tools.py)Features
Toolkit Management
mindtrace/agents/tools/with metadata--packageflag--installflagCLI Commands
mindtrace tools list- List available toolkits and toolsmindtrace tools pull- Verify and inspect toolkits (with optional package installation)mindtrace tools serve- Launch production servers with registry trackingmindtrace tools stop- Stop servers by name, port, host/port, or URLmindtrace tools list-servers- List all registered servers with statusmindtrace tools info- Get detailed information about toolkits and toolsServer Management
mindtrace.registrywith unique namespsutilProduction Features
Usage Examples
Python API
CLI Usage
External Toolkit Integration
External packages can register toolkits via entry points:
Testing
The system includes:
basler_camera_tools) demonstrating the patternDocumentation
Comprehensive README includes: