Skip to content

fix: use UTC-aware datetimes to prevent MCP scheduling in the past#15

Open
elean-latedev wants to merge 2 commits intodevelopfrom
fix/mcp-schedule-naive-datetime
Open

fix: use UTC-aware datetimes to prevent MCP scheduling in the past#15
elean-latedev wants to merge 2 commits intodevelopfrom
fix/mcp-schedule-naive-datetime

Conversation

@elean-latedev
Copy link

Summary

  • datetime.now() returns naive local time. When serialized via .isoformat(), it produces a string without timezone suffix (e.g. 2026-03-05T18:00:20). The API interprets this as UTC, so users in non-UTC timezones get scheduledFor shifted into the past, causing immediate publish instead of scheduling.
  • Replace all datetime.now() with datetime.now(timezone.utc) across MCP server, cross-poster pipeline, rate limiter, examples, docs, and tests.
  • MCP schedule display now appends "UTC" for clarity.

Root cause chain

  1. User in PST (UTC-8) calls MCP posts_create with schedule_minutes=401
  2. datetime.now() returns 11:19 (naive local PST)
  3. 11:19 + 401min = 18:00 serialized as "2026-03-05T18:00:20" (no tz suffix)
  4. API convertToUTC() treats it as UTC → 18:00 UTC, but actual time is 19:19 UTC
  5. scheduledFor <= now → publishes immediately

Files changed

File Change
src/late/mcp/server.py Fix scheduling (2 sites), cache (2 sites), UTC display
src/late/pipelines/cross_poster.py Fix post() and post_sync() base time
src/late/client/rate_limiter.py Fix seconds_until_reset and fromtimestamp
src/late/resources/posts.py Fix docstring example
examples/, README.md Fix code examples
tests/test_integration.py Fix test datetimes

Test plan

  • 44/44 integration tests pass
  • Zero datetime.now() remaining in src/
  • Serialization chain verified: +00:00 suffix matches API's convertToUTC regex
  • No mixed naive/aware datetime comparisons (would cause TypeError)
  • No-op for users already on UTC machines

Reported by: Harshil Shah (Crisp session 3c3c8df5)

🤖 Generated with Claude Code

getlatedev and others added 2 commits January 8, 2026 11:19
* chore: Update repository URLs to getlatedev/late-python-sdk

* fix: Resolve lint errors and update MCP test imports

* fix: Resolve mypy errors and add params to HTTP methods

* ci: Improve release workflow with better visibility and PyPI check

- Add release summary in GitHub Actions
- Check if version exists on PyPI before publishing
- Show clear notices for skip/release decisions
- Bump version to 1.0.1

* ci: Add release preview comment on PRs to main

Shows version info and release status before merging:
- Version from pyproject.toml
- Whether git tag exists
- Whether version exists on PyPI
- Clear indication if release will happen or be skipped

* chore: trigger workflow re-run

* fix: Add explicit permissions for checkout in private repo

* fix: Add explicit token and permissions to all workflows for private repo

* feat: Add typed responses with Pydantic models (v1.1.0)

- All resource methods now return typed Pydantic models instead of dicts
- Generate proper Enum classes instead of Literal types
- Add response models: PostsListResponse, ProfileGetResponse, etc.
- Add upload module with direct and Vercel Blob support
- Update tests to use attribute access syntax
- Sync version to 1.1.0 across pyproject.toml and __init__.py

* feat(mcp): Add is_draft parameter and centralized tool definitions

- Add is_draft parameter to posts_create and posts_cross_post
- Create tool_definitions.py as single source of truth for MCP params
- Add script to generate MDX docs from definitions

* fix: Move Callable imports to TYPE_CHECKING block and fix trailing whitespace

* fix: Fix mypy errors and format code

* feat(ai): Add model property to OpenAI provider

* Refactor MCP server to use typed models and tool docs

Refactors MCP server to use typed model attributes instead of dicts for accounts, profiles, and posts, improving type safety and code clarity. Adds the @use_tool_def decorator to all MCP tool functions, automatically applying centralized docstrings from tool_definitions.py. Updates tool_definitions.py to expand tool documentation, add summaries, and improve MDX generation. Updates README examples to use enums and new tool names. Bumps version to 1.1.1 and regenerates models for improved type annotations.

* Update lint config and import Any in server.py

Added per-file ignores for generated models in Ruff config to allow old-style annotations and trailing whitespace. Also imported 'Any' from typing in src/late/mcp/server.py.

* feat(mcp): Add docs_search tool for documentation search

- Add docs_search tool to search Late API documentation
- Fetch and cache llms-full.txt with 24h TTL
- Score-based search across markdown sections
- Returns top 5 relevant documentation sections

---------

Co-authored-by: Carlos Martínez <carlimvg02@gmail.com>
datetime.now() returns naive local time, which .isoformat() serializes
without a timezone suffix. The API interprets this as UTC, so users in
non-UTC timezones (e.g. PST) get scheduledFor shifted into the past,
triggering immediate publish instead of scheduling.

Fix: datetime.now(timezone.utc) across all scheduling, caching, and
rate-limiting code. Display now appends "UTC" for clarity.

Reported by: Harshil Shah (Crisp session 3c3c8df5)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants