Skip to main content
Glama
rankes96-dev

Instagram Relationship MCP

by rankes96-dev
README.md
# Instagram Relationship MCP

A local MCP server that answers one question for an Instagram account you control:

> Which accounts do I follow that do not follow me back?

The server is read-only toward Instagram. It can sign in through a dedicated Playwright Chromium profile, read relationship data, cache derived results locally, and disconnect. It cannot follow, unfollow, block, like, comment, or send messages.

> [!IMPORTANT]
> This project uses Instagram's private, unsupported Web API rather than Meta's supported Instagram Graph API. Instagram can change these endpoints without notice. Unknown response shapes fail closed with `PRIVATE_API_CHANGED`, and rate limits stop the scan. Use only an account you control and comply with Instagram's terms and policies.

## How it works

```text
Instagram
    ↑ dedicated authenticated browser profile
Playwright persistent Chromium context
    ↑ same-origin private Web API requests
InstagramWebAdapter
    ↑ normalized relationship records only
RelationshipService + local derived cache
    ↑ business-level MCP results
ChatGPT Work, Codex, or another MCP client
```

Instagram credentials are entered only in the visible `instagram.com` browser window. Passwords, cookies, CSRF tokens, request headers, and browser storage are never accepted as MCP arguments or returned in tool results.

## Requirements

