# Directory Structure ``` ├── .eslintrc.json ├── .gitignore ├── app │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── components │ ├── Base.tsx │ └── ui │ ├── accordion.tsx │ └── button.tsx ├── components.json ├── functions │ └── get_geo.ts ├── lib │ └── utils.ts ├── LICENSE ├── mcp-server │ ├── .gitignore │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── public │ └── next.svg ├── README_zh-CN.md ├── README.md ├── tailwind.config.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- ```json { "extends": "next/core-web-vitals" } ``` -------------------------------------------------------------------------------- /mcp-server/.gitignore: -------------------------------------------------------------------------------- ``` # Dependencies node_modules/ # Build output dist/ # Environment variables .env .env.local .env.* # NPM .npmrc .npmrc.local .npmrc.* .npm # IDE .idea/ .vscode/ *.sublime-* # Logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # System files .DS_Store Thumbs.db test.ts ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js .yarn/install-state.gz # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts # Tencent Cloud Edge Functions .env .edgeone edgeone.json ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # EdgeOne Pages MCP: Geo Location Service This project demonstrates how to use EdgeOne Pages Functions to retrieve user geolocation information and integrate it with large language models through MCP (Model Context Protocol). ## Demo  ## Deploy [](https://edgeone.ai/pages/new?template=mcp-geo) More Templates: [EdgeOne Pages](https://edgeone.ai/pages/templates) ## Components ### 1. EdgeOne Pages Functions: Geolocation The project includes an EdgeOne Pages Function that retrieves user geolocation information: * Uses the EdgeOne request context to access geolocation data * Returns location information in a JSON format * Located in `functions/get_geo.ts` ### 2. MCP Server Integration The MCP server component provides an interface for large language models to access geolocation data: * Implements the Model Context Protocol (MCP) * Exposes a `get_geolocation` tool that can be used by AI models * Uses the EdgeOne Pages Function to fetch geolocation data * Located in `mcp-server/index.ts` ## MCP Configuration To use the MCP server with large language models, add the following configuration: ```json { "mcpServers": { "edgeone-geo-mcp-server": { "command": "tsx", "args": ["path/to/mcp-server/index.ts"] } } } ``` ## Learn More * [EdgeOne Pages](https://edgeone.ai/products/pages) * [EdgeOne Pages Functions documentation](https://edgeone.ai/document/162227908259442688) * [Model Context Protocol (MCP)](https://modelcontextprotocol.github.io) - Learn about integrating AI models with external tools and services ``` -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- ``` /** @type {import('next').NextConfig} */ const nextConfig = { output: 'export', }; export default nextConfig; ``` -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- ```typescript import { Base } from '@/components/Base'; export default function Home() { return ( <main> <Base /> </main> ); } ``` -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- ``` /** @type {import('postcss-load-config').Config} */ const config = { plugins: { tailwindcss: {}, }, }; export default config; ``` -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- ```typescript import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } ``` -------------------------------------------------------------------------------- /functions/get_geo.ts: -------------------------------------------------------------------------------- ```typescript export function onRequest({ request }: { request: any }) { const geo = request.eo.geo; const res = JSON.stringify({ geo: geo, }); return new Response(res, { headers: { 'content-type': 'application/json; charset=UTF-8', 'Access-Control-Allow-Origin': '*', }, }); } ``` -------------------------------------------------------------------------------- /mcp-server/package.json: -------------------------------------------------------------------------------- ```json { "name": "mcp-geo-server", "version": "0.0.1", "description": "A tool to get user geolocation information", "main": "index.ts", "type": "module", "scripts": {}, "keywords": [], "author": "EdgeOne Pages", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.7.0", "zod": "^3.24.2" } } ``` -------------------------------------------------------------------------------- /mcp-server/tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2020", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "./dist", "rootDir": "./", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["index.ts"], "exclude": ["node_modules", "dist"] } ``` -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- ```json { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", "rsc": true, "tsx": true, "tailwind": { "config": "tailwind.config.ts", "css": "app/globals.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" }, "aliases": { "components": "@/components", "utils": "@/lib/utils", "ui": "@/components/ui", "lib": "@/lib", "hooks": "@/hooks" } } ``` -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- ```typescript import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "EdgeOne Pages MCP: Geo Location Service", description: "A demo project showcasing EdgeOne Pages Functions for geolocation information and MCP integration", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en"> <body className={inter.className}>{children}</body> </html> ); } ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "plugins": [ { "name": "next" } ], "paths": { "@/*": ["./*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules", "mcp-server"] } ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "functions-geo", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "postinstall": "cd mcp-server && npm install" }, "dependencies": { "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-slot": "^1.1.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.438.0", "next": "14.2.5", "react": "^18", "react-dom": "^18", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", "@jridgewell/gen-mapping": "0.3.5" }, "devDependencies": { "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", "eslint-config-next": "14.2.5", "postcss": "^8", "tailwindcss": "^3.4.1", "typescript": "^5" } } ``` -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- ``` <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg> ``` -------------------------------------------------------------------------------- /mcp-server/index.ts: -------------------------------------------------------------------------------- ```typescript #!/usr/bin/env node import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; const server = new McpServer({ name: 'edgeone-geo-mcp-server', version: '1.0.0', description: 'An MCP service for getting user geolocation information', }); const handleApiError = (error: any) => { console.error('API Error:', error); const errorMessage = error.message || 'Unknown error occurred'; return { content: [ { type: 'text' as const, text: `Error: ${errorMessage}`, }, ], isError: true, }; }; export async function getGeoLocation(): Promise<any> { try { const res = await fetch('https://mcp-geo.edgeone.app/get_geo'); if (!res.ok) { throw new Error(`HTTP error: ${res.status} ${res.statusText}`); } return await res.json(); } catch (error) { console.error('Failed to get geolocation:', error); throw error; } } server.tool( 'get_geolocation', "Get the user's geolocation information", {}, async () => { try { const geoData = await getGeoLocation(); return { content: [ { type: 'text' as const, text: JSON.stringify(geoData, null, 2), }, ], }; } catch (e) { return handleApiError(e); } } ); console.log('Starting edgeone-geo-mcp-server...'); const transport = new StdioServerTransport(); await server.connect(transport); console.log('edgeone-geo-mcp-server started successfully!'); ``` -------------------------------------------------------------------------------- /components/ui/button.tsx: -------------------------------------------------------------------------------- ```typescript import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const buttonVariants = cva( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, size: { default: "h-9 px-4 py-2", sm: "h-8 rounded-md px-3 text-xs", lg: "h-10 rounded-md px-8", icon: "h-9 w-9", }, }, defaultVariants: { variant: "default", size: "default", }, } ) export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { asChild?: boolean } const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button" return ( <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} /> ) } ) Button.displayName = "Button" export { Button, buttonVariants } ``` -------------------------------------------------------------------------------- /components/ui/accordion.tsx: -------------------------------------------------------------------------------- ```typescript "use client" import * as React from "react" import * as AccordionPrimitive from "@radix-ui/react-accordion" import { ChevronDownIcon } from "@radix-ui/react-icons" import { cn } from "@/lib/utils" const Accordion = AccordionPrimitive.Root const AccordionItem = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Item>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> >(({ className, ...props }, ref) => ( <AccordionPrimitive.Item ref={ref} className={cn("border-b", className)} {...props} /> )) AccordionItem.displayName = "AccordionItem" const AccordionTrigger = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Trigger>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> >(({ className, children, ...props }, ref) => ( <AccordionPrimitive.Header className="flex"> <AccordionPrimitive.Trigger ref={ref} className={cn( "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180", className )} {...props} > {children} <ChevronDownIcon className="w-4 h-4 transition-transform duration-200 shrink-0 text-muted-foreground" /> </AccordionPrimitive.Trigger> </AccordionPrimitive.Header> )) AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName const AccordionContent = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Content>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> >(({ className, children, ...props }, ref) => ( <AccordionPrimitive.Content ref={ref} className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down" {...props} > <div className={cn("pb-4 pt-0", className)}>{children}</div> </AccordionPrimitive.Content> )) AccordionContent.displayName = AccordionPrimitive.Content.displayName export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } ``` -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- ```css @tailwind base; @tailwind components; @tailwind utilities; :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; } @media (prefers-color-scheme: dark) { :root { --foreground-rgb: 255, 255, 255; --background-start-rgb: 0, 0, 0; --background-end-rgb: 0, 0, 0; } } @layer utilities { .text-balance { text-wrap: balance; } } @layer base { :root { --background: 0 0% 100%; --foreground: 0 0% 3.9%; --card: 0 0% 100%; --card-foreground: 0 0% 3.9%; --popover: 0 0% 100%; --popover-foreground: 0 0% 3.9%; --primary: 0 0% 9%; --primary-foreground: 0 0% 98%; --secondary: 0 0% 96.1%; --secondary-foreground: 0 0% 9%; --muted: 0 0% 96.1%; --muted-foreground: 0 0% 45.1%; --accent: 0 0% 96.1%; --accent-foreground: 0 0% 9%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; --border: 0 0% 89.8%; --input: 0 0% 89.8%; --ring: 0 0% 3.9%; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; } .dark { --background: 0 0% 3.9%; --foreground: 0 0% 98%; --card: 0 0% 3.9%; --card-foreground: 0 0% 98%; --popover: 0 0% 3.9%; --popover-foreground: 0 0% 98%; --primary: 0 0% 98%; --primary-foreground: 0 0% 9%; --secondary: 0 0% 14.9%; --secondary-foreground: 0 0% 98%; --muted: 0 0% 14.9%; --muted-foreground: 0 0% 63.9%; --accent: 0 0% 14.9%; --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 0 0% 98%; --border: 0 0% 14.9%; --input: 0 0% 14.9%; --ring: 0 0% 83.1%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } } ``` -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- ```typescript import type { Config } from "tailwindcss"; const config: Config = { darkMode: ["class"], content: [ "./pages/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { extend: { backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))' }, borderRadius: { lg: 'var(--radius)', md: 'calc(var(--radius) - 2px)', sm: 'calc(var(--radius) - 4px)' }, colors: { background: 'hsl(var(--background))', foreground: 'hsl(var(--foreground))', card: { DEFAULT: 'hsl(var(--card))', foreground: 'hsl(var(--card-foreground))' }, popover: { DEFAULT: 'hsl(var(--popover))', foreground: 'hsl(var(--popover-foreground))' }, primary: { DEFAULT: 'hsl(var(--primary))', foreground: 'hsl(var(--primary-foreground))' }, secondary: { DEFAULT: 'hsl(var(--secondary))', foreground: 'hsl(var(--secondary-foreground))' }, muted: { DEFAULT: 'hsl(var(--muted))', foreground: 'hsl(var(--muted-foreground))' }, accent: { DEFAULT: 'hsl(var(--accent))', foreground: 'hsl(var(--accent-foreground))' }, destructive: { DEFAULT: 'hsl(var(--destructive))', foreground: 'hsl(var(--destructive-foreground))' }, border: 'hsl(var(--border))', input: 'hsl(var(--input))', ring: 'hsl(var(--ring))', chart: { '1': 'hsl(var(--chart-1))', '2': 'hsl(var(--chart-2))', '3': 'hsl(var(--chart-3))', '4': 'hsl(var(--chart-4))', '5': 'hsl(var(--chart-5))' } }, keyframes: { 'accordion-down': { from: { height: '0' }, to: { height: 'var(--radix-accordion-content-height)' } }, 'accordion-up': { from: { height: 'var(--radix-accordion-content-height)' }, to: { height: '0' } } }, animation: { 'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out' } } }, plugins: [require("tailwindcss-animate")], }; export default config; ``` -------------------------------------------------------------------------------- /components/Base.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react'; export const Base = () => { return ( <div className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100 py-12 px-4 sm:px-6 lg:px-8"> <div className="max-w-4xl mx-auto"> <div className="text-center"> <h1 className="text-4xl font-bold text-gray-900 mb-4"> EdgeOne Pages MCP: Geo Location Service </h1> <p className="text-xl text-gray-600 mb-8"> A geolocation service example demonstrating how to obtain user location information in EdgeOne Pages and interact with AI models through the MCP protocol. </p> </div> {/* Project Overview */} <div className="bg-white rounded-lg shadow-lg p-6 mb-8"> <h2 className="text-2xl font-semibold text-gray-900 mb-4"> Project Overview </h2> <p className="text-gray-600 mb-4"> In modern web applications, obtaining user geolocation information is a common requirement. This project demonstrates how to: </p> <ul className="list-disc list-inside text-gray-600 space-y-2 mb-4"> <li> Securely obtain user geolocation using EdgeOne Pages Functions </li> <li> Enable AI models to access location data through the MCP protocol </li> <li>Elegantly display location information in the frontend</li> </ul> </div> {/* Core Features */} <div className="bg-white rounded-lg shadow-lg p-6 mb-8"> <h2 className="text-2xl font-semibold text-gray-900 mb-4"> Core Features </h2> <div className="grid md:grid-cols-2 gap-6"> <div className="border border-gray-200 rounded-lg p-4"> <h3 className="text-lg font-medium text-gray-900 mb-2"> EdgeOne Pages Functions </h3> <ul className="text-gray-600 space-y-2"> <li> • Automatically retrieve visitor's geolocation information </li> <li> • Includes detailed data about country, region, city, etc. </li> <li> • Precise positioning through EdgeOne's global node network </li> <li>• Returns standardized JSON format data</li> </ul> </div> <div className="border border-gray-200 rounded-lg p-4"> <h3 className="text-lg font-medium text-gray-900 mb-2"> MCP Protocol Integration </h3> <ul className="text-gray-600 space-y-2"> <li>• Implements Model Context Protocol standard</li> <li>• Provides get_geolocation tool interface</li> <li> • Supports real-time AI model calls to geolocation service </li> <li>• Convenient server-side integration solution</li> </ul> </div> </div> </div> {/* Quick Start */} <div className="text-center space-y-4"> <a href="https://edgeone.ai/pages/new?template=mcp-geo" target="_blank" rel="noopener noreferrer" aria-label="Deploy your own instance of the MCP Geo Location Service" className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 mr-4" > Deploy Now </a> <a href="https://edgeone.ai/pages/templates" target="_blank" rel="noopener noreferrer" aria-label="View more EdgeOne Pages templates" className="inline-flex items-center px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50" > View More Templates </a> </div> </div> </div> ); }; ```