limitless_get_by_natural_time
Retrieve lifelogs from Limitless Pendant recordings using natural language time expressions like 'yesterday', 'this week', or 'past 3 days' with pagination support.
Instructions
Get lifelogs using natural time expressions. SUPPORTED: 'today', 'yesterday', 'this week', 'last Monday', 'past 3 days', '2 hours ago'. CONSTRAINTS: limit max 10 per request, use cursor for more. EXAMPLES: time_expression='yesterday', limit=5. NOT SUPPORTED: 'Monday July 14' (use 'July 14 2025' instead).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| time_expression | Yes | Natural language time expression like 'today', 'yesterday', 'this morning', 'this week', 'last Monday', 'past 3 days', '2 hours ago', etc. | |
| limit | No | Maximum number of lifelogs to return per request (Max: 10 per API constraint). Use cursor for pagination. | |
| cursor | No | Pagination cursor from previous response. Use to fetch next page of results. | |
| timezone | No | IANA timezone for time calculations (defaults to system timezone). | |
| includeMarkdown | No | Include markdown content in the response. | |
| includeHeadings | No | Include headings content in the response. | |
| isStarred | No | Filter for starred lifelogs only. |
Implementation Reference
- src/server.ts:653-701 (handler)Main handler function: validates args, parses natural time expression using NaturalTimeParser, calls getLifelogsWithPagination with the computed range, handles pagination and truncation, returns formatted results.async (args, _extra) => { try { // Validate constraints const validation = validateApiConstraints(args); if (!validation.valid) { return { content: [{ type: "text", text: validation.error! }], isError: true }; } const parser = new NaturalTimeParser({ timezone: args.timezone }); const timeRange = parser.parseTimeExpression(args.time_expression); const apiOptions: LifelogParams = { start: timeRange.start, end: timeRange.end, timezone: timeRange.timezone, includeMarkdown: args.includeMarkdown, includeHeadings: args.includeHeadings, limit: args.limit || MAX_API_LIMIT, direction: 'asc', isStarred: args.isStarred, cursor: args.cursor }; const result = await getLifelogsWithPagination(limitlessApiKey, apiOptions); if (result.lifelogs.length === 0) { return { content: [{ type: "text", text: `No lifelogs found for "${args.time_expression}".` }] }; } return createSafeResponse( result.lifelogs, `Found ${result.lifelogs.length} lifelog(s) for "${args.time_expression}" (${timeRange.start} to ${timeRange.end})`, { nextCursor: result.pagination.nextCursor, hasMore: result.pagination.hasMore, totalFetched: result.pagination.count } ); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); // Enhanced error message for time parsing if (errorMessage.includes('Unsupported time expression')) { return { content: [{ type: "text", text: `š **TIME EXPRESSION ERROR**: ${errorMessage}\n\nš” **Quick Fixes:**\n- Try 'today', 'yesterday', 'this week' instead\n- For specific dates: Use 'July 14 2025' not 'Monday July 14'\n- Use relative terms: 'past 3 days', 'last Monday'` }], isError: true }; } return { content: [{ type: "text", text: `ā **API ERROR**: ${errorMessage}` }], isError: true }; } }
- src/server.ts:221-229 (schema)Zod schema for input validation of tool arguments including time_expression, pagination, and filtering options.const NaturalTimeArgsSchema = { time_expression: z.string().describe("Natural language time expression like 'today', 'yesterday', 'this morning', 'this week', 'last Monday', 'past 3 days', '2 hours ago', etc."), limit: z.number().int().positive().max(MAX_API_LIMIT).optional().default(MAX_API_LIMIT).describe(`Maximum number of lifelogs to return per request (Max: ${MAX_API_LIMIT} per API constraint). Use cursor for pagination.`), cursor: z.string().optional().describe("Pagination cursor from previous response. Use to fetch next page of results."), timezone: z.string().optional().describe("IANA timezone for time calculations (defaults to system timezone)."), includeMarkdown: z.boolean().optional().default(true).describe("Include markdown content in the response."), includeHeadings: z.boolean().optional().default(true).describe("Include headings content in the response."), isStarred: z.boolean().optional().describe("Filter for starred lifelogs only."), };
- src/server.ts:650-702 (registration)MCP server.tool call registering the tool with name, description, schema, and handler function.server.tool("limitless_get_by_natural_time", "Get lifelogs using natural time expressions. SUPPORTED: 'today', 'yesterday', 'this week', 'last Monday', 'past 3 days', '2 hours ago'. CONSTRAINTS: limit max 10 per request, use cursor for more. EXAMPLES: time_expression='yesterday', limit=5. NOT SUPPORTED: 'Monday July 14' (use 'July 14 2025' instead).", NaturalTimeArgsSchema, async (args, _extra) => { try { // Validate constraints const validation = validateApiConstraints(args); if (!validation.valid) { return { content: [{ type: "text", text: validation.error! }], isError: true }; } const parser = new NaturalTimeParser({ timezone: args.timezone }); const timeRange = parser.parseTimeExpression(args.time_expression); const apiOptions: LifelogParams = { start: timeRange.start, end: timeRange.end, timezone: timeRange.timezone, includeMarkdown: args.includeMarkdown, includeHeadings: args.includeHeadings, limit: args.limit || MAX_API_LIMIT, direction: 'asc', isStarred: args.isStarred, cursor: args.cursor }; const result = await getLifelogsWithPagination(limitlessApiKey, apiOptions); if (result.lifelogs.length === 0) { return { content: [{ type: "text", text: `No lifelogs found for "${args.time_expression}".` }] }; } return createSafeResponse( result.lifelogs, `Found ${result.lifelogs.length} lifelog(s) for "${args.time_expression}" (${timeRange.start} to ${timeRange.end})`, { nextCursor: result.pagination.nextCursor, hasMore: result.pagination.hasMore, totalFetched: result.pagination.count } ); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); // Enhanced error message for time parsing if (errorMessage.includes('Unsupported time expression')) { return { content: [{ type: "text", text: `š **TIME EXPRESSION ERROR**: ${errorMessage}\n\nš” **Quick Fixes:**\n- Try 'today', 'yesterday', 'this week' instead\n- For specific dates: Use 'July 14 2025' not 'Monday July 14'\n- Use relative terms: 'past 3 days', 'last Monday'` }], isError: true }; } return { content: [{ type: "text", text: `ā **API ERROR**: ${errorMessage}` }], isError: true }; } } );
- src/advanced-features.ts:130-370 (helper)Core parseTimeExpression method in NaturalTimeParser class that converts natural language time expressions (e.g., 'today', 'last Monday', 'past 3 days') into precise start/end datetime ranges used by the handler.parseTimeExpression(expression: string): TimeRange { const normalized = expression.toLowerCase().trim(); // Get current time in target timezone const now = new Date(this.referenceTime); const currentDate = new Date(now.toLocaleString("en-US", { timeZone: this.timezone })); // Basic day references switch (normalized) { case "today": return this.getDayRange(currentDate); case "yesterday": const yesterday = new Date(currentDate); yesterday.setDate(yesterday.getDate() - 1); return this.getDayRange(yesterday); case "tomorrow": const tomorrow = new Date(currentDate); tomorrow.setDate(tomorrow.getDate() + 1); return this.getDayRange(tomorrow); // Time of day - today case "this morning": case "morning": return this.getTimeOfDayRange(currentDate, 6, 12); case "this afternoon": case "afternoon": return this.getTimeOfDayRange(currentDate, 12, 18); case "this evening": case "evening": return this.getTimeOfDayRange(currentDate, 18, 22); case "tonight": case "this night": return this.getTimeOfDayRange(currentDate, 20, 23, 59); case "earlier today": case "earlier": const earlierEnd = new Date(currentDate); return this.getTimeOfDayRange(currentDate, 0, earlierEnd.getHours()); case "later today": const laterStart = new Date(currentDate); return this.getTimeOfDayRange(currentDate, laterStart.getHours(), 23, 59); // Yesterday time periods case "yesterday morning": const yesterdayMorning = new Date(currentDate); yesterdayMorning.setDate(yesterdayMorning.getDate() - 1); return this.getTimeOfDayRange(yesterdayMorning, 6, 12); case "yesterday afternoon": const yesterdayAfternoon = new Date(currentDate); yesterdayAfternoon.setDate(yesterdayAfternoon.getDate() - 1); return this.getTimeOfDayRange(yesterdayAfternoon, 12, 18); case "yesterday evening": const yesterdayEvening = new Date(currentDate); yesterdayEvening.setDate(yesterdayEvening.getDate() - 1); return this.getTimeOfDayRange(yesterdayEvening, 18, 22); case "last night": const lastNight = new Date(currentDate); lastNight.setDate(lastNight.getDate() - 1); return this.getTimeOfDayRange(lastNight, 20, 23, 59); // Tomorrow time periods case "tomorrow morning": const tomorrowMorning = new Date(currentDate); tomorrowMorning.setDate(tomorrowMorning.getDate() + 1); return this.getTimeOfDayRange(tomorrowMorning, 6, 12); case "tomorrow afternoon": const tomorrowAfternoon = new Date(currentDate); tomorrowAfternoon.setDate(tomorrowAfternoon.getDate() + 1); return this.getTimeOfDayRange(tomorrowAfternoon, 12, 18); case "tomorrow evening": const tomorrowEvening = new Date(currentDate); tomorrowEvening.setDate(tomorrowEvening.getDate() + 1); return this.getTimeOfDayRange(tomorrowEvening, 18, 22); // Week references case "this week": return this.getWeekRange(currentDate); case "last week": const lastWeek = new Date(currentDate); lastWeek.setDate(lastWeek.getDate() - 7); return this.getWeekRange(lastWeek); case "next week": const nextWeek = new Date(currentDate); nextWeek.setDate(nextWeek.getDate() + 7); return this.getWeekRange(nextWeek); // Weekend references case "this weekend": return this.getWeekendRange(currentDate); case "last weekend": const lastWeekendDate = new Date(currentDate); lastWeekendDate.setDate(lastWeekendDate.getDate() - 7); return this.getWeekendRange(lastWeekendDate); case "next weekend": const nextWeekendDate = new Date(currentDate); nextWeekendDate.setDate(nextWeekendDate.getDate() + 7); return this.getWeekendRange(nextWeekendDate); // Month references case "this month": return this.getMonthRange(currentDate); case "last month": const lastMonth = new Date(currentDate); lastMonth.setMonth(lastMonth.getMonth() - 1); return this.getMonthRange(lastMonth); case "next month": const nextMonth = new Date(currentDate); nextMonth.setMonth(nextMonth.getMonth() + 1); return this.getMonthRange(nextMonth); // Year references case "this year": return this.getYearRange(currentDate); case "last year": const lastYear = new Date(currentDate); lastYear.setFullYear(lastYear.getFullYear() - 1); return this.getYearRange(lastYear); // Quarter references case "this quarter": case "q" + (Math.floor(currentDate.getMonth() / 3) + 1): return this.getQuarterRange(currentDate); case "last quarter": const lastQuarter = new Date(currentDate); lastQuarter.setMonth(lastQuarter.getMonth() - 3); return this.getQuarterRange(lastQuarter); case "q1": return this.getSpecificQuarterRange(currentDate.getFullYear(), 1); case "q2": return this.getSpecificQuarterRange(currentDate.getFullYear(), 2); case "q3": return this.getSpecificQuarterRange(currentDate.getFullYear(), 3); case "q4": return this.getSpecificQuarterRange(currentDate.getFullYear(), 4); // Informal references case "recently": return this.getRelativeDayRange(currentDate, -14, 0); case "the other day": return this.getRelativeDayRange(currentDate, -4, -2); case "a few days ago": return this.getRelativeDayRange(currentDate, -4, -2); case "a couple days ago": case "couple days ago": return this.getRelativeDayRange(currentDate, -2, -2); // Boundary references case "beginning of the week": case "start of the week": const weekStart = new Date(currentDate); weekStart.setDate(weekStart.getDate() - weekStart.getDay()); return this.getDayRange(weekStart); case "end of the week": const weekEnd = new Date(currentDate); weekEnd.setDate(weekEnd.getDate() + (6 - weekEnd.getDay())); return this.getDayRange(weekEnd); case "beginning of the month": case "start of the month": const monthStart = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); return this.getDayRange(monthStart); case "end of the month": const monthEnd = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); return this.getDayRange(monthEnd); // Special fixed ranges (backward compatibility) case "past 3 days": return this.getRelativeDayRange(currentDate, -3, 0); case "past week": return this.getRelativeDayRange(currentDate, -7, 0); case "past month": return this.getRelativeDayRange(currentDate, -30, 0); default: // Try to parse flexible relative expressions like "past N days" const flexibleMatch = this.parseFlexibleRelativeExpression(normalized, currentDate); if (flexibleMatch) return flexibleMatch; // Try to parse specific day names like "last monday", "tuesday" const dayMatch = this.parseDayReference(normalized, currentDate); if (dayMatch) return dayMatch; // Try to parse relative expressions like "2 days ago" const relativeMatch = this.parseRelativeExpression(normalized, currentDate); if (relativeMatch) return relativeMatch; // Try to parse future expressions like "in 2 days" const futureMatch = this.parseFutureExpression(normalized, currentDate); if (futureMatch) return futureMatch; // Try to parse specific date formats like "Monday July 14" const specificDateMatch = this.parseSpecificDateFormat(normalized, currentDate); if (specificDateMatch) return specificDateMatch; const supportedExpressions = [ 'today', 'yesterday', 'tomorrow', 'this morning', 'this afternoon', 'this evening', 'tonight', 'yesterday morning', 'yesterday afternoon', 'yesterday evening', 'last night', 'this week', 'last week', 'next week', 'this weekend', 'last weekend', 'next weekend', 'this month', 'last month', 'next month', 'this quarter', 'last quarter', 'q1', 'q2', 'q3', 'q4', 'past 3 days', 'past week', 'past month', 'last monday', 'next friday', 'tuesday', '2 days ago', '3 hours ago', 'in 2 days', 'recently', 'the other day', 'a few days ago' ]; throw new Error(`Unsupported time expression: "${expression}". Supported expressions (${supportedExpressions.length} total): ${supportedExpressions.join(', ')}. For specific dates, use formats like 'July 14 2025' or '2025-07-14' instead of 'Monday July 14'.`); } }