- replace http.DefaultClient with a 60s-timeout client and send a
User-Agent header
- retry 5xx and transport errors with the same ctx-aware backoff used
for 429s; drop the unreachable trailing return and defer-in-loop body
- resolveUser returns the username; posts carry the handle (was numeric
ID), error on 200-with-empty-data, and skip empty-ID tweets
- remove the lower limit floor so small limits pass through
- AGENTS.md: refresh the stale twitter clamp/backoff gotcha
- Change base URI from api.twitter.com to api.x.com/2
- Rate limit: honor x-rate-limit-remaining and x-rate-limit-reset headers
- Remove GetActor/SearchActors from Twitter client (policy: prohibits reselling user/profile data)
- Block Twitter from getActor, searchActors, getPost, getLatestPosts, getPostsInTime, getFeed in MCP server
- Allow Twitter for getStyleProfile with exact /bin/bash.10 pricing (minimum across all platforms)
- Remove GetActor/SearchActors from twitterBackend interface
- Update tests for new Twitter restrictions
- Add upto EVM scheme support for future dynamic pricing
Introduce a shared actor.Profile type (internal/actor) so every platform client can return a
common shape without importing the server package (which would be an import cycle).
- Bluesky: GetActor (app.bsky.actor.getProfile) and SearchActors (app.bsky.actor.searchActors) via XRPC.
- ActivityPub: GetActor (accounts/lookup, falling back to accounts/{id}) and SearchActors
(accounts/search).
- Nostr: GetActor subscribes to kind:0 metadata; SearchActors is best-effort (resolves a
pubkey/npub query to a single profile; Nostr has no global actor search).
- Twitter: GetActor (/users/by/username) and SearchActors (/users/search).
server: register getActor and searchActors as payable tools; searchActors fans the query out
to all four platforms and merges results, skipping any platform that errors.
Schema fixes: document that getPost.collection is Bluesky-only, and that getFeed.uri is a feed
URI for Bluesky but an author handle for other platforms.
Co-Authored-By: Claude (AI assistant) <noreply@anthropic.com>
Add runnable (HTTP-mocked) unit tests for the client methods added when extending the
post tools to all platforms:
- twitter: GetPost (single-tweet object response + author expansion + public_metrics) and
GetPostsInTimeframe (start_time/end_time query params + metrics)
- activitypub: GetPostsInTimeframe (paginated Mastodon statuses within a window)
- nostr: resolvePubkey/resolveEventID resolution helpers via real npub/note encoding
Also fix a latent bug in internal/nostr: resolvePubkey/resolveEventID wrapped the wrong
error variable (the nip19 error instead of the hex-decode error) when the 64-hex fallback
failed, producing misleading messages.
Co-Authored-By: Claude (AI assistant) <noreply@anthropic.com>
Previously these tools were Bluesky-only. They now dispatch by platform:
- getPost: Bluesky uses actor+collection+rkey; ActivityPub uses GetPost(statusID);
Nostr uses GetPost(noteID); Twitter uses GetPost(tweetID)
- getLatestPosts / getPostsInTime: delegate to each platform client
- getFeed: Bluesky uses GetFeed; other platforms treat the URI as an author handle and
return their latest posts (no custom-feed concept elsewhere)
Client additions:
- twitter: GetPost, GetPostsInTimeframe; Post now carries like/repost/quote/reply
counts from public_metrics (populated in GetLatestNPosts and GetPostsInTimeframe)
- nostr: GetPost, GetPostsInTimeframe (relay queries with Since/Until); resolveEventID
helper mirroring resolvePubkey
Server gains normalized mapping/dispatch helpers (bskyPostViewToResponse,
apPostToResponse, nostrPostToResponse, twitterPostToResponse, fetchSinglePost,
fetchLatestPosts, fetchPostsInTime, fetchFeed) that return a uniform
GetPostResponsePost, mapping missing engagement counts to 0.
Co-Authored-By: Claude (AI assistant) <noreply@anthropic.com>
- Nostr client (fiatjaf.com/nostr) fetching kind:1 notes from relays
- Twitter client using raw Twitter API v2 (no external X library), optional (no token = lazy error)
- getStyleProfile dispatches by platform; a profile is updated only when a request is
made and the cached profile is older than a week (served from cache otherwise)
- shared requestStyleProfile path used by both the tool and the prewarmer
- StartPrewarm discovers interesting authors (from a feed URI or seed list) and
pre-fetches at most one profile per minute; fresh profiles are never re-embedded