pull/715/head
Travis Fischer 2025-06-28 04:00:57 -05:00
rodzic ab85f07c6d
commit db4d24a452
7 zmienionych plików z 330 dodań i 142 usunięć

Wyświetl plik

@ -2,8 +2,8 @@ import Link from 'next/link'
import { DotsSection } from '@/components/dots-section'
import { ExampleAgenticConfigs } from '@/components/example-agentic-configs'
import { Features } from '@/components/features'
import { GitHubStarCounter } from '@/components/github-star-counter'
import { MCPGatewayFeatures } from '@/components/mcp-gateway-features'
import { SupplySideCTA } from '@/components/supply-side-cta'
import { githubUrl, twitterUrl } from '@/lib/config'
@ -63,7 +63,7 @@ export default function MCPAuthorsPage() {
Production-Ready MCP Gateway
</h2>
<Features />
<MCPGatewayFeatures />
</section>
{/* Open source section */}

Wyświetl plik

@ -5,6 +5,8 @@ import { DemandSideCTA } from '@/components/demand-side-cta'
import { DotsSection } from '@/components/dots-section'
import { ExampleUsage } from '@/components/example-usage'
import { GitHubStarCounter } from '@/components/github-star-counter'
import { HeroSimulation2 } from '@/components/hero-simulation-2'
import { MCPMarketplaceFeatures } from '@/components/mcp-marketplace-features'
import { githubUrl, twitterUrl } from '@/lib/config'
import {
defaultConfig,
@ -50,10 +52,6 @@ export default async function TheBestDamnLandingPageEver() {
</div>
</DotsSection>
{/* <div className='w-[40%] h-full min-h-full' />
<HeroSimulation2 className='absolute! top-[-50%]! left-[30%] w-full h-[200%]!' /> */}
{/* How it works section */}
<section className='flex flex-col gap-8 mb-16'>
<h2 className='text-center text-balance leading-snug md:leading-none text-3xl font-heading'>
@ -90,15 +88,26 @@ export default async function TheBestDamnLandingPageEver() {
</div>
</section>
{/* Marketplace section */}
<section className='flex flex-col gap-8 mb-16'>
<section className='flex flex-col items-center gap-8 text-center mb-16'>
<h2 className='text-center text-balance leading-snug md:leading-none text-3xl font-heading'>
MCP Tools that just work
Agentic tools are{' '}
<span className='font-semibold'>production-ready</span>
</h2>
<p>
<i>Coming soon...</i>
</p>
<MCPMarketplaceFeatures />
</section>
<section className='flex flex-col items-center gap-8 text-center mb-16 max-w-2xl'>
<h2 className='text-center text-balance leading-snug md:leading-none text-3xl font-heading'>
Agentic makes managing{' '}
<span className='font-semibold'>MCP servers</span> simple
</h2>
<div className='h-96 w-full rounded-lg overflow-hidden shadow-sm'>
<HeroSimulation2 />
</div>
<p className='text-sm'>TODO</p>
</section>
{/* Open source section */}

Wyświetl plik

@ -0,0 +1,137 @@
'use client'
import type { ComponentType, ReactNode } from 'react'
import {
motion,
type MotionValue,
useMotionTemplate,
useMotionValue
} from 'motion/react'
import Link from 'next/link'
import { GridPattern } from './grid-pattern'
export type FeatureData = {
name: string
description: ReactNode
icon: ComponentType<{ className?: string }>
pattern: Omit<GridPattern, 'width' | 'height' | 'x'>
href?: string
}
export function Feature({
name,
description,
icon,
pattern,
href
}: FeatureData) {
const mouseX = useMotionValue(0)
const mouseY = useMotionValue(0)
function onMouseMove({
currentTarget,
clientX,
clientY
}: React.MouseEvent<HTMLElement>) {
const { left, top } = currentTarget.getBoundingClientRect()
mouseX.set(clientX - left)
mouseY.set(clientY - top)
}
const content = (
<>
<FeaturePattern {...pattern} mouseX={mouseX} mouseY={mouseY} />
<div className='ring-gray-900/7.5 group-hover:ring-gray-900/10 absolute inset-0 rounded-2xl ring-1 ring-inset dark:ring-white/10 dark:group-hover:ring-white/20' />
<div className='relative rounded-2xl p-4 flex flex-col gap-6 pt-12 text-start'>
<FeatureIcon icon={icon} />
<h3 className='text-gray-900 text-lg font-semibold leading-0 dark:text-white'>
{/* <span className='absolute inset-0 rounded-2xl' /> */}
{name}
</h3>
<p className='text-gray-600 dark:text-gray-400 text-[0.875rem] leading-[1.5rem]'>
{description}
</p>
</div>
</>
)
const className =
'dark:bg-white/2.5 bg-gray-50 hover:shadow-gray-900/5 group relative flex rounded-2xl transition-shadow hover:shadow-md dark:hover:shadow-black/5'
if (href) {
return (
<Link
href={href}
key={name}
onMouseMove={onMouseMove}
className={className}
>
{content}
</Link>
)
} else {
return (
<div key={name} onMouseMove={onMouseMove} className={className}>
{content}
</div>
)
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
function FeatureIcon({ icon: Icon }: { icon: FeatureData['icon'] }) {
return (
<div className='dark:bg-white/7.5 bg-gray-900/5 ring-gray-900/25 group-hover:ring-gray-900/25 dark:group-hover:bg-sky-300/10 dark:group-hover:ring-sky-400 flex size-7 items-center justify-center rounded-full ring-1 backdrop-blur-[2px] transition duration-300 group-hover:bg-white/50 dark:ring-white/15'>
<Icon className='fill-gray-700/10 stroke-gray-700 group-hover:stroke-gray-900 dark:stroke-gray-400 dark:group-hover:stroke-sky-400 dark:group-hover:fill-sky-300/10 size-5 transition-colors duration-300 dark:fill-white/10' />
</div>
)
}
function FeaturePattern({
mouseX,
mouseY,
...gridProps
}: FeatureData['pattern'] & {
mouseX: MotionValue<number>
mouseY: MotionValue<number>
}) {
const maskImage = useMotionTemplate`radial-gradient(180px at ${mouseX}px ${mouseY}px, white, transparent)`
const style = { maskImage, WebkitMaskImage: maskImage }
return (
<div className='pointer-events-none'>
<div className='absolute inset-0 rounded-2xl transition duration-300 [mask-image:linear-gradient(white,transparent)] group-hover:opacity-50'>
<GridPattern
width={72}
height={56}
x='50%'
className='dark:fill-white/1 dark:stroke-white/2.5 absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/[0.02] stroke-black/5'
{...gridProps}
/>
</div>
<motion.div
className='from-sky-100 to-sky-300 dark:from-sky-500 dark:to-sky-300 absolute inset-0 rounded-2xl bg-gradient-to-r opacity-0 transition duration-300 group-hover:opacity-50 dark:group-hover:opacity-15'
style={style}
/>
<motion.div
className='absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay transition duration-300 group-hover:opacity-100'
style={style}
>
<GridPattern
width={72}
height={56}
x='50%'
className='dark:fill-white/2.5 absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/50 stroke-black/70 dark:stroke-white/10'
{...gridProps}
/>
</motion.div>
</div>
)
}

Wyświetl plik

@ -10,6 +10,7 @@ import {
SMAA,
SSAO
} from '@react-three/postprocessing'
import { useEffect, useState } from 'react'
import * as THREE from 'three'
const rfs = THREE.MathUtils.randFloatSpread
@ -21,6 +22,22 @@ const baubleMaterial = new THREE.MeshStandardMaterial({
})
export function HeroSimulation2({ className }: { className?: string }) {
const [hovered, setHovered] = useState(false)
// Change cursor on hovered state
useEffect(() => {
if (hovered) {
document.body.style.cursor = 'none'
} else {
document.body.style.cursor = 'auto'
}
// document.body.style.cursor = hovered
// ? 'none'
// : `url('data:image/svg+xml;base64,${btoa(
// '<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="16" cy="16" r="10" fill="#E8B059"/></svg>'
// )}'), auto`
}, [hovered])
return (
<Canvas
shadows
@ -28,6 +45,8 @@ export function HeroSimulation2({ className }: { className?: string }) {
dpr={[1, 1.5]}
camera={{ position: [0, 0, 20], fov: 35, near: 1, far: 40 }}
className={className}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
>
<ambientLight intensity={0.5} />

Wyświetl plik

@ -1,6 +1,5 @@
'use client'
import type { ComponentType, ReactNode } from 'react'
import {
ChartNoAxesCombinedIcon,
CheckCheckIcon,
@ -12,27 +11,13 @@ import {
TextSelectIcon,
UserIcon
} from 'lucide-react'
import {
motion,
type MotionValue,
useMotionTemplate,
useMotionValue
} from 'motion/react'
import Link from 'next/link'
import { calendarBookingUrl, docsUrl } from '@/lib/config'
import { GridPattern } from './grid-pattern'
import { Feature, type FeatureData } from './feature'
type Feature = {
name: string
description: ReactNode
icon: ComponentType<{ className?: string }>
pattern: Omit<GridPattern, 'width' | 'height' | 'x'>
href?: string
}
const FEATURES: Feature[] = [
const mcpGatewayFeatures: FeatureData[] = [
{
name: 'Auth',
description: (
@ -210,122 +195,11 @@ const FEATURES: Feature[] = [
}
]
function Feature({ name, description, icon, pattern, href }: Feature) {
const mouseX = useMotionValue(0)
const mouseY = useMotionValue(0)
function onMouseMove({
currentTarget,
clientX,
clientY
}: React.MouseEvent<HTMLElement>) {
const { left, top } = currentTarget.getBoundingClientRect()
mouseX.set(clientX - left)
mouseY.set(clientY - top)
}
const content = (
<>
<FeaturePattern {...pattern} mouseX={mouseX} mouseY={mouseY} />
<div className='ring-gray-900/7.5 group-hover:ring-gray-900/10 absolute inset-0 rounded-2xl ring-1 ring-inset dark:ring-white/10 dark:group-hover:ring-white/20' />
<div className='relative rounded-2xl p-4 flex flex-col gap-6 pt-12'>
<FeatureIcon icon={icon} />
<h3 className='text-gray-900 text-lg font-semibold leading-0 dark:text-white'>
{/* <span className='absolute inset-0 rounded-2xl' /> */}
{name}
</h3>
<p className='text-gray-600 dark:text-gray-400 text-[0.875rem] leading-[1.5rem]'>
{description}
</p>
</div>
</>
)
const className =
'dark:bg-white/2.5 bg-gray-50 hover:shadow-gray-900/5 group relative flex rounded-2xl transition-shadow hover:shadow-md dark:hover:shadow-black/5'
if (href) {
return (
<Link
href={href}
key={name}
onMouseMove={onMouseMove}
className={className}
>
{content}
</Link>
)
} else {
return (
<div key={name} onMouseMove={onMouseMove} className={className}>
{content}
</div>
)
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
function FeatureIcon({ icon: Icon }: { icon: Feature['icon'] }) {
return (
<div className='dark:bg-white/7.5 bg-gray-900/5 ring-gray-900/25 group-hover:ring-gray-900/25 dark:group-hover:bg-sky-300/10 dark:group-hover:ring-sky-400 flex size-7 items-center justify-center rounded-full ring-1 backdrop-blur-[2px] transition duration-300 group-hover:bg-white/50 dark:ring-white/15'>
<Icon className='fill-gray-700/10 stroke-gray-700 group-hover:stroke-gray-900 dark:stroke-gray-400 dark:group-hover:stroke-sky-400 dark:group-hover:fill-sky-300/10 size-5 transition-colors duration-300 dark:fill-white/10' />
</div>
)
}
function FeaturePattern({
mouseX,
mouseY,
...gridProps
}: Feature['pattern'] & {
mouseX: MotionValue<number>
mouseY: MotionValue<number>
}) {
const maskImage = useMotionTemplate`radial-gradient(180px at ${mouseX}px ${mouseY}px, white, transparent)`
const style = { maskImage, WebkitMaskImage: maskImage }
return (
<div className='pointer-events-none'>
<div className='absolute inset-0 rounded-2xl transition duration-300 [mask-image:linear-gradient(white,transparent)] group-hover:opacity-50'>
<GridPattern
width={72}
height={56}
x='50%'
className='dark:fill-white/1 dark:stroke-white/2.5 absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/[0.02] stroke-black/5'
{...gridProps}
/>
</div>
<motion.div
className='from-sky-100 to-sky-300 dark:from-sky-500 dark:to-sky-300 absolute inset-0 rounded-2xl bg-gradient-to-r opacity-0 transition duration-300 group-hover:opacity-50 dark:group-hover:opacity-15'
style={style}
/>
<motion.div
className='absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay transition duration-300 group-hover:opacity-100'
style={style}
>
<GridPattern
width={72}
height={56}
x='50%'
className='dark:fill-white/2.5 absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/50 stroke-black/70 dark:stroke-white/10'
{...gridProps}
/>
</motion.div>
</div>
)
}
export function Features() {
export function MCPGatewayFeatures() {
return (
<div className='xl:max-w-none'>
<div className='not-prose grid grid-cols-1 gap-8 sm:grid-cols-2 xl:grid-cols-3'>
{FEATURES.map((feature) => (
{mcpGatewayFeatures.map((feature) => (
<Feature key={feature.name} {...feature} />
))}
</div>

Wyświetl plik

@ -0,0 +1,136 @@
'use client'
import {
CheckCheckIcon,
CreditCardIcon,
FileJsonIcon,
HistoryIcon,
ShieldCheckIcon,
StarIcon
} from 'lucide-react'
import { Feature, type FeatureData } from './feature'
const mcpMarketplaceFeatures: FeatureData[] = [
{
name: 'Highly Curated Tools',
description: (
<>
All Agentic tools have been hand-crafted specifically for LLM tool use.
We call this Agentic DX, and it's at the heart of why Agentic tools work
better for LLM &amp; MCP use cases than legacy APIs.
</>
),
icon: StarIcon,
pattern: {
y: 16,
squares: [
[0, 1],
[1, 3]
]
}
},
{
name: 'Battle-Tested',
description: (
<>
Forget random GitHub repos and piping MCP servers together. Agentic
tools are all battle-tested in production and come with real SLAs.
</>
),
icon: ShieldCheckIcon,
pattern: {
y: 22,
squares: [[0, 1]]
}
},
{
name: 'World-Class TypeScript DX',
description: (
<>
Agentic is written in TypeScript and strives for a Vercel-like DX.
<span className='font-semibold'>One-line tool integrations</span> for
all the popular TS LLM SDKs (
<span className='font-semibold'>Vercel AI SDK</span>,{' '}
<span className='font-semibold'>OpenAI</span>,{' '}
<span className='font-semibold'>LangChain</span>, etc).
</>
),
icon: FileJsonIcon,
pattern: {
y: 8,
squares: [
[0, 2],
[-1, 1]
]
}
},
{
name: 'Stripe Billing',
description: (
<>
Agentic uses Stripe for billing, and most tools are{' '}
<span className='font-semibold'>usage-based</span>, so you'll only pay
for what you (and your agents) actually use.
</>
),
icon: CreditCardIcon,
pattern: {
y: -6,
squares: [
[-1, 2],
[1, 3]
]
}
},
{
name: 'Support both MCP and HTTP',
description: (
<>
All Agentic tools are exposed as both{' '}
<span className='font-semibold'>MCP servers</span> as well as simple{' '}
<span className='font-semibold'>HTTP APIs</span>. MCP is important for
interop and future-proofing, whereas simple HTTP POST requests make
tools easy to debug and simplifies integrating with LLM SDKs.
</>
),
icon: CheckCheckIcon,
pattern: {
y: 32,
squares: [
[0, 2],
[1, 4]
]
}
},
{
name: 'Semantic Versioning',
description: (
<>
All Agentic tools are versioned using{' '}
<span className='font-semibold'>semver</span>, so you can choose how to
handle breaking changes.
</>
),
icon: HistoryIcon,
pattern: {
y: 26,
squares: [
[2, 4],
[-2, 3]
]
}
}
]
export function MCPMarketplaceFeatures() {
return (
<div className='xl:max-w-none'>
<div className='not-prose grid grid-cols-1 gap-8 sm:grid-cols-2 xl:grid-cols-3'>
{mcpMarketplaceFeatures.map((feature) => (
<Feature key={feature.name} {...feature} />
))}
</div>
</div>
)
}

Wyświetl plik

@ -0,0 +1,13 @@
import { cn } from '@/lib/utils'
export function TypeScriptIcon({ className }: { className?: string }) {
return (
<svg viewBox='0 0 400 400' className={cn('w-4 h-4', className)}>
<path fill='#007acc' d='M0 200V0h400v400H0' />
<path
fill='#fff'
d='M87.7 200.7V217h52v148h36.9V217h52v-16c0-9 0-16.3-.4-16.5 0-.3-31.7-.4-70.2-.4l-70 .3v16.4l-.3-.1zM321.4 184c10.2 2.4 18 7 25 14.3 3.7 4 9.2 11 9.6 12.8 0 .6-17.3 12.3-27.8 18.8-.4.3-2-1.4-3.6-4-5.2-7.4-10.5-10.6-18.8-11.2-12-.8-20 5.5-20 16 0 3.2.6 5 1.8 7.6 2.7 5.5 7.7 8.8 23.2 15.6 28.6 12.3 41 20.4 48.5 32 8.5 13 10.4 33.4 4.7 48.7-6.4 16.7-22 28-44.3 31.7-7 1.2-23 1-30.5-.3-16-3-31.3-11-40.7-21.3-3.7-4-10.8-14.7-10.4-15.4l3.8-2.4 15-8.7 11.3-6.6 2.6 3.5c3.3 5.2 10.7 12.2 15 14.6 13 6.7 30.4 5.8 39-2 3.7-3.4 5.3-7 5.3-12 0-4.6-.7-6.7-3-10.2-3.2-4.4-9.6-8-27.6-16-20.7-8.8-29.5-14.4-37.7-23-4.7-5.2-9-13.3-11-20-1.5-5.8-2-20-.6-25.7 4.3-20 19.4-34 41-38 7-1.4 23.5-.8 30.4 1l-.2.2z'
/>
</svg>
)
}