DCI MCP Server
An MCP server for AI assistants to query, analyze, and report on DCI data, with documented integrations into related engineering systems.
Query DCI components, jobs, teams, and remotecis with DCI query language, pagination, field selection, sorting, and Elasticsearch aggregations.
Get today's date/current time and download DCI job files.
Generate DCI-focused RCA, weekly/biweekly/quarterly, and support-case reports via built-in prompts.
Integrate with Jira, GitHub, GitLab, Red Hat Support Cases, and Google Drive for richer investigation and documentation.
Search issues and pull requests using GitHub's query syntax and retrieve detailed information including comments, labels, assignees, and pull request metadata.
Convert DCI reports and markdown content to Google Docs with rich formatting, including table, code block, header, list, and link support.
Retrieve comprehensive ticket data including comments and changelog, search using JQL, get project information, and extract Jira tickets from DCI job comments.
Access Red Hat support case data from the Customer Portal, including case details, comments, linked Bugzilla bugs, and errata/advisory information.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@DCI MCP Server show me the latest DCI jobs for topic RHEL-9"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
DCI MCP Server
This project provides a Model Context Protocol (MCP) server adapted for the DCI API. It allows AI models to interact with DCI for comprehensive data extraction about DCI jobs, components, topics and files.
Features
š FastAPI: Built on a modern, fast web framework
š¤ MCP: Implements the Model Context Protocol for AI integration
š Comprehensive DCI API: Full access to DCI components, jobs, files, pipelines, products, teams, and topics
š§ Smart PR Detection: Advanced PR build finder that analyzes job URLs and metadata
š DCI Integration: Native DCI API support with authentication
š Easy Configuration: Support for .env files for simple setup
ā Code Quality: Comprehensive pre-commit checks and linting
š Google Drive Integration: Convert DCI reports to Google Docs with rich formatting
š« Jira Integration: Collect comprehensive ticket data from Jira with comments and changelog
š GitHub Integration: Search issues and pull requests using GitHub's powerful search API
š“ Red Hat Support Case Integration: Access Red Hat support case data from the Customer Portal
Related MCP server: ask-panda
Installation
# Clone the repository
git clone https://github.com/redhat-community-ai-tools/dci-mcp-server
cd dci-mcp-server
# Install dependencies
uv sync
# Activate virtual environment
source .venv/bin/activateConfiguration
The server supports multiple ways to configure DCI authentication:
Copy the example file and customize it:
cp env.example .env
# Edit .env with your DCI credentialsExample .env file:
# Method 1: API Key Authentication
DCI_CLIENT_ID=<client_type>/<client_id>
DCI_API_SECRET=<api_secret>
# Method 2: User ID/Password (alternative to API key)
# DCI_LOGIN=foo
# DCI_PASSWORD=bar
# Google Drive Integration (optional)
# GOOGLE_CREDENTIALS_PATH=credentials.json
# GOOGLE_TOKEN_PATH=token.jsonMCP Configuration
Cursor IDE (stdio transport)
Add to your ~/.cursor/mcp.json:
{
"mcpServers": {
"dci": {
"command": "uv",
"args": ["run", "/path/to/dci-mcp-server/.venv/bin/python", "/path/to/dci-mcp-server/main.py"],
"description": "MCP server for DCI integration"
}
}
}Claude Desktop (~/.claude.json)
{
"mcpServers": {
"dci": {
"command": "uv",
"args": ["run", "--directory", "/path/to/dci-mcp-server", "main.py"],
"env": {
"DCI_CLIENT_ID": "your-client-id",
"DCI_API_SECRET": "your-secret",
"DCI_CS_URL": "https://api.distributed-ci.io"
}
}
}
}Claude CLI (~/.claude.json)
close the claude cli
include the block above in the ~/.claude.json file
start a claude session from the /path/to/dci-mcp-server/ directory
verify MCP is connected with /mcp
Web-based Integration (SSE transport)
For web applications or services that need HTTP-based communication:
{
"mcpServers": {
"dci": {
"url": "http://0.0.0.0:8000/sse/",
"description": "MCP server for DCI integration with direct SSE",
"env": {
"MCP_TRANSPORT": "sse"
}
}
}
}SSE Endpoint: http://0.0.0.0:8000/sse/
Note: Make sure to start the SSE server separately with
MCP_TRANSPORT=sse uv run main.pybefore using this configuration.
Prompts
You can then use prompts to explore the DCI data.
There are also parameterized prompts defined in the MCP server:
/dci/rca <job id>conducts a Root Cause Analysis of a failing DCI job. The prompt is dynamic: it pre-fetches job metadata and files, classifies the job type (ACM, ZTP, upgrade, day2, SNO, or standard), and tailors the guidance and file priority list accordingly. Downloads files to/tmp/dci/<job id>/and generates a report at/tmp/dci/rca-<job id>.md./dci/weekly <team name/id or remoteci name/id>conducts a report for the last 7 days stored at/tmp/dci./dci/biweekly <team name/id or remoteci name/id>conducts a report for the last 14 days stored at/tmp/dci./dci/quarterly <remoteci name/id>conducts a comprehensive quarterly analysis (last 3 months) with statistics about pipelines, topics, failure rates, trends, and component usage. Uses pagination and caching to handle large datasets. Report stored at/tmp/dci/<remoteci>/quarterly/<date-range>/report.md./dci/support_case_report <case number>generates a comprehensive support case report. Fetches the support case details, follows all linked Jira tickets (including transitive links), gathers associated PRs/MRs with diffs, and checks for related errata/advisories. The report includes an executive summary, root cause analysis, timeline, code changes classification (short-term vs long-term), corrective measures, and next steps. Report stored at/tmp/dci/support-case-<case_number>-<YYYY-MM-DD>.md.
Debugging Prompts
You can render any prompt outside the MCP server for debugging:
# List available prompts
uv run python -m mcp_server.prompts --list
# Render a prompt with its parameters
uv run python -m mcp_server.prompts rca dci_job_id=<job-id>
uv run python -m mcp_server.prompts weekly subject=<team-name>
# Also available as a standalone command
uv run dci-render-prompt rca dci_job_id=<job-id>Google Drive Integration
The server includes Google Drive integration to convert DCI reports and markdown content to Google Docs with rich formatting support.
Features
š Markdown to Google Docs: Convert markdown content to properly formatted Google Docs
š DCI Report Conversion: Specialized tools for converting DCI weekly/biweekly reports
šØ Rich Formatting: Support for tables, code blocks, headers, lists, and links
š OAuth2 Authentication: Secure authentication with Google Drive API
š Folder Organization: Option to organize documents in specific Google Drive folders
Setup
To use Google Drive features, follow the Google Drive Setup Guide for detailed configuration instructions.
Quick Setup:
Set up Google Cloud Project and enable Google Drive API
Download OAuth2 credentials and save as
credentials.jsonInitialize the service:
uv run python -c "from mcp_server.services.google_drive_service import GoogleDriveService; GoogleDriveService()"Complete browser authentication when prompted
Usage Examples
# Convert a DCI report to Google Doc in a specific folder by name
result = await convert_dci_report_to_google_doc(
report_path="/tmp/dci/the_weekly_report_2025-09-09.md",
doc_title="The Weekly Report - September 2025",
folder_name="DCI Reports",
)
# Create a Google Doc from markdown content in a folder by ID
result = await create_google_doc_from_markdown(
markdown_content="# My Report\n\nThis is a **test** document.",
doc_title="My Custom Report",
folder_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
)
# Create a Google Doc from a file in a folder by name
result = await create_google_doc_from_file(
file_path="/path/to/report.md",
doc_title="My Report",
folder_name="Project Documents",
)Jira Integration
The server includes Jira integration to collect comprehensive ticket data from Red Hat Jira, including comments and changelog information.
Features
š« Ticket Data Collection: Retrieve comprehensive ticket information including summary, description, status, and dates
š¬ Comments Analysis: Get up to 50 recent comments with author and timestamp information
š Changelog Tracking: Access complete ticket history and field changes
š JQL Search: Search tickets using Jira Query Language (JQL)
š Project Information: Get project details and metadata
š DCI Integration: Seamlessly extract Jira tickets from DCI job comments
āļø Write Operations (opt-in): Create tickets, update fields, add comments, and transition status
Setup
To use Jira features, follow the Jira Setup Guide for detailed configuration instructions.
Quick Setup:
Get your Jira API token from https://id.atlassian.com/manage-profile/security/api-tokens
Set environment variables in your
.envfile:JIRA_API_TOKEN=your_jira_api_token_here JIRA_EMAIL=you@redhat.com JIRA_URL=https://redhat.atlassian.net # Optional: enable write operations (create/update tickets, add comments) JIRA_WRITE_ENABLED=true
Usage Examples
# Get comprehensive ticket data with comments
ticket_data = await get_jira_ticket("CILAB-1234", max_comments=10)
# Search for tickets using JQL
open_tickets = await search_jira_tickets("project = CILAB AND status = Open")
# Get project information
project_info = await get_jira_project_info("CILAB")
# Extract Jira tickets from DCI job comments
jobs_with_tickets = await search_dci_jobs("comment=~'.*CILAB.*'")
for job in jobs_with_tickets:
if job.get("comment"):
ticket_data = await get_jira_ticket(job["comment"])GitHub Integration
The server includes GitHub integration to search for issues and pull requests and retrieve detailed information about them.
Features
š Issue & PR Search: Search using GitHub's powerful query syntax
š Comprehensive Data: Get detailed information including comments, labels, assignees, and more
š Pull Request Metadata: Access PR-specific data like merge status, branch info, and file changes
š Repository Information: Retrieve repository metadata and statistics
š Token Authentication: Secure authentication with GitHub personal access tokens
Setup
Quick Setup:
Get your GitHub personal access token from https://github.com/settings/tokens
Create a new token (classic) with
reposcope (for private repos) orpublic_repo(for public repos only)Set environment variable in your
.envfile:GITHUB_TOKEN=your_github_token_here
GitLab Integration
The server includes GitLab integration to search for issues, merge requests, and retrieve diffs and project information.
Setup
Quick Setup:
Create a personal access token in your GitLab instance (User Settings ā Access Tokens) with
read_apiscopeSet environment variables in your
.envfile:GITLAB_TOKEN=your_gitlab_token_here GITLAB_URL=https://gitlab.cee.redhat.com
SSL verification ā for internal GitLab instances with corporate CA certificates, point GITLAB_SSL_VERIFY to your CA bundle instead of disabling verification:
GITLAB_SSL_VERIFY=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pemHost allowlist ā the gitlab_url tool parameter is LLM-controlled. To prevent your token from being sent to unauthorized hosts, only the host from GITLAB_URL is allowed by default. To allow additional hosts:
GITLAB_ALLOWED_HOSTS=gitlab.com,gitlab.cee.redhat.comRed Hat Support Case Integration
The server includes Red Hat Support Case integration to retrieve case data from the Red Hat Customer Portal.
Features
š Case Data Retrieval: Get comprehensive case information including summary, status, severity, and product details
š¬ Comments: Access case comments and communication history
š Linked Bugs: View Bugzilla bugs linked to the case
š¢ Errata/Advisory Details: Retrieve errata information including CVEs, affected products, and references
š Offline Token Authentication: Secure authentication using Red Hat API offline tokens
Setup
Quick Setup:
Get your offline token from https://access.redhat.com/management/api
Set environment variable in your
.envfile:OFFLINE_TOKEN=your_offline_token_here
Available Tools exposed by the MCP server
The server provides tools for interacting with DCI API components:
Component Tools
query_dci_components(query, limit, offset, sort, fields): Query components with advanced query language and pagination
Date Tools
Enabled by default. Set DATE_TOOLS_ENABLED=false to disable.
today(): Returns today's date in YYYY-MM-DD format.now(): Returns current date and time in DCI compatible format (GMT).
Job Tools
search_dci_jobs(query, sort, limit, offset, fields): Search jobs with advanced query language and pagination
File Tools
download_dci_file(job_id, file_id, output_path): Download a file to local path
Google Drive Tools
create_google_doc_from_markdown(markdown_content, doc_title, folder_id, folder_name): Create a Google Doc from markdown contentcreate_google_doc_from_file(file_path, doc_title, folder_id, folder_name): Create a Google Doc from a markdown fileconvert_dci_report_to_google_doc(report_path, doc_title, folder_id, folder_name): Convert a DCI report to Google Doclist_google_docs(query, max_results): List Google Docs in your Drive
Note: For folder placement, you can use either folder_id (exact folder ID) or folder_name (searches for folder by name). Do not use both parameters together.
Jira Tools
get_jira_ticket(ticket_key, max_comments): Get comprehensive ticket data including comments and changelogsearch_jira_tickets(jql, max_results): Search tickets using JQL (Jira Query Language)get_jira_project_info(project_key): Get project information and metadatasearch_jira_child_tickets(parent_jql, child_jql, ...): Traverse a 2-level Jira hierarchy (e.g. TELCOSTRAT ā Epics ā Stories) in a single call, returning leaf tickets with full ancestry info
Note: Jira tools require JIRA_API_TOKEN environment variable to be set.
Jira Write Tools
create_jira_ticket(project_key, summary, ...): Create a new Jira ticket with optional description, issue type, priority, labels, components, and assigneeupdate_jira_ticket(ticket_key, ...): Update ticket fields (summary, description, priority, labels, components, assignee) or transition statusadd_jira_comment(ticket_key, body): Add a comment to a ticketadd_jira_issue_link(ticket_key, target_ticket_key, link_type): Create a typed link between two tickets (e.g., "Blocks", "Clones", "Duplicates"). Uselist_jira_issue_link_typesto discover available link typesadd_jira_weblink(ticket_key, url, title): Add a web/remote link to a ticketlist_jira_transitions(ticket_key): List available workflow transitions for a ticketlist_jira_issue_link_types(): List available issue link types with their inward/outward descriptions
Note: Jira write tools require both JIRA_API_TOKEN and JIRA_WRITE_ENABLED=true environment variables.
GitHub Tools
search_github_issues(query, max_results): Search issues and pull requests using GitHub search query syntaxget_github_issue(repo, issue_number, max_comments): Get comprehensive issue/PR data including comments and PR-specific informationget_github_repository_info(repo): Get repository information and statisticsget_github_pr_diff(repo, pull_number, max_files): Get the diff/patch for a pull request with per-file unified diffsget_github_pr_checks(repo, pull_number): Get CI check runs and commit statuses for a pull request, including status, conclusion, and links to each CI job
Note: GitHub tools require GITHUB_TOKEN environment variable to be set.
Support Case Tools
get_support_case(case_number): Get Red Hat support case data including comments and linked Bugzilla bugsget_support_case_comments(case_number, start_date?, end_date?): Get comments for a case with optional date filteringlist_support_case_attachments(case_number): List attachment metadata for a caseget_errata(advisory_id): Get Red Hat errata/advisory details (RHSA, RHBA, RHEA)
Note: Support Case tools require OFFLINE_TOKEN environment variable to be set.
Code Quality Checks
The project includes comprehensive code quality checks:
Manual Checks
# Run all checks
bash scripts/run-checks.sh
# Or run individual checks
./.venv/bin/python -m black --check .
./.venv/bin/python -m isort --check-only .
./.venv/bin/python -m ruff check .
./.venv/bin/python -m mypy mcp_server/
./.venv/bin/python -m bandit -r mcp_server/ -c pyproject.tomlPre-commit Hooks (Optional)
# Install pre-commit hooks
./.venv/bin/python -m pre_commit install
# Run pre-commit on all files
./.venv/bin/python -m pre_commit run --all-filesDevelopment
Project Structure
mcp_server/
āāā config.py # Configuration and authentication
āāā main.py # Server entry point
āāā services/ # DCI API services
ā āāā dci_base_service.py
ā āāā dci_component_service.py
ā āāā dci_job_service.py
ā āāā dci_file_service.py
ā āāā dci_log_service.py
ā āāā dci_pipeline_service.py
ā āāā dci_product_service.py
ā āāā dci_team_service.py
ā āāā dci_remoteci_service.py
ā āāā dci_topic_service.py
ā āāā google_drive_service.py
ā āāā jira_service.py
ā āāā github_service.py
ā āāā support_case_service.py
āāā prompts/ # Templatized prompts
ā āāā prompts.py # Prompt definitions and registration
ā āāā render.py # Render prompts outside MCP (library)
ā āāā __main__.py # CLI: python -m mcp_server.prompts
āāā tools/ # MCP tools
ā āāā component_tools.py
ā āāā date_tools.py
ā āāā job_tools.py
ā āāā file_tools.py
ā āāā google_drive_tools.py
ā āāā jira_tools.py
ā āāā jira_write_tools.py
ā āāā github_tools.py
ā āāā support_case_tools.py
ā āāā log_tools.py
āāā utils/ # Utility functions
āāā http_client.pyTesting
# Run all checks (format, lint, tests + evals)
bash scripts/run-checks.sh
# Run evals only
uv run pytest -m eval -v
# Run evals with a different model
EVAL_MODEL=haiku uv run pytest -m eval -vEval tests use claude -p to verify that Claude selects the correct MCP tools for natural language prompts. Each eval case is automatically skipped if its required credentials (DCI, Jira, GitHub, etc.) are not configured in .env.
Adding New Tools
Create a new service in
mcp_server/services/if neededCreate a new tool file in
mcp_server/tools/Register the tools in
mcp_server/main.pyUpdate this README with documentation
Available Tools
7 toolsdownload_dci_fileB
Download a DCI file to a local path.
Returns: JSON string with download status
Inputs: job_id (required), file_id (required), output_path (required). Returns: result
nowA
Get current date and time.
Returns: JSON string with current date and time in this format: "2025-09-12T21:47:02.908617"
Inputs: no named parameters. Returns: result
query_dci_componentsA
Lookup DCI components with an advanced query language.
Common queries:
List OCP/OpenShift components:
eq(type,ocp)(filter by type, NOT by name)List all components:
ilike(name,%)List GA OCP components:
and(eq(type,ocp),contains(tags,build:ga))
The query language is based on this DSL:
eq(<field>,<value>) to lookup resources with a <field> having the value <value>.
IMPORTANT: Values must NOT be quoted. Use eq(type,ocp) not eq(type,'ocp').
You can use the comparison functions gt (greater than), ge (greater or equal),
lt (less than) or le (less or equal) using the same syntax as eq: <op>(<field>,<value>).
like(<field>,<value with percent>) and ilike(<field>,<value with percent>)
to lookup a field with a SQL glob like way. For example, to get the components
with a specific name pattern, use like(name,ocp-%).
contains(<field>,<value1>,...) and not_contains(<field>,<value1>,...)
to lookup elements in an array. This is useful mainly for tags.
and(<op1>(...),<op2>(...)), or(<op1>(...),<op2>(...)) and not(<op>) allow
to build nested boolean queries.
null(<field>) to lookup resources with a field having a NULL value.Here are all the fields of a DCI component that can be used in the query:
- id: unique identifier
- name: name of the component
- type: type of the component. Use `eq(type,ocp)` to query OpenShift components (do NOT use name for this).
- team_id: The ID of the team that owns the component. Use the `query_dci_teams` tool to get it.
- released_at: The release timestamp. Use `today` tool to compute relative dates.
- topic_id: The ID of the topic associated with the component. Use the `query_dci_topics` tool to get it.
- state: The current state of the component (active, inactive, etc.).
- url: The URL of the component, if applicable.
- tags: list of tags associated with the component. For components of type ocp, it has a build status tag like `build:dev` (also called engineering candidate or ec), `build:candidate` (also called release candidate or rc), `build:ga` or `build:nightly`.Counting Components: To get the total count of components matching a query, set limit=1 and read the count field in the _meta section of the response.
Example for counting components by type:
{
"query": "eq(type,ocp)",
"limit": 1,
"offset": 0,
"fields": []
}This will return a response like:
{
"components": [],
"_meta": {"count": 150},
...
}The total count is 150 components.
Returns: JSON string with list of components and pagination info
Inputs: query (required), sort, limit, offset, +1 more. Returns: result
query_dci_remotecisA
Lookup DCI remotecis with an advanced query language.
Listing all remotecis: To list all remotecis, use ilike(name,%) as the query.
The query language is based on this DSL:
eq(<field>,<value>) to lookup resources with a <field> having the value <value>.
IMPORTANT: Values must NOT be quoted. Use eq(name,dallas) not eq(name,'dallas').
You can use the comparison functions gt (greater than), ge (greater or equal),
lt (less than) or le (less or equal) using the same syntax as eq: <op>(<field>,<value>).
like(<field>,<value with percent>) and ilike(<field>,<value with percent>)
to lookup a field with a SQL glob like way. For example, to get the remotecis
with a specific name pattern, use like(name,dallas-%).
contains(<field>,<value1>,...) and not_contains(<field>,<value1>,...)
to lookup elements in an array. This is useful mainly for tags.
and(<op1>(...),<op2>(...)), or(<op1>(...),<op2>(...)) and not(<op>) allow
to build nested boolean queries.
null(<field>) to lookup resources with a field having a NULL value.Here are all the fields of a DCI remoteci that can be used in the query:
- id: unique identifier
- name: name of the remoteci (lab)
- created_at: The creation timestamp. Use `today` tool to compute relative dates.
- updated_at: The last update timestamp. Use `today` tool to compute relative dates.
- tags: list of tags associated with the remoteci.Listing all remotecis: To list all remotecis, use ilike(name,%) as the query.
Counting Remotecis: To get the total count of remotecis matching a query, set limit=1 and read the count field in the _meta section of the response.
Example for counting remotecis by name:
{
"query": "eq(name,dallas)",
"limit": 1,
"offset": 0,
"fields": []
}This will return a response like:
{
"remotecis": [],
"_meta": {"count": 2},
...
}The total count is 2 remotecis.
Returns: JSON string with list of remotecis and pagination info
Inputs: query (required), sort, limit, offset, +1 more. Returns: result
query_dci_teamsA
Lookup DCI teams with an advanced query language.
Listing all teams: To list all teams, use ilike(name,%) as the query.
The query language is based on this DSL:
eq(<field>,<value>) to lookup resources with a <field> having the value <value>.
IMPORTANT: Values must NOT be quoted. Use eq(name,DCI) not eq(name,'DCI').
You can use the comparison functions gt (greater than), ge (greater or equal),
lt (less than) or le (less or equal) using the same syntax as eq: <op>(<field>,<value>).
like(<field>,<value with percent>) and ilike(<field>,<value with percent>)
to lookup a field with a SQL glob with at least one % character. For example, to get the teams
with a specific name pattern, use like(name,%Name%).
contains(<field>,<value1>,...) and not_contains(<field>,<value1>,...)
to lookup elements in an array. This is useful mainly for tags.
and(<op1>(...),<op2>(...)), or(<op1>(...),<op2>(...)) and not(<op>) allow
to build nested boolean queries.
null(<field>) to lookup resources with a field having a NULL value.Here are all the fields of a DCI team that can be used in the query:
- id: unique identifier
- name: name of the team
- created_at: The creation timestamp. Use `today` tool to compute relative dates.
- updated_at: The last update timestamp. Use `today` tool to compute relative dates.
- tags: list of tags associated with the team.Listing all teams: To list all teams, use ilike(name,%) as the query.
Counting Teams: To get the total count of teams matching a query, set limit=1 and read the count field in the _meta section of the response.
Example for counting teams by name:
{
"query": "eq(name,DCI)",
"limit": 1,
"offset": 0,
"fields": []
}This will return a response like:
{
"teams": [],
"_meta": {"count": 10},
...
}The total count is 10 teams.
Returns: JSON string with list of teams and pagination info
Inputs: query (required), sort, limit, offset, +1 more. Returns: result
search_dci_jobsA
Search DCI (Distributed CI) job documents from Elasticsearch.
DCI jobs represent CI/CD pipeline executions that test software components (like OpenShift, storage solutions, etc.) across different environments.
ā ļø COMMON MISTAKES TO AVOID
1. ALWAYS wrap conditions in parentheses - CRITICAL RULE
Each condition MUST be wrapped in parentheses. When combining conditions with AND/OR, add EXTRA parentheses to group them.
Single condition:
ā
status='failure'ā INVALID (no parentheses)ā
(status='failure')ā VALID (wrapped)
Two conditions (AND/OR):
ā
(status='failure') and (tags in ['daily'])ā INVALID (missing outer parentheses)ā
((status='failure') and (tags in ['daily']))ā VALID (each condition + outer grouping)
Three or more conditions:
ā
((status='failure') and (created_at>='2024-01-01') and (duration>=1000))ā MAY FAILā
(((status='failure') and (created_at>='2024-01-01')) and (duration>=1000))ā VALID (proper grouping)ā
(((tests.testsuites.testcases.action='failure') and (nodes.hardware.cpu_vendor='Intel')) and (created_at>='2024-01-01'))ā VALID (complex nested fields)
Rule of thumb: Count your conditions. If N conditions, you need N pairs of parentheses PLUS (N-1) grouping pairs.
1 condition = 1 pair:
(field='value')2 conditions = 3 pairs:
((cond1) and (cond2))3 conditions = 5 pairs:
(((cond1) and (cond2)) and (cond3))
2. NEVER use = for dates
ā
created_at='2024-01-15'ā INVALIDā
(created_at>='2024-01-15')ā VALID for "since Jan 15"ā
((created_at>='2024-01-15') and (created_at<='2024-01-20'))ā VALID for period
3. Use in for lists, not =
ā
status='failure' or status='error'ā Works but verboseā
(status in ['failure', 'error'])ā BETTERā
tags='daily'ā INVALIDā
(tags in ['daily'])ā VALID
4. Multiple components = separate conditions with AND
ā
(components.type in ['ocp', 'storage'])ā Finds jobs with OCP OR storageā
(((components.type='ocp') and (components.version='4.19.0')) and ((components.type='storage') and (components.name='ceph')))ā Finds jobs with OCP 4.19.0 AND Ceph storage
5. Specify necessary fields
ā
fields=[]ā No data returned (only metadata)ā
fields=['id', 'status', 'created_at', 'components.name']ā Essential dataš” Use dot notation for nested fields:
components.name,tests.testsuites.testcases.action
6. Large results = save to file
ā
limit=200without__save_to_fileā Context overloadā
limit=200, __save_to_file='/tmp/jobs.json'ā Saves context
Query Language (DSL)
Basic Operators:
field='value'- exact matchfield!=value- not equalfield>value,field>=value,field<value,field<=value- comparisonsfield=~'regex'- regex match
List Operators:
field in ['value1', 'value2']- value in listfield not_in ['value1', 'value2']- value not in list
Logical Operators:
and,or- combine criteria()- group criteria with parentheses (ALWAYS REQUIRED)
Simple Examples (1-2 conditions):
Single condition:
(remoteci.name='telco-cilab-bos2')Failing daily jobs:
((tags in ['daily']) and (status in ['failure', 'error']))OpenShift 4.19 jobs:
((components.type='ocp') and (components.version='4.19.0'))Jobs with workarounds:
((keys_values.key='workarounds') and (keys_values.value>0))Date range:
((created_at>='2024-09-16') and (created_at<='2025-09-20'))
Complex Examples (3+ conditions - note the extra grouping parentheses):
Success jobs in date range:
(((status='success') and (created_at>='2024-01-01')) and (duration>=1000))Failed test with specific firmware:
(((tests.testsuites.testcases.name=~'PTP.*') and (tests.testsuites.testcases.action='failure')) and (nodes.hardware.network_interfaces.firmware_version='2.50'))Multiple criteria:
(((tags in ['daily']) and (status='failure')) and (created_at>='2024-01-01'))Nested fields combo:
(((components.type='ocp') and (components.version=~'4.1?.*')) and (team.name='my-team'))
Available Fields
Basic Job Information:
id: unique job identifiername: job name (just a label, don't over-interpret)status: current state (new, running, success, failure, error, killed)state: internal job statestatus_reason: explanation for failed jobs (free text)comment: free text, may contain JIRA ticket numbersconfiguration: job configuration (free text)duration: execution time in seconds
Timestamps:
created_at: job creation timeupdated_at: last update timeUse
todayornowtools for relative datesUse >, <, >=, <= operators (NEVER use = for dates)
Format:
2025-09-12or2025-09-12T21:47:02.908617For a period:
((created_at>='2024-09-16') and (created_at<='2025-09-20'))
Components & Software:
components.(type, name, version, tags): list of software components testedComponent types:
ocp(OpenShift),storage,cnf,hwcertBuild tags:
build:ga(GA release),build:candidate(RC),build:dev(EC),build:nightlyExample:
((components.type='ocp') and (components.version='4.19.0'))Multiple components:
(((components.type='ocp') and (components.version='4.19.0')) and ((components.type='storage') and (components.name='ceph')))
Infrastructure:
remoteci.(name, id): lab/environment where job ran (prefer remoteci.name)product.(name, id): product being tested (prefer product.name)team.(name, id): team owning the job (prefer team.name)topic.(name, id): topic/category (prefer topic.name)
Pipeline Information:
pipeline.(name, id): pipeline detailsprevious_job_id: previous job in same pipeline
Tags & Classification:
tags: list of tags for categorization (ALWAYS useinoperator)Common tags:
daily- daily jobsagent:openshift- OpenShift/OCP install jobsagent:openshift-app- OpenShift/OCP application/workload jobsconnected- connected mode jobsdisconnected- disconnected mode jobsuse-dci-container- containerized jobsinstall_type:ipi- IPI installinstall_type:abi- Agent-Based Installerinstall_type:acm- ACM installinstall_type:sno- Single Node OpenShiftinstall_type:upi- UPI install
Files & Artifacts:
files.(id, name, size, state, mime): job artifactsUse
download_dci_filetool to download filesExample: Get file IDs with
fields=['id', 'files.id', 'files.name'], then download
Metrics & Measurements:
keys_values.(key, value): job metricsCommon metrics:
install_time(seconds),workarounds(count),test_count,failure_countQuery:
((keys_values.key='workarounds') and (keys_values.value>0))
Node Information:
nodes: list of nodes involved in the jobnodes.(node, role): hostname and role (sno, master, worker, control-plane)nodes.kernel.(version, params): kernel detailsnodes.hardware.system_(vendor, model, family, sku): system infonodes.hardware.cpu_(model, vendor, sockets, total_cores, total_threads, frequency_mhz): CPU infonodes.hardware.memory_(total_gb, dimm_count): memory infonodes.hardware.bios_(vendor, version, date, type): BIOS infonodes.hardware.(network_interfaces, pci_accelerators, pci_network_controllers, storage_devices): device listsExample:
((nodes.role='sno') and (nodes.hardware.cpu_vendor='Intel'))
Test Results (3-level nested structure):
tests: complex nested structure with test resultsStructure:
Level 1:
tests.(file_id, name)- test file/suite filesLevel 2:
tests.testsuites.(name, testcases)- test suitesLevel 3:
tests.testsuites.testcases.(name, action, classname, time, type, properties, message, stdout, stderr)- individual test casesActions:
run(success),skip(skipped),error(error),failure(failed)Types:
junit,robotExamples:
All jobs with at least 1 failed test:
(tests.testsuites.testcases.action='failure')Failed tests in specific suite file:
((tests.name='test_suite.xml') and (tests.testsuites.testcases.action='failure'))Specific test by name:
((tests.testsuites.testcases.name='test_install') and (tests.testsuites.testcases.action='failure'))Test matching pattern:
((tests.testsuites.testcases.name=~'.*43336-V-BR.*') and (tests.testsuites.testcases.action='success'))
URLs:
url: GitHub PR or Gerrit change URL
Other:
jobstates: internal job state informationresults: job results datauser_agent: client information
Common Use Cases
Find Failing Jobs: (status in ['failure', 'error'])
Daily Jobs: (tags in ['daily'])
OpenShift Jobs: (product.name='OpenShift')
OpenShift Install Jobs: (tags in ['agent:openshift'])
OpenShift Application/Workload Jobs: (tags in ['agent:openshift-app'])
Jobs with Specific Component Version: ((components.type='ocp') and (components.version='4.19.0'))
Jobs with Multiple Components: (((components.type='ocp') and (components.version='4.19.0')) and ((components.type='storage') and (components.name='ceph')))
Jobs by Date Range: ((created_at>='2024-09-16') and (created_at<='2025-09-16'))
Jobs with Specific Metrics: ((keys_values.key='install_time') and (keys_values.value>3600))
Compare Jobs: Look for jobs with same name, topic, remoteci, configuration, and url
ElasticSearch Aggregations (for Statistics & Analysis)
When to use aggregations (aggs parameter):
User asks for counts, statistics, trends, distributions, or averages
Examples: "How many jobs failed?", "Show daily trend", "Average duration", "Count by status"
Aggregations compute stats server-side (much faster than fetching all documents)
Use
limit=1withfields=['id']for aggregations to minimize bandwidth (limit=0 is auto-set to 1)
Aggregation syntax (ElasticSearch 7.16):
The aggs parameter takes a dict with aggregation definitions: {"<agg_name>": {"<agg_type>": {...}}}
Simple field aggregations (keyword, date, numeric fields):
Terms (group by):
{"by_status": {"terms": {"field": "status", "size": 10}}}Date histogram:
{"daily": {"date_histogram": {"field": "created_at", "calendar_interval": "day"}}}Stats (avg, min, max, sum):
{"duration_stats": {"stats": {"field": "duration"}}}Count:
{"total": {"value_count": {"field": "id"}}}
Nested field aggregations (components, tests, team, remoteci, pipeline, topic, files, keys_values, nodes): Nested fields require special nested aggregation syntax:
Step 1: Wrap in nested aggregation with path
Step 2: Add filter or terms aggregation inside
Example for components.version:
{ "components_agg": { "nested": {"path": "components"}, "aggs": { "ocp_only": { "filter": {"term": {"components.type": "ocp"}}, "aggs": { "versions": {"terms": {"field": "components.version", "size": 50}} } } } } }
Triple-nested: tests.testsuites.testcases (test results): Test data requires three levels of nested aggregations:
{
"tests_agg": {
"nested": {"path": "tests"},
"aggs": {
"testsuites_agg": {
"nested": {"path": "tests.testsuites"},
"aggs": {
"testcases_agg": {
"nested": {"path": "tests.testsuites.testcases"},
"aggs": {
"by_action": {"terms": {"field": "tests.testsuites.testcases.action"}}
}
}
}
}
}
}
}Available aggregation fields by type:
Simple fields (direct aggregation): status, tags, team_id, remoteci_id, pipeline_id, topic_id, product_id, created_at, updated_at, duration, state, name
Nested fields (need nested agg):
components: type, name, version, tags, canonical_project_name
tests ā testsuites ā testcases: name, action (success/failure/error/skip), classname, time, type
team: id, name, state, external, has_pre_release_access
remoteci: id, name, state, public
pipeline: id, name, state
topic: id, name, component_types, export_control
files: id, name, mime, size, state
keys_values: key, value
nodes ā hardware: (nested within nested)
nodes ā kernel: (nested within nested)
Common aggregation patterns:
"How many jobs by status?" ā
{"by_status": {"terms": {"field": "status"}}}"Daily job count last week" ā
{"daily": {"date_histogram": {"field": "created_at", "calendar_interval": "day"}}}"Average job duration" ā
{"avg_duration": {"avg": {"field": "duration"}}}"Count by team" ā
{"by_team": {"terms": {"field": "team_id"}}}"OCP version distribution" ā Use nested aggregation on components (see example above)
"Test failure rate" ā Use triple-nested aggregation on tests.testsuites.testcases (see example above)
Combining aggregations with sub-aggregations: You can nest aggregations for multi-dimensional analysis:
{
"daily": {
"date_histogram": {"field": "created_at", "calendar_interval": "day"},
"aggs": {
"by_status": {"terms": {"field": "status"}},
"avg_duration": {"avg": {"field": "duration"}}
}
}
}Response format with aggregations: When aggregations are provided, the response includes an "aggregations" field with the computed statistics. The "hits" field will contain 1 job document (auto-set when aggs provided), or more if limit is explicitly set higher.
Function Parameters
query (required): Query DSL string - see examples above
fields (optional, default: []): List of fields to return
[]: NO data returned (only metadata: count, total)['id', 'status']: only these fieldsUse dot notation for nested fields:
components.name,tests.testsuites.testcases.actionRecommended minimum:
['id', 'status', 'created_at']For components:
['components.type', 'components.name', 'components.version']For tests:
['tests.testsuites.testcases.name', 'tests.testsuites.testcases.action']
limit (optional, default: 20, max: 200): Page size ā maximum job documents in hits for this call. Not repeated in the JSON response; you must remember what you passed. If limit > 50, use __save_to_file.
offset (optional, default: 0): Number of matching jobs to skip (same sort as sort). Page 1: offset=0. Next page: offset = previous offset + limit. Not repeated in the JSON response.
sort (optional, default: '-created_at'): Sort criteria
-created_at: newest to oldest (default)created_at: oldest to newest-duration: longest to shortestduration: shortest to longestā ļø WARNING: Only date and numeric fields are sortable (
created_at,updated_at,duration)Text fields like
nameorstatusare NOT sortable and will return empty results
__save_to_file (optional, available on ALL tools): Path to save complete result
Use systematically if:
limit > 50, many fields, multiple pagination, bulk analysisExample:
__save_to_file='/tmp/dci/jobs.json'
Field Filtering
The fields parameter filters which fields are returned in the response:
If
fieldsis empty[], no job data is returned (only metadata)If
fieldscontains field names, only those fields are returnedUse dot notation for nested fields:
components.name,topic.id,tests.testsuites.testcases.nameCommon field combinations:
Basic info:
['id', 'name', 'status', 'created_at']Component details:
['components.name', 'components.version', 'components.tags']Test results:
['tests.testsuites.testcases.name', 'tests.testsuites.testcases.action']
Response format and pagination
The return value is a JSON string. Parse it once; the top-level object has at least:
hits: array of job objects for this page (up tolimititems). Shape matches yourfields(nested objects only where you requested them). Iffields=[], this is always[]ā you still gettotalfor counting.total: hit metadata for the query. Either a non-negative integer (exact match count) or an Elasticsearch-style object with:value(int): number of matching jobs, or a lower bound whenrelationisgte(see below).relation(string, when present):eqmeansvalueis the exact total.gtemeans there are at leastvaluematches; the true count may be higher (Elasticsearch did not track the full total). Ifrelationis omitted, treatvalueas exact unless your deployment documents otherwise.
Let N = total if total is an int, else N = total["value"]. Let rel be None if total is an int, else total.get("relation").
There is no limit or offset key in the response; pagination is driven only by the tool arguments on the next call.
How to paginate: Keep the same query, sort, and fields. Start with offset=0. After each page, advance offset by limit while there may be more rows:
If
rel == "gte": the only reliable stop rule islen(hits) < limit(no more full pages). Do not assumeNis the full count.Otherwise (
reliseq,None, ortotalis an int): stop whenlen(hits) < limitoroffset + len(hits) >= N.
Quick count: Exact total only when total is an int or relation == "eq" (or relation absent and your stack guarantees exact value). If relation == "gte", value is a lower bound only ā you must paginate through all pages (or accept an approximate count) for a true total.
Example (illustrative; field names depend on fields):
{
"hits": [
{
"id": "job-abc-123",
"status": "failure",
"created_at": "2026-03-30T10:00:00",
"components": [
{
"type": "ocp",
"name": "OpenShift",
"version": "4.19.0",
"tags": ["build:ga"]
}
]
}
],
"total": {"value": 150, "relation": "eq"}
}Inputs: query (required), sort, limit, offset, +2 more. Returns: result
todayA
Get today's date.
Returns: JSON string with today's date in this format: "YYYY-MM-DD"
Inputs: no named parameters. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelogā¦
TDQS
Scored across 7 tools
Each tool has a clearly distinct purpose: file download, time utilities, and querying different entities (components, remotecis, teams, jobs). No overlap or ambiguity between tool functions.
Naming is inconsistent: 'now' and 'today' are standalone, while other tools use prefix patterns like 'query_dci_plural' or 'search_dci_jobs'. There are minor style differences (singular vs plural) and verb choice inconsistencies (query vs search).
7 tools is well-scoped for a DCI server. It covers essential query operations and file download without being excessive or insufficient.
The tool set provides read-only queries and file download, but lacks any create, update, or delete operations. While likely intentional for a query-focused server, notable gaps exist in lifecycle coverage.
Maintenance
Related MCP Connectors
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
MCP server that lets AI assistants use all OneSchema features exposed via the public API.
One MCP server for the services your AI needs: memory, files, Microsoft 365, kanban and more.
MCP server for AI access to Swagger by SmartBear.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnterprise-grade MCP server exposing Ansible Automation Platform 2.x as a complete AI interface for LLMs, enabling natural language management of automation resources.861Apache 2.0
- AlicenseNot gradedqualityDmaintenanceMCP server for querying PanDA documentation, analyzing job errors, and retrieving JSON data using multiple AI models.1Apache 2.0
- AlicenseNot gradedqualityDmaintenanceA comprehensive MCP server suite for Red Hat's automation ecosystem, enabling AI assistants to interact with Ansible Automation Platform, Event-Driven Ansible, ansible-lint, and official Red Hat documentation with secure domain validation.35MIT
- AlicenseAqualityDmaintenanceAI-powered MCP server for enterprise OpenShift/Kubernetes cluster management, providing diagnostic tools, RAG knowledge retrieval, and autonomous remediation recommendations.9MIT