DEV Community

gentic news
gentic news

Posted on • Originally published at gentic.news

Build an MCP-Powered SaaS Discovery Engine with Next.js and PostgreSQL

Build an AI-ready SaaS directory by designing a canonical product entity first, then exposing it via MCP. Use Prisma, Zod, and JSON-LD to serve humans, search engines, and AI agents from one source of truth.

Build an MCP-Powered SaaS Discovery Engine with Next.js and PostgreSQL

Software discovery is no longer just a search problem. Today, a product might be discovered through a search engine, a curated directory, an AI assistant, or an autonomous agent assembling a shortlist. Your directory must serve all these consumers from a single source of truth.

Here's how to build a SaaS discovery engine that serves humans, search engines, and AI agents using Next.js, PostgreSQL, and the Model Context Protocol (MCP).

Key Takeaways

  • Build an AI-ready SaaS directory by designing a canonical product entity first, then exposing it via MCP.
  • Use Prisma, Zod, and JSON-LD to serve humans, search engines, and AI agents from one source of truth.

The Architecture Principle: One Source of Truth

Building an MCP Server with .NET SDK: A simple example | by Mukesh ...

The most important design principle: store product facts once, then generate every public representation from the same canonical record.

Without a single source of truth, your homepage may call a product an "automation tool," your directory listing may call it a "productivity platform," and your API response may classify it as a "developer tool." Humans can sometimes resolve that inconsistency. Machines are much less forgiving.

1. Design the Product Entity Before the UI

It's tempting to start with cards, filters, and landing pages. Start with the entity model instead.

A useful product profile needs more than a name and description. Here's a Prisma model that provides a solid foundation:

enum ProductStatus {
  DRAFT
  PENDING_REVIEW
  PUBLISHED
  REJECTED
  ARCHIVED
}

enum PricingModel {
  FREE
  FREEMIUM
  SUBSCRIPTION
  USAGE_BASED
  ONE_TIME
  ENTERPRISE
  OPEN_SOURCE
}

model Product {
  id               String        @id @default(cuid())
  slug             String        @unique
  name             String
  tagline          String
  shortDescription String
  description      String
  websiteUrl       String
  logoUrl          String?
  pricingModel     PricingModel
  isAiNative       Boolean       @default(false)
  status           ProductStatus @default(DRAFT)
  // ... categories, audiences, features, FAQs, media, social links
  visibilityScore  Int           @default(0)
  publishedAt      DateTime?
  createdAt        DateTime      @default(now())
  updatedAt        DateTime      @updatedAt
}
Enter fullscreen mode Exit fullscreen mode

This schema deliberately separates categories, audiences, features, FAQs, and media. Storing all of them in one JSON column would make initial development faster, but it would make filtering, analytics, validation, and recommendations harder later.

Separate fields by purpose:

  • Identity fields (change rarely): name, slug, website URL, company, primary category
  • Descriptive fields (can evolve): tagline, long description, features, screenshots, FAQs
  • Operational fields (platform-maintained): moderation status, visibility score, verification date

This distinction helps with permissions. A founder may edit a tagline, but should not directly edit a visibility score or moderation status.

2. Build a Strict Submission Pipeline

Public directories attract inconsistent data. A submission endpoint should perform four stages:

  1. Parse and validate — Use Zod to define the input boundary
  2. Normalize — Strip tracking parameters, lowercase URLs, clean category names
  3. Detect duplicates — Check for existing products with the same website URL
  4. Store as pending review — Don't publish immediately
const productSubmissionSchema = z.object({
  name: z.string().trim().min(2).max(100),
  websiteUrl: z.string().url().transform(cleanUrl),
  pricingModel: z.nativeEnum(PricingModel),
  categorySlugs: z.array(z.string()).min(1).max(5),
  // ...
});
Enter fullscreen mode Exit fullscreen mode

3. Expose Your Data via MCP for AI Clients

Build an MCP Server in Next.js for Claude Code (Complete ...

This is where the Model Context Protocol (MCP) comes in. Instead of building a separate API for AI agents, expose your product data through an MCP server that connects directly to Claude Code and other AI assistants.

// mcp-server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server({
  name: "saas-discovery",
  version: "1.0.0",
}, {
  capabilities: {
    resources: {},
    tools: {},
  },
});

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: "search_products",
    description: "Search SaaS products by category, audience, or keyword",
    inputSchema: {
      type: "object",
      properties: {
        query: { type: "string" },
        category: { type: "string" },
        audience: { type: "string" },
        limit: { type: "number", default: 10 },
      },
    },
  }],
}));
Enter fullscreen mode Exit fullscreen mode

With this MCP server, Claude Code users can query your directory directly:

claude code search_products query="project management" audience="startups" limit=5
Enter fullscreen mode Exit fullscreen mode

4. Add JSON-LD for Search Engines

Each product page should include structured data for search engines:

// app/products/[slug]/page.tsx
import { jsonLdScript } from "@/lib/json-ld";

export default async function ProductPage({ params }) {
  const product = await getProduct(params.slug);

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{
          __html: JSON.stringify(jsonLdScript(product))
        }}
      />
      {/* rest of the page */}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Try It Now: Your Next Steps

  1. Define your Prisma schema first — start with the entity model, not the UI
  2. Add Zod validation to your submission endpoint
  3. Create an MCP server that exposes your product data as tools and resources
  4. Add JSON-LD to every product page for SEO
  5. Connect your MCP server to Claude Code with claude mcp add

Your SaaS directory should be a product knowledge system that serves every consumer from one canonical record. Start with the data model, and everything else follows.


Source: dev.to

[Updated 20 Jul via devto_mcp]

The MCP ecosystem has exploded to 13,000+ servers on npm and GitHub as of May 2026, with 97 million monthly SDK downloads — triple the volume from six months ago, according to a recent ecosystem analysis [per devto_mcp]. This 400% YoY growth in new server registrations underscores the urgency of building discoverable directories like the one described above. Anthropic's official servers alone see 48,500 downloads per month for the filesystem server, highlighting the gap between supply and findability that a well-structured SaaS discovery engine can fill.


Originally published on gentic.news

Top comments (0)