Skip to main content
This guide walks you through installing Toolshed, writing your first plugin, and starting an MCP server. By the end you’ll have a working server exposing a tool that any MCP-compatible client — such as Claude Desktop, Cursor, or your own agent — can discover and invoke.
1

Install the CLI and SDK

Install the Toolshed CLI globally so the toolshed command is available in your terminal. Then add the SDK to your project as a dependency.
npm install -g @toolshed/cli
npm install @toolshed/sdk
Verify the CLI installed correctly:
toolshed --version
2

Log in to your Toolshed server

Authenticate with your Toolshed server. The CLI opens your browser to complete the OAuth flow and stores the session token in ~/.toolshed/config.json.
toolshed login
By default, toolshed login connects to http://localhost:3000. Set the TOOLSHED_SERVER environment variable to point to a remote server before running this command.
3

Define your first plugin

Create a new TypeScript file for your plugin. The example below defines a plugin with a single tool that returns the current UTC time. Use defineTool() to declare the tool’s schema and handler, then pass it to definePlugin().
plugin.ts
import { definePlugin, defineTool } from "@toolshed/sdk"
import { z } from "zod"

const getCurrentTime = defineTool({
  path: "utils.get_current_time",
  name: "get_current_time",
  description:
    "Returns the current UTC date and time as an ISO 8601 string. " +
    "Use this when you need the current timestamp without any external dependencies.",
  inputSchema: z.object({}),
  outputSchema: z.object({
    utc: z.string().describe("Current UTC time in ISO 8601 format"),
  }),
  handler: async (_ctx, _input) => {
    return { utc: new Date().toISOString() }
  },
})

export const plugin = definePlugin({
  id: "utils",
  name: "Utilities",
  description: "General-purpose utility tools",
  authProviders: [],
  tools: [getCurrentTime],
})
A few things to note about this structure:
  • path uses dot-separated namespacing and snake_case for the tool name — utils.get_current_time means the tool lives in the utils namespace.
  • inputSchema and outputSchema are Zod schemas. Toolshed converts them to JSON Schema automatically.
  • handler receives a PluginContext as the first argument (used for auth and elicitation) and the validated input as the second.
Follow the verb_noun pattern for tool names: get_current_time, create_issue, list_users. Prefix with the service name to avoid collisions across plugins.
4

Start the MCP server

Run toolshed serve to start the MCP server on stdio. This makes your tool catalog available to any MCP-compatible client.
toolshed serve
You should see:
[toolshed] MCP server running on stdio
The server runs until you press Ctrl+C. It handles cleanup automatically on SIGINT.
toolshed serve uses the local runtime by default. To run executions inside Vercel Firecracker microVMs, set TOOLSHED_RUNTIME=vercel and configure the required Vercel environment variables before starting the server.
To connect Claude Desktop or another MCP client, add an entry to its MCP server configuration pointing to toolshed serve as a stdio command. Refer to your client’s documentation for the exact config format.

Next steps

Now that you have a working MCP server, explore the rest of the Toolshed platform:

Concepts: Plugins

Deep dive into definePlugin() and defineTool() — schemas, auth providers, destructive flags, and more.

Concepts: Sources

Auto-generate tools from an OpenAPI spec or GraphQL endpoint without writing handlers.

Concepts: Policy

Define roles with tool path patterns to control agent access across your entire catalog.

Elicitation guide

Learn how to require human approval before your tools perform destructive operations.