Skip to content

Commit 134b0c2

Browse files
authored
refactor(types): replace json-schema library with custom JSONSchema type (#220)
* refactor(types): replace json-schema library with custom JSONSchema type - Define comprehensive JSONSchema interface in types.ts covering all JSON Schema draft-07 properties - Add toJsonSchema() method to BaseTool and Tools classes for framework-agnostic schema export - Refactor toOpenAI(), toAnthropic(), and toOpenAIResponses() to use toJsonSchema() internally, reducing code duplication - Use type-fest OverrideProperties for ObjectJSONSchema type to ensure type: 'object' is always set - Remove json-schema and @types/json-schema dependencies This reduces external dependencies while providing a more flexible JSONSchema type that works seamlessly with OpenAI, Anthropic, and other LLM providers. * refactor(tool): use toJsonSchema() in toAISDK method Consolidate schema generation by reusing toJsonSchema() instead of manually constructing the schema object. This reduces duplication and ensures consistency across all conversion methods. * refactor(tool): add type-safe schema validation for AI SDK - Import JSONSchema7 type from @ai-sdk/provider as AISDKJSONSchema - Use satisfies AISDKJSONSchema to validate schema at compile time - Move jsonSchema type import to top-level for cleaner code - Add @ai-sdk/provider as dev dependency for type checking * refactor(utils): add tryImport utility for optional dependencies - Create tryImport() helper function for dynamic imports with friendly error messages when optional dependencies are not installed - Refactor toAISDK() to use tryImport() for cleaner code - Remove unused jsonSchema type import from top-level * test(utils): add tests for tryImport utility - Test successful import of existing modules - Test StackOneError is thrown for non-existent modules - Verify error message includes module name and install hint * refactor(tool): clean up toAISDK method - Remove deprecated v4 parameters property - Use satisfies for type-safe tool definition - Remove outdated TODO comment about ts-ignore - Simplify tool definition by constructing all properties upfront * chore(oxfmt): ignore .claude/settings.local.json * chore(deps): move @ai-sdk/provider to catalog:dev * docs: tanstack ai jsonschema * Revert "chore(oxfmt): ignore .claude/settings.local.json" This reverts commit 91d6c79.
1 parent e131433 commit 134b0c2

8 files changed

Lines changed: 204 additions & 112 deletions

File tree

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,12 @@
3838
"@modelcontextprotocol/sdk": "catalog:prod",
3939
"@orama/orama": "catalog:prod",
4040
"defu": "catalog:prod",
41-
"json-schema": "catalog:prod",
4241
"zod": "catalog:dev"
4342
},
4443
"devDependencies": {
44+
"@ai-sdk/provider": "catalog:dev",
4545
"@ai-sdk/provider-utils": "catalog:dev",
4646
"@hono/mcp": "catalog:dev",
47-
"@types/json-schema": "catalog:dev",
4847
"@types/node": "catalog:dev",
4948
"@typescript/native-preview": "catalog:dev",
5049
"@vitest/coverage-v8": "catalog:dev",

pnpm-lock.yaml

Lines changed: 31 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ catalogMode: strict
77
catalogs:
88
dev:
99
'@ai-sdk/openai': ^2.0.80
10-
'@clack/prompts': ^0.11.0
10+
'@ai-sdk/provider': ^2.0.0
1111
'@ai-sdk/provider-utils': ^3.0.18
12+
'@clack/prompts': ^0.11.0
1213
'@hono/mcp': ^0.1.4
13-
'@types/json-schema': ^7.0.15
1414
'@types/node': ^22.13.5
1515
'@typescript/native-preview': ^7.0.0-dev.20251209.1
1616
'@vitest/coverage-v8': ^4.0.15
@@ -35,7 +35,6 @@ catalogs:
3535
'@modelcontextprotocol/sdk': ^1.24.3
3636
'@orama/orama': ^3.1.11
3737
defu: ^6.1.4
38-
json-schema: ^0.4.0
3938

4039
enablePrePostScripts: true
4140

src/tool.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { jsonSchema } from 'ai';
2-
import type { JSONSchema7 } from 'json-schema';
32
import { BaseTool, type MetaToolSearchResult, StackOneTool, Tools } from './tool';
4-
import { type ExecuteConfig, ParameterLocation, type ToolParameters } from './types';
3+
import {
4+
type ExecuteConfig,
5+
type JSONSchema,
6+
ParameterLocation,
7+
type ToolParameters,
8+
} from './types';
59
import { StackOneAPIError } from './utils/errors';
610

711
// Create a mock tool for testing
@@ -1107,11 +1111,10 @@ describe('Schema Validation', () => {
11071111
);
11081112

11091113
const parameters = tool.toOpenAI().function.parameters;
1110-
expect(parameters).toBeDefined();
1111-
const properties = parameters?.properties as Record<string, JSONSchema7>;
1114+
const properties = parameters?.properties as Record<string, JSONSchema>;
11121115

11131116
expect(properties.arrayWithItems.items).toBeDefined();
1114-
expect((properties.arrayWithItems.items as JSONSchema7).type).toBe('number');
1117+
expect((properties.arrayWithItems.items as JSONSchema).type).toBe('number');
11151118
});
11161119

11171120
it('should handle nested object structure', () => {
@@ -1144,7 +1147,7 @@ describe('Schema Validation', () => {
11441147

11451148
const parameters = tool.toOpenAI().function.parameters;
11461149
expect(parameters).toBeDefined();
1147-
const properties = parameters?.properties as Record<string, JSONSchema7>;
1150+
const properties = parameters?.properties as Record<string, JSONSchema>;
11481151
const nestedObject = properties.nestedObject;
11491152

11501153
expect(nestedObject.type).toBe('object');
@@ -1185,7 +1188,7 @@ describe('Schema Validation', () => {
11851188
// @ts-ignore - jsonSchema is available on Schema wrapper from ai sdk
11861189
const arrayWithItems = toolObj.inputSchema.jsonSchema.properties?.arrayWithItems;
11871190
expect(arrayWithItems?.type).toBe('array');
1188-
expect((arrayWithItems?.items as JSONSchema7)?.type).toBe('string');
1191+
expect((arrayWithItems?.items as JSONSchema)?.type).toBe('string');
11891192
});
11901193

11911194
it('should handle nested filter object for AI SDK', async () => {
@@ -1220,14 +1223,14 @@ describe('Schema Validation', () => {
12201223

12211224
const parameters = tool.toOpenAI().function.parameters;
12221225
expect(parameters).toBeDefined();
1223-
const aiSchema = jsonSchema(parameters as JSONSchema7);
1226+
const aiSchema = jsonSchema(parameters as JSONSchema);
12241227
expect(aiSchema).toBeDefined();
12251228

12261229
const aiSdkTool = await tool.toAISDK();
12271230
// TODO: Remove ts-ignore once AISDKToolDefinition properly types inputSchema.jsonSchema
12281231
// @ts-ignore - jsonSchema is available on Schema wrapper from ai sdk
12291232
const filterProp = aiSdkTool[tool.name].inputSchema.jsonSchema.properties?.filter as
1230-
| (JSONSchema7 & { properties: Record<string, JSONSchema7> })
1233+
| (JSONSchema & { properties: Record<string, JSONSchema> })
12311234
| undefined;
12321235

12331236
expect(filterProp?.type).toBe('object');

0 commit comments

Comments
 (0)