build-toolset
Create and customize a personalized toolset by selecting specific tools for your workflow. Define names and include tools by their identifiers to streamline tasks and improve efficiency on the hypertool-mcp server.
Instructions
Build and save a custom toolset by selecting specific tools. Like assembling tools from a workshop - pick the exact tools you need for a specific task or workflow. You must specify which tools to include. Each tool must specify either namespacedName or refId for identification. Example: {name: 'dev-essentials', tools: [{namespacedName: 'git.status'}, {namespacedName: 'docker.ps'}], autoEquip: true} creates and immediately equips a development toolset.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| autoEquip | No | Automatically equip this toolset after creation (default: false) | |
| description | No | Optional description of what this toolset is for (e.g., 'Essential tools for web development') | |
| name | Yes | Name for the new toolset. Use lowercase with hyphens (e.g., 'dev-essentials', 'git-workflow', 'debug-kit') | |
| tools | Yes | Array of tools to include in the toolset. Each tool must specify either namespacedName or refId for identification. Use list-available-tools to see available options. |
Implementation Reference
- The main handler function for the 'build-toolset' MCP tool. It receives input arguments, calls toolsetManager.buildToolset to perform the actual creation, handles auto-equip, and returns the result.handler: async (args: any) => { if (deps.discoveryEngine) { const result = await deps.toolsetManager.buildToolset( args?.name || "", args?.tools || [], { description: args?.description, autoEquip: args?.autoEquip, } ); // Auto-exit to normal mode if autoEquip succeeded if (args?.autoEquip && result.meta?.success && onModeChangeRequest) { onModeChangeRequest(); } return { content: [ { type: "text", text: JSON.stringify(result), }, ], structuredContent: result, }; } else { return { content: [ { type: "text", text: "❌ **Tool discovery not available**\n\nDiscovery engine is not initialized. Server may not be fully started.", }, ], }; }
- Tool definition including input schema for validation and output schema reference. Defines the structure expected for input parameters like name, tools array, description, and autoEquip.export const buildToolsetDefinition: Tool = { name: "build-toolset", description: "Build and save a custom toolset by selecting specific tools. Like assembling tools from a workshop - pick the exact tools you need for a specific task or workflow. You must specify which tools to include. Each tool must specify either namespacedName or refId for identification. Example: {name: 'dev-essentials', tools: [{namespacedName: 'git.status'}, {namespacedName: 'docker.ps'}], autoEquip: true} creates and immediately equips a development toolset.", inputSchema: { type: "object" as const, properties: { name: { type: "string", description: "Name for the new toolset. Use lowercase with hyphens (e.g., 'dev-essentials', 'git-workflow', 'debug-kit')", pattern: "^[a-z0-9-]+$", minLength: 2, maxLength: 50, }, tools: { type: "array", description: "Array of tools to include in the toolset. Each tool must specify either namespacedName or refId for identification. Use list-available-tools to see available options.", minItems: 1, maxItems: 100, items: { type: "object", properties: { namespacedName: { type: "string", description: "Tool reference by namespaced name (e.g., 'git.status', 'docker.ps')", }, refId: { type: "string", description: "Tool reference by unique hash identifier (e.g., 'abc123def456...')", }, }, oneOf: [{ required: ["namespacedName"] }, { required: ["refId"] }], additionalProperties: false, }, }, description: { type: "string", description: "Optional description of what this toolset is for (e.g., 'Essential tools for web development')", maxLength: 200, }, autoEquip: { type: "boolean", description: "Automatically equip this toolset after creation (default: false)", }, }, required: ["name", "tools"], additionalProperties: false, }, outputSchema: buildToolsetResponseSchema as any, };
- src/server/tools/schemas.ts:116-243 (schema)Zod schema and JSON schema for the build-toolset response, defining the output structure including meta (success, error) and toolset info.export const buildToolsetResponseZodSchema = z.object({ meta: z .object({ success: z .boolean() .describe("Whether the toolset was successfully created"), toolsetName: z .string() .optional() .describe("Name of the created toolset"), autoEquipped: z .boolean() .optional() .describe( "Whether the toolset was automatically equipped after creation" ), error: z .string() .optional() .describe("Error message if the operation failed"), }) .describe("Operation metadata"), toolset: toolsetInfoZodSchema .optional() .describe("Toolset information (only present if successful)"), }); /** * Zod schema for equip toolset response */ export const equipToolsetResponseZodSchema = z.object({ success: z .boolean() .describe("Whether the toolset was successfully equipped"), error: z .string() .optional() .describe("Error message if the operation failed"), toolset: toolsetInfoZodSchema .optional() .describe("Equipped toolset information (only present if successful)"), }); /** * Zod schema for get active toolset response */ export const getActiveToolsetResponseZodSchema = z.object({ equipped: z.boolean().describe("Whether a toolset is currently equipped"), toolset: toolsetInfoZodSchema .optional() .describe("Toolset information (only present if equipped)"), serverStatus: z .object({ totalConfigured: z .number() .describe("Total number of configured servers"), enabled: z.number().describe("Number of enabled servers"), available: z.number().describe("Number of available servers"), unavailable: z.number().describe("Number of unavailable servers"), disabled: z.number().describe("Number of disabled servers"), }) .optional() .describe("Server status summary"), toolSummary: z .object({ currentlyExposed: z .number() .describe("Number of tools currently exposed"), totalDiscovered: z.number().describe("Total number of discovered tools"), filteredOut: z.number().describe("Number of tools filtered out"), }) .optional() .describe("Tool summary information"), exposedTools: z .record(z.array(toolInfoResponseZodSchema)) .describe("Tools grouped by server with full details"), unavailableServers: z .array(z.string()) .describe("List of unavailable server names"), warnings: z.array(z.string()).describe("List of warnings"), context: contextInfoZodSchema .optional() .describe("Context usage information for the exposed tools"), }); /** * TypeScript types inferred from Zod schemas */ export type ContextInfo = z.infer<typeof contextInfoZodSchema>; export type ToolInfoResponse = z.infer<typeof toolInfoResponseZodSchema>; export type ListSavedToolsetsResponse = z.infer< typeof listSavedToolsetsResponseZodSchema >; export type BuildToolsetResponse = z.infer< typeof buildToolsetResponseZodSchema >; export type EquipToolsetResponse = z.infer< typeof equipToolsetResponseZodSchema >; export type GetActiveToolsetResponse = z.infer< typeof getActiveToolsetResponseZodSchema >; /** * JSON Schemas generated from Zod schemas using zod-to-json-schema * Note: Using $refStrategy: 'none' to avoid $ref definitions for MCP compatibility */ export const serverConfigSchema = zodToJsonSchema(serverConfigZodSchema, { $refStrategy: "none", }); export const toolsetInfoSchema = zodToJsonSchema(toolsetInfoZodSchema, { $refStrategy: "none", }); export const listSavedToolsetsResponseSchema = zodToJsonSchema( listSavedToolsetsResponseZodSchema, { $refStrategy: "none", } ); export const buildToolsetResponseSchema = zodToJsonSchema( buildToolsetResponseZodSchema, { $refStrategy: "none", } );
- src/server/tools/config-tools/registry.ts:23-34 (registration)Registration of the build-toolset tool factory in the configuration tools factories array.export const CONFIG_TOOL_FACTORIES: ToolModuleFactory[] = [ createListAvailableToolsModule, createBuildToolsetModule, createListSavedToolsetsModule, createEquipToolsetModule, createDeleteToolsetModule, createUnequipToolsetModule, createGetActiveToolsetModule, createAddToolAnnotationModule, createListPersonasModule, // Persona management tool createExitConfigurationModeModule, ];
- Core helper method in ToolsetManager that implements the toolset building logic: validates inputs, resolves tools, saves to user preferences, generates detailed info, and handles auto-equip.async buildToolset( name: string, tools: DynamicToolReference[], options: { description?: string; autoEquip?: boolean; } = {} ): Promise<BuildToolsetResponse> { try { // Validate toolset name format const namePattern = /^[a-z0-9-]+$/; if (!namePattern.test(name)) { return { meta: { success: false, error: "Invalid toolset name format. Use only lowercase letters, numbers, and hyphens (a-z, 0-9, -)", }, }; } if (name.length < 2 || name.length > 50) { return { meta: { success: false, error: "Toolset name must be between 2 and 50 characters", }, }; } if (!tools || tools.length === 0) { return { meta: { success: false, error: "Toolset must include at least one tool", }, }; } // Validate tool references if discovery engine is available if (this.discoveryEngine) { const validationResult = this.validateToolReferences(tools); if (!validationResult.valid) { return { meta: { success: false, error: `Invalid tool references: ${validationResult.invalidReferences.join(", ")}`, }, }; } } // Check if toolset already exists const preferences = await import("../../../config/preferenceStore.js"); const loadToolsetsFromPreferences = preferences.loadStoredToolsets; const saveToolsetsToPreferences = preferences.saveStoredToolsets; const stored = await loadToolsetsFromPreferences(); if (stored[name]) { return { meta: { success: false, error: `Toolset "${name}" already exists. Use a different name or delete the existing toolset first.`, }, }; } // Create toolset configuration const config: ToolsetConfig = { name, description: options.description, version: "1.0.0", createdAt: new Date(), tools, }; // Validate configuration const validation = validateToolsetConfig(config); if (!validation.valid) { return { meta: { success: false, error: `Invalid toolset configuration: ${validation.errors.join(", ")}`, }, }; } // Save toolset stored[name] = config; await saveToolsetsToPreferences(stored); // Generate detailed toolset information const toolsetInfo = await this.generateToolsetInfo(config); const result = { meta: { success: true, toolsetName: name, autoEquipped: false, }, toolset: toolsetInfo, }; // Auto-equip if requested if (options.autoEquip) { const equipResult = await this.equipToolset(name); if (equipResult.success) { result.meta.autoEquipped = true; result.toolset.active = true; } } return result; } catch (error) { return { meta: { success: false, error: `Failed to create toolset: ${error instanceof Error ? error.message : String(error)}`, }, }; } }