Every tool follows two rules enforced at the schema level:
Swagger-strict — each tool’s zod input mirrors the Swagger parameter list exactly. Nothing invented, nothing dropped. Integer fields reject strings, enums reject free-form text, dates enforce YYYY-MM-DD.
Chain-first — responses are passed through as-is (app_id, external_id, version_id, category_id, publisher.external_id are never stripped). Each tool description ends with a “use with: {tool_a}, {tool_b}” hint so the caller can plan multi-step lookups.
The 32 tools split as 28 read-only + 4 write. Read-only tools (everything below except the ones marked ✏️) carry readOnlyHint: true, so MCP clients render them with the safe-to-call indicator.
The 4 write tools — track_app, untrack_app, add_competitor, remove_competitor — carry readOnlyHint: false. Destructive operations (untrack_app, remove_competitor) additionally carry destructiveHint: true. Idempotent ones carry idempotentHint: true. Claude Code surfaces these hints to the user and asks for confirmation before invoking write tools (unless the user has explicitly allowlisted them).
# 2. Add a competitor by store id — no need to track it first.
# The server creates the competitor's app row on demand and links it,
# WITHOUT adding it to your `list_tracked_apps` view.
add_competitor(
platform: "ios",
external_id: "6446901002",
competitor_external_id: "835599320", # TikTok
relationship: "direct"
)
→ returns the new AppCompetitor row
The competitor’s app row is auto-registered if it doesn’t exist yet, but it is not added to the caller’s user_apps watchlist. So list_tracked_apps keeps showing only the apps you actually chose to track, while reports, charts, and change feeds still see the competitors via the app_competitors link.
If you happen to already know the competitor’s internal numeric id (e.g. you tracked it earlier), you can pass competitor_app_id instead of competitor_external_id. Both forms produce the same row.
All tracked competitor groups {parent, competitors[]}.
add_competitor ✏️
Add a competitor to a tracked app. Pass competitor_external_id (preferred — auto-registers the app row without touching your watchlist) or the legacy competitor_app_id. Optional relationship: direct, indirect, aspiration (default direct).
remove_competitor ✏️
Remove a competitor relationship. competitor_id is the relationship row id from list_app_competitors. Idempotent.