Skip to content

Commit 564df5f

Browse files
committed
Fixes #5216
1 parent 3a8ba27 commit 564df5f

File tree

4 files changed

+426
-20
lines changed

4 files changed

+426
-20
lines changed

marketplace-data/README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Marketplace Data
2+
3+
This directory contains marketplace data files for Roo Code's marketplace system.
4+
5+
## Structure
6+
7+
- `mcps.yaml` - MCP (Model Context Protocol) servers available in the marketplace
8+
- `modes.yaml` - Custom modes available in the marketplace (to be added)
9+
10+
## MCP Servers
11+
12+
The `mcps.yaml` file contains a list of MCP servers that can be installed through the Roo Code marketplace. Each MCP server entry includes:
13+
14+
- `id` - Unique identifier for the MCP server
15+
- `name` - Display name
16+
- `description` - Detailed description of functionality
17+
- `author` - Author name
18+
- `authorUrl` - Author's website or GitHub profile
19+
- `url` - Repository or documentation URL
20+
- `tags` - Array of tags for categorization
21+
- `prerequisites` - Array of requirements needed before installation
22+
- `content` - Installation methods (can be a single string or array of methods)
23+
24+
### Installation Methods
25+
26+
Each installation method can include:
27+
28+
- `name` - Method name (e.g., "uvx", "pip install")
29+
- `content` - JSON configuration to be added to MCP settings
30+
- `parameters` - Optional parameters that users can customize
31+
- `prerequisites` - Method-specific prerequisites
32+
33+
### Parameters
34+
35+
Parameters allow users to customize the installation:
36+
37+
- `name` - Display name for the parameter
38+
- `key` - Key used in the configuration template
39+
- `placeholder` - Default value or example
40+
- `optional` - Whether the parameter is optional (default: false)
41+
42+
## VoiceVox MCP Server
43+
44+
The VoiceVox MCP Server provides Japanese text-to-speech capabilities using the VoiceVox engine. It includes:
45+
46+
### Features
47+
48+
- Text-to-Speech conversion for Japanese text
49+
- Multiple voice characters with different personalities
50+
- Customizable speech speed and voice selection
51+
- Automatic audio playback after generation
52+
- WAV format audio file management
53+
54+
### Available Tools
55+
56+
1. `get_voices` - Retrieve list of available voices from VoiceVox
57+
2. `text_to_speech` - Convert text to speech with customizable settings
58+
59+
### Use Cases
60+
61+
- Educational content creation with Japanese narration
62+
- Accessibility improvements for Japanese content
63+
- Podcast and video production
64+
- Language learning applications
65+
- Chatbot voice functionality
66+
67+
### Installation Options
68+
69+
1. **uvx (Recommended)** - Uses uvx to run the server directly
70+
2. **pip install** - Install via pip and run with Python
71+
3. **Custom Configuration** - Full customization with all available parameters
72+
73+
### Prerequisites
74+
75+
- VoiceVox Engine running (default: http://localhost:50021)
76+
- Python 3.10+
77+
- For uvx method: `pip install uvx`
78+
- For pip method: `pip install mcp-server-voicevox`
79+
80+
## Usage
81+
82+
This marketplace data is used by the Roo Code extension to populate the marketplace UI. Users can browse, search, and install MCP servers directly from the extension.
83+
84+
## Contributing
85+
86+
To add a new MCP server to the marketplace:
87+
88+
1. Add an entry to `mcps.yaml` following the schema
89+
2. Ensure all required fields are provided
90+
3. Test the installation methods
91+
4. Submit a pull request
92+
93+
## References
94+
95+
- [VoiceVox MCP Server Repository](https://github.com/Sunwood-ai-labs/mcp-voicevox)
96+
- [VoiceVox MCP Server PyPI Package](https://pypi.org/project/mcp-server-voicevox/)
97+
- [Model Context Protocol Documentation](https://docs.roocode.com/advanced-usage/mcp)

marketplace-data/mcps.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
items:
2+
- id: "voicevox-tts"
3+
name: "VoiceVox Text-to-Speech Server"
4+
description: "Japanese Text-to-Speech integration using VoiceVox engine with multiple voice characters and customizable playback settings"
5+
author: "Sunwood AI Labs"
6+
authorUrl: "https://github.com/Sunwood-ai-labs"
7+
url: "https://github.com/Sunwood-ai-labs/mcp-voicevox"
8+
tags:
9+
- "text-to-speech"
10+
- "japanese"
11+
- "audio"
12+
- "voice"
13+
- "accessibility"
14+
- "content-creation"
15+
prerequisites:
16+
- "VoiceVox Engine running (locally or remotely)"
17+
- "Python 3.10+"
18+
content:
19+
- name: "uvx (Recommended)"
20+
content: |
21+
{
22+
"voicevox": {
23+
"command": "uvx",
24+
"args": ["mcp-server-voicevox", "--voicevox-url=http://localhost:50021"]
25+
}
26+
}
27+
parameters:
28+
- name: "VoiceVox URL"
29+
key: "voicevox-url"
30+
placeholder: "http://localhost:50021"
31+
optional: true
32+
prerequisites:
33+
- "uvx installed (pip install uvx)"
34+
- "VoiceVox Engine running on specified URL"
35+
- name: "pip install"
36+
content: |
37+
{
38+
"voicevox": {
39+
"command": "python",
40+
"args": ["-m", "mcp_server_voicevox", "--voicevox-url=http://localhost:50021"]
41+
}
42+
}
43+
parameters:
44+
- name: "VoiceVox URL"
45+
key: "voicevox-url"
46+
placeholder: "http://localhost:50021"
47+
optional: true
48+
prerequisites:
49+
- "pip install mcp-server-voicevox"
50+
- "VoiceVox Engine running on specified URL"
51+
- name: "Custom Configuration"
52+
content: |
53+
{
54+
"voicevox": {
55+
"command": "uvx",
56+
"args": ["mcp-server-voicevox", "--voicevox-url={{voicevox-url}}", "--default-speaker={{default-speaker}}", "--speed={{speed}}"]
57+
}
58+
}
59+
parameters:
60+
- name: "VoiceVox URL"
61+
key: "voicevox-url"
62+
placeholder: "http://localhost:50021"
63+
optional: false
64+
- name: "Default Speaker ID"
65+
key: "default-speaker"
66+
placeholder: "1"
67+
optional: true
68+
- name: "Speech Speed"
69+
key: "speed"
70+
placeholder: "1.0"
71+
optional: true
72+
prerequisites:
73+
- "uvx installed (pip install uvx)"
74+
- "VoiceVox Engine running on specified URL"

src/services/marketplace/RemoteConfigLoader.ts

Lines changed: 132 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import axios from "axios"
22
import * as yaml from "yaml"
3+
import * as fs from "fs/promises"
4+
import * as path from "path"
35
import { z } from "zod"
46
import { getRooCodeApiUrl } from "@roo-code/cloud"
57
import type { MarketplaceItem, MarketplaceItemType } from "@roo-code/types"
@@ -37,39 +39,49 @@ export class RemoteConfigLoader {
3739
const cached = this.getFromCache(cacheKey)
3840
if (cached) return cached
3941

40-
const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/modes`)
42+
try {
43+
const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/modes`)
4144

42-
// Parse and validate YAML response
43-
const yamlData = yaml.parse(data)
44-
const validated = modeMarketplaceResponse.parse(yamlData)
45+
// Parse and validate YAML response
46+
const yamlData = yaml.parse(data)
47+
const validated = modeMarketplaceResponse.parse(yamlData)
4548

46-
const items: MarketplaceItem[] = validated.items.map((item) => ({
47-
type: "mode" as const,
48-
...item,
49-
}))
49+
const items: MarketplaceItem[] = validated.items.map((item) => ({
50+
type: "mode" as const,
51+
...item,
52+
}))
5053

51-
this.setCache(cacheKey, items)
52-
return items
54+
this.setCache(cacheKey, items)
55+
return items
56+
} catch (error) {
57+
console.warn("Failed to fetch modes from remote API, trying local fallback:", error)
58+
return this.fetchLocalModes()
59+
}
5360
}
5461

5562
private async fetchMcps(): Promise<MarketplaceItem[]> {
5663
const cacheKey = "mcps"
5764
const cached = this.getFromCache(cacheKey)
5865
if (cached) return cached
5966

60-
const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/mcps`)
67+
try {
68+
const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/mcps`)
6169

62-
// Parse and validate YAML response
63-
const yamlData = yaml.parse(data)
64-
const validated = mcpMarketplaceResponse.parse(yamlData)
70+
// Parse and validate YAML response
71+
const yamlData = yaml.parse(data)
72+
const validated = mcpMarketplaceResponse.parse(yamlData)
6573

66-
const items: MarketplaceItem[] = validated.items.map((item) => ({
67-
type: "mcp" as const,
68-
...item,
69-
}))
74+
const items: MarketplaceItem[] = validated.items.map((item) => ({
75+
type: "mcp" as const,
76+
...item,
77+
}))
7078

71-
this.setCache(cacheKey, items)
72-
return items
79+
this.setCache(cacheKey, items)
80+
return items
81+
} catch (error) {
82+
console.warn("Failed to fetch MCPs from remote API, trying local fallback:", error)
83+
return this.fetchLocalMcps()
84+
}
7385
}
7486

