diff --git a/marketplace/README.md b/marketplace/README.md new file mode 100644 index 00000000000..3707c4ed1f1 --- /dev/null +++ b/marketplace/README.md @@ -0,0 +1,98 @@ +# Roo Code Marketplace + +This directory contains the marketplace configuration files for Roo Code extensions, including MCP servers and custom modes. + +## Structure + +- `mcps.yml` - Configuration for MCP (Model Context Protocol) servers +- `modes.yml` - Configuration for custom modes + +## Adding New MCP Servers + +To add a new MCP server to the marketplace, add an entry to `mcps.yml` following this structure: + +```yaml +- id: "unique-server-id" + name: "Display Name" + description: "Detailed description of what the MCP server does" + author: "Author Name" + authorUrl: "https://github.com/author" + url: "https://github.com/author/repo" + tags: ["tag1", "tag2", "tag3"] + prerequisites: ["Node.js 18+", "npm"] + content: + - name: "Installation Method Name" + content: | + { + "mcpServers": { + "server-name": { + "command": "npx", + "args": ["package-name"], + "env": {} + } + } + } + parameters: [] + prerequisites: ["Node.js 18+"] +``` + +## Adding New Modes + +To add a new custom mode to the marketplace, add an entry to `modes.yml` following this structure: + +```yaml +- id: "unique-mode-id" + name: "Mode Display Name" + description: "Description of what the mode does" + author: "Author Name" + authorUrl: "https://github.com/author" + tags: ["tag1", "tag2"] + prerequisites: ["Required MCP Server"] + content: | + name: Mode Name + slug: mode-slug + description: Brief mode description + + instructions: | + Detailed instructions for the mode... +``` + +## Daft.ie MCP Server + +The Daft.ie MCP Server has been added to the marketplace as requested in GitHub issue #4756. This server provides: + +- Property search functionality for Irish rental market +- Integration with Daft.ie's property database +- Filtering by location, price, property type, and amenities +- Real-time property data and availability + +### Installation + +The Daft.ie MCP server can be installed via: + +1. **NPM Installation** (Recommended) + + ```bash + npx daft-ie-mcp + ``` + +2. **Local Development** + - Clone the repository from https://github.com/amineremache/daft-ie-mcp + - Build and run locally + +### Related Mode + +A complementary "Property Search Mode" has also been added to help users effectively utilize the Daft.ie MCP server for property searches and market analysis. + +## Contributing + +To contribute new marketplace items: + +1. Fork the repository +2. Add your item to the appropriate YAML file +3. Test the configuration +4. Submit a pull request + +## Validation + +All marketplace items should follow the schema defined in `packages/types/src/marketplace.ts` to ensure compatibility with the Roo Code extension. diff --git a/marketplace/mcps.yml b/marketplace/mcps.yml new file mode 100644 index 00000000000..01c23ec194f --- /dev/null +++ b/marketplace/mcps.yml @@ -0,0 +1,43 @@ +# Marketplace MCP Servers Configuration +# This file contains the configuration for MCP servers available in the marketplace + +items: + - id: "daft-ie-mcp" + name: "Daft.ie MCP Server" + description: "An MCP server for searching rental properties on Daft.ie, Ireland's largest property website. Search for apartments, houses, and other rental properties with filters for location, price, property type, and more." + author: "amineremache" + authorUrl: "https://github.com/amineremache" + url: "https://github.com/amineremache/daft-ie-mcp" + tags: ["property", "rental", "ireland", "search", "real-estate"] + prerequisites: ["Node.js 18+", "npm or yarn"] + content: + - name: "NPM Installation" + content: | + { + "mcpServers": { + "daft-ie": { + "command": "npx", + "args": ["daft-ie-mcp"], + "env": {} + } + } + } + parameters: [] + prerequisites: ["Node.js 18+", "npm"] + - name: "Local Development" + content: | + { + "mcpServers": { + "daft-ie": { + "command": "node", + "args": ["path/to/daft-ie-mcp/dist/index.js"], + "env": {} + } + } + } + parameters: + - name: "Installation Path" + key: "path" + placeholder: "/path/to/daft-ie-mcp" + optional: false + prerequisites: ["Node.js 18+", "Git", "Local clone of the repository"] \ No newline at end of file diff --git a/marketplace/modes.yml b/marketplace/modes.yml new file mode 100644 index 00000000000..bf88cbb6bd2 --- /dev/null +++ b/marketplace/modes.yml @@ -0,0 +1,43 @@ +# Marketplace Modes Configuration +# This file contains the configuration for custom modes available in the marketplace + +items: + - id: "property-search-mode" + name: "Property Search Mode" + description: "A specialized mode for searching and analyzing rental properties using the Daft.ie MCP server. Optimized for property research, market analysis, and rental searches in Ireland." + author: "RooCode Team" + authorUrl: "https://github.com/RooCodeInc" + tags: ["property", "rental", "ireland", "search", "real-estate", "analysis"] + prerequisites: ["Daft.ie MCP Server"] + content: | + name: Property Search Specialist + slug: property-search + description: I am a property search specialist focused on helping you find rental properties in Ireland using Daft.ie. I can search for apartments, houses, and other rental properties with various filters, analyze market trends, and provide detailed property information. + + instructions: | + You are a property search specialist with access to the Daft.ie MCP server. Your role is to help users find rental properties in Ireland by: + + 1. **Property Search**: Use the Daft.ie MCP tools to search for rental properties based on user criteria such as: + - Location (county, city, area) + - Price range + - Property type (apartment, house, studio, etc.) + - Number of bedrooms/bathrooms + - Amenities and features + + 2. **Market Analysis**: Provide insights on: + - Average rental prices in specific areas + - Property availability trends + - Comparison between different locations + - Market recommendations + + 3. **Property Details**: When users are interested in specific properties, provide: + - Detailed property descriptions + - Photos and virtual tours (if available) + - Contact information for landlords/agents + - Nearby amenities and transport links + + 4. **Search Optimization**: Help users refine their search criteria to find the best matches for their needs and budget. + + Always be helpful, informative, and provide accurate property information. If you cannot find properties matching specific criteria, suggest alternative options or broader search parameters. + + Remember to use the Daft.ie MCP server tools effectively to provide real-time property data and search results. \ No newline at end of file diff --git a/scripts/validate-marketplace.js b/scripts/validate-marketplace.js new file mode 100644 index 00000000000..ab6cb2b72e5 --- /dev/null +++ b/scripts/validate-marketplace.js @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +const fs = require("fs") +const path = require("path") +const yaml = require("yaml") + +// Simple validation script for marketplace files +function validateMarketplaceFiles() { + const marketplaceDir = path.join(__dirname, "..", "marketplace") + + console.log("šŸ” Validating marketplace files...\n") + + // Check mcps.yml + try { + const mcpsPath = path.join(marketplaceDir, "mcps.yml") + const mcpsContent = fs.readFileSync(mcpsPath, "utf-8") + const mcpsData = yaml.parse(mcpsContent) + + console.log("āœ… mcps.yml is valid YAML") + console.log(` Found ${mcpsData.items.length} MCP server(s)`) + + // Check for Daft.ie MCP + const daftieServer = mcpsData.items.find((item) => item.id === "daft-ie-mcp") + if (daftieServer) { + console.log("āœ… Daft.ie MCP Server found") + console.log(` Name: ${daftieServer.name}`) + console.log(` Author: ${daftieServer.author}`) + console.log(` URL: ${daftieServer.url}`) + console.log(` Tags: ${daftieServer.tags.join(", ")}`) + console.log(` Installation methods: ${daftieServer.content.length}`) + } else { + console.log("āŒ Daft.ie MCP Server not found") + } + } catch (error) { + console.log("āŒ Error validating mcps.yml:", error.message) + } + + console.log("") + + // Check modes.yml + try { + const modesPath = path.join(marketplaceDir, "modes.yml") + const modesContent = fs.readFileSync(modesPath, "utf-8") + const modesData = yaml.parse(modesContent) + + console.log("āœ… modes.yml is valid YAML") + console.log(` Found ${modesData.items.length} mode(s)`) + + // Check for Property Search mode + const propertyMode = modesData.items.find((item) => item.id === "property-search-mode") + if (propertyMode) { + console.log("āœ… Property Search Mode found") + console.log(` Name: ${propertyMode.name}`) + console.log(` Author: ${propertyMode.author}`) + console.log(` Tags: ${propertyMode.tags.join(", ")}`) + } else { + console.log("āŒ Property Search Mode not found") + } + } catch (error) { + console.log("āŒ Error validating modes.yml:", error.message) + } + + console.log("\nšŸŽ‰ Marketplace validation complete!") +} + +validateMarketplaceFiles() diff --git a/src/services/marketplace/__tests__/marketplace-data.spec.ts b/src/services/marketplace/__tests__/marketplace-data.spec.ts new file mode 100644 index 00000000000..d760be1b3b9 --- /dev/null +++ b/src/services/marketplace/__tests__/marketplace-data.spec.ts @@ -0,0 +1,138 @@ +import * as fs from "fs/promises" +import * as path from "path" +import * as yaml from "yaml" +import { modeMarketplaceItemSchema, mcpMarketplaceItemSchema } from "@roo-code/types" + +describe("Marketplace Data Validation", () => { + const marketplaceDir = path.join(__dirname, "../../../../marketplace") + + describe("MCP Servers Configuration", () => { + it("should have valid mcps.yml file", async () => { + const mcpsPath = path.join(marketplaceDir, "mcps.yml") + const content = await fs.readFile(mcpsPath, "utf-8") + const data = yaml.parse(content) + + expect(data).toHaveProperty("items") + expect(Array.isArray(data.items)).toBe(true) + }) + + it("should validate all MCP items against schema", async () => { + const mcpsPath = path.join(marketplaceDir, "mcps.yml") + const content = await fs.readFile(mcpsPath, "utf-8") + const data = yaml.parse(content) + + for (const item of data.items) { + expect(() => mcpMarketplaceItemSchema.parse(item)).not.toThrow() + } + }) + + it("should contain Daft.ie MCP server", async () => { + const mcpsPath = path.join(marketplaceDir, "mcps.yml") + const content = await fs.readFile(mcpsPath, "utf-8") + const data = yaml.parse(content) + + const daftieServer = data.items.find((item: any) => item.id === "daft-ie-mcp") + expect(daftieServer).toBeDefined() + expect(daftieServer.name).toBe("Daft.ie MCP Server") + expect(daftieServer.author).toBe("amineremache") + expect(daftieServer.url).toBe("https://github.com/amineremache/daft-ie-mcp") + expect(daftieServer.tags).toContain("ireland") + expect(daftieServer.tags).toContain("rental") + expect(daftieServer.tags).toContain("property") + }) + + it("should have valid installation methods for Daft.ie MCP", async () => { + const mcpsPath = path.join(marketplaceDir, "mcps.yml") + const content = await fs.readFile(mcpsPath, "utf-8") + const data = yaml.parse(content) + + const daftieServer = data.items.find((item: any) => item.id === "daft-ie-mcp") + expect(daftieServer.content).toBeDefined() + expect(Array.isArray(daftieServer.content)).toBe(true) + expect(daftieServer.content.length).toBeGreaterThan(0) + + // Check NPM installation method + const npmMethod = daftieServer.content.find((method: any) => method.name === "NPM Installation") + expect(npmMethod).toBeDefined() + expect(npmMethod.content).toContain("daft-ie") + expect(npmMethod.content).toContain("npx") + + // Check local development method + const localMethod = daftieServer.content.find((method: any) => method.name === "Local Development") + expect(localMethod).toBeDefined() + expect(localMethod.parameters).toBeDefined() + expect(localMethod.parameters.length).toBeGreaterThan(0) + }) + }) + + describe("Modes Configuration", () => { + it("should have valid modes.yml file", async () => { + const modesPath = path.join(marketplaceDir, "modes.yml") + const content = await fs.readFile(modesPath, "utf-8") + const data = yaml.parse(content) + + expect(data).toHaveProperty("items") + expect(Array.isArray(data.items)).toBe(true) + }) + + it("should validate all mode items against schema", async () => { + const modesPath = path.join(marketplaceDir, "modes.yml") + const content = await fs.readFile(modesPath, "utf-8") + const data = yaml.parse(content) + + for (const item of data.items) { + expect(() => modeMarketplaceItemSchema.parse(item)).not.toThrow() + } + }) + + it("should contain Property Search mode", async () => { + const modesPath = path.join(marketplaceDir, "modes.yml") + const content = await fs.readFile(modesPath, "utf-8") + const data = yaml.parse(content) + + const propertyMode = data.items.find((item: any) => item.id === "property-search-mode") + expect(propertyMode).toBeDefined() + expect(propertyMode.name).toBe("Property Search Mode") + expect(propertyMode.tags).toContain("property") + expect(propertyMode.tags).toContain("rental") + expect(propertyMode.tags).toContain("ireland") + expect(propertyMode.prerequisites).toContain("Daft.ie MCP Server") + }) + + it("should have valid mode content structure", async () => { + const modesPath = path.join(marketplaceDir, "modes.yml") + const content = await fs.readFile(modesPath, "utf-8") + const data = yaml.parse(content) + + const propertyMode = data.items.find((item: any) => item.id === "property-search-mode") + expect(propertyMode.content).toBeDefined() + expect(propertyMode.content).toContain("name:") + expect(propertyMode.content).toContain("slug:") + expect(propertyMode.content).toContain("description:") + expect(propertyMode.content).toContain("instructions:") + }) + }) + + describe("Cross-references", () => { + it("should have matching tags between related MCP and mode", async () => { + const mcpsPath = path.join(marketplaceDir, "mcps.yml") + const modesPath = path.join(marketplaceDir, "modes.yml") + + const mcpsContent = await fs.readFile(mcpsPath, "utf-8") + const modesContent = await fs.readFile(modesPath, "utf-8") + + const mcpsData = yaml.parse(mcpsContent) + const modesData = yaml.parse(modesContent) + + const daftieServer = mcpsData.items.find((item: any) => item.id === "daft-ie-mcp") + const propertyMode = modesData.items.find((item: any) => item.id === "property-search-mode") + + // Check that they share common tags + const commonTags = ["property", "rental", "ireland"] + for (const tag of commonTags) { + expect(daftieServer.tags).toContain(tag) + expect(propertyMode.tags).toContain(tag) + } + }) + }) +})