feat(app): add MCP feature to user profile page

This commit is contained in:
Fatih Kadir Akın
2025-12-17 01:14:34 +03:00
parent 892fb0a32e
commit ea0725205e
12 changed files with 612 additions and 247 deletions

View File

@@ -62,6 +62,8 @@ export default defineConfig({
aiSearch: true,
// Enable AI-powered generation features (requires OPENAI_API_KEY)
aiGeneration: true,
// Enable MCP (Model Context Protocol) features including API key generation
mcp: true,
},
// Homepage customization

8
public/context7.svg Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" rx="4" fill="black" style="fill:black;fill-opacity:1;"/>
<path d="M10.5724 15.2565C10.5724 17.5025 9.6613 19.3778 8.17805 21.1047H11.6319L11.6319 22.7786H6.33459V21.1895C7.95557 19.3566 8.58065 17.8628 8.58065 15.2565L10.5724 15.2565Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M17.4276 15.2565C17.4276 17.5025 18.3387 19.3778 19.822 21.1047H16.3681V22.7786H21.6654V21.1895C20.0444 19.3566 19.4194 17.8628 19.4194 15.2565H17.4276Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M10.5724 12.7435C10.5724 10.4975 9.66131 8.62224 8.17807 6.89532L11.6319 6.89532V5.22137L6.33461 5.22137V6.81056C7.95558 8.64343 8.58066 10.1373 8.58066 12.7435L10.5724 12.7435Z" fill="white" style="fill:white;fill-opacity:1;"/>
<path d="M17.4276 12.7435C17.4276 10.4975 18.3387 8.62224 19.822 6.89532L16.3681 6.89532L16.3681 5.22138L21.6654 5.22138V6.81056C20.0445 8.64343 19.4194 10.1373 19.4194 12.7435H17.4276Z" fill="white" style="fill:white;fill-opacity:1;"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -7,6 +7,7 @@ import { getPromptUrl } from "@/lib/urls";
import { Calendar, ArrowBigUp, FileText, Settings, GitPullRequest, Clock, Check, X, Pin, BadgeCheck, Users, ShieldCheck } from "lucide-react";
import { auth } from "@/lib/auth";
import { db } from "@/lib/db";
import config from "@/../prompts.config";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -326,7 +327,7 @@ export default async function UserProfilePage({ params, searchParams }: UserProf
</div>
{/* Actions - desktop only */}
<div className="hidden md:flex items-center gap-2 shrink-0">
<McpServerPopup initialUsers={[user.username]} />
{config.features.mcp !== false && <McpServerPopup initialUsers={[user.username]} />}
{isOwner && (
<Button variant="outline" size="sm" asChild>
<Link href="/settings">
@@ -363,7 +364,7 @@ export default async function UserProfilePage({ params, searchParams }: UserProf
{/* Actions - mobile only */}
<div className="md:hidden flex gap-2">
<McpServerPopup initialUsers={[user.username]} />
{config.features.mcp !== false && <McpServerPopup initialUsers={[user.username]} />}
{isOwner && (
<Button variant="outline" size="sm" asChild className="flex-1">
<Link href="/settings">

260
src/app/brand/page.tsx Normal file
View File

@@ -0,0 +1,260 @@
"use client";
import { useState } from "react";
import Image from "next/image";
import { Download, Copy, Check } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useBranding } from "@/components/providers/branding-provider";
import { notFound } from "next/navigation";
interface AssetCardProps {
title: string;
description: string;
bgClass: string;
children: React.ReactNode;
downloadUrl: string;
filename: string;
}
function AssetCard({ title, description, bgClass, children, downloadUrl, filename }: AssetCardProps) {
const handleDownload = async () => {
try {
const response = await fetch(downloadUrl);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error("Download failed:", error);
}
};
return (
<div className="border rounded-lg overflow-hidden">
<div className={`${bgClass} h-40 flex items-center justify-center p-8`}>
{children}
</div>
<div className="p-4 flex items-center justify-between bg-background">
<div>
<p className="font-medium text-sm">{title}</p>
<p className="text-xs text-muted-foreground">{description}</p>
</div>
<Button variant="outline" size="sm" onClick={handleDownload}>
<Download className="h-3 w-3 mr-1" />
SVG
</Button>
</div>
</div>
);
}
interface ColorCardProps {
color: string;
name: string;
description: string;
}
function ColorCard({ color, name, description }: ColorCardProps) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(color);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<button
onClick={handleCopy}
className="flex items-center justify-between p-3 rounded-md transition-colors hover:opacity-80"
style={{ backgroundColor: color }}
>
<div className="text-left">
<p className="font-mono text-sm font-medium" style={{ color: isLight(color) ? "#000" : "#fff" }}>
{color}
</p>
<p className="text-xs opacity-80" style={{ color: isLight(color) ? "#000" : "#fff" }}>
{description}
</p>
</div>
{copied ? (
<Check className="h-4 w-4" style={{ color: isLight(color) ? "#000" : "#fff" }} />
) : (
<Copy className="h-4 w-4 opacity-60" style={{ color: isLight(color) ? "#000" : "#fff" }} />
)}
</button>
);
}
function isLight(color: string): boolean {
const hex = color.replace("#", "");
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
return brightness > 128;
}
export default function BrandAssetsPage() {
const branding = useBranding();
// Redirect if using clone branding
if (branding.useCloneBranding) {
notFound();
}
return (
<div className="container max-w-4xl py-10">
<h1 className="text-3xl font-bold mb-2">Brand Assets</h1>
<p className="text-muted-foreground mb-10">
Official logos, colors, and brand guidelines for {branding.name}. Free to use for press, partnerships, and community projects.
</p>
<div className="space-y-10">
{/* Logos Section */}
<section>
<h2 className="text-xl font-semibold mb-4">Logos</h2>
<div className="grid md:grid-cols-2 gap-4">
{/* Logo for light backgrounds */}
<AssetCard
title="Logo"
description="For light backgrounds"
bgClass="bg-gray-100"
downloadUrl="/logo.svg"
filename="prompts-chat-logo.svg"
>
<Image
src="/logo.svg"
alt="prompts.chat logo"
width={80}
height={80}
className="h-20 w-auto"
/>
</AssetCard>
{/* Logo for dark backgrounds */}
<AssetCard
title="Logo"
description="For dark backgrounds"
bgClass="bg-gray-900"
downloadUrl="/logo-dark.svg"
filename="prompts-chat-logo-dark.svg"
>
<Image
src="/logo-dark.svg"
alt="prompts.chat logo dark"
width={80}
height={80}
className="h-20 w-auto"
/>
</AssetCard>
{/* Logo with text - light */}
<AssetCard
title="Logo with Name"
description="For light backgrounds"
bgClass="bg-gray-100"
downloadUrl="/logo.svg"
filename="prompts-chat-logo.svg"
>
<div className="flex items-center gap-3">
<Image
src="/logo.svg"
alt="prompts.chat logo"
width={48}
height={48}
className="h-12 w-auto"
/>
<span className="text-2xl font-bold text-gray-900">{branding.name}</span>
</div>
</AssetCard>
{/* Logo with text - dark */}
<AssetCard
title="Logo with Name"
description="For dark backgrounds"
bgClass="bg-gray-900"
downloadUrl="/logo-dark.svg"
filename="prompts-chat-logo-dark.svg"
>
<div className="flex items-center gap-3">
<Image
src="/logo-dark.svg"
alt="prompts.chat logo dark"
width={48}
height={48}
className="h-12 w-auto"
/>
<span className="text-2xl font-bold text-white">{branding.name}</span>
</div>
</AssetCard>
</div>
</section>
{/* Colors Section */}
<section>
<h2 className="text-xl font-semibold mb-4">Brand Colors</h2>
<p className="text-sm text-muted-foreground mb-4">Click to copy hex value</p>
<div className="grid gap-2">
<ColorCard
color="#000000"
name="Primary"
description="Primary brand color"
/>
<ColorCard
color="#ffffff"
name="Background"
description="Light background"
/>
<ColorCard
color="#6366f1"
name="Accent"
description="Indigo accent color"
/>
<ColorCard
color="#71717a"
name="Muted"
description="Muted text color"
/>
</div>
</section>
{/* Usage Guidelines */}
<section>
<h2 className="text-xl font-semibold mb-4">Usage Guidelines</h2>
<div className="prose prose-neutral dark:prose-invert max-w-none">
<ul className="list-disc list-inside text-muted-foreground space-y-2">
<li>Do not stretch, distort, or rotate the logo</li>
<li>Maintain adequate spacing around the logo</li>
<li>Use the dark logo on light backgrounds and vice versa</li>
<li>Do not add effects like shadows or gradients to the logo</li>
<li>The logo should be clearly visible against the background</li>
</ul>
</div>
</section>
{/* License */}
<section>
<h2 className="text-xl font-semibold mb-4">License</h2>
<p className="text-muted-foreground">
The {branding.name} brand assets are provided under{" "}
<a
href="https://creativecommons.org/publicdomain/zero/1.0/"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
CC0 1.0 Universal
</a>
. You are free to use these assets for any purpose without attribution.
</p>
</section>
</div>
</div>
);
}

View File

@@ -5,6 +5,7 @@ import { getTranslations } from "next-intl/server";
import { ArrowLeft } from "lucide-react";
import { auth } from "@/lib/auth";
import { db } from "@/lib/db";
import config from "@/../prompts.config";
import { Button } from "@/components/ui/button";
import { PromptList } from "@/components/prompts/prompt-list";
import { SubscribeButton } from "@/components/categories/subscribe-button";
@@ -139,7 +140,7 @@ export default async function CategoryPage({ params }: CategoryPageProps) {
<span>{category._count.subscribers} subscribers</span>
</div>
</div>
<McpServerPopup initialCategories={[slug]} />
{config.features.mcp !== false && <McpServerPopup initialCategories={[slug]} />}
</div>
</div>

View File

@@ -10,10 +10,11 @@ import {
TableRow,
} from "@/components/ui/table";
import { McpConfigTabs } from "@/components/mcp/mcp-config-tabs";
import config from "@/../prompts.config";
export const metadata = {
title: "API Documentation - prompts.chat",
description: "MCP-first API for searching and discovering AI prompts programmatically",
description: "API for searching and discovering AI prompts programmatically",
};
export default async function ApiDocsPage() {
@@ -25,83 +26,87 @@ export default async function ApiDocsPage() {
<div className="container max-w-4xl py-10">
<h1 className="text-2xl font-bold mb-2">API Documentation</h1>
<p className="text-muted-foreground mb-8">
prompts.chat provides an MCP-first API for searching and discovering AI prompts programmatically.
Use the MCP endpoint directly with any MCP-compatible client, or make standard HTTP requests.
{config.features.mcp !== false
? "prompts.chat provides an MCP-first API for searching and discovering AI prompts programmatically. Use the MCP endpoint directly with any MCP-compatible client, or make standard HTTP requests."
: "prompts.chat provides an API for searching and discovering AI prompts programmatically."
}
</p>
<div className="prose prose-neutral dark:prose-invert max-w-none space-y-10">
{/* MCP Overview */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Zap className="h-5 w-5" />
MCP-First API
</h2>
<p className="text-muted-foreground">
Our API is built on the{" "}
<Link
href="https://modelcontextprotocol.io"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
Model Context Protocol (MCP)
</Link>
, enabling seamless integration with AI assistants, IDEs, and automation tools.
The same endpoint works for both MCP clients and traditional REST-style requests.
</p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
<p className="text-muted-foreground"># MCP Endpoint</p>
<p>POST {baseUrl}/api/mcp</p>
</div>
</section>
{/* Using with MCP Clients */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Terminal className="h-5 w-5" />
Using with MCP Clients
</h2>
<p className="text-muted-foreground">
Add prompts.chat to your MCP client configuration. Choose your client and connection type below:
</p>
<McpConfigTabs baseUrl={baseUrl} className="[&_button]:text-sm [&_button]:px-3 [&_button]:py-1.5 [&_pre]:text-sm [&_pre]:p-4" />
<p className="text-muted-foreground text-sm">
<strong>Remote</strong> connects directly to prompts.chat API. <strong>Local</strong> runs the MCP server locally via npx.
</p>
</section>
{/* Authentication */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Key className="h-5 w-5" />
Authentication
</h2>
<p className="text-muted-foreground">
Most API features work without authentication. However, to save prompts via MCP or access your private prompts,
you need to authenticate using an API key.
</p>
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
<div>
<p className="font-medium">Generate an API Key</p>
{config.features.mcp !== false && (
<>
{/* MCP Overview */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Zap className="h-5 w-5" />
MCP-First API
</h2>
<p className="text-muted-foreground">
Go to{" "}
<Link href="/settings" className="underline hover:text-foreground">
Settings
Our API is built on the{" "}
<Link
href="https://modelcontextprotocol.io"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
Model Context Protocol (MCP)
</Link>
{" "}to generate your API key. Keys start with <code className="bg-muted px-1.5 py-0.5 rounded">pchat_</code>.
, enabling seamless integration with AI assistants, IDEs, and automation tools.
The same endpoint works for both MCP clients and traditional REST-style requests.
</p>
</div>
<div>
<p className="font-medium">Using the API Key</p>
<p className="text-muted-foreground">
Pass your API key via the <code className="bg-muted px-1.5 py-0.5 rounded">PROMPTS_API_KEY</code> header.
</p>
</div>
</div>
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
<p className="text-muted-foreground"># MCP Endpoint</p>
<p>POST {baseUrl}/api/mcp</p>
</div>
</section>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`# Remote: HTTP transport with headers
{/* Using with MCP Clients */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Terminal className="h-5 w-5" />
Using with MCP Clients
</h2>
<p className="text-muted-foreground">
Add prompts.chat to your MCP client configuration. Choose your client and connection type below:
</p>
<McpConfigTabs baseUrl={baseUrl} className="[&_button]:text-sm [&_button]:px-3 [&_button]:py-1.5 [&_pre]:text-sm [&_pre]:p-4" />
<p className="text-muted-foreground text-sm">
<strong>Remote</strong> connects directly to prompts.chat API. <strong>Local</strong> runs the MCP server locally via npx.
</p>
</section>
{/* Authentication */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Key className="h-5 w-5" />
Authentication
</h2>
<p className="text-muted-foreground">
Most API features work without authentication. However, to save prompts via MCP or access your private prompts,
you need to authenticate using an API key.
</p>
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
<div>
<p className="font-medium">Generate an API Key</p>
<p className="text-muted-foreground">
Go to{" "}
<Link href="/settings" className="underline hover:text-foreground">
Settings
</Link>
{" "}to generate your API key. Keys start with <code className="bg-muted px-1.5 py-0.5 rounded">pchat_</code>.
</p>
</div>
<div>
<p className="font-medium">Using the API Key</p>
<p className="text-muted-foreground">
Pass your API key via the <code className="bg-muted px-1.5 py-0.5 rounded">PROMPTS_API_KEY</code> header.
</p>
</div>
</div>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`# Remote: HTTP transport with headers
"prompts-chat": {
"url": "${baseUrl}/api/mcp",
"headers": {
@@ -123,30 +128,30 @@ curl -X POST ${baseUrl}/api/mcp \\
-H "Content-Type: application/json" \\
-H "PROMPTS_API_KEY: pchat_your_api_key_here" \\
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'`}</pre>
</div>
</div>
<p className="text-muted-foreground text-sm">
<strong>Remote (HTTP)</strong> sends requests to prompts.chat with the API key in headers.
<strong> Local (stdio)</strong> runs the MCP server locally via npx with the API key as an environment variable.
With authentication, you can use the <code className="bg-muted px-1.5 py-0.5 rounded">save_prompt</code> tool
and search results will include your private prompts.
</p>
</section>
<p className="text-muted-foreground text-sm">
<strong>Remote (HTTP)</strong> sends requests to prompts.chat with the API key in headers.
<strong> Local (stdio)</strong> runs the MCP server locally via npx with the API key as an environment variable.
With authentication, you can use the <code className="bg-muted px-1.5 py-0.5 rounded">save_prompt</code> tool
and search results will include your private prompts.
</p>
</section>
{/* MCP Prompts */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Terminal className="h-5 w-5" />
MCP Prompts
</h2>
<p className="text-muted-foreground">
All public prompts are exposed as native MCP prompts. This allows MCP clients to list
and use prompts directly via slash commands or prompt pickers. You can filter prompts
by category or tag using URL query parameters.
</p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`# Filter by users (one or more usernames)
{/* MCP Prompts */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Terminal className="h-5 w-5" />
MCP Prompts
</h2>
<p className="text-muted-foreground">
All public prompts are exposed as native MCP prompts. This allows MCP clients to list
and use prompts directly via slash commands or prompt pickers. You can filter prompts
by category or tag using URL query parameters.
</p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`# Filter by users (one or more usernames)
"prompts-chat": {
"url": "${baseUrl}/api/mcp?users=f,torvalds"
}
@@ -165,24 +170,24 @@ curl -X POST ${baseUrl}/api/mcp \\
"prompts-chat": {
"url": "${baseUrl}/api/mcp?users=f&categories=coding&tags=js"
}`}</pre>
</div>
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
<div>
<p className="font-medium">prompts/list</p>
<p className="text-muted-foreground">Browse all available prompts with pagination support.</p>
</div>
<div>
<p className="font-medium">prompts/get</p>
<p className="text-muted-foreground">
Retrieve a prompt by ID. Variables ({"${name}"} or {"${name:default}"}) are automatically
substituted with provided arguments.
</p>
</div>
</div>
</div>
<div className="bg-muted/50 rounded-lg p-4 text-sm space-y-3">
<div>
<p className="font-medium">prompts/list</p>
<p className="text-muted-foreground">Browse all available prompts with pagination support.</p>
</div>
<div>
<p className="font-medium">prompts/get</p>
<p className="text-muted-foreground">
Retrieve a prompt by ID. Variables ({"${name}"} or {"${name:default}"}) are automatically
substituted with provided arguments.
</p>
</div>
</div>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`# List prompts
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`# List prompts
curl -X POST ${baseUrl}/api/mcp \\
-H "Content-Type: application/json" \\
-H "Accept: application/json, text/event-stream" \\
@@ -201,15 +206,15 @@ curl -X POST ${baseUrl}/api/mcp \\
"arguments": { "topic": "AI safety" }
}
}'`}</pre>
</div>
</section>
</div>
</section>
{/* Available Tools */}
<section className="space-y-6">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Box className="h-5 w-5" />
Available Tools
</h2>
{/* Available Tools */}
<section className="space-y-6">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Box className="h-5 w-5" />
Available Tools
</h2>
{/* search_prompts Tool */}
<div className="space-y-4">
@@ -408,17 +413,17 @@ curl -X POST ${baseUrl}/api/mcp \\
</div>
</section>
{/* MCP Protocol Example */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Code className="h-5 w-5" />
MCP Protocol Examples
</h2>
{/* MCP Protocol Example */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Code className="h-5 w-5" />
MCP Protocol Examples
</h2>
<div className="space-y-3">
<h3 className="font-medium">Initialize Connection</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
<div className="space-y-3">
<h3 className="font-medium">Initialize Connection</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
-H "Content-Type: application/json" \\
-H "Accept: application/json, text/event-stream" \\
-d '{
@@ -431,13 +436,13 @@ curl -X POST ${baseUrl}/api/mcp \\
"clientInfo": { "name": "my-app", "version": "1.0.0" }
}
}'`}</pre>
</div>
</div>
</div>
</div>
<div className="space-y-3">
<h3 className="font-medium">List Available Tools</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
<div className="space-y-3">
<h3 className="font-medium">List Available Tools</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
-H "Content-Type: application/json" \\
-H "Accept: application/json, text/event-stream" \\
-d '{
@@ -445,13 +450,13 @@ curl -X POST ${baseUrl}/api/mcp \\
"id": 2,
"method": "tools/list"
}'`}</pre>
</div>
</div>
</div>
</div>
<div className="space-y-3">
<h3 className="font-medium">Search Prompts</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
<div className="space-y-3">
<h3 className="font-medium">Search Prompts</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`curl -X POST ${baseUrl}/api/mcp \\
-H "Content-Type: application/json" \\
-H "Accept: application/json, text/event-stream" \\
-d '{
@@ -466,18 +471,18 @@ curl -X POST ${baseUrl}/api/mcp \\
}
}
}'`}</pre>
</div>
</div>
</section>
</div>
</div>
</section>
{/* Response Format */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">Response Format</h2>
<p className="text-muted-foreground">
The <code className="bg-muted px-1.5 py-0.5 rounded text-sm">search_prompts</code> tool returns results in the following format:
</p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`{
{/* Response Format */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">Response Format</h2>
<p className="text-muted-foreground">
The <code className="bg-muted px-1.5 py-0.5 rounded text-sm">search_prompts</code> tool returns results in the following format:
</p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`{
"query": "code review",
"count": 2,
"prompts": [
@@ -495,14 +500,16 @@ curl -X POST ${baseUrl}/api/mcp \\
}
]
}`}</pre>
</div>
</section>
</div>
</section>
</>
)}
{/* REST API Alternative */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">REST API Alternative</h2>
<h2 className="text-lg font-semibold">REST API</h2>
<p className="text-muted-foreground">
For simpler integrations, you can also use the standard REST endpoint:
Use the standard REST endpoint to search and retrieve prompts:
</p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm overflow-x-auto">
<pre>{`# Search prompts via REST
@@ -513,17 +520,6 @@ curl "${baseUrl}/api/prompts/{id}"`}</pre>
</div>
</section>
{/* Server Info */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">Server Information</h2>
<p className="text-muted-foreground">
Get MCP server information and available tools:
</p>
<div className="bg-muted rounded-lg p-4 font-mono text-sm">
<p>GET {baseUrl}/api/mcp</p>
</div>
</section>
{/* Rate Limits */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">Rate Limits</h2>

View File

@@ -2,6 +2,7 @@ import Link from "next/link";
import Image from "next/image";
import { Server, Database, Key, Palette, Globe, Settings, HardDrive, Cpu, BookOpen } from "lucide-react";
import DeepWikiIcon from "@/../public/deepwiki.svg";
import Context7Icon from "@/../public/context7.svg";
import {
Table,
TableBody,
@@ -41,47 +42,83 @@ export default function SelfHostingPage() {
</ul>
</section>
{/* DeepWiki */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Image src={DeepWikiIcon} alt="" width={20} height={20} />
DeepWiki
</h2>
<p className="text-muted-foreground">
<Link
href="https://deepwiki.com/f/awesome-chatgpt-prompts"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
DeepWiki
</Link>
{" "}provides AI-powered documentation and insights for this repository. It automatically generates comprehensive documentation from the codebase, making it easier to understand the project structure, APIs, and implementation details.
</p>
<ul className="list-disc list-inside text-muted-foreground space-y-1">
<li>AI-generated documentation from source code</li>
<li>Interactive codebase exploration</li>
<li>Architecture diagrams and component relationships</li>
<li>Ask questions about the codebase</li>
</ul>
{/* Using Documentation AI-Agents */}
<section className="space-y-6">
<h2 className="text-xl font-bold">Using Documentation AI-Agents</h2>
<div className="grid md:grid-cols-2 gap-6">
{/* DeepWiki */}
<div className="space-y-4">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Image src={DeepWikiIcon} alt="" width={20} height={20} />
DeepWiki
</h3>
<p className="text-muted-foreground">
<Link
href="https://deepwiki.com/f/awesome-chatgpt-prompts"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
DeepWiki
</Link>
{" "}provides AI-powered documentation and insights for this repository.
</p>
<ul className="list-disc list-inside text-muted-foreground space-y-1 text-sm">
<li>AI-generated documentation from source code</li>
<li>Interactive codebase exploration</li>
<li>Architecture diagrams and component relationships</li>
<li>Available as an MCP server</li>
</ul>
</div>
{/* Context7 */}
<div className="space-y-4">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Image src={Context7Icon} alt="" width={20} height={20} className="rounded" />
Context7
</h3>
<p className="text-muted-foreground">
<Link
href="https://context7.com/f/awesome-chatgpt-prompts?tab=chat"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
Context7
</Link>
{" "}is an AI-powered chat interface for exploring and understanding this repository.
</p>
<ul className="list-disc list-inside text-muted-foreground space-y-1 text-sm">
<li>Chat with the codebase using natural language</li>
<li>Get answers with code references</li>
<li>Understand implementation details quickly</li>
<li>Available as an MCP server</li>
</ul>
</div>
</div>
</section>
{/* Prerequisites */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Database className="h-5 w-5" />
Prerequisites
</h2>
<ul className="list-disc list-inside text-muted-foreground space-y-1">
<li>Node.js 18+</li>
<li>PostgreSQL database</li>
<li>npm or yarn</li>
</ul>
</section>
{/* Manually */}
<section className="space-y-6">
<h2 className="text-xl font-bold">Manually</h2>
{/* Installation */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">Quick Start</h2>
{/* Prerequisites */}
<div className="space-y-4">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Database className="h-5 w-5" />
Prerequisites
</h3>
<ul className="list-disc list-inside text-muted-foreground space-y-1">
<li>Node.js 18+</li>
<li>PostgreSQL database</li>
<li>npm or yarn</li>
</ul>
</div>
{/* Installation */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">Quick Start</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm space-y-1 overflow-x-auto">
<p className="text-muted-foreground"># Clone the repository</p>
<p>git clone https://github.com/f/awesome-chatgpt-prompts.git</p>
@@ -96,14 +133,14 @@ export default function SelfHostingPage() {
<p className="text-muted-foreground mt-3"># Start development server</p>
<p>npm run dev</p>
</div>
</section>
</div>
{/* Environment Variables */}
<section className="space-y-6">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Key className="h-5 w-5" />
Environment Variables
</h2>
{/* Environment Variables */}
<div className="space-y-6">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Key className="h-5 w-5" />
Environment Variables
</h3>
<p className="text-muted-foreground">
Create a <code className="bg-muted px-1.5 py-0.5 rounded text-sm">.env</code> file based on <code className="bg-muted px-1.5 py-0.5 rounded text-sm">.env.example</code>:
</p>
@@ -280,14 +317,14 @@ export default function SelfHostingPage() {
</Table>
</div>
</div>
</section>
</div>
{/* Configuration */}
<section className="space-y-6">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Palette className="h-5 w-5" />
Configuration (prompts.config.ts)
</h2>
{/* Configuration */}
<div className="space-y-6">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Palette className="h-5 w-5" />
Configuration (prompts.config.ts)
</h3>
<p className="text-muted-foreground">
Customize your instance by editing <code className="bg-muted px-1.5 py-0.5 rounded text-sm">prompts.config.ts</code>:
</p>
@@ -508,14 +545,14 @@ export default function SelfHostingPage() {
</Table>
</div>
</div>
</section>
</div>
{/* White-label */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Settings className="h-5 w-5" />
White-Label Mode
</h2>
{/* White-label */}
<div className="space-y-4">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Settings className="h-5 w-5" />
White-Label Mode
</h3>
<p className="text-muted-foreground">
Set <code className="bg-muted px-1.5 py-0.5 rounded text-sm">useCloneBranding = true</code> at the top of the config to:
</p>
@@ -530,14 +567,14 @@ export default function SelfHostingPage() {
<p className="text-muted-foreground">// prompts.config.ts</p>
<p>const useCloneBranding = true;</p>
</div>
</section>
</div>
{/* Production */}
<section className="space-y-4">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Globe className="h-5 w-5" />
Production Deployment
</h2>
{/* Production */}
<div className="space-y-4">
<h3 className="text-lg font-semibold flex items-center gap-2">
<Globe className="h-5 w-5" />
Production Deployment
</h3>
<div className="bg-muted rounded-lg p-4 font-mono text-sm space-y-1">
<p>npm run build</p>
<p>npm run start</p>
@@ -545,11 +582,11 @@ export default function SelfHostingPage() {
<p className="text-muted-foreground">
Deploy to Vercel, Railway, Render, or any Node.js hosting platform. Make sure to set all environment variables in your hosting provider&apos;s dashboard.
</p>
</section>
</div>
{/* Support */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">Support</h2>
{/* Support */}
<div className="space-y-4">
<h3 className="text-lg font-semibold">Support</h3>
<p className="text-muted-foreground">
For issues and questions, please open a{" "}
<Link
@@ -570,7 +607,8 @@ export default function SelfHostingPage() {
SELF-HOSTING.md
</Link>
{" "}file in the repository.
</p>
</p>
</div>
</section>
</div>
</div>

View File

@@ -183,7 +183,7 @@ export default async function PromptsPage({ searchParams }: PromptsPageProps) {
{!config.homepage?.useCloneBranding && (
<div className="flex items-center gap-2">
<HFDataStudioDropdown aiGenerationEnabled={aiGenerationAvailable} />
<McpServerPopup />
{config.features.mcp !== false && <McpServerPopup />}
</div>
)}
<Button size="sm" className="h-8 text-xs w-full sm:w-auto" asChild>

View File

@@ -2,6 +2,7 @@ import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { auth } from "@/lib/auth";
import { db } from "@/lib/db";
import config from "@/../prompts.config";
import { ProfileForm } from "@/components/settings/profile-form";
import { ApiKeySettings } from "@/components/settings/api-key-settings";
@@ -41,10 +42,12 @@ export default async function SettingsPage() {
<div className="space-y-6">
<ProfileForm user={user} />
<ApiKeySettings
initialApiKey={user.apiKey}
initialPublicByDefault={user.mcpPromptsPublicByDefault}
/>
{config.features.mcp !== false && (
<ApiKeySettings
initialApiKey={user.apiKey}
initialPublicByDefault={user.mcpPromptsPublicByDefault}
/>
)}
</div>
</div>
);

View File

@@ -4,6 +4,7 @@ import { getTranslations } from "next-intl/server";
import { ArrowLeft, Tag } from "lucide-react";
import { db } from "@/lib/db";
import { auth } from "@/lib/auth";
import config from "@/../prompts.config";
import { Button } from "@/components/ui/button";
import { PromptCard } from "@/components/prompts/prompt-card";
import { McpServerPopup } from "@/components/mcp/mcp-server-popup";
@@ -124,7 +125,7 @@ export default async function TagPage({ params, searchParams }: TagPageProps) {
{total} {t("prompts")}
</span>
</div>
<McpServerPopup initialTags={[slug]} />
{config.features.mcp !== false && <McpServerPopup initialTags={[slug]} />}
</div>
</div>

View File

@@ -3,6 +3,7 @@
import { useState } from "react";
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useSession, signOut } from "next-auth/react";
import { useTranslations } from "next-intl";
import {
@@ -15,6 +16,8 @@ import {
Globe,
Moon,
Sun,
Copy,
ExternalLink,
} from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
@@ -66,10 +69,33 @@ export function Header({ authProvider = "credentials", allowRegistration = true
const t = useTranslations();
const { theme, setTheme } = useTheme();
const branding = useBranding();
const router = useRouter();
const user = session?.user;
const isAdmin = user?.role === "ADMIN";
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [logoMenuOpen, setLogoMenuOpen] = useState(false);
const [logoMenuPosition, setLogoMenuPosition] = useState({ x: 0, y: 0 });
const handleLogoContextMenu = (e: React.MouseEvent) => {
if (branding.useCloneBranding) return;
e.preventDefault();
setLogoMenuPosition({ x: e.clientX, y: e.clientY });
setLogoMenuOpen(true);
};
const handleCopyLogoSvg = async () => {
try {
const logoUrl = theme === "dark" ? (branding.logoDark || branding.logo) : branding.logo;
if (!logoUrl) return;
const response = await fetch(logoUrl);
const svgContent = await response.text();
await navigator.clipboard.writeText(svgContent);
setLogoMenuOpen(false);
} catch (error) {
console.error("Failed to copy logo:", error);
}
};
return (
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
@@ -168,7 +194,7 @@ export function Header({ authProvider = "credentials", allowRegistration = true
</Sheet>
{/* Logo */}
<Link href="/" className="flex gap-2">
<Link href="/" className="flex gap-2" onContextMenu={handleLogoContextMenu}>
{branding.logo && (
<>
<Image
@@ -190,6 +216,34 @@ export function Header({ authProvider = "credentials", allowRegistration = true
<span className="font-semibold leading-none">{branding.name}</span>
</Link>
{/* Logo context menu */}
{!branding.useCloneBranding && (
<DropdownMenu open={logoMenuOpen} onOpenChange={setLogoMenuOpen}>
<DropdownMenuTrigger asChild>
<span className="sr-only">Logo menu</span>
</DropdownMenuTrigger>
<DropdownMenuContent
style={{
position: "fixed",
left: logoMenuPosition.x,
top: logoMenuPosition.y,
}}
>
<DropdownMenuItem onClick={handleCopyLogoSvg}>
<Copy className="mr-2 h-4 w-4" />
Copy Logo SVG
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {
router.push("/brand");
setLogoMenuOpen(false);
}}>
<ExternalLink className="mr-2 h-4 w-4" />
Brand Assets
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
{/* Desktop nav */}
<nav className="hidden md:flex items-center gap-1 text-sm">
{user && (

View File

@@ -45,6 +45,7 @@ export interface FeaturesConfig {
tags: boolean;
aiSearch?: boolean;
aiGeneration?: boolean;
mcp?: boolean;
}
export interface Sponsor {