rossum-mcp 1.0.1__tar.gz → 1.1.0__tar.gz

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.
Files changed (33) hide show
  1. {rossum_mcp-1.0.1/rossum_mcp.egg-info → rossum_mcp-1.1.0}/PKG-INFO +26 -6
  2. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/README.md +25 -5
  3. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/pyproject.toml +1 -1
  4. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/__init__.py +1 -1
  5. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/server.py +18 -5
  6. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/base.py +21 -2
  7. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/catalog.py +3 -0
  8. rossum_mcp-1.1.0/rossum_mcp/tools/rules.py +241 -0
  9. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0/rossum_mcp.egg-info}/PKG-INFO +26 -6
  10. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/tests/test_catalog.py +3 -0
  11. rossum_mcp-1.0.1/rossum_mcp/tools/rules.py +0 -63
  12. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/LICENSE +0 -0
  13. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/logging_config.py +0 -0
  14. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/py.typed +0 -0
  15. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/__init__.py +0 -0
  16. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/annotations.py +0 -0
  17. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/discovery.py +0 -0
  18. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/document_relations.py +0 -0
  19. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/email_templates.py +0 -0
  20. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/engines.py +0 -0
  21. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/hooks.py +0 -0
  22. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/queues.py +0 -0
  23. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/relations.py +0 -0
  24. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/schemas.py +0 -0
  25. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/users.py +0 -0
  26. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp/tools/workspaces.py +0 -0
  27. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp.egg-info/SOURCES.txt +0 -0
  28. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp.egg-info/dependency_links.txt +0 -0
  29. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp.egg-info/entry_points.txt +0 -0
  30. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp.egg-info/requires.txt +0 -0
  31. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/rossum_mcp.egg-info/top_level.txt +0 -0
  32. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/setup.cfg +0 -0
  33. {rossum_mcp-1.0.1 → rossum_mcp-1.1.0}/tests/test_logging_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rossum-mcp
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Summary: MCP server for AI-powered Rossum orchestration: document workflows, debug pipelines automatically, and configure intelligent document processing through natural language.
5
5
  Author-email: "Dan Stancl (Rossum AI)" <daniel.stancl@gmail.com>
6
6
  License: MIT
@@ -51,14 +51,14 @@ Dynamic: license-file
51
51
 
52
52
  <div align="center">
53
53
 
54
- **MCP server for AI-powered Rossum document processing. 56 tools for queues, schemas, hooks, engines, and more.**
54
+ **MCP server for AI-powered Rossum document processing. 59 tools for queues, schemas, hooks, engines, and more.**
55
55
 
