nocfo-cli 1.4.2__tar.gz → 1.4.4__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.2 → nocfo_cli-1.4.4}/PKG-INFO +1 -1
  2. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/pyproject.toml +1 -1
  3. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/app.py +9 -0
  4. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/server.py +39 -3
  5. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/LICENSE +0 -0
  6. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/README.md +0 -0
  7. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/__init__.py +0 -0
  8. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/api_client.py +0 -0
  9. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/__init__.py +0 -0
  10. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/__init__.py +0 -0
  11. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/_helpers.py +0 -0
  12. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/accounts.py +0 -0
  13. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/auth.py +0 -0
  14. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/businesses.py +0 -0
  15. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/contacts.py +0 -0
  16. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/documents.py +0 -0
  17. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/files.py +0 -0
  18. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/invoices.py +0 -0
  19. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/products.py +0 -0
  20. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/purchase_invoices.py +0 -0
  21. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/reports.py +0 -0
  22. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/schema.py +0 -0
  23. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/tags.py +0 -0
  24. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/user.py +0 -0
  25. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/context.py +0 -0
  26. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/output.py +0 -0
  27. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/config.py +0 -0
  28. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/__init__.py +0 -0
  29. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/auth.py +0 -0
  30. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/contract_validation.py +0 -0
  31. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/error_handling.py +0 -0
  32. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/http_error_capture.py +0 -0
  33. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/middleware.py +0 -0
  34. {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/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.2
3
+ Version: 1.4.4
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.2"
7
+ version = "1.4.4"
8
8
  description = "NoCFO CLI, MCP server, and Cursor skill toolkit."
9
9
  authors = ["NoCFO"]
10
10
  readme = "README.md"
@@ -103,6 +103,14 @@ def run_mcp_server(
103
103
  "--required-scopes",
104
104
  help="Comma-separated OAuth scopes required for MCP tools.",
105
105
  ),
106
+ stateless_http: bool = typer.Option(
107
+ False,
108
+ "--stateless-http/--stateful-http",
109
+ help=(
110
+ "Use stateless streamable HTTP mode (recommended behind "
111
+ "load balancers with multiple MCP tasks)."
112
+ ),
113
+ ),
106
114
  ) -> None:
107
115
  """Run NoCFO MCP server over stdio or HTTP transport.
108
116
 
@@ -135,6 +143,7 @@ def run_mcp_server(
135
143
  auth_mode=auth_mode_value,
136
144
  mcp_base_url=mcp_base_url or os.getenv("NOCFO_MCP_BASE_URL"),
137
145
  required_scopes=scope_items,
146
+ stateless_http=stateless_http,
138
147
  )
139
148
 
140
149
  if transport_normalized == "http":
@@ -125,6 +125,28 @@ def apply_mcp_operation_metadata(route: Any, component: Any) -> None:
125
125
  component.meta = meta
126
126
 
127
127
 
128
+ _VALIDATION_ONLY_KEYS = frozenset({"enum", "const"})
129
+
130
+
131
+ def _strip_validation_constraints(schema: Any) -> Any:
132
+ """Recursively strip strict validation constraints from a JSON schema.
133
+
134
+ Removes ``enum`` and ``const`` so that the MCP SDK's unconditional
135
+ ``jsonschema.validate()`` on ``outputSchema`` never rejects responses
136
+ containing values absent from the declared set (e.g. legacy DB values).
137
+ Structural information (types, properties, descriptions) is preserved.
138
+ """
139
+ if isinstance(schema, dict):
140
+ return {
141
+ key: _strip_validation_constraints(value)
142
+ for key, value in schema.items()
143
+ if key not in _VALIDATION_ONLY_KEYS
144
+ }
145
+ if isinstance(schema, list):
146
+ return [_strip_validation_constraints(item) for item in schema]
147
+ return schema
148
+
149
+
128
150
  def restore_openapi_output_schema(route: Any, component: Any) -> None:
129
151
  """Restore the real OpenAPI output schema on tools for agent-facing metadata.
130
152
 
@@ -132,7 +154,13 @@ def restore_openapi_output_schema(route: Any, component: Any) -> None:
132
154
  permissive fallback so that runtime responses are never rejected. This
133
155
  function re-derives the accurate schema from the route and writes it back
134
156
  onto the tool so that ``tools/list`` still exposes precise type information
135
- to the agent — without enforcing it at call time.
157
+ to the agent.
158
+
159
+ The MCP SDK (``mcp.server.lowlevel``) unconditionally validates
160
+ ``structured_content`` against ``outputSchema`` via ``jsonschema``.
161
+ To prevent validation failures on legacy or unexpected values the
162
+ restored schema has strict value constraints (``enum``, ``const``)
163
+ stripped while keeping full structural and type information.
136
164
  """
137
165
  if not isinstance(component, Tool):
138
166
  return
@@ -145,7 +173,7 @@ def restore_openapi_output_schema(route: Any, component: Any) -> None:
145
173
  getattr(route, "openapi_version", None),
146
174
  )
147
175
  if real_schema is not None:
148
- component.output_schema = real_schema
176
+ component.output_schema = _strip_validation_constraints(real_schema)
149
177
 
150
178
 
151
179
  def _get_server_instructions(openapi_spec: dict[str, Any]) -> str | None:
@@ -168,6 +196,7 @@ class MCPServerOptions:
168
196
  jwt_exchange_path: str = "/auth/jwt/"
169
197
  token_refresh_skew_seconds: int = 60
170
198
  required_scopes: tuple[str, ...] = ()
199
+ stateless_http: bool = False
171
200
 
172
201
 
173
202
  def _create_pat_client(
@@ -288,7 +317,14 @@ def run_http_server(
288
317
  """Run NoCFO MCP server over streamable HTTP transport."""
289
318
 
290
319
  server = create_server(config, options=options)
291
- server.run(transport="http", host=host, port=port, path=path)
320
+ opts = options or MCPServerOptions()
321
+ server.run(
322
+ transport="http",
323
+ host=host,
324
+ port=port,
325
+ path=path,
326
+ stateless_http=opts.stateless_http,
327
+ )
292
328
 
293
329
 
294
330
  async def run_server_async(
File without changes
File without changes