Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions marketplace-data/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Marketplace Data

This directory contains marketplace data files for Roo Code's marketplace system.

## Structure

- `mcps.yaml` - MCP (Model Context Protocol) servers available in the marketplace
- `modes.yaml` - Custom modes available in the marketplace (to be added)

## MCP Servers

The `mcps.yaml` file contains a list of MCP servers that can be installed through the Roo Code marketplace. Each MCP server entry includes:

- `id` - Unique identifier for the MCP server
- `name` - Display name
- `description` - Detailed description of functionality
- `author` - Author name
- `authorUrl` - Author's website or GitHub profile
- `url` - Repository or documentation URL
- `tags` - Array of tags for categorization
- `prerequisites` - Array of requirements needed before installation
- `content` - Installation methods (can be a single string or array of methods)

### Installation Methods

Each installation method can include:

- `name` - Method name (e.g., "uvx", "pip install")
- `content` - JSON configuration to be added to MCP settings
- `parameters` - Optional parameters that users can customize
- `prerequisites` - Method-specific prerequisites

### Parameters

Parameters allow users to customize the installation:

- `name` - Display name for the parameter
- `key` - Key used in the configuration template
- `placeholder` - Default value or example
- `optional` - Whether the parameter is optional (default: false)

## VoiceVox MCP Server

The VoiceVox MCP Server provides Japanese text-to-speech capabilities using the VoiceVox engine. It includes:

### Features

- Text-to-Speech conversion for Japanese text
- Multiple voice characters with different personalities
- Customizable speech speed and voice selection
- Automatic audio playback after generation
- WAV format audio file management

### Available Tools

1. `get_voices` - Retrieve list of available voices from VoiceVox
2. `text_to_speech` - Convert text to speech with customizable settings

### Use Cases

- Educational content creation with Japanese narration
- Accessibility improvements for Japanese content
- Podcast and video production
- Language learning applications
- Chatbot voice functionality

### Installation Options

1. **uvx (Recommended)** - Uses uvx to run the server directly
2. **pip install** - Install via pip and run with Python
3. **Custom Configuration** - Full customization with all available parameters

### Prerequisites