56
56
  [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://stancld.github.io/rossum-agents/)
57
57
  [![Python](https://img.shields.io/pypi/pyversions/rossum-mcp.svg)](https://pypi.org/project/rossum-mcp/)
58
58
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
59
59
  [![PyPI - rossum-mcp](https://img.shields.io/pypi/v/rossum-mcp?label=rossum-mcp)](https://pypi.org/project/rossum-mcp/)
60
60
  [![Coverage](https://codecov.io/gh/stancld/rossum-agents/branch/master/graph/badge.svg?flag=rossum-mcp)](https://codecov.io/gh/stancld/rossum-agents)
61
- [![MCP Tools](https://img.shields.io/badge/MCP_Tools-56-blue.svg)](#available-tools)
61
+ [![MCP Tools](https://img.shields.io/badge/MCP_Tools-59-blue.svg)](#available-tools)
62
62
 
63
63
  [![Rossum API](https://img.shields.io/badge/Rossum-API-orange.svg)](https://github.com/rossumai/rossum-api)
64
64
  [![MCP](https://img.shields.io/badge/MCP-compatible-green.svg)](https://modelcontextprotocol.io/)
@@ -127,9 +127,29 @@ Configure Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_c
127
127
 
128
128
  Set `ROSSUM_MCP_MODE=read-only` to disable all CREATE, UPDATE, and UPLOAD operations. Only GET and LIST operations will be available.
129
129
 
130
+ ### Runtime Mode Switching
131
+
132
+ Two tools allow dynamic mode control:
133
+
134
+ | Tool | Description |
135
+ |------|-------------|
136
+ | `get_mcp_mode` | Returns current operation mode (`read-only` or `read-write`) |
137
+ | `set_mcp_mode` | Switches between modes at runtime |
138
+
139
+ **Use case:** Start in read-only mode for safe exploration, then switch to read-write when ready to make changes.
140
+
141
+ ```
142
+ User: What mode are we in?
143
+ Assistant: [calls get_mcp_mode] → "read-only"
144
+
145
+ User: I'm ready to update the schema now.
146
+ Assistant: [calls set_mcp_mode("read-write")] → Mode switched to read-write
147
+ [calls update_schema(...)]
148
+ ```
149
+
130
150
  ## Available Tools
131
151
 
132
- The server provides **56 tools** organized into categories:
152
+ The server provides **59 tools** organized into categories:
133
153
 
134
154
  | Category | Tools | Description |
135
155
  |----------|-------|-------------|
@@ -137,7 +157,7 @@ The server provides **56 tools** organized into categories:
137
157
  | **Queue Management** | 9 | Create, configure, delete, and list queues |
138
158
  | **Schema Management** | 8 | Define, modify, and delete field structures |
139
159
  | **Engine Management** | 6 | Configure extraction and splitting engines |
140
- | **Extensions & Rules** | 11 | Webhooks, serverless functions, business rules |
160
+ | **Extensions & Rules** | 14 | Webhooks, serverless functions, business rules |
141
161
  | **Workspace Management** | 4 | Organize and delete workspaces |
142
162
  | **User Management** | 3 | List users and roles |
143
163
  | **Relations** | 4 | Annotation and document relations |
@@ -160,7 +180,7 @@ The server provides **56 tools** organized into categories:
160
180
  `get_engine`, `list_engines`, `create_engine`, `update_engine`, `create_engine_field`, `get_engine_fields`
161
181
 
162
182
  **Extensions & Rules:**
163
- `get_hook`, `list_hooks`, `create_hook`, `update_hook`, `list_hook_templates`, `create_hook_from_template`, `list_hook_logs`, `delete_hook`, `get_rule`, `list_rules`, `delete_rule`
183
+ `get_hook`, `list_hooks`, `create_hook`, `update_hook`, `list_hook_templates`, `create_hook_from_template`, `list_hook_logs`, `delete_hook`, `get_rule`, `list_rules`, `create_rule`, `update_rule`, `patch_rule`, `delete_rule`
164
184
 
165
185
  **Workspace Management:**
166
186
  `get_workspace`, `list_workspaces`, `create_workspace`, `delete_workspace`
@@ -2,14 +2,14 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- **MCP server for AI-powered Rossum document processing. 56 tools for queues, schemas, hooks, engines, and more.**
5
+ **MCP server for AI-powered Rossum document processing. 59 tools for queues, schemas, hooks, engines, and more.**
6
6
 
7
7
  [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://stancld.github.io/rossum-agents/)
8
8
  [![Python](https://img.shields.io/pypi/pyversions/rossum-mcp.svg)](https://pypi.org/project/rossum-mcp/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
10
  [![PyPI - rossum-mcp](https://img.shields.io/pypi/v/rossum-mcp?label=rossum-mcp)](https://pypi.org/project/rossum-mcp/)
11
11
  [![Coverage](https://codecov.io/gh/stancld/rossum-agents/branch/master/graph/badge.svg?flag=rossum-mcp)](https://codecov.io/gh/stancld/rossum-agents)
12
- [![MCP Tools](https://img.shields.io/badge/MCP_Tools-56-blue.svg)](#available-tools)
12
+ [![MCP Tools](https://img.shields.io/badge/MCP_Tools-59-blue.svg)](#available-tools)
13
13
 
14
14
  [![Rossum API](https://img.shields.io/badge/Rossum-API-orange.svg)](https://github.com/rossumai/rossum-api)
15
15
  [![MCP](https://img.shields.io/badge/MCP-compatible-green.svg)](https://modelcontextprotocol.io/)
@@ -78,9 +78,29 @@ Configure Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_c
78
78
 
79
79
  Set `ROSSUM_MCP_MODE=read-only` to disable all CREATE, UPDATE, and UPLOAD operations. Only GET and LIST operations will be available.
80
80
 
81
+ ### Runtime Mode Switching
82
+
83
+ Two tools allow dynamic mode control:
84
+
85
+ | Tool | Description |
86
+ |------|-------------|
87
+ | `get_mcp_mode` | Returns current operation mode (`read-only` or `read-write`) |
88
+ | `set_mcp_mode` | Switches between modes at runtime |
89
+
90
+ **Use case:** Start in read-only mode for safe exploration, then switch to read-write when ready to make changes.
91
+
92
+ ```
93
+ User: What mode are we in?
94
+ Assistant: [calls get_mcp_mode] → "read-only"
95
+
96
+ User: I'm ready to update the schema now.
97
+ Assistant: [calls set_mcp_mode("read-write")] → Mode switched to read-write
98
+ [calls update_schema(...)]
99
+ ```
100
+
81
101
  ## Available Tools
82
102
 
83
- The server provides **56 tools** organized into categories:
103
+ The server provides **59 tools** organized into categories:
84
104
 
85
105
  | Category | Tools | Description |
86
106
  |----------|-------|-------------|
@@ -88,7 +108,7 @@ The server provides **56 tools** organized into categories:
88
108
  | **Queue Management** | 9 | Create, configure, delete, and list queues |
89
109
  | **Schema Management** | 8 | Define, modify, and delete field structures |
90
110
  | **Engine Management** | 6 | Configure extraction and splitting engines |
91
- | **Extensions & Rules** | 11 | Webhooks, serverless functions, business rules |
111
+ | **Extensions & Rules** | 14 | Webhooks, serverless functions, business rules |
92
112
  | **Workspace Management** | 4 | Organize and delete workspaces |
93
113
  | **User Management** | 3 | List users and roles |
94
114
  | **Relations** | 4 | Annotation and document relations |
@@ -111,7 +131,7 @@ The server provides **56 tools** organized into categories:
111
131
  `get_engine`, `list_engines`, `create_engine`, `update_engine`, `create_engine_field`, `get_engine_fields`
112
132
 
113
133
  **Extensions & Rules:**
114
- `get_hook`, `list_hooks`, `create_hook`, `update_hook`, `list_hook_templates`, `create_hook_from_template`, `list_hook_logs`, `delete_hook`, `get_rule`, `list_rules`, `delete_rule`
134
+ `get_hook`, `list_hooks`, `create_hook`, `update_hook`, `list_hook_templates`, `create_hook_from_template`, `list_hook_logs`, `delete_hook`, `get_rule`, `list_rules`, `create_rule`, `update_rule`, `patch_rule`, `delete_rule`
115
135
 
116
136
  **Workspace Management:**
117
137
  `get_workspace`, `list_workspaces`, `create_workspace`, `delete_workspace`
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rossum-mcp"
7
- version = "1.0.1"
7
+ version = "1.1.0"
8
8
  description = "MCP server for AI-powered Rossum orchestration: document workflows, debug pipelines automatically, and configure intelligent document processing through natural language."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "1.0.1"
3
+ __version__ = "1.1.0"
@@ -29,6 +29,7 @@ from rossum_mcp.tools import (
29
29
  register_user_tools,
30
30
  register_workspace_tools,
31
31
  )
32
+ from rossum_mcp.tools.base import get_mcp_mode, set_mcp_mode
32
33
 
33
34
  setup_logging(app_name="rossum-mcp-server", log_level="DEBUG", use_console=False)
34
35
 
@@ -36,12 +37,8 @@ logger = logging.getLogger(__name__)
36
37
 
37
38
  BASE_URL = os.environ["ROSSUM_API_BASE_URL"].rstrip("/")
38
39
  API_TOKEN = os.environ["ROSSUM_API_TOKEN"]
39
- MODE = os.environ.get("ROSSUM_MCP_MODE", "read-write").lower()
40
40
 
41
- if MODE not in ("read-only", "read-write"):
42
- raise ValueError(f"Invalid ROSSUM_MCP_MODE: {MODE}. Must be 'read-only' or 'read-write'")
43
-
44
- logger.info(f"Rossum MCP Server starting in {MODE} mode")
41
+ logger.info(f"Rossum MCP Server starting in {get_mcp_mode()} mode")
45
42
 
46
43
  mcp = FastMCP("rossum-mcp-server")
47
44
  client = AsyncRossumAPIClient(base_url=BASE_URL, credentials=Token(token=API_TOKEN))
@@ -60,6 +57,22 @@ register_user_tools(mcp, client)
60
57
  register_workspace_tools(mcp, client)
61
58
 
62
59
 
60
+ @mcp.tool(description="Get the current MCP operation mode (read-only or read-write).")
61
+ async def get_mcp_mode_tool() -> dict:
62
+ return {"mode": get_mcp_mode()}
63
+
64
+
65
+ @mcp.tool(
66
+ description="Set the MCP operation mode. Use 'read-only' to disable write operations, 'read-write' to enable them."
67
+ )
68
+ async def set_mcp_mode_tool(mode: str) -> dict:
69
+ try:
70
+ set_mcp_mode(mode)
71
+ return {"message": f"MCP mode set to '{get_mcp_mode()}'"}
72
+ except ValueError as e:
73
+ return {"error": str(e)}
74
+
75
+
63
76
  def main() -> None:
64
77
  """Main entry point for console script."""
65
78
  mcp.run()
@@ -13,11 +13,30 @@ if TYPE_CHECKING:
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
  BASE_URL = os.environ.get("ROSSUM_API_BASE_URL", "").rstrip("/")
16
- MODE = os.environ.get("ROSSUM_MCP_MODE", "read-write").lower()
17
16
 
18
17
  # Marker used to indicate omitted fields in list responses
19
18
  TRUNCATED_MARKER = "<omitted>"
20
19
 
20
+ VALID_MODES = ("read-only", "read-write")
21
+
22
+ _mcp_mode = os.environ.get("ROSSUM_MCP_MODE", "read-write").lower()
23
+ if _mcp_mode not in VALID_MODES:
24
+ raise ValueError(f"Invalid ROSSUM_MCP_MODE: {_mcp_mode}. Must be one of: {VALID_MODES}")
25
+
26
+
27
+ def get_mcp_mode() -> str:
28
+ """Return the current MCP mode."""
29
+ return _mcp_mode
30
+
31
+
32
+ def set_mcp_mode(mode: str) -> None:
33
+ """Set the MCP mode (case-insensitive)."""
34
+ global _mcp_mode
35
+ normalized = mode.lower()
36
+ if normalized not in VALID_MODES:
37
+ raise ValueError(f"Invalid mode '{mode}'. Must be one of: {VALID_MODES}")
38
+ _mcp_mode = normalized
39
+
21
40
 
22
41
  def build_resource_url(resource_type: str, resource_id: int) -> str:
23
42
  """Build a full URL for a Rossum API resource."""
@@ -26,7 +45,7 @@ def build_resource_url(resource_type: str, resource_id: int) -> str:
26
45
 
27
46
  def is_read_write_mode() -> bool:
28
47
  """Check if server is in read-write mode."""
29
- return MODE == "read-write"
48
+ return _mcp_mode == "read-write"
30
49
 
31
50
 
32
51
  def truncate_dict_fields(data: dict[str, Any], fields: tuple[str, ...]) -> dict[str, Any]:
@@ -139,6 +139,9 @@ TOOL_CATALOG: dict[str, ToolCategory] = {
139
139
  tools=[
140
140
  ToolInfo("get_rule", "Retrieve rule details"),
141
141
  ToolInfo("list_rules", "List validation rules"),
142
+ ToolInfo("create_rule", "Create validation rule", read_only=False),
143
+ ToolInfo("update_rule", "Full update rule (PUT)", read_only=False),
144
+ ToolInfo("patch_rule", "Partial update rule (PATCH)", read_only=False),
142
145
  ToolInfo("delete_rule", "Delete rule", read_only=False),
143
146
  ],
144
147
  keywords=["rule", "validation", "constraint"],
@@ -0,0 +1,241 @@
1
+ """Rule tools for Rossum MCP Server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING, Literal, TypedDict
7
+
8
+ from rossum_api.domain_logic.resources import Resource
9
+ from rossum_api.models.rule import Rule
10
+
11
+ from rossum_mcp.tools.base import build_resource_url, delete_resource, is_read_write_mode
12
+
13
+ if TYPE_CHECKING:
14
+ from fastmcp import FastMCP
15
+ from rossum_api import AsyncRossumAPIClient
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ RuleActionType = Literal[
21
+ "show_message",
22
+ "add_automation_blocker",
23
+ "add_validation_source",
24
+ "change_queue",
25
+ "send_email",
26
+ "hide_field",
27
+ "show_field",
28
+ "show_hide_field",
29
+ "change_status",
30
+ "add_label",
31
+ "remove_label",
32
+ "custom",
33
+ ]
34
+
35
+
36
+ class RuleActionPayload(TypedDict, total=False):
37
+ """Payload for rule actions. Fields depend on action type."""
38
+
39
+ type: Literal["info", "warning", "error"] # for show_message
40
+ content: str # message content or template
41
+ schema_id: str # target field schema_id
42
+ queue_url: str # for change_queue
43
+ status: str # for change_status
44
+ label: str # for add_label/remove_label
45
+
46
+
47
+ class RuleAction(TypedDict):
48
+ """Rule action definition."""
49
+
50
+ id: str # unique action identifier
51
+ type: RuleActionType
52
+ event: Literal["validation"]
53
+ payload: RuleActionPayload
54
+
55
+
56
+ async def _get_rule(client: AsyncRossumAPIClient, rule_id: int) -> Rule:
57
+ logger.debug(f"Retrieving rule: rule_id={rule_id}")
58
+ rule: Rule = await client.retrieve_rule(rule_id)
59
+ return rule
60
+
61
+
62
+ async def _list_rules(
63
+ client: AsyncRossumAPIClient,
64
+ schema_id: int | None = None,
65
+ organization_id: int | None = None,
66
+ enabled: bool | None = None,
67
+ ) -> list[Rule]:
68
+ logger.debug(f"Listing rules: schema_id={schema_id}, organization_id={organization_id}, enabled={enabled}")
69
+ filters: dict = {}
70
+ if schema_id is not None:
71
+ filters["schema"] = schema_id
72
+ if organization_id is not None:
73
+ filters["organization"] = organization_id
74
+ if enabled is not None:
75
+ filters["enabled"] = enabled
76
+
77
+ rules_list: list[Rule] = [rule async for rule in client.list_rules(**filters)]
78
+ return rules_list
79
+
80
+
81
+ async def _create_rule(
82
+ client: AsyncRossumAPIClient,
83
+ name: str,
84
+ schema_id: int,
85
+ trigger_condition: str,
86
+ actions: list[RuleAction],
87
+ enabled: bool = True,
88
+ queue_ids: list[int] | None = None,
89
+ ) -> Rule | dict:
90
+ if not is_read_write_mode():
91
+ return {"error": "create_rule is not available in read-only mode"}
92
+
93
+ schema_url = build_resource_url("schemas", schema_id)
94
+ logger.info(f"Creating rule: name={name}, schema_id={schema_id}, enabled={enabled}")
95
+
96
+ rule_data: dict = {
97
+ "name": name,
98
+ "schema": schema_url,
99
+ "trigger_condition": trigger_condition,
100
+ "actions": actions,
101
+ "enabled": enabled,
102
+ }
103
+
104
+ if queue_ids is not None:
105
+ rule_data["queues"] = [build_resource_url("queues", qid) for qid in queue_ids]
106
+
107
+ logger.debug(f"Rule creation payload: {rule_data}")
108
+ rule: Rule = await client.create_new_rule(rule_data)
109
+ logger.info(f"Successfully created rule: id={rule.id}, name={rule.name}")
110
+ return rule
111
+
112
+
113
+ async def _update_rule(
114
+ client: AsyncRossumAPIClient,
115
+ rule_id: int,
116
+ name: str,
117
+ trigger_condition: str,
118
+ actions: list[RuleAction],
119
+ enabled: bool,
120
+ queue_ids: list[int] | None = None,
121
+ ) -> Rule | dict:
122
+ """Full update (PUT) - all fields required."""
123
+ if not is_read_write_mode():
124
+ return {"error": "update_rule is not available in read-only mode"}
125
+
126
+ logger.info(f"Updating rule: rule_id={rule_id}, name={name}")
127
+ existing_rule: Rule = await client.retrieve_rule(rule_id)
128
+
129
+ rule_data: dict = {
130
+ "name": name,
131
+ "schema": existing_rule.schema,
132
+ "trigger_condition": trigger_condition,
133
+ "actions": actions,
134
+ "enabled": enabled,
135
+ }
136
+
137
+ if queue_ids is not None:
138
+ rule_data["queues"] = [build_resource_url("queues", qid) for qid in queue_ids]
139
+
140
+ logger.debug(f"Rule update payload: {rule_data}")
141
+ await client._http_client.update(Resource.Rule, rule_id, rule_data)
142
+ updated_rule: Rule = await client.retrieve_rule(rule_id)
143
+ logger.info(f"Successfully updated rule: id={updated_rule.id}")
144
+ return updated_rule
145
+
146
+
147
+ async def _patch_rule(
148
+ client: AsyncRossumAPIClient,
149
+ rule_id: int,
150
+ name: str | None = None,
151
+ trigger_condition: str | None = None,
152
+ actions: list[RuleAction] | None = None,
153
+ enabled: bool | None = None,
154
+ queue_ids: list[int] | None = None,
155
+ ) -> Rule | dict:
156
+ """Partial update (PATCH) - only provided fields are updated."""
157
+ if not is_read_write_mode():
158
+ return {"error": "patch_rule is not available in read-only mode"}
159
+
160
+ logger.info(f"Patching rule: rule_id={rule_id}")
161
+
162
+ patch_data: dict = {}
163
+ if name is not None:
164
+ patch_data["name"] = name
165
+ if trigger_condition is not None:
166
+ patch_data["trigger_condition"] = trigger_condition
167
+ if actions is not None:
168
+ patch_data["actions"] = actions
169
+ if enabled is not None:
170
+ patch_data["enabled"] = enabled
171
+ if queue_ids is not None:
172
+ patch_data["queues"] = [build_resource_url("queues", qid) for qid in queue_ids]
173
+
174
+ if not patch_data:
175
+ return {"error": "No fields provided to update"}
176
+
177
+ logger.debug(f"Rule patch payload: {patch_data}")
178
+ updated_rule: Rule = await client.update_part_rule(rule_id, patch_data)
179
+ logger.info(f"Successfully patched rule: id={updated_rule.id}")
180
+ return updated_rule
181
+
182
+
183
+ async def _delete_rule(client: AsyncRossumAPIClient, rule_id: int) -> dict:
184
+ return await delete_resource("rule", rule_id, client.delete_rule)
185
+
186
+
187
+ def register_rule_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
188
+ """Register rule-related tools with the FastMCP server."""
189
+
190
+ @mcp.tool(description="Retrieve rule details.")
191
+ async def get_rule(rule_id: int) -> Rule:
192
+ return await _get_rule(client, rule_id)
193
+
194
+ @mcp.tool(description="List all rules.")
195
+ async def list_rules(
196
+ schema_id: int | None = None, organization_id: int | None = None, enabled: bool | None = None
197
+ ) -> list[Rule]:
198
+ return await _list_rules(client, schema_id, organization_id, enabled)
199
+
200
+ @mcp.tool(
201
+ description="Create a new rule. Rules automate field operations based on trigger conditions (TxScript formulas like 'field.amount > 1000'). Actions require: id (unique str), type (show_message|hide_field|show_field|change_status|custom|etc), event (validation), payload (dict with type, content, schema_id for show_message). queue_ids limits rule to specific queues."
202
+ )
203
+ async def create_rule(
204
+ name: str,
205
+ schema_id: int,
206
+ trigger_condition: str,
207
+ actions: list[RuleAction],
208
+ enabled: bool = True,
209
+ queue_ids: list[int] | None = None,
210
+ ) -> Rule | dict:
211
+ return await _create_rule(client, name, schema_id, trigger_condition, actions, enabled, queue_ids)
212
+
213
+ @mcp.tool(
214
+ description="Full update of a rule (PUT). All fields required. Use patch_rule for partial updates. queue_ids limits rule to specific queues."
215
+ )
216
+ async def update_rule(
217
+ rule_id: int,
218
+ name: str,
219
+ trigger_condition: str,
220
+ actions: list[RuleAction],
221
+ enabled: bool,
222
+ queue_ids: list[int] | None = None,
223
+ ) -> Rule | dict:
224
+ return await _update_rule(client, rule_id, name, trigger_condition, actions, enabled, queue_ids)
225
+
226
+ @mcp.tool(
227
+ description="Partial update of a rule (PATCH). Only provided fields are updated. queue_ids limits rule to specific queues (empty list removes all)."
228
+ )
229
+ async def patch_rule(
230
+ rule_id: int,
231
+ name: str | None = None,
232
+ trigger_condition: str | None = None,
233
+ actions: list[RuleAction] | None = None,
234
+ enabled: bool | None = None,
235
+ queue_ids: list[int] | None = None,
236
+ ) -> Rule | dict:
237
+ return await _patch_rule(client, rule_id, name, trigger_condition, actions, enabled, queue_ids)
238
+
239
+ @mcp.tool(description="Delete a rule.")
240
+ async def delete_rule(rule_id: int) -> dict:
241
+ return await _delete_rule(client, rule_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rossum-mcp
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Summary: MCP server for AI-powered Rossum orchestration: document workflows, debug pipelines automatically, and configure intelligent document processing through natural language.
5
5
  Author-email: "Dan Stancl (Rossum AI)" <daniel.stancl@gmail.com>
6
6
  License: MIT
@@ -51,14 +51,14 @@ Dynamic: license-file
51
51
 
52
52
  <div align="center">
53
53
 
54
- **MCP server for AI-powered Rossum document processing. 56 tools for queues, schemas, hooks, engines, and more.**
54
+ **MCP server for AI-powered Rossum document processing. 59 tools for queues, schemas, hooks, engines, and more.**
55
55
 
56
56
  [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://stancld.github.io/rossum-agents/)
57
57
  [![Python](https://img.shields.io/pypi/pyversions/rossum-mcp.svg)](https://pypi.org/project/rossum-mcp/)
58
58
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
59
59
  [![PyPI - rossum-mcp](https://img.shields.io/pypi/v/rossum-mcp?label=rossum-mcp)](https://pypi.org/project/rossum-mcp/)
60
60
  [![Coverage](https://codecov.io/gh/stancld/rossum-agents/branch/master/graph/badge.svg?flag=rossum-mcp)](https://codecov.io/gh/stancld/rossum-agents)
61
- [![MCP Tools](https://img.shields.io/badge/MCP_Tools-56-blue.svg)](#available-tools)
61
+ [![MCP Tools](https://img.shields.io/badge/MCP_Tools-59-blue.svg)](#available-tools)
62
62
 
63
63
  [![Rossum API](https://img.shields.io/badge/Rossum-API-orange.svg)](https://github.com/rossumai/rossum-api)
64
64
  [![MCP](https://img.shields.io/badge/MCP-compatible-green.svg)](https://modelcontextprotocol.io/)
@@ -127,9 +127,29 @@ Configure Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_c
127
127
 
128
128
  Set `ROSSUM_MCP_MODE=read-only` to disable all CREATE, UPDATE, and UPLOAD operations. Only GET and LIST operations will be available.
129
129
 
130
+ ### Runtime Mode Switching
131
+
132
+ Two tools allow dynamic mode control:
133
+
134
+ | Tool | Description |
135
+ |------|-------------|
136
+ | `get_mcp_mode` | Returns current operation mode (`read-only` or `read-write`) |
137
+ | `set_mcp_mode` | Switches between modes at runtime |
138
+
139
+ **Use case:** Start in read-only mode for safe exploration, then switch to read-write when ready to make changes.
140
+
141
+ ```
142
+ User: What mode are we in?
143
+ Assistant: [calls get_mcp_mode] → "read-only"
144
+
145
+ User: I'm ready to update the schema now.
146
+ Assistant: [calls set_mcp_mode("read-write")] → Mode switched to read-write
147
+ [calls update_schema(...)]
148
+ ```
149
+
130
150
  ## Available Tools
131
151
 
132
- The server provides **56 tools** organized into categories:
152
+ The server provides **59 tools** organized into categories:
133
153
 
134
154
  | Category | Tools | Description |
135
155
  |----------|-------|-------------|
@@ -137,7 +157,7 @@ The server provides **56 tools** organized into categories:
137
157
  | **Queue Management** | 9 | Create, configure, delete, and list queues |
138
158
  | **Schema Management** | 8 | Define, modify, and delete field structures |
139
159
  | **Engine Management** | 6 | Configure extraction and splitting engines |
140
- | **Extensions & Rules** | 11 | Webhooks, serverless functions, business rules |
160
+ | **Extensions & Rules** | 14 | Webhooks, serverless functions, business rules |
141
161
  | **Workspace Management** | 4 | Organize and delete workspaces |
142
162
  | **User Management** | 3 | List users and roles |
143
163
  | **Relations** | 4 | Annotation and document relations |
@@ -160,7 +180,7 @@ The server provides **56 tools** organized into categories:
160
180
  `get_engine`, `list_engines`, `create_engine`, `update_engine`, `create_engine_field`, `get_engine_fields`
161
181
 
162
182
  **Extensions & Rules:**
163
- `get_hook`, `list_hooks`, `create_hook`, `update_hook`, `list_hook_templates`, `create_hook_from_template`, `list_hook_logs`, `delete_hook`, `get_rule`, `list_rules`, `delete_rule`
183
+ `get_hook`, `list_hooks`, `create_hook`, `update_hook`, `list_hook_templates`, `create_hook_from_template`, `list_hook_logs`, `delete_hook`, `get_rule`, `list_rules`, `create_rule`, `update_rule`, `patch_rule`, `delete_rule`
164
184
 
165
185
  **Workspace Management:**
166
186
  `get_workspace`, `list_workspaces`, `create_workspace`, `delete_workspace`
@@ -96,6 +96,9 @@ class TestToolCatalog:
96
96
  ("hooks", "create_hook_from_template"),
97
97
  ("hooks", "delete_hook"),
98
98
  ("email_templates", "create_email_template"),
99
+ ("rules", "create_rule"),
100
+ ("rules", "update_rule"),
101
+ ("rules", "patch_rule"),
99
102
  ("rules", "delete_rule"),
100
103
  ("workspaces", "create_workspace"),
101
104
  ("workspaces", "delete_workspace"),
@@ -1,63 +0,0 @@
1
- """Rule tools for Rossum MCP Server."""
2
-
3
- from __future__ import annotations
4
-
5
- import logging
6
- from typing import TYPE_CHECKING
7
-
8
- from rossum_api.models.rule import Rule
9
-
10
- from rossum_mcp.tools.base import delete_resource
11
-
12
- if TYPE_CHECKING:
13
- from fastmcp import FastMCP
14
- from rossum_api import AsyncRossumAPIClient
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
- async def _get_rule(client: AsyncRossumAPIClient, rule_id: int) -> Rule:
20
- logger.debug(f"Retrieving rule: rule_id={rule_id}")
21
- rule: Rule = await client.retrieve_rule(rule_id)
22
- return rule
23
-
24
-
25
- async def _list_rules(
26
- client: AsyncRossumAPIClient,
27
- schema_id: int | None = None,
28
- organization_id: int | None = None,
29
- enabled: bool | None = None,
30
- ) -> list[Rule]:
31
- logger.debug(f"Listing rules: schema_id={schema_id}, organization_id={organization_id}, enabled={enabled}")
32
- filters: dict = {}
33
- if schema_id is not None:
34
- filters["schema"] = schema_id
35
- if organization_id is not None:
36
- filters["organization"] = organization_id
37
- if enabled is not None:
38
- filters["enabled"] = enabled
39
-
40
- rules_list: list[Rule] = [rule async for rule in client.list_rules(**filters)]
41
- return rules_list
42
-
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
-
48
- def register_rule_tools(mcp: FastMCP, client: AsyncRossumAPIClient) -> None:
49
- """Register rule-related tools with the FastMCP server."""
50
-
51
- @mcp.tool(description="Retrieve rule details.")
52
- async def get_rule(rule_id: int) -> Rule:
53
- return await _get_rule(client, rule_id)
54
-
55
- @mcp.tool(description="List all rules.")
56
- async def list_rules(
57
- schema_id: int | None = None, organization_id: int | None = None, enabled: bool | None = None
58
- ) -> list[Rule]:
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)
File without changes
File without changes