Social Harvest MCP
A local Instagram exporter for MCP clients that exports profile metadata, posts, reels, captions, and comments via a manually authenticated browser session.
README
Social Harvest MCP
Local-first Instagram export, inbox search, and messaging automation for MCP-compatible AI agents.
Social Harvest connects Claude Code, Codex, OpenCode, and other MCP clients to Instagram through a browser session that you authenticate manually. It can export profiles and media, synchronize a searchable local inbox, send messages and attachments, manage common conversation actions, and monitor inbox changes.
Social Harvest executes locally, and all persistent data stays on your machine. Instagram traffic originates from the local authenticated browser session.
[!IMPORTANT] Social Harvest uses Instagram’s authenticated web experience—not Meta OAuth, the official Instagram APIs, or webhooks. Instagram can change its endpoints and controls without notice. Use this project only with accounts and content you are authorized to access.
Contents
- Highlights
- How it works
- Quick start
- Common workflows
- MCP tools
- Connect an MCP client
- Storage and privacy
- Operational semantics
- Limitations
- Development
Highlights
| Area | What Social Harvest provides |
|---|---|
| Profile export | Profile metadata, posts, reels, captions, hashtags, mentions, reported metrics, bounded comments, and optional active stories |
| Media archive | Streaming downloads for profile images, posts, reels, carousels, stories, thumbnails, and DM attachments, with SHA-256 metadata |
| Local inbox | Primary, General, Requests, groups, and spam/hidden system buckets synchronized into SQLite |
| Search | FTS5 full-text message search with thread, folder, sender, and date filters |
| Messaging | Text, local images, video, audio, GIF/sticker search, group-thread targeting, and replies |
| Conversation actions | React or unreact, mark read or unread, and accept or decline message requests |
| Automation | Background jobs, adaptive inbox monitoring, durable events, and optional idempotency keys |
| Agent safety | Bounded reads, explicit side-effect annotations, fail-closed message targeting, and confirmed purge operations |
How it works
flowchart LR
A[MCP client] --> B[Social Harvest MCP]
C[CLI] --> D[Instagram services]
B --> D
D --> E[Shared Playwright session]
E --> F[Instagram Web]
D --> G[JSON and NDJSON exports]
D --> H[SQLite inbox and FTS5]
D --> I[Content-addressed media]
H --> J[Durable event queue]
Social Harvest launches Chromium with a persistent profile directory. You complete login and any verification yourself; later CLI and MCP operations reuse that authenticated profile. Concurrent operations share one browser context while receiving isolated pages.
Quick start
Requirements
- Node.js 20 or newer
- npm
- An Instagram account permitted to access the requested content
1. Install
git clone https://github.com/IotA-asce/social-harvest-mcp.git
cd social-harvest-mcp
npm install
npx playwright install chromium
2. Authenticate Instagram
npm run login
A visible browser opens. Log in, complete any verification, return to the terminal, and press Enter. The session is saved under .playwright-profile/instagram/, which is excluded from Git.
Social Harvest never asks for or stores your Instagram password directly.
3. Verify the environment
npm run doctor
The doctor checks the Node.js version, Playwright browser availability, profile directory, and authenticated Instagram session.
4. Start the MCP server
npm run mcp
For client-specific configuration, see Connect an MCP client.
Common workflows
Export a profile and its media
npm run scrape -- --username nasa --max-items 20 --max-comments 50
Media downloading is enabled by default. Normalized assets retain their remote URL and receive local path, MIME type, byte size, and SHA-256 metadata.
Exports use a deterministic run structure:
exports/
└── instagram/
└── nasa/
└── 2026-07-28T.../
├── manifest.json
├── profile.json
├── posts.ndjson
├── reels.ndjson
├── stories.ndjson
├── comments.ndjson
├── errors.ndjson
├── checkpoint.json
└── media/
Only files containing records are created.
To backfill or resume media for an existing export:
npm run download-media -- --export-dir ./exports/instagram/nasa/2026-07-28T...
To watch the browser and include active stories:
npm run scrape -- \
--username nasa \
--max-items 20 \
--include-stories true \
--headless false
[!WARNING] Opening an active story creates Instagram’s normal story-view receipt. The story owner may see the authenticated account in their viewer list. Story export is disabled by default.
Synchronize the inbox
npm run sync-inbox -- --folders primary,general,requests,system
npm run inbox-status
Inbox sync stores normalized threads, participants, messages, attachments, reactions, receipts, and pagination state in SQLite. Attachments are stored by content hash so identical files can be reused safely.
The first successful sync of each account and folder is a silent seed: existing history is stored without generating a flood of “new message” events. Later syncs produce deduplicated events for observed changes.
Folder names exposed by the CLI and MCP are normalized as follows:
| Folder | Meaning |
|---|---|
primary |
Instagram Primary, including the internal main label |
general |
Instagram General |
requests |
Pending message requests |
system |
Current spam and hidden-request buckets |
Capture one conversation snapshot
Use instagram_get_messages when an agent needs a portable, bounded snapshot for an exact username. The complete requested history is written locally while the MCP response returns only a small preview.
dm-exports/
└── instagram/
└── username/
└── 2026-07-28T.../
├── manifest.json
├── thread.json
├── messages.ndjson
└── media/
The configured message maximum is 1,000 records per snapshot.
[!NOTE] Opening or fetching a conversation may apply Instagram’s normal read-state behavior.
Send messages and attachments
instagram_send_message targets exactly one username or stable threadId. Thread targeting supports one-to-one conversations and groups.
Supported message parts:
- text up to 1,000 characters;
- up to 10 local JPEG, PNG, MP4, MOV, or supported audio files;
- one GIF/sticker selected with
gifQueryand optionalgifIndex; - an optional
replyToMessageIdafter the conversation has been synchronized.
Text, an attachment batch, and a GIF/sticker are submitted as separate message parts. Local .gif uploads are not accepted by the current Instagram composer; use gifQuery instead.
Example agent request:
Send “The draft is ready” and ./preview.png to the synchronized Instagram
thread 123456. Use clientRequestId "draft-preview-2026-07-28".
[!CAUTION] Messaging, reactions, read-state changes, and request decisions happen immediately. Social Harvest does not provide a draft-confirm step.
Monitor changes
instagram_start_inbox_monitor performs an immediate sync and then polls at a 60-second interval with ±15% jitter. Transient failures use exponential backoff capped at 15 minutes.
Authentication, checkpoint, or challenge failures pause the monitor and release its browser session hold. Monitors never restart automatically after a pause or process restart.
Use:
instagram_inbox_monitor_statusto inspect the current process-local state;instagram_stop_inbox_monitorto stop polling;instagram_list_inbox_eventsto read durable local events;instagram_ack_inbox_eventsto mark events acknowledged locally.
Acknowledgement does not change Instagram state and does not delete the event record.
MCP tools
Core, jobs, and exports
| Tool | Purpose | Side effects |
|---|---|---|
social_capabilities |
Describe supported platforms, record types, and limits | None |
instagram_start_export |
Start a bounded profile export job | Reads Instagram; writes local files |
social_job_status |
Read status for any retained background job | None |
social_export_status |
Compatibility alias for social_job_status |
None |
social_list_export_files |
List files in an export directory | None |
social_read_export |
Read bounded JSON or NDJSON records | None |
Inbox sync and search
| Tool | Purpose | Side effects |
|---|---|---|
instagram_start_inbox_sync |
Synchronize selected inbox folders | Reads Instagram; writes local data |
instagram_get_messages |
Capture a bounded exact-username snapshot and index it locally | May mark a conversation read; writes local data |
instagram_list_conversations |
List locally synchronized threads with filters | None |
instagram_get_conversation |
Read bounded local history, optionally refreshing first | Optional Instagram read; optional local writes |
instagram_search_messages |
Search local messages through FTS5 | None |
Immediate Instagram actions
| Tool | Purpose | Side effects |
|---|---|---|
instagram_send_message |
Send text, local attachments, GIF/sticker content, or a reply | Immediate Instagram write |
instagram_set_message_reaction |
Set or remove the viewer’s emoji reaction | Immediate Instagram write |
instagram_set_conversation_state |
Mark a thread read or unread | Immediate Instagram write |
instagram_handle_message_request |
Accept or decline a pending request | Immediate, potentially destructive Instagram write |
Monitoring, events, and retention
| Tool | Purpose | Side effects |
|---|---|---|
instagram_start_inbox_monitor |
Start process-local adaptive polling | Repeated Instagram reads and local writes |
instagram_inbox_monitor_status |
Read monitor state | None |
instagram_stop_inbox_monitor |
Stop polling and release the held session | Local process state only |
instagram_list_inbox_events |
Read durable local events | None |
instagram_ack_inbox_events |
Mark events acknowledged | Local database write only |
instagram_purge_inbox_data |
Delete scoped thread/message data and unreferenced media | Destructive local write; requires confirm=true |
Export and inbox-sync tools return immediately with a job ID. Poll them with social_job_status. Jobs are retained in memory for the lifetime of the MCP server process.
Connect an MCP client
Use the included templates and replace the example project path with an absolute path to your clone.
| Client | Configuration template |
|---|---|
| Claude Code | examples/claude-code.json |
| Codex | examples/codex.toml |
| OpenCode | examples/opencode.json |
A typical agent workflow looks like this:
Use Social Harvest to synchronize my Instagram inbox without downloading media.
Poll the job until it completes, list unread conversations, and summarize the
five most recent messages without sending or changing anything.
Storage and privacy
Social Harvest excludes its default private-data directories from Git:
| Path | Contents |
|---|---|
.playwright-profile/ |
Authenticated browser state and cookies |
exports/ |
Profile exports and public/session-visible media |
dm-exports/ |
Private bounded conversation snapshots |
inbox-data/ |
SQLite inbox, event queue, mutation audit, and private media |
The inbox layout is:
inbox-data/
├── instagram.sqlite
├── instagram.sqlite-wal
├── instagram.sqlite-shm
└── media/
└── ab/
└── abcdef...<original-extension>
On POSIX platforms, Social Harvest forces inbox directories to mode 0700 and the database/media files it creates to mode 0600.
[!WARNING] The database, media, exports, and browser profile are plaintext. Filesystem permissions and the security of your device and backups are the only protection in this release.
instagram_purge_inbox_data removes a selected thread or messages before a timestamp and garbage-collects only media no longer referenced by another stored message. Durable event and mutation-audit rows remain in the SQLite database. To erase all local inbox state, stop Social Harvest and remove inbox-data/ yourself.
See SECURITY.md for acceptable-use boundaries and handling guidance.
Operational semantics
Submitted is not delivered
A mutation result with status: "submitted" means Instagram accepted the browser request or composer interaction. It is not a delivery receipt, read receipt, or guarantee that Instagram will retain the message.
Retry safety
Mutation tools accept an optional clientRequestId. Once a request ID completes, retrying it returns the stored result without repeating the browser interaction. Reusing the same ID for a different operation is rejected.
Calls without a request ID are not idempotent and can repeat an action or send duplicate content.
Partial sends
When a call includes text, attachments, and a GIF/sticker, Instagram receives separate parts. A later part can fail after an earlier part was submitted. Social Harvest reports this as direct_send_partial; inspect submitted_parts before retrying.
Fail-closed message mapping
Replies and reactions map a synchronized message ID to one unique rendered text fingerprint. If the message is not rendered, contains no uniquely usable text, or matches multiple visible messages, Social Harvest refuses the action instead of risking interaction with the wrong message. Media-only and duplicate-text messages can therefore require a different target or a later adapter improvement.
Completeness
“Complete” means all records discoverable by the authenticated browser session within the configured bounds. It does not include deleted content, expired stories, inaccessible private content, platform-hidden content, or records Instagram did not load.
Absence is never treated as deletion. Only explicit tombstones or unsend state are stored as deleted messages. Media download failures do not roll back successfully synchronized message metadata.
Limitations
- Instagram’s private web endpoints, response shapes, numeric folder selectors, and rendered controls can change without notice.
- Comments and stories are best-effort and bounded.
- Inbox history is bounded by configured folder, thread, and message pagination limits.
- Disappearing or unsupported message types may retain metadata without usable content.
- Story collection creates a normal story-view receipt.
- Reading conversations can create normal read-state behavior.
- The project does not solve CAPTCHAs, bypass access controls, rotate proxies, or attempt anti-bot evasion.
- Eligible professional accounts should prefer an official Instagram API when its permissions and data coverage satisfy the use case.
Development
Run the complete test suite and syntax checks:
npm test
npm run check
Useful diagnostics:
npm run doctor
npm run inbox-status
npm audit
The project uses Node.js ESM, Playwright, the Model Context Protocol TypeScript SDK, Zod, better-sqlite3, SQLite WAL/FTS5, and the built-in Node.js test runner.
License
Released under the MIT License.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。