nocfo-cli 1.4.3__tar.gz → 1.4.5__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 (34) hide show
  1. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/PKG-INFO +1 -1
  2. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/pyproject.toml +1 -1
  3. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/mcp/middleware.py +16 -2
  4. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/mcp/server.py +39 -2
  5. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/LICENSE +0 -0
  6. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/README.md +0 -0
  7. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/__init__.py +0 -0
  8. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/api_client.py +0 -0
  9. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/__init__.py +0 -0
  10. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/app.py +0 -0
  11. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/__init__.py +0 -0
  12. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/_helpers.py +0 -0
  13. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/accounts.py +0 -0
  14. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/auth.py +0 -0
  15. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/businesses.py +0 -0
  16. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/contacts.py +0 -0
  17. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/documents.py +0 -0
  18. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/files.py +0 -0
  19. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/invoices.py +0 -0
  20. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/products.py +0 -0
  21. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/purchase_invoices.py +0 -0
  22. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/reports.py +0 -0
  23. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/schema.py +0 -0
  24. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/tags.py +0 -0
  25. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/commands/user.py +0 -0
  26. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/context.py +0 -0
  27. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/cli/output.py +0 -0
  28. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/config.py +0 -0
  29. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/mcp/__init__.py +0 -0
  30. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/mcp/auth.py +0 -0
  31. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/mcp/contract_validation.py +0 -0
  32. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/mcp/error_handling.py +0 -0
  33. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/mcp/http_error_capture.py +0 -0
  34. {nocfo_cli-1.4.3 → nocfo_cli-1.4.5}/src/nocfo_toolkit/openapi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nocfo-cli
3
- Version: 1.4.3
3
+ Version: 1.4.5
4
4
  Summary: NoCFO CLI, MCP server, and Cursor skill toolkit.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "nocfo-cli"
7
- version = "1.4.3"
7
+ version = "1.4.5"
8
8
  description = "NoCFO CLI, MCP server, and Cursor skill toolkit."
9
9
  authors = ["NoCFO"]
10
10
  readme = "README.md"
@@ -83,9 +83,23 @@ class MCPToolErrorMiddleware(Middleware):
83
83
  normalized.get("summary"),
84
84
  )
85
85
  raise ToolError(json.dumps(normalized, ensure_ascii=True)) from exc
86
- except Exception:
86
+ except Exception as exc:
87
87
  logger.exception("MCP tool call crashed tool=%s", tool_name)
88
- raise
88
+ captured = get_last_http_error()
89
+ if captured:
90
+ normalized = normalize_http_error(
91
+ tool_name=tool_name,
92
+ status_code=captured.get("status_code"),
93
+ payload=captured.get("payload"),
94
+ fallback_message=f"{type(exc).__name__}: {exc}",
95
+ )
96
+ else:
97
+ normalized = {
98
+ "tool": tool_name,
99
+ "error_type": "tool_error",
100
+ "summary": f"{type(exc).__name__}: {exc}",
101
+ }
102
+ raise ToolError(json.dumps(normalized, ensure_ascii=True)) from exc
89
103
  finally:
90
104
  clear_last_http_error()
91
105
 
@@ -15,6 +15,7 @@ from fastmcp.server.providers.openapi.components import (
15
15
  )
16
16
  from fastmcp.server.providers.openapi.routing import MCPType, RouteMap
17
17
  from fastmcp.tools.tool import Tool
18
+ from mcp import types as mcp_types
18
19
 
19
20
  from nocfo_toolkit.config import AUTH_HEADER_SCHEME, ToolkitConfig
20
21
  from nocfo_toolkit.mcp.auth import (
@@ -125,6 +126,36 @@ def apply_mcp_operation_metadata(route: Any, component: Any) -> None:
125
126
  component.meta = meta
126
127
 
127
128
 
129
+ def patch_mcp_runtime_output_validation(server: FastMCP) -> None:
130
+ """Disable SDK output validation at runtime without changing listed schemas.
131
+
132
+ MCP SDK validates tool outputs against ``outputSchema`` unconditionally in the
133
+ low-level ``call_tool`` handler. We patch the low-level lookup used only by that
134
+ validation path to return a cloned tool definition with ``outputSchema=None``.
135
+ ``tools/list`` responses are unaffected and keep the original schema intact.
136
+ """
137
+ lowlevel = getattr(server, "_mcp_server", None)
138
+ if lowlevel is None:
139
+ return
140
+ if getattr(lowlevel, "_nocfo_runtime_output_validation_patched", False):
141
+ return
142
+
143
+ original_get_cached_tool_definition = lowlevel._get_cached_tool_definition
144
+
145
+ async def _get_cached_tool_definition_without_output_schema(
146
+ tool_name: str,
147
+ ) -> mcp_types.Tool | None:
148
+ tool = await original_get_cached_tool_definition(tool_name)
149
+ if tool is None or tool.outputSchema is None:
150
+ return tool
151
+ return tool.model_copy(update={"outputSchema": None})
152
+
153
+ lowlevel._get_cached_tool_definition = (
154
+ _get_cached_tool_definition_without_output_schema
155
+ )
156
+ lowlevel._nocfo_runtime_output_validation_patched = True
157
+
158
+
128
159
  def restore_openapi_output_schema(route: Any, component: Any) -> None:
129
160
  """Restore the real OpenAPI output schema on tools for agent-facing metadata.
130
161
 
@@ -132,7 +163,11 @@ def restore_openapi_output_schema(route: Any, component: Any) -> None:
132
163
  permissive fallback so that runtime responses are never rejected. This
133
164
  function re-derives the accurate schema from the route and writes it back
134
165
  onto the tool so that ``tools/list`` still exposes precise type information
135
- to the agent — without enforcing it at call time.
166
+ to the agent.
167
+
168
+ Runtime output validation bypass is handled separately in
169
+ :func:`patch_mcp_runtime_output_validation`, so the listed schema can remain
170
+ fully backend-authored (including enums and other constraints).
136
171
  """
137
172
  if not isinstance(component, Tool):
138
173
  return
@@ -254,7 +289,7 @@ def create_server(
254
289
  required_scopes=opts.required_scopes,
255
290
  )
256
291
 
257
- return FastMCP.from_openapi(
292
+ server = FastMCP.from_openapi(
258
293
  openapi_spec=filtered_spec,
259
294
  client=client,
260
295
  name=opts.name,
@@ -265,6 +300,8 @@ def create_server(
265
300
  mcp_component_fn=component_mapper,
266
301
  validate_output=False,
267
302
  )
303
+ patch_mcp_runtime_output_validation(server)
304
+ return server
268
305
 
269
306
 
270
307
  def run_server(
File without changes
File without changes