rossum-mcp 0.4.0__py3-none-any.whl → 1.0.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.
rossum_mcp/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.4.0"
3
+ __version__ = "1.0.1"
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Literal
9
9
 
10
10
  from rossum_api.models.annotation import Annotation
11
11
 
12
- from rossum_mcp.tools.base import is_read_write_mode
12
+ from rossum_mcp.tools.base import delete_resource, is_read_write_mode
13
13
 
14
14
  if TYPE_CHECKING:
15
15
  from fastmcp import FastMCP
@@ -130,6 +130,12 @@ async def _confirm_annotation(client: AsyncRossumAPIClient, annotation_id: int)
130
130
  }
131
131
 
132
132
 
133
+ async def _delete_annotation(client: AsyncRossumAPIClient, annotation_id: int) -> dict:
134
+ return await delete_resource(
135
+ "annotation", annotation_id, client.delete_annotation, f"Annotation {annotation_id} moved to 'deleted' status"
136
+ )
137
+
138
+
133
139
  def register_annotation_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
134
140
  """Register annotation-related tools with the FastMCP server."""
135
141
 
@@ -165,3 +171,7 @@ def register_annotation_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> Non
165
171
  )
166
172
  async def confirm_annotation(annotation_id: int) -> dict:
167
173
  return await _confirm_annotation(client, annotation_id)
174
+
175
+ @mcp.tool(description="Delete an annotation. Moves to 'deleted' status (soft delete).")
176
+ async def delete_annotation(annotation_id: int) -> dict:
177
+ return await _delete_annotation(client, annotation_id)
rossum_mcp/tools/base.py CHANGED
@@ -2,12 +2,16 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
5
6
  import os
6
7
  from typing import TYPE_CHECKING
7
8
 
8
9
  if TYPE_CHECKING:
10
+ from collections.abc import Awaitable, Callable
9
11
  from typing import Any
10
12
 
13
+ logger = logging.getLogger(__name__)
14
+
11
15
  BASE_URL = os.environ.get("ROSSUM_API_BASE_URL", "").rstrip("/")
12
16
  MODE = os.environ.get("ROSSUM_MCP_MODE", "read-write").lower()
13
17
 
@@ -38,3 +42,32 @@ def truncate_dict_fields(data: dict[str, Any], fields: tuple[str, ...]) -> dict[
38
42
  if field in result:
39
43
  result[field] = TRUNCATED_MARKER
40
44
  return result
45
+
46
+
47
+ async def delete_resource(
48
+ resource_type: str,
49
+ resource_id: int,
50
+ delete_fn: Callable[[int], Awaitable[None]],
51
+ success_message: str | None = None,
52
+ ) -> dict:
53
+ """Generic delete operation with read-only mode check.
54
+
55
+ Args:
56
+ resource_type: Name of the resource (e.g., "queue", "workspace")
57
+ resource_id: ID of the resource to delete
58
+ delete_fn: Async function that performs the deletion
59
+ success_message: Custom success message. If None, uses default format.
60
+
61
+ Returns:
62
+ Dict with "message" on success or "error" in read-only mode.
63
+ """
64
+ tool_name = f"delete_{resource_type}"
65
+ if not is_read_write_mode():
66
+ return {"error": f"{tool_name} is not available in read-only mode"}
67
+
68
+ logger.debug(f"Deleting {resource_type}: {resource_type}_id={resource_id}")
69
+ await delete_fn(resource_id)
70
+
71
+ if success_message is None:
72
+ success_message = f"{resource_type.title()} {resource_id} deleted successfully"
73
+ return {"message": success_message}
@@ -16,6 +16,7 @@ class ToolInfo:
16
16
 
17
17
  name: str
18
18
  description: str
19
+ read_only: bool = True
19
20
 
20
21
 
21
22
  @dataclass
@@ -35,12 +36,13 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
35
36
  name="annotations",
36
37
  description="Document processing: upload, retrieve, update, and confirm annotations",
37
38
  tools=[
38
- ToolInfo("upload_document", "Upload document to queue"),
39
+ ToolInfo("upload_document", "Upload document to queue", read_only=False),
39
40
  ToolInfo("get_annotation", "Retrieve annotation with extracted data"),
40
41
  ToolInfo("list_annotations", "List annotations for a queue"),
41
- ToolInfo("start_annotation", "Start annotation (to_review -> reviewing)"),
42
- ToolInfo("bulk_update_annotation_fields", "Bulk update annotation fields"),
43
- ToolInfo("confirm_annotation", "Confirm annotation (-> confirmed)"),
42
+ ToolInfo("start_annotation", "Start annotation (to_review -> reviewing)", read_only=False),
43
+ ToolInfo("bulk_update_annotation_fields", "Bulk update annotation fields", read_only=False),
44
+ ToolInfo("confirm_annotation", "Confirm annotation (-> confirmed)", read_only=False),
45
+ ToolInfo("delete_annotation", "Delete annotation (soft delete)", read_only=False),
44
46
  ],
45
47
  keywords=["annotation", "document", "upload", "extract", "confirm", "review"],
46
48
  ),
