Skip to content

[EPIC-D][LOU-D22] Tools carry their own inputSchema and execute; defineTool no longer wraps ai.tool() - #79

Merged
LinuxDevil merged 2 commits into
mainfrom
lou-d22-own-tool-contract
Oct 1, 2026
Merged

LinuxDevil merged 2 commits into
mainfrom
lou-d22-own-tool-contract

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What

  • ToolDescriptor (src/types/tool.ts) gains optional inputSchema?: z.ZodTypeAny and execute?(args, ctx). They are documented as the canonical fields. .tool (the ai v4 Tool) is documented as legacy and kept for compatibility this release.
  • defineTool() sets inputSchema (same schema as input) and execute itself. It no longer imports tool from ai: it builds the legacy .tool object { description, parameters, execute } by hand, using the same execute function. DefinedTool types inputSchema: S and execute(args: z.output<S>, ctx): Promise<O>.
  • New src/tools/toolContract.ts: getToolInputSchema(desc) and getToolExecute(desc) prefer the canonical fields and fall back to tool.parameters / tool.execute. Every runtime read now goes through them:
    • toolArgsValidation.ts: argument validation
    • generateStep.ts buildTools(): the provider ToolDefinition builder (one-line swap; the read lives only there)
    • sandboxGuard.ts: execution
    • toolCallExecution.ts findExecutableTool()
    • resume.ts: the "tool not found" guard on approval resume (one-line swap; the read lives only there)
    • tools/mcp/server/buildServer.ts: the MCP server export
  • AgentExecutor.ts is unchanged.
  • CHANGELOG [Unreleased] -> Added entry. docs/api-overview.md: the tools section now describes the canonical fields, and argument validation now says inputSchema instead of parameters. llms-full.txt regenerated.

src/tools files that still import from ai after this change (non-test):

  • src/tools/defineTool.ts: type-only ToolExecutionOptions, which D23 replaces
  • src/tools/built-in/currentDate.ts, dayName.ts, email.ts, http.ts, slack.ts, src/tools/mcp/McpToolLoader.ts: D24
  • src/tools/built-in/github.ts, jira.ts: D25

Why

ai v5+ renamed parameters to inputSchema. Because the SDK read tool.parameters directly, every user tool and the SDK itself were tied to ai v4. With the SDK's own types as the source of truth, D23-D27 can swap the ai adapter without touching tool authors.

Acceptance criteria

  • ToolDescriptor has optional inputSchema (zod 3) and execute at descriptor level. Both are documented as canonical, and .tool is documented as legacy.
  • DefinedTool sets inputSchema and execute, does not import tool from ai, and still produces a v4-shaped .tool ({ description, parameters, execute }). ToolRegistry, Agent Forge and existing callers are unchanged.
  • DefineToolOptions.input is kept. The inputSchema option alias was not added: keeping inference typed for input | inputSchema and the validation messages correct costs more than ~10 lines.
  • Every runtime schema read goes through getToolInputSchema(). Execute reads go through getToolExecute(), including sandboxGuard.
  • Type tests: the existing defineTool .test-d.ts passes. A new case checks that defineTool(...).inputSchema is the input zod type and that execute's argument, context and return types are inferred.
  • Runtime tests (src/tools/toolContract.test.ts):
    • a defineTool tool runs end-to-end with mockModel, with ai.tool spied and asserted not called
    • a legacy ai.tool() descriptor still validates and runs
    • the canonical fields are preferred over legacy ones, with fallback
  • The MCP tests (src/tools/mcp, including server) pass.
  • The PR lists the src/tools files that still import ai (above).
  • CHANGELOG and docs are updated.
  • The diff is about 65 non-test, non-doc lines, so no D22.2 split is needed.

How verified

  • npx tsc --noEmit: no errors
  • npm run lint: ✖ 433 problems (0 errors, 433 warnings), the same warning count as main
  • npm run test:types: Tests 20 passed (20), Type Errors no errors
  • npx vitest run: Tests 2023 passed | 1 failed | 3 skipped (2027). The one failure differed between runs, was in unrelated code that depends on timing, and passed when re-run on its own:
    • first run: guardrails.test.ts child-process timeout (re-run: 23/23 passed)
    • second run: cloudflare.test.ts wrangler dev (re-run: 15/15 passed)
  • npm run test:coverage: Test Files 138 passed (138), Tests 2024 passed | 3 skipped (2027)
  • npm run build && npm run build --workspace=packages/create-loushy-agent: both succeeded
  • npm run docs:verify-snippets -- --skip-build: all 91 snippet(s) type-check against src/; 6 also run cleanly
  • npm run docs:llms:check: passed
  • npm run fallow: ✓ No issues found. The only duplication reported is an existing 15-line clone between AnthropicProvider and OpenAIProvider.

Note for D24/D25: github.ts (it disables out-of-scope tools by reassigning descriptor.tool.execute) and sandboxFetch.ts routeFetchThroughSandbox (it reads descriptor.tool.execute) still work on .tool.execute. When those built-ins move to defineTool, they must override or read the canonical execute instead, or the disablement would be bypassed.

🤖 Generated with Claude Code

https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR


Generated by Claude Code

claude added 2 commits October 1, 2026 12:44
…o longer wraps ai.tool()

ToolDescriptor gains optional canonical `inputSchema` (zod) and `execute`
fields. defineTool() sets both and builds the legacy ai v4 `.tool` object
({ description, parameters, execute }) by hand instead of calling ai's
tool(). Runtime reads of a tool's schema and execute go through
getToolInputSchema()/getToolExecute() (src/tools/toolContract.ts), which
prefer the canonical fields and fall back to tool.parameters/tool.execute:
argument validation, the provider ToolDefinition builder, the sandbox
guard, tool lookup, approval resume and the MCP server export.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants