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.
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/PKG-INFO +1 -1
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/pyproject.toml +1 -1
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/app.py +9 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/server.py +39 -3
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/LICENSE +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/README.md +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/__init__.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/api_client.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/__init__.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/__init__.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/_helpers.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/accounts.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/auth.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/businesses.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/contacts.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/documents.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/files.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/invoices.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/products.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/purchase_invoices.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/reports.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/schema.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/tags.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/commands/user.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/context.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/cli/output.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/config.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/__init__.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/auth.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/contract_validation.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/error_handling.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/http_error_capture.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/mcp/middleware.py +0 -0
- {nocfo_cli-1.4.2 → nocfo_cli-1.4.4}/src/nocfo_toolkit/openapi.py +0 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|