# MCP Tools — Quick Reference

> The Hypermass Media MCP exposes 30 read-only tools to any MCP-aware
> client (Claude Code, Claude Desktop, Cursor, Cline, custom agents).
> This is the "what can I ask?" reference. For protocol / auth / write
> tiers, see [`mcp-spec.md`](./mcp-spec.md).

---

## Connect

1. Mint a key: dashboard → workspace → **Settings → MCP API keys → Mint key**. Copy the raw `hyper_…` value (shown once).
2. Add to your client config:

```json
{
  "mcpServers": {
    "hypermass": {
      "type": "http",
      "url": "https://hypermass.media/api/mcp",
      "headers": { "Authorization": "Bearer hyper_…" }
    }
  }
}
```

3. Restart your client. You'll see 16 new tools prefixed by their domain (`workspace.*`, `metrics.*`, `posts.*`, etc.).

Every call lands in **Settings → MCP API keys → Recent calls** for audit.

---

## Tools

All tools accept an optional `username` arg to disambiguate when the workspace has multiple IG accounts. Default = first/oldest active account. Defaults shown in `()`.

### Workspace

| Tool | What it returns |
|---|---|
| `workspace.get` | Workspace identity + connected IG accounts with followers/follows/media counts and `last_synced_at`. **Always call first.** |
| `workspace.listMembers` | (OWNER only) Every workspace member with role + email + join date. |

### Account-level metrics

| Tool | Args | What it returns |
|---|---|---|
| `metrics.getCurrent` | `username?` | The most-recent fully-synced day of all account-level metrics (followers, reach, views, interactions, follows/unfollows, profile views, etc.). |
| `metrics.getPeriod` | `days (30)`, `username?` | One row per day for the last N days (max 365). Use for charts / trend analysis. |
| `metrics.comparePeriods` | `days (7)`, `username?` | Last N days vs the N days before — sum + percent change for every core metric. |

### Posts

| Tool | Args | What it returns |
|---|---|---|
| `posts.list` | `limit (25)`, `media_type?`, `media_product_type?`, `username?` | Recent posts with core metrics. Filter by `IMAGE / VIDEO / CAROUSEL_ALBUM` or `FEED / REELS / STORY / AD`. |
| `posts.get` | `ig_media_id` | Full record for one post (long numeric Meta id). |
| `posts.top` | `by ("engagement")`, `days (30)`, `limit (10)`, `username?` | Top performers ranked by `engagement / reach / views / like_count`. |
| `posts.byHashtag` | `hashtag`, `limit (25)`, `username?` | The workspace's own posts containing the hashtag. Leading `#` optional. |

### Audience

| Tool | Args | What it returns |
|---|---|---|
| `audience.demographics` | `metric (follower_demographics)`, `breakdown (country)`, `username?` | Latest snapshot for `follower_demographics` or `engaged_audience_demographics`, broken down by `age / gender / city / country`. Sorted desc by value. |
| `audience.onlineHours` | `days (28)`, `username?` | 24-hour distribution of when followers are online, averaged over the period. Use for best-posting-time recommendations. |

### Derived analytics

| Tool | Args | What it returns |
|---|---|---|
| `posting.bestTimes` | `days (60)`, `top_n (5)`, `username?` | Cross-references when followers are online × historical engagement on Jay's posts. Ranks top hours-of-day by combined score. |
| `engagement.byContentType` | `days (60)`, `username?` | Avg likes / comments / shares / saves / reach / engagement-rate per format (REELS vs CAROUSEL vs FEED vs IMAGE). Answers "what format wins for me." |
| `audience.cities` | `top_n (20)`, `metric (follower_demographics)`, `username?` | City-level demographic drill-down. Use when `audience.demographics breakdown='country'` is too coarse. |
| `posts.outliers` | `metric ("engagement")`, `days (90)`, `sigma (1.5)`, `username?` | Posts >= N σ above/below the account's mean. Returns hits_above and hits_below with z-score + multiple-of-mean. Use for "why did this post pop / flop?" |
| `events.detectSpikes` | `metric ("followers_count")`, `days (60)`, `sigma (2)`, `username?` | Unusual daily-metric jumps correlated with same-day posts + mentions. Surfaces "announcement day," viral reels, mention waves. For followers, uses day-over-day delta against last non-null snapshot (handles partial sync gaps). |
| `hashtags.myPerformance` | `days (90)`, `min_uses (2)`, `username?` | For every hashtag the account has used in its own captions: usage count + avg engagement vs the account's overall average. Surfaces "your #X posts get K× your normal." |

### Stories (captured by 30-min polling cron)

