fix: send edgeFunctionCode per the v3 custom-tools contract (422 on every run)#17
Merged
Conversation
The v3 create-custom-tool endpoint requires edgeFunctionCode (it deploys the edge function itself, computes the URL, and upserts the tool by name). The action was instead sending an edgeFunctionUrl built from tool.json uuid + preview_domain and never sending edgeFunctionCode, so every invocation failed Laravel validation with a 422. - Read fn.js from each tool directory (sibling of tool.json) and send it as edgeFunctionCode with toolName, description, inputSchema, and the optional accepted fields (category, responseMode, outputSchema, outputSchemaDescription, isAsync, timeoutSeconds) - tool.json timeout is documented as seconds (5-300); values > 300 are assumed to be milliseconds and converted with a warning - Fix existing-tool detection: the list API returns toolName, not name, so the pre-flight check always reported "Found 0 existing tool(s)" - Surface HTTP status and the full JSON error body on failure instead of the (usually absent) .error field - Deprecate preview_domain and quant_project inputs (accepted, ignored) and warn when tool.json carries a uuid (platform-managed now) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Every invocation of this action against the v3 API fails with a
422 Unprocessable Entity— for all consumers, including first use. This PR fixes the request contract plus two related bugs, and improves error output so future API failures are diagnosable from the workflow log.Bug 1 — missing
edgeFunctionCode(the 422 root cause)The v3 endpoint
POST /api/v3/organizations/{org}/ai/custom-toolsrequiresedgeFunctionCode(the edge function source): the platform deploys the function itself, computes its URL, and registers the tool — upserting by name and reusing the existing tool's uuid on update. The action instead sent anedgeFunctionUrlbuilt fromtool.json'suuid+ thepreview_domaininput, and never sentedgeFunctionCode, so server-side validation rejected every request.The action now reads
fn.jsfrom each tool directory (sibling oftool.json— the existing convention in consuming repos) and sends it asedgeFunctionCode, along withtoolName,description,inputSchema, and the optional accepted fields:category,responseMode,outputSchema,outputSchemaDescription,isAsync,timeoutSeconds. A missingfn.jsfails with a clear error.timeoutintool.jsonis documented as seconds (API range 5–300); values above 300 are assumed to be milliseconds from older configs and converted with a warning.Bug 2 — existing-tool detection always found 0 tools
The pre-flight list check read
tool.name, but the list API returnstoolName. Every tool was therefore reported as "Creating"/"Created" even when it already existed. The action now readstoolName ?? nameand reports created vs updated accurately. (Correctness was never at risk — the endpoint upserts by name — but the reporting was wrong.)Bug 3 — errors were swallowed
Failures read
err.response.data.error, which Laravel validation responses don't populate (they return{ errors: {...} }). The 422s surfaced as an unhelpful generic message. Failures now include the HTTP status and the full JSON response body.Migration notes for existing workflows
fn.jsto each tool directory — the edge function source the platform should deploy.preview_domainandquant_projectinputs are deprecated — still accepted (no workflow breakage) but ignored with a warning. You can remove them.uuidintool.jsonis informational only and triggers a warning. The platform manages uuids, reusing the existing tool's uuid on update — so a uuid aligned with yourfunctions.jsonstays stable only if the tool is already registered with that uuid in its edge function URL. Tools registered for the first time get a platform-assigned uuid.Verification
npx tsc --noEmitcleannpm run build(ncc) —dist/rebuilt and committed (actions ship dist), verified the bundle sendsedgeFunctionCodetestscript references jest, but it isn't installed), so no unit tests were addedRelated
The generated types in
@quantcdn/quant-clienthave drifted from the API:ListCustomTools200ResponseToolsInnerkeys the tool name asnamewhile the API returnstoolName, andCreateCustomToolRequeststill requiresedgeFunctionUrlwith noedgeFunctionCodefield. Worth fixing in the OpenAPI spec at the source — this action works around both with a documented cast.🤖 Generated with Claude Code