Skip to content

Releases

v0.8.0

Choose a tag to compare

@IvanBBaev IvanBBaev released this 12 Oct 23:09

Added

  • --help/-h print usage and exit 0; an unknown subcommand or flag exits 2
    instead of starting the stdio server (0.7.0 treated every argument as a start).

  • scripts/build-mcpb.sh builds a lean MCPB bundle (~3 MB) from the npm tarball
    contents plus production dependencies.

  • scripts/live-qa.mjs and docs/live-qa.md: one-command live verification of
    T-E1–T-E4 and the open [verify] probes, read-only unless --write is confirmed.

  • server.json declares IG_AUTH_MODE, IG_WRITE_MODE and IG_TOOL_PACKAGES.

  • List, media, comment and insights results report what was left out
    (omittedWithoutId, omittedValues, repliesTruncated, paging.truncated)
    instead of dropping it silently.

  • discover_business: new mediaAfter input and mediaPaging output. The
    nested media edge now reports truncated: true with an after cursor when
    Instagram returned one; pass it back as mediaAfter (sent as
    media.after(<cursor>)) to read the next page of the profile's media
    (CC-DATA-116).

  • Composite publish previews (post_image, post_reel, post_story) now show
    details.caption.excerpt (flattened, cut after 200 code points with …) and
    details.media_hosts (the distinct hosts Instagram will fetch from; never the
    path, query or userinfo) (CC-PUB-60).

  • A resume (resumeContainerId) that also receives media inputs now warns, in the
    preview and the applied result, that those inputs are ignored and names them
    (CC-PUB-59).

  • token_status and doctor report an expiring_soon state (a doctor WARN)
    when the Path B data-access window closes within 7 days.

  • instagram_list_stories: a read-only tool (package media) that lists the
    account's live stories from GET /{ig-user-id}/stories. It is paged like
    instagram_list_media, captions are fenced, and it covers only Instagram's
    24-hour window. The tool count goes from 28 to 29 (CC-DATA-119).

  • manifest.json (MCPB) now declares a tools array — every tool of the all
    surface with its first-sentence summary — so Claude Desktop's Extensions detail
    pane lists the tools. It is generated from the registry by npm run gen:manifest
    and locked by a drift gate in test/release/mcpb-manifest.test.ts.

  • The MCPB bundle has been built (scripts/build-mcpb.sh, 3.4 MB) and smoke-tested
    locally: the unzipped bundle starts as manifest.json launches it and lists its
    tools over stdio without any network call.