@@ -52,10 +54,11 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
52
54
  ToolInfo("list_queues", "List all queues"),
53
55
  ToolInfo("get_queue_schema", "Get queue's schema"),
54
56
  ToolInfo("get_queue_engine", "Get queue's AI engine"),
55
- ToolInfo("create_queue", "Create a queue"),
56
- ToolInfo("update_queue", "Update queue settings"),
57
+ ToolInfo("create_queue", "Create a queue", read_only=False),
58
+ ToolInfo("update_queue", "Update queue settings", read_only=False),
57
59
  ToolInfo("get_queue_template_names", "List available queue templates"),
58
- ToolInfo("create_queue_from_template", "Create queue from template"),
60
+ ToolInfo("create_queue_from_template", "Create queue from template", read_only=False),
61
+ ToolInfo("delete_queue", "Delete queue (24h delayed)", read_only=False),
59
62
  ],
60
63
  keywords=["queue", "inbox", "connector"],
61
64
  ),
@@ -65,11 +68,12 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
65
68
  tools=[
66
69
  ToolInfo("get_schema", "Retrieve schema details"),
67
70
  ToolInfo("list_schemas", "List all schemas"),
68
- ToolInfo("update_schema", "Update schema"),
69
- ToolInfo("create_schema", "Create new schema"),
70
- ToolInfo("patch_schema", "Add/update/remove schema fields"),
71
+ ToolInfo("update_schema", "Update schema", read_only=False),
72
+ ToolInfo("create_schema", "Create new schema", read_only=False),
73
+ ToolInfo("patch_schema", "Add/update/remove schema fields", read_only=False),
71
74
  ToolInfo("get_schema_tree_structure", "Get lightweight schema tree"),
72
- ToolInfo("prune_schema_fields", "Bulk remove schema fields"),
75
+ ToolInfo("prune_schema_fields", "Bulk remove schema fields", read_only=False),
76
+ ToolInfo("delete_schema", "Delete schema", read_only=False),
73
77
  ],
74
78
  keywords=["schema", "field", "datapoint", "section", "multivalue", "tuple"],
75
79
  ),
@@ -79,9 +83,9 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
79
83
  tools=[
80
84
  ToolInfo("get_engine", "Retrieve engine details"),
81
85
  ToolInfo("list_engines", "List all engines"),
82
- ToolInfo("update_engine", "Update engine settings"),
83
- ToolInfo("create_engine", "Create new engine"),
84
- ToolInfo("create_engine_field", "Create engine field mapping"),
86
+ ToolInfo("update_engine", "Update engine settings", read_only=False),
87
+ ToolInfo("create_engine", "Create new engine", read_only=False),
88
+ ToolInfo("create_engine_field", "Create engine field mapping", read_only=False),
85
89
  ToolInfo("get_engine_fields", "List engine fields"),
86
90
  ],
87
91
  keywords=["engine", "ai", "extractor", "splitter", "training"],
