rossum-mcp 0.3.5__py3-none-any.whl → 1.0.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 +2 -0
- rossum_mcp/tools/__init__.py +12 -0
- rossum_mcp/tools/annotations.py +11 -1
- rossum_mcp/tools/base.py +33 -0
- rossum_mcp/tools/catalog.py +176 -0
- rossum_mcp/tools/discovery.py +37 -0
- rossum_mcp/tools/email_templates.py +5 -9
- rossum_mcp/tools/hooks.py +15 -11
- rossum_mcp/tools/queues.py +31 -19
- rossum_mcp/tools/rules.py +10 -0
- rossum_mcp/tools/schemas.py +27 -8
- rossum_mcp/tools/workspaces.py +10 -5
- rossum_mcp-1.0.0.dist-info/METADATA +241 -0
- rossum_mcp-1.0.0.dist-info/RECORD +25 -0
- {rossum_mcp-0.3.5.dist-info → rossum_mcp-1.0.0.dist-info}/WHEEL +1 -1
- rossum_mcp-1.0.0.dist-info/licenses/LICENSE +21 -0
- rossum_mcp-0.3.5.dist-info/METADATA +0 -1787
- rossum_mcp-0.3.5.dist-info/RECORD +0 -22
- {rossum_mcp-0.3.5.dist-info → rossum_mcp-1.0.0.dist-info}/entry_points.txt +0 -0
- {rossum_mcp-0.3.5.dist-info → rossum_mcp-1.0.0.dist-info}/top_level.txt +0 -0
rossum_mcp/__init__.py
CHANGED
rossum_mcp/server.py
CHANGED
|
@@ -17,6 +17,7 @@ from rossum_api.dtos import Token
|
|
|
17
17
|
from rossum_mcp.logging_config import setup_logging
|
|
18
18
|
from rossum_mcp.tools import (
|
|
19
19
|
register_annotation_tools,
|
|
20
|
+
register_discovery_tools,
|
|
20
21
|
register_document_relation_tools,
|
|
21
22
|
register_email_template_tools,
|
|
22
23
|
register_engine_tools,
|
|
@@ -45,6 +46,7 @@ logger.info(f"Rossum MCP Server starting in {MODE} mode")
|
|
|
45
46
|
mcp = FastMCP("rossum-mcp-server")
|
|
46
47
|
client = AsyncRossumAPIClient(base_url=BASE_URL, credentials=Token(token=API_TOKEN))
|
|
47
48
|
|
|
49
|
+
register_discovery_tools(mcp)
|
|
48
50
|
register_annotation_tools(mcp, client)
|
|
49
51
|
register_queue_tools(mcp, client)
|
|
50
52
|
register_schema_tools(mcp, client)
|
rossum_mcp/tools/__init__.py
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from rossum_mcp.tools.annotations import register_annotation_tools
|
|
6
|
+
from rossum_mcp.tools.catalog import (
|
|
7
|
+
TOOL_CATALOG,
|
|
8
|
+
ToolCategory,
|
|
9
|
+
ToolInfo,
|
|
10
|
+
get_catalog_summary,
|
|
11
|
+
)
|
|
12
|
+
from rossum_mcp.tools.discovery import register_discovery_tools
|
|
6
13
|
from rossum_mcp.tools.document_relations import register_document_relation_tools
|
|
7
14
|
from rossum_mcp.tools.email_templates import register_email_template_tools
|
|
8
15
|
from rossum_mcp.tools.engines import register_engine_tools
|
|
@@ -15,7 +22,12 @@ from rossum_mcp.tools.users import register_user_tools
|
|
|
15
22
|
from rossum_mcp.tools.workspaces import register_workspace_tools
|
|
16
23
|
|
|
17
24
|
__all__ = [
|
|
25
|
+
"TOOL_CATALOG",
|
|
26
|
+
"ToolCategory",
|
|
27
|
+
"ToolInfo",
|
|
28
|
+
"get_catalog_summary",
|
|
18
29
|
"register_annotation_tools",
|
|
30
|
+
"register_discovery_tools",
|
|
19
31
|
"register_document_relation_tools",
|
|
20
32
|
"register_email_template_tools",
|
|
21
33
|
"register_engine_tools",
|
rossum_mcp/tools/annotations.py
CHANGED
|
@@ -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}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Tool catalog for dynamic tool discovery.
|
|
2
|
+
|
|
3
|
+
Provides lightweight metadata for all MCP tools organized by category.
|
|
4
|
+
This is the single source of truth for tool categorization - the agent
|
|
5
|
+
fetches this catalog from MCP to avoid data duplication.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class ToolInfo:
|
|
15
|
+
"""Lightweight tool metadata for catalog."""
|
|
16
|
+
|
|
17
|
+
name: str
|
|
18
|
+
description: str
|
|
19
|
+
destructive: bool = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ToolCategory:
|
|
24
|
+
"""A category of related tools."""
|
|
25
|
+
|
|
26
|
+
name: str
|
|
27
|
+
description: str
|
|
28
|
+
tools: list[ToolInfo]
|
|
29
|
+
keywords: list[str]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Tool catalog organized by functional category
|
|
33
|
+
# Keywords enable automatic pre-loading based on user request text
|
|
34
|
+
TOOL_CATALOG: dict[str, ToolCategory] = {
|
|
35
|
+
"annotations": ToolCategory(
|
|
36
|
+
name="annotations",
|
|
37
|
+
description="Document processing: upload, retrieve, update, and confirm annotations",
|
|
38
|
+
tools=[
|
|
39
|
+
ToolInfo("upload_document", "Upload document to queue"),
|
|
40
|
+
ToolInfo("get_annotation", "Retrieve annotation with extracted data"),
|
|
41
|
+
ToolInfo("list_annotations", "List annotations for a queue"),
|
|
42
|
+
ToolInfo("start_annotation", "Start annotation (to_review -> reviewing)"),
|
|
43
|
+
ToolInfo("bulk_update_annotation_fields", "Bulk update annotation fields"),
|
|
44
|
+
ToolInfo("confirm_annotation", "Confirm annotation (-> confirmed)"),
|
|
45
|
+
ToolInfo("delete_annotation", "Delete annotation (soft delete)", destructive=True),
|
|
46
|
+
],
|
|
47
|
+
keywords=["annotation", "document", "upload", "extract", "confirm", "review"],
|
|
48
|
+
),
|
|
49
|
+
"queues": ToolCategory(
|
|
50
|
+
name="queues",
|
|
51
|
+
description="Queue management: create, configure, and list document processing queues",
|
|
52
|
+
tools=[
|
|
53
|
+
ToolInfo("get_queue", "Retrieve queue details"),
|
|
54
|
+
ToolInfo("list_queues", "List all queues"),
|
|
55
|
+
ToolInfo("get_queue_schema", "Get queue's schema"),
|
|
56
|
+
ToolInfo("get_queue_engine", "Get queue's AI engine"),
|
|
57
|
+
ToolInfo("create_queue", "Create a queue"),
|
|
58
|
+
ToolInfo("update_queue", "Update queue settings"),
|
|
59
|
+
ToolInfo("get_queue_template_names", "List available queue templates"),
|
|
60
|
+
ToolInfo("create_queue_from_template", "Create queue from template"),
|
|
61
|
+
ToolInfo("delete_queue", "Delete queue (24h delayed)", destructive=True),
|
|
62
|
+
],
|
|
63
|
+
keywords=["queue", "inbox", "connector"],
|
|
64
|
+
),
|
|
65
|
+
"schemas": ToolCategory(
|
|
66
|
+
name="schemas",
|
|
67
|
+
description="Schema management: define and modify document field structures",
|
|
68
|
+
tools=[
|
|
69
|
+
ToolInfo("get_schema", "Retrieve schema details"),
|
|
70
|
+
ToolInfo("list_schemas", "List all schemas"),
|
|
71
|
+
ToolInfo("update_schema", "Update schema"),
|
|
72
|
+
ToolInfo("create_schema", "Create new schema"),
|
|
73
|
+
ToolInfo("patch_schema", "Add/update/remove schema fields"),
|
|
74
|
+
ToolInfo("get_schema_tree_structure", "Get lightweight schema tree"),
|
|
75
|
+
ToolInfo("prune_schema_fields", "Bulk remove schema fields"),
|
|
76
|
+
ToolInfo("delete_schema", "Delete schema", destructive=True),
|
|
77
|
+
],
|
|
78
|
+
keywords=["schema", "field", "datapoint", "section", "multivalue", "tuple"],
|
|
79
|
+
),
|
|
80
|
+
"engines": ToolCategory(
|
|
81
|
+
name="engines",
|
|
82
|
+
description="AI engine management: create and configure extraction/splitting engines",
|
|
83
|
+
tools=[
|
|
84
|
+
ToolInfo("get_engine", "Retrieve engine details"),
|
|
85
|
+
ToolInfo("list_engines", "List all engines"),
|
|
86
|
+
ToolInfo("update_engine", "Update engine settings"),
|
|
87
|
+
ToolInfo("create_engine", "Create new engine"),
|
|
88
|
+
ToolInfo("create_engine_field", "Create engine field mapping"),
|
|
89
|
+
ToolInfo("get_engine_fields", "List engine fields"),
|
|
90
|
+
],
|
|
91
|
+
keywords=["engine", "ai", "extractor", "splitter", "training"],
|
|
92
|
+
),
|
|
93
|
+
"hooks": ToolCategory(
|
|
94
|
+
name="hooks",
|
|
95
|
+
description="Extensions/webhooks: create and manage automation hooks",
|
|
96
|
+
tools=[
|
|
97
|
+
ToolInfo("get_hook", "Retrieve hook details with code"),
|
|
98
|
+
ToolInfo("list_hooks", "List all hooks for a queue"),
|
|
99
|
+
ToolInfo("create_hook", "Create new hook"),
|
|
100
|
+
ToolInfo("update_hook", "Update hook configuration"),
|
|
101
|
+
ToolInfo("list_hook_logs", "View hook execution logs"),
|
|
102
|
+
ToolInfo("list_hook_templates", "List Rossum Store templates"),
|
|
103
|
+
ToolInfo("create_hook_from_template", "Create hook from template"),
|
|
104
|
+
ToolInfo("delete_hook", "Delete hook", destructive=True),
|
|
105
|
+
],
|
|
106
|
+
keywords=["hook", "extension", "webhook", "automation", "function", "serverless"],
|
|
107
|
+
),
|
|
108
|
+
"email_templates": ToolCategory(
|
|
109
|
+
name="email_templates",
|
|
110
|
+
description="Email templates: configure automated email responses",
|
|
111
|
+
tools=[
|
|
112
|
+
ToolInfo("get_email_template", "Retrieve email template"),
|
|
113
|
+
ToolInfo("list_email_templates", "List email templates"),
|
|
114
|
+
ToolInfo("create_email_template", "Create email template"),
|
|
115
|
+
],
|
|
116
|
+
keywords=["email", "template", "notification", "rejection"],
|
|
117
|
+
),
|
|
118
|
+
"document_relations": ToolCategory(
|
|
119
|
+
name="document_relations",
|
|
120
|
+
description="Document relations: manage export/einvoice document links",
|
|
121
|
+
tools=[
|
|
122
|
+
ToolInfo("get_document_relation", "Retrieve document relation"),
|
|
123
|
+
ToolInfo("list_document_relations", "List document relations"),
|
|
124
|
+
],
|
|
125
|
+
keywords=["document relation", "export", "einvoice"],
|
|
126
|
+
),
|
|
127
|
+
"relations": ToolCategory(
|
|
128
|
+
name="relations",
|
|
129
|
+
description="Annotation relations: manage edit/attachment/duplicate links",
|
|
130
|
+
tools=[
|
|
131
|
+
ToolInfo("get_relation", "Retrieve relation details"),
|
|
132
|
+
ToolInfo("list_relations", "List annotation relations"),
|
|
133
|
+
],
|
|
134
|
+
keywords=["relation", "duplicate", "attachment"],
|
|
135
|
+
),
|
|
136
|
+
"rules": ToolCategory(
|
|
137
|
+
name="rules",
|
|
138
|
+
description="Validation rules: manage schema validation rules",
|
|
139
|
+
tools=[
|
|
140
|
+
ToolInfo("get_rule", "Retrieve rule details"),
|
|
141
|
+
ToolInfo("list_rules", "List validation rules"),
|
|
142
|
+
ToolInfo("delete_rule", "Delete rule", destructive=True),
|
|
143
|
+
],
|
|
144
|
+
keywords=["rule", "validation", "constraint"],
|
|
145
|
+
),
|
|
146
|
+
"users": ToolCategory(
|
|
147
|
+
name="users",
|
|
148
|
+
description="User management: list users and roles",
|
|
149
|
+
tools=[
|
|
150
|
+
ToolInfo("get_user", "Retrieve user details"),
|
|
151
|
+
ToolInfo("list_users", "List users with filters"),
|
|
152
|
+
ToolInfo("list_user_roles", "List available user roles"),
|
|
153
|
+
],
|
|
154
|
+
keywords=["user", "role", "permission", "token_owner"],
|
|
155
|
+
),
|
|
156
|
+
"workspaces": ToolCategory(
|
|
157
|
+
name="workspaces",
|
|
158
|
+
description="Workspace management: organize queues into workspaces",
|
|
159
|
+
tools=[
|
|
160
|
+
ToolInfo("get_workspace", "Retrieve workspace details"),
|
|
161
|
+
ToolInfo("list_workspaces", "List all workspaces"),
|
|
162
|
+
ToolInfo("create_workspace", "Create new workspace"),
|
|
163
|
+
ToolInfo("delete_workspace", "Delete workspace", destructive=True),
|
|
164
|
+
],
|
|
165
|
+
keywords=["workspace", "organization"],
|
|
166
|
+
),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def get_catalog_summary() -> str:
|
|
171
|
+
"""Get a compact text summary of all tool categories for the system prompt."""
|
|
172
|
+
lines = ["Available MCP tool categories (use `list_tool_categories` for details):"]
|
|
173
|
+
for category in TOOL_CATALOG.values():
|
|
174
|
+
tool_names = ", ".join(t.name for t in category.tools)
|
|
175
|
+
lines.append(f"- **{category.name}**: {category.description} [{tool_names}]")
|
|
176
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Discovery tools for dynamic tool loading.
|
|
2
|
+
|
|
3
|
+
Provides MCP tool to explore available tool categories and their metadata.
|
|
4
|
+
The agent uses this to fetch the catalog and load tools on-demand.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import asdict
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from rossum_mcp.tools.catalog import TOOL_CATALOG
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from fastmcp import FastMCP
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def register_discovery_tools(mcp: FastMCP) -> None:
|
|
19
|
+
"""Register discovery tools with the FastMCP server."""
|
|
20
|
+
|
|
21
|
+
@mcp.tool(
|
|
22
|
+
description="List all available tool categories with descriptions, tool names, and keywords. "
|
|
23
|
+
"Use this to discover what tools are available, then use load_tool_category to load "
|
|
24
|
+
"tools from specific categories before using them. Tools with destructive=true require "
|
|
25
|
+
"explicit user request to use (delete operations)."
|
|
26
|
+
)
|
|
27
|
+
async def list_tool_categories() -> list[dict]:
|
|
28
|
+
return [
|
|
29
|
+
{
|
|
30
|
+
"name": category.name,
|
|
31
|
+
"description": category.description,
|
|
32
|
+
"tool_count": len(category.tools),
|
|
33
|
+
"tools": [asdict(tool) for tool in category.tools],
|
|
34
|
+
"keywords": category.keywords,
|
|
35
|
+
}
|
|
36
|
+
for category in TOOL_CATALOG.values()
|
|
37
|
+
]
|
|
@@ -38,15 +38,11 @@ async def _list_email_templates(
|
|
|
38
38
|
if name is not None:
|
|
39
39
|
filters["name"] = name
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
templates_list
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
hooks_list
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
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)
|
rossum_mcp/tools/queues.py
CHANGED
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
7
|
from dataclasses import replace
|
|
8
|
-
from typing import TYPE_CHECKING, cast
|
|
8
|
+
from typing import TYPE_CHECKING, Literal, cast, get_args
|
|
9
9
|
|
|
10
10
|
from rossum_api import APIClientError
|
|
11
11
|
from rossum_api.domain_logic.resources import Resource
|
|
@@ -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,
|
|
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,8 +160,14 @@ 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",
|
|
161
172
|
"AP&R EU Demo Template",
|
|
162
173
|
"Tax Invoice EU Demo Template",
|
|
@@ -177,13 +188,14 @@ QUEUE_TEMPLATE_NAMES = (
|
|
|
177
188
|
"Credit Note Demo Template",
|
|
178
189
|
"Debit Note Demo Template",
|
|
179
190
|
"Proforma Invoice Demo Template",
|
|
180
|
-
|
|
191
|
+
]
|
|
192
|
+
QUEUE_TEMPLATE_NAMES = get_args(QueueTemplateName)
|
|
181
193
|
|
|
182
194
|
|
|
183
195
|
async def _create_queue_from_template(
|
|
184
196
|
client: AsyncRossumAPIClient,
|
|
185
197
|
name: str,
|
|
186
|
-
template_name:
|
|
198
|
+
template_name: QueueTemplateName,
|
|
187
199
|
workspace_id: int,
|
|
188
200
|
include_documents: bool = False,
|
|
189
201
|
engine_id: int | None = None,
|
|
@@ -226,9 +238,11 @@ def register_queue_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
|
|
|
226
238
|
async def get_queue(queue_id: int) -> Queue:
|
|
227
239
|
return await _get_queue(client, queue_id)
|
|
228
240
|
|
|
229
|
-
@mcp.tool(description="List all queues with optional filters.")
|
|
230
|
-
async def list_queues(
|
|
231
|
-
|
|
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)
|
|
232
246
|
|
|
233
247
|
@mcp.tool(description="Retrieve queue schema.")
|
|
234
248
|
async def get_queue_schema(queue_id: int) -> Schema:
|
|
@@ -271,6 +285,12 @@ def register_queue_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
|
|
|
271
285
|
async def update_queue(queue_id: int, queue_data: dict) -> Queue | dict:
|
|
272
286
|
return await _update_queue(client, queue_id, queue_data)
|
|
273
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
|
+
|
|
274
294
|
@mcp.tool(description="Get available queue template names for create_queue_from_template.")
|
|
275
295
|
async def get_queue_template_names() -> list[str]:
|
|
276
296
|
return list(QUEUE_TEMPLATE_NAMES)
|
|
@@ -281,19 +301,11 @@ def register_queue_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
|
|
|
281
301
|
)
|
|
282
302
|
async def create_queue_from_template(
|
|
283
303
|
name: str,
|
|
284
|
-
template_name:
|
|
304
|
+
template_name: QueueTemplateName,
|
|
285
305
|
workspace_id: int,
|
|
286
306
|
include_documents: bool = False,
|
|
287
307
|
engine_id: int | None = None,
|
|
288
308
|
) -> Queue | dict:
|
|
289
|
-
"""
|
|
290
|
-
Available templates: EU Demo Template, AP&R EU Demo Template, Tax Invoice EU Demo Template,
|
|
291
|
-
US Demo Template, AP&R US Demo Template, Tax Invoice US Demo Template, UK Demo Template,
|
|
292
|
-
AP&R UK Demo Template, Tax Invoice UK Demo Template, CZ Demo Template, Empty Organization Template,
|
|
293
|
-
Delivery Notes Demo Template, Delivery Note Demo Template, Chinese Invoices (Fapiao) Demo Template,
|
|
294
|
-
Tax Invoice CN Demo Template, Certificates of Analysis Demo Template, Purchase Order Demo Template,
|
|
295
|
-
Credit Note Demo Template, Debit Note Demo Template, Proforma Invoice Demo Template.
|
|
296
|
-
"""
|
|
297
309
|
return await _create_queue_from_template(
|
|
298
310
|
client, name, template_name, workspace_id, include_documents, engine_id
|
|
299
311
|
)
|
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)
|
rossum_mcp/tools/schemas.py
CHANGED
|
@@ -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
|
-
|
|
432
|
-
|
|
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.")
|
|
@@ -737,6 +749,8 @@ def register_schema_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
|
|
|
737
749
|
@mcp.tool(
|
|
738
750
|
description="""Patch schema nodes (add/update/remove fields in a schema).
|
|
739
751
|
|
|
752
|
+
You MUST load `schema-patching` skill first to avoid errors.
|
|
753
|
+
|
|
740
754
|
Operations:
|
|
741
755
|
- add: Create new field. Requires parent_id (section or tuple id) and node_data.
|
|
742
756
|
- update: Modify existing field. Requires node_data with fields to change.
|
|
@@ -748,7 +762,8 @@ Node types for add:
|
|
|
748
762
|
- Multivalue (table): {"label": "Table", "category": "multivalue", "children": <tuple>}
|
|
749
763
|
- Tuple (table row): {"id": "row_id", "label": "Row", "category": "tuple", "children": [<datapoints with id>]}
|
|
750
764
|
|
|
751
|
-
Important: Datapoints inside a tuple MUST have an "id" field. Section-level datapoints get id from node_id parameter.
|
|
765
|
+
Important: Datapoints inside a tuple MUST have an "id" field. Section-level datapoints get id from node_id parameter.
|
|
766
|
+
"""
|
|
752
767
|
)
|
|
753
768
|
async def patch_schema(
|
|
754
769
|
schema_id: int,
|
|
@@ -761,7 +776,7 @@ Important: Datapoints inside a tuple MUST have an "id" field. Section-level data
|
|
|
761
776
|
return await _patch_schema(client, schema_id, operation, node_id, node_data, parent_id, position)
|
|
762
777
|
|
|
763
778
|
@mcp.tool(description="Get lightweight tree structure of schema with only ids, labels, categories, and types.")
|
|
764
|
-
async def get_schema_tree_structure(schema_id: int) -> list[dict]:
|
|
779
|
+
async def get_schema_tree_structure(schema_id: int) -> list[dict] | dict:
|
|
765
780
|
return await _get_schema_tree_structure(client, schema_id)
|
|
766
781
|
|
|
767
782
|
@mcp.tool(
|
|
@@ -779,3 +794,7 @@ Returns dict with removed_fields and remaining_fields lists. Sections cannot be
|
|
|
779
794
|
fields_to_remove: list[str] | None = None,
|
|
780
795
|
) -> dict:
|
|
781
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)
|