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

Wyświetl plik

@ -1,9 +1,19 @@
import { OpenSourceSection } from '@/components/open-source-section'
import { SupplySideCTA } from '@/components/supply-side-cta'
import Link from 'next/link'
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 (
<>
{/* Hero section */}
<section className='gap-8'>
<h1 className='text-center text-balance leading-snug md:leading-none text-4xl font-extrabold'>
Your API Paid MCP, Instantly
@ -18,13 +28,208 @@ export default function IndexPage() {
<SupplySideCTA />
</section>
<section className='flex-1'>
<h2 className='text-center text-balance text-lg'>How it works</h2>
{/* How it works section */}
<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>
</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 { useCallback } from 'react'
import { randomInRange } from '@/lib/utils'
export type ConfettiOptions = Simplify<
ConfettiBaseOptions & {
duration?: number
@ -55,7 +57,3 @@ export function useConfettiFireworks() {
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='space-y-4'>
<h3 className='text-lg font-semibold'>Site</h3>
<nav className='flex flex-col space-y-2'>
<span>
<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='space-y-4 flex flex-col w-full'>
<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='link'>TODO</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'>
<h3 className='text-lg font-semibold'>Social</h3>
<nav className='flex flex-col space-y-2'>
<nav className='flex flex-col gap-4'>
<Link
href={twitterUrl}
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 { githubUrl } from '@/lib/config'
import { cn } from '@/lib/utils'
import { Button } from './ui/button'
// TODO: fetch this dynamically
const numGitHubStars = 17_600
export function GitHubStarCounter() {
export function GitHubStarCounter({ className }: { className?: string }) {
const [numStars, setNumStars] = useState(0)
useEffect(() => {
setNumStars(numGitHubStars)
}, [])
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'>
<GitHubIcon className='' />
<GitHubIcon />
<NumberFlow
value={numStars}
@ -30,6 +36,7 @@ export function GitHubStarCounter() {
roundingPriority: 'morePrecision'
}}
suffix=' stars'
willChange
/>
</Link>
</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 { HeroButton } from '@/components/hero-button'
import { Button } from '@/components/ui/button'
import { calendarBookingUrl, docsQuickStartUrl } from '@/lib/config'
import { useAgentic } from './agentic-provider'
export function SupplySideCTA() {
const ctx = useAgentic()
return (
<div className='flex justify-center items-center gap-8'>
<HeroButton asChild className='h-full'>
<Link href={docsQuickStartUrl}>Get Started</Link>
<Link
href={
ctx?.isAuthenticated
? docsQuickStartUrl
: `/signup?${sanitizeSearchParams({ next: docsQuickStartUrl })}`
}
>
Get Started
</Link>
</HeroButton>
<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 docsQuickStartUrl = `${docsUrl}/quick-start`
export const docsMarketplaceUrl = `${docsUrl}/marketplace`
export const discordUrl = 'https://discord.agentic.so'
export const keywords = [
'agentic',

Wyświetl plik

@ -4,3 +4,7 @@ import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
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 billing portal
- should we bypass stripe for `free` plans to increase conversions?
- handle browser back/forward with `?next=`
- **API gateway**
- oauth flow
- https://docs.scalekit.com/guides/mcp/oauth