pull/718/head
Travis Fischer 2025-07-01 14:45:34 -05:00
rodzic 4faadbf166
commit a23fc97c56
4 zmienionych plików z 55 dodań i 90 usunięć

Wyświetl plik

@ -1,51 +0,0 @@
import { parseProjectIdentifier } from '@agentic/platform-validators'
import { notFound } from 'next/navigation'
import { toastError } from '@/lib/notifications'
import { MarketplacePublicProjectDetail } from '../marketplace-public-project-detail'
import {
type MarketplacePublicProjectDetailTab,
marketplacePublicProjectDetailTabsSet
} from '../utils'
export default async function PublicProjectDetailPageTabPage({
params
}: {
params: Promise<{
namespace: string
'project-slug': string
tab: string
}>
}) {
const {
namespace: rawNamespace,
'project-slug': rawProjectSlug,
tab
} = await params
if (!marketplacePublicProjectDetailTabsSet.has(tab)) {
return notFound()
}
try {
const namespace = decodeURIComponent(rawNamespace)
const projectSlug = decodeURIComponent(rawProjectSlug)
const { projectIdentifier } = parseProjectIdentifier(
`${namespace}/${projectSlug}`,
{ strict: true }
)
return (
<MarketplacePublicProjectDetail
projectIdentifier={projectIdentifier}
tab={tab as MarketplacePublicProjectDetailTab}
/>
)
} catch (err: any) {
void toastError(err, { label: 'Invalid project identifier' })
return notFound()
}
}

Wyświetl plik