7587
private async fetchWithRetry<T>(url: string, maxRetries = 3): Promise<T> {
@@ -126,4 +138,104 @@ export class RemoteConfigLoader {
126138
clearCache(): void {
127139
this.cache.clear()
128140
}
141+
142+
/**
143+
* Fallback method to load MCPs from local marketplace data file
144+
*/
145+
private async fetchLocalMcps(): Promise<MarketplaceItem[]> {
146+
try {
147+
// Try to load from local marketplace-data directory
148+
// Look for marketplace-data in current directory, parent directories, or relative to __dirname
149+
const possiblePaths = [
150+
path.join(process.cwd(), "marketplace-data", "mcps.yaml"),
151+
path.join(process.cwd(), "..", "marketplace-data", "mcps.yaml"),
152+
path.join(process.cwd(), "..", "..", "marketplace-data", "mcps.yaml"),
153+
path.join(process.cwd(), "..", "..", "..", "marketplace-data", "mcps.yaml"),
154+
path.join(__dirname, "..", "..", "..", "marketplace-data", "mcps.yaml"),
155+
]
156+
157+
let data: string | null = null
158+
let usedPath: string | null = null
159+
160+
for (const localMcpPath of possiblePaths) {
161+
try {
162+
data = await fs.readFile(localMcpPath, "utf-8")
163+
usedPath = localMcpPath
164+
break
165+
} catch (error) {
166+
// Continue to next path
167+
continue
168+
}
169+
}
170+
171+
if (!data) {
172+
throw new Error("Could not find mcps.yaml in any expected location")
173+
}
174+
175+
// Parse and validate YAML response
176+
const yamlData = yaml.parse(data)
177+
const validated = mcpMarketplaceResponse.parse(yamlData)
178+
179+
const items: MarketplaceItem[] = validated.items.map((item) => ({
180+
type: "mcp" as const,
181+
...item,
182+
}))
183+
184+
console.log(`Loaded ${items.length} MCP items from local marketplace data at ${usedPath}`)
185+
return items
186+
} catch (error) {
187+
console.warn("Failed to load local MCP data:", error)
188+
return []
189+
}
190+
}
191+
192+
/**
193+
* Fallback method to load modes from local marketplace data file
194+
*/
195+
private async fetchLocalModes(): Promise<MarketplaceItem[]> {
196+
try {
197+
// Try to load from local marketplace-data directory
198+
// Look for marketplace-data in current directory, parent directories, or relative to __dirname
199+
const possiblePaths = [
200+
path.join(process.cwd(), "marketplace-data", "modes.yaml"),
201+
path.join(process.cwd(), "..", "marketplace-data", "modes.yaml"),
202+
path.join(process.cwd(), "..", "..", "marketplace-data", "modes.yaml"),
203+
path.join(process.cwd(), "..", "..", "..", "marketplace-data", "modes.yaml"),
204+
path.join(__dirname, "..", "..", "..", "marketplace-data", "modes.yaml"),
205+
]
206+
207+
let data: string | null = null
208+
let usedPath: string | null = null
209+
210+
for (const localModePath of possiblePaths) {
211+
try {
212+
data = await fs.readFile(localModePath, "utf-8")
213+
usedPath = localModePath
214+
break
215+
} catch (error) {
216+
// Continue to next path
217+
continue
218+
}
219+
}
220+
221+
if (!data) {
222+
throw new Error("Could not find modes.yaml in any expected location")
223+
}
224+
225+
// Parse and validate YAML response
226+
const yamlData = yaml.parse(data)
227+
const validated = modeMarketplaceResponse.parse(yamlData)
228+
229+
const items: MarketplaceItem[] = validated.items.map((item) => ({
230+
type: "mode" as const,
231+
...item,
232+
}))
233+
234+
console.log(`Loaded ${items.length} mode items from local marketplace data at ${usedPath}`)
235+
return items
236+
} catch (error) {
237+
console.warn("Failed to load local mode data:", error)
238+
return []
239+
}
240+
}
129241
}

0 commit comments

Comments
 (0)