document/content/plugin/system-tool-development.en.mdx
This document targets system tool development after FastGPT v4.15.0. The new FastGPT Plugin service unifies system tools, model presets, and similar capabilities as installable, updatable, runtime-isolated plugin packages. A plugin is eventually delivered to the FastGPT Plugin service as a .pkg file.
The currently stable system tool plugin types are:
defineTool().defineToolSet().System tool plugins run in the runtime provided by the FastGPT Plugin service. The FastGPT main service invokes tools through the plugin service, and plugin code uses @fastgpt-plugin/sdk-factory to describe input, output, secret configuration, and execution logic.
.pkg format, making installation, version management, hot updates, and future plugin type expansion easier.local-pool, where each plugin version has its own process pool, queue, and runtime configuration.@fastgpt-plugin/cli and @fastgpt-plugin/sdk-factory. The legacy config.ts, versionList, and bun run build:pkg flow is no longer the primary development model.Clarify these items before coding:
| Information | Description |
|---|---|
| Plugin type | tool or tool-suite. |
| Plugin ID | pluginId, globally stable and unique. Keep it unchanged after release. |
| Child tool ID | Required for tool suites. children[].id stays unchanged after release. |
| Chinese and English names | name.en and name.zh-CN. |
| Chinese and English descriptions | description.en and description.zh-CN. |
| Inputs | Type, constraints, default value, UI title, and description for each field. |
| Outputs | Type, meaning, and downstream usage for each field. |
| Secrets | API Key, Base URL, username/password, and similar values, described through secretSchema. |
| External API | Request method, auth method, timeout, rate limit, error response, and test account. |
| File capability | Use ctx.invoke.uploadFile() when file upload is needed. |
| Streaming output | Use ctx.streamResponse() when intermediate progress should be shown to the user. |
| Test cases | Include at least success, invalid parameters, auth failure, and upstream failure. |
Missing information that affects plugin ID, auth method, billing, or listing security should be confirmed first. Other missing information can use reasonable defaults, with assumptions recorded in the submission notes.
When using Claude Code, Codex, or another agent tool, copy this prompt:
请根据以下 FastGPT 官方插件开发 Skill 开发插件:
https://raw.githubusercontent.com/labring/fastgpt-official-plugins/refs/heads/main/.agents/skills/develop-fastgpt-plugin/SKILL.md
执行要求:
1. 先读取并理解该 Skill 的完整内容,后续开发流程以该 Skill 为准。
2. 在开始编码前,收集插件名称、插件类型、中文/英文名称与描述、输入输出、密钥、外部 API、预期行为、错误处理和测试样例。
3. 如需求缺失,最多提出 3 个关键问题;如果可以合理默认,说明假设后继续推进。
4. 使用 `@fastgpt-plugin/cli` 创建插件骨架,并优先遵循仓库内已有插件的结构、命名、测试和构建方式。
5. 实现完成后运行必要验证,包括测试、构建、插件检查和打包;无法验证的项目需要说明原因。
6. 最终输出变更文件、验证结果、剩余假设和需要人工确认的外部 API 行为。
When developing or maintaining SDK/CLI in the fastgpt-plugin repository, also refer to local skills:
sdk/factory/skills/fastgpt-plugin-development/SKILL.mdsdk/factory/skills/fastgpt-system-tool-development/SKILL.mdsdk/factory/skills/fastgpt-sdk-factory/SKILL.mdRecommended environment:
pnpm; the fastgpt-plugin repository uses pnpm workspace.gh, used for forking, creating repositories, and submitting PRs.When developing community plugins, first fork and clone the community repository:
gh repo fork labring/fastgpt-community-plugins --clone
cd fastgpt-community-plugins
pnpm install
When debugging the CLI or SDK in the fastgpt-plugin repository, install dependencies and build the CLI/SDK first:
pnpm install
pnpm build:sdk-factory
pnpm build:cli
Single-tool plugin:
pnpx @fastgpt-plugin/cli create my-tool --type tool --cwd packages/tools
Tool-suite plugin:
pnpx @fastgpt-plugin/cli create my-tool-suite --type tool-suite --cwd packages/tools
You can also enter the target directory and create interactively:
pnpx @fastgpt-plugin/cli create
The CLI creates the plugin directory and common files:
| File | Purpose |
|---|---|
index.ts | Plugin entry, default-exporting defineTool() or defineToolSet(). |
package.json | Plugin dependencies and build, build:dev, pack, and test scripts. |
tsconfig.json | TypeScript config. |
vitest.config.ts | Test config. |
README.md | Plugin description. |
logo.svg | Main plugin icon. |
The system tool entry must default-export an SDK factory instance:
import {
createToolHandler,
defineTool,
type InputSchemaMetaType,
type OutputSchemaMetaType,
type SecretSchemaMetaType
} from '@fastgpt-plugin/sdk-factory';
import z from 'zod';
const secretSchema = z.object({
apiKey: z
.string()
.min(1)
.meta({
title: 'API Key',
isSecret: true
} satisfies SecretSchemaMetaType)
});
const handler = createToolHandler({
inputSchema: z.object({
query: z
.string()
.min(1)
.meta({
title: 'Query',
description: 'Search keyword'
} satisfies InputSchemaMetaType)
}),
outputSchema: z.object({
result: z.string().meta({
title: 'Result'
} satisfies OutputSchemaMetaType)
}),
secretSchema,
handler: async (input, ctx) => {
return {
result: input.query
};
}
});
export default defineTool({
manifest: {
pluginId: 'example-search',
version: '1.0.0',
name: {
en: 'Example Search',
'zh-CN': '示例搜索'
},
description: {
en: 'Search example data',
'zh-CN': '搜索示例数据'
},
versionDescription: {
en: 'Initial version',
'zh-CN': '初始版本'
},
tags: ['tools']
},
handler
});
Core rules:
pluginId, child tool id, input field names, and output field names stable after publishing.{ en, 'zh-CN' } for manifest.name, manifest.description, and versionDescription.InputSchemaMetaType to input fields and OutputSchemaMetaType to output fields.SecretSchemaMetaType to secret fields and set isSecret: true for sensitive fields.outputSchema.ctx.invoke.uploadFile() when host file upload is needed, and prefer preserving the returned err.ctx.streamResponse() when progress should be shown to users.Use defineToolSet() for tool suites. Put shared information in the top-level manifest and secretSchema, and declare each child tool's independent id, name, description, and handler in children.
import {
createToolHandler,
defineToolSet,
type InputSchemaMetaType,
type OutputSchemaMetaType,
type SecretSchemaMetaType
} from '@fastgpt-plugin/sdk-factory';
import z from 'zod';
const secretSchema = z.object({
apiKey: z.string().meta({
title: 'API Key',
isSecret: true
} satisfies SecretSchemaMetaType)
});
const searchHandler = createToolHandler({
inputSchema: z.object({
query: z.string().meta({
title: 'Query'
} satisfies InputSchemaMetaType)
}),
outputSchema: z.object({
items: z.array(z.string()).meta({
title: 'Items'
} satisfies OutputSchemaMetaType)
}),
secretSchema,
handler: async (input) => ({ items: [input.query] })
});
const summaryHandler = createToolHandler({
inputSchema: z.object({
content: z.string().meta({
title: 'Content'
} satisfies InputSchemaMetaType)
}),
outputSchema: z.object({
summary: z.string().meta({
title: 'Summary'
} satisfies OutputSchemaMetaType)
}),
secretSchema,
handler: async (input) => ({ summary: input.content.slice(0, 100) })
});
export default defineToolSet({
manifest: {
pluginId: 'text-tools',
version: '1.0.0',
name: {
en: 'Text Tools',
'zh-CN': '文本工具集'
},
description: {
en: 'Search and summarize text',
'zh-CN': '搜索和总结文本'
}
},
children: [
{
id: 'search',
name: { en: 'Search', 'zh-CN': '搜索' },
description: { en: 'Search text', 'zh-CN': '搜索文本' },
toolDescription: 'Search text by query',
handler: searchHandler
},
{
id: 'summary',
name: { en: 'Summary', 'zh-CN': '总结' },
description: { en: 'Summarize text', 'zh-CN': '总结文本' },
toolDescription: 'Summarize text content',
handler: summaryHandler
}
],
secretSchema
});
During build, the CLI scans icons in the plugin root and writes them into the built manifest.json.
| Scenario | File name |
|---|---|
| Main plugin icon | logo.svg, logo.png, logo.jpg, logo.jpeg, logo.webp, or logo.gif |
| Tool-suite child icon | <childId>.logo.svg, <childId>.logo.png, and similar names |
Notes:
<childId> of a child icon must exactly match children[].id.icon field in dist/manifest.json.Install dependencies in the plugin directory first:
cd packages/tools/my-tool
pnpm install
View plugin and debuggable tool information:
pnpx @fastgpt-plugin/cli debug .
Run one single-tool debug invocation:
pnpx @fastgpt-plugin/cli debug . --run --input '{"query":"hello"}' --secrets '{"apiKey":"test"}'
Run a child tool in a tool suite:
pnpx @fastgpt-plugin/cli debug . --run --tool search --input '{"query":"hello"}' --secrets '{"apiKey":"test"}'
Use files when input, secrets, or system variables are large:
pnpx @fastgpt-plugin/cli debug . --run --input-file input.json --secrets-file secrets.json --system-var-file system-var.json
Local debug boundaries:
ctx.invoke.uploadFile() uses a local mock implementation and defaults to .fastgpt-plugin-debug/uploads.Remote debugging connects a locally developed plugin to a FastGPT test environment. The FastGPT page authenticates the user and generates a debug link, while the CLI uses that link to create a WSS debug channel. Debug plugins are visible only to the current debugger.
Before using it, confirm that the test environment has deployed the FastGPT Plugin service and Connection Gateway, and that your local machine can reach the Gateway WSS endpoint returned by the test environment.
The debug link is only for connecting your local CLI to the test environment. Do not commit it to code repositories, documentation examples, or chat logs.
Run this command in a plugin directory or a workspace that contains multiple plugin directories:
fastgpt-plugin dev
After startup, paste the debug link copied from FastGPT into the TUI. The CLI exchanges the connection key from the link for a short-lived WSS connect token, then mounts local plugins to the FastGPT debug channel.
Scripts and Agents can use non-interactive mode:
fastgpt-plugin dev --no-interactive \
--connect "https://fastgpt.example.com/api/plugin/debug-channel/connection-key:exchange?connectionKey=fpg_dbg_..."
When passing only a raw connection key, tell the CLI where the exchange endpoint is:
FASTGPT_PLUGIN_DEBUG_CONNECT_URL=https://fastgpt.example.com/api/plugin/debug-channel/connection-key:exchange \
fastgpt-plugin dev --no-interactive --connect "fpg_dbg_..."
After --connect connects successfully, it saves the connection key so later fastgpt-plugin dev runs can reuse the local config. In the TUI, press c to enter and save a new debug link or connection key.
When no plugin directory is passed, dev auto-discovers plugins from the current directory. If the current directory contains index.ts, it is used as the plugin entry; otherwise, the CLI scans one level of child directories for index.ts.
You can also pass one or more plugin directories explicitly:
fastgpt-plugin dev ./plugins/getTime ./plugins/dbops --watch
--watch reloads local plugins and recreates the remote-debug session after local file changes. The CLI reconnects by default when disconnected; use --no-reconnect to disable automatic reconnect.
After the CLI reports that remote debugging is ready, return to the FastGPT test environment:
The debug tool source is bound to the currently signed-in member, so other members do not see that debug plugin by default.
Press Ctrl+C in the local terminal to close the current CLI debug session; press Ctrl+C again to force exit.
The End Debugging action in FastGPT revokes the current member's debug channel and removes the debug plugin entry from the page. If the debug link is exposed, the signed-in member changes, or authorization needs to be renewed, use Refresh Link to generate a new link.
Inside a plugin directory, usually run:
pnpm run test
pnpm run build
pnpx @fastgpt-plugin/cli check --entry . --output ./dist
pnpm run pack
You can also pass directories explicitly:
pnpx @fastgpt-plugin/cli build --entry packages/tools/my-tool --output packages/tools/my-tool/dist --minify
pnpx @fastgpt-plugin/cli check --entry packages/tools/my-tool --output packages/tools/my-tool/dist
pnpx @fastgpt-plugin/cli pack --entry packages/tools/my-tool --dist ./dist --output packages/tools/my-tool/out
Build artifacts should include:
dist/index.jsdist/manifest.jsonREADME.mdassets/**Packaging produces a .pkg file. Uploading, installation, and listing should all use that .pkg file.
Before submitting, confirm:
index.ts default export is correct.manifest.pluginId, manifest.version, Chinese and English names, and descriptions are complete.children[].id values are stable and unique.inputSchema covers all user inputs and includes required type and range constraints.outputSchema matches handler return values.secretSchema covers all secret configuration and sensitive fields set isSecret: true.pnpm run test passes, or the reason it cannot be tested is documented.build, check, and pack pass.dist/manifest.json are as expected..pkg can be installed in a test environment and complete a real invocation.Community plugins usually start by creating and pushing an independent GitHub repository from the plugin directory:
cd packages/tools/my-tool
git init
git add .
git commit -m "feat: add my-tool plugin"
gh repo create --public --source=. --remote=origin --push
Then return to the fastgpt-community-plugins repository, submit the submodule or reference update, and open a PR to labring/fastgpt-community-plugins.
Official plugins require:
.pkg installation in a test environment.Business plugins are released to private repositories. Manage versions, secrets, installation packages, and acceptance records according to the customer delivery process. Security boundaries for external APIs, customer private addresses, and account secrets should be recorded separately.
If you do not need official inclusion, see Upload System Tool to use the plugin in your own FastGPT deployment.
tool and tool-suite?Use tool for a single capability. Use tool-suite for multiple capabilities that share authentication, the same upstream API, and strong business relevance, such as search, detail, and task creation in one plugin.
Use semantic versioning for manifest.version. Upgrade patch for compatible fixes, minor for compatible new features, and major when changing input/output fields, child tool IDs, or user configuration. Evaluate existing workflow compatibility before major changes.
Plugins should declare secrets through secretSchema and read them through ctx.secrets. Real secrets should not appear in code repositories, test snapshots, error logs, or README files.
Yes. Local debug quickly validates plugin logic and schemas. Test environment validation confirms real installation, runtime, host reverse invocation, network, and permission behavior.