- VoiceVox Engine running (default: http://localhost:50021)
- Python 3.10+
- For uvx method: `pip install uvx`
- For pip method: `pip install mcp-server-voicevox`

## Usage

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.

## Contributing

To add a new MCP server to the marketplace:

1. Add an entry to `mcps.yaml` following the schema
2. Ensure all required fields are provided
3. Test the installation methods
4. Submit a pull request

## References

- [VoiceVox MCP Server Repository](https://github.com/Sunwood-ai-labs/mcp-voicevox)
- [VoiceVox MCP Server PyPI Package](https://pypi.org/project/mcp-server-voicevox/)
- [Model Context Protocol Documentation](https://docs.roocode.com/advanced-usage/mcp)
74 changes: 74 additions & 0 deletions marketplace-data/mcps.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
items:
- id: "voicevox-tts"
name: "VoiceVox Text-to-Speech Server"
description: "Japanese Text-to-Speech integration using VoiceVox engine with multiple voice characters and customizable playback settings"
author: "Sunwood AI Labs"
authorUrl: "https://github.com/Sunwood-ai-labs"
url: "https://github.com/Sunwood-ai-labs/mcp-voicevox"
tags:
- "text-to-speech"
- "japanese"
- "audio"
- "voice"
- "accessibility"
- "content-creation"
prerequisites:
- "VoiceVox Engine running (locally or remotely)"
- "Python 3.10+"
content:
- name: "uvx (Recommended)"
content: |
{
"voicevox": {
"command": "uvx",
"args": ["mcp-server-voicevox", "--voicevox-url=http://localhost:50021"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an inconsistency in the module naming. In the 'uvx (Recommended)' and 'Custom Configuration' sections, the command argument is 'mcp-server-voicevox', but in the 'pip install' section, it appears as 'mcp_server_voicevox'. Please confirm if this is intentional or if it should be standardized.

Suggested change
"args": ["mcp-server-voicevox", "--voicevox-url=http://localhost:50021"]
"args": ["mcp_server_voicevox", "--voicevox-url=http://localhost:50021"]

}
}
parameters:
- name: "VoiceVox URL"
key: "voicevox-url"
placeholder: "http://localhost:50021"
optional: true
prerequisites:
- "uvx installed (pip install uvx)"
- "VoiceVox Engine running on specified URL"
- name: "pip install"
content: |
{
"voicevox": {
"command": "python",
"args": ["-m", "mcp_server_voicevox", "--voicevox-url=http://localhost:50021"]
}
}
parameters:
- name: "VoiceVox URL"
key: "voicevox-url"
placeholder: "http://localhost:50021"
optional: true
prerequisites:
- "pip install mcp-server-voicevox"
- "VoiceVox Engine running on specified URL"
- name: "Custom Configuration"
content: |
{
"voicevox": {
"command": "uvx",
"args": ["mcp-server-voicevox", "--voicevox-url={{voicevox-url}}", "--default-speaker={{default-speaker}}", "--speed={{speed}}"]
}
}
parameters:
- name: "VoiceVox URL"
key: "voicevox-url"
placeholder: "http://localhost:50021"
optional: false
- name: "Default Speaker ID"
key: "default-speaker"
placeholder: "1"
optional: true
- name: "Speech Speed"
key: "speed"
placeholder: "1.0"
optional: true
prerequisites:
- "uvx installed (pip install uvx)"
- "VoiceVox Engine running on specified URL"
152 changes: 132 additions & 20 deletions src/services/marketplace/RemoteConfigLoader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from "axios"
import * as yaml from "yaml"
import * as fs from "fs/promises"
import * as path from "path"
import { z } from "zod"
import { getRooCodeApiUrl } from "@roo-code/cloud"
import type { MarketplaceItem, MarketplaceItemType } from "@roo-code/types"
Expand Down Expand Up @@ -37,39 +39,49 @@ export class RemoteConfigLoader {
const cached = this.getFromCache(cacheKey)
if (cached) return cached

const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/modes`)
try {
const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/modes`)

// Parse and validate YAML response
const yamlData = yaml.parse(data)
const validated = modeMarketplaceResponse.parse(yamlData)
// Parse and validate YAML response
const yamlData = yaml.parse(data)
const validated = modeMarketplaceResponse.parse(yamlData)

const items: MarketplaceItem[] = validated.items.map((item) => ({
type: "mode" as const,
...item,
}))
const items: MarketplaceItem[] = validated.items.map((item) => ({
type: "mode" as const,
...item,
}))

this.setCache(cacheKey, items)
return items
this.setCache(cacheKey, items)
return items
} catch (error) {
console.warn("Failed to fetch modes from remote API, trying local fallback:", error)
return this.fetchLocalModes()
}
}

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

const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/mcps`)
try {
const data = await this.fetchWithRetry<string>(`${this.apiBaseUrl}/api/marketplace/mcps`)

// Parse and validate YAML response
const yamlData = yaml.parse(data)
const validated = mcpMarketplaceResponse.parse(yamlData)
// Parse and validate YAML response
const yamlData = yaml.parse(data)
const validated = mcpMarketplaceResponse.parse(yamlData)

const items: MarketplaceItem[] = validated.items.map((item) => ({
type: "mcp" as const,
...item,
}))
const items: MarketplaceItem[] = validated.items.map((item) => ({
type: "mcp" as const,
...item,
}))

this.setCache(cacheKey, items)
return items
this.setCache(cacheKey, items)
return items
} catch (error) {
console.warn("Failed to fetch MCPs from remote API, trying local fallback:", error)
return this.fetchLocalMcps()
}
}

private async fetchWithRetry<T>(url: string, maxRetries = 3): Promise<T> {
Expand Down Expand Up @@ -126,4 +138,104 @@ export class RemoteConfigLoader {
clearCache(): void {
this.cache.clear()
}

/**
* Fallback method to load MCPs from local marketplace data file
*/
private async fetchLocalMcps(): Promise<MarketplaceItem[]> {
try {
// Try to load from local marketplace-data directory
// Look for marketplace-data in current directory, parent directories, or relative to __dirname
const possiblePaths = [
path.join(process.cwd(), "marketplace-data", "mcps.yaml"),
path.join(process.cwd(), "..", "marketplace-data", "mcps.yaml"),
path.join(process.cwd(), "..", "..", "marketplace-data", "mcps.yaml"),
path.join(process.cwd(), "..", "..", "..", "marketplace-data", "mcps.yaml"),
path.join(__dirname, "..", "..", "..", "marketplace-data", "mcps.yaml"),
]

let data: string | null = null
let usedPath: string | null = null

for (const localMcpPath of possiblePaths) {
try {
data = await fs.readFile(localMcpPath, "utf-8")
usedPath = localMcpPath
break
} catch (error) {
// Continue to next path
continue
}
}

if (!data) {
throw new Error("Could not find mcps.yaml in any expected location")
}

// Parse and validate YAML response
const yamlData = yaml.parse(data)
const validated = mcpMarketplaceResponse.parse(yamlData)

const items: MarketplaceItem[] = validated.items.map((item) => ({
type: "mcp" as const,
...item,
}))

console.log(`Loaded ${items.length} MCP items from local marketplace data at ${usedPath}`)
return items
} catch (error) {
console.warn("Failed to load local MCP data:", error)
return []
}
}

/**
* Fallback method to load modes from local marketplace data file
*/
private async fetchLocalModes(): Promise<MarketplaceItem[]> {
try {
// Try to load from local marketplace-data directory
// Look for marketplace-data in current directory, parent directories, or relative to __dirname
const possiblePaths = [
path.join(process.cwd(), "marketplace-data", "modes.yaml"),
path.join(process.cwd(), "..", "marketplace-data", "modes.yaml"),
path.join(process.cwd(), "..", "..", "marketplace-data", "modes.yaml"),
path.join(process.cwd(), "..", "..", "..", "marketplace-data", "modes.yaml"),
path.join(__dirname, "..", "..", "..", "marketplace-data", "modes.yaml"),
]

let data: string | null = null
let usedPath: string | null = null

for (const localModePath of possiblePaths) {
try {
data = await fs.readFile(localModePath, "utf-8")
usedPath = localModePath
break
} catch (error) {
// Continue to next path
continue
}
}

if (!data) {
throw new Error("Could not find modes.yaml in any expected location")
}

// Parse and validate YAML response
const yamlData = yaml.parse(data)
const validated = modeMarketplaceResponse.parse(yamlData)

const items: MarketplaceItem[] = validated.items.map((item) => ({
type: "mode" as const,
...item,
}))

console.log(`Loaded ${items.length} mode items from local marketplace data at ${usedPath}`)
return items
} catch (error) {
console.warn("Failed to load local mode data:", error)
return []
}
}
}
Loading
Loading