From 10d9380bfeb47777d93c8d101485d072a957d1c5 Mon Sep 17 00:00:00 2001 From: Finley Ge Date: Tue, 7 Jul 2026 18:22:29 +0800 Subject: [PATCH 1/5] feat(sdk/factory): add isToolParams metadata to input schemas --- apps/cli/templates/tool/index.tool.ts | 3 ++- apps/cli/templates/tool/index.toolset.ts | 6 +++-- docs/dev/how-to-devlop-plugin.en.md | 5 +++-- docs/dev/how-to-devlop-plugin.md | 5 +++-- sdk/factory/README.en.md | 8 ++++--- sdk/factory/README.md | 22 ++++++++++++------- .../skills/fastgpt-sdk-factory/SKILL.md | 14 +++++++----- .../fastgpt-system-tool-development/SKILL.md | 10 +++++---- sdk/factory/src/index.ts | 7 +++++- sdk/factory/src/tool-factory.test.ts | 8 +++++-- sdk/factory/test/fixtures/tool.ts | 3 ++- sdk/factory/test/fixtures/toolset.ts | 3 ++- test/fixtures/tool-suites/dbops/index.ts | 6 +++-- test/fixtures/tools/delay/index.ts | 3 ++- test/fixtures/tools/uploadFileEcho/index.ts | 3 ++- 15 files changed, 70 insertions(+), 36 deletions(-) diff --git a/apps/cli/templates/tool/index.tool.ts b/apps/cli/templates/tool/index.tool.ts index 95f21b8a..a221d960 100644 --- a/apps/cli/templates/tool/index.tool.ts +++ b/apps/cli/templates/tool/index.tool.ts @@ -10,7 +10,8 @@ const handler = createToolHandler({ inputSchema: z.object({ delay: z.number().meta({ title: 'Delay', - description: 'Delay duration in milliseconds' + description: 'Delay duration in milliseconds', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({}).meta({} satisfies OutputSchemaMetaType), diff --git a/apps/cli/templates/tool/index.toolset.ts b/apps/cli/templates/tool/index.toolset.ts index f81a6b9c..61c80161 100644 --- a/apps/cli/templates/tool/index.toolset.ts +++ b/apps/cli/templates/tool/index.toolset.ts @@ -33,7 +33,8 @@ const secretSchema = z.object({ const mysqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'SQL Query' + title: 'SQL Query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -62,7 +63,8 @@ const mysqlHandler = createToolHandler({ const pgsqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'SQL Query' + title: 'SQL Query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/docs/dev/how-to-devlop-plugin.en.md b/docs/dev/how-to-devlop-plugin.en.md index 1fdae257..7af22f2f 100644 --- a/docs/dev/how-to-devlop-plugin.en.md +++ b/docs/dev/how-to-devlop-plugin.en.md @@ -176,7 +176,8 @@ const handler = createToolHandler({ inputSchema: z.object({ query: z.string().min(1).meta({ title: 'Query', - description: 'Search keyword' + description: 'Search keyword', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -218,7 +219,7 @@ Core rules: - Keep `pluginId`, child tool `id`, input field names, and output field names stable after publishing. - Use `{ en, 'zh-CN' }` for `manifest.name`, `manifest.description`, and `versionDescription`. - Describe inputs, outputs, and secrets with Zod schemas. -- Add `InputSchemaMetaType` to input fields and `OutputSchemaMetaType` to output fields. +- Add `InputSchemaMetaType` to input fields and set `isToolParams: true` for parameters recommended to be managed by AI; add `OutputSchemaMetaType` to output fields. - Add `SecretSchemaMetaType` to secret fields and set `isSecret: true` for sensitive fields. - Handler return values must match `outputSchema`. - Convert external API errors into actionable messages and avoid exposing secrets, tokens, or complete sensitive responses. diff --git a/docs/dev/how-to-devlop-plugin.md b/docs/dev/how-to-devlop-plugin.md index 2e8532a4..4bd2ced2 100644 --- a/docs/dev/how-to-devlop-plugin.md +++ b/docs/dev/how-to-devlop-plugin.md @@ -176,7 +176,8 @@ const handler = createToolHandler({ inputSchema: z.object({ query: z.string().min(1).meta({ title: 'Query', - description: 'Search keyword' + description: 'Search keyword', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -218,7 +219,7 @@ export default defineTool({ - `pluginId`、子工具 `id`、输入字段名、输出字段名发布后保持稳定。 - `manifest.name`、`manifest.description` 和 `versionDescription` 使用 `{ en, 'zh-CN' }`。 - 输入、输出和密钥都用 Zod schema 描述。 -- 输入字段补充 `InputSchemaMetaType`,输出字段补充 `OutputSchemaMetaType`。 +- 输入字段补充 `InputSchemaMetaType`,推荐由 AI 托管补充的参数设置 `isToolParams: true`;输出字段补充 `OutputSchemaMetaType`。 - 密钥字段补充 `SecretSchemaMetaType`,敏感字段设置 `isSecret: true`。 - handler 返回值必须匹配 `outputSchema`。 - 外部 API 错误需要转成可定位的错误信息,同时避免输出密钥、令牌和完整敏感响应。 diff --git a/sdk/factory/README.en.md b/sdk/factory/README.en.md index 772944a9..7737feee 100644 --- a/sdk/factory/README.en.md +++ b/sdk/factory/README.en.md @@ -28,7 +28,8 @@ import z from 'zod'; const handler = createToolHandler({ inputSchema: z.object({ text: z.string().meta({ - title: 'Text' + title: 'Text', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -118,7 +119,7 @@ Defines a tool set with multiple child tools. All child tools share the top-leve A single tool can declare `secretSchema` in its handler. A tool set can declare a shared `secretSchema` at the top level of `defineToolSet()`. -Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields, `OutputSchemaMetaType` for output fields, and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. +Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParams: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. ## Host Invocation @@ -128,7 +129,8 @@ Use `ctx.invoke` to call FastGPT host capabilities. The SDK currently exposes fi const handler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ - title: 'Content' + title: 'Content', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/README.md b/sdk/factory/README.md index 24ebb8e6..76c95830 100644 --- a/sdk/factory/README.md +++ b/sdk/factory/README.md @@ -34,7 +34,8 @@ import z from 'zod'; const handler = createToolHandler({ inputSchema: z.object({ text: z.string().meta({ - title: 'Text' + title: 'Text', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -83,7 +84,8 @@ Defines a tool handler and infers `input`, `output`, and `secrets` types from Zo const handler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'Query' + title: 'Query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -144,7 +146,8 @@ Defines a tool set with multiple child tools. All child tools share the top-leve const searchHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'Query' + title: 'Query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -158,7 +161,8 @@ const searchHandler = createToolHandler({ const summaryHandler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ - title: 'Content' + title: 'Content', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -240,9 +244,9 @@ export default defineToolSet({ A single tool can declare `secretSchema` in its handler. A tool set can declare a shared `secretSchema` at the top level of `defineToolSet()`. -Schema 字段元数据通过 Zod `.meta()` 写入构建后的 JSON Schema。输入字段使用 `InputSchemaMetaType`,输出字段使用 `OutputSchemaMetaType`,密钥字段使用 `SecretSchemaMetaType`。`secretSchema` 的每个字段都要包含 `isSecret`,需要加密存储时设为 `true`。 +Schema 字段元数据通过 Zod `.meta()` 写入构建后的 JSON Schema。输入字段使用 `InputSchemaMetaType`,推荐由 AI 托管补充的输入参数可设置 `isToolParams: true`。输出字段使用 `OutputSchemaMetaType`,密钥字段使用 `SecretSchemaMetaType`。`secretSchema` 的每个字段都要包含 `isSecret`,需要加密存储时设为 `true`。 -Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields, `OutputSchemaMetaType` for output fields, and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. +Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParams: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. ```ts const secretSchema = z.object({ @@ -259,7 +263,8 @@ const secretSchema = z.object({ const handler = createToolHandler({ inputSchema: z.object({ prompt: z.string().meta({ - title: 'Prompt' + title: 'Prompt', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -286,7 +291,8 @@ Use `ctx.invoke` to call FastGPT host capabilities. The SDK currently exposes fi const handler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ - title: 'Content' + title: 'Content', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md b/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md index cb6c3c66..a11f366b 100644 --- a/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md +++ b/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md @@ -23,7 +23,8 @@ import z from 'zod'; const handler = createToolHandler({ inputSchema: z.object({ text: z.string().meta({ - title: 'Text' + title: 'Text', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -64,7 +65,7 @@ export default defineTool({ - Use `z.object(...)` for `inputSchema` and `outputSchema` so TypeScript can infer `input` and return types. - Import `InputSchemaMetaType`, `OutputSchemaMetaType`, and `SecretSchemaMetaType` from `@fastgpt-plugin/sdk-factory` when schema metadata is used. - Add `.meta({ ... } satisfies InputSchemaMetaType)` to input fields and `.meta({ ... } satisfies OutputSchemaMetaType)` to output fields that need UI/manifest metadata. -- Set `toolDescription` in an input field's `.meta()` when that field should be available as an automatically filled tool-call parameter; without `toolDescription`, users must specify the field manually. +- Set `isToolParams: true` in an input field's `.meta()` when that field is recommended to be managed by AI; use `toolDescription` for the model-facing parameter description. - Add `.meta({ isSecret: true | false, ... } satisfies SecretSchemaMetaType)` to every `secretSchema` field; set `isSecret: true` for values that must be encrypted at rest. - Return an object that matches `outputSchema`; throw errors for failed operations. - Add `secretSchema` when plugin configuration needs secrets such as API keys. @@ -84,7 +85,8 @@ const handler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'Query', - toolDescription: 'Search query' + toolDescription: 'Search query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -162,7 +164,8 @@ const secretSchema = z.object({ const searchHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'Query' + title: 'Query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -214,7 +217,8 @@ Use `ctx.invoke` for host capabilities. The SDK exposes `userInfo()` and `upload const uploadHandler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ - title: 'Content' + title: 'Content', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md b/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md index e11ae45e..b75b821d 100644 --- a/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md +++ b/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md @@ -62,7 +62,7 @@ npx @fastgpt-plugin/cli pack --entry --dist { const handler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'Query' + title: 'Query', + toolDescription: 'Search query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -354,7 +356,9 @@ describe('ToolFactory schema metadata', () => { const secretJsonSchema = z.toJSONSchema(secretSchema) as JsonSchemaObject; expect(inputSchema.properties?.query).toMatchObject({ - title: 'Query' + title: 'Query', + toolDescription: 'Search query', + isToolParams: true }); expect(outputSchema.properties?.answer).toMatchObject({ title: 'Answer' diff --git a/sdk/factory/test/fixtures/tool.ts b/sdk/factory/test/fixtures/tool.ts index eb02e822..cbfbf7b2 100644 --- a/sdk/factory/test/fixtures/tool.ts +++ b/sdk/factory/test/fixtures/tool.ts @@ -11,7 +11,8 @@ import { const handler = createToolHandler({ inputSchema: z.object({ msg: z.string().meta({ - title: 'Message' + title: 'Message', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/test/fixtures/toolset.ts b/sdk/factory/test/fixtures/toolset.ts index af5a9794..b7da3744 100644 --- a/sdk/factory/test/fixtures/toolset.ts +++ b/sdk/factory/test/fixtures/toolset.ts @@ -17,7 +17,8 @@ const toolSetSecretSchema = z.object({ const handler = createToolHandler({ inputSchema: z.object({ msg: z.string().meta({ - title: 'Message' + title: 'Message', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/test/fixtures/tool-suites/dbops/index.ts b/test/fixtures/tool-suites/dbops/index.ts index f81a6b9c..61c80161 100644 --- a/test/fixtures/tool-suites/dbops/index.ts +++ b/test/fixtures/tool-suites/dbops/index.ts @@ -33,7 +33,8 @@ const secretSchema = z.object({ const mysqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'SQL Query' + title: 'SQL Query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -62,7 +63,8 @@ const mysqlHandler = createToolHandler({ const pgsqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ - title: 'SQL Query' + title: 'SQL Query', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/test/fixtures/tools/delay/index.ts b/test/fixtures/tools/delay/index.ts index 95f21b8a..a221d960 100644 --- a/test/fixtures/tools/delay/index.ts +++ b/test/fixtures/tools/delay/index.ts @@ -10,7 +10,8 @@ const handler = createToolHandler({ inputSchema: z.object({ delay: z.number().meta({ title: 'Delay', - description: 'Delay duration in milliseconds' + description: 'Delay duration in milliseconds', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({}).meta({} satisfies OutputSchemaMetaType), diff --git a/test/fixtures/tools/uploadFileEcho/index.ts b/test/fixtures/tools/uploadFileEcho/index.ts index 1db6ae19..e5e1dab1 100644 --- a/test/fixtures/tools/uploadFileEcho/index.ts +++ b/test/fixtures/tools/uploadFileEcho/index.ts @@ -9,7 +9,8 @@ import z from 'zod'; const handler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ - title: 'Content' + title: 'Content', + isToolParams: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ From e365840a6dcc8a79c550b1e03e10d52109de01d3 Mon Sep 17 00:00:00 2001 From: Finley Ge Date: Tue, 7 Jul 2026 18:25:17 +0800 Subject: [PATCH 2/5] chore(version): bump package versions to alpha release --- apps/cli/package.json | 2 +- sdk/client/package.json | 2 +- sdk/factory/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 2161004c..11112bc0 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "@fastgpt-plugin/cli", - "version": "0.2.1", + "version": "0.2.2-alpha.1", "description": "CLI for creating, debugging, building, checking, and packaging FastGPT Plugin tools.", "keywords": [ "fastgpt", diff --git a/sdk/client/package.json b/sdk/client/package.json index c80ca9cf..e0514911 100644 --- a/sdk/client/package.json +++ b/sdk/client/package.json @@ -1,6 +1,6 @@ { "name": "@fastgpt-plugin/sdk-client", - "version": "0.0.1", + "version": "0.1.0-alpha.1", "description": "TypeScript client SDK for calling the FastGPT Plugin service APIs.", "keywords": [ "fastgpt", diff --git a/sdk/factory/package.json b/sdk/factory/package.json index e4259a36..d515129d 100644 --- a/sdk/factory/package.json +++ b/sdk/factory/package.json @@ -1,6 +1,6 @@ { "name": "@fastgpt-plugin/sdk-factory", - "version": "0.0.1", + "version": "0.1.0-alpha.1", "description": "TypeScript factory SDK for authoring FastGPT Plugin tools and tool suites.", "keywords": [ "fastgpt", From de452938bd5c560a77a091ff72598d33b535f776 Mon Sep 17 00:00:00 2001 From: Finley Ge Date: Thu, 9 Jul 2026 12:08:08 +0800 Subject: [PATCH 3/5] fix(plugin): normalize tool param metadata in tool detail --- .../src/plugin/tool.impl.test.ts | 96 +++++++++++++++++++ .../infrastructure/src/plugin/tool.impl.ts | 73 ++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/packages/infrastructure/src/plugin/tool.impl.test.ts b/packages/infrastructure/src/plugin/tool.impl.test.ts index 8c6c4a8b..30580e0c 100644 --- a/packages/infrastructure/src/plugin/tool.impl.test.ts +++ b/packages/infrastructure/src/plugin/tool.impl.test.ts @@ -113,6 +113,102 @@ describe('ToolManager.detail', () => { version: '1.10.0' }); }); + + it('normalizes input schema tool parameter metadata in tool detail', async () => { + const toolManager = createToolManager(); + listVersions.mockResolvedValue(successResult([{ version: '1.0.0' }])); + getPluginByUserPluginId.mockResolvedValue( + successResult({ + ...makeTool('1.0.0'), + inputSchema: { + type: 'object', + properties: { + withToolDescription: { + type: 'string', + toolDescription: 'Existing model-facing description' + }, + manualByDefault: { + type: 'string', + description: { + en: 'Fallback manual description', + 'zh-CN': '手动参数说明' + } + }, + explicitFalse: { + type: 'string', + description: { + en: 'Explicit false fallback' + }, + isToolParams: false + }, + explicitTrue: { + type: 'string', + description: { + en: 'Explicit true fallback' + }, + isToolParams: true + } + } + }, + children: [ + { + id: 'child', + name: { en: 'Child' }, + description: { en: 'Child tool' }, + icon: 'https://example.com/child.svg', + toolDescription: 'Child tool', + inputSchema: { + type: 'object', + properties: { + childParam: { + type: 'string', + description: { + en: 'Child fallback' + } + } + } + }, + outputSchema: { + type: 'object' + } + } + ] + } satisfies ToolType) + ); + + const [result, err] = await toolManager.detail({ + pluginId: 'getTime', + source: 'system', + version: '1.0.0' + }); + + const properties = (result?.inputSchema as { properties: Record }).properties; + const childProperties = (result?.children?.[0]?.inputSchema as { + properties: Record; + }).properties; + + expect(err).toBeNull(); + expect(properties.withToolDescription).toMatchObject({ + toolDescription: 'Existing model-facing description', + isToolParams: true + }); + expect(properties.manualByDefault).toMatchObject({ + toolDescription: 'Fallback manual description', + isToolParams: false + }); + expect(properties.explicitFalse).toMatchObject({ + toolDescription: 'Explicit false fallback', + isToolParams: false + }); + expect(properties.explicitTrue).toMatchObject({ + toolDescription: 'Explicit true fallback', + isToolParams: true + }); + expect(childProperties.childParam).toMatchObject({ + toolDescription: 'Child fallback', + isToolParams: false + }); + }); }); describe('ToolManager.run', () => { diff --git a/packages/infrastructure/src/plugin/tool.impl.ts b/packages/infrastructure/src/plugin/tool.impl.ts index 4f7220f7..ad7f944b 100644 --- a/packages/infrastructure/src/plugin/tool.impl.ts +++ b/packages/infrastructure/src/plugin/tool.impl.ts @@ -28,6 +28,8 @@ import { ErrorCode } from '@infrastructure/errors/error.registry'; import { InvokeManager } from './invoke/invoke.impl'; import { Semver } from './utils/semver'; +type JsonObject = Record; + export type ToolManagerDeps = { pluginRepo: PluginRepoPort; pluginRuntimeManager: PluginRuntimeManagerPort; @@ -41,6 +43,72 @@ export type PluginToolRunPayloadType = { childId?: string; }; +function isJsonObject(value: unknown): value is JsonObject { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function getDescriptionFallback(description: unknown): string | undefined { + if (typeof description === 'string' && description.length > 0) { + return description; + } + + if (!isJsonObject(description)) { + return undefined; + } + + const en = description.en; + return typeof en === 'string' && en.length > 0 ? en : undefined; +} + +function normalizeInputSchemaToolParams(inputSchema: unknown): unknown { + if (!isJsonObject(inputSchema) || !isJsonObject(inputSchema.properties)) { + return inputSchema; + } + + const properties = Object.fromEntries( + Object.entries(inputSchema.properties).map(([key, property]) => { + if (!isJsonObject(property)) { + return [key, property]; + } + + const normalizedProperty = { ...property }; + const hasToolDescription = + typeof normalizedProperty.toolDescription === 'string' && + normalizedProperty.toolDescription.length > 0; + const hasIsToolParams = normalizedProperty.isToolParams !== undefined; + + if (hasIsToolParams) { + if (!hasToolDescription) { + const fallback = getDescriptionFallback(normalizedProperty.description); + if (fallback !== undefined) { + normalizedProperty.toolDescription = fallback; + } + } + + return [key, normalizedProperty]; + } + + if (hasToolDescription) { + normalizedProperty.isToolParams = true; + return [key, normalizedProperty]; + } + + const fallback = getDescriptionFallback(normalizedProperty.description); + if (fallback !== undefined) { + normalizedProperty.toolDescription = fallback; + } + normalizedProperty.isToolParams = false; + + return [key, normalizedProperty]; + }) + ); + + return { + ...inputSchema, + properties + }; +} + export class ToolManager implements ToolManagerPort { private static instance: ToolManager; private constructor(private deps: ToolManagerDeps) {} @@ -61,6 +129,11 @@ export class ToolManager implements ToolManagerPort { }): ToolDetailType { return { ...tool, + inputSchema: normalizeInputSchemaToolParams(tool.inputSchema), + children: tool.children?.map((child) => ({ + ...child, + inputSchema: normalizeInputSchemaToolParams(child.inputSchema) + })), source, isToolset: Boolean(tool.children?.length), isLatestVersion From b0c6001f34a3300af6cb3a232d6e0593277ebbaf Mon Sep 17 00:00:00 2001 From: Finley Ge Date: Mon, 20 Jul 2026 22:35:04 +0800 Subject: [PATCH 4/5] fix(sdk): rename tool parameter metadata flag --- apps/cli/templates/tool/index.tool.ts | 2 +- apps/cli/templates/tool/index.toolset.ts | 4 ++-- docs/dev/how-to-devlop-plugin.en.md | 4 ++-- docs/dev/how-to-devlop-plugin.md | 4 ++-- .../infrastructure/src/plugin/tool.impl.test.ts | 14 +++++++------- packages/infrastructure/src/plugin/tool.impl.ts | 8 ++++---- sdk/factory/README.en.md | 6 +++--- sdk/factory/README.md | 16 ++++++++-------- sdk/factory/skills/fastgpt-sdk-factory/SKILL.md | 10 +++++----- .../fastgpt-system-tool-development/SKILL.md | 8 ++++---- sdk/factory/src/index.ts | 2 +- sdk/factory/src/tool-factory.test.ts | 4 ++-- sdk/factory/test/fixtures/tool.ts | 2 +- sdk/factory/test/fixtures/toolset.ts | 2 +- test/fixtures/tool-suites/dbops/index.ts | 4 ++-- test/fixtures/tools/delay/index.ts | 2 +- test/fixtures/tools/uploadFileEcho/index.ts | 2 +- 17 files changed, 47 insertions(+), 47 deletions(-) diff --git a/apps/cli/templates/tool/index.tool.ts b/apps/cli/templates/tool/index.tool.ts index a221d960..4fe7ee17 100644 --- a/apps/cli/templates/tool/index.tool.ts +++ b/apps/cli/templates/tool/index.tool.ts @@ -11,7 +11,7 @@ const handler = createToolHandler({ delay: z.number().meta({ title: 'Delay', description: 'Delay duration in milliseconds', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({}).meta({} satisfies OutputSchemaMetaType), diff --git a/apps/cli/templates/tool/index.toolset.ts b/apps/cli/templates/tool/index.toolset.ts index 61c80161..1762bfd9 100644 --- a/apps/cli/templates/tool/index.toolset.ts +++ b/apps/cli/templates/tool/index.toolset.ts @@ -34,7 +34,7 @@ const mysqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'SQL Query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -64,7 +64,7 @@ const pgsqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'SQL Query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/docs/dev/how-to-devlop-plugin.en.md b/docs/dev/how-to-devlop-plugin.en.md index 7af22f2f..7aa205f5 100644 --- a/docs/dev/how-to-devlop-plugin.en.md +++ b/docs/dev/how-to-devlop-plugin.en.md @@ -177,7 +177,7 @@ const handler = createToolHandler({ query: z.string().min(1).meta({ title: 'Query', description: 'Search keyword', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -219,7 +219,7 @@ Core rules: - Keep `pluginId`, child tool `id`, input field names, and output field names stable after publishing. - Use `{ en, 'zh-CN' }` for `manifest.name`, `manifest.description`, and `versionDescription`. - Describe inputs, outputs, and secrets with Zod schemas. -- Add `InputSchemaMetaType` to input fields and set `isToolParams: true` for parameters recommended to be managed by AI; add `OutputSchemaMetaType` to output fields. +- Add `InputSchemaMetaType` to input fields and set `isToolParam: true` for parameters recommended to be managed by AI; add `OutputSchemaMetaType` to output fields. - Add `SecretSchemaMetaType` to secret fields and set `isSecret: true` for sensitive fields. - Handler return values must match `outputSchema`. - Convert external API errors into actionable messages and avoid exposing secrets, tokens, or complete sensitive responses. diff --git a/docs/dev/how-to-devlop-plugin.md b/docs/dev/how-to-devlop-plugin.md index 4bd2ced2..408134d9 100644 --- a/docs/dev/how-to-devlop-plugin.md +++ b/docs/dev/how-to-devlop-plugin.md @@ -177,7 +177,7 @@ const handler = createToolHandler({ query: z.string().min(1).meta({ title: 'Query', description: 'Search keyword', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -219,7 +219,7 @@ export default defineTool({ - `pluginId`、子工具 `id`、输入字段名、输出字段名发布后保持稳定。 - `manifest.name`、`manifest.description` 和 `versionDescription` 使用 `{ en, 'zh-CN' }`。 - 输入、输出和密钥都用 Zod schema 描述。 -- 输入字段补充 `InputSchemaMetaType`,推荐由 AI 托管补充的参数设置 `isToolParams: true`;输出字段补充 `OutputSchemaMetaType`。 +- 输入字段补充 `InputSchemaMetaType`,推荐由 AI 托管补充的参数设置 `isToolParam: true`;输出字段补充 `OutputSchemaMetaType`。 - 密钥字段补充 `SecretSchemaMetaType`,敏感字段设置 `isSecret: true`。 - handler 返回值必须匹配 `outputSchema`。 - 外部 API 错误需要转成可定位的错误信息,同时避免输出密钥、令牌和完整敏感响应。 diff --git a/packages/infrastructure/src/plugin/tool.impl.test.ts b/packages/infrastructure/src/plugin/tool.impl.test.ts index 30580e0c..d9364d52 100644 --- a/packages/infrastructure/src/plugin/tool.impl.test.ts +++ b/packages/infrastructure/src/plugin/tool.impl.test.ts @@ -139,14 +139,14 @@ describe('ToolManager.detail', () => { description: { en: 'Explicit false fallback' }, - isToolParams: false + isToolParam: false }, explicitTrue: { type: 'string', description: { en: 'Explicit true fallback' }, - isToolParams: true + isToolParam: true } } }, @@ -190,23 +190,23 @@ describe('ToolManager.detail', () => { expect(err).toBeNull(); expect(properties.withToolDescription).toMatchObject({ toolDescription: 'Existing model-facing description', - isToolParams: true + isToolParam: true }); expect(properties.manualByDefault).toMatchObject({ toolDescription: 'Fallback manual description', - isToolParams: false + isToolParam: false }); expect(properties.explicitFalse).toMatchObject({ toolDescription: 'Explicit false fallback', - isToolParams: false + isToolParam: false }); expect(properties.explicitTrue).toMatchObject({ toolDescription: 'Explicit true fallback', - isToolParams: true + isToolParam: true }); expect(childProperties.childParam).toMatchObject({ toolDescription: 'Child fallback', - isToolParams: false + isToolParam: false }); }); }); diff --git a/packages/infrastructure/src/plugin/tool.impl.ts b/packages/infrastructure/src/plugin/tool.impl.ts index ad7f944b..6bbff60d 100644 --- a/packages/infrastructure/src/plugin/tool.impl.ts +++ b/packages/infrastructure/src/plugin/tool.impl.ts @@ -75,9 +75,9 @@ function normalizeInputSchemaToolParams(inputSchema: unknown): unknown { const hasToolDescription = typeof normalizedProperty.toolDescription === 'string' && normalizedProperty.toolDescription.length > 0; - const hasIsToolParams = normalizedProperty.isToolParams !== undefined; + const hasIsToolParam = normalizedProperty.isToolParam !== undefined; - if (hasIsToolParams) { + if (hasIsToolParam) { if (!hasToolDescription) { const fallback = getDescriptionFallback(normalizedProperty.description); if (fallback !== undefined) { @@ -89,7 +89,7 @@ function normalizeInputSchemaToolParams(inputSchema: unknown): unknown { } if (hasToolDescription) { - normalizedProperty.isToolParams = true; + normalizedProperty.isToolParam = true; return [key, normalizedProperty]; } @@ -97,7 +97,7 @@ function normalizeInputSchemaToolParams(inputSchema: unknown): unknown { if (fallback !== undefined) { normalizedProperty.toolDescription = fallback; } - normalizedProperty.isToolParams = false; + normalizedProperty.isToolParam = false; return [key, normalizedProperty]; }) diff --git a/sdk/factory/README.en.md b/sdk/factory/README.en.md index 7737feee..5e79a06a 100644 --- a/sdk/factory/README.en.md +++ b/sdk/factory/README.en.md @@ -29,7 +29,7 @@ const handler = createToolHandler({ inputSchema: z.object({ text: z.string().meta({ title: 'Text', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -119,7 +119,7 @@ Defines a tool set with multiple child tools. All child tools share the top-leve A single tool can declare `secretSchema` in its handler. A tool set can declare a shared `secretSchema` at the top level of `defineToolSet()`. -Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParams: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. +Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParam: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. ## Host Invocation @@ -130,7 +130,7 @@ const handler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ title: 'Content', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/README.md b/sdk/factory/README.md index 76c95830..2417dacb 100644 --- a/sdk/factory/README.md +++ b/sdk/factory/README.md @@ -35,7 +35,7 @@ const handler = createToolHandler({ inputSchema: z.object({ text: z.string().meta({ title: 'Text', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -85,7 +85,7 @@ const handler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'Query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -147,7 +147,7 @@ const searchHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'Query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -162,7 +162,7 @@ const summaryHandler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ title: 'Content', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -244,9 +244,9 @@ export default defineToolSet({ A single tool can declare `secretSchema` in its handler. A tool set can declare a shared `secretSchema` at the top level of `defineToolSet()`. -Schema 字段元数据通过 Zod `.meta()` 写入构建后的 JSON Schema。输入字段使用 `InputSchemaMetaType`,推荐由 AI 托管补充的输入参数可设置 `isToolParams: true`。输出字段使用 `OutputSchemaMetaType`,密钥字段使用 `SecretSchemaMetaType`。`secretSchema` 的每个字段都要包含 `isSecret`,需要加密存储时设为 `true`。 +Schema 字段元数据通过 Zod `.meta()` 写入构建后的 JSON Schema。输入字段使用 `InputSchemaMetaType`,推荐由 AI 托管补充的输入参数可设置 `isToolParam: true`。输出字段使用 `OutputSchemaMetaType`,密钥字段使用 `SecretSchemaMetaType`。`secretSchema` 的每个字段都要包含 `isSecret`,需要加密存储时设为 `true`。 -Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParams: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. +Schema field metadata is written into the built JSON Schema through Zod `.meta()`. Use `InputSchemaMetaType` for input fields and optionally set `isToolParam: true` for input parameters recommended to be managed by AI. Use `OutputSchemaMetaType` for output fields and `SecretSchemaMetaType` for secret fields. Every `secretSchema` field must include `isSecret`; set it to `true` for values that need encrypted storage. ```ts const secretSchema = z.object({ @@ -264,7 +264,7 @@ const handler = createToolHandler({ inputSchema: z.object({ prompt: z.string().meta({ title: 'Prompt', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -292,7 +292,7 @@ const handler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ title: 'Content', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md b/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md index a11f366b..228010e9 100644 --- a/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md +++ b/sdk/factory/skills/fastgpt-sdk-factory/SKILL.md @@ -24,7 +24,7 @@ const handler = createToolHandler({ inputSchema: z.object({ text: z.string().meta({ title: 'Text', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -65,7 +65,7 @@ export default defineTool({ - Use `z.object(...)` for `inputSchema` and `outputSchema` so TypeScript can infer `input` and return types. - Import `InputSchemaMetaType`, `OutputSchemaMetaType`, and `SecretSchemaMetaType` from `@fastgpt-plugin/sdk-factory` when schema metadata is used. - Add `.meta({ ... } satisfies InputSchemaMetaType)` to input fields and `.meta({ ... } satisfies OutputSchemaMetaType)` to output fields that need UI/manifest metadata. -- Set `isToolParams: true` in an input field's `.meta()` when that field is recommended to be managed by AI; use `toolDescription` for the model-facing parameter description. +- Set `isToolParam: true` in an input field's `.meta()` when that field is recommended to be managed by AI; use `toolDescription` for the model-facing parameter description. - Add `.meta({ isSecret: true | false, ... } satisfies SecretSchemaMetaType)` to every `secretSchema` field; set `isSecret: true` for values that must be encrypted at rest. - Return an object that matches `outputSchema`; throw errors for failed operations. - Add `secretSchema` when plugin configuration needs secrets such as API keys. @@ -86,7 +86,7 @@ const handler = createToolHandler({ query: z.string().meta({ title: 'Query', toolDescription: 'Search query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -165,7 +165,7 @@ const searchHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'Query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -218,7 +218,7 @@ const uploadHandler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ title: 'Content', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md b/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md index b75b821d..e9605913 100644 --- a/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md +++ b/sdk/factory/skills/fastgpt-system-tool-development/SKILL.md @@ -62,7 +62,7 @@ npx @fastgpt-plugin/cli pack --entry --dist { query: z.string().meta({ title: 'Query', toolDescription: 'Search query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -358,7 +358,7 @@ describe('ToolFactory schema metadata', () => { expect(inputSchema.properties?.query).toMatchObject({ title: 'Query', toolDescription: 'Search query', - isToolParams: true + isToolParam: true }); expect(outputSchema.properties?.answer).toMatchObject({ title: 'Answer' diff --git a/sdk/factory/test/fixtures/tool.ts b/sdk/factory/test/fixtures/tool.ts index cbfbf7b2..a6a81d15 100644 --- a/sdk/factory/test/fixtures/tool.ts +++ b/sdk/factory/test/fixtures/tool.ts @@ -12,7 +12,7 @@ const handler = createToolHandler({ inputSchema: z.object({ msg: z.string().meta({ title: 'Message', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/sdk/factory/test/fixtures/toolset.ts b/sdk/factory/test/fixtures/toolset.ts index b7da3744..b5d74fc7 100644 --- a/sdk/factory/test/fixtures/toolset.ts +++ b/sdk/factory/test/fixtures/toolset.ts @@ -18,7 +18,7 @@ const handler = createToolHandler({ inputSchema: z.object({ msg: z.string().meta({ title: 'Message', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/test/fixtures/tool-suites/dbops/index.ts b/test/fixtures/tool-suites/dbops/index.ts index 61c80161..1762bfd9 100644 --- a/test/fixtures/tool-suites/dbops/index.ts +++ b/test/fixtures/tool-suites/dbops/index.ts @@ -34,7 +34,7 @@ const mysqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'SQL Query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ @@ -64,7 +64,7 @@ const pgsqlHandler = createToolHandler({ inputSchema: z.object({ query: z.string().meta({ title: 'SQL Query', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ diff --git a/test/fixtures/tools/delay/index.ts b/test/fixtures/tools/delay/index.ts index a221d960..4fe7ee17 100644 --- a/test/fixtures/tools/delay/index.ts +++ b/test/fixtures/tools/delay/index.ts @@ -11,7 +11,7 @@ const handler = createToolHandler({ delay: z.number().meta({ title: 'Delay', description: 'Delay duration in milliseconds', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({}).meta({} satisfies OutputSchemaMetaType), diff --git a/test/fixtures/tools/uploadFileEcho/index.ts b/test/fixtures/tools/uploadFileEcho/index.ts index e5e1dab1..d5507b1a 100644 --- a/test/fixtures/tools/uploadFileEcho/index.ts +++ b/test/fixtures/tools/uploadFileEcho/index.ts @@ -10,7 +10,7 @@ const handler = createToolHandler({ inputSchema: z.object({ content: z.string().meta({ title: 'Content', - isToolParams: true + isToolParam: true } satisfies InputSchemaMetaType) }), outputSchema: z.object({ From e513aa753e2d320a67947707a2262cc68e930a68 Mon Sep 17 00:00:00 2001 From: Finley Ge Date: Tue, 21 Jul 2026 17:45:33 +0800 Subject: [PATCH 5/5] chore(sdk-client): bump package version to 0.1.0-alpha.2 --- sdk/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/client/package.json b/sdk/client/package.json index e0514911..f7b15b72 100644 --- a/sdk/client/package.json +++ b/sdk/client/package.json @@ -1,6 +1,6 @@ { "name": "@fastgpt-plugin/sdk-client", - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.2", "description": "TypeScript client SDK for calling the FastGPT Plugin service APIs.", "keywords": [ "fastgpt",