The toolbox supports user-defined tool plugins. Drop a .js file into the plugin directory and it is registered alongside every built-in tool the next time the plugin starts.
Plugin directory: ~/.lm-studio-toolbox/plugins/
Create the plugin directory if it doesn't exist, then drop a .js file in:
mkdir -p ~/.lm-studio-toolbox/plugins
Restart the LM Studio Toolbox plugin (or reload the plugin in LM Studio's plugin manager) for new files to take effect.
A plugin file may export one of three shapes:
Parameters default to an empty schema when omitted — the model can still call the tool; it just receives no typed arguments.
{ z } — recommended)The loader passes Zod's z object to any exported function. Use this to define typed parameters without a separate require("zod") call:
Both Zod raw shapes and wrapped z.object({}) are accepted:
console.warn — other plugins and all built-in tools continue loading normally.name or implementation are skipped individually.implementation are caught by the toolbox and returned to the model as { error: "..." }.| Use case | Pattern |
|---|---|
| Call a company API | Factory with z.string() params and fetch() |
| Run a custom deploy script | Object export, child_process.execFile() |
| Read a proprietary file format | Object export, return parsed data as JSON |
| Chain two operations | Array export |
disabledTools config field applies to plugin tools the same way it does to built-ins.// ~/.lm-studio-toolbox/plugins/greet.js
module.exports = {
name: "greet_user",
description: "Say hello to the user by name.",
implementation: async ({ name }) => {
return { message: `Hello, ${name}!` };
},
};
// ~/.lm-studio-toolbox/plugins/call_api.js
module.exports = function ({ z }) {
return {
name: "call_internal_api",
description: "POST to an internal REST API endpoint.",
parameters: {
endpoint: z.string().describe("Relative path, e.g. /users/123"),
payload: z.string().optional().describe("JSON body string"),
},
implementation: async ({ endpoint, payload }) => {
const res = await fetch(`https://api.internal${endpoint}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: payload,
});
return { status: res.status, body: await res.text() };
},
};
};
// ~/.lm-studio-toolbox/plugins/deploy.js
module.exports = function ({ z }) {
const envSchema = { env: z.enum(["staging", "production"]) };
return [
{
name: "deploy_app",
description: "Deploy the app to an environment.",
parameters: envSchema,
implementation: async ({ env }) => {
// ... your deploy logic
return { deployed: true, env };
},
},
{
name: "rollback_deploy",
description: "Roll back the last deployment.",
parameters: envSchema,
implementation: async ({ env }) => {
// ... rollback logic
return { rolled_back: true, env };
},
},
];
};
// Raw shape (preferred — matches built-in tool convention)
parameters: { name: z.string(), age: z.number().int() }
// Wrapped z.object() — also works; unwrapped automatically
parameters: z.object({ name: z.string(), age: z.number().int() })