Skip to main content
Glama

Your Instagram saved posts — deep-read by AI, searchable forever, on your machine.

License: MIT PRs Welcome Stars

Claude Code Cursor Claude Desktop

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 | bash

  • Windows (PowerShell): irm https://raw.githubusercontent.com/pjpoulose/PIL/master/bootstrap.ps1 | iex

Then 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 .gitignore blocks databases, configs, and exports from ever being committed.

PIL dashboard concept — gallery of deep-read saved posts with a synthesized answer

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 stop

That'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 stop

Wiring 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

search_posts

Text search over captions + deep-read knowledge, with optional folder/tag filters

get_post

Full record for one post: summary, key points, how-to, links, folders, tags

list_folders

Your saved collections with indexed counts

list_tags

Tags by usage

library_stats

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 set account_id to your user_fbid from instagram-cli accounts.

  • Extraction is slow on huge libraries — it's resume-safe; just re-run it.

  • 429 rate limits are handled with backoff inside the scripts.

  • The MCP server needs mcp<2 in 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 tools
get_postB

Return everything PIL knows about one post: metadata, deep-read knowledge (summary, key_points, howto, links), folders, and tags.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes

TDQS

B3.2/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

C2.4/5.0
Behavior2/5

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.

Conciseness3/5

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.

Completeness2/5

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.

Parameters4/5

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.

Purpose2/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNo
limitNo
queryYes
folderNo

TDQS

A3.8/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

  1. 5 tool updatesv0.1.0
    • First observedget_post
    • First observedlibrary_stats
    • First observedlist_folders
    • First observedlist_tags
    • First observedsearch_posts

TDQS

B3.4/5.0

Scored across 5 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A 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
  • A
    license
    Not graded
    quality
    B
    maintenance
    Privacy-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
  • A
    license
    Not graded
    quality
    D
    maintenance
    Fully local, privacy-first MCP server that turns exported X/Twitter bookmarks into a searchable, enriched knowledge base for Claude Desktop and Claude Code.
    2
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    A 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.
    8
    63 npm
    MIT