relentless_list
Retrieve all entries from a Notion database to view existing content or search through database records for comprehensive content management.
Instructions
List all entries from a Notion database. Returns full content for all entries. Use this to see what content exists or to search through entries.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| database | Yes | The database name (e.g., "blog", "docs", "leads") |
Implementation Reference
- src/index.ts:454-478 (handler)Handler for the relentless_list tool. Validates input, constructs API endpoint, calls relentlessRequest to fetch all entries from the database, and returns formatted text content with the list of entries.case 'relentless_list': { const { database } = args as { database: string } if (!database) { throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: database') } console.error(`[${new Date().toISOString()}] Listing all from ${database}`) const endpoint = `${RELENTLESS_API_BASE}/api/v1/public/db/${database}/list` const result = await relentlessRequest(endpoint) return { content: [ { type: 'text', text: `Found ${Array.isArray(result) ? result.length : 0} entries:\n\n${JSON.stringify( result, null, 2 )}`, }, ], } }
- src/index.ts:264-278 (registration)Tool registration entry for 'relentless_list' in the ListToolsRequestSchema handler, defining the tool name, description, and input schema.{ name: 'relentless_list', description: 'List all entries from a Notion database. Returns full content for all entries. Use this to see what content exists or to search through entries.', inputSchema: { type: 'object', properties: { database: { type: 'string', description: 'The database name (e.g., "blog", "docs", "leads")', }, }, required: ['database'], }, },
- src/index.ts:268-277 (schema)Input schema definition for the relentless_list tool, specifying the required 'database' parameter.inputSchema: { type: 'object', properties: { database: { type: 'string', description: 'The database name (e.g., "blog", "docs", "leads")', }, }, required: ['database'], },
- src/index.ts:64-171 (helper)Helper function used by the handler to make API requests to Relentless with retry logic, timeout, and error handling.async function relentlessRequest( endpoint: string, options?: RequestInit, maxRetries = 3 ): Promise<any> { const url = endpoint.includes('?') ? `${endpoint}&api_key=${RELENTLESS_API_KEY}` : `${endpoint}?api_key=${RELENTLESS_API_KEY}` let lastError: Error | null = null for (let attempt = 0; attempt < maxRetries; attempt++) { try { // Add timeout using AbortController const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), 30000) // 30s timeout const response = await fetch(url, { ...options, signal: controller.signal, headers: { 'Content-Type': 'application/json', ...options?.headers, }, }) clearTimeout(timeoutId) // Handle rate limiting (429) if (response.status === 429) { const retryAfter = response.headers.get('Retry-After') const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000 console.error(`⏳ Rate limited. Retrying in ${delay / 1000}s...`) await sleep(delay) continue } // Handle success if (response.ok) { return response.json() } // Handle errors with detailed messages const errorBody = await response.json().catch(() => ({ message: 'Unknown error' })) let errorMessage = errorBody.message || errorBody.error || 'Unknown error' // Provide helpful error messages based on status code switch (response.status) { case 401: errorMessage = 'Invalid API key. Check RELENTLESS_API_KEY environment variable.' break case 403: errorMessage = 'Access forbidden. Verify you own this API and have proper permissions.' break case 404: errorMessage = `API path not found. Check that the API exists in your Relentless dashboard.` break case 422: errorMessage = `Validation error: ${errorMessage}. Check that property names match your Notion database exactly (case-sensitive).` break case 500: errorMessage = `Relentless API error: ${errorMessage}` break } // Don't retry client errors (4xx) except 429 if (response.status >= 400 && response.status < 500) { throw new McpError( ErrorCode.InvalidRequest, `Relentless API error (${response.status}): ${errorMessage}` ) } // Server errors (5xx) - will retry lastError = new Error(`Server error (${response.status}): ${errorMessage}`) console.error(`❌ Attempt ${attempt + 1}/${maxRetries} failed: ${errorMessage}`) } catch (error: any) { lastError = error // Handle timeout if (error.name === 'AbortError') { throw new McpError(ErrorCode.InternalError, 'Request timeout after 30 seconds') } // Handle MCP errors (don't retry) if (error instanceof McpError) { throw error } // Network errors - will retry console.error(`❌ Attempt ${attempt + 1}/${maxRetries} failed: ${error.message}`) } // Exponential backoff before retry (except on last attempt) if (attempt < maxRetries - 1) { const backoffDelay = Math.min(Math.pow(2, attempt) * 1000, 10000) // Max 10s console.error(`⏳ Retrying in ${backoffDelay / 1000}s...`) await sleep(backoffDelay) } } // All retries exhausted throw new McpError( ErrorCode.InternalError, `Failed after ${maxRetries} attempts: ${lastError?.message || 'Unknown error'}` ) }