A plugin template for GUIChat/MulmoChat with integrated chat demo environment.
This template is designed for junior engineers to learn plugin development with a working chat interface that demonstrates how plugins interact with LLMs.
Note: This template includes a Quiz plugin as a working sample. The Quiz plugin demonstrates how to create interactive plugins with user input. Replace it with your own plugin implementation.
- Chat-Integrated Demo: Test your plugin with a real chat interface
- Mock Mode: Develop without needing an API key
- Real API Mode: Test with OpenAI API for production-like behavior
- Framework-agnostic Core: Plugin logic separated from UI framework
- Vue + React Support: Both frameworks supported out of the box
- TypeScript: Full type safety
- Tailwind CSS 4: Modern styling
⚠️ Important: No Arbitrary ValuesDo NOT use Tailwind's arbitrary values (e.g.,
bg-[#1a1a2e],w-[137px]) in plugin code. MulmoChat uses@sourcedirective to scan plugins, which only supports standard Tailwind classes. Use standard classes likebg-slate-900instead.
# Install dependencies
yarn install
# Start development server
yarn devOpen http://localhost:5173 to see the demo.
- Chat Panel: Send messages and see LLM responses
- Mock Mode: Toggle to test without API key (recognizes "quiz" and "hello" keywords)
- Real API Mode: Enter your OpenAI API key for actual LLM integration
- View Component: See how your plugin renders results
- Preview Component: See the sidebar thumbnail
- Quick Samples: Execute plugin samples directly
cp -r GUIChatPluginTemplate GUIChatPluginMyPlugin
cd GUIChatPluginMyPluginChange the package name:
{
"name": "guichat-plugin-my-plugin",
"description": "Your plugin description"
}Edit the files in src/core/ (shared by Vue/React):
- types.ts - Define your data types
- definition.ts - Define tool name and JSON schema
- samples.ts - Add test data
- plugin.ts - Implement execute function
Edit the files in src/vue/:
- View.vue - Main UI component
- Preview.vue - Sidebar thumbnail
Start dev server: yarn dev
Edit the files in src/react/:
- View.tsx - Main UI component
- Preview.tsx - Sidebar thumbnail
Start dev server: yarn dev:react
Edit demo/shared/chat-utils.ts to add mock responses for your plugin:
export const DEFAULT_MOCK_RESPONSES: Record<string, MockResponse> = {
// Add your plugin's mock response
myKeyword: {
toolCall: {
name: "myToolName",
args: { /* your args */ },
},
},
// ...
};✏️ = Edit this file 🚫 = Don't edit (use as-is)
GUIChatPluginTemplate/
│
├── src/ # 📦 Distributed as npm package
│ │ # Used by apps like MulmoChat
│ ├── index.ts # 🚫 Default export (core)
│ ├── style.css # 🚫 Tailwind CSS entry
│ ├── core/ # Framework-agnostic (no Vue/React dependencies)
│ │ ├── index.ts # 🚫 Core exports
│ │ ├── types.ts # ✏️ Plugin-specific types
│ │ ├── definition.ts # ✏️ Tool definition (schema for LLM)
│ │ ├── samples.ts # ✏️ Sample data for testing
│ │ └── plugin.ts # ✏️ Execute function
│ ├── vue/ # Vue-specific implementation
│ │ ├── index.ts # 🚫 Vue plugin (combines core + components)
│ │ ├── View.vue # ✏️ Main view component
│ │ └── Preview.vue # ✏️ Sidebar preview component
│ └── react/ # React-specific implementation
│ ├── index.ts # 🚫 React plugin (combines core + components)
│ ├── View.tsx # ✏️ Main view component
│ └── Preview.tsx # ✏️ Sidebar preview component
│
└── demo/ # 🔧 For development/testing only (NOT distributed)
│ # Chat demo to test your plugin
│ # 🚫 Generally no edits needed
├── vue/ # Vue demo
├── react/ # React demo
└── shared/
└── chat-utils.ts # ✏️ Edit only to add mock responses
For Beginners:
src/= Your plugin code. After npm publish, other apps import and use thisdemo/= Development environment to test your plugin. Not included in npm package- Just edit the ✏️ files to create your plugin
Normally, the chat communicates with the LLM via OpenAI API. Mock Mode is for development testing only, allowing you to test plugin behavior without an API key.
User Input
↓
useChat.sendMessage()
↓
┌─────────────────────────────────────┐
│ Mock Mode? (for testing) │
│ ├─ Yes → Return mock response │
│ └─ No → Call OpenAI API ← normal │
└─────────────────────────────────────┘
↓
LLM Response (may include tool_calls)
↓
┌─────────────────────────────────────┐
│ Has tool_calls? │
│ ├─ Yes → plugin.execute(args) │
│ │ → Update result │
│ │ → Show in View component │
│ │ → Call API again │
│ │ → Get LLM response │
│ └─ No → Show text response │
└─────────────────────────────────────┘
The execute function returns a ToolResult:
interface ToolResult<T, J> {
toolName: string; // Must match TOOL_NAME (required)
message: string; // Brief status for LLM
jsonData?: J; // Data visible to LLM
data?: T; // Data for UI only (not sent to LLM)
title?: string; // Result title
instructions?: string; // Follow-up instructions for LLM
}// Props received by View.vue
{
selectedResult: ToolResult; // Current result to display
sendTextMessage: (text: string) => void; // Send message back to chat
}
// Event emitted
@updateResult="(updated: ToolResult) => void"In View.vue, always use ref + watch pattern instead of computed:
// ✅ Correct
const data = ref<MyData | null>(null);
watch(
() => props.selectedResult,
(newResult) => {
if (newResult?.jsonData) {
data.value = newResult.jsonData;
}
},
{ immediate: true }
);
// ❌ Wrong - causes reactivity issues
const data = computed(() => props.selectedResult?.jsonData);yarn dev # Start Vue demo
yarn dev:react # Start React demo
yarn build # Build for production
yarn typecheck # Type check
yarn lint # Lint codeAfter developing your plugin:
- Publish to npm or use local path
- Install in MulmoChat:
yarn add guichat-plugin-my-plugin
- Import in MulmoChat's
src/tools/index.ts:import MyPlugin from "guichat-plugin-my-plugin/vue";
MIT