@@ -92,11 +96,12 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
92
96
  tools=[
93
97
  ToolInfo("get_hook", "Retrieve hook details with code"),
94
98
  ToolInfo("list_hooks", "List all hooks for a queue"),
95
- ToolInfo("create_hook", "Create new hook"),
96
- ToolInfo("update_hook", "Update hook configuration"),
99
+ ToolInfo("create_hook", "Create new hook", read_only=False),
100
+ ToolInfo("update_hook", "Update hook configuration", read_only=False),
97
101
  ToolInfo("list_hook_logs", "View hook execution logs"),
98
102
  ToolInfo("list_hook_templates", "List Rossum Store templates"),
99
- ToolInfo("create_hook_from_template", "Create hook from template"),
103
+ ToolInfo("create_hook_from_template", "Create hook from template", read_only=False),
104
+ ToolInfo("delete_hook", "Delete hook", read_only=False),
100
105
  ],
101
106
  keywords=["hook", "extension", "webhook", "automation", "function", "serverless"],
102
107
  ),
@@ -106,7 +111,7 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
106
111
  tools=[
107
112
  ToolInfo("get_email_template", "Retrieve email template"),
108
113
  ToolInfo("list_email_templates", "List email templates"),
109
- ToolInfo("create_email_template", "Create email template"),
114
+ ToolInfo("create_email_template", "Create email template", read_only=False),
110
115
  ],
111
116
  keywords=["email", "template", "notification", "rejection"],
112
117
  ),
@@ -126,7 +131,7 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
126
131
  ToolInfo("get_relation", "Retrieve relation details"),
127
132
  ToolInfo("list_relations", "List annotation relations"),
128
133
  ],
129
- keywords=["relation", "duplicate", "attachment", "edit"],
134
+ keywords=["relation", "duplicate", "attachment"],
130
135
  ),
131
136
  "rules": ToolCategory(
132
137
  name="rules",
@@ -134,6 +139,7 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
134
139
  tools=[
135
140
  ToolInfo("get_rule", "Retrieve rule details"),
136
141
  ToolInfo("list_rules", "List validation rules"),
142
+ ToolInfo("delete_rule", "Delete rule", read_only=False),
137
143
  ],
138
144
  keywords=["rule", "validation", "constraint"],
139
145
  ),
@@ -153,7 +159,8 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
153
159
  tools=[
154
160
  ToolInfo("get_workspace", "Retrieve workspace details"),
155
161
  ToolInfo("list_workspaces", "List all workspaces"),
156
- ToolInfo("create_workspace", "Create new workspace"),
162
+ ToolInfo("create_workspace", "Create new workspace", read_only=False),
163
+ ToolInfo("delete_workspace", "Delete workspace", read_only=False),
157
164
  ],
158
165
  keywords=["workspace", "organization"],
159
166
  ),
@@ -21,7 +21,8 @@ def register_discovery_tools(mcp: FastMCP) -> None:
21
21
  @mcp.tool(
22
22
  description="List all available tool categories with descriptions, tool names, and keywords. "
23
23
  "Use this to discover what tools are available, then use load_tool_category to load "
24
- "tools from specific categories before using them."
24
+ "tools from specific categories before using them. Tools with read_only=false are write "
25
+ "operations (create, update, delete)."
25
26
  )
26
27
  async def list_tool_categories() -> list[dict]:
