unique-sdk 2026.44.0.dev2__tar.gz → 2026.44.0.dev3__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 (91) hide show
  1. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/PKG-INFO +1 -1
  2. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/pyproject.toml +1 -1
  3. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/cli.py +6 -2
  4. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/mcp.py +76 -7
  5. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/README.md +0 -0
  6. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/__init__.py +0 -0
  7. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_api_requestor.py +0 -0
  8. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_api_resource.py +0 -0
  9. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_api_version.py +0 -0
  10. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_error.py +0 -0
  11. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_http_client.py +0 -0
  12. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_list_object.py +0 -0
  13. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_object_classes.py +0 -0
  14. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_request_options.py +0 -0
  15. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_unique_object.py +0 -0
  16. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_unique_ql.py +0 -0
  17. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_unique_response.py +0 -0
  18. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_util.py +0 -0
  19. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_version.py +0 -0
  20. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/_webhook.py +0 -0
  21. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/__init__.py +0 -0
  22. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_acronyms.py +0 -0
  23. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_agentic_table.py +0 -0
  24. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_analytics_order.py +0 -0
  25. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_benchmarking.py +0 -0
  26. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_briefing.py +0 -0
  27. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_chat_completion.py +0 -0
  28. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_content.py +0 -0
  29. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_context_memory.py +0 -0
  30. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_elicitation.py +0 -0
  31. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_embedding.py +0 -0
  32. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_event.py +0 -0
  33. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_folder.py +0 -0
  34. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_group.py +0 -0
  35. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_integrated.py +0 -0
  36. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_llm_models.py +0 -0
  37. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_mcp.py +0 -0
  38. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_message.py +0 -0
  39. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_message_assessment.py +0 -0
  40. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_message_execution.py +0 -0
  41. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_message_log.py +0 -0
  42. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_message_tool.py +0 -0
  43. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_module.py +0 -0
  44. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  45. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_search.py +0 -0
  46. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_search_string.py +0 -0
  47. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  48. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_space.py +0 -0
  49. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_user.py +0 -0
  50. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/api_resources/_web_search.py +0 -0
  51. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/__init__.py +0 -0
  52. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/__main__.py +0 -0
  53. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/__init__.py +0 -0
  54. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  55. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/agentic_table.py +0 -0
  56. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/agentic_table_write.py +0 -0
  57. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/browser.py +0 -0
  58. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/cite_file.py +0 -0
  59. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/elicitation.py +0 -0
  60. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/files.py +0 -0
  61. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/folders.py +0 -0
  62. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/navigation.py +0 -0
  63. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/read.py +0 -0
  64. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  65. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/search.py +0 -0
  66. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/subagent.py +0 -0
  67. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/web_search.py +0 -0
  68. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/commands/web_search_config.py +0 -0
  69. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/config.py +0 -0
  70. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/formatting.py +0 -0
  71. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/identity.py +0 -0
  72. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/metadata_filter.py +0 -0
  73. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/shell.py +0 -0
  74. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-agentic-table/SKILL.md +0 -0
  75. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  76. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  77. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  78. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  79. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
  80. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  81. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-uploaded-search/SKILL.md +0 -0
  82. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  83. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/state.py +0 -0
  84. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/cli/workspace.py +0 -0
  85. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/utils/analytics_order_run.py +0 -0
  86. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/utils/benchmarking_run.py +0 -0
  87. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/utils/chat_history.py +0 -0
  88. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/utils/chat_in_space.py +0 -0
  89. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/utils/file_io.py +0 -0
  90. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/utils/sources.py +0 -0
  91. {unique_sdk-2026.44.0.dev2 → unique_sdk-2026.44.0.dev3}/unique_sdk/utils/token.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-sdk
3
- Version: 2026.44.0.dev2
3
+ Version: 2026.44.0.dev3
4
4
  Summary:
5
5
  Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
6
6
  Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_sdk"
3
- version = "2026.44.0.dev2"
3
+ version = "2026.44.0.dev3"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -63,6 +63,9 @@ from unique_sdk.cli.commands.folders import (
63
63
  is_mkdir_error_output as _is_mkdir_error_output,
64
64
  )
65
65
  from unique_sdk.cli.commands.mcp import cmd_mcp
66
+ from unique_sdk.cli.commands.mcp import (
67
+ is_error_output as _is_mcp_error_output,
68
+ )
66
69
  from unique_sdk.cli.commands.navigation import cmd_cd, cmd_ls, cmd_pwd
67
70
  from unique_sdk.cli.commands.read import cmd_read
