kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/715/head
rodzic
ab85f07c6d
commit
db4d24a452
|
@ -2,8 +2,8 @@ import Link from 'next/link'
|
||||||
|
|
||||||
import { DotsSection } from '@/components/dots-section'
|
import { DotsSection } from '@/components/dots-section'
|
||||||
import { ExampleAgenticConfigs } from '@/components/example-agentic-configs'
|
import { ExampleAgenticConfigs } from '@/components/example-agentic-configs'
|
||||||
import { Features } from '@/components/features'
|
|
||||||
import { GitHubStarCounter } from '@/components/github-star-counter'
|
import { GitHubStarCounter } from '@/components/github-star-counter'
|
||||||
|
import { MCPGatewayFeatures } from '@/components/mcp-gateway-features'
|
||||||
import { SupplySideCTA } from '@/components/supply-side-cta'
|
import { SupplySideCTA } from '@/components/supply-side-cta'
|
||||||
import { githubUrl, twitterUrl } from '@/lib/config'
|
import { githubUrl, twitterUrl } from '@/lib/config'
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ export default function MCPAuthorsPage() {
|
||||||
Production-Ready MCP Gateway
|
Production-Ready MCP Gateway
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<Features />
|
<MCPGatewayFeatures />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Open source section */}
|
{/* Open source section */}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { DemandSideCTA } from '@/components/demand-side-cta'
|
||||||
import { DotsSection } from '@/components/dots-section'
|
import { DotsSection } from '@/components/dots-section'
|
||||||
import { ExampleUsage } from '@/components/example-usage'
|
import { ExampleUsage } from '@/components/example-usage'
|
||||||
import { GitHubStarCounter } from '@/components/github-star-counter'
|
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 { githubUrl, twitterUrl } from '@/lib/config'
|
||||||
import {
|
import {
|
||||||
defaultConfig,
|
defaultConfig,
|
||||||
|
@ -50,10 +52,6 @@ export default async function TheBestDamnLandingPageEver() {
|
||||||
</div>
|
</div>
|
||||||
</DotsSection>
|
</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 */}
|
{/* How it works section */}
|
||||||
<section className='flex flex-col gap-8 mb-16'>
|
<section className='flex flex-col gap-8 mb-16'>
|
||||||
<h2 className='text-center text-balance leading-snug md:leading-none text-3xl font-heading'>
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Marketplace section */}
|
<section className='flex flex-col items-center gap-8 text-center mb-16'>
|
||||||
<section className='flex flex-col gap-8 mb-16'>
|
|
||||||
<h2 className='text-center text-balance leading-snug md:leading-none text-3xl font-heading'>
|
<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>
|
</h2>
|
||||||
|
|
||||||
<p>
|
<MCPMarketplaceFeatures />
|
||||||
<i>Coming soon...</i>
|
</section>
|
||||||
</p>
|
|
||||||
|
<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>
|
</section>
|
||||||
|
|
||||||
{/* Open source section */}
|
{/* Open source section */}
|
||||||
|
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import {
|
||||||
SMAA,
|
SMAA,
|
||||||
SSAO
|
SSAO
|
||||||
} from '@react-three/postprocessing'
|
} from '@react-three/postprocessing'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
|
|
||||||
const rfs = THREE.MathUtils.randFloatSpread
|
const rfs = THREE.MathUtils.randFloatSpread
|
||||||
|
@ -21,6 +22,22 @@ const baubleMaterial = new THREE.MeshStandardMaterial({
|
||||||
})
|
})
|
||||||
|
|
||||||
export function HeroSimulation2({ className }: { className?: string }) {
|
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 (
|
return (
|
||||||
<Canvas
|
<Canvas
|
||||||
shadows
|
shadows
|
||||||
|
@ -28,6 +45,8 @@ export function HeroSimulation2({ className }: { className?: string }) {
|
||||||
dpr={[1, 1.5]}
|
dpr={[1, 1.5]}
|
||||||
camera={{ position: [0, 0, 20], fov: 35, near: 1, far: 40 }}
|
camera={{ position: [0, 0, 20], fov: 35, near: 1, far: 40 }}
|
||||||
className={className}
|
className={className}
|
||||||
|
onPointerOver={() => setHovered(true)}
|
||||||
|
onPointerOut={() => setHovered(false)}
|
||||||
>
|
>
|
||||||
<ambientLight intensity={0.5} />
|
<ambientLight intensity={0.5} />
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { ComponentType, ReactNode } from 'react'
|
|
||||||
import {
|
import {
|
||||||
ChartNoAxesCombinedIcon,
|
ChartNoAxesCombinedIcon,
|
||||||
CheckCheckIcon,
|
CheckCheckIcon,
|
||||||
|
@ -12,27 +11,13 @@ import {
|
||||||
TextSelectIcon,
|
TextSelectIcon,
|
||||||
UserIcon
|
UserIcon
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import {
|
|
||||||
motion,
|
|
||||||
type MotionValue,
|
|
||||||
useMotionTemplate,
|
|
||||||
useMotionValue
|
|
||||||
} from 'motion/react'
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
import { calendarBookingUrl, docsUrl } from '@/lib/config'
|
import { calendarBookingUrl, docsUrl } from '@/lib/config'
|
||||||
|
|
||||||
import { GridPattern } from './grid-pattern'
|
import { Feature, type FeatureData } from './feature'
|
||||||
|
|
||||||
type Feature = {
|
const mcpGatewayFeatures: FeatureData[] = [
|
||||||
name: string
|
|
||||||
description: ReactNode
|
|
||||||
icon: ComponentType<{ className?: string }>
|
|
||||||
pattern: Omit<GridPattern, 'width' | 'height' | 'x'>
|
|
||||||
href?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const FEATURES: Feature[] = [
|
|
||||||
{
|
{
|
||||||
name: 'Auth',
|
name: 'Auth',
|
||||||
description: (
|
description: (
|
||||||
|
@ -210,122 +195,11 @@ const FEATURES: Feature[] = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
function Feature({ name, description, icon, pattern, href }: Feature) {
|
export function MCPGatewayFeatures() {
|
||||||
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() {
|
|
||||||
return (
|
return (
|
||||||
<div className='xl:max-w-none'>
|
<div className='xl:max-w-none'>
|
||||||
<div className='not-prose grid grid-cols-1 gap-8 sm:grid-cols-2 xl:grid-cols-3'>
|
<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} />
|
<Feature key={feature.name} {...feature} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
|
@ -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 & 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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
Ładowanie…
Reference in New Issue