From 839e1b3befc72c949346d4c4fe8507b08b1e4b92 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Fri, 16 Jan 2026 16:57:50 +1000 Subject: [PATCH] feat: show MCP URLs and API access info on public collection/agent pages - Always show McpUrlSection on public collection pages (not just owners) - Add note for non-owners about providing their own credentials - Add API usage example for non-owners on collections - Add AgentApiSection component for public agent pages - Show conversation API endpoint and usage example - Make Chat button available to everyone on public agent pages --- .../[username]/agents/[uid]/page.tsx | 128 ++++++++++++++++-- .../[username]/collections/[slug]/page.tsx | 79 +++++++++-- 2 files changed, 182 insertions(+), 25 deletions(-) diff --git a/apps/web/src/app/(profile)/[username]/agents/[uid]/page.tsx b/apps/web/src/app/(profile)/[username]/agents/[uid]/page.tsx index 961dd7b..9c5f4a2 100644 --- a/apps/web/src/app/(profile)/[username]/agents/[uid]/page.tsx +++ b/apps/web/src/app/(profile)/[username]/agents/[uid]/page.tsx @@ -2,6 +2,7 @@ import { Badge } from '@tpmjs/ui/Badge/Badge'; import { Button } from '@tpmjs/ui/Button/Button'; +import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock'; import { Icon } from '@tpmjs/ui/Icon/Icon'; import Link from 'next/link'; import { notFound, useParams } from 'next/navigation'; @@ -71,6 +72,113 @@ interface PublicAgent { } | null; } +function AgentApiSection({ + agentId, + provider, + isOwner, +}: { + agentId: string; + provider: string; + isOwner: boolean; +}) { + const [showExample, setShowExample] = useState(false); + + const baseUrl = typeof window !== 'undefined' ? window.location.origin : 'https://tpmjs.com'; + const apiUrl = `${baseUrl}/api/agents/${agentId}/conversation`; + + const apiExampleSnippet = `// Create a conversation and send a message +const createConvo = await fetch("${apiUrl}", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer YOUR_TPMJS_API_KEY" + }, + body: JSON.stringify({ + name: "My Conversation" + }) +}); +const { data: { conversation } } = await createConvo.json(); + +// Send a message (include providerApiKey for non-owners) +const response = await fetch(\`${apiUrl}/\${conversation.id}\`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer YOUR_TPMJS_API_KEY" + }, + body: JSON.stringify({ + message: "Hello!", + providerApiKey: "YOUR_${provider.toUpperCase()}_API_KEY", // Required for non-owners + env: { + // Your env vars for any tools the agent uses + "SOME_API_KEY": "your-key-here" + } + }) +});`; + + return ( +
+
+
+ +
+

API Access

+
+ +
+
+
+ + Conversation API + +
+
+
+ {apiUrl} +
+
+
+
+ + {/* Note for non-owners */} + {!isOwner && ( +
+

+ + You'll need to provide your own {provider} API key via the{' '} + providerApiKey field, + plus any tool credentials via the{' '} + env parameter. +

+
+ )} + +
+ + + {showExample && ( +
+ +
+ )} +
+ +

+ + Learn more about the Agent Conversation API + +

+
+ ); +} + export default function PrettyAgentDetailPage(): React.ReactElement { const params = useParams(); const rawUsername = params.username as string; @@ -154,14 +262,12 @@ export default function PrettyAgentDetailPage(): React.ReactElement {
- {isOwner && ( - - - - )} + + +
@@ -185,10 +291,8 @@ export default function PrettyAgentDetailPage(): React.ReactElement { Temperature: {agent.temperature} - {/* Fork CTA for non-owners */} - {!isOwner && ( - - )} + {/* API Access - Available to everyone (non-owners must provide their own credentials) */} + {/* System Prompt */} {agent.systemPrompt && ( diff --git a/apps/web/src/app/(profile)/[username]/collections/[slug]/page.tsx b/apps/web/src/app/(profile)/[username]/collections/[slug]/page.tsx index 39eeeef..2e10129 100644 --- a/apps/web/src/app/(profile)/[username]/collections/[slug]/page.tsx +++ b/apps/web/src/app/(profile)/[username]/collections/[slug]/page.tsx @@ -74,9 +74,18 @@ interface PublicCollection { useCasesGeneratedAt: string | null; } -function McpUrlSection({ username, slug }: { username: string; slug: string }) { +function McpUrlSection({ + username, + slug, + isOwner, +}: { + username: string; + slug: string; + isOwner: boolean; +}) { const [copiedUrl, setCopiedUrl] = useState<'http' | 'sse' | null>(null); const [showConfig, setShowConfig] = useState(false); + const [showApiExample, setShowApiExample] = useState(false); const baseUrl = typeof window !== 'undefined' ? window.location.origin : 'https://tpmjs.com'; const httpUrl = `${baseUrl}/api/mcp/${username}/${slug}/http`; @@ -100,6 +109,28 @@ function McpUrlSection({ username, slug }: { username: string; slug: string }) { } }`; + const apiExampleSnippet = `// Call a tool with your own credentials +const response = await fetch("${httpUrl}", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer YOUR_TPMJS_API_KEY" + }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "tools/call", + params: { + name: "tool-name", + arguments: { /* tool args */ }, + env: { + // Your env vars for the tools + "API_KEY": "your-key-here" + } + }, + id: 1 + }) +});`; + return (
@@ -159,8 +190,20 @@ function McpUrlSection({ username, slug }: { username: string; slug: string }) {
+ {/* Note for non-owners */} + {!isOwner && ( +
+

+ + You'll need to provide your own API keys for any tools that require them. Pass + credentials via the env{' '} + parameter in your API calls. +

+
+ )} + {/* Config snippet toggle */} -
+
)} + + {!isOwner && ( + <> + + + {showApiExample && ( +
+ +
+ )} + + )}

@@ -310,17 +372,8 @@ export default function PrettyCollectionDetailPage(): React.ReactElement { )} - {/* MCP Server URLs - Only shown for owner */} - {isOwner ? ( - - ) : ( - - )} + {/* MCP Server URLs - Available to everyone (non-owners must provide their own credentials) */} + {/* Tools */} {collection.tools.length > 0 ? (