| Tool | Args | What it returns |
|---|---|---|
| `stories.list` | `days (7)`, `limit (50)`, `username?` | Recent stories with media_type, permalink (only valid while live), timestamp, captured insights (reach, views, replies, exits, taps_forward, taps_back). |
| `stories.insights` | `ig_media_id` | Full insights for one specific story. |
| `stories.summary` | `days (7)`, `username?` | Rollup: total stories, avg reach/views/replies, avg taps_forward (skip-rate proxy), top story by reach, completion-rate proxy (1 - exits/reach). |

> **Stories caveat**: stories vanish from Instagram after 24h. We capture them via a polling cron every 30 min, so anything posted > ~30 min before the next poll is captured with its final-state insights. The first day after deploy will have empty results until the cron has run a few cycles.

### Competitor sets (storage only)

| Tool | Args | What it returns |
|---|---|---|
| `competitors.listSets` | — | All named competitor sets for this workspace + their handles. |
| `competitors.set` | `name`, `handles[]`, `description?` | Save / replace a named competitor list (e.g. "actor-comedians"). Pure storage — competitor *fetching* via `business_discovery` is currently blocked by our IG Login auth stack. The set persists for when we unblock the lookup ops. |

### Mentions, comments, brand, history

| Tool | Args | What it returns |
|---|---|---|
| `mentions.list` | `limit (25)`, `username?` | Recent mentions — posts where someone tagged this account. Includes tagger username, media url, like/comment counts. |
| `mentions.get` | `id` (uuid) | Full record for one mention. |
| `comments.list` | `ig_media_id`, `limit (50)` | Comments on one of your posts, oldest first. |
| `comments.threadOnMyPost` | `ig_media_id`, `limit (100)` | Same as `comments.list` plus a pre-built `thread_map` (parent → children ids) so the agent can reconstruct conversation shape without scanning. |
| `mentions.summary` | `days (30)`, `username?` | Roll-up: total mentions, top-mentioning accounts by frequency, format breakdown, avg engagement on the mentioning post. |
| `posts.carouselChildren` | `ig_media_id` | Individual slides of a carousel post — per-slide media_url, type, metrics. |
| `brand.get` | `username?` | The workspace's BrandProfile: niche, voice, goals, off-limits topics, peer accounts, etc. **Ground all recommendations in this.** |
| `history.dailyFollowers` | `days (90)`, `username?` | Follower-count snapshot per day (max 365). Use for follower-trajectory analysis. |

---

## Things to ask Claude

Once connected, natural questions work:

- "What's my engagement trend over the last 7 days vs the 7 before?"
- "Show me my top 5 reels from the last 90 days."
- "What countries are most of my followers in?"
- "What time of day are my followers online?"
- "Who's mentioning me lately?"
- "What's my brand profile so you can help me draft a caption?"
- "Find my posts with #yoga and tell me which one performed best."

Claude will pick the right tool. If a question needs multiple tools (e.g. "compare this to my brand goals"), it'll chain them.

---

## Currently blocked (was on the wish list, but Meta won't let us)

We deliberately use **Instagram Business Login** (cleaner UX for celebs, no Facebook Page dependency). Meta restricts a handful of cross-account endpoints to apps on the older Facebook Login for Business path. Until we add a parallel auth stack, these are unreachable:

- **`competitor.profile / posts / top / compare / heatmap`** — needs `business_discovery`, returns code 100 on our auth.
- **`hashtag.top / hashtag.recent`** — needs `/ig_hashtag_search`, same.
- **`reel_audio_id`** — not exposed by any Meta API on any auth path.
- **`comments_list` on third-party posts** — Meta only exposes comments on posts you own or that tagged you.
- **`reels_trending` (true trending)** — no Meta API. Closest legit replacement is `competitors.heatmap` (blocked above) or hashtag-niche queries (blocked above).

Code for the doable-via-other-auth ones is preserved in `src/lib/mcp/operations.ts` inside `/* DEAD */` blocks for fast revival.

---

## Limits + safety

- **Read-only.** Nothing here writes to the DB or to Instagram. Write tiers (drafts, publish) are designed in `mcp-spec.md` and gated behind future approval flows.
- **Workspace-scoped.** A key for workspace A can't see workspace B's data — enforced at the DB layer (RLS) and re-checked in every handler that takes an id (`posts.get`, `mentions.get`, `comments.list`).
- **Rate-limited.** 60 calls/min per key. Returns HTTP 429 with `Retry-After` if you blast it.
- **Audit-logged.** Every call (success or error) appears in Settings → Recent calls within seconds.

---

## Adding a new tool

Append to `OPERATIONS` in [`src/lib/mcp/operations.ts`](../src/lib/mcp/operations.ts). The route picks it up automatically — `tools/list` will include it, `tools/call` will dispatch to it. Include a description that helps the LLM choose it.
