PIL
Provides read-only tools to search and retrieve deep-read knowledge from your local Personal Instagram Library, including saved posts, collections, tags, summaries, key points, how-to steps, and extracted links.
Your Instagram saved posts — deep-read by AI, searchable forever, on your machine.
Get started · How it works · Other AI tools · What's inside
Get PIL in 3 steps
1. Copy-paste this into Muse:
Clone https://github.com/pjpoulose/PIL into your workspace and follow its SKILL.md to set up my Personal Instagram Library. Work through it step by step — install, build the library from my saved posts, and hand me the installable app.2. When it asks, link your Instagram (one tap in your browser).
3. Download the app file it sends you → unzip → double-click Start PIL → click Install.
That's the whole thing. Your Muse does the setup, the reading, and the building. Want it on your phone too? Just ask your Muse — it handles that as well.
Mac / Linux:
curl -fsSL https://raw.githubusercontent.com/pjpoulose/PIL/master/bootstrap.sh | bashWindows (PowerShell):
irm https://raw.githubusercontent.com/pjpoulose/PIL/master/bootstrap.ps1 | iexThen build your library with the commands in Manual setup.
Every day you save posts you'll never find again. PIL (Personal Instagram Library) turns your Instagram saved collection into a private knowledge base on your own machine: every post deep-read by vision AI — narrative summaries, key points, how-to steps, links it discusses, automatic tags — searchable in seconds and queryable live from your AI coding tools.
Code is shared, data stays home. This repo contains only code, the database schema, config examples, and docs. Your saved posts, captions, and account details never leave your computer — ingestion, extraction, search, and the MCP server all run locally. A
.gitignoreblocks databases, configs, and exports from ever being committed.

Concept mockup with sample data — your library looks like this, with your posts.

Want your Claude, Codex, or Cursor to access this?
Connect the read-only MCP server and your other AI tools can query your library live — always current, nothing to re-upload. Your Muse can wire it up for you. How to connect →

How it works
flowchart LR
A["Instagram saved posts"] -->|"ingest"| B["Local SQLite on your machine"]
B -->|"vision AI<br/>deep read"| C["Summaries, key points, how-tos, links, tags"]
C --> D["Query two ways"]
D --> E["MCP server — Cursor, Claude Code, Desktop"]
D --> F["Static JSON export — any AI tool"]Related MCP server: potluck

Manual setup (do it yourself)
Prerequisites: python3 (3.11+) and instagram-cli with your Instagram
account linked (run instagram-cli accounts — it must list your account).
The one-line installer at the top of this page handles all of this for you.
# 1. Get the code
git clone https://github.com/pjpoulose/PIL.git pil && cd pil
# 2. Configure (your data lives in data_dir, default ~/.local/share/pil)
cp pil.config.example.json ~/.config/pil/pil.config.json
# edit it: set account_id to your user_fbid from `instagram-cli accounts`
# 3. MCP server dependency
pip install "mcp<2"Build your library (each step is resume-safe — re-run any time):
cd bin
python3 ingest_saved.py # collections + saved posts
python3 extract_content.py # vision-AI deep read (batches of 25)
python3 tag_all.py # programmatic tags for untagged posts
python3 export_web.py # static JSON export -> <data_dir>/web_data.json
python3 export_html.py # searchable HTML dashboard -> <data_dir>/pil_library.html
python3 export_pwa.py # installable PWA bundle -> <data_dir>/pwa/Ask it anything — via the live MCP server or the static export:
python3 bin/mcp_server.py # read-only, stdio — Ctrl-C to stopThat's it. Re-run ingest_saved.py whenever you save new posts; extract_content.py
only processes posts it hasn't seen yet.
The app: ask your Muse to build and send it — see Your library as an app.

Why not just scroll your saved tab?
Instagram saved tab | PIL | |
Find a post from 2 years ago | Scroll endlessly | Full-text search in seconds |
Remember what a post actually said | Rewatch / reread it | AI summary, key points, how-to |
Links a post mentioned | Gone unless you saved them | Extracted and clickable |
Use it inside your AI tools | Screenshots and retyping | MCP server or JSON export |
Where your data lives | Meta's servers | Your machine, SQLite |

