feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
46
dexto/packages/registry/CHANGELOG.md
Normal file
46
dexto/packages/registry/CHANGELOG.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# @dexto/registry
|
||||
|
||||
## 1.5.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 042f4f0: ### CLI Improvements
|
||||
- Add `/export` command to export conversations as Markdown or JSON
|
||||
- Add `Ctrl+T` toggle for task list visibility during processing
|
||||
- Improve task list UI with collapsible view near the processing message
|
||||
- Fix race condition causing duplicate rendering (mainly visible with explore tool)
|
||||
- Don't truncate `pattern` and `question` args in tool output display
|
||||
|
||||
### Bug Fixes
|
||||
- Fix build script to preserve `.dexto` storage (conversations, logs) during clean builds
|
||||
- Fix `@dexto/tools-todo` versioning - add to fixed version group in changeset config
|
||||
|
||||
### Configuration Changes
|
||||
- Remove approval timeout defaults - now waits indefinitely (better UX for CLI)
|
||||
- Add package versioning guidelines to AGENTS.md
|
||||
|
||||
## 1.5.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 6df3ca9: Updated readme. Removed stale filesystem and process tool from dexto/core.
|
||||
|
||||
## 1.5.4
|
||||
|
||||
## 1.5.3
|
||||
|
||||
## 1.5.2
|
||||
|
||||
## 1.5.1
|
||||
|
||||
## 1.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- e7722e5: Minor version bump for new release with bundler, custom tool pkgs, etc.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- f73a519: Revamp CLI. Breaking change to DextoAgent.generate() and stream() apis and hono message APIs, so new minor version. Other fixes for logs, web UI related to message streaming/generating
|
||||
34
dexto/packages/registry/package.json
Normal file
34
dexto/packages/registry/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@dexto/registry",
|
||||
"version": "1.5.6",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup && tsc -p tsconfig.json --emitDeclarationOnly",
|
||||
"dev": "tsup --watch",
|
||||
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
||||
"lint": "eslint . --ext .ts"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"devDependencies": {
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
8
dexto/packages/registry/src/index.ts
Normal file
8
dexto/packages/registry/src/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @dexto/registry
|
||||
*
|
||||
* Shared registry data for Dexto CLI and WebUI.
|
||||
* Contains MCP server presets and future registry types.
|
||||
*/
|
||||
|
||||
export * from './mcp/index.js';
|
||||
6
dexto/packages/registry/src/mcp/index.ts
Normal file
6
dexto/packages/registry/src/mcp/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* MCP Server Registry exports
|
||||
*/
|
||||
|
||||
export * from './types.js';
|
||||
export { ServerRegistryService, getServerRegistry, serverRegistry } from './server-registry.js';
|
||||
558
dexto/packages/registry/src/mcp/server-registry-data.json
Normal file
558
dexto/packages/registry/src/mcp/server-registry-data.json
Normal file
@@ -0,0 +1,558 @@
|
||||
[
|
||||
{
|
||||
"id": "filesystem",
|
||||
"name": "Filesystem",
|
||||
"description": "Secure file operations with configurable access controls for reading and writing files",
|
||||
"category": "productivity",
|
||||
"icon": "📁",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["file", "directory", "filesystem", "io"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"author": "Anthropic",
|
||||
"homepage": "https://github.com/modelcontextprotocol/servers",
|
||||
"matchIds": ["filesystem"]
|
||||
},
|
||||
{
|
||||
"id": "meme-mcp",
|
||||
"name": "Meme Generator",
|
||||
"description": "Create memes using Imgflip templates",
|
||||
"category": "creative",
|
||||
"icon": "🖼️",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "meme-mcp"],
|
||||
"env": {
|
||||
"IMGFLIP_USERNAME": "",
|
||||
"IMGFLIP_PASSWORD": ""
|
||||
},
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["meme", "image", "creative"],
|
||||
"isOfficial": false,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Community",
|
||||
"homepage": "https://www.npmjs.com/package/meme-mcp",
|
||||
"matchIds": ["meme-mcp"]
|
||||
},
|
||||
{
|
||||
"id": "product-name-scout",
|
||||
"name": "Product Name Scout",
|
||||
"description": "SERP analysis, autocomplete, dev collisions, and scoring for product names",
|
||||
"category": "research",
|
||||
"icon": "🔎",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@truffle-ai/product-name-scout-mcp"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["research", "naming", "brand"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["product-name-scout"]
|
||||
},
|
||||
{
|
||||
"id": "duckduckgo",
|
||||
"name": "DuckDuckGo Search",
|
||||
"description": "Search the web using DuckDuckGo",
|
||||
"category": "research",
|
||||
"icon": "🦆",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["duckduckgo-mcp-server"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["search", "web", "research"],
|
||||
"isOfficial": false,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "python": ">=3.10", "dependencies": ["uv"] },
|
||||
"author": "Community",
|
||||
"homepage": "https://github.com/duckduckgo/mcp-server",
|
||||
"matchIds": ["duckduckgo", "ddg"]
|
||||
},
|
||||
{
|
||||
"id": "domain-checker",
|
||||
"name": "Domain Checker",
|
||||
"description": "Check domain availability across TLDs",
|
||||
"category": "research",
|
||||
"icon": "🌐",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["truffle-ai-domain-checker-mcp"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["domains", "availability", "research"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "python": ">=3.10" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["domain-checker"]
|
||||
},
|
||||
{
|
||||
"id": "linear",
|
||||
"name": "Linear",
|
||||
"description": "Manage Linear issues, projects, and workflows",
|
||||
"category": "productivity",
|
||||
"icon": "📋",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "mcp-remote", "https://mcp.linear.app/sse"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["linear", "tasks", "projects"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Linear",
|
||||
"homepage": "https://mcp.linear.app",
|
||||
"matchIds": ["linear"]
|
||||
},
|
||||
{
|
||||
"id": "image-editor",
|
||||
"name": "Image Editor",
|
||||
"description": "Comprehensive image processing and manipulation tools",
|
||||
"category": "creative",
|
||||
"icon": "🖌️",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["truffle-ai-image-editor-mcp"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["image", "edit", "opencv", "pillow"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "python": ">=3.10" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["image_editor", "image-editor"]
|
||||
},
|
||||
{
|
||||
"id": "music-creator",
|
||||
"name": "Music Creator",
|
||||
"description": "Create, analyze, and transform music and audio",
|
||||
"category": "creative",
|
||||
"icon": "🎵",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["truffle-ai-music-creator-mcp"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["audio", "music", "effects"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "python": ">=3.10" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["music_creator", "music-creator"]
|
||||
},
|
||||
{
|
||||
"id": "elevenlabs",
|
||||
"name": "ElevenLabs",
|
||||
"description": "Text-to-speech and voice synthesis using ElevenLabs API",
|
||||
"category": "creative",
|
||||
"icon": "🎤",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["elevenlabs-mcp"],
|
||||
"env": {
|
||||
"ELEVENLABS_API_KEY": ""
|
||||
},
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["tts", "voice", "audio", "synthesis"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "python": ">=3.10" },
|
||||
"author": "ElevenLabs",
|
||||
"homepage": "https://github.com/elevenlabs/elevenlabs-mcp",
|
||||
"matchIds": ["elevenlabs"]
|
||||
},
|
||||
{
|
||||
"id": "hf",
|
||||
"name": "Hugging Face",
|
||||
"description": "Access Hugging Face models and datasets",
|
||||
"category": "development",
|
||||
"icon": "🤗",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@llmindset/mcp-hfspace"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["huggingface", "models", "ai", "ml"],
|
||||
"isOfficial": false,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "LLMindset",
|
||||
"homepage": "https://github.com/llmindset/mcp-hfspace",
|
||||
"matchIds": ["hf", "huggingface"]
|
||||
},
|
||||
{
|
||||
"id": "tavily",
|
||||
"name": "Tavily Search",
|
||||
"description": "Web search and research using Tavily AI search engine",
|
||||
"category": "research",
|
||||
"icon": "🔍",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "tavily-mcp@0.1.3"],
|
||||
"env": {
|
||||
"TAVILY_API_KEY": ""
|
||||
},
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["search", "web", "research", "ai"],
|
||||
"isOfficial": false,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Tavily AI",
|
||||
"homepage": "https://www.npmjs.com/package/tavily-mcp",
|
||||
"matchIds": ["tavily"]
|
||||
},
|
||||
{
|
||||
"id": "puppeteer",
|
||||
"name": "Puppeteer",
|
||||
"description": "Browser automation and web interaction tools",
|
||||
"category": "productivity",
|
||||
"icon": "🌐",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@truffle-ai/puppeteer-server"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["browser", "automation", "web", "puppeteer"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["puppeteer"]
|
||||
},
|
||||
{
|
||||
"id": "gemini-tts",
|
||||
"name": "Gemini TTS",
|
||||
"description": "Google Gemini Text-to-Speech with 30 prebuilt voices and multi-speaker conversation support",
|
||||
"category": "creative",
|
||||
"icon": "🎙️",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@truffle-ai/gemini-tts-server"],
|
||||
"env": {
|
||||
"GEMINI_API_KEY": ""
|
||||
},
|
||||
"timeout": 60000
|
||||
},
|
||||
"tags": ["tts", "speech", "voice", "audio", "gemini", "multi-speaker"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["gemini-tts", "gemini_tts"]
|
||||
},
|
||||
{
|
||||
"id": "nano-banana",
|
||||
"name": "Nano Banana",
|
||||
"description": "Google Gemini 2.5 Flash Image for advanced image generation, editing, and manipulation",
|
||||
"category": "creative",
|
||||
"icon": "🍌",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@truffle-ai/nano-banana-server@0.1.2"],
|
||||
"env": {
|
||||
"GEMINI_API_KEY": ""
|
||||
},
|
||||
"timeout": 60000
|
||||
},
|
||||
"tags": ["image", "generation", "editing", "ai", "gemini", "nano-banana"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["nano-banana", "nano_banana"]
|
||||
},
|
||||
{
|
||||
"id": "openai-image",
|
||||
"name": "OpenAI Image",
|
||||
"description": "OpenAI's image generation and editing API with GPT Image models and DALL-E support",
|
||||
"category": "creative",
|
||||
"icon": "🎨",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@truffle-ai/openai-image-server"],
|
||||
"env": {
|
||||
"OPENAI_API_KEY": ""
|
||||
},
|
||||
"timeout": 60000
|
||||
},
|
||||
"tags": ["image", "generation", "editing", "ai", "openai", "dall-e", "gpt-image"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers/tree/main/src/openai-image",
|
||||
"matchIds": ["openai-image", "openai_image"]
|
||||
},
|
||||
{
|
||||
"id": "heygen",
|
||||
"name": "HeyGen",
|
||||
"description": "Generate realistic human-like audio using HeyGen",
|
||||
"category": "creative",
|
||||
"icon": "🎤",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["heygen-mcp"],
|
||||
"env": {
|
||||
"HEYGEN_API_KEY": ""
|
||||
},
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["audio", "voice", "synthesis", "heygen"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "python": ">=3.10" },
|
||||
"author": "HeyGen",
|
||||
"homepage": "https://github.com/heygen-com/heygen-mcp",
|
||||
"matchIds": ["heygen"]
|
||||
},
|
||||
{
|
||||
"id": "runway",
|
||||
"name": "Runway",
|
||||
"description": "AI-powered creative suite for video and image generation",
|
||||
"category": "creative",
|
||||
"icon": "🎬",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"mcp-remote",
|
||||
"https://mcp.runway.team",
|
||||
"--header",
|
||||
"Authorization: Bearer ${RUNWAY_API_KEY}"
|
||||
],
|
||||
"env": {
|
||||
"RUNWAY_API_KEY": ""
|
||||
},
|
||||
"timeout": 60000
|
||||
},
|
||||
"tags": ["runway", "video", "generation", "ai", "creative"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Runway",
|
||||
"homepage": "https://docs.runway.team/api/runway-mcp-server",
|
||||
"matchIds": ["runway"]
|
||||
},
|
||||
{
|
||||
"id": "perplexity",
|
||||
"name": "Perplexity",
|
||||
"description": "AI-powered search engine for real-time web search and research",
|
||||
"category": "research",
|
||||
"icon": "🔍",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@perplexity-ai/mcp-server"],
|
||||
"env": {
|
||||
"PERPLEXITY_API_KEY": "",
|
||||
"PERPLEXITY_TIMEOUT_MS": "600000"
|
||||
},
|
||||
"timeout": 600000
|
||||
},
|
||||
"tags": ["search", "web", "research", "ai"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Perplexity AI",
|
||||
"homepage": "https://github.com/perplexityai/modelcontextprotocol/tree/main",
|
||||
"matchIds": ["perplexity"]
|
||||
},
|
||||
{
|
||||
"id": "sora",
|
||||
"name": "Sora",
|
||||
"description": "AI-powered video generation using OpenAI's Sora technology",
|
||||
"category": "creative",
|
||||
"icon": "🎬",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@truffle-ai/sora-video-server"],
|
||||
"env": {
|
||||
"OPENAI_API_KEY": ""
|
||||
},
|
||||
"timeout": 60000
|
||||
},
|
||||
"tags": ["video", "generation", "ai", "creative"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Truffle AI",
|
||||
"homepage": "https://github.com/truffle-ai/mcp-servers",
|
||||
"matchIds": ["sora", "sora_video"]
|
||||
},
|
||||
{
|
||||
"id": "chartjs",
|
||||
"name": "ChartJS",
|
||||
"description": "Charting and visualization tool using ChartJS",
|
||||
"category": "data",
|
||||
"icon": "📊",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@ax-crew/chartjs-mcp-server"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["chart", "visualization", "data", "chartjs"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "ax-crew",
|
||||
"homepage": "https://github.com/ax-crew/chartjs-mcp-server",
|
||||
"matchIds": ["chartjs"]
|
||||
},
|
||||
{
|
||||
"id": "rag-lite-ts",
|
||||
"name": "Rag-lite TS",
|
||||
"description": "A local-first TypeScript retrieval engine for semantic search over static documents.",
|
||||
"category": "data",
|
||||
"icon": "🔍",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "raglite-mcp"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["rag", "data", "ai"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "FrugalX",
|
||||
"homepage": "https://github.com/raglite/rag-lite-ts",
|
||||
"matchIds": ["rag-lite-ts"]
|
||||
},
|
||||
{
|
||||
"id": "exa",
|
||||
"name": "Exa",
|
||||
"description": "AI-powered web search and research API with semantic search capabilities.",
|
||||
"category": "data",
|
||||
"icon": "🔍",
|
||||
"config": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.exa.ai/mcp",
|
||||
"headers": {}
|
||||
},
|
||||
"tags": ["rag", "data", "ai"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Exa",
|
||||
"homepage": "https://docs.exa.ai/reference/exa-mcp",
|
||||
"matchIds": ["exa"]
|
||||
},
|
||||
{
|
||||
"id": "firecrawl",
|
||||
"name": "Firecrawl",
|
||||
"description": "AI-powered web search and research API that performs semantic search across web sources, aggregates and ranks results for relevance, and returns contextual summaries with citations",
|
||||
"category": "data",
|
||||
"icon": "🔍",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "firecrawl-mcp"],
|
||||
"env": {
|
||||
"FIRECRAWL_API_KEY": ""
|
||||
},
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["search", "web", "research", "ai"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Firecrawl",
|
||||
"homepage": "https://docs.firecrawl.dev/mcp-server",
|
||||
"matchIds": ["firecrawl"]
|
||||
},
|
||||
{
|
||||
"id": "shadcn",
|
||||
"name": "Shadcn",
|
||||
"description": "Shadcn UI components for MCP servers",
|
||||
"category": "development",
|
||||
"icon": "🖌️",
|
||||
"config": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["shadcn@latest", "mcp"],
|
||||
"timeout": 30000
|
||||
},
|
||||
"tags": ["shadcn", "ui", "components", "mcp"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Shadcn",
|
||||
"homepage": "https://ui.shadcn.com/docs/mcp#configuration",
|
||||
"matchIds": ["shadcn"]
|
||||
},
|
||||
{
|
||||
"id": "kite-trade",
|
||||
"name": "Kite",
|
||||
"description": "Zerodha Kite API MCP server",
|
||||
"category": "data",
|
||||
"icon": "💰",
|
||||
"config": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.kite.trade/mcp",
|
||||
"headers": {}
|
||||
},
|
||||
"tags": ["kite", "trade", "data", "ai"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "Zerodha",
|
||||
"homepage": "https://github.com/zerodha/kite-mcp-server",
|
||||
"matchIds": ["kite-trade"]
|
||||
},
|
||||
{
|
||||
"id": "coingecko",
|
||||
"name": "CoinGecko",
|
||||
"description": "CoinGecko API MCP server",
|
||||
"category": "data",
|
||||
"icon": "💰",
|
||||
"config": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.api.coingecko.com/mcp",
|
||||
"headers": {}
|
||||
},
|
||||
"tags": ["coingecko", "data", "ai"],
|
||||
"isOfficial": true,
|
||||
"isInstalled": false,
|
||||
"requirements": { "platform": "all", "node": ">=18.0.0" },
|
||||
"author": "CoinGecko",
|
||||
"homepage": "https://docs.coingecko.com/docs/mcp-server",
|
||||
"matchIds": ["coingecko"]
|
||||
}
|
||||
]
|
||||
181
dexto/packages/registry/src/mcp/server-registry.ts
Normal file
181
dexto/packages/registry/src/mcp/server-registry.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* MCP Server Registry Service
|
||||
*
|
||||
* Provides access to the built-in registry of MCP servers.
|
||||
* This is a shared service used by both CLI and WebUI.
|
||||
*/
|
||||
|
||||
import type { ServerRegistryEntry, ServerRegistryFilter } from './types.js';
|
||||
import builtinRegistryData from './server-registry-data.json' with { type: 'json' };
|
||||
|
||||
/**
|
||||
* Normalize an ID for comparison
|
||||
*/
|
||||
function normalizeId(s: string): string {
|
||||
return s
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* MCP Server Registry Service
|
||||
*
|
||||
* Manages a registry of available MCP servers that can be quickly added to agents.
|
||||
* The built-in registry data is loaded from an external JSON file.
|
||||
*/
|
||||
export class ServerRegistryService {
|
||||
private static instance: ServerRegistryService;
|
||||
private registryEntries: ServerRegistryEntry[] = [];
|
||||
private isInitialized = false;
|
||||
|
||||
private constructor() {
|
||||
// Private constructor for singleton
|
||||
}
|
||||
|
||||
static getInstance(): ServerRegistryService {
|
||||
if (!ServerRegistryService.instance) {
|
||||
ServerRegistryService.instance = new ServerRegistryService();
|
||||
}
|
||||
return ServerRegistryService.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the registry with default entries
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
if (this.isInitialized) return;
|
||||
|
||||
// Load built-in registry entries from JSON file
|
||||
this.registryEntries = builtinRegistryData as ServerRegistryEntry[];
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registry entries with optional filtering
|
||||
*/
|
||||
async getEntries(filter?: ServerRegistryFilter): Promise<ServerRegistryEntry[]> {
|
||||
await this.initialize();
|
||||
|
||||
let filtered = [...this.registryEntries];
|
||||
|
||||
if (filter?.category) {
|
||||
filtered = filtered.filter((entry) => entry.category === filter.category);
|
||||
}
|
||||
|
||||
if (filter?.tags?.length) {
|
||||
filtered = filtered.filter((entry) =>
|
||||
filter.tags!.some((tag) => entry.tags.includes(tag))
|
||||
);
|
||||
}
|
||||
|
||||
if (filter?.search) {
|
||||
const searchLower = filter.search.toLowerCase();
|
||||
filtered = filtered.filter(
|
||||
(entry) =>
|
||||
entry.name.toLowerCase().includes(searchLower) ||
|
||||
entry.description.toLowerCase().includes(searchLower) ||
|
||||
entry.tags.some((tag) => tag.toLowerCase().includes(searchLower))
|
||||
);
|
||||
}
|
||||
|
||||
if (filter?.installed !== undefined) {
|
||||
filtered = filtered.filter((entry) => entry.isInstalled === filter.installed);
|
||||
}
|
||||
|
||||
if (filter?.official !== undefined) {
|
||||
filtered = filtered.filter((entry) => entry.isOfficial === filter.official);
|
||||
}
|
||||
|
||||
return filtered.sort((a, b) => {
|
||||
// Sort by: installed first, then official, then name
|
||||
if (a.isInstalled !== b.isInstalled) {
|
||||
return a.isInstalled ? -1 : 1;
|
||||
}
|
||||
if (a.isOfficial !== b.isOfficial) {
|
||||
return a.isOfficial ? -1 : 1;
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing registry entry's state
|
||||
*/
|
||||
async updateEntry(id: string, updates: Partial<ServerRegistryEntry>): Promise<boolean> {
|
||||
await this.initialize();
|
||||
const entry = this.registryEntries.find((e) => e.id === id);
|
||||
if (!entry) return false;
|
||||
|
||||
// Use Object.assign to merge partial updates
|
||||
Object.assign(entry, updates);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a server as installed/uninstalled
|
||||
*/
|
||||
async setInstalled(id: string, installed: boolean): Promise<boolean> {
|
||||
return this.updateEntry(id, { isInstalled: installed });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync registry installed status with a list of connected server IDs
|
||||
*/
|
||||
async syncInstalledStatus(connectedServerIds: string[]): Promise<void> {
|
||||
await this.initialize();
|
||||
|
||||
const normalizedIds = new Set(connectedServerIds.map(normalizeId));
|
||||
|
||||
for (const entry of this.registryEntries) {
|
||||
const aliases = [entry.id, entry.name, ...(entry.matchIds || [])]
|
||||
.filter(Boolean)
|
||||
.map((x) => normalizeId(String(x)));
|
||||
|
||||
const isInstalled = aliases.some((alias) => normalizedIds.has(alias));
|
||||
|
||||
if (entry.isInstalled !== isInstalled) {
|
||||
entry.isInstalled = isInstalled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single server configuration by ID
|
||||
*/
|
||||
async getServerConfig(id: string): Promise<ServerRegistryEntry | null> {
|
||||
await this.initialize();
|
||||
return this.registryEntries.find((entry) => entry.id === id) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available categories
|
||||
*/
|
||||
async getCategories(): Promise<string[]> {
|
||||
await this.initialize();
|
||||
const categories = new Set(this.registryEntries.map((entry) => entry.category));
|
||||
return Array.from(categories).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available tags
|
||||
*/
|
||||
async getTags(): Promise<string[]> {
|
||||
await this.initialize();
|
||||
const tags = new Set(this.registryEntries.flatMap((entry) => entry.tags));
|
||||
return Array.from(tags).sort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton registry instance
|
||||
*/
|
||||
export function getServerRegistry(): ServerRegistryService {
|
||||
return ServerRegistryService.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export singleton instance for convenience
|
||||
*/
|
||||
export const serverRegistry = ServerRegistryService.getInstance();
|
||||
95
dexto/packages/registry/src/mcp/types.ts
Normal file
95
dexto/packages/registry/src/mcp/types.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* MCP Server Registry Types
|
||||
*
|
||||
* Defines types for the MCP server registry, which provides
|
||||
* preset server configurations for easy installation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Server category for organization and filtering
|
||||
*/
|
||||
export type ServerCategory =
|
||||
| 'productivity'
|
||||
| 'development'
|
||||
| 'research'
|
||||
| 'creative'
|
||||
| 'data'
|
||||
| 'communication'
|
||||
| 'custom';
|
||||
|
||||
/**
|
||||
* Server configuration for different transport types
|
||||
*/
|
||||
export interface ServerConfig {
|
||||
type: 'stdio' | 'sse' | 'http';
|
||||
command?: string;
|
||||
args?: string[];
|
||||
url?: string;
|
||||
baseUrl?: string;
|
||||
env?: Record<string, string>;
|
||||
headers?: Record<string, string>;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform and dependency requirements for a server
|
||||
*/
|
||||
export interface ServerRequirements {
|
||||
platform?: 'win32' | 'darwin' | 'linux' | 'all';
|
||||
node?: string;
|
||||
python?: string;
|
||||
dependencies?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A single entry in the MCP server registry
|
||||
*/
|
||||
export interface ServerRegistryEntry {
|
||||
/** Unique identifier for the server */
|
||||
id: string;
|
||||
/** Display name */
|
||||
name: string;
|
||||
/** Description of what the server does */
|
||||
description: string;
|
||||
/** Category for organization */
|
||||
category: ServerCategory;
|
||||
/** Emoji icon for display */
|
||||
icon?: string;
|
||||
/** Author or maintainer */
|
||||
author?: string;
|
||||
/** Homepage or documentation URL */
|
||||
homepage?: string;
|
||||
/** Server connection configuration */
|
||||
config: ServerConfig;
|
||||
/** Tags for search and filtering */
|
||||
tags: string[];
|
||||
/** Whether this is an official/verified server */
|
||||
isOfficial: boolean;
|
||||
/** Whether this server is currently installed (runtime state) */
|
||||
isInstalled: boolean;
|
||||
/** System requirements */
|
||||
requirements?: ServerRequirements;
|
||||
/** Alternative IDs used to match against connected servers */
|
||||
matchIds?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter options for querying the registry
|
||||
*/
|
||||
export interface ServerRegistryFilter {
|
||||
category?: string;
|
||||
tags?: string[];
|
||||
search?: string;
|
||||
installed?: boolean;
|
||||
official?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* State container for registry UI
|
||||
*/
|
||||
export interface ServerRegistryState {
|
||||
entries: ServerRegistryEntry[];
|
||||
isLoading: boolean;
|
||||
error?: string;
|
||||
lastUpdated?: Date;
|
||||
}
|
||||
15
dexto/packages/registry/tsconfig.json
Normal file
15
dexto/packages/registry/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"emitDeclarationOnly": false,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["**/*.test.ts", "**/*.spec.ts", "**/*.integration.test.ts", "dist", "node_modules"]
|
||||
}
|
||||
4
dexto/packages/registry/tsconfig.typecheck.json
Normal file
4
dexto/packages/registry/tsconfig.typecheck.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["dist", "node_modules"]
|
||||
}
|
||||
17
dexto/packages/registry/tsup.config.ts
Normal file
17
dexto/packages/registry/tsup.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: ['src/index.ts'],
|
||||
format: ['esm', 'cjs'],
|
||||
outDir: 'dist',
|
||||
dts: false, // Use tsc for DTS generation (consistent with other packages)
|
||||
clean: true,
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
// Include JSON files in the bundle
|
||||
loader: {
|
||||
'.json': 'json',
|
||||
},
|
||||
},
|
||||
]);
|
||||
Reference in New Issue
Block a user