feat: marketing page work

pull/715/head
Travis Fischer 2025-06-18 22:05:28 +08:00
rodzic 33e943a441
commit a42ea77ff2
11 zmienionych plików z 252 dodań i 50 usunięć

Wyświetl plik

@ -1,7 +1,6 @@
import './globals.css' import './globals.css'
import type { Metadata } from 'next' import type { Metadata } from 'next'
import cs from 'clsx'
import { Geist } from 'next/font/google' import { Geist } from 'next/font/google'
import { Toaster } from 'sonner' import { Toaster } from 'sonner'
@ -11,7 +10,6 @@ import { Header } from '@/components/header'
import * as config from '@/lib/config' import * as config from '@/lib/config'
import Providers from './providers' import Providers from './providers'
import styles from './styles.module.css'
const geist = Geist({ const geist = Geist({
variable: '--font-geist', variable: '--font-geist',
@ -49,10 +47,10 @@ export default function RootLayout({
<html lang='en' suppressHydrationWarning> <html lang='en' suppressHydrationWarning>
<body className={`${geist.variable} antialiased`}> <body className={`${geist.variable} antialiased`}>
<Providers> <Providers>
<div className={styles.root}> <div className='w-full min-h-[100vh] relative flex flex-col items-center'>
<Header /> <Header />
<main className={cs(styles.main, 'pt-8 pb-16 px-4 md:px-0')}> <main className='flex-1 w-full flex flex-col items-center max-w-[1200px] gap-16 pt-8 pb-16 px-4 md:px-0'>
{children} {children}
</main> </main>

Wyświetl plik

@ -1,9 +1,19 @@
import { OpenSourceSection } from '@/components/open-source-section' import Link from 'next/link'
import { SupplySideCTA } from '@/components/supply-side-cta'
export default function IndexPage() { import { GitHubStarCounter } from '@/components/github-star-counter'
import { SupplySideCTA } from '@/components/supply-side-cta'
import {
calendarBookingUrl,
discordUrl,
docsUrl,
githubUrl,
twitterUrl
} from '@/lib/config'
export default function TheBestDamnLandingPageEver() {
return ( return (
<> <>
{/* Hero section */}
<section className='gap-8'> <section className='gap-8'>
<h1 className='text-center text-balance leading-snug md:leading-none text-4xl font-extrabold'> <h1 className='text-center text-balance leading-snug md:leading-none text-4xl font-extrabold'>
Your API Paid MCP, Instantly Your API Paid MCP, Instantly
@ -18,13 +28,208 @@ export default function IndexPage() {
<SupplySideCTA /> <SupplySideCTA />
</section> </section>
<section className='flex-1'> {/* How it works section */}
<h2 className='text-center text-balance text-lg'>How it works</h2> <section className='flex flex-col gap-8'>
<h2 className='text-center text-balance leading-snug md:leading-none text-2xl font-heading'>
How It Works
</h2>
<div>TODO</div> <div>TODO</div>
</section> </section>
<OpenSourceSection /> {/* Features section */}
<section className='flex flex-col gap-8'>
<h2 className='text-center text-balance leading-snug md:leading-none text-2xl font-heading'>
Production-Ready and Extremely Flexible
</h2>
<div className='grid gap-6'>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
Auth
</h4>
<p className='text-sm'>
Ship to production fast with Agentic's free, built-in
authentication. Email & password, OAuth, GitHub, Google, Twitter,
etc if your origin API requires OAuth credentials, Agentic
likely already supports it, and if not,{' '}
<Link
href={calendarBookingUrl}
target='_blank'
rel='noopener'
className='link'
>
let me know
</Link>
.
</p>
</div>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
Monetization
</h4>
<p className='text-sm'>
Charge for your MCP products with a flexible, declarative pricing
model built on top of Stripe. Agentic supports almost any
combination of fixed and usage-based billing models, both at the
MCP level, at the tool-call level, and at the custom metric level
(e.g., tokens, image transformations, etc).
</p>
</div>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
Support both MCP <em>and</em> HTTP
</h4>
<p className='text-sm'>
All agentic products support being used both as a standard MCP
server <em>and</em> as an extremely simple HTTP API. MCP is
important for interop, discoverability, and future-proofing,
whereas being able to call your agentic tools via simple{' '}
<em>HTTP POST</em> requests makes tool use easy to debug and makes
integration with existing LLM tool calling patterns a breeze. With
Agentic, you get the best of both worlds, including future support
for unreleased MCP features and related specs.
</p>
</div>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
Rate-limiting
</h4>
<p className='text-sm'>
Customize your MCP product with durable rate-limiting built on top
of Cloudflare's global infrastructure. Create default rate-limits,
change them based on a customer's subscribed pricing plan, and
create custom tool-specific overrides, all with a simple,
strongly-typed JSON config. REST assured that your origin API will
be safe behind Agentic's robust API gateway.
</p>
</div>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
Caching
</h4>
<p className='text-sm'>
Opt-in to caching with familiar <em>cache-control</em> and{' '}
<em>stale-while-revalidate</em> features. MCP tool calls include
caching information in their <em>_meta</em> fields with symmetry
to standard HTTP headers. All caching takes place in Cloudflare's
global edge cache, and will only be enabled if you choose to
enable it for your product or specific tools.
</p>
</div>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
Analytics
</h4>
<p className='text-sm'>
Agentic tracks usage-based billing and analytics at a fine-grained
level, so you can understand how your customers are using your
product.
</p>
</div>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
Versioning
</h4>
<p className='text-sm'>
Just like Vercel, Agentic uses immutable deployments, so every
time you make a change to your product's config, pricing, or docs,
a unique preview deployment is created for that change. This
enables instant rollbacks if there are problems with a deployment.
Publish deployment publicly uses semantic versioning (
<em>semver</em>), so your customers can choose how to handle
breaking changes.
</p>
</div>
<div className='flex flex-col gap-2'>
<h4 className='text-center text-balance text-lg font-heading'>
And more!
</h4>
<p className='text-sm'>
Checkout our <Link href={docsUrl}>docs</Link> for more details on
Agentic's MCP API gateway.
</p>
</div>
</div>
</section>
{/* Marketplace section */}
<section className='flex flex-col gap-8'>
<h2 className='text-center text-balance leading-snug md:leading-none text-2xl font-heading'>
MCP Marketplace
</h2>
<p>Coming soon...</p>
</section>
{/* Open source section */}
<section className='flex flex-col items-center gap-8 max-w-2xl text-center'>
<h2 className='text-center text-balance leading-snug md:leading-none text-2xl font-heading italic'>
Agentic is 100% Open Source!
</h2>
<p className='text-sm'>
Join the tens of thousands of TypeScript AI engineers who've{' '}
<Link
href={githubUrl}
target='_blank'
rel='noopener'
className='link'
>
starred the project on GitHub
</Link>
.{' '}
<Link
href={githubUrl}
target='_blank'
rel='noopener'
className='link'
>
Check out the source on GitHub
</Link>
,{' '}
<Link
href={discordUrl}
target='_blank'
rel='noopener'
className='link'
>
join our community on Discord
</Link>
, or{' '}
<Link
href={twitterUrl}
target='_blank'
rel='noopener'
className='link'
>
ping me on Twitter
</Link>
.
</p>
<GitHubStarCounter />
</section>
{/* CTA section */}
<section className='flex flex-col gap-8'>
<SupplySideCTA />
</section>
</> </>
) )
} }

Wyświetl plik

@ -1,17 +0,0 @@
.root {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
min-height: 100vh;
}
.main {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
max-width: 1200px;
}

Wyświetl plik

@ -4,6 +4,8 @@ import type { Simplify } from 'type-fest'
import confetti, { type Options as ConfettiBaseOptions } from 'canvas-confetti' import confetti, { type Options as ConfettiBaseOptions } from 'canvas-confetti'
import { useCallback } from 'react' import { useCallback } from 'react'
import { randomInRange } from '@/lib/utils'
export type ConfettiOptions = Simplify< export type ConfettiOptions = Simplify<
ConfettiBaseOptions & { ConfettiBaseOptions & {
duration?: number duration?: number
@ -55,7 +57,3 @@ export function useConfettiFireworks() {
fireConfetti fireConfetti
} }
} }
function randomInRange(min: number, max: number) {
return Math.random() * (max - min) + min
}

Wyświetl plik

@ -13,6 +13,7 @@ export function Footer() {
<div className='flex flex-col md:items-center'> <div className='flex flex-col md:items-center'>
<div className='space-y-4'> <div className='space-y-4'>
<h3 className='text-lg font-semibold'>Site</h3> <h3 className='text-lg font-semibold'>Site</h3>
<nav className='flex flex-col space-y-2'> <nav className='flex flex-col space-y-2'>
<span> <span>
<ActiveLink href='/' className='link'> <ActiveLink href='/' className='link'>
@ -38,16 +39,18 @@ export function Footer() {
<div className='flex flex-col order-last md:order-none col-span-2'> <div className='flex flex-col order-last md:order-none col-span-2'>
<div className='space-y-4 flex flex-col w-full'> <div className='space-y-4 flex flex-col w-full'>
<h3 className='text-lg font-semibold'>TODO</h3> <h3 className='text-lg font-semibold'>TODO</h3>
<div className='grid grid-cols-[repeat(auto-fill,_minmax(10em,_1fr))] gap-y-4 gap-x-8 w-full flex-auto'> <div className='grid grid-cols-[repeat(auto-fill,_minmax(10em,_1fr))] gap-y-4 gap-x-8 w-full flex-auto'>
<div className='link'>TODO</div> <div className='link'>TODO</div>
</div> </div>
</div> </div>
</div> </div>
<div className='flex flex-col md:items-center'> <div className='flex flex-col md:items-center gap-4'>
<div className='space-y-4'> <div className='space-y-4'>
<h3 className='text-lg font-semibold'>Social</h3> <h3 className='text-lg font-semibold'>Social</h3>
<nav className='flex flex-col space-y-2'>
<nav className='flex flex-col gap-4'>
<Link <Link
href={twitterUrl} href={twitterUrl}
className='flex items-center space-x-2' className='flex items-center space-x-2'

Wyświetl plik

@ -6,22 +6,28 @@ import { useEffect, useState } from 'react'
import { GitHubIcon } from '@/icons/github' import { GitHubIcon } from '@/icons/github'
import { githubUrl } from '@/lib/config' import { githubUrl } from '@/lib/config'
import { cn } from '@/lib/utils'
import { Button } from './ui/button' import { Button } from './ui/button'
// TODO: fetch this dynamically // TODO: fetch this dynamically
const numGitHubStars = 17_600 const numGitHubStars = 17_600
export function GitHubStarCounter() { export function GitHubStarCounter({ className }: { className?: string }) {
const [numStars, setNumStars] = useState(0) const [numStars, setNumStars] = useState(0)
useEffect(() => { useEffect(() => {
setNumStars(numGitHubStars) setNumStars(numGitHubStars)
}, []) }, [])
return ( return (
<Button variant='outline' className='flex items-center gap-2' asChild> <Button
variant='outline'
className={cn('flex items-center gap-2', className)}
asChild
>
<Link href={githubUrl} target='_blank' rel='noopener'> <Link href={githubUrl} target='_blank' rel='noopener'>
<GitHubIcon className='' /> <GitHubIcon />
<NumberFlow <NumberFlow
value={numStars} value={numStars}
@ -30,6 +36,7 @@ export function GitHubStarCounter() {
roundingPriority: 'morePrecision' roundingPriority: 'morePrecision'
}} }}
suffix=' stars' suffix=' stars'
willChange
/> />
</Link> </Link>
</Button> </Button>

Wyświetl plik

@ -1,13 +0,0 @@
import { GitHubStarCounter } from './github-star-counter'
export function OpenSourceSection() {
return (
<section className='flex flex-col items-center gap-8'>
<h2 className='text-center text-balance leading-snug md:leading-none text-2xl font-heading italic'>
Agentic is 100% open source
</h2>
<GitHubStarCounter />
</section>
)
}

Wyświetl plik

@ -1,14 +1,29 @@
'use client'
import { sanitizeSearchParams } from '@agentic/platform-core'
import Link from 'next/link' import Link from 'next/link'
import { HeroButton } from '@/components/hero-button' import { HeroButton } from '@/components/hero-button'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { calendarBookingUrl, docsQuickStartUrl } from '@/lib/config' import { calendarBookingUrl, docsQuickStartUrl } from '@/lib/config'
import { useAgentic } from './agentic-provider'
export function SupplySideCTA() { export function SupplySideCTA() {
const ctx = useAgentic()
return ( return (
<div className='flex justify-center items-center gap-8'> <div className='flex justify-center items-center gap-8'>
<HeroButton asChild className='h-full'> <HeroButton asChild className='h-full'>
<Link href={docsQuickStartUrl}>Get Started</Link> <Link
href={
ctx?.isAuthenticated
? docsQuickStartUrl
: `/signup?${sanitizeSearchParams({ next: docsQuickStartUrl })}`
}
>
Get Started
</Link>
</HeroButton> </HeroButton>
<Button variant='outline' asChild className='h-full'> <Button variant='outline' asChild className='h-full'>

Wyświetl plik

@ -21,6 +21,7 @@ export const calendarBookingUrl =
export const docsUrl = 'https://docs.agentic.so' export const docsUrl = 'https://docs.agentic.so'
export const docsQuickStartUrl = `${docsUrl}/quick-start` export const docsQuickStartUrl = `${docsUrl}/quick-start`
export const docsMarketplaceUrl = `${docsUrl}/marketplace` export const docsMarketplaceUrl = `${docsUrl}/marketplace`
export const discordUrl = 'https://discord.agentic.so'
export const keywords = [ export const keywords = [
'agentic', 'agentic',

Wyświetl plik

@ -4,3 +4,7 @@ import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))
} }
export function randomInRange(min: number, max: number) {
return Math.random() * (max - min) + min
}

Wyświetl plik

@ -20,6 +20,7 @@
- stripe checkout for changing plans? (need to at least be able to upgrade) - stripe checkout for changing plans? (need to at least be able to upgrade)
- stripe billing portal - stripe billing portal
- should we bypass stripe for `free` plans to increase conversions? - should we bypass stripe for `free` plans to increase conversions?
- handle browser back/forward with `?next=`
- **API gateway** - **API gateway**
- oauth flow - oauth flow
- https://docs.scalekit.com/guides/mcp/oauth - https://docs.scalekit.com/guides/mcp/oauth