@ -8,8 +8,8 @@ import { useRouter, useSearchParams } from 'next/navigation'
import plur from 'plur' import plur from 'plur'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { ActiveLink } from '@/components/active-link'
import { useAgentic } from '@/components/agentic-provider' import { useAgentic } from '@/components/agentic-provider'
import { CodeBlock } from '@/components/code-block'
import { ExampleUsage } from '@/components/example-usage' import { ExampleUsage } from '@/components/example-usage'
import { HeroButton } from '@/components/hero-button' import { HeroButton } from '@/components/hero-button'
import { LoadingIndicator } from '@/components/loading-indicator' import { LoadingIndicator } from '@/components/loading-indicator'
@ -29,15 +29,14 @@ import { useQuery } from '@/lib/query-client'
import { import {
type MarketplacePublicProjectDetailTab, type MarketplacePublicProjectDetailTab,
marketplacePublicProjectDetailTabsSet,
MAX_TOOLS_TO_SHOW MAX_TOOLS_TO_SHOW
} from './utils' } from './utils'
export function MarketplacePublicProjectDetail({ export function MarketplacePublicProjectDetail({
projectIdentifier, projectIdentifier
tab = 'overview'
}: { }: {
projectIdentifier: string projectIdentifier: string
tab?: MarketplacePublicProjectDetailTab
}) { }) {
const ctx = useAgentic() const ctx = useAgentic()
const searchParams = useSearchParams() const searchParams = useSearchParams()
@ -177,13 +176,18 @@ export function MarketplacePublicProjectDetail({
) )
}, [deployment]) }, [deployment])
const inferredTab = useMemo(() => { const tab = useMemo<MarketplacePublicProjectDetailTab>(() => {
const tab = searchParams.get('tab')?.toLowerCase()
if (!tab || !marketplacePublicProjectDetailTabsSet.has(tab)) {
return 'overview'
}
if (tab === 'readme' && !deployment?.readme?.trim()) { if (tab === 'readme' && !deployment?.readme?.trim()) {
return 'overview' return 'overview'
} }
return tab return tab as MarketplacePublicProjectDetailTab
}, [tab, deployment]) }, [searchParams, deployment])
return ( return (
<PageContainer> <PageContainer>
@ -196,16 +200,16 @@ export function MarketplacePublicProjectDetail({
<p>Project "{projectIdentifier}" not found</p> <p>Project "{projectIdentifier}" not found</p>
) : ( ) : (
<div className='flex flex-col gap-4 w-full'> <div className='flex flex-col gap-4 w-full'>
<ProjectHeader project={project} tab={inferredTab} /> <ProjectHeader project={project} tab={tab} />
<Tabs <Tabs
value={inferredTab} value={tab}
onValueChange={(value) => { onValueChange={(value) => {
if (value === 'overview') { if (value === 'overview') {
router.push(`/marketplace/projects/${projectIdentifier}`) router.push(`/marketplace/projects/${projectIdentifier}`)
} else { } else {
router.push( router.push(
`/marketplace/projects/${projectIdentifier}/${value}` `/marketplace/projects/${projectIdentifier}?tab=${value}`
) )
} }
}} }}
@ -243,7 +247,7 @@ export function MarketplacePublicProjectDetail({
</TabsList> </TabsList>
<div className='bg-card p-4 border rounded-lg shadow-sm color-card-foreground'> <div className='bg-card p-4 border rounded-lg shadow-sm color-card-foreground'>
{inferredTab === 'overview' && ( {tab === 'overview' && (
<TabsContent value='overview' className='flex flex-col gap-4'> <TabsContent value='overview' className='flex flex-col gap-4'>
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'> <h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
Overview Overview
@ -292,7 +296,7 @@ export function MarketplacePublicProjectDetail({
variant='outline' variant='outline'
> >
<Link <Link
href={`/marketplace/projects/${projectIdentifier}/tools`} href={`/marketplace/projects/${projectIdentifier}?tab=tools`}
> >
View{' '} View{' '}
{deployment.tools.length - {deployment.tools.length -
@ -328,7 +332,7 @@ export function MarketplacePublicProjectDetail({
</TabsContent> </TabsContent>
)} )}
{deployment?.readme?.trim() && inferredTab === 'readme' && ( {deployment?.readme?.trim() && tab === 'readme' && (
<TabsContent value='readme' className='flex flex-col gap-4'> <TabsContent value='readme' className='flex flex-col gap-4'>
<SSRMarkdown <SSRMarkdown
markdown={deployment.readme} markdown={deployment.readme}
@ -337,7 +341,7 @@ export function MarketplacePublicProjectDetail({
</TabsContent> </TabsContent>
)} )}
{inferredTab === 'tools' && ( {tab === 'tools' && (
<TabsContent value='tools' className='flex flex-col gap-4'> <TabsContent value='tools' className='flex flex-col gap-4'>
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'> <h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
Tools Tools
@ -370,9 +374,15 @@ export function MarketplacePublicProjectDetail({
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent> <CollapsibleContent>
<pre className='max-w-full overflow-x-auto border p-4 rounded-sm'> <CodeBlock
{JSON.stringify(tool.inputSchema, null, 2)} lang='json'
</pre> code={JSON.stringify(
tool.inputSchema,
null,
2
)}
className='border rounded-sm'
/>
</CollapsibleContent> </CollapsibleContent>
</Collapsible> </Collapsible>
@ -389,9 +399,15 @@ export function MarketplacePublicProjectDetail({
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent> <CollapsibleContent>
<pre className='max-w-full overflow-x-auto border p-4 rounded-sm'> <CodeBlock
{JSON.stringify(tool.outputSchema, null, 2)} lang='json'
</pre> code={JSON.stringify(
tool.outputSchema,
null,
2
)}
className='border rounded-sm'
/>
</CollapsibleContent> </CollapsibleContent>
</Collapsible> </Collapsible>
)} )}
@ -402,7 +418,7 @@ export function MarketplacePublicProjectDetail({
</TabsContent> </TabsContent>
)} )}
{inferredTab === 'pricing' && ( {tab === 'pricing' && (
<TabsContent value='pricing' className='flex flex-col gap-4'> <TabsContent value='pricing' className='flex flex-col gap-4'>
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'> <h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
Pricing Pricing
@ -419,7 +435,7 @@ export function MarketplacePublicProjectDetail({
</TabsContent> </TabsContent>
)} )}
{inferredTab === 'debug' && ( {tab === 'debug' && (
<TabsContent value='debug' className='flex flex-col gap-4'> <TabsContent value='debug' className='flex flex-col gap-4'>
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'> <h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
Debug Debug
@ -476,11 +492,11 @@ function ProjectHeader({
className='justify-self-end' className='justify-self-end'
disabled={tab === 'pricing'} disabled={tab === 'pricing'}
> >
<ActiveLink <Link
href={`/marketplace/projects/${project.identifier}/pricing`} href={`/marketplace/projects/${project.identifier}?tab=pricing`}
> >
Subscribe to {project.identifier} Subscribe to {project.identifier}
</ActiveLink> </Link>
</HeroButton> </HeroButton>
</div> </div>

Wyświetl plik

@ -15,12 +15,13 @@ export function ProjectPricingPlans({
onSubscribe: (planSlug: string) => void onSubscribe: (planSlug: string) => void
className?: string className?: string
}) { }) {
// TODO: add support for different pricing intervals
const numPricingPlans = const numPricingPlans =
project.lastPublishedDeployment?.pricingPlans.length || 1 project.lastPublishedDeployment?.pricingPlans.length || 1
return ( return (
<div <div
className={`grid grid-cols grid-cols-1 gap-8 sm:grid-cols-${Math.min( className={`grid grid-cols grid-cols-1 gap-4 sm:grid-cols-${Math.min(
2, 2,
numPricingPlans numPricingPlans
)} xl:grid-cols-${Math.min(3, numPricingPlans)}`} )} xl:grid-cols-${Math.min(3, numPricingPlans)}`}

25
todo.md
Wyświetl plik

@ -12,30 +12,24 @@
- example usage - example usage
- double check example usage for all TS sdks now that real examples are working - double check example usage for all TS sdks now that real examples are working
- fix mcp examples - fix mcp examples
- import react example usage component into docs - **replace json pricing plans and consumers with actual designs**
- replace json pricing plans and consumers with actual designs
- double-check free-tier rate-limits for `@agentic/search` - double-check free-tier rate-limits for `@agentic/search`
- add feature about optimized context to docs
- add ts sdk examples to e2e tests
- submit to awesome mcp, mcp discord, etc - submit to awesome mcp, mcp discord, etc
- add scroll appearance motion to hero animation
- replace how-it-works diagram with better version - replace how-it-works diagram with better version
- docs: add notes about constraints on mcp origin servers (static tools) - docs: add notes about constraints on mcp origin servers (static tools)
- improve public project detail page - improve public project detail page
- mcp inspector - mcp inspector
- **add support to example-usage for api keys** - **add support to example-usage for api keys**
- **api keys should go beyond 1:1 consumers** - **api keys should go beyond 1:1 consumers**
- replace JSON schemas (tool input/output schemas) with `json` code blocks
- improve upgrade flow UX
- **currently not obvious how to get api key** - **currently not obvious how to get api key**
- improve upgrade flow UX
- marketplace project page - marketplace project page
- replace tabs with links - break out into a few subcomponents; some can be server components
- too much of a delay on individual pages and makes no sense to have these imports on the other tab pages - add last published date somewhere
- add last published date to
- add breadcrumb nav: marketplace > @agentic > search - add breadcrumb nav: marketplace > @agentic > search
- add a basic page + docs on pricing - tool input/output schemas; move `$schema` to top
- [react query prefetching for public pages](https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data) - add a basic page + docs on pricing => contact
- add [ping](https://modelcontextprotocol.io/specification/2025-03-26/basic/utilities/ping) support to mcp servers - [**react query prefetching for public pages**](https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data)
- add ability to point at remote readmes, icons, files, urls, etc and upload to our own blob storage at deploy time - add ability to point at remote readmes, icons, files, urls, etc and upload to our own blob storage at deploy time
- **create agentic products for select legacy tools** - **create agentic products for select legacy tools**
@ -131,3 +125,8 @@
- add support for [`@google/genai`](https://github.com/googleapis/js-genai) tools adapter - add support for [`@google/genai`](https://github.com/googleapis/js-genai) tools adapter
- currently difficult due to their use of non-standard json schemas - currently difficult due to their use of non-standard json schemas
- validate example args against the tool's input schema during config validation - validate example args against the tool's input schema during config validation
- add scroll appearance motion to hero animation
- add ts sdk examples to e2e tests
- add feature about optimized context to docs
- import react example usage component into docs
- add [ping](https://modelcontextprotocol.io/specification/2025-03-26/basic/utilities/ping) support to mcp servers