Skip to content

Commit e8afb95

Browse files
floatingIce91DevRev
andauthored
added tools for parts in DevRev (#19)
Added support for creating, updating and getting parts (enhancement) in DevRev Issue: https://app.devrev.ai/devrev/works/ISS-175688 Co-authored-by: DevRev <[email protected]>
1 parent 1a80be1 commit e8afb95

File tree

1 file changed

+212
-33
lines changed

1 file changed

+212
-33
lines changed

src/devrev_mcp/server.py

Lines changed: 212 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,41 +38,84 @@ async def handle_list_tools() -> list[types.Tool]:
3838
},
3939
),
4040
types.Tool(
41-
name="get_object",
42-
description="Get all information about a DevRev issue and ticket using its ID",
41+
name="get_work",
42+
description="Get all information about a DevRev work item (issue, ticket) using its ID",
4343
inputSchema={
4444
"type": "object",
4545
"properties": {
46-
"id": {"type": "string"},
46+
"id": {"type": "string", "description": "The DevRev ID of the work item"},
4747
},
4848
"required": ["id"],
4949
},
5050
),
5151
types.Tool(
52-
name="create_object",
53-
description="Create a new isssue or ticket in DevRev",
52+
name="create_work",
53+
description="Create a new work item (issue, ticket) in DevRev",
5454
inputSchema={
5555
"type": "object",
5656
"properties": {
5757
"type": {"type": "string", "enum": ["issue", "ticket"]},
5858
"title": {"type": "string"},
5959
"body": {"type": "string"},
60-
"applies_to_part": {"type": "string"},
61-
"owned_by": {"type": "array", "items": {"type": "string"}}
60+
"applies_to_part": {"type": "string", "description": "The DevRev ID of the part to which the work item applies"},
61+
"owned_by": {"type": "array", "items": {"type": "string"}, "description": "The DevRev IDs of the users who are assigned to the work item"}
6262
},
6363
"required": ["type", "title", "applies_to_part"],
6464
},
6565
),
6666
types.Tool(
67-
name="update_object",
68-
description="Update an existing issue or ticket in DevRev",
67+
name="update_work",
68+
description="Update an existing work item (issue, ticket) in DevRev",
6969
inputSchema={
7070
"type": "object",
7171
"properties": {
7272
"type": {"type": "string", "enum": ["issue", "ticket"]},
7373
"id": {"type": "string"},
7474
"title": {"type": "string"},
7575
"body": {"type": "string"},
76+
"applies_to_part": {"type": "string", "description": "The DevRev ID of the part to which the work item applies"},
77+
"owned_by": {"type": "array", "items": {"type": "string"}, "description": "The DevRev IDs of the users who are assigned to the work item"},
78+
},
79+
"required": ["id", "type"],
80+
},
81+
),
82+
types.Tool(
83+
name="get_part",
84+
description="Get information about a part (enhancement) in DevRev using its ID",
85+
inputSchema={
86+
"type": "object",
87+
"properties": {"id": {"type": "string", "description": "The DevRev ID of the part"}},
88+
"required": ["id"],
89+
},
90+
),
91+
types.Tool(
92+
name="create_part",
93+
description="Create a new part (enhancement) in DevRev",
94+
inputSchema={
95+
"type": "object",
96+
"properties": {
97+
"type": {"type": "string", "enum": ["enhancement"]},
98+
"name": {"type": "string"},
99+
"owned_by": {"type": "array", "items": {"type": "string"}, "description": "The DevRev IDs of the users assigned to the part"},
100+
"parent_part": {"type": "array", "items": {"type": "string"}, "description": "The DevRev IDs of the parent parts"},
101+
"description": {"type": "string", "description": "The description of the part"},
102+
},
103+
"required": ["type", "name", "owned_by", "parent_part"],
104+
},
105+
),
106+
types.Tool(
107+
name="update_part",
108+
description="Update an existing part (enhancement) in DevRev",
109+
inputSchema={
110+
"type": "object",
111+
"properties": {
112+
"type": {"type": "string", "enum": ["enhancement"]},
113+
"id": {"type": "string", "description": "The DevRev ID of the part"},
114+
"name": {"type": "string", "description": "The name of the part"},
115+
"owned_by": {"type": "array", "items": {"type": "string"}, "description": "The DevRev IDs of the users assigned to the part"},
116+
"description": {"type": "string", "description": "The description of the part"},
117+
"target_close_date": {"type": "string", "description": "The target closed date of the part, for example: 2025-06-03T00:00:00Z"},
118+
"target_start_date": {"type": "string", "description": "The target start date of the part, for example: 2025-06-03T00:00:00Z"},
76119
},
77120
"required": ["id", "type"],
78121
},
@@ -101,7 +144,10 @@ async def handle_call_tool(
101144

102145
response = make_devrev_request(
103146
"search.hybrid",
104-
{"query": query, "namespace": namespace}
147+
{
148+
"query": query,
149+
"namespace": namespace
150+
}
105151
)
106152
if response.status_code != 200:
107153
error_text = response.text
@@ -119,7 +165,7 @@ async def handle_call_tool(
119165
text=f"Search results for '{query}':\n{search_results}"
120166
)
121167
]
122-
elif name == "get_object":
168+
elif name == "get_work":
123169
if not arguments:
124170
raise ValueError("Missing arguments")
125171

@@ -129,7 +175,9 @@ async def handle_call_tool(
129175

130176
response = make_devrev_request(
131177
"works.get",
132-
{"id": id}
178+
{
179+
"id": id
180+
}
133181
)
134182
if response.status_code != 200:
135183
error_text = response.text
@@ -140,20 +188,18 @@ async def handle_call_tool(
140188
)
141189
]
142190

143-
object_info = response.json()
144191
return [
145192
types.TextContent(
146193
type="text",
147-
text=f"Object information for '{id}':\n{object_info}"
194+
text=f"Object information for '{id}':\n{response.json()}"
148195
)
149196
]
150-
elif name == "create_object":
197+
elif name == "create_work":
151198
if not arguments:
152199
raise ValueError("Missing arguments")
153200

154-
# Mandatory fields
155-
object_type = arguments.get("type")
156-
if not object_type:
201+
type = arguments.get("type")
202+
if not type:
157203
raise ValueError("Missing type parameter")
158204

159205
title = arguments.get("title")
@@ -164,14 +210,13 @@ async def handle_call_tool(
164210
if not applies_to_part:
165211
raise ValueError("Missing applies_to_part parameter")
166212

167-
# Optional fields
168213
body = arguments.get("body", "")
169214
owned_by = arguments.get("owned_by", [])
170215

171216
response = make_devrev_request(
172217
"works.create",
173218
{
174-
"type": object_type,
219+
"type": type,
175220
"title": title,
176221
"body": body,
177222
"applies_to_part": applies_to_part,
@@ -193,37 +238,35 @@ async def handle_call_tool(
193238
text=f"Object created successfully: {response.json()}"
194239
)
195240
]
196-
elif name == "update_object":
197-
# Update mandatory fields
241+
elif name == "update_work":
198242
if not arguments:
199243
raise ValueError("Missing arguments")
200244

201245
id = arguments.get("id")
202246
if not id:
203247
raise ValueError("Missing id parameter")
204248

205-
object_type = arguments.get("type")
206-
if not object_type:
249+
type = arguments.get("type")
250+
if not type:
207251
raise ValueError("Missing type parameter")
208252

209-
# Update title and body
210253
title = arguments.get("title")
211254
body = arguments.get("body")
212-
213-
# Build request payload with only the fields that have values
214-
update_payload = {"id": id, "type": object_type}
255+
sprint = arguments.get("sprint")
256+
257+
payload = {"id": id, "type": type}
215258
if title:
216-
update_payload["title"] = title
259+
payload["title"] = title
217260
if body:
218-
update_payload["body"] = body
261+
payload["body"] = body
262+
if sprint:
263+
payload["sprint"] = sprint
219264

220-
# Make devrev request to update the object
221265
response = make_devrev_request(
222266
"works.update",
223-
update_payload
267+
payload
224268
)
225269

226-
# Check if the request was successful
227270
if response.status_code != 200:
228271
error_text = response.text
229272
return [
@@ -239,6 +282,142 @@ async def handle_call_tool(
239282
text=f"Object updated successfully: {id}"
240283
)
241284
]
285+
elif name == "get_part":
286+
if not arguments:
287+
raise ValueError("Missing arguments")
288+
289+
id = arguments.get("id")
290+
if not id:
291+
raise ValueError("Missing id parameter")
292+
293+
response = make_devrev_request(
294+
"parts.get",
295+
{
296+
"id": id
297+
}
298+
)
299+
300+
if response.status_code != 200:
301+
error_text = response.text
302+
return [
303+
types.TextContent(
304+
type="text",
305+
text=f"Get part failed with status {response.status_code}: {error_text}"
306+
)
307+
]
308+
309+
return [
310+
types.TextContent(
311+
type="text",
312+
text=f"Part information for '{id}':\n{response.json()}"
313+
)
314+
]
315+
elif name == "create_part":
316+
if not arguments:
317+
raise ValueError("Missing arguments")
318+
319+
payload = {}
320+
321+
type = arguments.get("type")
322+
if not type:
323+
raise ValueError("Missing type parameter")
324+
payload["type"] = type
325+
326+
part_name = arguments.get("name")
327+
if not part_name:
328+
raise ValueError("Missing name parameter")
329+
payload["name"] = part_name
330+
331+
owned_by = arguments.get("owned_by")
332+
if not owned_by:
333+
raise ValueError("Missing owned_by parameter")
334+
payload["owned_by"] = owned_by
335+
336+
parent_part = arguments.get("parent_part")
337+
if not parent_part:
338+
raise ValueError("Missing parent_part parameter")
339+
payload["parent_part"] = parent_part
340+
341+
description = arguments.get("description")
342+
if description:
343+
payload["description"] = description
344+
345+
response = make_devrev_request(
346+
"parts.create",
347+
payload
348+
)
349+
350+
if response.status_code != 201:
351+
error_text = response.text
352+
return [
353+
types.TextContent(
354+
type="text",
355+
text=f"Create part failed with status {response.status_code}: {error_text}"
356+
)
357+
]
358+
359+
return [
360+
types.TextContent(
361+
type="text",
362+
text=f"Part created successfully: {response.json()}"
363+
)
364+
]
365+
elif name == "update_part":
366+
if not arguments:
367+
raise ValueError("Missing arguments")
368+
369+
payload = {}
370+
371+
id = arguments.get("id")
372+
if not id:
373+
raise ValueError("Missing id parameter")
374+
payload["id"] = id
375+
376+
type = arguments.get("type")
377+
if not type:
378+
raise ValueError("Missing type parameter")
379+
payload["type"] = type
380+
381+
part_name = arguments.get("name")
382+
if part_name:
383+
payload["name"] = part_name
384+
385+
owned_by = arguments.get("owned_by")
386+
if owned_by:
387+
payload["owned_by"] = owned_by
388+
389+
description = arguments.get("description")
390+
if description:
391+
payload["description"] = description
392+
393+
target_close_date = arguments.get("target_close_date")
394+
if target_close_date:
395+
payload["target_close_date"] = target_close_date
396+
397+
target_start_date = arguments.get("target_start_date")
398+
if target_start_date:
399+
payload["target_start_date"] = target_start_date
400+
401+
response = make_devrev_request(
402+
"parts.update",
403+
payload
404+
)
405+
406+
if response.status_code != 200:
407+
error_text = response.text
408+
return [
409+
types.TextContent(
410+
type="text",
411+
text=f"Update part failed with status {response.status_code}: {error_text}"
412+
)
413+
]
414+
415+
return [
416+
types.TextContent(
417+
type="text",
418+
text=f"Part updated successfully: {id}"
419+
)
420+
]
242421
else:
243422
raise ValueError(f"Unknown tool: {name}")
244423

0 commit comments

Comments
 (0)