Ask your library from other AI tools
Live (recommended): MCP. Point any MCP-compatible assistant at the read-only server and every question reads your current database — always up to date, no exports, no re-uploads:
python3 /path/to/pil/bin/mcp_server.py # stdio; Ctrl-C to stopWiring for Claude Code, Claude Desktop, and Cursor: references/mcp_clients.md. Any MCP-compatible client works — and your Muse can connect it for you if you'd rather not touch configs. Available tools:
Tool | What it does |
| Text search over captions + deep-read knowledge, with optional folder/tag filters |
| Full record for one post: summary, key points, how-to, links, folders, tags |
| Your saved collections with indexed counts |
| Tags by usage |
| Totals + deep-read coverage per field |
The server opens the database with SQLite mode=ro and exposes SELECT-only
tools — it cannot modify your library. (Attack-tested: SQL injection, write
attempts, and limit abuse all verified blocked.)
Snapshot: file upload. Ask your Muse to send you the web_data.json file
(built with export_web.py): every post with its deep-read knowledge in one
file. Attach it to any AI chat (Claude, ChatGPT, …) and ask questions like any
document. It's frozen at export time — a snapshot, not a live connection — so
ask your Muse for a fresh copy after you save new posts.
Directly (advanced). The database is plain SQLite at <data_dir>/pil.sqlite
(schema in schema.sql). Open it read-only with any SQLite tool.
These files hold your personal Instagram data — keep them on your own machine and only share them with tools you trust.

Your library as an app
Your Muse builds the app for you and sends it to you — for your computer and your phone. Just ask:
"Send me my PIL app" — download the file it sends you, unzip, double-click Start PIL, click Install. It lives on your computer like any other app and works fully offline.
"Put PIL on my phone" — it handles the publishing and gives you a QR code to scan. Tap Install (Android) or Share → Add to Home Screen (iPhone).
No commands, no hosting setup, no terminal — your Muse takes care of all of it.

What's inside
pil/
├── assets/logo.svg # the seal above
├── SKILL.md # skill definition (for Muse)
├── README.md # this file
├── LICENSE # MIT
├── schema.sql # the five tables: folders, posts, post_folders, knowledge, tags
├── pil.config.example.json # copy to pil.config.json and set your account_id
├── bootstrap.sh # one-line installer for Mac/Linux
├── bootstrap.ps1 # one-line installer for Windows
├── bin/
│ ├── pil_common.py # config resolution + DB helpers
│ ├── ingest_saved.py # step 1: ingest (resume-safe)
│ ├── extract_content.py # step 2: vision-AI extraction (resume-safe)
│ ├── tag_all.py # step 3: tagging
│ ├── export_web.py # step 4: static export
│ ├── export_html.py # step 5: self-contained HTML dashboard
│ ├── export_pwa.py # step 6: installable PWA bundle (manifest + SW + icons)
│ ├── publish_pwa.py # step 7: publish PWA to your own host for phone install
│ └── mcp_server.py # read-only MCP server (stdio)
└── references/
└── mcp_clients.md # Cursor / Claude Code / Claude Desktop wiring
FAQ
Do I need to know how to code? No. Copy-paste the prompt at the top of this page into Muse — it does everything with you. The only things you'll do yourself are linking Instagram (one tap in your browser) and downloading the app file it sends you.
Where does my Instagram data go? Nowhere. Code is shared, data stays home: your saved posts, captions, and account details never leave your computer. Nothing is uploaded to us — there is no "us"; there's no server, no account, no cloud.
Does it cost anything? PIL is free and open-source (MIT). There is no subscription and no account to create. The AI deep-read step runs through your own Instagram/AI setup.
Does the app work offline? Yes. Once installed, the desktop and phone apps run fully offline — search, rooms, tags, and answers all work without internet.
Which phones and computers? Windows, Mac, and Linux for the desktop app; iPhone and Android for the phone app. On Android, tap Install app in Chrome; on iPhone, use Safari's Share → Add to Home Screen.
I saved new posts — how do I add them? Just tell your Muse "I saved new posts." It fetches and deep-reads only the new ones, then refreshes your app and files.
Can I share my library with someone? Your library is files on your machine — you can copy them to someone else's computer, but they contain your personal Instagram data. Treat them like anything private: don't publish or upload them anywhere public.
Something failed — what now?
Tell your Muse what you saw — it can diagnose and fix it directly. (Running
the manual setup below? Re-run the installer — it's safe to run any number of
times — and check python3 --version is 3.11+.)

