feat: Add /tools page with centralized tool registry management

- Add ToolsPage for managing MCP tools activation and persistence
- Centralize all tool handlers in services/tools/handlers/
- toolRegistry.ts is now the single source of truth for tool state
- Add tools store for pinned tools (persisted in localStorage)
- Tools can be pinned to stay active across page navigation
- Remove old individual tool files, replaced by centralized handlers
This commit is contained in:
2026-02-13 13:46:55 -06:00
parent da6111bd1f
commit 4450d1e034
22 changed files with 2386 additions and 1832 deletions

View File

@@ -0,0 +1,170 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export interface ToolDefinition {
name: string
description: string
category: 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project'
schema: object
handler: Function
}
export const useToolsStore = defineStore('tools', () => {
// All available tool definitions
const toolDefinitions = ref<Map<string, ToolDefinition>>(new Map())
// Pinned tools persist across page changes (use array for reactivity)
const pinnedToolsArray = ref<string[]>([])
// Currently active tools (use array for reactivity)
const activeToolsArray = ref<string[]>([])
// Computed
const allTools = computed(() => Array.from(toolDefinitions.value.values()))
const activeToolsDefs = computed(() =>
allTools.value.filter(t => activeToolsArray.value.includes(t.name))
)
const inactiveToolsDefs = computed(() =>
allTools.value.filter(t => !activeToolsArray.value.includes(t.name))
)
const pinnedToolsDefs = computed(() =>
allTools.value.filter(t => pinnedToolsArray.value.includes(t.name))
)
const toolsByCategory = computed(() => {
const categories: Record<string, { active: ToolDefinition[], inactive: ToolDefinition[] }> = {}
for (const tool of allTools.value) {
if (!categories[tool.category]) {
categories[tool.category] = { active: [], inactive: [] }
}
if (activeToolsArray.value.includes(tool.name)) {
categories[tool.category].active.push(tool)
} else {
categories[tool.category].inactive.push(tool)
}
}
return categories
})
// Actions
function registerToolDefinition(tool: ToolDefinition) {
toolDefinitions.value.set(tool.name, tool)
}
function registerToolDefinitions(tools: ToolDefinition[]) {
for (const tool of tools) {
toolDefinitions.value.set(tool.name, tool)
}
}
function setToolActive(name: string, active: boolean) {
const index = activeToolsArray.value.indexOf(name)
if (active && index === -1) {
activeToolsArray.value.push(name)
} else if (!active && index !== -1) {
activeToolsArray.value.splice(index, 1)
}
}
function pinTool(name: string) {
if (!pinnedToolsArray.value.includes(name)) {
pinnedToolsArray.value.push(name)
savePinnedTools()
}
}
function unpinTool(name: string) {
const index = pinnedToolsArray.value.indexOf(name)
if (index !== -1) {
pinnedToolsArray.value.splice(index, 1)
savePinnedTools()
}
}
function togglePin(name: string) {
if (pinnedToolsArray.value.includes(name)) {
unpinTool(name)
} else {
pinTool(name)
}
}
function isToolPinned(name: string): boolean {
return pinnedToolsArray.value.includes(name)
}
function isToolActive(name: string): boolean {
return activeToolsArray.value.includes(name)
}
function getPinnedToolNames(): string[] {
return [...pinnedToolsArray.value]
}
function getToolDefinition(name: string): ToolDefinition | undefined {
return toolDefinitions.value.get(name)
}
// Persistence
function savePinnedTools() {
localStorage.setItem('pinnedTools', JSON.stringify(pinnedToolsArray.value))
}
function loadPinnedTools() {
try {
const saved = localStorage.getItem('pinnedTools')
if (saved) {
const parsed = JSON.parse(saved)
pinnedToolsArray.value = parsed
}
} catch (e) {
console.error('[ToolsStore] Failed to load pinned tools:', e)
}
}
function clearActiveTools() {
activeToolsArray.value = []
}
function setActiveTools(names: string[]) {
activeToolsArray.value = [...names]
}
// Initialize
loadPinnedTools()
return {
// State (reactive arrays)
activeTools: activeToolsArray,
pinnedTools: pinnedToolsArray,
toolDefinitions,
// Computed
allTools,
activeToolsDefs,
inactiveToolsDefs,
pinnedToolsDefs,
toolsByCategory,
// Actions
registerToolDefinition,
registerToolDefinitions,
setToolActive,
pinTool,
unpinTool,
togglePin,
isToolPinned,
isToolActive,
getPinnedToolNames,
getToolDefinition,
clearActiveTools,
setActiveTools,
loadPinnedTools
}
})