68
71
  from unique_sdk.cli.commands.read import (
@@ -844,7 +847,7 @@ def mcp(
844
847
  """
845
848
  resolved_message_id = _resolve_cli_message_id(ctx, message_id, required=True)
846
849
  resolved_chat_id = _resolve_cli_chat_id(ctx, chat_id, required=True)
847
- click.echo(
850
+ emit(
848
851
  cmd_mcp(
849
852
  LazyState.get(ctx),
850
853
  chat_id=resolved_chat_id or "",
@@ -852,7 +855,8 @@ def mcp(
852
855
  payload=payload,
853
856
  file=file_path,
854
857
  stdin=use_stdin,
855
- )
858
+ ),
859
+ is_error=_is_mcp_error_output,
856
860
  )
857
861
 
858
862
 
@@ -26,6 +26,9 @@ from unique_sdk.cli.workspace import unique_dir as workspace_unique_dir
26
26
 
27
27
  _LOGGER = logging.getLogger(__name__)
28
28
 
29
+ MCP_ERROR_PREFIX = "mcp:"
30
+ _FORMATTER_ERROR_PREFIX = "mcp: formatter error"
31
+
29
32
  # Per-turn manifest of MCP tool output text, consumed by the Swappable
30
33
  # Intelligence runner to ground the hallucination check against MCP-retrieved
31
34
  # information (UN-21951). Carries *text only* — no source numbers/markers
@@ -1058,6 +1061,58 @@ def _parse_and_validate(raw: str) -> tuple[str, dict[str, Any]]:
1058
1061
  return name, arguments
1059
1062
 
1060
1063
 
1064
+ class McpCommandResult(str):
1065
+ """Text returned by ``cmd_mcp``, with whether that call failed.
1066
+
1067
+ Subclasses ``str`` so callers can print and search the text. ``failed``
1068
+ is set when the process should exit 1: a payload error, any
1069
+ ``UniqueError``, or a tool result with ``isError`` set.
1070
+ """
1071
+
1072
+ failed: bool
1073
+
1074
+ def __new__(cls, text: str, *, failed: bool) -> McpCommandResult:
1075
+ result = str.__new__(cls, text)
1076
+ result.failed = failed
1077
+ return result
1078
+
1079
+
1080
+ def _response_is_error(response: Any) -> bool:
1081
+ """Return whether the tool result itself is marked ``isError``.
1082
+
1083
+ Args:
1084
+ response (Any): Raw MCP tool response.
1085
+
1086
+ Returns:
1087
+ bool: True when ``isError`` is set. False when it is absent, false,
1088
+ or unreadable, so a formatter failure on a successful result still
1089
+ exits 0.
1090
+ """
1091
+ try:
1092
+ return bool(getattr(response, "isError", False))
1093
+ except Exception:
1094
+ return False
1095
+
1096
+
1097
+ def is_error_output(output: str) -> bool:
1098
+ """Return whether an MCP command result should exit non-zero.
1099
+
1100
+ Args:
1101
+ output (str): Text returned by ``cmd_mcp``.
1102
+
1103
+ Returns:
1104
+ bool: True for payload errors, SDK errors, and ``isError`` tool
1105
+ results. False when the call succeeded, including when formatting
1106
+ that successful result failed and the fallback text was returned.
1107
+ """
1108
+ failed = getattr(output, "failed", None)
1109
+ if isinstance(failed, bool):
1110
+ return failed
1111
+ if output.startswith(_FORMATTER_ERROR_PREFIX):
1112
+ return False
1113
+ return output.startswith(MCP_ERROR_PREFIX)
1114
+
1115
+
1061
1116
  def cmd_mcp(
1062
1117
  state: ShellState,
1063
1118
  chat_id: str,
@@ -1065,8 +1120,21 @@ def cmd_mcp(
1065
1120
  payload: str | None = None,
1066
1121
  file: str | None = None,
1067
1122
  stdin: bool = False,
1068
- ) -> str:
1069
- """Call an MCP tool with a JSON payload containing name and arguments."""
1123
+ ) -> McpCommandResult:
1124
+ """Call an MCP tool with a JSON payload containing name and arguments.
1125
+
1126
+ Args:
1127
+ state (ShellState): CLI session holding the user and company ids.
1128
+ chat_id (str): Chat the tool call is attributed to.
1129
+ message_id (str): Message the tool call is attributed to.
1130
+ payload (str | None): Inline JSON payload.
1131
+ file (str | None): Path to a JSON payload file.
1132
+ stdin (bool): Read the JSON payload from stdin.
1133
+
1134
+ Returns:
1135
+ McpCommandResult: Formatted tool output, or an ``mcp:`` error line.
1136
+ ``failed`` is true when the call itself failed.
1137
+ """
1070
1138
  try:
1071
1139
  raw = _read_payload(payload, file, stdin)
1072
1140
  name, arguments = _parse_and_validate(raw)
@@ -1080,8 +1148,8 @@ def cmd_mcp(
1080
1148
  arguments=arguments,
1081
1149
  )
1082
1150
 
1083
- except (ValueError, OSError, unique_sdk.APIError) as e:
1084
- return f"mcp: {e}"
1151
+ except (ValueError, OSError, unique_sdk.UniqueError) as e:
1152
+ return McpCommandResult(f"{MCP_ERROR_PREFIX} {e}", failed=True)
1085
1153
 
1086
1154
  try:
1087
1155
  formatted = format_mcp_response(response, tool_name=name)
@@ -1090,7 +1158,8 @@ def cmd_mcp(
1090
1158
  fallback = json.dumps(dict(response), indent=2, default=str)
1091
1159
  except Exception:
1092
1160
  fallback = repr(response)
1093
- formatted = f"mcp: formatter error ({fmt_exc}); raw response:\n{fallback}"
1161
+ formatted = f"{_FORMATTER_ERROR_PREFIX} ({fmt_exc}); raw response:\n{fallback}"
1162
+ call_failed = _response_is_error(response)
1094
1163
 
1095
1164
  server_name = _server_name_from_tool(name) or getattr(response, "mcpServerId", None)
1096
1165
  sources_block = record_mcp_citations(
@@ -1104,5 +1173,5 @@ def cmd_mcp(
1104
1173
  # Sources block FIRST: large outputs get spilled/truncated tail-first by
1105
1174
  # the agent harness, and a trailing block is what partial reads miss.
1106
1175
  if sources_block:
1107
- return sources_block + "\n\n" + formatted
1108
- return formatted
1176
+ formatted = sources_block + "\n\n" + formatted
1177
+ return McpCommandResult(formatted, failed=call_failed)