devrev-Python-SDK 3.0.4__py3-none-any.whl → 3.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- devrev/client.py +0 -16
- devrev/models/__init__.py +0 -11
- devrev/models/works.py +0 -42
- devrev/services/__init__.py +0 -4
- devrev/services/works.py +0 -20
- devrev_mcp/server.py +1 -0
- devrev_mcp/tools/webhooks.py +153 -0
- devrev_mcp/utils/don_id.py +2 -0
- {devrev_python_sdk-3.0.4.dist-info → devrev_python_sdk-3.1.1.dist-info}/METADATA +4 -16
- {devrev_python_sdk-3.0.4.dist-info → devrev_python_sdk-3.1.1.dist-info}/RECORD +12 -13
- {devrev_python_sdk-3.0.4.dist-info → devrev_python_sdk-3.1.1.dist-info}/WHEEL +1 -1
- devrev/models/survey_responses.py +0 -77
- devrev/services/survey_responses.py +0 -122
- {devrev_python_sdk-3.0.4.dist-info → devrev_python_sdk-3.1.1.dist-info}/entry_points.txt +0 -0
devrev/client.py
CHANGED
|
@@ -42,10 +42,6 @@ from devrev.services.rev_orgs import AsyncRevOrgsService, RevOrgsService
|
|
|
42
42
|
from devrev.services.rev_users import AsyncRevUsersService, RevUsersService
|
|
43
43
|
from devrev.services.search import AsyncSearchService, SearchService
|
|
44
44
|
from devrev.services.slas import AsyncSlasService, SlasService
|
|
45
|
-
from devrev.services.survey_responses import (
|
|
46
|
-
AsyncSurveyResponsesService,
|
|
47
|
-
SurveyResponsesService,
|
|
48
|
-
)
|
|
49
45
|
from devrev.services.tags import AsyncTagsService, TagsService
|
|
50
46
|
from devrev.services.timeline_entries import (
|
|
51
47
|
AsyncTimelineEntriesService,
|
|
@@ -168,7 +164,6 @@ class DevRevClient:
|
|
|
168
164
|
self._rev_orgs = RevOrgsService(self._http)
|
|
169
165
|
self._rev_users = RevUsersService(self._http)
|
|
170
166
|
self._slas = SlasService(self._http)
|
|
171
|
-
self._survey_responses = SurveyResponsesService(self._http)
|
|
172
167
|
self._tags = TagsService(self._http)
|
|
173
168
|
self._timeline_entries = TimelineEntriesService(self._http)
|
|
174
169
|
self._webhooks = WebhooksService(self._http)
|
|
@@ -264,11 +259,6 @@ class DevRevClient:
|
|
|
264
259
|
"""Access the SLAs service."""
|
|
265
260
|
return self._slas
|
|
266
261
|
|
|
267
|
-
@property
|
|
268
|
-
def survey_responses(self) -> SurveyResponsesService:
|
|
269
|
-
"""Access the Survey Responses service."""
|
|
270
|
-
return self._survey_responses
|
|
271
|
-
|
|
272
262
|
@property
|
|
273
263
|
def tags(self) -> TagsService:
|
|
274
264
|
"""Access the Tags service."""
|
|
@@ -502,7 +492,6 @@ class AsyncDevRevClient:
|
|
|
502
492
|
self._rev_orgs = AsyncRevOrgsService(self._http)
|
|
503
493
|
self._rev_users = AsyncRevUsersService(self._http)
|
|
504
494
|
self._slas = AsyncSlasService(self._http)
|
|
505
|
-
self._survey_responses = AsyncSurveyResponsesService(self._http)
|
|
506
495
|
self._tags = AsyncTagsService(self._http)
|
|
507
496
|
self._timeline_entries = AsyncTimelineEntriesService(self._http)
|
|
508
497
|
self._webhooks = AsyncWebhooksService(self._http)
|
|
@@ -598,11 +587,6 @@ class AsyncDevRevClient:
|
|
|
598
587
|
"""Access the SLAs service."""
|
|
599
588
|
return self._slas
|
|
600
589
|
|
|
601
|
-
@property
|
|
602
|
-
def survey_responses(self) -> AsyncSurveyResponsesService:
|
|
603
|
-
"""Access the Survey Responses service."""
|
|
604
|
-
return self._survey_responses
|
|
605
|
-
|
|
606
590
|
@property
|
|
607
591
|
def tags(self) -> AsyncTagsService:
|
|
608
592
|
"""Access the Tags service."""
|
devrev/models/__init__.py
CHANGED
|
@@ -328,12 +328,6 @@ from devrev.models.slas import (
|
|
|
328
328
|
SlasUpdateResponse,
|
|
329
329
|
SlaTrackerStatus,
|
|
330
330
|
)
|
|
331
|
-
from devrev.models.survey_responses import (
|
|
332
|
-
SurveyResponse,
|
|
333
|
-
SurveyResponsesListMode,
|
|
334
|
-
SurveyResponsesListRequest,
|
|
335
|
-
SurveyResponsesListResponse,
|
|
336
|
-
)
|
|
337
331
|
from devrev.models.sync import (
|
|
338
332
|
ExternalRef,
|
|
339
333
|
StagedInfo,
|
|
@@ -705,11 +699,6 @@ __all__ = [
|
|
|
705
699
|
"QuestionAnswersUpdateRequest",
|
|
706
700
|
"QuestionAnswersUpdateResponse",
|
|
707
701
|
"QuestionAnswersDeleteRequest",
|
|
708
|
-
# Survey Responses
|
|
709
|
-
"SurveyResponse",
|
|
710
|
-
"SurveyResponsesListMode",
|
|
711
|
-
"SurveyResponsesListRequest",
|
|
712
|
-
"SurveyResponsesListResponse",
|
|
713
702
|
# Recommendations
|
|
714
703
|
"MessageRole",
|
|
715
704
|
"ChatMessage",
|
devrev/models/works.py
CHANGED
|
@@ -96,48 +96,6 @@ class Work(DevRevResponseModel):
|
|
|
96
96
|
actual_close_date: datetime | None = Field(default=None, description="Actual close date")
|
|
97
97
|
custom_fields: dict[str, Any] | None = Field(default=None, description="Custom fields")
|
|
98
98
|
external_ref: str | None = Field(default=None, description="External reference")
|
|
99
|
-
account: dict[str, Any] | str | None = Field(
|
|
100
|
-
default=None,
|
|
101
|
-
description="Account associated with the ticket/work, when returned by the API",
|
|
102
|
-
)
|
|
103
|
-
rev_org: dict[str, Any] | str | None = Field(
|
|
104
|
-
default=None,
|
|
105
|
-
description="Rev organization associated with the ticket/work",
|
|
106
|
-
)
|
|
107
|
-
sentiment: dict[str, Any] | str | None = Field(
|
|
108
|
-
default=None,
|
|
109
|
-
description="Current ticket sentiment, when returned by the API",
|
|
110
|
-
)
|
|
111
|
-
sentiment_summary: dict[str, Any] | str | None = Field(
|
|
112
|
-
default=None,
|
|
113
|
-
description="Structured or textual sentiment summary",
|
|
114
|
-
)
|
|
115
|
-
sentiment_modified_date: datetime | None = Field(
|
|
116
|
-
default=None,
|
|
117
|
-
description="When ticket sentiment was last modified",
|
|
118
|
-
)
|
|
119
|
-
sla_summary: dict[str, Any] | None = Field(default=None, description="Ticket SLA summary")
|
|
120
|
-
needs_response: bool | None = Field(
|
|
121
|
-
default=None,
|
|
122
|
-
description="Whether the ticket needs a customer response",
|
|
123
|
-
)
|
|
124
|
-
channels: list[dict[str, Any] | str] | None = Field(
|
|
125
|
-
default=None,
|
|
126
|
-
description="Channels associated with the ticket",
|
|
127
|
-
)
|
|
128
|
-
source_channel: dict[str, Any] | str | None = Field(
|
|
129
|
-
default=None,
|
|
130
|
-
description="Source channel that created the ticket",
|
|
131
|
-
)
|
|
132
|
-
group: dict[str, Any] | str | None = Field(
|
|
133
|
-
default=None,
|
|
134
|
-
description="Group associated with the work item",
|
|
135
|
-
)
|
|
136
|
-
is_frozen: bool | None = Field(default=None, description="Whether the work item is frozen")
|
|
137
|
-
visibility: dict[str, Any] | str | None = Field(
|
|
138
|
-
default=None,
|
|
139
|
-
description="Work item visibility, when returned by the API",
|
|
140
|
-
)
|
|
141
99
|
|
|
142
100
|
|
|
143
101
|
class WorkSummary(DevRevResponseModel):
|
devrev/services/__init__.py
CHANGED
|
@@ -38,7 +38,6 @@ from devrev.services.rev_orgs import AsyncRevOrgsService, RevOrgsService
|
|
|
38
38
|
from devrev.services.rev_users import AsyncRevUsersService, RevUsersService
|
|
39
39
|
from devrev.services.search import AsyncSearchService, SearchService
|
|
40
40
|
from devrev.services.slas import AsyncSlasService, SlasService
|
|
41
|
-
from devrev.services.survey_responses import AsyncSurveyResponsesService, SurveyResponsesService
|
|
42
41
|
from devrev.services.tags import AsyncTagsService, TagsService
|
|
43
42
|
from devrev.services.timeline_entries import (
|
|
44
43
|
AsyncTimelineEntriesService,
|
|
@@ -113,9 +112,6 @@ __all__ = [
|
|
|
113
112
|
# SLAs
|
|
114
113
|
"SlasService",
|
|
115
114
|
"AsyncSlasService",
|
|
116
|
-
# Survey Responses
|
|
117
|
-
"SurveyResponsesService",
|
|
118
|
-
"AsyncSurveyResponsesService",
|
|
119
115
|
# Tags
|
|
120
116
|
"TagsService",
|
|
121
117
|
"AsyncTagsService",
|
devrev/services/works.py
CHANGED
|
@@ -147,21 +147,6 @@ class WorksService(BaseService):
|
|
|
147
147
|
response = self._post("/works.get", request, WorksGetResponse)
|
|
148
148
|
return response.work
|
|
149
149
|
|
|
150
|
-
def get_raw(self, id: str) -> dict[str, Any]:
|
|
151
|
-
"""Get the raw API payload for a work item.
|
|
152
|
-
|
|
153
|
-
Use this supported escape hatch when the DevRev API returns fields that
|
|
154
|
-
are not yet represented on the typed :class:`Work` model.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
id: Work item ID
|
|
158
|
-
|
|
159
|
-
Returns:
|
|
160
|
-
Raw ``/works.get`` response payload.
|
|
161
|
-
"""
|
|
162
|
-
request = WorksGetRequest(id=id)
|
|
163
|
-
return self._post("/works.get", request)
|
|
164
|
-
|
|
165
150
|
def list(
|
|
166
151
|
self,
|
|
167
152
|
*,
|
|
@@ -454,11 +439,6 @@ class AsyncWorksService(AsyncBaseService):
|
|
|
454
439
|
response = await self._post("/works.get", request, WorksGetResponse)
|
|
455
440
|
return response.work
|
|
456
441
|
|
|
457
|
-
async def get_raw(self, id: str) -> dict[str, Any]:
|
|
458
|
-
"""Get the raw API payload for a work item."""
|
|
459
|
-
request = WorksGetRequest(id=id)
|
|
460
|
-
return await self._post("/works.get", request)
|
|
461
|
-
|
|
462
442
|
async def list(
|
|
463
443
|
self,
|
|
464
444
|
*,
|
devrev_mcp/server.py
CHANGED
|
@@ -249,6 +249,7 @@ from devrev_mcp.tools import slas as _slas_tools # noqa: E402, F401
|
|
|
249
249
|
from devrev_mcp.tools import tags as _tags_tools # noqa: E402, F401
|
|
250
250
|
from devrev_mcp.tools import timeline as _timeline_tools # noqa: E402, F401
|
|
251
251
|
from devrev_mcp.tools import users as _users_tools # noqa: E402, F401
|
|
252
|
+
from devrev_mcp.tools import webhooks as _webhooks_tools # noqa: E402, F401
|
|
252
253
|
from devrev_mcp.tools import works as _works_tools # noqa: E402, F401
|
|
253
254
|
|
|
254
255
|
# Beta tools (only if beta tools are enabled)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""MCP tools for DevRev webhook operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from mcp.server.fastmcp import Context
|
|
9
|
+
|
|
10
|
+
from devrev.exceptions import DevRevError
|
|
11
|
+
from devrev.models.webhooks import (
|
|
12
|
+
WebhooksCreateRequest,
|
|
13
|
+
WebhooksDeleteRequest,
|
|
14
|
+
WebhooksGetRequest,
|
|
15
|
+
WebhooksListRequest,
|
|
16
|
+
WebhookStatus,
|
|
17
|
+
WebhooksUpdateRequest,
|
|
18
|
+
)
|
|
19
|
+
from devrev_mcp.server import _config, mcp
|
|
20
|
+
from devrev_mcp.utils.don_id import validate_don_id
|
|
21
|
+
from devrev_mcp.utils.errors import format_devrev_error
|
|
22
|
+
from devrev_mcp.utils.formatting import serialize_model, serialize_models
|
|
23
|
+
from devrev_mcp.utils.pagination import clamp_page_size
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@mcp.tool()
|
|
29
|
+
async def devrev_webhooks_list(
|
|
30
|
+
ctx: Context[Any, Any, Any],
|
|
31
|
+
cursor: str | None = None,
|
|
32
|
+
limit: int | None = None,
|
|
33
|
+
) -> dict[str, Any]:
|
|
34
|
+
"""List DevRev webhooks.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
cursor: Pagination cursor from a previous response.
|
|
38
|
+
limit: Maximum number of items to return (default: 25, max: 100).
|
|
39
|
+
"""
|
|
40
|
+
app = ctx.request_context.lifespan_context
|
|
41
|
+
try:
|
|
42
|
+
request = WebhooksListRequest(
|
|
43
|
+
cursor=cursor,
|
|
44
|
+
limit=clamp_page_size(
|
|
45
|
+
limit, default=app.config.default_page_size, maximum=app.config.max_page_size
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
webhooks = await app.get_client().webhooks.list(request)
|
|
49
|
+
items = serialize_models(list(webhooks))
|
|
50
|
+
return {"count": len(items), "webhooks": items}
|
|
51
|
+
except DevRevError as e:
|
|
52
|
+
raise RuntimeError(format_devrev_error(e)) from e
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@mcp.tool()
|
|
56
|
+
async def devrev_webhooks_get(
|
|
57
|
+
ctx: Context[Any, Any, Any],
|
|
58
|
+
id: str,
|
|
59
|
+
) -> dict[str, Any]:
|
|
60
|
+
"""Get a DevRev webhook by ID.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
id: Webhook ID (e.g., "don:integration:dvrv-us-1:devo/1:webhook/123").
|
|
64
|
+
"""
|
|
65
|
+
validate_don_id(id, "webhook", "devrev_webhooks_get")
|
|
66
|
+
app = ctx.request_context.lifespan_context
|
|
67
|
+
try:
|
|
68
|
+
request = WebhooksGetRequest(id=id)
|
|
69
|
+
webhook = await app.get_client().webhooks.get(request)
|
|
70
|
+
return serialize_model(webhook)
|
|
71
|
+
except DevRevError as e:
|
|
72
|
+
raise RuntimeError(format_devrev_error(e)) from e
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Destructive tools (only registered when enabled)
|
|
76
|
+
if _config.enable_destructive_tools:
|
|
77
|
+
|
|
78
|
+
@mcp.tool()
|
|
79
|
+
async def devrev_webhooks_create(
|
|
80
|
+
ctx: Context[Any, Any, Any],
|
|
81
|
+
url: str,
|
|
82
|
+
event_types: list[str] | None = None,
|
|
83
|
+
secret: str | None = None,
|
|
84
|
+
) -> dict[str, Any]:
|
|
85
|
+
"""Create a new DevRev webhook.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
url: Target URL that will receive webhook event POSTs.
|
|
89
|
+
event_types: Event types to subscribe to (default: all events).
|
|
90
|
+
secret: Shared secret used to sign and verify webhook payloads.
|
|
91
|
+
"""
|
|
92
|
+
app = ctx.request_context.lifespan_context
|
|
93
|
+
try:
|
|
94
|
+
request = WebhooksCreateRequest(url=url, event_types=event_types, secret=secret)
|
|
95
|
+
webhook = await app.get_client().webhooks.create(request)
|
|
96
|
+
return serialize_model(webhook)
|
|
97
|
+
except DevRevError as e:
|
|
98
|
+
raise RuntimeError(format_devrev_error(e)) from e
|
|
99
|
+
|
|
100
|
+
@mcp.tool()
|
|
101
|
+
async def devrev_webhooks_update(
|
|
102
|
+
ctx: Context[Any, Any, Any],
|
|
103
|
+
id: str,
|
|
104
|
+
url: str | None = None,
|
|
105
|
+
event_types: list[str] | None = None,
|
|
106
|
+
status: str | None = None,
|
|
107
|
+
) -> dict[str, Any]:
|
|
108
|
+
"""Update a DevRev webhook.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
id: Webhook ID to update.
|
|
112
|
+
url: New target URL.
|
|
113
|
+
event_types: New list of event types to subscribe to.
|
|
114
|
+
status: New status (active, inactive, unverified).
|
|
115
|
+
"""
|
|
116
|
+
validate_don_id(id, "webhook", "devrev_webhooks_update")
|
|
117
|
+
app = ctx.request_context.lifespan_context
|
|
118
|
+
try:
|
|
119
|
+
resolved_status: WebhookStatus | None = None
|
|
120
|
+
if status is not None:
|
|
121
|
+
try:
|
|
122
|
+
resolved_status = WebhookStatus(status.lower())
|
|
123
|
+
except ValueError as e:
|
|
124
|
+
raise RuntimeError(
|
|
125
|
+
f"Invalid webhook status '{status}'. "
|
|
126
|
+
"Valid values: active, inactive, unverified."
|
|
127
|
+
) from e
|
|
128
|
+
request = WebhooksUpdateRequest(
|
|
129
|
+
id=id, url=url, event_types=event_types, status=resolved_status
|
|
130
|
+
)
|
|
131
|
+
webhook = await app.get_client().webhooks.update(request)
|
|
132
|
+
return serialize_model(webhook)
|
|
133
|
+
except DevRevError as e:
|
|
134
|
+
raise RuntimeError(format_devrev_error(e)) from e
|
|
135
|
+
|
|
136
|
+
@mcp.tool()
|
|
137
|
+
async def devrev_webhooks_delete(
|
|
138
|
+
ctx: Context[Any, Any, Any],
|
|
139
|
+
id: str,
|
|
140
|
+
) -> dict[str, Any]:
|
|
141
|
+
"""Delete a DevRev webhook.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
id: Webhook ID to delete.
|
|
145
|
+
"""
|
|
146
|
+
validate_don_id(id, "webhook", "devrev_webhooks_delete")
|
|
147
|
+
app = ctx.request_context.lifespan_context
|
|
148
|
+
try:
|
|
149
|
+
request = WebhooksDeleteRequest(id=id)
|
|
150
|
+
await app.get_client().webhooks.delete(request)
|
|
151
|
+
return {"deleted": True, "id": id}
|
|
152
|
+
except DevRevError as e:
|
|
153
|
+
raise RuntimeError(format_devrev_error(e)) from e
|
devrev_mcp/utils/don_id.py
CHANGED
|
@@ -35,6 +35,7 @@ DON_TYPE_MAP: dict[str, str] = {
|
|
|
35
35
|
"qa": "question_answer",
|
|
36
36
|
"question_answer": "question_answer",
|
|
37
37
|
"timeline_entry": "timeline_entry",
|
|
38
|
+
"webhook": "webhook",
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
TOOL_SUGGESTIONS: dict[str, str] = {
|
|
@@ -57,6 +58,7 @@ TOOL_SUGGESTIONS: dict[str, str] = {
|
|
|
57
58
|
"qa": "devrev_question_answers_get",
|
|
58
59
|
"question_answer": "devrev_question_answers_get",
|
|
59
60
|
"timeline_entry": "devrev_timeline_get",
|
|
61
|
+
"webhook": "devrev_webhooks_get",
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devrev-Python-SDK
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.1
|
|
4
4
|
Summary: A modern, type-safe Python SDK for the DevRev API
|
|
5
5
|
Project-URL: Homepage, https://github.com/mgmonteleone/py-dev-rev
|
|
6
6
|
Project-URL: Documentation, https://github.com/mgmonteleone/py-dev-rev
|
|
@@ -75,7 +75,7 @@ Description-Content-Type: text/markdown
|
|
|
75
75
|
|
|
76
76
|
# py-devrev
|
|
77
77
|
|
|
78
|
-
A modern, type-safe Python SDK for the DevRev API.
|
|
78
|
+
A modern, type-safe Python SDK and MCP for the DevRev API.
|
|
79
79
|
|
|
80
80
|
[](https://www.augmentcode.com/)
|
|
81
81
|
[](https://pypi.org/project/devrev-Python-SDK/)
|
|
@@ -159,7 +159,7 @@ This SDK is generated and maintained from the official DevRev OpenAPI specificat
|
|
|
159
159
|
|
|
160
160
|
### MCP Server (AI Integration)
|
|
161
161
|
|
|
162
|
-
- ✅ **
|
|
162
|
+
- ✅ **83+ MCP Tools** — Full CRUD for tickets, accounts, users, articles, webhooks, and all DevRev resources
|
|
163
163
|
- ✅ **7 Resource Templates** — `devrev://` URI scheme for AI-accessible data browsing
|
|
164
164
|
- ✅ **8 Workflow Prompts** — Pre-built triage, escalation, investigation, and reporting workflows
|
|
165
165
|
- ✅ **3 Transport Modes** — stdio (local), Streamable HTTP (production), SSE (legacy)
|
|
@@ -215,17 +215,6 @@ for account in accounts:
|
|
|
215
215
|
work = client.works.get(id="don:core:...")
|
|
216
216
|
print(f"Work: {work.title} - Status: {work.stage.name}")
|
|
217
217
|
|
|
218
|
-
# Ticket integrations can read optional account/sentiment/SLA fields
|
|
219
|
-
ticket = client.works.get(id="don:core:dvrv-us-1:devo/1:ticket/123")
|
|
220
|
-
print(ticket.rev_org, ticket.account, ticket.needs_response)
|
|
221
|
-
|
|
222
|
-
# Use a supported raw escape hatch when the API returns newer fields
|
|
223
|
-
raw_ticket = client.works.get_raw(id="don:core:dvrv-us-1:devo/1:ticket/123")
|
|
224
|
-
print(raw_ticket["work"].keys())
|
|
225
|
-
|
|
226
|
-
# List survey/CSAT responses for a ticket without using private _http
|
|
227
|
-
survey_responses = client.survey_responses.list(object_id=ticket.id)
|
|
228
|
-
|
|
229
218
|
# Create a new ticket
|
|
230
219
|
ticket = client.works.create(
|
|
231
220
|
title="Bug: Login page not loading",
|
|
@@ -308,7 +297,6 @@ The SDK provides complete coverage of all 209 DevRev public API endpoints, organ
|
|
|
308
297
|
|---------|-----------|-------------|
|
|
309
298
|
| **Articles** | 5 | Knowledge base articles |
|
|
310
299
|
| **Conversations** | 5 | Customer conversations |
|
|
311
|
-
| **Survey Responses** | 1 | Survey and CSAT response listing |
|
|
312
300
|
| **Timeline Entries** | 5 | Activity timeline management |
|
|
313
301
|
| **Tags** | 5 | Tagging and categorization |
|
|
314
302
|
|
|
@@ -737,7 +725,7 @@ The DevRev MCP Server exposes the full DevRev platform as [Model Context Protoco
|
|
|
737
725
|
|
|
738
726
|
### MCP Server Features
|
|
739
727
|
|
|
740
|
-
- **
|
|
728
|
+
- **83+ MCP Tools** — Full CRUD for tickets, accounts, users, conversations, articles, parts, tags, groups, timeline, links, SLAs, webhooks, plus beta tools (search, recommendations, incidents, engagements)
|
|
741
729
|
- **7 Resource Templates** — `devrev://` URI scheme for browsing tickets, accounts, articles, users, parts, conversations
|
|
742
730
|
- **8 Workflow Prompts** — Triage, draft response, escalate, summarize account, investigate, weekly report, find similar, onboard customer
|
|
743
731
|
- **3 Transports** — stdio (local dev), Streamable HTTP (production), SSE (legacy)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
devrev/__init__.py,sha256=iBKIRvS10mFWpBgnMjQQiQR04rgb8ZyXVk1-7cYJikI,1555
|
|
2
|
-
devrev/client.py,sha256
|
|
2
|
+
devrev/client.py,sha256=FXEadZd0NzBUzXXY60e4qnN2Osv5vHE_9rj8nPpVPxI,26458
|
|
3
3
|
devrev/config.py,sha256=BKFhD8lMJvq-I9kpchiA50fYefVFhnNwcK9ReFogh4k,8122
|
|
4
4
|
devrev/exceptions.py,sha256=Bs0pYgOgG26cyI785rdJeTR72gM-rXHaCEt24HAJzEk,7540
|
|
5
5
|
devrev/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
|
-
devrev/models/__init__.py,sha256=
|
|
6
|
+
devrev/models/__init__.py,sha256=D9J3kwZrAUJijh4wuLbfvb7WbokL84_dRg93OxtT_k4,24033
|
|
7
7
|
devrev/models/accounts.py,sha256=LrXAit2gIr34XyRO1T0fL6EwdXHsfJOyXwcegPnKnaE,6974
|
|
8
8
|
devrev/models/articles.py,sha256=DgrZC_wtLsXIfilAvHJBoQkhbGDg3Vx5UX7h9MD63xA,16732
|
|
9
9
|
devrev/models/artifacts.py,sha256=3iA58RyVlX0S0U5iID3Cnoc096Y70QfGqYfKEhxfE9g,4375
|
|
@@ -25,7 +25,6 @@ devrev/models/rev_orgs.py,sha256=tVJiPezfBe6MN-jgfQtp0Efh6RkLIJs4LAXutuWbwp8,621
|
|
|
25
25
|
devrev/models/rev_users.py,sha256=8psDrbQQQkG2rM7JRfRZYgyxv2FFvhSxXaDMZy0Y8_4,10136
|
|
26
26
|
devrev/models/search.py,sha256=_5OhgVz1yaqK00-rSJrG3xacp22qByYQq5HSnS1inV8,4650
|
|
27
27
|
devrev/models/slas.py,sha256=U7nForRREIBV-KpTxrDsPPpyzCNwb7yHeeB94pk7Mfw,3272
|
|
28
|
-
devrev/models/survey_responses.py,sha256=fHJoW4jvGHJM7W2guPzEJ1G-ErgH3gXOYqJfUc4PwdQ,3484
|
|
29
28
|
devrev/models/sync.py,sha256=_Wef7zgI1aYG9GBWXVTO3_oktY0SIdMuEbwLcGTJTUM,5407
|
|
30
29
|
devrev/models/tags.py,sha256=5IDRerA3bUxSRVcGjvwCheRzOpgjI4AU8Rh1LYNT6tk,2473
|
|
31
30
|
devrev/models/tasks.py,sha256=OLffCAEegv-8z0oXDCGXB4KiXuobizrOZ-RjILGs6f4,5055
|
|
@@ -35,8 +34,8 @@ devrev/models/track_events.py,sha256=bb9belz1W3XvrUEgp9mGdOKELhRcWL-59wS9cIa1geY
|
|
|
35
34
|
devrev/models/uoms.py,sha256=bxjk2YhJ3qrX6C52xuokgLCHD3K4-8DRDeu2QwjnK4E,5700
|
|
36
35
|
devrev/models/webhooks.py,sha256=DzrLJiW1GYWvfaB-aVPcYCf4XkEGHz3dkyzbBKKdhFI,3655
|
|
37
36
|
devrev/models/widgets.py,sha256=7WWN17_ySqnu1pjYSIS5B8J5dEaqkoB8X8GWzvI0ZYc,4577
|
|
38
|
-
devrev/models/works.py,sha256=
|
|
39
|
-
devrev/services/__init__.py,sha256=
|
|
37
|
+
devrev/models/works.py,sha256=sHZ_eJ6eCadWxPkM6HGcmbkiGRDYT2RnH3QZRO4T3tY,11198
|
|
38
|
+
devrev/services/__init__.py,sha256=LWy_0xGNH692d6Wc4S4E9v6AoxjgfExFMzFmbrL2NTY,3975
|
|
40
39
|
devrev/services/_pagination.py,sha256=J6AXu1P3iNRyC8_nH709Z8nOLhHeSctOLRmY_eYI6QA,2100
|
|
41
40
|
devrev/services/accounts.py,sha256=X7FgcODex0XKLiV_VvXKDl2Jm8XsNpn9qp40oRjZqME,9704
|
|
42
41
|
devrev/services/articles.py,sha256=xOpJOG9f29a5W3IFROplL1a9eJdGucbUFkK-MAIIYlc,41637
|
|
@@ -59,13 +58,12 @@ devrev/services/rev_orgs.py,sha256=I7oiZZNKJ1PRApUyfjFab2ke-Ox39JBGambzvAMusKY,7
|
|
|
59
58
|
devrev/services/rev_users.py,sha256=-QfGNUzz6MQbG4n51F5WzjRxcdIGrrHB_PzjUX7rI38,15472
|
|
60
59
|
devrev/services/search.py,sha256=qxXtYRgGCxkw35CazNxIkCUWsSuv1NCWxr6nVTyKFvQ,12341
|
|
61
60
|
devrev/services/slas.py,sha256=9n6mdZ8E9vDrO4CeV4snc6IwyjDdoTQjQpjFw1oT4ag,2786
|
|
62
|
-
devrev/services/survey_responses.py,sha256=vSmlMIQnKQxc10-3GNBM21NMuRZ9wk73s_8yIMKjKu8,4647
|
|
63
61
|
devrev/services/tags.py,sha256=1Xmq6VFIvL2OTCJ48PeLkTcZm2aa2BJ7OjbZVHd8rbU,2642
|
|
64
62
|
devrev/services/timeline_entries.py,sha256=6nbVPcWlE3-ohLhvkM421eqYS5ztKtHmGsPek5WoT_Q,3362
|
|
65
63
|
devrev/services/track_events.py,sha256=lI4wXkWu3uUuXtuRg1MGNkTZ7B0Lc1PjM8Kw-6sUnWc,1439
|
|
66
64
|
devrev/services/uoms.py,sha256=AA3ymoHj24FIbsZpYC4tg2elSdQ3iINTVOz7MraZcj8,8163
|
|
67
65
|
devrev/services/webhooks.py,sha256=-TSkcaya1y48WB24_vHd-bqO5xSqxRsCLiilncNzQZU,3917
|
|
68
|
-
devrev/services/works.py,sha256=
|
|
66
|
+
devrev/services/works.py,sha256=hAK7ZUqLph5Db15p4RDbeRFLZUALAF5tCFOQSYytj_g,20456
|
|
69
67
|
devrev/utils/__init__.py,sha256=NOrbpkjDVLH8n9xf-xpZJiIIa_GVI_6vqTm3E8L3Udw,857
|
|
70
68
|
devrev/utils/content_converter.py,sha256=emRBLiVoOfDGpPDzrMRnqQr4-QkqN13OdWlYOyU_LCg,28141
|
|
71
69
|
devrev/utils/deprecation.py,sha256=7qB2Dx531oP7mNi7q2txOYsOKC9YwdHqlKPMFHOW9Ws,1275
|
|
@@ -75,7 +73,7 @@ devrev/utils/pagination.py,sha256=zsAZdGw2eGs2p5XEFX-Dng3zjsP-ERsUqQn7PWmI9Oc,48
|
|
|
75
73
|
devrev_mcp/__init__.py,sha256=-VAlWWe5rx2UqfOUK8L2Evr28FEfapEmMKgYpCbVxjQ,216
|
|
76
74
|
devrev_mcp/__main__.py,sha256=PfuBYR0I4yIj1CqTikFBcqB_TNo34R1I5_8bv0D2ekc,1521
|
|
77
75
|
devrev_mcp/config.py,sha256=4GllGfYM6k7OixEKfR9JH_TE1F7LFeCbDo1bLEC9ACA,4002
|
|
78
|
-
devrev_mcp/server.py,sha256=
|
|
76
|
+
devrev_mcp/server.py,sha256=4Zhkom-2LQ0qo91o70Uif9U0uSqwFAZ9Xip74ihhmxw,18674
|
|
79
77
|
devrev_mcp/middleware/__init__.py,sha256=QphqWGURVzKXWJ0oLJ5Qub6V33z5YyQwN5r6Gue6hd8,52
|
|
80
78
|
devrev_mcp/middleware/audit.py,sha256=PRnyNCobXSJnSqVh4Z5K8b7jauNOTMSiMEIjKcd1s2A,13459
|
|
81
79
|
devrev_mcp/middleware/auth.py,sha256=wgdYZEWxBIAj7XAc8c95ct9aZ1aUQ6s29SiWuUW6IiU,16214
|
|
@@ -113,13 +111,14 @@ devrev_mcp/tools/slas.py,sha256=q7eYC7nMxCGoHMDp1QbtCgSWiG4oHk78sydPtYwsh10,4902
|
|
|
113
111
|
devrev_mcp/tools/tags.py,sha256=zj_my9CkPQu8_TTOqszfLULPNgxm83kHux-rWFxHvXQ,4711
|
|
114
112
|
devrev_mcp/tools/timeline.py,sha256=p7P699Oq205jrzCVyYS6v4bXHEQizircHVyS5JE-GJ4,5326
|
|
115
113
|
devrev_mcp/tools/users.py,sha256=7CtrxjgxniEb5-DaPh0rwL680a2fDFLJSIU5huuTjYE,4896
|
|
114
|
+
devrev_mcp/tools/webhooks.py,sha256=UdoKxZprZygVopqYYf02RF84HVCL-gJBpTNm0jrw_QI,5166
|
|
116
115
|
devrev_mcp/tools/works.py,sha256=IFaIXYR4j_2pZ39ykZgdHdo5nCk8OfgWU5bxBdHwp5E,15241
|
|
117
116
|
devrev_mcp/utils/__init__.py,sha256=2_5b1KC5kjoUqFY1ZSdB2Tefd2ekjbZ-eHyFWBKI-0A,49
|
|
118
|
-
devrev_mcp/utils/don_id.py,sha256=
|
|
117
|
+
devrev_mcp/utils/don_id.py,sha256=jbVKXW_BeKjPHhz1F9p14fNmrXP2R36rgWZqdC4QlOc,6358
|
|
119
118
|
devrev_mcp/utils/errors.py,sha256=5mRAo76rJvvEVi6b1ZokPxDtX5JKkptaqmiYDLCkwBE,2110
|
|
120
119
|
devrev_mcp/utils/formatting.py,sha256=6JssG5x1BxjdgSiQ8Ou3H-9Wo3wgWTWmejsrGez4wKc,2431
|
|
121
120
|
devrev_mcp/utils/pagination.py,sha256=EOUgL-ZdSToM1Q-ydXmjhibsef5K1u1g3CaS9K8I2fY,1286
|
|
122
|
-
devrev_python_sdk-3.
|
|
123
|
-
devrev_python_sdk-3.
|
|
124
|
-
devrev_python_sdk-3.
|
|
125
|
-
devrev_python_sdk-3.
|
|
121
|
+
devrev_python_sdk-3.1.1.dist-info/METADATA,sha256=KuDpsWwhLZQ-048S8B7FchJkkjP1ATp3a2upRwVfxJM,40934
|
|
122
|
+
devrev_python_sdk-3.1.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
123
|
+
devrev_python_sdk-3.1.1.dist-info/entry_points.txt,sha256=XiV4J_yy0yzVZVxg7T66YERVIlqdPNp3O-NHTHkllqQ,63
|
|
124
|
+
devrev_python_sdk-3.1.1.dist-info/RECORD,,
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"""Survey response models for DevRev SDK.
|
|
2
|
-
|
|
3
|
-
This module contains Pydantic models for the ``surveys.responses.list`` API.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
from enum import StrEnum
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from pydantic import ConfigDict, Field
|
|
13
|
-
|
|
14
|
-
from devrev.models.base import DevRevBaseModel, DevRevResponseModel, PaginatedResponse
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class SurveyResponsesListMode(StrEnum):
|
|
18
|
-
"""Survey response cursor iteration mode."""
|
|
19
|
-
|
|
20
|
-
AFTER = "after"
|
|
21
|
-
BEFORE = "before"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class SurveyResponse(DevRevResponseModel):
|
|
25
|
-
"""DevRev survey response model."""
|
|
26
|
-
|
|
27
|
-
model_config = ConfigDict(extra="allow", populate_by_name=True, str_strip_whitespace=True)
|
|
28
|
-
|
|
29
|
-
id: str = Field(..., description="Survey response ID")
|
|
30
|
-
display_id: str | None = Field(default=None, description="Human-readable display ID")
|
|
31
|
-
created_by: dict[str, Any] | None = Field(default=None, description="Creator user summary")
|
|
32
|
-
created_date: datetime | None = Field(default=None, description="Creation timestamp")
|
|
33
|
-
modified_by: dict[str, Any] | None = Field(default=None, description="Modifier user summary")
|
|
34
|
-
modified_date: datetime | None = Field(default=None, description="Last modification timestamp")
|
|
35
|
-
object_version: int | None = Field(default=None, description="Object version")
|
|
36
|
-
dispatch_id: str | None = Field(default=None, description="Dispatched survey ID")
|
|
37
|
-
dispatched_channels: list[dict[str, Any]] | None = Field(
|
|
38
|
-
default=None,
|
|
39
|
-
description="Channels on which the survey was dispatched",
|
|
40
|
-
)
|
|
41
|
-
object: str | None = Field(default=None, description="Object for which the survey was taken")
|
|
42
|
-
recipient: dict[str, Any] | str | None = Field(default=None, description="Survey recipient")
|
|
43
|
-
response: dict[str, Any] | str | int | float | bool | None = Field(
|
|
44
|
-
default=None,
|
|
45
|
-
description="Survey response payload",
|
|
46
|
-
)
|
|
47
|
-
stage: dict[str, Any] | int | str | None = Field(default=None, description="Survey stage")
|
|
48
|
-
survey: dict[str, Any] | str | None = Field(default=None, description="Survey summary or ID")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class SurveyResponsesListRequest(DevRevBaseModel):
|
|
52
|
-
"""Request to list survey responses."""
|
|
53
|
-
|
|
54
|
-
created_by: list[str] | None = Field(default=None, description="Creator user filters")
|
|
55
|
-
cursor: str | None = Field(default=None, description="Pagination cursor")
|
|
56
|
-
dispatch_ids: list[str] | None = Field(default=None, description="Survey dispatch IDs")
|
|
57
|
-
limit: int | None = Field(default=None, ge=1, le=100, description="Maximum results")
|
|
58
|
-
mode: SurveyResponsesListMode | None = Field(default=None, description="Cursor iteration mode")
|
|
59
|
-
object_id: str | None = Field(
|
|
60
|
-
default=None,
|
|
61
|
-
alias="object",
|
|
62
|
-
description="Single object ID to filter responses by",
|
|
63
|
-
)
|
|
64
|
-
objects: list[str] | None = Field(default=None, description="Object IDs to filter responses by")
|
|
65
|
-
recipient: list[str] | None = Field(default=None, description="Recipient user filters")
|
|
66
|
-
sort_by: list[str] | None = Field(default=None, description="Sort order entries")
|
|
67
|
-
stages: list[int] | None = Field(default=None, description="Survey response stage filters")
|
|
68
|
-
surveys: list[str] | None = Field(default=None, description="Survey IDs to filter by")
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class SurveyResponsesListResponse(PaginatedResponse):
|
|
72
|
-
"""Response from listing survey responses."""
|
|
73
|
-
|
|
74
|
-
survey_responses: list[SurveyResponse] = Field(
|
|
75
|
-
default_factory=list,
|
|
76
|
-
description="List of survey responses",
|
|
77
|
-
)
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"""Survey response service for DevRev SDK."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from collections.abc import Sequence
|
|
6
|
-
|
|
7
|
-
from devrev.models.survey_responses import (
|
|
8
|
-
SurveyResponsesListMode,
|
|
9
|
-
SurveyResponsesListRequest,
|
|
10
|
-
SurveyResponsesListResponse,
|
|
11
|
-
)
|
|
12
|
-
from devrev.services.base import AsyncBaseService, BaseService
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _merge_object_filters(object_id: str | None, objects: Sequence[str] | None) -> list[str] | None:
|
|
16
|
-
"""Merge singular and plural object filters without duplicates."""
|
|
17
|
-
merged = list(objects or [])
|
|
18
|
-
if object_id and object_id not in merged:
|
|
19
|
-
merged.insert(0, object_id)
|
|
20
|
-
return merged or None
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class SurveyResponsesService(BaseService):
|
|
24
|
-
"""Synchronous service for survey/CSAT responses."""
|
|
25
|
-
|
|
26
|
-
def list(
|
|
27
|
-
self,
|
|
28
|
-
*,
|
|
29
|
-
object_id: str | None = None,
|
|
30
|
-
objects: Sequence[str] | None = None,
|
|
31
|
-
created_by: Sequence[str] | None = None,
|
|
32
|
-
cursor: str | None = None,
|
|
33
|
-
dispatch_ids: Sequence[str] | None = None,
|
|
34
|
-
limit: int | None = None,
|
|
35
|
-
mode: SurveyResponsesListMode | None = None,
|
|
36
|
-
recipient: Sequence[str] | None = None,
|
|
37
|
-
sort_by: Sequence[str] | None = None,
|
|
38
|
-
stages: Sequence[int] | None = None,
|
|
39
|
-
surveys: Sequence[str] | None = None,
|
|
40
|
-
) -> SurveyResponsesListResponse:
|
|
41
|
-
"""List survey responses.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
object_id: Convenience filter for a single ticket/work/object ID.
|
|
45
|
-
objects: Object IDs to filter responses by.
|
|
46
|
-
created_by: Creator user filters.
|
|
47
|
-
cursor: Pagination cursor.
|
|
48
|
-
dispatch_ids: Survey dispatch IDs.
|
|
49
|
-
limit: Maximum number of responses to return.
|
|
50
|
-
mode: Cursor iteration mode.
|
|
51
|
-
recipient: Recipient user filters.
|
|
52
|
-
sort_by: Sort order entries.
|
|
53
|
-
stages: Survey response stage filters.
|
|
54
|
-
surveys: Survey IDs to filter by.
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
Paginated survey response list.
|
|
58
|
-
"""
|
|
59
|
-
request = SurveyResponsesListRequest(
|
|
60
|
-
created_by=list(created_by) if created_by else None,
|
|
61
|
-
cursor=cursor,
|
|
62
|
-
dispatch_ids=list(dispatch_ids) if dispatch_ids else None,
|
|
63
|
-
limit=limit,
|
|
64
|
-
mode=mode,
|
|
65
|
-
objects=_merge_object_filters(object_id, objects),
|
|
66
|
-
recipient=list(recipient) if recipient else None,
|
|
67
|
-
sort_by=list(sort_by) if sort_by else None,
|
|
68
|
-
stages=list(stages) if stages else None,
|
|
69
|
-
surveys=list(surveys) if surveys else None,
|
|
70
|
-
)
|
|
71
|
-
return self._post("/surveys.responses.list", request, SurveyResponsesListResponse)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class AsyncSurveyResponsesService(AsyncBaseService):
|
|
75
|
-
"""Asynchronous service for survey/CSAT responses."""
|
|
76
|
-
|
|
77
|
-
async def list(
|
|
78
|
-
self,
|
|
79
|
-
*,
|
|
80
|
-
object_id: str | None = None,
|
|
81
|
-
objects: Sequence[str] | None = None,
|
|
82
|
-
created_by: Sequence[str] | None = None,
|
|
83
|
-
cursor: str | None = None,
|
|
84
|
-
dispatch_ids: Sequence[str] | None = None,
|
|
85
|
-
limit: int | None = None,
|
|
86
|
-
mode: SurveyResponsesListMode | None = None,
|
|
87
|
-
recipient: Sequence[str] | None = None,
|
|
88
|
-
sort_by: Sequence[str] | None = None,
|
|
89
|
-
stages: Sequence[int] | None = None,
|
|
90
|
-
surveys: Sequence[str] | None = None,
|
|
91
|
-
) -> SurveyResponsesListResponse:
|
|
92
|
-
"""List survey responses.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
object_id: Convenience filter for a single ticket/work/object ID.
|
|
96
|
-
objects: Object IDs to filter responses by.
|
|
97
|
-
created_by: Creator user filters.
|
|
98
|
-
cursor: Pagination cursor.
|
|
99
|
-
dispatch_ids: Survey dispatch IDs.
|
|
100
|
-
limit: Maximum number of responses to return.
|
|
101
|
-
mode: Cursor iteration mode.
|
|
102
|
-
recipient: Recipient user filters.
|
|
103
|
-
sort_by: Sort order entries.
|
|
104
|
-
stages: Survey response stage filters.
|
|
105
|
-
surveys: Survey IDs to filter by.
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
Paginated survey response list.
|
|
109
|
-
"""
|
|
110
|
-
request = SurveyResponsesListRequest(
|
|
111
|
-
created_by=list(created_by) if created_by else None,
|
|
112
|
-
cursor=cursor,
|
|
113
|
-
dispatch_ids=list(dispatch_ids) if dispatch_ids else None,
|
|
114
|
-
limit=limit,
|
|
115
|
-
mode=mode,
|
|
116
|
-
objects=_merge_object_filters(object_id, objects),
|
|
117
|
-
recipient=list(recipient) if recipient else None,
|
|
118
|
-
sort_by=list(sort_by) if sort_by else None,
|
|
119
|
-
stages=list(stages) if stages else None,
|
|
120
|
-
surveys=list(surveys) if surveys else None,
|
|
121
|
-
)
|
|
122
|
-
return await self._post("/surveys.responses.list", request, SurveyResponsesListResponse)
|
|
File without changes
|