| layout | default |
|---|---|
| title | Chapter 6: Plugin Development |
| nav_order | 6 |
| has_children | false |
| parent | LobeChat AI Platform |
Welcome to Chapter 6: Plugin Development. In this part of LobeChat AI Platform: Deep Dive Tutorial, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.
Building custom plugins to extend LobeChat's capabilities with Function Calling
By the end of this chapter, you'll understand:
- LobeChat's plugin architecture and SDK
- Building plugins with Function Calling
- Plugin manifest structure and API design
- Testing and publishing custom plugins
- Integration with external services
LobeChat uses a Function Calling-based plugin system. Plugins expose API endpoints that the LLM can invoke during conversation, enabling real-time data access and external service integration.
graph TB
subgraph Chat["Chat Flow"]
USER[User Message]
LLM[LLM with FC]
RESP[Response]
end
subgraph Plugin["Plugin System"]
MANIFEST[Plugin Manifest]
API[Plugin API]
RENDER[UI Renderer]
end
USER --> LLM
LLM -->|Function Call| API
API -->|Result| LLM
LLM --> RESP
MANIFEST --> LLM
API --> RENDER
Every plugin defines a manifest describing its capabilities:
{
"identifier": "weather-plugin",
"name": "Weather",
"description": "Get current weather and forecasts for any location",
"author": "your-name",
"version": "1.0.0",
"homepage": "https://github.com/your-name/weather-plugin",
"openapi": "https://your-plugin.vercel.app/openapi.json",
"api": [
{
"name": "getWeather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or coordinates"
},
"units": {
"type": "string",
"enum": ["metric", "imperial"],
"default": "metric"
}
},
"required": ["location"]
}
},
{
"name": "getForecast",
"description": "Get weather forecast for the next 5 days",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or coordinates"
},
"days": {
"type": "number",
"default": 5,
"maximum": 10
}
},
"required": ["location"]
}
}
],
"ui": {
"url": "https://your-plugin.vercel.app",
"height": 400
},
"settings": {
"type": "object",
"properties": {
"apiKey": {
"type": "string",
"title": "API Key",
"description": "Your weather API key"
}
}
}
}# Use the LobeChat plugin template
npx create-lobe-plugin my-plugin
cd my-plugin
# Or scaffold manually
mkdir my-plugin && cd my-plugin
npm init -y
npm install @lobehub/chat-plugin-sdkmy-plugin/
├── src/
│ ├── api/ # API route handlers
│ │ ├── getWeather.ts
│ │ └── getForecast.ts
│ ├── components/ # UI components (optional)
│ │ └── WeatherCard.tsx
│ └── index.ts # Plugin entry
├── public/
│ ├── manifest.json # Plugin manifest
│ └── openapi.json # OpenAPI specification
├── package.json
└── vercel.json # Deployment config
// src/api/getWeather.ts
import { PluginServerConfig } from "@lobehub/chat-plugin-sdk";
export const config: PluginServerConfig = {
runtime: "edge",
};
interface WeatherParams {
location: string;
units?: "metric" | "imperial";
}
export default async function handler(req: Request) {
const params: WeatherParams = await req.json();
// Fetch weather data from external API
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?` +
`q=${encodeURIComponent(params.location)}` +
`&units=${params.units || "metric"}` +
`&appid=${process.env.WEATHER_API_KEY}`
);
const data = await response.json();
// Return structured data for the LLM
return Response.json({
location: data.name,
country: data.sys?.country,
temperature: data.main?.temp,
feels_like: data.main?.feels_like,
humidity: data.main?.humidity,
description: data.weather?.[0]?.description,
wind_speed: data.wind?.speed,
icon: data.weather?.[0]?.icon,
});
}Plugins can render custom UI in the chat:
// src/components/WeatherCard.tsx
import { memo } from "react";
interface WeatherData {
location: string;
temperature: number;
description: string;
humidity: number;
icon: string;
}
const WeatherCard = memo<{ data: WeatherData }>(({ data }) => {
return (
<div style={{
padding: "16px",
borderRadius: "12px",
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
color: "white",
fontFamily: "system-ui",
}}>
<div style={{ fontSize: "18px", fontWeight: 600 }}>
📍 {data.location}
</div>
<div style={{ fontSize: "48px", fontWeight: 700, margin: "8px 0" }}>
{Math.round(data.temperature)}°
</div>
<div style={{ fontSize: "14px", opacity: 0.9 }}>
{data.description} • Humidity: {data.humidity}%
</div>
</div>
);
});
export default WeatherCard;LobeChat uses the LLM's Function Calling capability to invoke plugins:
// How LobeChat processes plugin calls internally
class PluginExecutor {
async execute(
functionCall: FunctionCall,
pluginManifest: PluginManifest
): Promise<FunctionResult> {
const api = pluginManifest.api.find(
a => a.name === functionCall.name
);
if (!api) {
throw new Error(`API not found: ${functionCall.name}`);
}
// Call the plugin's API endpoint
const response = await fetch(pluginManifest.openapi, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
apiName: functionCall.name,
arguments: JSON.parse(functionCall.arguments),
}),
});
const result = await response.json();
return {
name: functionCall.name,
content: JSON.stringify(result),
};
}
}Plugins expose an OpenAPI schema for discovery:
{
"openapi": "3.0.0",
"info": {
"title": "Weather Plugin",
"version": "1.0.0"
},
"servers": [
{ "url": "https://your-plugin.vercel.app" }
],
"paths": {
"/api/getWeather": {
"post": {
"operationId": "getWeather",
"summary": "Get current weather for a location",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": { "type": "string" },
"units": { "type": "string" }
},
"required": ["location"]
}
}
}
},
"responses": {
"200": {
"description": "Weather data",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": { "type": "string" },
"temperature": { "type": "number" },
"description": { "type": "string" }
}
}
}
}
}
}
}
}
}
}The @lobehub/chat-plugin-sdk provides utilities:
import {
createHeadersWithPluginSettings,
getPluginSettingsFromRequest,
PluginErrorType,
createErrorResponse,
} from "@lobehub/chat-plugin-sdk";
// Access plugin settings (API keys, etc.)
export default async function handler(req: Request) {
const settings = getPluginSettingsFromRequest(req);
const apiKey = settings?.apiKey;
if (!apiKey) {
return createErrorResponse(
PluginErrorType.PluginSettingsInvalid,
"API key is required"
);
}
// Use the API key for external service calls
const result = await fetchExternalService(apiKey);
return Response.json(result);
}// __tests__/getWeather.test.ts
import handler from "../src/api/getWeather";
describe("getWeather", () => {
it("returns weather data for a valid location", async () => {
const req = new Request("http://localhost/api/getWeather", {
method: "POST",
body: JSON.stringify({ location: "New York" }),
});
const response = await handler(req);
const data = await response.json();
expect(data).toHaveProperty("location");
expect(data).toHaveProperty("temperature");
expect(typeof data.temperature).toBe("number");
});
});// vercel.json
{
"rewrites": [
{ "source": "/api/:path*", "destination": "/api/:path*" }
],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET,POST" }
]
}
]
}# Deploy to Vercel
vercel --prodFROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]Submit your plugin to the LobeChat Plugin Index:
# Fork https://github.com/lobehub/lobe-chat-plugins
# Add your plugin manifest to plugins/your-plugin/manifest.json
# Submit a pull request| Concept | Key Takeaway |
|---|---|
| Plugin Manifest | JSON definition of capabilities, API endpoints, and UI |
| Function Calling | LLM invokes plugin APIs during conversation via FC |
| API Routes | Edge-compatible serverless functions for plugin logic |
| Custom UI | React components rendered inline in chat messages |
| OpenAPI | Standard schema for plugin discovery and validation |
| SDK | @lobehub/chat-plugin-sdk for settings, errors, headers |
| Deployment | Vercel or Docker; plugins are standalone services |
Next Steps: Chapter 7: Advanced Customization — Explore LobeChat's theme engine, i18n system, monorepo architecture, and component customization.
Built with insights from the LobeChat repository and community documentation.
Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for plugin, location, json so behavior stays predictable as complexity grows.
In practical terms, this chapter helps you avoid three common failures:
- coupling core logic too tightly to one implementation path
- missing the handoff boundaries between setup, execution, and validation
- shipping changes without clear rollback or observability strategy
After working through this chapter, you should be able to reason about Chapter 6: Plugin Development as an operating subsystem inside LobeChat AI Platform: Deep Dive Tutorial, with explicit contracts for inputs, state transitions, and outputs.
Use the implementation notes around name, weather, description as your checklist when adapting these patterns to your own repository.
Under the hood, Chapter 6: Plugin Development usually follows a repeatable control path:
- Context bootstrap: initialize runtime config and prerequisites for
plugin. - Input normalization: shape incoming data so
locationreceives stable contracts. - Core execution: run the main logic branch and propagate intermediate state through
json. - Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
- Output composition: return canonical result payloads for downstream consumers.
- Operational telemetry: emit logs/metrics needed for debugging and performance tuning.
When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.
Use the following upstream sources to verify implementation details while reading this chapter:
- LobeChat
Why it matters: authoritative reference on
LobeChat(github.com).
Suggested trace strategy:
- search upstream code for
pluginandlocationto map concrete implementation paths - compare docs claims against actual runtime/config code before reusing patterns in production