- Windows 10/11 for the commands shown below. The Node server itself is portable, but the Secure Tunnel walkthrough is Windows/PowerShell focused.
- [Git](https://git-scm.com/downloads)
- [Node.js 24 or newer](https://nodejs.org/)
- A graphical desktop session for Instagram login, 2FA, and challenges
- An Instagram account you control
- One connection option:
  - ChatGPT Work with OpenAI Secure MCP Tunnel, or
  - a local MCP client that can launch a stdio server

This package is installed from source; it is not published to npm.

## Install

Open PowerShell and run:

```powershell
git clone https://github.com/rankes96-dev/instagram-relationship-mcp.git
Set-Location instagram-relationship-mcp

npm ci
npm run browser:install
npm run build
npm run check
```

`npm ci` installs the exact versions in `package-lock.json`. Playwright is pinned because every Playwright release expects matching browser binaries. Run `npm run browser:install` again after changing or updating Playwright.

## Choose how to connect

### Option A: ChatGPT Work through Secure MCP Tunnel

Use this path to keep the MCP server on your computer while making it available to a ChatGPT developer-mode app. Secure MCP Tunnel uses an outbound HTTPS connection; it does not require a public MCP port or inbound firewall rule.

#### 1. Prepare OpenAI access

1. Create or inspect a tunnel in [OpenAI Platform tunnel settings](https://platform.openai.com/settings/organization/tunnels).
2. Associate the tunnel with both the Platform organization that owns it and the target ChatGPT workspace. A tunnel associated only with a Platform organization may not appear in ChatGPT Work.
3. Ensure the operator has the correct Platform permissions:
   - **Tunnels Read + Manage** to create or edit tunnels.
   - **Tunnels Read + Use** to run `tunnel-client` and select the tunnel in ChatGPT.
4. Ensure Developer mode is allowed by the ChatGPT workspace policy. In ChatGPT, enable it under **Settings → Security and login → Developer mode**.
5. Create a dedicated runtime API key for a user or service account whose Platform organization role has **Tunnels Read + Use**. Restrict the key where supported. Do not use an admin key for the long-running tunnel process.

See OpenAI's [Secure MCP Tunnel guide](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels) for the current permission and workspace-association model.

#### 2. Install `tunnel-client`

Download the correct Windows archive from [the latest official tunnel-client release](https://github.com/openai/tunnel-client/releases/latest), extract it, and place `tunnel-client.exe` on `PATH`. Do not hard-code a release URL in an operational runbook.

Verify the binary:

```powershell
tunnel-client --version
tunnel-client help quickstart
```

#### 3. Create and validate a local stdio profile

From the repository directory, set the runtime key only in the current PowerShell process and initialize a named profile. Replace the placeholders; never commit the real key or tunnel ID.

```powershell
$env:CONTROL_PLANE_API_KEY = "<restricted-runtime-api-key>"
$repo = (Resolve-Path .).Path
$mcpCommand = "node `"$repo\scripts\tunnel-mcp-entry.mjs`""

tunnel-client init `
  --sample sample_mcp_stdio_local `
  --profile instagram-relationships `
  --tunnel-id "tunnel_<32-lowercase-hex-characters>" `
  --mcp-command $mcpCommand

tunnel-client doctor --profile instagram-relationships --explain
tunnel-client run --profile instagram-relationships
```

Keep `tunnel-client run` running while ChatGPT uses the app. Its local `/ui`, `/healthz`, `/readyz`, and `/metrics` surfaces show runtime health; the startup output prints the loopback URL.

The tunnel profile must launch [`scripts/tunnel-mcp-entry.mjs`](scripts/tunnel-mcp-entry.mjs), not `dist/index.js` directly. The wrapper removes OpenAI control-plane keys before importing the Instagram MCP server, so the Instagram subprocess does not inherit them. Prefer `CONTROL_PLANE_API_KEY`; `OPENAI_API_KEY` is only a tunnel-client fallback.

The Instagram server does not load `.env` automatically. Never put plaintext API keys in a tunnel profile or YAML file; use an `env:` reference, keep generated profiles outside Git (or ignored), and do not expose keys in command arguments or logs.

#### 4. Add the tunnel in ChatGPT Work

1. Open [ChatGPT Plugins](https://chatgpt.com/#settings/Connectors).
2. Select the plus button and enter a user-facing name and description.
3. Under **Connection**, choose **Tunnel**.
4. Select the tunnel or paste its `tunnel_id`.
5. Create the connection and review the four discovered tools.
6. Start a new chat and add the connection from the tools menu.

After changing tool names, descriptions, schemas, or annotations, restart the tunnel-backed MCP server, open the connection in ChatGPT Plugins, select **Refresh**, and test in a new chat.

### Option B: local stdio MCP client

For Codex or another local MCP client, build the project and configure the client with absolute paths:

```json
{
  "mcpServers": {
    "instagram-relationships": {
      "command": "node",
      "args": [
        "C:\\absolute\\path\\to\\instagram-relationship-mcp\\dist\\index.js"
      ]
    }
  }
}
```

A complete example is available at [`docs/mcp-config.example.json`](docs/mcp-config.example.json).

The transport is stdio. The server does not bind a port. Standard output is reserved for MCP JSON-RPC; sanitized operational logs go only to standard error.

## First login and first scan

Use this sequence in ChatGPT or your MCP client:

1. Call `instagram_connect`.
2. If it returns `LOGIN_PENDING`, complete Instagram login, 2FA, or a challenge only in the visible `instagram.com` Chromium window.
3. Call `instagram_connection_status` until it returns `CONNECTED`. `CHALLENGE_REQUIRED` means the visible window still needs action.
4. Call `instagram_get_non_followers`.

Never enter an Instagram password, cookie, session ID, CSRF token, or 2FA code into ChatGPT or an MCP tool argument. If a saved session is still valid, `instagram_connect` may return `connected: true` without opening a window.

## MCP tools

### `instagram_connect`

Reuses a verified saved session or opens one visible Chromium window and returns promptly with `LOGIN_PENDING`. Repeated calls while login is pending reuse the same attempt rather than opening another browser.

### `instagram_connection_status`

Performs one bounded session check. It can return `CONNECTED`, `LOGIN_PENDING`, `NOT_CONNECTED`, `SESSION_EXPIRED`, `LOGIN_REQUIRED`, or `CHALLENGE_REQUIRED`. Once interactive login succeeds, it confirms that the saved profile also works in the background before reporting `CONNECTED`.

### `instagram_get_non_followers`

Fetches Following pages, deduplicates numeric Instagram user IDs, checks relationship statuses in bounded batches, and returns only users with explicit `followed_by === false` status.

Inputs:

- `refresh` (optional boolean): ignore a fresh derived cache and scan again.
- `limit` (optional integer, 1–100): number of users to return; default 50.
- `cursor` (optional string): opaque signed cursor returned by the previous page. Do not combine it with `refresh: true`.

The result states whether it came from `live` or `cache`, when it was computed, whether the scan is complete, and how many statuses remain `UNKNOWN`. A partial fetch is never presented as a complete result.

### `instagram_disconnect`

Closes Chromium, securely removes the dedicated browser profile, and purges local account metadata and cached relationship data. The next Instagram operation requires manual login again.

## Configuration

All settings are optional. Supply them through the MCP client's environment or the operating system. The server does **not** load `.env` automatically.

| Variable | Default | Allowed values |
| --- | ---: | --- |
| `INSTAGRAM_MCP_DATA_DIR` | `%LOCALAPPDATA%\InstagramRelationshipMcp` | Absolute local directory |
| `INSTAGRAM_MCP_CACHE_TTL_MINUTES` | `15` | `1`–`1440` |
| `INSTAGRAM_MCP_FOLLOWING_PAGE_SIZE` | `12` | `1`–`200` |
| `INSTAGRAM_MCP_RELATIONSHIP_BATCH_SIZE` | `12` | `1`–`200` |
| `INSTAGRAM_MCP_RESULT_PAGE_SIZE` | `50` | `1`–`100` |
| `INSTAGRAM_MCP_LOGIN_TIMEOUT_SECONDS` | `300` | `30`–`1800` |
| `INSTAGRAM_MCP_NAVIGATION_TIMEOUT_SECONDS` | `45` | `5`–`180` |
| `INSTAGRAM_MCP_REQUEST_TIMEOUT_SECONDS` | `30` | `5`–`120` |
| `INSTAGRAM_MCP_INTER_REQUEST_DELAY_MS` | `250` | `0`–`10000` |
| `INSTAGRAM_MCP_NETWORK_RETRIES` | `1` | `0`–`3` |
| `INSTAGRAM_MCP_MAX_FOLLOWING_PAGES` | `10000` | `1`–`100000` |
| `INSTAGRAM_MCP_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error`, `silent` |

See [`.env.example`](.env.example) as an environment-variable reference only; copying it to `.env` has no effect. Put values in the MCP client's `env` block or set them in the process that launches the server, for example:

```powershell
$env:INSTAGRAM_MCP_LOG_LEVEL = "debug"
npm start
```

Batch sizes are runtime settings, not claimed Instagram API limits.

## Local data and security

By default, data is stored outside the repository:

```text
%LOCALAPPDATA%\InstagramRelationshipMcp\
├── instagram-profile\
└── instagram.db
```

The dedicated browser profile contains Instagram authentication state. The SQLite database stores only account metadata, normalized users, and derived relationship scans. Do not point `INSTAGRAM_MCP_DATA_DIR` at your normal Chrome profile, this repository, a synchronized folder, or a shared directory.

Never commit or share:

- `.env` or runtime API keys
- tunnel IDs or tunnel-client profiles
- the Playwright browser profile
- SQLite database, WAL, or SHM files
- cookies, headers, HAR files, screenshots, or network dumps
- tunnel or MCP logs containing private account context

The included `.gitignore` excludes these local artifacts, but ignore rules are not an access-control boundary. See [`SECURITY.md`](SECURITY.md) for the threat model and safe diagnostics policy.

## Update

Stop the foreground tunnel process, then run:

```powershell
git pull
npm ci
npm run browser:install
npm run check
tunnel-client run --profile instagram-relationships
```

The browser profile and database live outside the repository and survive source updates unless `instagram_disconnect` is called. Refresh the ChatGPT developer-mode connection after MCP metadata changes.

## Development

```powershell
npm run build
npm test
npm run smoke:mcp
npm run check
npm run inspect
```

- `npm run check` builds the project, runs every test, and runs the stdio smoke test.
- `npm run smoke:mcp` builds and verifies both legacy stdio and MCP 2026-07-28 without opening a browser.
- Run `npm run build` before `npm run inspect`; the Inspector uses `dist/index.js`.
- Browser and live-account scenarios require manual testing with an account you control. Automated tests use sanitized synthetic fixtures and contain no production credentials or usernames.

## Troubleshooting

### The tunnel is not visible in ChatGPT

Confirm that the tunnel is associated with the target ChatGPT workspace—not only a Platform organization—and that the app creator has Tunnels Read + Use. Developer mode must also be permitted by workspace policy.

### ChatGPT cannot discover or call tools

Keep `tunnel-client run --profile instagram-relationships` running, then execute:

```powershell
tunnel-client doctor --profile instagram-relationships --explain
```

Check the loopback `/ui` and ensure health, readiness, and the main MCP channel are green. If metadata is stale, refresh the connection in ChatGPT Plugins and start a new chat.

### No Instagram window opens

Run `npm run browser:install`, confirm a graphical desktop session is active, and call `instagram_connect` again. A valid saved session may intentionally connect without a visible window.

### The result remains `LOGIN_PENDING`

Finish the visible browser flow, then call `instagram_connection_status`. Do not repeatedly start new connections. If the browser was closed, call `instagram_connect` once to start a fresh attempt.

### Instagram reports a rate limit or private API change

Stop and wait after `INSTAGRAM_RATE_LIMITED`; do not retry aggressively. `PRIVATE_API_CHANGED` means the expected private response contract no longer matches and requires a reviewed code update, not a guessed parser fallback.

## Licensing and reference boundary

No software license is currently included. Source availability on GitHub does not grant an open-source reuse license.

[IG-Cleaner](https://github.com/denoyey/IG-Cleaner) was reviewed only for the general idea of manual local login and a dedicated persistent browser profile. This is a clean-room implementation and does not copy IG-Cleaner's Selenium, DOM-scrolling, username-comparison, or write-action design.

TDQS

A4.4/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a distinct role: connect initiates login, status checks login state, get_non_followers performs the main analysis, and disconnect cleans up. There is no overlap or ambiguity between them.

Naming Consistency4/5

All tools share the 'instagram_' prefix, but the verb usage is slightly inconsistent: 'connect' and 'disconnect' are bare verbs, 'connection_status' is a noun phrase, and 'get_non_followers' uses a get_ prefix. Overall the pattern is readable and predictable.

Tool Count5/5

With 4 tools, the server is well-scoped for its specific purpose of managing Instagram relationships. Each tool is necessary and none feel redundant.

Completeness4/5

The set covers the full lifecycle from connect to disconnect, with the core non-follower analysis in between. Minor gaps exist, such as no explicit refresh action or follow/unfollow operations, but these are outside the stated purpose.

Maintenance

ActivitySlowing
ResponsivenessNo issues