Troubleshooting
Something wrong? Tell your Muse what happened — it can diagnose and fix most issues itself.
Running the manual setup yourself?
PIL account_id is not configured→ copy the example config and setaccount_idto youruser_fbidfrominstagram-cli accounts.Extraction is slow on huge libraries — it's resume-safe; just re-run it.
429rate limits are handled with backoff inside the scripts.The MCP server needs
mcp<2in the Python that runs it (2.x renamed the API).

Contributing
PRs and issues welcome — better extraction prompts, new query clients, new export formats. Fork it, ship it, make it yours. If it saved you from the endless scroll, a star helps others find it.

License
MIT. Built by Paul Poulose.
Available Tools
5 toolsget_postB
Return everything PIL knows about one post: metadata, deep-read knowledge (summary, key_points, howto, links), folders, and tags.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden, and it does disclose the return content well (summary, key_points, howto, links, folders, tags). However, it says nothing about permission requirements, behavior when post_id is unknown/invalid, or any rate limits, so key behavioral traits remain undisclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence that leads with the verb and resource and then lists return contents; every clause carries information. The parenthetical enumeration is dense but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no annotations and no output schema, the description partly compensates by detailing what is returned, which is genuinely useful. It still omits how to obtain post_id and what happens on a missing/invalid post, which an agent needs for reliable invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for the single required post_id parameter, and the description never mentions the parameter at all. It adds no meaning about where post_id comes from (e.g., from search_posts or list results) or its expected format, so the parameter is undocumented in both places.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Return') and resource ('one post') and enumerates the content categories returned (metadata, deep-read knowledge, folders, tags). It implicitly distinguishes itself from sibling search_posts by scoping to a single post, though it never names the sibling explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: call this when you have a post_id and need the full record for that post. There is no explicit when-to-use/when-not guidance and no mention of alternatives such as search_posts for discovery, leaving the routing decision to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
library_statsC
Totals for the library plus deep-read coverage per field.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It doesn't state whether the result is a computed aggregate, whether it is cached, what 'deep-read coverage' implies, or anything about permissions or cost beyond the bare summary phrase.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence, which is appropriately sized for a zero-parameter tool. However, 'deep-read coverage per field' is jargon that only becomes meaningful with more context that the description doesn't provide.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless stats tool with no annotations and no output schema, the description needs to explain what the returned totals represent and how 'coverage per field' is computed. It doesn't, leaving the agent unable to interpret or trust the output.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate. Baseline 4 is appropriate — no parameter guidance is needed, and the description doesn't need to explain any inputs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The phrase 'Totals for the library plus deep-read coverage per field' is vague about what resources are counted and what 'deep-read coverage per field' means. It gestures at an aggregate/stats operation but doesn't clarify the domain (posts? tags? folders?) versus siblings like search_posts or list_tags.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No indication of when to use this tool versus the sibling list/search tools. An agent cannot tell whether this is a summary view, a health check, or a prerequisite for other calls.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_foldersA
List the user's saved-post collections (rooms). item_count is the
count Instagram reports; mapped_posts is how many are actually indexed.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. 'List' implies a read-only operation, and the item_count vs mapped_posts caveat usefully discloses a data-quality gap, but ordering, pagination, and auth requirements are unstated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, front-loaded with the action and resource, then the field caveat. Nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description's explanation of item_count and mapped_posts does real work. It stops short of describing the folder identifier/name fields or whether results are paginated, which for a list tool without an output schema would have been worth one more clause.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4; there is nothing for the description to disambiguate, and it correctly avoids inventing parameter guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (list saved-post collections) and clarifies the domain term 'rooms'. The resource is clearly distinct from siblings like list_tags or get_post, though no sibling is named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied by the verb 'List' — an agent can infer this is the browse-collections entry point, but there is no statement of when to prefer it over library_stats or search_posts, and no prerequisites or exclusions are given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tagsC
List tags by usage, most-used first.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It discloses the sort order, which is useful, but says nothing about pagination, result cap behavior, auth requirements, or result shape. One behavioral fact is not enough for an unannotated tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single clause, front-loaded with the verb and resource, with the ordering constraint attached. Nothing wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only list tool with no output schema, the core purpose and sort order are covered, but the sole parameter's meaning and any result-size behavior are absent, leaving a real gap for an agent sizing its request.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
One parameter (limit, default 100) with 0% schema description coverage, and the description never mentions it. The agent gets no indication of what limit governs or what happens at the boundary.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List tags') and adds a meaningful ordering scope ('most-used first'). It doesn't name or differentiate from any sibling, but the purpose is immediately clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use, when-not, or alternative guidance. The ordering hint implies a discovery/browsing scenario, but nothing tells the agent how this relates to siblings like search_posts or library_stats.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_postsA
Search the library. Matches query text against captions and deep-read
knowledge (summaries, key points, how-to). Optionally restrict to one
folder (collection id or folder name) and/or one tag. Returns up to limit
matches (1-50) as compact cards with a has_knowledge flag.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | ||
| limit | No | ||
| query | Yes | ||
| folder | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does disclose meaningful behavior: the match fields (captions plus summaries/key points/how-to), the return shape (compact cards with a has_knowledge flag), and the limit bound (1-50). It omits read-only/safety framing and any behavior for empty or no-match results, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences, front-loaded with the action before the filters and return shape; nothing is padded. Very little could be cut without losing parameter meaning, though the parenthetical listing of knowledge types is slightly verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema and no annotations, so the description properly covers the return format (compact cards, has_knowledge flag) and the searchable surface. It is adequate for an agent to call correctly, with only edge-case behavior (no results, multiple tags) unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate and largely does: query is defined by what it matches against, folder accepts 'collection id or folder name', tag is singular ('one tag'), and limit is bounded at 1-50. Only minor gaps remain, such as tag matching semantics or default limit behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Search) and resource (the library) and clarifies what is matched (captions and deep-read knowledge), which distinguishes it from get_post and the list_* siblings. It does not name those siblings explicitly, so the differentiation is inferred rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The word 'Optionally restrict to one folder' and/or one tag' implies the usage pattern (broad search, then narrow), but there is no explicit when-to-use statement, no when-not, and no named alternative such as get_post for known IDs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
get_post - First observed
library_stats - First observed
list_folders - First observed
list_tags - First observed
search_posts
TDQS
Scored across 5 tools
Each tool targets a clearly distinct purpose: single-post retrieval (get_post), collection listing (list_folders), tag listing (list_tags), aggregate metrics (library_stats), and query-based retrieval (search_posts). There is no meaningful overlap between them, so an agent can select the right tool unambiguously.
Four tools follow a clean verb_noun pattern (get_post, list_folders, list_tags, search_posts). library_stats breaks the pattern by being noun-only with no verb, a minor deviation that doesn't hurt readability.
Five tools is well-scoped for a read/browse library server. Each tool earns its place, covering retrieval, browsing by folder/tag, search, and stats without redundancy.
The read-side surface is solid: single-item lookup, folder/tag enumeration, search, and stats cover the main workflows. Minor gaps exist—no plain list-all-posts or folder-contents tool—but search_posts with folder/tag filters largely compensates.
Maintenance
Related MCP Connectors
- MysocialOAuthio.mysocial
Social media MCP server: your Instagram, TikTok, YouTube, LinkedIn and Threads history for your AI.
Personal knowledge base MCP server with semantic search, auto-categorization, metadata extraction
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
Instagram MCP for public posts, comments, replies, users, and video/Reels speech-to-text.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA private, local-first MCP server that gives any AI long-term memory — its own diary. Zero models, zero network, zero subscription; smarter search than Notion, running entirely on your machine.MIT
- AlicenseNot gradedqualityBmaintenancePrivacy-first personal knowledge database for your AI that ingests data exports (Google Takeout first) and exposes them via MCP tools like search and list items. Runs locally with no cloud or telemetry.MIT
- AlicenseNot gradedqualityDmaintenanceFully local, privacy-first MCP server that turns exported X/Twitter bookmarks into a searchable, enriched knowledge base for Claude Desktop and Claude Code.2MIT
- AlicenseAqualityAmaintenanceA local, read-only MCP server that makes bookmarks from browsers, read-later apps, and export files accessible to AI assistants, enabling search, triage, and summarization without sending data anywhere.863 npmMIT