#
tokens: 9344/50000 22/22 files
lines: on (toggle) GitHub
raw markdown copy reset
# 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
1 | {
2 |   "extends": "next/core-web-vitals"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/mcp-server/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | 
 4 | # Build output
 5 | dist/
 6 | 
 7 | # Environment variables
 8 | .env
 9 | .env.local
10 | .env.*
11 | 
12 | # NPM
13 | .npmrc
14 | .npmrc.local
15 | .npmrc.*
16 | .npm
17 | 
18 | # IDE
19 | .idea/
20 | .vscode/
21 | *.sublime-*
22 | 
23 | # Logs
24 | *.log
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 | 
29 | # System files
30 | .DS_Store
31 | Thumbs.db 
32 | 
33 | test.ts
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
 2 | 
 3 | # dependencies
 4 | /node_modules
 5 | /.pnp
 6 | .pnp.js
 7 | .yarn/install-state.gz
 8 | 
 9 | # testing
10 | /coverage
11 | 
12 | # next.js
13 | /.next/
14 | /out/
15 | 
16 | # production
17 | /build
18 | 
19 | # misc
20 | .DS_Store
21 | *.pem
22 | 
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 | 
28 | # local env files
29 | .env*.local
30 | 
31 | # vercel
32 | .vercel
33 | 
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 | 
38 | # Tencent Cloud Edge Functions
39 | .env
40 | .edgeone
41 | edgeone.json
42 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # EdgeOne Pages MCP: Geo Location Service
 2 | 
 3 | 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).
 4 | 
 5 | ## Demo
 6 | 
 7 | ![](https://cloudcache.tencent-cloud.com/qcloud/ui/static/static_source_business/f180b5ae-0e9c-40a8-a76a-b8f2a5e6108f.gif)
 8 | 
 9 | ## Deploy
10 | 
11 | [![Deploy with EdgeOne Pages](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?template=mcp-geo)
12 | 
13 | More Templates: [EdgeOne Pages](https://edgeone.ai/pages/templates)
14 | 
15 | ## Components
16 | 
17 | ### 1. EdgeOne Pages Functions: Geolocation
18 | 
19 | The project includes an EdgeOne Pages Function that retrieves user geolocation information:
20 | 
21 | * Uses the EdgeOne request context to access geolocation data
22 | * Returns location information in a JSON format
23 | * Located in `functions/get_geo.ts`
24 | 
25 | ### 2. MCP Server Integration
26 | 
27 | The MCP server component provides an interface for large language models to access geolocation data:
28 | 
29 | * Implements the Model Context Protocol (MCP)
30 | * Exposes a `get_geolocation` tool that can be used by AI models
31 | * Uses the EdgeOne Pages Function to fetch geolocation data
32 | * Located in `mcp-server/index.ts`
33 | 
34 | ## MCP Configuration
35 | 
36 | To use the MCP server with large language models, add the following configuration:
37 | 
38 | ```json
39 | {
40 |   "mcpServers": {
41 |     "edgeone-geo-mcp-server": {
42 |       "command": "tsx",
43 |       "args": ["path/to/mcp-server/index.ts"]
44 |     }
45 |   }
46 | }
47 | ```
48 | 
49 | ## Learn More
50 | 
51 | * [EdgeOne Pages](https://edgeone.ai/products/pages)
52 | * [EdgeOne Pages Functions documentation](https://edgeone.ai/document/162227908259442688)
53 | * [Model Context Protocol (MCP)](https://modelcontextprotocol.github.io) - Learn about integrating AI models with external tools and services
54 | 
```

--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------

```
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 |   output: 'export',
4 | };
5 | 
6 | export default nextConfig;
7 | 
```

--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import { Base } from '@/components/Base';
 2 | 
 3 | export default function Home() {
 4 |   return (
 5 |     <main>
 6 |       <Base />
 7 |     </main>
 8 |   );
 9 | }
10 | 
```

--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------

```
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 |   plugins: {
4 |     tailwindcss: {},
5 |   },
6 | };
7 | 
8 | export default config;
9 | 
```

--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { clsx, type ClassValue } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 | 
4 | export function cn(...inputs: ClassValue[]) {
5 |   return twMerge(clsx(inputs))
6 | }
7 | 
```

--------------------------------------------------------------------------------
/functions/get_geo.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export function onRequest({ request }: { request: any }) {
 2 |   const geo = request.eo.geo;
 3 |   const res = JSON.stringify({
 4 |     geo: geo,
 5 |   });
 6 | 
 7 |   return new Response(res, {
 8 |     headers: {
 9 |       'content-type': 'application/json; charset=UTF-8',
10 |       'Access-Control-Allow-Origin': '*',
11 |     },
12 |   });
13 | }
14 | 
```

--------------------------------------------------------------------------------
/mcp-server/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-geo-server",
 3 |   "version": "0.0.1",
 4 |   "description": "A tool to get user geolocation information",
 5 |   "main": "index.ts",
 6 |   "type": "module",
 7 |   "scripts": {},
 8 |   "keywords": [],
 9 |   "author": "EdgeOne Pages",
10 |   "license": "MIT",
11 |   "dependencies": {
12 |     "@modelcontextprotocol/sdk": "^1.7.0",
13 |     "zod": "^3.24.2"
14 |   }
15 | }
16 | 
```

--------------------------------------------------------------------------------
/mcp-server/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "outDir": "./dist",
 7 |     "rootDir": "./",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": ["index.ts"],
14 |   "exclude": ["node_modules", "dist"]
15 | } 
```

--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "$schema": "https://ui.shadcn.com/schema.json",
 3 |   "style": "new-york",
 4 |   "rsc": true,
 5 |   "tsx": true,
 6 |   "tailwind": {
 7 |     "config": "tailwind.config.ts",
 8 |     "css": "app/globals.css",
 9 |     "baseColor": "neutral",
10 |     "cssVariables": true,
11 |     "prefix": ""
12 |   },
13 |   "aliases": {
14 |     "components": "@/components",
15 |     "utils": "@/lib/utils",
16 |     "ui": "@/components/ui",
17 |     "lib": "@/lib",
18 |     "hooks": "@/hooks"
19 |   }
20 | }
```

--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import type { Metadata } from "next";
 2 | import { Inter } from "next/font/google";
 3 | import "./globals.css";
 4 | 
 5 | const inter = Inter({ subsets: ["latin"] });
 6 | 
 7 | export const metadata: Metadata = {
 8 |   title: "EdgeOne Pages MCP: Geo Location Service",
 9 |   description: "A demo project showcasing EdgeOne Pages Functions for geolocation information and MCP integration",
10 | };
11 | 
12 | export default function RootLayout({
13 |   children,
14 | }: Readonly<{
15 |   children: React.ReactNode;
16 | }>) {
17 |   return (
18 |     <html lang="en">
19 |       <body className={inter.className}>{children}</body>
20 |     </html>
21 |   );
22 | }
23 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "lib": ["dom", "dom.iterable", "esnext"],
 4 |     "allowJs": true,
 5 |     "skipLibCheck": true,
 6 |     "strict": true,
 7 |     "noEmit": true,
 8 |     "esModuleInterop": true,
 9 |     "module": "esnext",
10 |     "moduleResolution": "bundler",
11 |     "resolveJsonModule": true,
12 |     "isolatedModules": true,
13 |     "jsx": "preserve",
14 |     "incremental": true,
15 |     "plugins": [
16 |       {
17 |         "name": "next"
18 |       }
19 |     ],
20 |     "paths": {
21 |       "@/*": ["./*"]
22 |     }
23 |   },
24 |   "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 |   "exclude": ["node_modules", "mcp-server"]
26 | }
27 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "functions-geo",
 3 |   "version": "0.1.0",
 4 |   "private": true,
 5 |   "scripts": {
 6 |     "dev": "next dev",
 7 |     "build": "next build",
 8 |     "start": "next start",
 9 |     "lint": "next lint",
10 |     "postinstall": "cd mcp-server && npm install"
11 |   },
12 |   "dependencies": {
13 |     "@radix-ui/react-accordion": "^1.2.0",
14 |     "@radix-ui/react-icons": "^1.3.0",
15 |     "@radix-ui/react-slot": "^1.1.0",
16 |     "class-variance-authority": "^0.7.0",
17 |     "clsx": "^2.1.1",
18 |     "lucide-react": "^0.438.0",
19 |     "next": "14.2.5",
20 |     "react": "^18",
21 |     "react-dom": "^18",
22 |     "tailwind-merge": "^2.5.2",
23 |     "tailwindcss-animate": "^1.0.7",
24 |     "@jridgewell/gen-mapping": "0.3.5"
25 |   },
26 |   "devDependencies": {
27 |     "@types/node": "^20",
28 |     "@types/react": "^18",
29 |     "@types/react-dom": "^18",
30 |     "eslint": "^8",
31 |     "eslint-config-next": "14.2.5",
32 |     "postcss": "^8",
33 |     "tailwindcss": "^3.4.1",
34 |     "typescript": "^5"
35 |   }
36 | }
37 | 
```

--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------

```
1 | <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
 1 | #!/usr/bin/env node
 2 | 
 3 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
 4 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 5 | 
 6 | const server = new McpServer({
 7 |   name: 'edgeone-geo-mcp-server',
 8 |   version: '1.0.0',
 9 |   description: 'An MCP service for getting user geolocation information',
10 | });
11 | 
12 | const handleApiError = (error: any) => {
13 |   console.error('API Error:', error);
14 |   const errorMessage = error.message || 'Unknown error occurred';
15 |   return {
16 |     content: [
17 |       {
18 |         type: 'text' as const,
19 |         text: `Error: ${errorMessage}`,
20 |       },
21 |     ],
22 |     isError: true,
23 |   };
24 | };
25 | 
26 | export async function getGeoLocation(): Promise<any> {
27 |   try {
28 |     const res = await fetch('https://mcp-geo.edgeone.app/get_geo');
29 |     if (!res.ok) {
30 |       throw new Error(`HTTP error: ${res.status} ${res.statusText}`);
31 |     }
32 |     return await res.json();
33 |   } catch (error) {
34 |     console.error('Failed to get geolocation:', error);
35 |     throw error;
36 |   }
37 | }
38 | 
39 | server.tool(
40 |   'get_geolocation',
41 |   "Get the user's geolocation information",
42 |   {},
43 |   async () => {
44 |     try {
45 |       const geoData = await getGeoLocation();
46 | 
47 |       return {
48 |         content: [
49 |           {
50 |             type: 'text' as const,
51 |             text: JSON.stringify(geoData, null, 2),
52 |           },
53 |         ],
54 |       };
55 |     } catch (e) {
56 |       return handleApiError(e);
57 |     }
58 |   }
59 | );
60 | 
61 | console.log('Starting edgeone-geo-mcp-server...');
62 | const transport = new StdioServerTransport();
63 | await server.connect(transport);
64 | console.log('edgeone-geo-mcp-server started successfully!');
65 | 
```

--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import * as React from "react"
 2 | import { Slot } from "@radix-ui/react-slot"
 3 | import { cva, type VariantProps } from "class-variance-authority"
 4 | 
 5 | import { cn } from "@/lib/utils"
 6 | 
 7 | const buttonVariants = cva(
 8 |   "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",
 9 |   {
10 |     variants: {
11 |       variant: {
12 |         default:
13 |           "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14 |         destructive:
15 |           "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16 |         outline:
17 |           "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18 |         secondary:
19 |           "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20 |         ghost: "hover:bg-accent hover:text-accent-foreground",
21 |         link: "text-primary underline-offset-4 hover:underline",
22 |       },
23 |       size: {
24 |         default: "h-9 px-4 py-2",
25 |         sm: "h-8 rounded-md px-3 text-xs",
26 |         lg: "h-10 rounded-md px-8",
27 |         icon: "h-9 w-9",
28 |       },
29 |     },
30 |     defaultVariants: {
31 |       variant: "default",
32 |       size: "default",
33 |     },
34 |   }
35 | )
36 | 
37 | export interface ButtonProps
38 |   extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39 |     VariantProps<typeof buttonVariants> {
40 |   asChild?: boolean
41 | }
42 | 
43 | const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
44 |   ({ className, variant, size, asChild = false, ...props }, ref) => {
45 |     const Comp = asChild ? Slot : "button"
46 |     return (
47 |       <Comp
48 |         className={cn(buttonVariants({ variant, size, className }))}
49 |         ref={ref}
50 |         {...props}
51 |       />
52 |     )
53 |   }
54 | )
55 | Button.displayName = "Button"
56 | 
57 | export { Button, buttonVariants }
58 | 
```

--------------------------------------------------------------------------------
/components/ui/accordion.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | "use client"
 2 | 
 3 | import * as React from "react"
 4 | import * as AccordionPrimitive from "@radix-ui/react-accordion"
 5 | import { ChevronDownIcon } from "@radix-ui/react-icons"
 6 | 
 7 | import { cn } from "@/lib/utils"
 8 | 
 9 | const Accordion = AccordionPrimitive.Root
10 | 
11 | const AccordionItem = React.forwardRef<
12 |   React.ElementRef<typeof AccordionPrimitive.Item>,
13 |   React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
14 | >(({ className, ...props }, ref) => (
15 |   <AccordionPrimitive.Item
16 |     ref={ref}
17 |     className={cn("border-b", className)}
18 |     {...props}
19 |   />
20 | ))
21 | AccordionItem.displayName = "AccordionItem"
22 | 
23 | const AccordionTrigger = React.forwardRef<
24 |   React.ElementRef<typeof AccordionPrimitive.Trigger>,
25 |   React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
26 | >(({ className, children, ...props }, ref) => (
27 |   <AccordionPrimitive.Header className="flex">
28 |     <AccordionPrimitive.Trigger
29 |       ref={ref}
30 |       className={cn(
31 |         "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180",
32 |         className
33 |       )}
34 |       {...props}
35 |     >
36 |       {children}
37 |       <ChevronDownIcon className="w-4 h-4 transition-transform duration-200 shrink-0 text-muted-foreground" />
38 |     </AccordionPrimitive.Trigger>
39 |   </AccordionPrimitive.Header>
40 | ))
41 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42 | 
43 | const AccordionContent = React.forwardRef<
44 |   React.ElementRef<typeof AccordionPrimitive.Content>,
45 |   React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
46 | >(({ className, children, ...props }, ref) => (
47 |   <AccordionPrimitive.Content
48 |     ref={ref}
49 |     className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
50 |     {...props}
51 |   >
52 |     <div className={cn("pb-4 pt-0", className)}>{children}</div>
53 |   </AccordionPrimitive.Content>
54 | ))
55 | AccordionContent.displayName = AccordionPrimitive.Content.displayName
56 | 
57 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
58 | 
```

--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------

```css
 1 | @tailwind base;
 2 | @tailwind components;
 3 | @tailwind utilities;
 4 | 
 5 | :root {
 6 |   --foreground-rgb: 0, 0, 0;
 7 |   --background-start-rgb: 214, 219, 220;
 8 |   --background-end-rgb: 255, 255, 255;
 9 | }
10 | 
11 | @media (prefers-color-scheme: dark) {
12 |   :root {
13 |     --foreground-rgb: 255, 255, 255;
14 |     --background-start-rgb: 0, 0, 0;
15 |     --background-end-rgb: 0, 0, 0;
16 |   }
17 | }
18 | 
19 | @layer utilities {
20 |   .text-balance {
21 |     text-wrap: balance;
22 |   }
23 | }
24 | 
25 | @layer base {
26 |   :root {
27 |     --background: 0 0% 100%;
28 |     --foreground: 0 0% 3.9%;
29 |     --card: 0 0% 100%;
30 |     --card-foreground: 0 0% 3.9%;
31 |     --popover: 0 0% 100%;
32 |     --popover-foreground: 0 0% 3.9%;
33 |     --primary: 0 0% 9%;
34 |     --primary-foreground: 0 0% 98%;
35 |     --secondary: 0 0% 96.1%;
36 |     --secondary-foreground: 0 0% 9%;
37 |     --muted: 0 0% 96.1%;
38 |     --muted-foreground: 0 0% 45.1%;
39 |     --accent: 0 0% 96.1%;
40 |     --accent-foreground: 0 0% 9%;
41 |     --destructive: 0 84.2% 60.2%;
42 |     --destructive-foreground: 0 0% 98%;
43 |     --border: 0 0% 89.8%;
44 |     --input: 0 0% 89.8%;
45 |     --ring: 0 0% 3.9%;
46 |     --chart-1: 12 76% 61%;
47 |     --chart-2: 173 58% 39%;
48 |     --chart-3: 197 37% 24%;
49 |     --chart-4: 43 74% 66%;
50 |     --chart-5: 27 87% 67%;
51 |     --radius: 0.5rem;
52 |   }
53 |   .dark {
54 |     --background: 0 0% 3.9%;
55 |     --foreground: 0 0% 98%;
56 |     --card: 0 0% 3.9%;
57 |     --card-foreground: 0 0% 98%;
58 |     --popover: 0 0% 3.9%;
59 |     --popover-foreground: 0 0% 98%;
60 |     --primary: 0 0% 98%;
61 |     --primary-foreground: 0 0% 9%;
62 |     --secondary: 0 0% 14.9%;
63 |     --secondary-foreground: 0 0% 98%;
64 |     --muted: 0 0% 14.9%;
65 |     --muted-foreground: 0 0% 63.9%;
66 |     --accent: 0 0% 14.9%;
67 |     --accent-foreground: 0 0% 98%;
68 |     --destructive: 0 62.8% 30.6%;
69 |     --destructive-foreground: 0 0% 98%;
70 |     --border: 0 0% 14.9%;
71 |     --input: 0 0% 14.9%;
72 |     --ring: 0 0% 83.1%;
73 |     --chart-1: 220 70% 50%;
74 |     --chart-2: 160 60% 45%;
75 |     --chart-3: 30 80% 55%;
76 |     --chart-4: 280 65% 60%;
77 |     --chart-5: 340 75% 55%;
78 |   }
79 | }
80 | 
81 | @layer base {
82 |   * {
83 |     @apply border-border;
84 |   }
85 |   body {
86 |     @apply bg-background text-foreground;
87 |   }
88 | }
89 | 
```

--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { Config } from "tailwindcss";
 2 | 
 3 | const config: Config = {
 4 |     darkMode: ["class"],
 5 |     content: [
 6 |     "./pages/**/*.{js,ts,jsx,tsx,mdx}",
 7 |     "./components/**/*.{js,ts,jsx,tsx,mdx}",
 8 |     "./app/**/*.{js,ts,jsx,tsx,mdx}",
 9 |   ],
10 |   theme: {
11 |   	extend: {
12 |   		backgroundImage: {
13 |   			'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
14 |   			'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))'
15 |   		},
16 |   		borderRadius: {
17 |   			lg: 'var(--radius)',
18 |   			md: 'calc(var(--radius) - 2px)',
19 |   			sm: 'calc(var(--radius) - 4px)'
20 |   		},
21 |   		colors: {
22 |   			background: 'hsl(var(--background))',
23 |   			foreground: 'hsl(var(--foreground))',
24 |   			card: {
25 |   				DEFAULT: 'hsl(var(--card))',
26 |   				foreground: 'hsl(var(--card-foreground))'
27 |   			},
28 |   			popover: {
29 |   				DEFAULT: 'hsl(var(--popover))',
30 |   				foreground: 'hsl(var(--popover-foreground))'
31 |   			},
32 |   			primary: {
33 |   				DEFAULT: 'hsl(var(--primary))',
34 |   				foreground: 'hsl(var(--primary-foreground))'
35 |   			},
36 |   			secondary: {
37 |   				DEFAULT: 'hsl(var(--secondary))',
38 |   				foreground: 'hsl(var(--secondary-foreground))'
39 |   			},
40 |   			muted: {
41 |   				DEFAULT: 'hsl(var(--muted))',
42 |   				foreground: 'hsl(var(--muted-foreground))'
43 |   			},
44 |   			accent: {
45 |   				DEFAULT: 'hsl(var(--accent))',
46 |   				foreground: 'hsl(var(--accent-foreground))'
47 |   			},
48 |   			destructive: {
49 |   				DEFAULT: 'hsl(var(--destructive))',
50 |   				foreground: 'hsl(var(--destructive-foreground))'
51 |   			},
52 |   			border: 'hsl(var(--border))',
53 |   			input: 'hsl(var(--input))',
54 |   			ring: 'hsl(var(--ring))',
55 |   			chart: {
56 |   				'1': 'hsl(var(--chart-1))',
57 |   				'2': 'hsl(var(--chart-2))',
58 |   				'3': 'hsl(var(--chart-3))',
59 |   				'4': 'hsl(var(--chart-4))',
60 |   				'5': 'hsl(var(--chart-5))'
61 |   			}
62 |   		},
63 |   		keyframes: {
64 |   			'accordion-down': {
65 |   				from: {
66 |   					height: '0'
67 |   				},
68 |   				to: {
69 |   					height: 'var(--radix-accordion-content-height)'
70 |   				}
71 |   			},
72 |   			'accordion-up': {
73 |   				from: {
74 |   					height: 'var(--radix-accordion-content-height)'
75 |   				},
76 |   				to: {
77 |   					height: '0'
78 |   				}
79 |   			}
80 |   		},
81 |   		animation: {
82 |   			'accordion-down': 'accordion-down 0.2s ease-out',
83 |   			'accordion-up': 'accordion-up 0.2s ease-out'
84 |   		}
85 |   	}
86 |   },
87 |   plugins: [require("tailwindcss-animate")],
88 | };
89 | export default config;
90 | 
```

--------------------------------------------------------------------------------
/components/Base.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React from 'react';
  2 | 
  3 | export const Base = () => {
  4 |   return (
  5 |     <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">
  6 |       <div className="max-w-4xl mx-auto">
  7 |         <div className="text-center">
  8 |           <h1 className="text-4xl font-bold text-gray-900 mb-4">
  9 |             EdgeOne Pages MCP: Geo Location Service
 10 |           </h1>
 11 |           <p className="text-xl text-gray-600 mb-8">
 12 |             A geolocation service example demonstrating how to obtain user
 13 |             location information in EdgeOne Pages and interact with AI models
 14 |             through the MCP protocol.
 15 |           </p>
 16 |         </div>
 17 | 
 18 |         {/* Project Overview */}
 19 |         <div className="bg-white rounded-lg shadow-lg p-6 mb-8">
 20 |           <h2 className="text-2xl font-semibold text-gray-900 mb-4">
 21 |             Project Overview
 22 |           </h2>
 23 |           <p className="text-gray-600 mb-4">
 24 |             In modern web applications, obtaining user geolocation information
 25 |             is a common requirement. This project demonstrates how to:
 26 |           </p>
 27 |           <ul className="list-disc list-inside text-gray-600 space-y-2 mb-4">
 28 |             <li>
 29 |               Securely obtain user geolocation using EdgeOne Pages Functions
 30 |             </li>
 31 |             <li>
 32 |               Enable AI models to access location data through the MCP protocol
 33 |             </li>
 34 |             <li>Elegantly display location information in the frontend</li>
 35 |           </ul>
 36 |         </div>
 37 | 
 38 |         {/* Core Features */}
 39 |         <div className="bg-white rounded-lg shadow-lg p-6 mb-8">
 40 |           <h2 className="text-2xl font-semibold text-gray-900 mb-4">
 41 |             Core Features
 42 |           </h2>
 43 |           <div className="grid md:grid-cols-2 gap-6">
 44 |             <div className="border border-gray-200 rounded-lg p-4">
 45 |               <h3 className="text-lg font-medium text-gray-900 mb-2">
 46 |                 EdgeOne Pages Functions
 47 |               </h3>
 48 |               <ul className="text-gray-600 space-y-2">
 49 |                 <li>
 50 |                   • Automatically retrieve visitor&apos;s geolocation information
 51 |                 </li>
 52 |                 <li>
 53 |                   • Includes detailed data about country, region, city, etc.
 54 |                 </li>
 55 |                 <li>
 56 |                   • Precise positioning through EdgeOne&apos;s global node network
 57 |                 </li>
 58 |                 <li>• Returns standardized JSON format data</li>
 59 |               </ul>
 60 |             </div>
 61 |             <div className="border border-gray-200 rounded-lg p-4">
 62 |               <h3 className="text-lg font-medium text-gray-900 mb-2">
 63 |                 MCP Protocol Integration
 64 |               </h3>
 65 |               <ul className="text-gray-600 space-y-2">
 66 |                 <li>• Implements Model Context Protocol standard</li>
 67 |                 <li>• Provides get_geolocation tool interface</li>
 68 |                 <li>
 69 |                   • Supports real-time AI model calls to geolocation service
 70 |                 </li>
 71 |                 <li>• Convenient server-side integration solution</li>
 72 |               </ul>
 73 |             </div>
 74 |           </div>
 75 |         </div>
 76 | 
 77 |         {/* Quick Start */}
 78 |         <div className="text-center space-y-4">
 79 |           <a
 80 |             href="https://edgeone.ai/pages/new?template=mcp-geo"
 81 |             target="_blank"
 82 |             rel="noopener noreferrer"
 83 |             aria-label="Deploy your own instance of the MCP Geo Location Service"
 84 |             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"
 85 |           >
 86 |             Deploy Now
 87 |           </a>
 88 |           <a
 89 |             href="https://edgeone.ai/pages/templates"
 90 |             target="_blank"
 91 |             rel="noopener noreferrer"
 92 |             aria-label="View more EdgeOne Pages templates"
 93 |             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"
 94 |           >
 95 |             View More Templates
 96 |           </a>
 97 |         </div>
 98 |       </div>
 99 |     </div>
100 |   );
101 | };
102 | 
```