Skip to content

Commit 4c6a829

Browse files
restored team and remoteci tools (#7)
1 parent 8281442 commit 4c6a829

File tree

5 files changed

+251
-1
lines changed

5 files changed

+251
-1
lines changed

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# DCI MCP Server - Change Log
22

3+
## [2025-11-21]
4+
5+
### Improvements
6+
7+
- Restored `query_dci_teams` and `query_dci_remotecis` tools that were previously removed
8+
39
## [2025-09-15] - 0.3.0
410

511
### Improvements

mcp_server/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
from .tools.google_drive_tools import register_google_drive_tools
2828
from .tools.jira_tools import register_jira_tools
2929
from .tools.job_tools import register_job_tools
30+
from .tools.remoteci_tools import register_remoteci_tools
31+
from .tools.team_tools import register_team_tools
3032

3133

3234
def create_server() -> FastMCP:
@@ -55,6 +57,8 @@ def create_server() -> FastMCP:
5557
register_date_tools(mcp)
5658
register_job_tools(mcp)
5759
register_file_tools(mcp)
60+
register_team_tools(mcp)
61+
register_remoteci_tools(mcp)
5862

5963
# Register Jira tools only when credentials are set
6064
if os.getenv("JIRA_API_TOKEN"):

mcp_server/tools/job_tools.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,11 @@ async def search_dci_jobs(
265265
266266
**Daily Jobs:** `(tags in ['daily'])`
267267
268-
**OpenShift Jobs:** `(components.type='ocp')`
268+
**OpenShift Jobs:** `(product.name='OpenShift')`
269+
270+
**OpenShift install Jobs:** `(tags in ['agent:openshift'])`
271+
272+
**OpenShift application/workload Jobs:** `(tags in ['agent:openshift-app'])`
269273
270274
**Jobs with Specific Component Version:** `((components.type='ocp') and (components.version='4.19.0'))`
271275

mcp_server/tools/remoteci_tools.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""MCP tools for DCI remoteci operations."""
2+
3+
import json
4+
from typing import Annotated
5+
6+
from fastmcp import FastMCP
7+
from pydantic import Field
8+
9+
from ..services.dci_remoteci_service import DCIRemoteCIService
10+
11+
12+
def register_remoteci_tools(mcp: FastMCP) -> None:
13+
"""Register remoteci-related tools with the MCP server."""
14+
15+
@mcp.tool()
16+
async def query_dci_remotecis(
17+
query: Annotated[
18+
str,
19+
Field(
20+
description="search criteria (e.g., and(ilike(name,dallas),contains(tags,ga))"
21+
),
22+
],
23+
sort: Annotated[str, Field(description="Sort criteria")] = "-created_at",
24+
limit: Annotated[
25+
int,
26+
Field(
27+
description="Maximum number of results to return for pagination (default 20, max 200). Use limit=1 to get count from metadata.",
28+
ge=1,
29+
le=200,
30+
),
31+
] = 20,
32+
offset: Annotated[int, Field(description="Offset for pagination", ge=0)] = 0,
33+
fields: Annotated[
34+
list[str],
35+
Field(
36+
description="List of fields to return. Fields are the one listed in the query description and responses. Must be specified as a list of strings. If empty, no fields are returned.",
37+
),
38+
] = [],
39+
) -> str:
40+
"""
41+
Lookup DCI remotecis with an advanced query language.
42+
43+
The query language is based on this DSL:
44+
45+
eq(<field>,<value>) to lookup resources with a <field> having the value <value>.
46+
47+
You can use the comparison functions gt (greater than), ge (greater or equal),
48+
lt (less than) or le (less or equal) using the same syntax as eq: <op>(<field>,<value>).
49+
50+
like(<field>,<value with percent>) and ilike(<field>,<value with percent>)
51+
to lookup a field with a SQL glob like way. For example, to get the remotecis
52+
with a specific name pattern, use like(name,dallas-%).
53+
54+
contains(<field>,<value1>,...) and not_contains(<field>,<value1>,...)
55+
to lookup elements in an array. This is useful mainly for tags.
56+
57+
and(<op1>(...),<op2>(...)), or(<op1>(...),<op2>(...)) and not(<op>) allow
58+
to build nested boolean queries.
59+
60+
null(<field>) to lookup resources with a field having a NULL value.
61+
62+
Here are all the fields of a DCI remoteci that can be used in the query:
63+
64+
- id: unique identifier
65+
66+
- name: name of the remoteci (lab)
67+
68+
- created_at: The creation timestamp. Use `today` tool to compute relative dates.
69+
70+
- updated_at: The last update timestamp. Use `today` tool to compute relative dates.
71+
72+
- tags: list of tags associated with the remoteci.
73+
74+
**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.
75+
76+
**Example for counting remotecis by name**:
77+
```json
78+
{
79+
"query": "eq(name,dallas)",
80+
"limit": 1,
81+
"offset": 0,
82+
"fields": []
83+
}
84+
```
85+
This will return a response like:
86+
```json
87+
{
88+
"remotecis": [],
89+
"_meta": {"count": 2},
90+
...
91+
}
92+
```
93+
The total count is 2 remotecis.
94+
95+
Returns:
96+
JSON string with list of remotecis and pagination info
97+
"""
98+
try:
99+
service = DCIRemoteCIService()
100+
101+
result = service.query_remotecis(
102+
query=query, sort=sort, limit=limit, offset=offset
103+
)
104+
105+
if isinstance(fields, list) and fields:
106+
# Filter the result to only include specified fields
107+
if "remotecis" in result:
108+
filtered_result = [
109+
{field: remoteci.get(field) for field in fields}
110+
for remoteci in result["remotecis"]
111+
]
112+
result["remotecis"] = filtered_result
113+
elif not fields:
114+
result["remotecis"] = []
115+
116+
return json.dumps(result, indent=2)
117+
except Exception as e:
118+
return json.dumps({"error": str(e)}, indent=2)

mcp_server/tools/team_tools.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""MCP tools for DCI team operations."""
2+
3+
import json
4+
from typing import Annotated
5+
6+
from fastmcp import FastMCP
7+
from pydantic import Field
8+
9+
from ..services.dci_team_service import DCITeamService
10+
11+
12+
def register_team_tools(mcp: FastMCP) -> None:
13+
"""Register team-related tools with the MCP server."""
14+
15+
@mcp.tool()
16+
async def query_dci_teams(
17+
query: Annotated[
18+
str,
19+
Field(
20+
description="search criteria (e.g., and(ilike(name,qa),contains(tags,ga))"
21+
),
22+
],
23+
sort: Annotated[str, Field(description="Sort criteria")] = "-created_at",
24+
limit: Annotated[
25+
int,
26+
Field(
27+
description="Maximum number of results to return for pagination (default 20, max 200). Use limit=1 to get count from metadata.",
28+
ge=1,
29+
le=200,
30+
),
31+
] = 20,
32+
offset: Annotated[int, Field(description="Offset for pagination", ge=0)] = 0,
33+
fields: Annotated[
34+
list[str],
35+
Field(
36+
description="List of fields to return. Fields are the one listed in the query description and responses. Must be specified as a list of strings. If empty, no fields are returned.",
37+
),
38+
] = [],
39+
) -> str:
40+
"""
41+
Lookup DCI teams with an advanced query language.
42+
43+
The query language is based on this DSL:
44+
45+
eq(<field>,<value>) to lookup resources with a <field> having the value <value>.
46+
47+
You can use the comparison functions gt (greater than), ge (greater or equal),
48+
lt (less than) or le (less or equal) using the same syntax as eq: <op>(<field>,<value>).
49+
50+
like(<field>,<value with percent>) and ilike(<field>,<value with percent>)
51+
to lookup a field with a SQL glob with at least one % character. For example, to get the teams
52+
with a specific name pattern, use like(name,%Name%).
53+
54+
contains(<field>,<value1>,...) and not_contains(<field>,<value1>,...)
55+
to lookup elements in an array. This is useful mainly for tags.
56+
57+
and(<op1>(...),<op2>(...)), or(<op1>(...),<op2>(...)) and not(<op>) allow
58+
to build nested boolean queries.
59+
60+
null(<field>) to lookup resources with a field having a NULL value.
61+
62+
Here are all the fields of a DCI team that can be used in the query:
63+
64+
- id: unique identifier
65+
66+
- name: name of the team
67+
68+
- created_at: The creation timestamp. Use `today` tool to compute relative dates.
69+
70+
- updated_at: The last update timestamp. Use `today` tool to compute relative dates.
71+
72+
- tags: list of tags associated with the team.
73+
74+
**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.
75+
76+
**Example for counting teams by name**:
77+
```json
78+
{
79+
"query": "eq(name,DCI)",
80+
"limit": 1,
81+
"offset": 0,
82+
"fields": []
83+
}
84+
```
85+
This will return a response like:
86+
```json
87+
{
88+
"teams": [],
89+
"_meta": {"count": 10},
90+
...
91+
}
92+
```
93+
The total count is 10 teams.
94+
95+
Returns:
96+
JSON string with list of teams and pagination info
97+
"""
98+
try:
99+
service = DCITeamService()
100+
101+
result = service.query_teams(
102+
query=query, sort=sort, limit=limit, offset=offset
103+
)
104+
105+
if isinstance(fields, list) and fields:
106+
# Filter the result to only include specified fields
107+
if "teams" in result:
108+
filtered_result = [
109+
{field: team.get(field) for field in fields}
110+
for team in result["teams"]
111+
]
112+
result["teams"] = filtered_result
113+
elif not fields:
114+
result["teams"] = []
115+
116+
return json.dumps(result, indent=2)
117+
except Exception as e:
118+
return json.dumps({"error": str(e)}, indent=2)

0 commit comments

Comments
 (0)