A Model Context Protocol (MCP) server built with FastMCP that provides comprehensive tools to interact with Teradata QueryGrid Manager. This server enables AI assistants and other MCP clients to manage QueryGrid resources programmatically using a standardized protocol.
- Comprehensive QueryGrid Management: 126 tools covering all major QueryGrid operations
- Secure Authentication: Basic authentication support for QueryGrid Manager
- RESTful API Integration: Full CRUD operations for QueryGrid resources
- Streamable HTTP Transport: Efficient bidirectional communication using FastMCP
- Type-Safe: Fully type-annotated Python code with strict type checking
- Production-Ready: Runs on Uvicorn ASGI server for optimal performance
- Flexible Deployment: Background daemon or foreground modes with hot reloading
- Comprehensive Logging: Configurable logging levels and output destinations
- Well-Tested: Comprehensive unit test coverage
teradata-qg-mcp-server/
├── src/
│ ├── mcp_server.py # FastMCP application and server initialization
│ ├── server.py # Main server entry point with graceful shutdown
│ ├── utils.py # Shared utilities and helpers
│ ├── qgm/ # QueryGrid Manager API clients
│ │ ├── base.py # Base HTTP client
│ │ ├── querygrid_manager.py # Main manager class
│ │ ├── systems.py # Systems API client
│ │ ├── connectors.py # Connectors API client
│ │ ├── links.py # Links API client
│ │ ├── fabrics.py # Fabrics API client
│ │ └── ... # Other resource clients
│ └── tools/ # MCP tool implementations (21 modules, 126 tools)
│ ├── __init__.py # Tool registration
│ ├── system_tools.py # System management tools
│ ├── connector_tools.py # Connector management tools
│ ├── link_tools.py # Link management tools
│ └── ... # Other tool modules
├── tests/ # Unit tests
├── scripts/ # Server management scripts
├── docs/ # Documentation
│ ├── CONTRIBUTING.md # Contribution guidelines
│ └── TESTING.md # Testing guide
└── requirements.txt # Python dependencies
- Python: 3.13+ (uses modern type annotations)
- Platform: Linux, macOS, and Windows
- Dependencies: fastmcp, requests, pytest, python-dotenv, fastapi, uvicorn
Follow these steps to get the server running:
-
Clone the repository:
git clone https://github.com/Teradata/teradata-qg-mcp-server.git cd teradata-qg-mcp-server -
Create a virtual environment:
python -m venv venv
This creates a new virtual environment in the
venvdirectory. -
Activate the virtual environment:
On Linux/macOS:
source venv/bin/activateOn Windows:
venv\Scripts\activate
You should see
(venv)appear at the beginning of your terminal prompt, indicating the virtual environment is active. -
Install dependencies:
For production use:
pip install -r requirements.txt
For development (includes testing and linting tools):
pip install -r requirements-dev.txt
-
Configure environment variables:
Create a
.envfile in the project root (see.env.examplefor reference):cp .env.example .env # Edit .env with your QueryGrid Manager credentials nano .env # or use your preferred editor
Required variables:
QG_MANAGER_HOST=your-querygrid-manager.com QG_MANAGER_PORT=9443 QG_MANAGER_USERNAME=your-username QG_MANAGER_PASSWORD=your-password QG_MANAGER_VERIFY_SSL=false
-
Start the server:
./scripts/td-qg-mcp-server.py start
The server will start in background mode. Check status with:
./scripts/td-qg-mcp-server.py status
- Always activate the virtual environment before running the server or any scripts
- If you close your terminal, you'll need to reactivate:
source venv/bin/activate - To deactivate the virtual environment:
deactivate - The virtual environment is local to your project and not committed to git
"No module named uvicorn" error:
- Make sure the virtual environment is activated
- Reinstall dependencies:
pip install -r requirements.txt
"Permission denied" when running scripts:
- Make the script executable:
chmod +x scripts/td-qg-mcp-server.py
Import errors when running the server:
- Ensure you're in the project root directory
- Verify virtual environment is activated
- Check all dependencies installed:
pip list
The server uses a three-tier configuration hierarchy with clear separation of concerns:
Priority: CLI Arguments > Environment Variables > config.yaml
(Highest) (Lowest)
-
config.yaml (Application Defaults - Lowest Priority)
- Contains sensible defaults for all settings
- Includes server configuration, timeouts, and logging settings
- Safe, non-sensitive values committed to version control
- See
config.yamlfor all available options
-
Environment Variables (Per-Environment Overrides - Middle Priority)
- Override config.yaml defaults
- Used for environment-specific settings and sensitive data
- Can be set via
.envfile or system environment
-
CLI Arguments (Runtime Overrides - Highest Priority)
- Override both environment variables and config.yaml
- Used for temporary changes and debugging
- Passed to
td-qg-mcp-server.pyscript
Example: If config.yaml sets port: 8000, .env sets QG_MCP_SERVER_PORT=8080, and you run with --port 9000, the server will use port 9000 (CLI wins).
For complete configuration documentation, see docs/CONFIGURATION.md.
QG_MANAGER_HOST: QueryGrid Manager hostQG_MANAGER_PORT: QueryGrid Manager portQG_MANAGER_USERNAME: QueryGrid Manager usernameQG_MANAGER_PASSWORD: QueryGrid Manager passwordQG_MANAGER_VERIFY_SSL: Whether to verify SSL certificates (default: fromconfig.yaml)
QG_MCP_SERVER_HOST: Host to bind the MCP server to (default: fromconfig.yaml)QG_MCP_SERVER_PORT: Port to bind the MCP server to (default: fromconfig.yaml)
QG_MCP_SERVER_LOG_FILE: Path to the log file (optional, if not set logs go to console)QG_MCP_SERVER_LOG_LEVEL: Logging level -DEBUG,INFO,WARNING,ERROR,CRITICAL(default: fromconfig.yaml)
You can also use a .env file to set environment variables. Create a .env file in the project root (see .env.example for reference):
# .env
QG_MANAGER_HOST=your-querygrid-manager.com
QG_MANAGER_PORT=8080
QG_MANAGER_USERNAME=your-username
QG_MANAGER_PASSWORD=your-password
QG_MANAGER_VERIFY_SSL=false
QG_MCP_SERVER_HOST=0.0.0.0
QG_MCP_SERVER_PORT=8000
QG_MCP_SERVER_LOG_LEVEL=DEBUGThe server will automatically load the .env file if it exists (requires python-dotenv package).
The config.yaml file at the project root contains default settings for:
- Server: host, port, health check timeout
- QueryGrid: request timeout, SSL verification defaults
- Logging: rotation, retention, formats, and levels
These values serve as sensible defaults and can be overridden by environment variables or CLI arguments.
Example configuration structure:
server:
host: "0.0.0.0"
port: 8000
health_check_timeout: 5
querygrid:
request_timeout: 10
verify_ssl: true
logging:
max_file_size_mb: 100
retention_days: 30
backup_count: 10
log_level: INFOSee config.yaml for complete configuration and docs/CONFIGURATION.md for detailed documentation.
The server implements automatic log rotation and retention management.
The config.yaml file contains the following logging settings:
logging:
# Maximum size of a single log file in megabytes before rotation
max_file_size_mb: 100
# Number of days to retain log files
retention_days: 30
# Number of backup log files to keep
backup_count: 10
# Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
# Can be overridden by QG_MCP_SERVER_LOG_LEVEL environment variable
log_level: INFO
# Log format
log_format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# Date format for log timestamps
date_format: "%Y-%m-%d %H:%M:%S"- Daily Log Files: The server creates one log file per day using the pattern
qg_server_YYYYMMDD.log(e.g.,qg_server_20251211.log). Multiple server restarts on the same day append to the same file. - Automatic Rotation: When a log file reaches the configured size (
max_file_size_mb), it is automatically rotated - Retention Policy: Log files older than
retention_daysare automatically deleted on server startup - Backup Files: Up to
backup_countrotated log files are kept (e.g.,qg_server_20251211.log.1,qg_server_20251211.log.2, etc.) - Default Values: If
config.yamlis not found, the server uses built-in defaults (100 MB, 30 days, 10 backups)
To customize logging behavior, edit the config.yaml file in the project root:
# Edit config.yaml
nano config.yaml
# Restart the server for changes to take effect
./scripts/td-qg-mcp-server.py stop
./scripts/td-qg-mcp-server.py startNote: Environment variables (QG_MCP_SERVER_LOG_LEVEL) and command-line arguments (--log-level) override the log_level setting in config.yaml.
The server must be run from within the activated virtual environment to ensure all dependencies (including uvicorn) are available:
# Activate the virtual environment first
source venv/bin/activate # On Windows: venv\Scripts\activate
# Then run the server
./scripts/td-qg-mcp-server.py startThe server provides three operational modes:
Runs the server as a detached background process with all output redirected to log files:
# Start server in background
source venv/bin/activate
./scripts/td-qg-mcp-server.py start
# Check server status
./scripts/td-qg-mcp-server.py status
# Stop server
./scripts/td-qg-mcp-server.py stopBackground mode features:
- Server detaches from terminal immediately
- All logs written to
logs/qg_server_YYYYMMDD.log - No console output or Ctrl+C option
- PID file created at
run/server.pidfor process tracking - Graceful shutdown with process tree cleanup (handles reload mode child processes)
- Survives terminal closure
Runs the server in the current terminal for debugging:
source venv/bin/activate
./scripts/td-qg-mcp-server.py start --foreground --log-level DEBUGForeground mode features:
- Console output for immediate feedback
- Can be stopped with Ctrl+C
- PID file created at
run/server.pid - Signal handlers kill entire process tree on shutdown
Automatically restarts server when code changes are detected:
source venv/bin/activate
./scripts/td-qg-mcp-server.py start --foreground --reload --log-level DEBUGHot reload features:
- Watches
src/directory for changes - Automatic restart on file modifications
- Spawns child processes for reloading
- Process tree cleanup ensures no orphaned processes
Basic usage:
source venv/bin/activate
./scripts/td-qg-mcp-server.py startCustom host and port:
source venv/bin/activate
./scripts/td-qg-mcp-server.py start --host 127.0.0.1 --port 8080Custom QueryGrid Manager connection:
source venv/bin/activate
./scripts/td-qg-mcp-server.py start --qgm-host your-manager.com --qgm-port 8080 --qgm-username admin --qgm-password secret --qgm-verify-ssl falseCustom logging:
source venv/bin/activate
./scripts/td-qg-mcp-server.py start --log-dir ./my-logs --log-level DEBUGThe stop command handles complex process trees (including reload mode child processes):
source venv/bin/activate
./scripts/td-qg-mcp-server.py stopStop behavior:
- Reads PID from
run/server.pid - Verifies the process is the correct server instance
- Finds and terminates all child processes (using
pgrep -P) - Terminates parent process
- Cleans up PID file
- Handles stale processes gracefully
Fallback: If PID file is missing, searches for running src.server:app processes and terminates their process trees.
source venv/bin/activate
./scripts/td-qg-mcp-server.py statusStatus output includes:
- Process ID (PID)
- Health check results (application and QueryGrid connectivity)
- QueryGrid Manager version (if connected)
Signal Handling:
- Custom SIGINT (Ctrl+C) and SIGTERM handlers
- Graceful shutdown with 5-second timeout
- Process tree detection with
pgrep -P <pid> - Kills all child processes before parent
- Proper session cleanup (QueryGrid Manager connection closed)
- PID file cleanup on shutdown
Multi-Instance Safety:
- Only kills processes associated with the specific PID file
- Verifies process command contains
src.server:appbefore killing - Won't affect other servers running on the same machine
Cross-Platform Support:
- Unix/Linux/macOS: Uses
pgrepandkillcommands - Windows: Uses
taskkill /F /T /PIDfor process tree termination
Server Configuration:
--host HOST: Host to bind the server to (default:0.0.0.0, orQG_MCP_SERVER_HOSTenv var)--port PORT: Port to bind the server to (default:8000, orQG_MCP_SERVER_PORTenv var)--foreground: Run server in foreground mode for development/debugging--reload: Enable hot reloading (automatically restart on code changes) - requires--foreground
Logging:
--log-dir LOG_DIR: Directory to write server logs to (default:./logs)--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}: Logging level (default:INFO)
QueryGrid Manager Connection:
--qgm-host QGM_HOST: QueryGrid Manager host--qgm-port QGM_PORT: QueryGrid Manager port--qgm-username QGM_USERNAME: QueryGrid Manager username--qgm-password QGM_PASSWORD: QueryGrid Manager password--qgm-verify-ssl QGM_VERIFY_SSL: Verify SSL certificates (accepts:true,1,yesfor enabled;false,0,nofor disabled)
For complete help:
./scripts/td-qg-mcp-server.py -hTo connect an MCP client to this server using the streamable-http transport protocol, first start the MCP server, then add the following configuration to your MCP client's configuration file (typically claude_desktop_config.json or mcp.json):
{
"mcpServers": {
"teradata-querygrid": {
"url": "http://localhost:8000/mcp/"
}
}
}- Start the MCP server: Run
./scripts/td-qg-mcp-server.py startwith the required environment variables set (see Environment Variables section) - Configure your MCP client: Add the JSON configuration above to your client's config file
- Connect: The MCP client will connect to the running server via the specified URL
Note: The env section in the JSON configuration should contain the same environment variables described in the Environment Variables section above.
This configuration uses the streamable-http transport protocol for efficient bidirectional communication between the MCP client and server. The FastMCP framework automatically handles the HTTP streaming connection.
- Never commit sensitive credentials to version control. Use environment variables or secure credential management.
- Use HTTPS for production QueryGrid Manager connections when possible.
- Restrict file permissions on configuration files containing credentials.
The server provides 126 tools organized by QueryGrid resource type. All tools follow consistent patterns and return formatted responses with operation results and metadata.
- Get/Read Operations (55 tools): Retrieve information about QueryGrid resources
- Create Operations (12 tools): Create new QueryGrid entities with comprehensive validation
- Update/Patch Operations (10 tools): Modify existing resources
- Put Operations (14 tools): Full replacement of resource configurations
- Delete Operations (28 tools): Remove individual entities
- Run/Execute Operations (4 tools): Execute queries, operations, and diagnostic checks
- Bulk Operations (1 tool): Bulk delete nodes or issues
- System Management (1 tool): Apply or revert pending configuration changes
All qg_delete_* tools (except qg_bulk_delete) are designed to delete ONE entity at a time. Each tool accepts a single entity ID and removes that specific resource from QueryGrid Manager.
Examples:
qg_delete_system(id)- Deletes a single systemqg_delete_connector(id)- Deletes a single connectorqg_delete_bridge(id)- Deletes a single bridge
Do NOT use these tools to delete multiple entities - use the bulk delete tool instead.
The qg_bulk_delete(config_type, ids) tool is specifically designed for bulk deletion operations, but only supports two entity types:
NODE- Bulk delete multiple nodesISSUE- Bulk delete multiple issues
This tool CANNOT be used for other entity types like systems, connectors, bridges, etc. For those resources, use their individual delete tools.
All create tools include comprehensive validation and edge case documentation derived from integration tests. Each tool's description contains:
⚠️ CRITICAL GOTCHAS FOR LLMs: Common mistakes to avoid- Required field validation rules
- Dependency requirements
- Duplicate handling behavior
- Enum value constraints
Review each create tool's documentation carefully before use to avoid validation errors.
qg_get_api_info(): Get QueryGrid Manager API version and information
qg_get_managers(extra_info, filter_by_name): Get all QueryGrid managersqg_get_manager_by_id(id, extra_info): Get specific manager details
qg_get_datacenters(filter_by_name): Get all datacentersqg_get_datacenter_by_id(id): Get specific datacenterqg_create_datacenter(name, description, tags): Create new datacenterqg_update_datacenter(id, name, description, tags): Update datacenterqg_delete_datacenter(id): Delete a single datacenter
qg_get_systems(extra_info, filter_by_name): Get all systems with filteringqg_get_system_by_id(id, extra_info): Get specific system detailsqg_create_system(name, system_type, platform_type, ...): Create new systemqg_update_system(id, ...): Update existing systemqg_put_system(id, ...): Replace system configurationqg_delete_system(id): Delete a single system
qg_get_nodes(...): Get nodes with extensive filtering optionsqg_get_node_by_id(id, extra_info): Get specific node detailsqg_get_node_heartbeat_by_id(id): Get node heartbeat statusqg_delete_node(id): Delete a single node
qg_get_node_virtual_ips(): Get all node virtual IPsqg_get_node_virtual_ip_by_id(id): Get specific virtual IP
qg_get_connectors(extra_info, filter_by_name): Get all connectorsqg_get_connector_by_id(id, extra_info): Get specific connectorqg_get_connector_active(id): Get active connector configurationqg_get_connector_pending(id): Get pending configurationqg_get_connector_previous(id): Get previous configurationqg_get_connector_drivers(id): Get connector driver informationqg_create_connector(name, connector_type, ...): Create new connectorqg_update_connector(id, ...): Update existing connectorqg_put_connector(id, ...): Replace connector configurationqg_delete_connector(id): Delete a single connectorqg_delete_connector_active(id): Delete active connector configurationqg_delete_connector_pending(id): Delete pending connector configuration
qg_get_links(extra_info, filter_by_name): Get all linksqg_get_link_by_id(id, extra_info): Get specific linkqg_get_link_active(id): Get active link configurationqg_get_link_pending(id): Get pending configurationqg_get_link_previous(id): Get previous configurationqg_create_link(name, initiator_id, target_id, ...): Create new linkqg_update_link(id, ...): Update existing linkqg_put_link(id, ...): Replace link configurationqg_delete_link(id): Delete a single linkqg_delete_link_active(id): Delete active link configurationqg_delete_link_pending(id): Delete pending link configuration
qg_get_fabrics(extra_info, filter_by_name): Get all fabricsqg_get_fabric_by_id(id, extra_info): Get specific fabricqg_get_fabric_active(id): Get active fabric configurationqg_get_fabric_pending(id): Get pending configurationqg_get_fabric_previous(id): Get previous configurationqg_create_fabric(name, datacenter_id, ...): Create new fabricqg_update_fabric(id, ...): Update existing fabricqg_put_fabric(id, ...): Replace fabric configurationqg_delete_fabric(id): Delete a single fabricqg_delete_fabric_active(id): Delete active fabric configurationqg_delete_fabric_pending(id): Delete pending fabric configuration
qg_get_bridges(extra_info, filter_by_name): Get all bridgesqg_get_bridge_by_id(id, extra_info): Get specific bridgeqg_create_bridge(name, ...): Create new bridgeqg_update_bridge(id, ...): Update existing bridgeqg_delete_bridge(id): Delete a single bridge
qg_get_networks(extra_info, filter_by_name): Get all networksqg_get_network_by_id(id, extra_info): Get specific networkqg_get_network_active(id): Get active network configurationqg_get_network_pending(id): Get pending configurationqg_get_network_previous(id): Get previous configurationqg_create_network(name, ...): Create new networkqg_update_network(id, ...): Update existing networkqg_put_network(id, ...): Replace network configurationqg_delete_network(id): Delete a single networkqg_delete_network_active(id): Delete active network configurationqg_delete_network_pending(id): Delete pending network configuration
qg_get_users(): Get all usersqg_get_user_by_id(id): Get specific userqg_create_user(name, password, ...): Create new userqg_update_user(id, ...): Update existing userqg_delete_user(id): Delete a single user
qg_get_user_mappings(filter_by_name): Get all user mappingsqg_get_user_mapping_by_id(id): Get specific user mappingqg_create_user_mapping(name, ...): Create new user mappingqg_update_user_mapping(id, ...): Update existing user mappingqg_delete_user_mapping(id): Delete a single user mapping
qg_get_comm_policies(): Get all communication policiesqg_get_comm_policy_by_id(id, extra_info): Get specific policyqg_get_comm_policy_active(id): Get active policy configurationqg_get_comm_policy_pending(id): Get pending configurationqg_get_comm_policy_previous(id): Get previous configurationqg_create_comm_policy(name, ...): Create new communication policyqg_update_comm_policy(id, ...): Update existing policyqg_put_comm_policy(id, ...): Replace policy configurationqg_delete_comm_policy(id): Delete a single communication policyqg_delete_comm_policy_active(id): Delete active policy configurationqg_delete_comm_policy_pending(id): Delete pending policy configuration
qg_get_queries(...): Get queries with filtering by state, system, userqg_get_query_by_id(id): Get specific query detailsqg_get_query_summary(...): Get query summaries with filteringqg_cancel_query(id): Cancel running query
qg_get_operations(...): Get operations with filteringqg_get_operation_by_id(id): Get specific operationqg_run_operation(operation_id, operation_type, ...): Execute operationqg_apply_operation(id): Apply pending operationqg_revert_operation(id): Revert operationqg_bulk_delete(config_type, ids): Bulk delete nodes or issues (NOTE: Only supports NODE and ISSUE types)
qg_get_issues(): Get all issuesqg_get_issue_by_id(id): Get specific issue detailsqg_create_issue(...): Create new issueqg_delete_issue(id): Delete a single issue
qg_run_diagnostic_check(type, ...): Run diagnostic checkqg_get_diagnostic_check_status(id): Get diagnostic check statusqg_get_create_foreign_server_status(id): Get foreign server creation status
qg_get_software(filter_by_name): Get all software packagesqg_get_software_by_id(id): Get specific softwareqg_get_software_jdbc_driver(): Get all JDBC driversqg_get_software_jdbc_driver_by_name(jdbc_driver_name): Get specific JDBC driver
qg_get_create_foreign_server_script(...): Generate foreign server creation scriptqg_get_create_foreign_server_template(): Get foreign server templateqg_create_foreign_server(...): Create foreign server on target system
qg_get_shared_memory_estimator(...): Estimate shared memory requirements
The project includes comprehensive unit and integration tests. For detailed information on:
- Running tests (unit, integration, or all)
- Writing new tests
- Test configuration and fixtures
- Troubleshooting test issues
See the Testing Guide.
Integration tests will CREATE, MODIFY, and DELETE entities in your configured QueryGrid Manager instance. Before running tests, you will see a confirmation prompt showing:
- The QueryGrid Manager connection details
- Information about test entities that will be created
- Cleanup behavior
Quick start:
# Run all tests (requires confirmation)
pytest -v
# Skip confirmation prompt with --force flag
pytest -v --force
# Run only unit tests (no external dependencies, no confirmation needed)
pytest -v -m unit
# Run only integration tests (requires QueryGrid Manager and confirmation)
pytest -v -m integrationTest Entities:
Integration tests create various entities (datacenters, systems, connectors, bridges, fabrics, policies, user mappings, etc.) in QueryGrid Manager. Test entities are typically named with prefixes like test_ or pytest_ for easy identification.
Tests will attempt to clean up all created entities after execution. However, if tests are interrupted or fail during cleanup, some entities may remain in your QueryGrid Manager.
We welcome contributions! Please see our Contributing Guidelines for details on:
- Code style and standards
- Development workflow
- Testing requirements
- Pull request process
Important: Please create a GitHub issue before starting work on a pull request.
See LICENSE file for details.
For issues, questions, or contributions, please use the GitHub issue tracker.