docs: update all URL references to new username/slug format
Update all documentation, code, and tests to use the new endpoint formats:
- MCP: /api/mcp/{username}/{collection-slug}/{transport}
- Agent: /api/{username}/agents/{agent-uid}/conversation/{id}
Changes include:
- Update MCP URLs in collections page, docs, and test script
- Update agent API URLs in chat page, docs, tests, and cron job
- Add API key authentication requirements to all examples
- Update ARCHITECTURE.md with correct endpoint formats
- Update discord cron job to fetch owner username for new URL format
This commit is contained in:
parent
2b4526ba95
commit
2fb790a847
9 changed files with 163 additions and 93 deletions
|
|
@ -635,10 +635,10 @@ The API is built on Next.js 16 App Router with standardized response formats.
|
|||
| **Tools** | `GET /api/tools` | List/search tools |
|
||||
| | `POST /api/tools/execute/[...slug]` | Execute tool (SSE) |
|
||||
| **Agents** | `GET /api/agents` | List user agents |
|
||||
| | `POST /api/agents/[id]/conversation/[convId]` | Chat with agent (SSE) |
|
||||
| | `POST /api/{username}/agents/{uid}/conversation/{convId}` | Chat with agent (SSE) |
|
||||
| **Collections** | `GET /api/collections` | List user collections |
|
||||
| | `POST /api/collections/[id]/tools` | Add tool to collection |
|
||||
| **MCP** | `POST /api/mcp/{user}/{slug}/{transport}` | MCP protocol |
|
||||
| **MCP** | `POST /api/mcp/{username}/{slug}/{transport}` | MCP protocol |
|
||||
| **Sync** | `POST /api/sync/changes` | Cron: npm changes |
|
||||
| **Stats** | `GET /api/stats` | Registry statistics |
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +1,67 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Test script for MCP endpoints
|
||||
# Usage: ./test-mcp.sh <collection-id> [base-url]
|
||||
# Usage: ./test-mcp.sh <username> <collection-slug> [base-url] [api-key]
|
||||
#
|
||||
# The new MCP URL format is: /api/mcp/{username}/{slug}/{transport}
|
||||
# Collection ID is also supported as slug for backwards compatibility
|
||||
|
||||
COLLECTION_ID=${1:-""}
|
||||
BASE_URL=${2:-"https://tpmjs.com"}
|
||||
USERNAME=${1:-""}
|
||||
COLLECTION_SLUG=${2:-""}
|
||||
BASE_URL=${3:-"https://tpmjs.com"}
|
||||
API_KEY=${4:-""}
|
||||
|
||||
if [ -z "$COLLECTION_ID" ]; then
|
||||
echo "Usage: ./test-mcp.sh <collection-id> [base-url]"
|
||||
echo "Example: ./test-mcp.sh clx123abc https://tpmjs.com"
|
||||
if [ -z "$USERNAME" ] || [ -z "$COLLECTION_SLUG" ]; then
|
||||
echo "Usage: ./test-mcp.sh <username> <collection-slug> [base-url] [api-key]"
|
||||
echo "Example: ./test-mcp.sh ajax my-collection https://tpmjs.com tpmjs_sk_xxx"
|
||||
echo ""
|
||||
echo "Note: Collection ID can also be used as slug for backwards compatibility"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build auth header if API key provided
|
||||
AUTH_HEADER=""
|
||||
if [ -n "$API_KEY" ]; then
|
||||
AUTH_HEADER="-H \"Authorization: Bearer $API_KEY\""
|
||||
fi
|
||||
|
||||
echo "================================================"
|
||||
echo "Testing MCP endpoints for collection: $COLLECTION_ID"
|
||||
echo "Testing MCP endpoints"
|
||||
echo "Username: $USERNAME"
|
||||
echo "Collection: $COLLECTION_SLUG"
|
||||
echo "Base URL: $BASE_URL"
|
||||
echo "Auth: ${API_KEY:+API key provided}"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# Test 1: HTTP transport - GET (server info)
|
||||
echo "1. Testing HTTP GET (server info)..."
|
||||
echo " GET $BASE_URL/api/collections/$COLLECTION_ID/mcp/http"
|
||||
curl -s "$BASE_URL/api/collections/$COLLECTION_ID/mcp/http" | jq .
|
||||
echo " GET $BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/http"
|
||||
eval curl -s "$BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/http" $AUTH_HEADER | jq .
|
||||
echo ""
|
||||
|
||||
# Test 2: HTTP transport - initialize
|
||||
echo "2. Testing HTTP POST (initialize)..."
|
||||
echo " POST $BASE_URL/api/collections/$COLLECTION_ID/mcp/http"
|
||||
curl -s -X POST "$BASE_URL/api/collections/$COLLECTION_ID/mcp/http" \
|
||||
echo " POST $BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/http"
|
||||
eval curl -s -X POST "$BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/http" \
|
||||
-H "Content-Type: application/json" \
|
||||
$AUTH_HEADER \
|
||||
-d '{"jsonrpc":"2.0","method":"initialize","id":1}' | jq .
|
||||
echo ""
|
||||
|
||||
# Test 3: HTTP transport - tools/list
|
||||
echo "3. Testing HTTP POST (tools/list)..."
|
||||
echo " POST $BASE_URL/api/collections/$COLLECTION_ID/mcp/http"
|
||||
curl -s -X POST "$BASE_URL/api/collections/$COLLECTION_ID/mcp/http" \
|
||||
echo " POST $BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/http"
|
||||
eval curl -s -X POST "$BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/http" \
|
||||
-H "Content-Type: application/json" \
|
||||
$AUTH_HEADER \
|
||||
-d '{"jsonrpc":"2.0","method":"tools/list","id":2}' | jq .
|
||||
echo ""
|
||||
|
||||
# Test 4: SSE transport - GET (event stream)
|
||||
echo "4. Testing SSE GET (event stream)..."
|
||||
echo " GET $BASE_URL/api/collections/$COLLECTION_ID/mcp/sse"
|
||||
curl -s -N "$BASE_URL/api/collections/$COLLECTION_ID/mcp/sse" &
|
||||
echo " GET $BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/sse"
|
||||
eval curl -s -N "$BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/sse" $AUTH_HEADER &
|
||||
SSE_PID=$!
|
||||
sleep 2
|
||||
kill $SSE_PID 2>/dev/null
|
||||
|
|
@ -52,26 +70,28 @@ echo ""
|
|||
|
||||
# Test 5: SSE transport - initialize
|
||||
echo "5. Testing SSE POST (initialize)..."
|
||||
echo " POST $BASE_URL/api/collections/$COLLECTION_ID/mcp/sse"
|
||||
curl -s -X POST "$BASE_URL/api/collections/$COLLECTION_ID/mcp/sse" \
|
||||
echo " POST $BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/sse"
|
||||
eval curl -s -X POST "$BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/sse" \
|
||||
-H "Content-Type: application/json" \
|
||||
$AUTH_HEADER \
|
||||
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Test 6: SSE transport - tools/list
|
||||
echo "6. Testing SSE POST (tools/list)..."
|
||||
echo " POST $BASE_URL/api/collections/$COLLECTION_ID/mcp/sse"
|
||||
curl -s -X POST "$BASE_URL/api/collections/$COLLECTION_ID/mcp/sse" \
|
||||
echo " POST $BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/sse"
|
||||
eval curl -s -X POST "$BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/sse" \
|
||||
-H "Content-Type: application/json" \
|
||||
$AUTH_HEADER \
|
||||
-d '{"jsonrpc":"2.0","method":"tools/list","id":2}'
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Test 7: Invalid transport
|
||||
echo "7. Testing invalid transport..."
|
||||
echo " GET $BASE_URL/api/collections/$COLLECTION_ID/mcp/invalid"
|
||||
curl -s "$BASE_URL/api/collections/$COLLECTION_ID/mcp/invalid" | jq .
|
||||
echo " GET $BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/invalid"
|
||||
eval curl -s "$BASE_URL/api/mcp/$USERNAME/$COLLECTION_SLUG/invalid" $AUTH_HEADER | jq .
|
||||
echo ""
|
||||
|
||||
echo "================================================"
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ interface Agent {
|
|||
collectionCount: number;
|
||||
createdBy: {
|
||||
id: string;
|
||||
username: string | null;
|
||||
name: string;
|
||||
image: string | null;
|
||||
};
|
||||
|
|
@ -266,11 +267,11 @@ export default function PublicAgentChatPage(): React.ReactElement {
|
|||
|
||||
// Fetch messages for conversation (initial load - gets most recent 50)
|
||||
const fetchMessages = useCallback(async () => {
|
||||
if (!agent) return;
|
||||
if (!agent || !agent.createdBy.username) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/agents/${agent.uid}/conversation/${conversationId}?limit=50`
|
||||
`/api/${agent.createdBy.username}/agents/${agent.uid}/conversation/${conversationId}?limit=50`
|
||||
);
|
||||
if (response.status === 404) {
|
||||
// Conversation doesn't exist yet, that's fine
|
||||
|
|
@ -294,7 +295,14 @@ export default function PublicAgentChatPage(): React.ReactElement {
|
|||
// Load older messages when scrolling up
|
||||
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Virtuoso scroll handler with pagination logic
|
||||
const loadMoreMessages = useCallback(async () => {
|
||||
if (!agent || isLoadingMore || !hasMoreMessages || messages.length === 0) return;
|
||||
if (
|
||||
!agent ||
|
||||
!agent.createdBy.username ||
|
||||
isLoadingMore ||
|
||||
!hasMoreMessages ||
|
||||
messages.length === 0
|
||||
)
|
||||
return;
|
||||
|
||||
setIsLoadingMore(true);
|
||||
try {
|
||||
|
|
@ -305,7 +313,7 @@ export default function PublicAgentChatPage(): React.ReactElement {
|
|||
if (!beforeTimestamp) return;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/agents/${agent.uid}/conversation/${conversationId}?limit=50&before=${encodeURIComponent(beforeTimestamp)}`
|
||||
`/api/${agent.createdBy.username}/agents/${agent.uid}/conversation/${conversationId}?limit=50&before=${encodeURIComponent(beforeTimestamp)}`
|
||||
);
|
||||
|
||||
if (!response.ok) return;
|
||||
|
|
@ -344,7 +352,7 @@ export default function PublicAgentChatPage(): React.ReactElement {
|
|||
|
||||
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Chat send handler with streaming and tool calls
|
||||
const handleSend = async () => {
|
||||
if (!input.trim() || !agent || isSending) return;
|
||||
if (!input.trim() || !agent || !agent.createdBy.username || isSending) return;
|
||||
|
||||
const messageContent = input.trim();
|
||||
setInput('');
|
||||
|
|
@ -363,11 +371,14 @@ export default function PublicAgentChatPage(): React.ReactElement {
|
|||
setMessages((prev) => [...prev, userMessage]);
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/agents/${agent.uid}/conversation/${conversationId}`, {
|
||||
const response = await fetch(
|
||||
`/api/${agent.createdBy.username}/agents/${agent.uid}/conversation/${conversationId}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message: messageContent }),
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
|
|
|
|||
|
|
@ -47,10 +47,15 @@ export async function POST(request: NextRequest) {
|
|||
}
|
||||
|
||||
try {
|
||||
// 3. Verify agent exists
|
||||
// 3. Verify agent exists and get owner info
|
||||
const agent = await prisma.agent.findUnique({
|
||||
where: { id: agentId },
|
||||
select: { id: true, name: true },
|
||||
select: {
|
||||
id: true,
|
||||
uid: true,
|
||||
name: true,
|
||||
user: { select: { username: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!agent) {
|
||||
|
|
@ -60,6 +65,13 @@ export async function POST(request: NextRequest) {
|
|||
);
|
||||
}
|
||||
|
||||
if (!agent.user.username || !agent.uid) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Agent owner has no username or agent has no UID' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Get or create a conversation for this cron job
|
||||
// Use a fixed slug for the cron job so we reuse the same conversation
|
||||
const cronSlug = 'discord-summary-cron';
|
||||
|
|
@ -100,10 +112,10 @@ Then use the discordPostTool to post the summary to channel "${summaryChannelId}
|
|||
|
||||
If there are no messages in the past 24 hours, post a brief message saying the server was quiet.`;
|
||||
|
||||
// 6. Call the agent conversation endpoint
|
||||
// 6. Call the agent conversation endpoint (new URL format)
|
||||
const baseUrl = env.BETTER_AUTH_URL || `http://localhost:${process.env.PORT || 3000}`;
|
||||
const response = await fetch(
|
||||
`${baseUrl}/api/agents/${agentId}/conversation/${conversation.id}`,
|
||||
`${baseUrl}/api/${agent.user.username}/agents/${agent.uid}/conversation/${conversation.id}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ interface PublicCollection {
|
|||
tools: CollectionTool[];
|
||||
}
|
||||
|
||||
function McpUrlSection({ collectionId }: { collectionId: string }) {
|
||||
function McpUrlSection({ username, slug }: { username: string; slug: string }) {
|
||||
const [copiedUrl, setCopiedUrl] = useState<'http' | 'sse' | null>(null);
|
||||
const [showConfig, setShowConfig] = useState(false);
|
||||
|
||||
const baseUrl = typeof window !== 'undefined' ? window.location.origin : 'https://tpmjs.com';
|
||||
const httpUrl = `${baseUrl}/api/collections/${collectionId}/mcp/http`;
|
||||
const sseUrl = `${baseUrl}/api/collections/${collectionId}/mcp/sse`;
|
||||
const httpUrl = `${baseUrl}/api/mcp/${username}/${slug}/http`;
|
||||
const sseUrl = `${baseUrl}/api/mcp/${username}/${slug}/sse`;
|
||||
|
||||
const copyToClipboard = async (url: string, type: 'http' | 'sse') => {
|
||||
await navigator.clipboard.writeText(url);
|
||||
|
|
@ -66,7 +66,9 @@ function McpUrlSection({ collectionId }: { collectionId: string }) {
|
|||
"command": "npx",
|
||||
"args": [
|
||||
"mcp-remote",
|
||||
"${httpUrl}"
|
||||
"${httpUrl}",
|
||||
"--header",
|
||||
"Authorization: Bearer YOUR_TPMJS_API_KEY"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -309,7 +311,9 @@ export default function PublicCollectionDetailPage(): React.ReactElement {
|
|||
</div>
|
||||
|
||||
{/* MCP URLs */}
|
||||
<McpUrlSection collectionId={collection.id} />
|
||||
{collection.createdBy?.username && collection.slug && (
|
||||
<McpUrlSection username={collection.createdBy.username} slug={collection.slug} />
|
||||
)}
|
||||
|
||||
{/* Tools */}
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -692,18 +692,30 @@ Always cite your sources and be transparent about limitations.`}
|
|||
<DocSubSection title="Endpoint">
|
||||
<CodeBlock
|
||||
language="text"
|
||||
code="POST /api/agents/[uid]/conversation/[conversationId]"
|
||||
code="POST /api/{username}/agents/{agent-uid}/conversation/{conversationId}"
|
||||
/>
|
||||
<div className="mt-4 space-y-2 text-foreground-secondary text-sm">
|
||||
<p>
|
||||
<strong className="text-foreground">uid:</strong> Your agent's unique
|
||||
identifier
|
||||
<strong className="text-foreground">username:</strong> The agent owner's
|
||||
username
|
||||
</p>
|
||||
<p>
|
||||
<strong className="text-foreground">agent-uid:</strong> The agent's unique
|
||||
identifier (slug)
|
||||
</p>
|
||||
<p>
|
||||
<strong className="text-foreground">conversationId:</strong> Unique ID for the
|
||||
conversation (create your own or use a new ID to start a new conversation)
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4 p-4 border border-primary/30 rounded-lg bg-primary/5">
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
<strong className="text-foreground">Authentication Required:</strong> Include
|
||||
the header{' '}
|
||||
<code className="text-primary">Authorization: Bearer YOUR_TPMJS_API_KEY</code>{' '}
|
||||
in all requests. Get your API key from the dashboard.
|
||||
</p>
|
||||
</div>
|
||||
</DocSubSection>
|
||||
<DocSubSection title="Request Body">
|
||||
<ParamTable
|
||||
|
|
@ -726,13 +738,19 @@ Always cite your sources and be transparent about limitations.`}
|
|||
<DocSubSection title="Example: JavaScript Client">
|
||||
<CodeBlock
|
||||
language="typescript"
|
||||
code={`const conversationId = 'conv-' + Date.now();
|
||||
code={`const username = 'ajax'; // Agent owner's username
|
||||
const agentUid = 'research-assistant'; // Agent UID
|
||||
const conversationId = 'conv-' + Date.now();
|
||||
const apiKey = 'tpmjs_sk_...'; // Your TPMJS API key
|
||||
|
||||
const response = await fetch(
|
||||
\`https://tpmjs.com/api/agents/\${agentUid}/conversation/\${conversationId}\`,
|
||||
\`https://tpmjs.com/api/\${username}/agents/\${agentUid}/conversation/\${conversationId}\`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': \`Bearer \${apiKey}\`,
|
||||
},
|
||||
body: JSON.stringify({ message: 'Search for AI tools' }),
|
||||
}
|
||||
);
|
||||
|
|
@ -825,11 +843,11 @@ while (true) {
|
|||
POST
|
||||
</Badge>
|
||||
<code className="text-foreground font-mono">
|
||||
/api/agents/[uid]/conversation/[conversationId]
|
||||
/api/[username]/agents/[uid]/conversation/[conversationId]
|
||||
</code>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Send a message and stream the AI response via SSE.
|
||||
Send a message and stream the AI response via SSE. Requires TPMJS API key.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-4 border border-border rounded-lg bg-surface">
|
||||
|
|
@ -838,11 +856,11 @@ while (true) {
|
|||
GET
|
||||
</Badge>
|
||||
<code className="text-foreground font-mono">
|
||||
/api/agents/[uid]/conversation/[conversationId]
|
||||
/api/[username]/agents/[uid]/conversation/[conversationId]
|
||||
</code>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
Get the full conversation history with all messages.
|
||||
Get the full conversation history with all messages. Requires TPMJS API key.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-4 border border-border rounded-lg bg-surface">
|
||||
|
|
@ -851,11 +869,11 @@ while (true) {
|
|||
GET
|
||||
</Badge>
|
||||
<code className="text-foreground font-mono">
|
||||
/api/agents/[uid]/conversations
|
||||
/api/[username]/agents/[uid]/conversations
|
||||
</code>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-secondary">
|
||||
List all conversations for an agent.
|
||||
List all conversations for an agent. Requires TPMJS API key.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-4 border border-border rounded-lg bg-surface">
|
||||
|
|
|
|||
|
|
@ -1219,10 +1219,11 @@ export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
|
|||
</p>
|
||||
<CodeBlock
|
||||
language="text"
|
||||
code="https://tpmjs.com/api/collections/<collection-id>/mcp/http"
|
||||
code="https://tpmjs.com/api/mcp/<username>/<collection-slug>/http"
|
||||
/>
|
||||
<p className="text-foreground-secondary mt-4">
|
||||
This URL is what you'll use to connect MCP clients to your collection.
|
||||
You'll also need your TPMJS API key for authentication.
|
||||
</p>
|
||||
</DocSubSection>
|
||||
</DocSection>
|
||||
|
|
@ -1244,7 +1245,9 @@ export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
|
|||
"command": "npx",
|
||||
"args": [
|
||||
"mcp-remote",
|
||||
"https://tpmjs.com/api/collections/<collection-id>/mcp/http"
|
||||
"https://tpmjs.com/api/mcp/<username>/<collection-slug>/http",
|
||||
"--header",
|
||||
"Authorization: Bearer YOUR_TPMJS_API_KEY"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1275,7 +1278,8 @@ export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
|
|||
<CodeBlock
|
||||
language="bash"
|
||||
code={`claude mcp add tpmjs-my-collection \\
|
||||
-- npx mcp-remote https://tpmjs.com/api/collections/<collection-id>/mcp/http`}
|
||||
-- npx mcp-remote https://tpmjs.com/api/mcp/<username>/<collection-slug>/http \\
|
||||
--header "Authorization: Bearer YOUR_TPMJS_API_KEY"`}
|
||||
/>
|
||||
<p className="text-foreground-secondary mt-4">
|
||||
This automatically adds the server to your Claude Code configuration.
|
||||
|
|
@ -1305,7 +1309,7 @@ export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
|
|||
<DocSubSection title="Endpoint Format">
|
||||
<CodeBlock
|
||||
language="text"
|
||||
code="POST https://tpmjs.com/api/collections/<collection-id>/mcp/http"
|
||||
code="POST https://tpmjs.com/api/mcp/<username>/<collection-slug>/http"
|
||||
/>
|
||||
</DocSubSection>
|
||||
<DocSubSection title="Request Headers">
|
||||
|
|
@ -1318,6 +1322,9 @@ export TPMJS_EXECUTOR_URL=https://executor.mycompany.com`}
|
|||
Accept: application/json, text/event-stream
|
||||
</code>
|
||||
</p>
|
||||
<p>
|
||||
<code className="text-primary">Authorization: Bearer YOUR_TPMJS_API_KEY</code>
|
||||
</p>
|
||||
</div>
|
||||
</DocSubSection>
|
||||
<DocSubSection title="Supported Methods">
|
||||
|
|
|
|||
|
|
@ -370,7 +370,9 @@ Invalid usernames:
|
|||
"command": "npx",
|
||||
"args": [
|
||||
"mcp-remote",
|
||||
"https://tpmjs.com/api/collections/{collection-id}/mcp/http"
|
||||
"https://tpmjs.com/api/mcp/{username}/{collection-slug}/http",
|
||||
"--header",
|
||||
"Authorization: Bearer YOUR_TPMJS_API_KEY"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -602,10 +604,10 @@ Invalid usernames:
|
|||
<tr>
|
||||
<td className="py-3 px-4 text-foreground">Agent Conversation</td>
|
||||
<td className="py-3 px-4 font-mono text-primary text-xs">
|
||||
/api/chat/{'{username}'}/{'{uid}'}/conversation/{'{id}'}
|
||||
/api/{'{username}'}/agents/{'{uid}'}/conversation/{'{id}'}
|
||||
</td>
|
||||
<td className="py-3 px-4 font-mono text-foreground-secondary text-xs">
|
||||
/api/chat/ajax/research-bot/conversation/abc123
|
||||
/api/ajax/agents/research-bot/conversation/abc123
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
name: string;
|
||||
userId: string;
|
||||
provider: string;
|
||||
user: { username: string | null };
|
||||
} | null = null;
|
||||
|
||||
let testConversationId: string | null = null;
|
||||
|
|
@ -110,11 +111,15 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
name: true,
|
||||
userId: true,
|
||||
provider: true,
|
||||
user: { select: { username: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!testAgent) {
|
||||
console.warn(`⚠️ Test agent "${TEST_AGENT_UID}" not found. Skipping agent tests.`);
|
||||
} else if (!testAgent.user.username) {
|
||||
console.warn(`⚠️ Test agent owner has no username. Skipping agent tests.`);
|
||||
testAgent = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not connect to database. Skipping agent tests.', error);
|
||||
|
|
@ -139,7 +144,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
}
|
||||
});
|
||||
|
||||
describe('POST /api/agents/[id]/conversation/[conversationId]', () => {
|
||||
describe('POST /api/[username]/agents/[uid]/conversation/[conversationId]', () => {
|
||||
it('should create a new conversation and receive a response', async () => {
|
||||
if (!serverAvailable || !testAgent) {
|
||||
console.log('Skipping: Server not available or no test agent');
|
||||
|
|
@ -147,7 +152,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/agents/${testAgent.uid}/conversation/${TEST_CONVERSATION_SLUG}`,
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversation/${TEST_CONVERSATION_SLUG}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
|
@ -224,7 +229,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/agents/${testAgent.uid}/conversation/${TEST_CONVERSATION_SLUG}`,
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversation/${TEST_CONVERSATION_SLUG}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
|
@ -260,7 +265,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
const toolConvSlug = `tool-test-${Date.now()}`;
|
||||
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/agents/${testAgent.uid}/conversation/${toolConvSlug}`,
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversation/${toolConvSlug}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
|
@ -312,7 +317,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('GET /api/agents/[id]/conversation/[conversationId]', () => {
|
||||
describe('GET /api/[username]/agents/[uid]/conversation/[conversationId]', () => {
|
||||
it('should retrieve conversation history', async () => {
|
||||
if (!serverAvailable || !testAgent || !testConversationId) {
|
||||
console.log('Skipping: Server not available or no test conversation');
|
||||
|
|
@ -320,7 +325,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/agents/${testAgent.uid}/conversation/${TEST_CONVERSATION_SLUG}`,
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversation/${TEST_CONVERSATION_SLUG}`,
|
||||
{ method: 'GET' }
|
||||
);
|
||||
|
||||
|
|
@ -341,7 +346,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/agents/${testAgent.uid}/conversation/non-existent-conv-12345`,
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversation/non-existent-conv-12345`,
|
||||
{ method: 'GET' }
|
||||
);
|
||||
|
||||
|
|
@ -349,16 +354,19 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('GET /api/agents/[id]/conversations', () => {
|
||||
describe('GET /api/[username]/agents/[uid]/conversations', () => {
|
||||
it('should list all conversations for an agent', async () => {
|
||||
if (!serverAvailable || !testAgent) {
|
||||
console.log('Skipping: Server not available or no test agent');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/api/agents/${testAgent.id}/conversations`, {
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversations`,
|
||||
{
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
|
||||
|
|
@ -380,7 +388,7 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/agents/${testAgent.id}/conversations?limit=1&offset=0`,
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversations?limit=1&offset=0`,
|
||||
{ method: 'GET' }
|
||||
);
|
||||
|
||||
|
|
@ -433,31 +441,19 @@ describe.skipIf(!INTEGRATION_TESTS_ENABLED)('Agent Endpoints', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Agent with lookup by ID or UID', () => {
|
||||
it('should accept agent ID in URL', async () => {
|
||||
describe('Agent with username/uid lookup', () => {
|
||||
it('should accept agent with username/uid format', async () => {
|
||||
if (!serverAvailable || !testAgent) {
|
||||
console.log('Skipping: Server not available or no test agent');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/api/agents/${testAgent.id}/conversations`, {
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/api/${testAgent.user.username}/agents/${testAgent.uid}/conversations`,
|
||||
{
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
const result = await response.json();
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should accept agent UID in URL', async () => {
|
||||
if (!serverAvailable || !testAgent) {
|
||||
console.log('Skipping: Server not available or no test agent');
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`${BASE_URL}/api/agents/${testAgent.uid}/conversations`, {
|
||||
method: 'GET',
|
||||
});
|
||||
);
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
const result = await response.json();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue