fix: improve n8n MCP client compatibility with JSON Schema types#61
Conversation
client compatibility with JSON Schema types This fix addresses issue #58 where n8n AI Agent encounters errors when connecting to the netbox-mcp-server. The error cant access property inputType, mapTypes[type] is undefined" occurs because n8n doesnt
There was a problem hiding this comment.
Pull request overview
This PR attempts to improve n8n MCP client compatibility by modifying type annotations to work around n8n's strict JSON Schema type requirements. The changes include converting parameter types from int to float, changing optional parameters from None defaults to empty string/list defaults, and adding more specific type annotations (dict[str, Any]).
Key changes:
- Parameter types changed from
inttofloatforlimit,offset, andobject_id(with internal conversion back to int) - Optional parameters changed from
Nonedefaults to empty defaults ([]for lists,""for strings) - Type annotations made more specific (
dict→dict[str, Any])
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
use string types for n8n MCP client compatibility n8n's MCP client only supports string, number, boolean, and json types in its UI. Parameters using dict (object) or list (array) types caused 'Cannot read properties of undefined (reading 'inputType')' errors. Changed parameter signatures to use string types while maintaining backward compatibility through helper functions that accept both string and native Python types. Fixes #58
Handle whitespace, "undefined", "null", and "none" string values that some MCP clients may pass for empty optional parameters. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Python 3.14-alpine lacks pre-built wheels for pydantic-core, causing the build to attempt compilation from source which requires Rust. Python 3.13-alpine has pre-built musllinux wheels available. Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 14 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Restores Python 3.14 Dockerfile and pydantic-core 2.41.5 lockfile. The older lockfile had pydantic-core 2.33.2 which lacks Python 3.14 musllinux wheels, causing CI build failures. Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…tests Add comment blocks at each float annotation (limit, offset, object_id) so future contributors don't revert to int — the workaround exists because n8n's mapTypes table has no entry for JSON Schema "integer". Clarify helper docstrings: MCP callers always hit the string path; dict/list branches exist only for direct Python callers (tests/library use), unreachable via the MCP boundary because Pydantic rejects non-string inputs. Add tests/test_mcp_boundary.py. Every other test uses .fn() which bypasses schema validation. These go through Client.call_tool() — the path real MCP clients take — and verify: (1) string filters work, (2) dict filters are rejected, (3) integer limit/object_id from LLM clients is coerced cleanly. Relates to #58. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Resolve 4 conflicts: - pyproject.toml: adopt main's fastmcp>=3.2.0,<4 and tightened pydantic/pydantic-settings floors; drop `requests` (unused — main's stack is httpx-only). - uv.lock: regenerate via `uv lock` on the merged pyproject. - tests/test_ordering.py, tests/test_search.py: keep this branch's string-only call semantics (the point of #61), and drop `.fn` — in fastmcp 3.x, `@mcp.tool` returns the raw function, so the decorated name is directly callable. Matches the style main already adopted. The four MCP-boundary tests from 14655b3 still pass end-to-end: fastmcp 3.x's Client + call_tool API is compatible with how we use it. Full suite: 93/93 on Python 3.14 + fastmcp 3.2.0. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Vulnerability Scan: PassedImage: No vulnerabilities found. Commit: 0bb2909 |
- netbox_get_objects.ordering widened back to str | list[str] | None (pre-#61 accepted the list form; initial compat-flag work narrowed it) - Expand _NETBOX_GET_OBJECTS_DESCRIPTION with the full pre-#61 Returns/ limit/offset/ordering guidance; fix the two-step pattern example to reference sites[0]['id'] instead of a hardcoded 1 - Promote the full pre-#61 changelogs docstring to _NETBOX_GET_CHANGELOGS_DESCRIPTION, wired into both strict and compat tool registrations so LLM clients see the same reference material in either mode Also prune three low-signal tests added earlier on this branch: - test_search_objects_strict_mode_accepts_lists (covered by pre-existing direct-call tests in test_search.py) - test_impl_parity.py case 4 (ordering — no transformation, identical kwargs in both modes) - test_ordering_empty_list_omits_parameter (empty list collapses to the same impl branch as existing "omit" tests) Add test_ordering_list_joined_comma_separated to guard the restored ordering list-input surface. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Restore signatures, docstrings, Field defaults, description strings, and
in-body comments that drifted from pre-revert origin/main during the
feat/n8n-compat-optin refactor. The strict-mode wrappers now differ from
main only where the dual-mode pattern requires: decorator stripped, body
delegates to the private _impl function, '-> Any' return annotation added.
Restored items:
- Field(default=5, ge=1, le=100) / Field(default=0, ge=0) on limit / offset
- filters: dict (required) on netbox_get_objects and netbox_get_changelogs
- ordering: str | list[str] | None = None on netbox_get_objects
- netbox_get_object_by_id: full fields docstring with IMPORTANT note,
token-reduction notes, and per-type examples
- netbox_get_changelogs: full docstring listing every filter option
- _NETBOX_GET_OBJECTS_DESCRIPTION: single-quoted dict examples,
'Optional list of specific fields', 'dict of filters', and main's
ordering paragraph verbatim
- _NETBOX_SEARCH_OBJECTS_DESCRIPTION: formatted default list, IT IS
STRONGLY RECOMMENDED note, three Example blocks, Returns section
- In-body comments inside _netbox_*_impl functions (# Validate object_type
exists in mapping, # Build params with pagination, # Make API call, etc.)
Also:
- test_tool_registration.py: strict-mode assertion changes from
{'object', 'null'} to {'object'} since filters is no longer nullable.
No behavior change for strict-mode users: these were all documentation
and style restorations or signature shapes that were already honored by
the runtime (ordering's list handling was dead relative to the typed
signature; now signature and runtime agree).
This fix addresses issue #58 where n8n AI Agent encounters errors when connecting to the netbox-mcp-server. The error "can't access property 'inputType', mapTypes[type] is undefined" occurs because n8n isn't flexible with the types. See issue: n8n-io/n8n#19835 (comment)
Raised by @jcollins313 and @DharmaBytesX
Additional input from @X-Coder
Fixes #58
Also fixed the ruff in the main dependencies (it's in dev)