fix(tools): add Zod discriminated union overrides for object creation and update tools - #145
Open
Hatgor wants to merge 1 commit into
Open
fix(tools): add Zod discriminated union overrides for object creation and update tools#145Hatgor wants to merge 1 commit into
Hatgor wants to merge 1 commit into
Conversation
… and update tools Add handcrafted Zod schema overrides (`ToolOverrides`) for `API-create-object` and `API-update-object` to replace broken OpenAPI polymorphic schema inference with an explicit `format`-discriminated union for object properties. Key changes: - Handcrafted Zod schemas with a discriminated union on `format` for all 11 Anytype property types (text, number, select, multi_select, date, files, checkbox, url, email, phone, objects) - Registered `ToolOverrides` injected via `OpenAPIToMCPConverter` and validated in `MCPProxy` Supporting improvements: - Full `IconSchema` support (emoji, file, named icon, and nullable to clear icon) - Strip root `$schema` metadata from tool input schemas for OpenAI / LLM compatibility - Set `isError: true` on `HttpClientError` responses per MCP specification - Normalize tool lookup map to handle >64 character name truncation - Added comprehensive unit tests and regenerated `scripts/tools.json`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR introduces handcrafted Zod schema overrides (
ToolOverrides) forAPI-create-objectandAPI-update-objectto resolve polymorphic schema inference issues when generating MCP tools.Problem
The Anytype OpenAPI specification for object properties (
PropertyLinkWithValue) defines 11 polymorphic property formats (text,number,select,multi_select,date,files,checkbox,url,email,phone,objects). The automatic OpenAPI-to-MCP converter produced flattened/unconstrained schemas that caused validation issues and deserialization failures for LLM agents.Real-World Reproduction: Why LLMs Fail Without Discriminated Unions
1. What the LLM Generates (Using Flat / Unconstrained Schema)
When the schema presents all 11 property fields as optional properties of a single object, LLM agents (such as ZeroClaw or other tool-calling agents) routinely populate all fields with default/empty placeholder values (
text: "",number: 0,checkbox: false,date: "",files: [],multi_select: []):{ "space_id": "bafyreieofefmnne6lgxjgpu4k2jt4f2dzgriev7jnqkqsbayrt2gax67fu.12orhxm981fr7", "object_id": "bafyreic7bzn5iwqhjvh7qv43hp5uglid6icjlmbmqy62jd6uq3ibdz4bom", "type_key": "task", "name": "AnyType - add MCP", "markdown": "## Details...", "icon": { "emoji": "✅", "format": "emoji" }, "properties": [ { "key": "status", "select": "63454af7c493f68e301890dd", "checkbox": false, "date": "", "email": "", "files": [], "multi_select": [], "number": 0, "objects": [], "phone": "", "text": "", "url": "" } ] }2. Anytype API Error Response
The Anytype backend (
anytype-heart) Go deserializer processes the empty string fields (liketext: ""or emptyselect: "") alongside the target field, causing deserialization conflicts and failing with400 Bad Request:{ "status": 400, "object": "error", "code": "bad_request", "message": "bad input: invalid select option for \"status\": " }3. Why Prompt Engineering Fails
Even when system prompts or explicit instructions ("never pass empty dummy fields, only pass the key and target field") are provided, LLMs repeatedly fall back to sending dummy values on subsequent turns because the model is guided primarily by the tool's JSON Schema. If the schema defines flat optional fields, the model's tool-calling tokenizer will continue to generate default values.
Solution
z.discriminatedUnion("format", [...])insrc/tools/object-tools.ts. Each branch exposes only{ key, format, <target_field> }.ToolOverridesmechanism injected intoOpenAPIToMCPConverterand validated/sanitized inMCPProxy.IconSchemato supportemoji,file,namedicon formats, andnullable(allowing clients to clear icons by passingnull).$schemametadata from generated tool input schemas to ensure compatibility across MCP and function-calling clients.isError: trueflag toHttpClientErrorhandling inMCPProxyso HTTP 4xx/5xx responses are properly flagged as tool call errors.openApiLookuplookups succeed.Upstream OpenAPI Note
Ideally, this polymorphic union should be modeled with an explicit discriminator in the upstream Anytype OpenAPI specification itself (
PropertyLinkWithValue). Once the OpenAPI specification is updated upstream with proper discriminated unions, these manualToolOverridescan simply be retired and deleted without any breaking changes.