rossum-mcp 0.3.4__py3-none-any.whl → 0.4.0__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.
- rossum_mcp/__init__.py +1 -1
- rossum_mcp/server.py +4 -0
- rossum_mcp/tools/__init__.py +14 -0
- rossum_mcp/tools/annotations.py +123 -86
- rossum_mcp/tools/base.py +22 -0
- rossum_mcp/tools/catalog.py +169 -0
- rossum_mcp/tools/discovery.py +36 -0
- rossum_mcp/tools/document_relations.py +1 -3
- rossum_mcp/tools/email_templates.py +131 -0
- rossum_mcp/tools/engines.py +106 -77
- rossum_mcp/tools/hooks.py +228 -150
- rossum_mcp/tools/queues.py +244 -85
- rossum_mcp/tools/relations.py +3 -7
- rossum_mcp/tools/rules.py +28 -17
- rossum_mcp/tools/schemas.py +505 -105
- rossum_mcp/tools/users.py +51 -27
- rossum_mcp/tools/workspaces.py +47 -37
- {rossum_mcp-0.3.4.dist-info → rossum_mcp-0.4.0.dist-info}/METADATA +370 -12
- rossum_mcp-0.4.0.dist-info/RECORD +24 -0
- rossum_mcp-0.3.4.dist-info/RECORD +0 -21
- {rossum_mcp-0.3.4.dist-info → rossum_mcp-0.4.0.dist-info}/WHEEL +0 -0
- {rossum_mcp-0.3.4.dist-info → rossum_mcp-0.4.0.dist-info}/entry_points.txt +0 -0
- {rossum_mcp-0.3.4.dist-info → rossum_mcp-0.4.0.dist-info}/top_level.txt +0 -0
rossum_mcp/tools/queues.py
CHANGED
|
@@ -4,16 +4,17 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
-
from
|
|
7
|
+
from dataclasses import replace
|
|
8
|
+
from typing import TYPE_CHECKING, Literal, cast, get_args
|
|
8
9
|
|
|
9
10
|
from rossum_api import APIClientError
|
|
10
11
|
from rossum_api.domain_logic.resources import Resource
|
|
11
12
|
from rossum_api.models import deserialize_default
|
|
12
|
-
from rossum_api.models.engine import Engine
|
|
13
|
-
from rossum_api.models.queue import Queue
|
|
14
|
-
from rossum_api.models.schema import Schema
|
|
13
|
+
from rossum_api.models.engine import Engine
|
|
14
|
+
from rossum_api.models.queue import Queue
|
|
15
|
+
from rossum_api.models.schema import Schema
|
|
15
16
|
|
|
16
|
-
from rossum_mcp.tools.base import build_resource_url, is_read_write_mode
|
|
17
|
+
from rossum_mcp.tools.base import build_resource_url, is_read_write_mode, truncate_dict_fields
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from fastmcp import FastMCP
|
|
@@ -21,56 +22,222 @@ if TYPE_CHECKING:
|
|
|
21
22
|
|
|
22
23
|
logger = logging.getLogger(__name__)
|
|
23
24
|
|
|
25
|
+
# Fields to truncate in queue.settings for list responses
|
|
26
|
+
_QUEUE_SETTINGS_TRUNCATE_FIELDS = (
|
|
27
|
+
"accepted_mime_types",
|
|
28
|
+
"annotation_list_table",
|
|
29
|
+
"users",
|
|
30
|
+
"dashboard_customization",
|
|
31
|
+
"email_notifications",
|
|
32
|
+
)
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
|
|
35
|
+
def _truncate_queue_for_list(queue: Queue) -> Queue:
|
|
36
|
+
"""Truncate verbose fields in queue settings to save context in list responses."""
|
|
37
|
+
if not queue.settings:
|
|
38
|
+
return queue
|
|
39
|
+
return replace(queue, settings=truncate_dict_fields(queue.settings, _QUEUE_SETTINGS_TRUNCATE_FIELDS))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def _get_queue(client: AsyncRossumAPIClient, queue_id: int) -> Queue:
|
|
43
|
+
logger.debug(f"Retrieving queue: queue_id={queue_id}")
|
|
44
|
+
queue: Queue = await client.retrieve_queue(queue_id)
|
|
45
|
+
return queue
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def _list_queues(
|
|
49
|
+
client: AsyncRossumAPIClient, workspace_id: int | None = None, name: str | None = None
|
|
50
|
+
) -> list[Queue]:
|
|
51
|
+
logger.debug(f"Listing queues: workspace_id={workspace_id}, name={name}")
|
|
52
|
+
filters: dict[str, int | str] = {}
|
|
53
|
+
if workspace_id is not None:
|
|
54
|
+
filters["workspace"] = workspace_id
|
|
55
|
+
if name is not None:
|
|
56
|
+
filters["name"] = name
|
|
57
|
+
|
|
58
|
+
queues = [queue async for queue in client.list_queues(**filters)] # type: ignore[arg-type]
|
|
59
|
+
return [_truncate_queue_for_list(queue) for queue in queues]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async def _get_queue_schema(client: AsyncRossumAPIClient, queue_id: int) -> Schema:
|
|
63
|
+
logger.debug(f"Retrieving queue schema: queue_id={queue_id}")
|
|
64
|
+
queue: Queue = await client.retrieve_queue(queue_id)
|
|
65
|
+
schema_url = queue.schema
|
|
66
|
+
schema_id = int(schema_url.rstrip("/").split("/")[-1])
|
|
67
|
+
schema: Schema = await client.retrieve_schema(schema_id)
|
|
68
|
+
return schema
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def _get_queue_engine(client: AsyncRossumAPIClient, queue_id: int) -> Engine | dict:
|
|
72
|
+
logger.debug(f"Retrieving queue engine: queue_id={queue_id}")
|
|
73
|
+
queue: Queue = await client.retrieve_queue(queue_id)
|
|
74
|
+
|
|
75
|
+
engine_url = None
|
|
76
|
+
if queue.dedicated_engine:
|
|
77
|
+
engine_url = queue.dedicated_engine
|
|
78
|
+
elif queue.generic_engine:
|
|
79
|
+
engine_url = queue.generic_engine
|
|
80
|
+
elif queue.engine:
|
|
81
|
+
engine_url = queue.engine
|
|
82
|
+
|
|
83
|
+
if not engine_url:
|
|
84
|
+
return {"message": "No engine assigned to this queue"}
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
if isinstance(engine_url, str):
|
|
88
|
+
engine_id = int(engine_url.rstrip("/").split("/")[-1])
|
|
89
|
+
engine: Engine = await client.retrieve_engine(engine_id)
|
|
90
|
+
else:
|
|
91
|
+
engine = deserialize_default(Resource.Engine, engine_url)
|
|
92
|
+
except APIClientError as e:
|
|
93
|
+
if e.status_code == 404:
|
|
94
|
+
return {"message": f"Engine not found (engine URL: {engine_url})"}
|
|
95
|
+
raise
|
|
96
|
+
|
|
97
|
+
return engine
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def _create_queue(
|
|
101
|
+
client: AsyncRossumAPIClient,
|
|
102
|
+
name: str,
|
|
103
|
+
workspace_id: int,
|
|
104
|
+
schema_id: int,
|
|
105
|
+
engine_id: int | None = None,
|
|
106
|
+
inbox_id: int | None = None,
|
|
107
|
+
connector_id: int | None = None,
|
|
108
|
+
locale: str = "en_GB",
|
|
109
|
+
automation_enabled: bool = False,
|
|
110
|
+
automation_level: str = "never",
|
|
111
|
+
training_enabled: bool = True,
|
|
112
|
+
splitting_screen_feature_flag: bool = False,
|
|
113
|
+
) -> Queue | dict:
|
|
114
|
+
if not is_read_write_mode():
|
|
115
|
+
return {"error": "create_queue is not available in read-only mode"}
|
|
116
|
+
|
|
117
|
+
logger.debug(
|
|
118
|
+
f"Creating queue: name={name}, workspace_id={workspace_id}, schema_id={schema_id}, engine_id={engine_id}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
queue_data: dict = {
|
|
122
|
+
"name": name,
|
|
123
|
+
"workspace": build_resource_url("workspaces", workspace_id),
|
|
124
|
+
"schema": build_resource_url("schemas", schema_id),
|
|
125
|
+
"locale": locale,
|
|
126
|
+
"automation_enabled": automation_enabled,
|
|
127
|
+
"automation_level": automation_level,
|
|
128
|
+
"training_enabled": training_enabled,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if engine_id is not None:
|
|
132
|
+
queue_data["engine"] = build_resource_url("engines", engine_id)
|
|
133
|
+
if inbox_id is not None:
|
|
134
|
+
queue_data["inbox"] = build_resource_url("inboxes", inbox_id)
|
|
135
|
+
if connector_id is not None:
|
|
136
|
+
queue_data["connector"] = build_resource_url("connectors", connector_id)
|
|
137
|
+
if splitting_screen_feature_flag:
|
|
138
|
+
if os.environ.get("SPLITTING_SCREEN_FLAG_NAME") and os.environ.get("SPLITTING_SCREEN_FLAG_VALUE"):
|
|
139
|
+
queue_data["settings"] = {
|
|
140
|
+
os.environ["SPLITTING_SCREEN_FLAG_NAME"]: os.environ["SPLITTING_SCREEN_FLAG_VALUE"]
|
|
141
|
+
}
|
|
142
|
+
else:
|
|
143
|
+
logger.error("Splitting screen failed to update")
|
|
144
|
+
|
|
145
|
+
queue: Queue = await client.create_new_queue(queue_data)
|
|
146
|
+
return queue
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async def _update_queue(client: AsyncRossumAPIClient, queue_id: int, queue_data: dict) -> Queue | dict:
|
|
150
|
+
if not is_read_write_mode():
|
|
151
|
+
return {"error": "update_queue is not available in read-only mode"}
|
|
152
|
+
|
|
153
|
+
logger.debug(f"Updating queue: queue_id={queue_id}, data={queue_data}")
|
|
154
|
+
updated_queue_data = await client._http_client.update(Resource.Queue, queue_id, queue_data)
|
|
155
|
+
return cast("Queue", client._deserializer(Resource.Queue, updated_queue_data))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# Available template names for create_queue_from_template
|
|
159
|
+
QueueTemplateName = Literal[
|
|
160
|
+
"EU Demo Template",
|
|
161
|
+
"AP&R EU Demo Template",
|
|
162
|
+
"Tax Invoice EU Demo Template",
|
|
163
|
+
"US Demo Template",
|
|
164
|
+
"AP&R US Demo Template",
|
|
165
|
+
"Tax Invoice US Demo Template",
|
|
166
|
+
"UK Demo Template",
|
|
167
|
+
"AP&R UK Demo Template",
|
|
168
|
+
"Tax Invoice UK Demo Template",
|
|
169
|
+
"CZ Demo Template",
|
|
170
|
+
"Empty Organization Template",
|
|
171
|
+
"Delivery Notes Demo Template",
|
|
172
|
+
"Delivery Note Demo Template",
|
|
173
|
+
"Chinese Invoices (Fapiao) Demo Template",
|
|
174
|
+
"Tax Invoice CN Demo Template",
|
|
175
|
+
"Certificates of Analysis Demo Template",
|
|
176
|
+
"Purchase Order Demo Template",
|
|
177
|
+
"Credit Note Demo Template",
|
|
178
|
+
"Debit Note Demo Template",
|
|
179
|
+
"Proforma Invoice Demo Template",
|
|
180
|
+
]
|
|
181
|
+
QUEUE_TEMPLATE_NAMES = get_args(QueueTemplateName)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async def _create_queue_from_template(
|
|
185
|
+
client: AsyncRossumAPIClient,
|
|
186
|
+
name: str,
|
|
187
|
+
template_name: QueueTemplateName,
|
|
188
|
+
workspace_id: int,
|
|
189
|
+
include_documents: bool = False,
|
|
190
|
+
engine_id: int | None = None,
|
|
191
|
+
) -> Queue | dict:
|
|
192
|
+
if not is_read_write_mode():
|
|
193
|
+
return {"error": "create_queue_from_template is not available in read-only mode"}
|
|
194
|
+
|
|
195
|
+
if template_name not in QUEUE_TEMPLATE_NAMES:
|
|
196
|
+
return {
|
|
197
|
+
"error": f"Invalid template_name: '{template_name}'",
|
|
198
|
+
"available_templates": QUEUE_TEMPLATE_NAMES,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
logger.debug(
|
|
202
|
+
f"Creating queue from template: name={name}, template_name={template_name}, workspace_id={workspace_id}"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
payload: dict = {
|
|
206
|
+
"name": name,
|
|
207
|
+
"template_name": template_name,
|
|
208
|
+
"workspace": build_resource_url("workspaces", workspace_id),
|
|
209
|
+
"include_documents": include_documents,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if engine_id is not None:
|
|
213
|
+
payload["engine"] = build_resource_url("engines", engine_id)
|
|
214
|
+
|
|
215
|
+
response = await client._http_client.request_json(
|
|
216
|
+
method="POST",
|
|
217
|
+
url="queues/from_template",
|
|
218
|
+
json=payload,
|
|
219
|
+
)
|
|
220
|
+
return cast("Queue", client._deserializer(Resource.Queue, response))
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def register_queue_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
|
|
26
224
|
"""Register queue-related tools with the FastMCP server."""
|
|
27
225
|
|
|
28
226
|
@mcp.tool(description="Retrieve queue details.")
|
|
29
227
|
async def get_queue(queue_id: int) -> Queue:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
228
|
+
return await _get_queue(client, queue_id)
|
|
229
|
+
|
|
230
|
+
@mcp.tool(description="List all queues with optional filters.")
|
|
231
|
+
async def list_queues(workspace_id: int | None = None, name: str | None = None) -> list[Queue]:
|
|
232
|
+
return await _list_queues(client, workspace_id, name)
|
|
34
233
|
|
|
35
234
|
@mcp.tool(description="Retrieve queue schema.")
|
|
36
235
|
async def get_queue_schema(queue_id: int) -> Schema:
|
|
37
|
-
|
|
38
|
-
logger.debug(f"Retrieving queue schema: queue_id={queue_id}")
|
|
39
|
-
queue: Queue = await client.retrieve_queue(queue_id)
|
|
40
|
-
schema_url = queue.schema
|
|
41
|
-
schema_id = int(schema_url.rstrip("/").split("/")[-1])
|
|
42
|
-
schema: Schema = await client.retrieve_schema(schema_id)
|
|
43
|
-
return schema
|
|
236
|
+
return await _get_queue_schema(client, queue_id)
|
|
44
237
|
|
|
45
238
|
@mcp.tool(description="Retrieve queue engine. Returns None if no engine assigned.")
|
|
46
239
|
async def get_queue_engine(queue_id: int) -> Engine | dict:
|
|
47
|
-
|
|
48
|
-
logger.debug(f"Retrieving queue engine: queue_id={queue_id}")
|
|
49
|
-
queue: Queue = await client.retrieve_queue(queue_id)
|
|
50
|
-
|
|
51
|
-
engine_url = None
|
|
52
|
-
if queue.dedicated_engine:
|
|
53
|
-
engine_url = queue.dedicated_engine
|
|
54
|
-
elif queue.generic_engine:
|
|
55
|
-
engine_url = queue.generic_engine
|
|
56
|
-
elif queue.engine:
|
|
57
|
-
engine_url = queue.engine
|
|
58
|
-
|
|
59
|
-
if not engine_url:
|
|
60
|
-
return {"message": "No engine assigned to this queue"}
|
|
61
|
-
|
|
62
|
-
try:
|
|
63
|
-
if isinstance(engine_url, str):
|
|
64
|
-
engine_id = int(engine_url.rstrip("/").split("/")[-1])
|
|
65
|
-
engine: Engine = await client.retrieve_engine(engine_id)
|
|
66
|
-
else:
|
|
67
|
-
engine = deserialize_default(Resource.Engine, engine_url)
|
|
68
|
-
except APIClientError as e:
|
|
69
|
-
if e.status_code == 404:
|
|
70
|
-
return {"message": f"Engine not found (engine URL: {engine_url})"}
|
|
71
|
-
raise
|
|
72
|
-
|
|
73
|
-
return engine
|
|
240
|
+
return await _get_queue_engine(client, queue_id)
|
|
74
241
|
|
|
75
242
|
@mcp.tool(description="Create a queue.")
|
|
76
243
|
async def create_queue(
|
|
@@ -86,48 +253,40 @@ def register_queue_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None: #
|
|
|
86
253
|
training_enabled: bool = True,
|
|
87
254
|
splitting_screen_feature_flag: bool = False,
|
|
88
255
|
) -> Queue | dict:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
256
|
+
return await _create_queue(
|
|
257
|
+
client,
|
|
258
|
+
name,
|
|
259
|
+
workspace_id,
|
|
260
|
+
schema_id,
|
|
261
|
+
engine_id,
|
|
262
|
+
inbox_id,
|
|
263
|
+
connector_id,
|
|
264
|
+
locale,
|
|
265
|
+
automation_enabled,
|
|
266
|
+
automation_level,
|
|
267
|
+
training_enabled,
|
|
268
|
+
splitting_screen_feature_flag,
|
|
95
269
|
)
|
|
96
270
|
|
|
97
|
-
queue_data: dict = {
|
|
98
|
-
"name": name,
|
|
99
|
-
"workspace": build_resource_url("workspaces", workspace_id),
|
|
100
|
-
"schema": build_resource_url("schemas", schema_id),
|
|
101
|
-
"locale": locale,
|
|
102
|
-
"automation_enabled": automation_enabled,
|
|
103
|
-
"automation_level": automation_level,
|
|
104
|
-
"training_enabled": training_enabled,
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if engine_id is not None:
|
|
108
|
-
queue_data["engine"] = build_resource_url("engines", engine_id)
|
|
109
|
-
if inbox_id is not None:
|
|
110
|
-
queue_data["inbox"] = build_resource_url("inboxes", inbox_id)
|
|
111
|
-
if connector_id is not None:
|
|
112
|
-
queue_data["connector"] = build_resource_url("connectors", connector_id)
|
|
113
|
-
if splitting_screen_feature_flag:
|
|
114
|
-
if os.environ.get("SPLITTING_SCREEN_FLAG_NAME") and os.environ.get("SPLITTING_SCREEN_FLAG_VALUE"):
|
|
115
|
-
queue_data["settings"] = {
|
|
116
|
-
os.environ["SPLITTING_SCREEN_FLAG_NAME"]: os.environ["SPLITTING_SCREEN_FLAG_VALUE"]
|
|
117
|
-
}
|
|
118
|
-
else:
|
|
119
|
-
logger.error("Splitting screen failed to update")
|
|
120
|
-
|
|
121
|
-
queue: Queue = await client.create_new_queue(queue_data)
|
|
122
|
-
return queue
|
|
123
|
-
|
|
124
271
|
@mcp.tool(description="Update queue settings.")
|
|
125
272
|
async def update_queue(queue_id: int, queue_data: dict) -> Queue | dict:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
273
|
+
return await _update_queue(client, queue_id, queue_data)
|
|
274
|
+
|
|
275
|
+
@mcp.tool(description="Get available queue template names for create_queue_from_template.")
|
|
276
|
+
async def get_queue_template_names() -> list[str]:
|
|
277
|
+
return list(QUEUE_TEMPLATE_NAMES)
|
|
278
|
+
|
|
279
|
+
@mcp.tool(
|
|
280
|
+
description="Create queue from a predefined template. Preferred method for new customer setup. "
|
|
281
|
+
"Templates include pre-configured schema and AI engine for common document types."
|
|
282
|
+
)
|
|
283
|
+
async def create_queue_from_template(
|
|
284
|
+
name: str,
|
|
285
|
+
template_name: QueueTemplateName,
|
|
286
|
+
workspace_id: int,
|
|
287
|
+
include_documents: bool = False,
|
|
288
|
+
engine_id: int | None = None,
|
|
289
|
+
) -> Queue | dict:
|
|
290
|
+
return await _create_queue_from_template(
|
|
291
|
+
client, name, template_name, workspace_id, include_documents, engine_id
|
|
292
|
+
)
|
rossum_mcp/tools/relations.py
CHANGED
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import TYPE_CHECKING, cast
|
|
7
7
|
|
|
8
8
|
from rossum_api.domain_logic.resources import Resource
|
|
9
|
-
from rossum_api.models.relation import
|
|
10
|
-
Relation, # noqa: TC002 - needed at runtime for FastMCP
|
|
11
|
-
RelationType, # noqa: TC002 - needed at runtime for FastMCP
|
|
12
|
-
)
|
|
9
|
+
from rossum_api.models.relation import Relation, RelationType
|
|
13
10
|
|
|
14
11
|
if TYPE_CHECKING:
|
|
15
12
|
from fastmcp import FastMCP
|
|
@@ -26,8 +23,7 @@ def register_relation_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
|
|
|
26
23
|
"""Retrieve relation details."""
|
|
27
24
|
logger.debug(f"Retrieving relation: relation_id={relation_id}")
|
|
28
25
|
relation_data = await client._http_client.fetch_one(Resource.Relation, relation_id)
|
|
29
|
-
|
|
30
|
-
return relation_obj
|
|
26
|
+
return cast("Relation", client._deserializer(Resource.Relation, relation_data))
|
|
31
27
|
|
|
32
28
|
@mcp.tool(
|
|
33
29
|
description="List all relations with optional filters. Relations introduce common relations between annotations (edit, attachment, duplicate)."
|
rossum_mcp/tools/rules.py
CHANGED
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
|
-
from rossum_api.models.rule import Rule
|
|
8
|
+
from rossum_api.models.rule import Rule
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from fastmcp import FastMCP
|
|
@@ -14,29 +14,40 @@ if TYPE_CHECKING:
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
async def _get_rule(client: AsyncRossumAPIClient, rule_id: int) -> Rule:
|
|
18
|
+
logger.debug(f"Retrieving rule: rule_id={rule_id}")
|
|
19
|
+
rule: Rule = await client.retrieve_rule(rule_id)
|
|
20
|
+
return rule
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def _list_rules(
|
|
24
|
+
client: AsyncRossumAPIClient,
|
|
25
|
+
schema_id: int | None = None,
|
|
26
|
+
organization_id: int | None = None,
|
|
27
|
+
enabled: bool | None = None,
|
|
28
|
+
) -> list[Rule]:
|
|
29
|
+
logger.debug(f"Listing rules: schema_id={schema_id}, organization_id={organization_id}, enabled={enabled}")
|
|
30
|
+
filters: dict = {}
|
|
31
|
+
if schema_id is not None:
|
|
32
|
+
filters["schema"] = schema_id
|
|
33
|
+
if organization_id is not None:
|
|
34
|
+
filters["organization"] = organization_id
|
|
35
|
+
if enabled is not None:
|
|
36
|
+
filters["enabled"] = enabled
|
|
37
|
+
|
|
38
|
+
rules_list: list[Rule] = [rule async for rule in client.list_rules(**filters)]
|
|
39
|
+
return rules_list
|
|
40
|
+
|
|
41
|
+
|
|
17
42
|
def register_rule_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
|
|
18
43
|
"""Register rule-related tools with the FastMCP server."""
|
|
19
44
|
|
|
20
45
|
@mcp.tool(description="Retrieve rule details.")
|
|
21
46
|
async def get_rule(rule_id: int) -> Rule:
|
|
22
|
-
|
|
23
|
-
logger.debug(f"Retrieving rule: rule_id={rule_id}")
|
|
24
|
-
rule: Rule = await client.retrieve_rule(rule_id)
|
|
25
|
-
return rule
|
|
47
|
+
return await _get_rule(client, rule_id)
|
|
26
48
|
|
|
27
49
|
@mcp.tool(description="List all rules.")
|
|
28
50
|
async def list_rules(
|
|
29
51
|
schema_id: int | None = None, organization_id: int | None = None, enabled: bool | None = None
|
|
30
52
|
) -> list[Rule]:
|
|
31
|
-
|
|
32
|
-
logger.debug(f"Listing rules: schema_id={schema_id}, organization_id={organization_id}, enabled={enabled}")
|
|
33
|
-
filters: dict = {}
|
|
34
|
-
if schema_id is not None:
|
|
35
|
-
filters["schema"] = schema_id
|
|
36
|
-
if organization_id is not None:
|
|
37
|
-
filters["organization"] = organization_id
|
|
38
|
-
if enabled is not None:
|
|
39
|
-
filters["enabled"] = enabled
|
|
40
|
-
|
|
41
|
-
rules_list: list[Rule] = [rule async for rule in client.list_rules(**filters)]
|
|
42
|
-
return rules_list
|
|
53
|
+
return await _list_rules(client, schema_id, organization_id, enabled)
|