Changed

  • Breaking for clients that parsed it: an error result no longer carries
    structuredContent.error. SDK clients validated that envelope against the
    tool's output schema and turned every typed error into -32602; the typed
    error is now the text line Instagram error (<kind>): <message> (code N, subcode M).

  • An unresolved MCPB placeholder (${user_config.X}) in an IG_* variable is
    treated as unset.

  • The MCPB manifest pins IG_AUTH_MODE (default ig-login), so filling both app
    fields no longer switches a Path-A token to fb-login.

  • The recorded token expiry is bound to a fingerprint of the token it was written
    for; a record for a different token is ignored.

  • The Claude Code plugin moved to plugins/instagram-mcp-ai/ and the marketplace
    entry now reads "source": "./plugins/instagram-mcp-ai". With "source": "./"
    Claude Code copied the whole repo into its plugin cache and ran npm ci on the
    root package.json, about 88 MB of dev dependencies per install. The plugin
    directory holds no package.json, so the cached copy is a few kilobytes.
    /plugin marketplace add IvanBBaev/instagram-mcp is unchanged.

  • Migrated to MCP TypeScript SDK v2 (npx @modelcontextprotocol/codemod v1-to-v2
    plus hand fixes). Runtime dependencies: @modelcontextprotocol/sdk 1.x is
    replaced by @modelcontextprotocol/server and @modelcontextprotocol/node
    (the Node HTTP adapter for Streamable HTTP), and zod goes from 3 to 4. The
    tool names, profiles, arguments and output fields are unchanged. User-visible
    in the published tools/list schemas: $schema is now JSON Schema draft
    2020-12 (was draft-07); integer fields with no declared bound advertise the
    safe-integer range as minimum/maximum; open (passthrough) objects publish
    additionalProperties: {} instead of true; record-shaped fields add
    propertyNames: {"type": "string"}; the recursive comment shape is published
    once under $defs and referenced by $ref; and the userTags item object no
    longer advertises additionalProperties: false (unknown keys in a tag were,
    and still are, dropped rather than rejected). Input-validation messages now use
    zod 4's wording (e.g. Invalid input: expected string, received number).

  • A bare IG_TOKEN_EXPIRES_AT record (<seconds> with no token fingerprint) is
    still read, but token_status and doctor now report it as unverified in every
    dated state, valid and never included, instead of presenting it as a fact
    about the current token (CC-AUTH-70).

  • The Path A expiry note now reads "This is the expiry login/refresh recorded
    in … for this token."

  • A JSON text block now has key-name masking too: an access_token value that
    was masked only in structuredContent is now [REDACTED] in the text as well
    (CC-DATA-104).

  • summarizeDataAccessExpiry in src/api/account.ts is the one place that reads
    data_access_expires_at, and both token_status and doctor use it, so the two
    diagnostics cannot disagree (CC-AUTH-77).

  • README lists the --app-id / --app-secret login options; docs/auth.md §2 links the setup guide instead of a "future README".

  • A still-image URL field (feed image, story image, reel cover) whose URL ends in
    a video extension now draws its own warning ("suggests a video, but this field
    takes a still image") instead of the non-JPEG one (CC-PUB-23).

  • instagram_post_image, instagram_post_reel and instagram_post_story return
    their warnings as warnings on every applied result (published,
    already_published, in_progress), not only in the preview (CC-PUB-25).

  • A relative config directory now resolves against the config home, not the
    process working directory (CC-CFG-11).

  • The untrusted-text renderer blanks invisible renderers in its visible-text view
    (CC-DATA-126), and a fenced block defangs near-miss delimiters to ⟦…⟧ instead
    of the old [ /UNTRUSTED] rewrite (CC-DATA-125).

  • The error-text token stripper and the secret redactor now share one set of
    token-shape backstops (src/core/token-shapes.ts) (CC-PROC-19).

  • instagram_set_comments_enabled belongs to the comments package instead of
    media, so package allow, deny and read-only selection treat it as a comments
    tool (CC-PROC-9).

  • instagram_discover_business publishes id as required. A profile-less answer,
    or a profile without a usable id, is now an upstream error instead of a
    note-only or id-less result (CC-DATA-32, supersedes CC-DATA-79).

  • The mediaAfter description states that a cursor taken from a different
    username is not detected (CC-DATA-133).

Removed

  • The unused text() result builder in src/mcp/result.ts; every tool result is
    JSON (CC-DATA-114).
  • The unused needsRefresh helper in core/refresh.ts; refresh stays
    operator-driven (CC-AUTH-67).

Fixed

  • Redaction of the login failure line, stray expiry records, and missing
    quota_usage no longer read as 0 — see docs/corner-cases.md audit waves 5–7.
  • A single-page listing (list_media, list_stories, list_comments,
    list_tagged_media, get_hashtag_media) that Instagram answers with the same
    cursor it was sent now reports truncated: true with the same-cursor note the
    fetchAll walk uses, instead of offering that cursor as the next page and
    inviting an endless re-read of one page (CC-DATA-134).
  • instagram_get_account_insights with metric_type: "time_series" now defaults to
    reach, the only account metric Instagram serves as a time series, and refuses
    any other metric with it before a call; a bare time_series used to request all
    eleven metrics and always fail (CC-INS-25).
  • instagram_get_account_insights now refuses a since after until instead of
    forwarding the inverted window (CC-INS-26).
  • instagram_get_audience_demographics now sends period=lifetime, which Meta
    marks required (CC-INS-27).
  • login no longer waits forever on a token endpoint that accepts the connection
    and never answers: each OAuth exchange now has the same 30 s timeout as
    refresh (CC-AUTH-72).
  • login no longer follows a redirect from a token endpoint. A 307/308 on the
    Instagram code exchange used to re-send the app secret and the authorization
    code to the redirect target; like refresh, it now fails instead
    (CC-AUTH-73).
  • A Retry-After value that is neither delta-seconds nor an IMF-fixdate (1.5,
    -1, +5, 60,) now falls back to exponential backoff. V8 read these as
    dates in 2001 or 1960, so the retry fired with no delay (CC-RATE-17).
  • A media URL (imageUrls, imageUrl, videoUrl, coverUrl) containing a
    control character is refused as not a well-formed https URL. The parser silently
    dropped a trailing newline or a tab/LF inside the host, so the URL was judged
    valid while the raw string, sent to Graph unchanged, named a different host
    (CC-PUB-57).
  • IG_PRETTY_JSON=true now pretty-prints the text block of every tool, and of the
    write gate's preview and refusal results; 25 of the 28 tools used to ignore it
    (CC-DATA-30, CC-DATA-112).
  • token_status no longer reports a Path B data_access_expires_at of 0 as a
    data-access window that closed in 1970; dataAccessExpiresAt is absent, as when
    Meta omits the field (CC-DATA-113).
  • login and refresh record the expiry when Meta quotes expires_in as a
    canonical non-negative integer string; any other string still leaves no record
    (CC-AUTH-69).
  • A Path B debug_token expires_at outside th...
Read more

v0.7.0 — first published release

Choose a tag to compare

@IvanBBaev IvanBBaev released this 25 Aug 18:52

First published release of instagram-mcp-ai, an MCP server for the Instagram Graph API.

0.7.0 rather than 0.1.0 because the feature surface is complete. What separates it from 1.0.0 is validation against live accounts, not missing functionality.

What's in it

  • 28 tools across five packages — account, media, insights, publishing, comments, plus Facebook-login-only discovery. Package profiles: core (25, default), reader (15), publisher (21), all (28).
  • Both auth paths — Instagram Login (graph.instagram.com) and Facebook Login (graph.facebook.com, with appsecret_proof). Graph API pinned to v25.0.
  • Write gate — every write is preview-by-default and needs apply to execute; irreversible deletes are double-gated; media_publish is never auto-retried.
  • CLI — login (loopback OAuth with a checked state), doctor (token validity, scope inventory, publishing quota, usage headroom, app mode), refresh.
  • Transports — stdio by default with stdout purity guarded, plus an opt-in loopback-bound Streamable HTTP transport with a constant-time bearer check.
  • Security — hard SSRF host allowlist, secret redaction covering runtime-minted tokens and appsecret_proof HMACs, and a prompt-injection fence around all untrusted Instagram content.

Quality

1248 offline tests, 100% line/branch/function/statement coverage, and fifteen modules driven to full mutation kill — every surviving mutant either killed by a behavioural assertion or documented in source as provably unobservable. Zero advisories in the runtime dependency tree.

Known limitation

No tool has been exercised against a live Instagram account by CI. The suite is offline by construction, so what is proven is behaviour against recorded Graph shapes, not against Meta's live responses.

Install

npx instagram-mcp-ai@0.7.0

See docs/setup-guide.md for Meta app configuration.