27
28
  return [
@@ -38,15 +38,11 @@ async def _list_email_templates(
38
38
  if name is not None:
39
39
  filters["name"] = name
40
40
 
41
- if first_n is not None:
42
- templates_iter = client.list_email_templates(**filters)
43
- templates_list: list[EmailTemplate] = []
44
- n = 0
45
- while n < first_n:
46
- templates_list.append(await anext(templates_iter))
47
- n += 1
48
- else:
49
- templates_list = [template async for template in client.list_email_templates(**filters)]
41
+ templates_list: list[EmailTemplate] = []
42
+ async for template in client.list_email_templates(**filters):
43
+ templates_list.append(template)
44
+ if first_n is not None and len(templates_list) >= first_n:
45
+ break
50
46
 
51
47
  return templates_list
52
48
 
rossum_mcp/tools/hooks.py CHANGED
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Annotated, Any, Literal
8
8
 
9
9
  from rossum_api.models.hook import Hook, HookRunData, HookType
10
10
 
11
- from rossum_mcp.tools.base import TRUNCATED_MARKER, is_read_write_mode
11
+ from rossum_mcp.tools.base import TRUNCATED_MARKER, delete_resource, is_read_write_mode
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from fastmcp import FastMCP
@@ -56,15 +56,11 @@ async def _list_hooks(
56
56
  if active is not None:
57
57
  filters["active"] = active
58
58
 
59
- if first_n is not None:
60
- hooks_iter = client.list_hooks(**filters)
61
- hooks_list: list[Hook] = []
62
- n = 0
63
- while n < first_n:
64
- hooks_list.append(await anext(hooks_iter))
65
- n += 1
66
- else:
67
- hooks_list = [hook async for hook in client.list_hooks(**filters)]
59
+ hooks_list: list[Hook] = []
60
+ async for hook in client.list_hooks(**filters):
61
+ hooks_list.append(hook)
62
+ if first_n is not None and len(hooks_list) >= first_n:
63
+ break
68
64
 
69
65
  return hooks_list
70
66
 
@@ -238,6 +234,10 @@ async def _create_hook_from_template(
238
234
  return {"error": "Hook wasn't likely created. Hook ID not available."}
239
235
 
240
236
 
237
+ async def _delete_hook(client: AsyncRossumAPIClient, hook_id: int) -> dict:
238
+ return await delete_resource("hook", hook_id, client.delete_hook)
239
+
240
+
241
241
  def register_hook_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
242
242
  """Register hook-related tools with the FastMCP server."""
243
243
 
@@ -256,7 +256,7 @@ def register_hook_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
256
256
  return await _list_hooks(client, queue_id, active, first_n)
257
257
 
258
258
  @mcp.tool(
259
- description="Create a new hook. If token_owner is provided, organization_group_admin users CANNOT be used (API will reject)."
259
+ description="Create a new hook. For function hooks: 'source' in config is auto-renamed to 'function', runtime defaults to 'python3.12', timeout_s is capped at 60s. If token_owner is provided, organization_group_admin users CANNOT be used."
260
260
  )
261
261
  async def create_hook(
262
262
  name: str,
@@ -341,3 +341,7 @@ def register_hook_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
341
341
  token_owner: str | None = None,
342
342
  ) -> Hook | dict:
343
343
  return await _create_hook_from_template(client, name, hook_template_id, queues, events, token_owner)
344
+
345
+ @mcp.tool(description="Delete a hook.")
346
+ async def delete_hook(hook_id: int) -> dict:
347
+ return await _delete_hook(client, hook_id)
@@ -14,7 +14,7 @@ from rossum_api.models.engine import Engine
14
14
  from rossum_api.models.queue import Queue
15
15
  from rossum_api.models.schema import Schema
16
16
 
17
- from rossum_mcp.tools.base import build_resource_url, is_read_write_mode, truncate_dict_fields
17
+ from rossum_mcp.tools.base import build_resource_url, delete_resource, is_read_write_mode, truncate_dict_fields
18
18
 
19
19
  if TYPE_CHECKING:
20
20
  from fastmcp import FastMCP
@@ -46,10 +46,15 @@ async def _get_queue(client: AsyncRossumAPIClient, queue_id: int) -> Queue:
46
46
 
47
47
 
48
48
  async def _list_queues(
49
- client: AsyncRossumAPIClient, workspace_id: int | None = None, name: str | None = None
49
+ client: AsyncRossumAPIClient,
50
+ id: str | None = None,
51
+ workspace_id: int | None = None,
52
+ name: str | None = None,
50
53
  ) -> list[Queue]:
51
- logger.debug(f"Listing queues: workspace_id={workspace_id}, name={name}")
54
+ logger.debug(f"Listing queues: id={id}, workspace_id={workspace_id}, name={name}")
52
55
  filters: dict[str, int | str] = {}
56
+ if id is not None:
57
+ filters["id"] = id
53
58
  if workspace_id is not None:
54
59
  filters["workspace"] = workspace_id
55
60
  if name is not None:
@@ -155,6 +160,12 @@ async def _update_queue(client: AsyncRossumAPIClient, queue_id: int, queue_data:
155
160
  return cast("Queue", client._deserializer(Resource.Queue, updated_queue_data))
156
161
 
157
162
 
163
+ async def _delete_queue(client: AsyncRossumAPIClient, queue_id: int) -> dict:
164
+ return await delete_resource(
165
+ "queue", queue_id, client.delete_queue, f"Queue {queue_id} scheduled for deletion (starts after 24 hours)"
166
+ )
167
+
168
+
158
169
  # Available template names for create_queue_from_template
159
170
  QueueTemplateName = Literal[
160
171
  "EU Demo Template",
@@ -227,9 +238,11 @@ def register_queue_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
227
238
  async def get_queue(queue_id: int) -> Queue:
228
239
  return await _get_queue(client, queue_id)
229
240
 
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)
241
+ @mcp.tool(description="List all queues with optional filters. id accepts comma-separated values (e.g. '1,2,3').")
242
+ async def list_queues(
243
+ id: str | None = None, workspace_id: int | None = None, name: str | None = None
244
+ ) -> list[Queue]:
245
+ return await _list_queues(client, id, workspace_id, name)
233
246
 
234
247
  @mcp.tool(description="Retrieve queue schema.")
235
248
  async def get_queue_schema(queue_id: int) -> Schema:
@@ -272,6 +285,12 @@ def register_queue_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
272
285
  async def update_queue(queue_id: int, queue_data: dict) -> Queue | dict:
273
286
  return await _update_queue(client, queue_id, queue_data)
274
287
 
288
+ @mcp.tool(
289
+ description="Delete a queue. Deletion starts after 24 hours. Also deletes all related objects (annotations, documents)."
290
+ )
291
+ async def delete_queue(queue_id: int) -> dict:
292
+ return await _delete_queue(client, queue_id)
293
+
275
294
  @mcp.tool(description="Get available queue template names for create_queue_from_template.")
276
295
  async def get_queue_template_names() -> list[str]:
277
296
  return list(QUEUE_TEMPLATE_NAMES)
rossum_mcp/tools/rules.py CHANGED
@@ -7,6 +7,8 @@ from typing import TYPE_CHECKING
7
7
 
8
8
  from rossum_api.models.rule import Rule
9
9
 
10
+ from rossum_mcp.tools.base import delete_resource
11
+
10
12
  if TYPE_CHECKING:
11
13
  from fastmcp import FastMCP
12
14
  from rossum_api import AsyncRossumAPIClient
@@ -39,6 +41,10 @@ async def _list_rules(
39
41
  return rules_list
40
42
 
41
43
 
44
+ async def _delete_rule(client: AsyncRossumAPIClient, rule_id: int) -> dict:
45
+ return await delete_resource("rule", rule_id, client.delete_rule)
46
+
47
+
42
48
  def register_rule_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
43
49
  """Register rule-related tools with the FastMCP server."""
44
50
 
@@ -51,3 +57,7 @@ def register_rule_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
51
57
  schema_id: int | None = None, organization_id: int | None = None, enabled: bool | None = None
52
58
  ) -> list[Rule]:
53
59
  return await _list_rules(client, schema_id, organization_id, enabled)
60
+
61
+ @mcp.tool(description="Delete a rule.")
62
+ async def delete_rule(rule_id: int) -> dict:
63
+ return await _delete_rule(client, rule_id)
@@ -7,10 +7,11 @@ import logging
7
7
  from dataclasses import asdict, dataclass, is_dataclass, replace
8
8
  from typing import TYPE_CHECKING, Any, Literal
9
9
 
10
+ from rossum_api import APIClientError
10
11
  from rossum_api.domain_logic.resources import Resource
11
12
  from rossum_api.models.schema import Schema
12
13
 
13
- from rossum_mcp.tools.base import TRUNCATED_MARKER, is_read_write_mode
14
+ from rossum_mcp.tools.base import TRUNCATED_MARKER, delete_resource, is_read_write_mode
14
15
 
15
16
  if TYPE_CHECKING:
16
17
  from fastmcp import FastMCP
@@ -427,9 +428,14 @@ def apply_schema_patch(
427
428
  return content
428
429
 
429
430
 
430
- async def _get_schema(client: AsyncRossumAPIClient, schema_id: int) -> Schema:
431
- schema: Schema = await client.retrieve_schema(schema_id)
432
- return schema
431
+ async def _get_schema(client: AsyncRossumAPIClient, schema_id: int) -> Schema | dict:
432
+ try:
433
+ schema: Schema = await client.retrieve_schema(schema_id)
434
+ return schema
435
+ except APIClientError as e:
436
+ if e.status_code == 404:
437
+ return {"error": f"Schema {schema_id} not found"}
438
+ raise
433
439
 
434
440
 
435
441
  @dataclass
@@ -666,8 +672,10 @@ async def _patch_schema(
666
672
  return updated_schema
667
673
 
668
674
 
669
- async def _get_schema_tree_structure(client: AsyncRossumAPIClient, schema_id: int) -> list[dict]:
675
+ async def _get_schema_tree_structure(client: AsyncRossumAPIClient, schema_id: int) -> list[dict] | dict:
670
676
  schema = await _get_schema(client, schema_id)
677
+ if isinstance(schema, dict):
678
+ return schema
671
679
  content_dicts: list[dict[str, Any]] = [
672
680
  asdict(section) if is_dataclass(section) else dict(section) # type: ignore[arg-type]
673
681
  for section in schema.content
@@ -715,11 +723,15 @@ async def _prune_schema_fields(
715
723
  return {"removed_fields": sorted(removed), "remaining_fields": sorted(remaining_ids)}
716
724
 
717
725
 
726
+ async def _delete_schema(client: AsyncRossumAPIClient, schema_id: int) -> dict:
727
+ return await delete_resource("schema", schema_id, client.delete_schema)
728
+
729
+
718
730
  def register_schema_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
719
731
  """Register schema-related tools with the FastMCP server."""
720
732
 
721
733
  @mcp.tool(description="Retrieve schema details.")
722
- async def get_schema(schema_id: int) -> Schema:
734
+ async def get_schema(schema_id: int) -> Schema | dict:
723
735
  return await _get_schema(client, schema_id)
724
736
 
725
737
  @mcp.tool(description="List all schemas with optional filters.")
@@ -764,7 +776,7 @@ Important: Datapoints inside a tuple MUST have an "id" field. Section-level data
764
776
  return await _patch_schema(client, schema_id, operation, node_id, node_data, parent_id, position)
765
777
 
766
778
  @mcp.tool(description="Get lightweight tree structure of schema with only ids, labels, categories, and types.")
767
- async def get_schema_tree_structure(schema_id: int) -> list[dict]:
779
+ async def get_schema_tree_structure(schema_id: int) -> list[dict] | dict:
768
780
  return await _get_schema_tree_structure(client, schema_id)
769
781
 
770
782
  @mcp.tool(
@@ -782,3 +794,7 @@ Returns dict with removed_fields and remaining_fields lists. Sections cannot be
782
794
  fields_to_remove: list[str] | None = None,
783
795
  ) -> dict:
784
796
  return await _prune_schema_fields(client, schema_id, fields_to_keep, fields_to_remove)
797
+
798
+ @mcp.tool(description="Delete a schema. Fails if schema is linked to a queue or annotation (HTTP 409).")
799
+ async def delete_schema(schema_id: int) -> dict:
800
+ return await _delete_schema(client, schema_id)
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
 
8
8
  from rossum_api.models.workspace import Workspace
9
9
 
10
- from rossum_mcp.tools.base import build_resource_url, is_read_write_mode
10
+ from rossum_mcp.tools.base import build_resource_url, delete_resource, is_read_write_mode
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from fastmcp import FastMCP
@@ -59,6 +59,10 @@ async def _create_workspace(
59
59
  return workspace
60
60
 
61
61
 
62
+ async def _delete_workspace(client: AsyncRossumAPIClient, workspace_id: int) -> dict:
63
+ return await delete_resource("workspace", workspace_id, client.delete_workspace)
64
+
65
+
62
66
  def register_workspace_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
63
67
  """Register workspace-related tools with the FastMCP server."""
64
68
 
@@ -73,3 +77,7 @@ def register_workspace_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None
73
77
  @mcp.tool(description="Create a new workspace.")
74
78
  async def create_workspace(name: str, organization_id: int, metadata: dict | None = None) -> Workspace | dict:
75
79
  return await _create_workspace(client, name, organization_id, metadata)
80
+
81
+ @mcp.tool(description="Delete a workspace. Fails if workspace contains queues.")
82
+ async def delete_workspace(workspace_id: int) -> dict:
83
+ return await _delete_workspace(client, workspace_id)