Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.git
**/node_modules
**/dist
**/.env*
.playwright-cli
output
33 changes: 33 additions & 0 deletions .github/workflows/mcp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: MCP app
on:
push:
paths: ['mcp/**', 'infra/mcp-gateway/**', 'Dockerfile.vercel', 'vercel.json', '.vercelignore', '.dockerignore', '.github/workflows/mcp.yml']
pull_request:
paths: ['mcp/**', 'infra/mcp-gateway/**', 'Dockerfile.vercel', 'vercel.json', '.vercelignore', '.dockerignore', '.github/workflows/mcp.yml']
permissions:
contents: read
jobs:
check:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: mcp
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: mcp/package-lock.json
- run: npm ci
- run: npm run check
- run: npm audit --omit=dev --audit-level=high
container:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -f mcp/Dockerfile -t meta-scraper .
- run: docker build -f Dockerfile.vercel -t meta-scraper-vercel .
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ test-image/*.webp
test-image/*.tiff

# Exiftool temp files
*_original
*_original

# Local browser verification artifacts
.playwright-cli/
output/
.vercel
.env*
25 changes: 25 additions & 0 deletions .vercelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/node_modules/
/.git/
/.agents/
/.env*
/test-image/
/output/
/.playwright-cli/
/src/
/out/
/dist/
/package.json
/package-lock.json
/skills-lock.json
/*.vsix
/.vscode/
/.github/
/test/
/docs/
/images/
/extension.js
/mcp/node_modules/
/mcp/dist/
/mcp/test/
/mcp/deploy/
/mcp/.env*
8 changes: 7 additions & 1 deletion .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ CHANGELOG.md
# Additional build artifacts to exclude
**/*.vsix

images/output.gif
images/output.gif
mcp/**
docs/**
.github/**
.dockerignore
output/**
.playwright-cli/**
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# MetaScraper

Follow [Lucci Labs MCP conventions](infra/mcp-gateway/CONVENTIONS.md) for deployment, routing, environment isolation and client naming. The shared product registry lives in `infra/mcp-gateway/registry.json`.

- Production endpoint: `https://mcp.luccilabs.xyz/metascraper`.
- Sandbox endpoint: `https://sandbox.mcp.luccilabs.xyz/metascraper`.
- Keep legacy URLs functional while existing clients migrate.
- Pin Vercel project/team IDs; use the deployment helper documented in the conventions.
- Run `npm --prefix mcp run check` for relevant server/widget changes. Live environment acceptance uses only synthetic photos: `node mcp/scripts/verify-environments.mjs`.
- Never supply production database credentials to sandbox or preview deployments.
- Preserve unrelated working-tree changes, including root extension package files.
20 changes: 20 additions & 0 deletions Dockerfile.vercel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY mcp/package*.json ./
RUN npm ci
COPY mcp/ ./
RUN npm run build && npm prune --omit=dev

FROM node:22-bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends perl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=build --chown=node:node /app/package.json ./
COPY --from=build --chown=node:node /app/node_modules ./node_modules
COPY --from=build --chown=node:node /app/src ./src
COPY --from=build --chown=node:node /app/skills ./skills
COPY --from=build --chown=node:node /app/dist ./dist
USER node
ENV HOST=0.0.0.0 PORT=3000 NODE_ENV=production
EXPOSE 3000
CMD ["node", "src/main.mjs"]
74 changes: 47 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,59 @@
# <img src="./images/icon.png" alt="Meta-Scraper Logo" width="30" /> Meta-Scraper: Image Metadata Cleaner
# MetaScraper

[![Version](https://img.shields.io/visual-studio-marketplace/v/NatalieHill.metascraper)](https://marketplace.visualstudio.com/items?itemName=NatalieHill.metascraper)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
Inspect and remove photo metadata in **ChatGPT, Claude, or another MCP client**. Upload an original photo, see its embedded fields, then remove selected fields or all removable metadata and download a separate copy.

Meta-Scraper is a Visual Studio Code extension for inspecting and removing image metadata, helping you avoid leaking sensitive information when sharing images. It currently has **250+ installs** on the Visual Studio Code Marketplace.
- A compact panel with searchable metadata, field selection, and a download button.
- JPEG, PNG, WebP, GIF, TIFF, and HEIF/HEIC support, including animated GIF/WebP and multipage TIFF.
- Removal verified by rereading the output. Original files and encoded pixels are preserved.
- Streamable HTTP and stdio MCP transports, ChatGPT attachment import, and a portable file picker with a standalone browser fallback.
- Temporary storage with private photo IDs, expiring downloads, and explicit deletion.

## Demo
## Run the MCP app

![Demo](images/output.gif)
Use **Node.js 22.13 or newer**. ExifTool is bundled; no OpenAI or Anthropic API key is needed.

## Features
```sh
cd mcp
npm ci
npm run build
npm start
```

- View metadata embedded in an image
- Remove metadata in a single action
- Supports `.jpg`, `.jpeg`, `.png`, `.gif`, `.tiff`, `.webp`
- Fast processing with real-time feedback
- All processing is done locally; no data leaves your machine
Open **http://127.0.0.1:3000** for the photo panel. MCP runs at **http://127.0.0.1:3000/mcp**.

<div align="center">
<p><b>Right-click any image file in VS Code to access Meta-Scraper actions.</b></p>
</div>
See [connection and deployment instructions](mcp/README.md) for ChatGPT, Claude, stdio, HTTPS hosting, and Docker. Remote chat hosts need a reachable HTTPS endpoint or supported private tunnel; localhost alone does not connect to them.

## Why Cleaning Image Metadata Matters
Try asking:

Modern image files can contain hidden metadata that exposes personal or sensitive details:
> “Open MetaScraper so I can check a photo before sharing it.”
>
> “Show the metadata in this attached photo.”
>
> “Remove just the GPS fields, and keep the camera information.”
>
> “Remove all metadata and give me a cleaned copy.”

| Metadata Type | Privacy Risk |
|--------------------|------------------------------------------------------------|
| GPS location | Reveals the exact place where the photo was taken |
| Device information | Exposes camera/phone model and unique identifiers |
| Creation time | Shows precisely when the photo was taken |
| Software details | Reveals which tools were used to edit the image |
| Author information | Can include your name, email, or other personal details |
## What “remove all” means

Examples of potential issues:
The app removes descriptive metadata recognized by ExifTool, including EXIF, GPS, XMP, IPTC, comments, and color profiles when present and supported. Pixel layout and format properties keep the file readable. Some fields cannot be removed independently; the app reports this and suggests **Remove all metadata** to clear the containing block. Failed verification does not produce a download.

- Revealing your home or workplace location
- Leaking details in screenshots or documents shared publicly
Removing orientation or color profiles can change how a viewer displays the copy; returned warnings make that explicit. Metadata stripping does not redact visible contents. Use original files: chat and social platforms may resize uploads or remove metadata before the app receives them.

## Privacy and checks

The MCP app processes uploads **on the machine running its server**. A remotely hosted server receives the original file. Photos and download links expire after 30 minutes; see [retention notes](mcp/README.md#privacy-and-retention). Metadata returned in chat is also subject to the host’s retention settings.

```sh
cd mcp
npm run check
```

Checks cover real image processing, pixel/frame preservation, MCP transports, UI bridge calls, downloads, expiry, deletion, and input/HTTP boundaries. [Validation and host acceptance checks](mcp/VALIDATION.md).

The original VS Code extension remains available with its own dependencies and commands: [usage](docs/VS_CODE.md), [testing](TESTING.md), [publishing](PUBLISHING.md).

[MIT license](LICENSE).

### Lucci Labs MCP hosting

MetaScraper uses `https://mcp.luccilabs.xyz/metascraper` in production and `https://sandbox.mcp.luccilabs.xyz/metascraper` for isolated testing. See [shared conventions](infra/mcp-gateway/CONVENTIONS.md), [deployment instructions](mcp/deploy/README.md), and the [agent migration prompt](infra/mcp-gateway/AGENT-PROMPT.md).
41 changes: 41 additions & 0 deletions docs/VS_CODE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
> This page describes the original VS Code extension. For the MCP app, see [Meta-Scraper](../README.md).

# <img src="../images/icon.png" alt="Meta-Scraper Logo" width="30" /> Meta-Scraper: Image Metadata Cleaner

[![Version](https://img.shields.io/visual-studio-marketplace/v/NatalieHill.metascraper)](https://marketplace.visualstudio.com/items?itemName=NatalieHill.metascraper)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](../LICENSE)

Meta-Scraper is a Visual Studio Code extension for inspecting and removing image metadata, helping you avoid leaking sensitive information when sharing images. It currently has **250+ installs** on the Visual Studio Code Marketplace.

## Demo

![Demo](../images/output.gif)

## Features

- View metadata embedded in an image
- Remove metadata in a single action
- Supports `.jpg`, `.jpeg`, `.png`, `.gif`, `.tiff`, `.webp`
- Fast processing with real-time feedback
- All processing is done locally; no data leaves your machine

<div align="center">
<p><b>Right-click any image file in VS Code to access Meta-Scraper actions.</b></p>
</div>

## Why Cleaning Image Metadata Matters

Modern image files can contain hidden metadata that exposes personal or sensitive details:

| Metadata Type | Privacy Risk |
|--------------------|------------------------------------------------------------|
| GPS location | Reveals the exact place where the photo was taken |
| Device information | Exposes camera/phone model and unique identifiers |
| Creation time | Shows precisely when the photo was taken |
| Software details | Reveals which tools were used to edit the image |
| Author information | Can include your name, email, or other personal details |

Examples of potential issues:

- Revealing your home or workplace location
- Leaking details in screenshots or documents shared publicly
13 changes: 13 additions & 0 deletions infra/mcp-gateway/AGENT-PROMPT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Update this product to follow the Lucci Labs MCP conventions.

Use the existing product name and a stable lowercase kebab-case slug:
- Production: `https://mcp.luccilabs.xyz/<slug>`
- Sandbox: `https://sandbox.mcp.luccilabs.xyz/<slug>`
- These are complete MCP endpoints; do not append `/mcp`.
- Name the client connections `<Product Name>` and `<Product Name> Sandbox`.

Read the shared conventions and route registry in the Meta-Scraper repository under `infra/mcp-gateway/`. Keep each product independently deployed on Vercel. Register its routes in the existing production and sandbox gateways; preserve other products' routes and never reassign the shared domains to a product project. Use separate projects, databases, storage, credentials and auth configuration for production and sandbox. Never give preview deployments production credentials.

Update this repository's AGENTS.md, README, deployment instructions, configuration examples, manifests, callbacks and generated links to use the new conventions. Keep supporting UI, file and API URLs under the product prefix. Preserve authentication, streaming, attachment handling and existing client compatibility. Keep widget upload helpers app-only. Pin the Vercel project and team IDs explicitly for every deployment.

Implement and verify the migration end to end: HTTPS, MCP discovery, tool calls, UI, downloads and environment isolation. Do not fabricate routes for unimplemented products or claim readiness from health checks alone. Report the final URLs, connection names, verification results and any remaining blocker. Never commit credentials or personal test files.
57 changes: 57 additions & 0 deletions infra/mcp-gateway/CONVENTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Lucci Labs MCP conventions

Use `luccilabs.xyz` exactly. Public MCP endpoints have one hostname per environment and one lowercase kebab-case product slug:

| Purpose | URL |
| --- | --- |
| Production MCP | `https://mcp.luccilabs.xyz/<slug>` |
| Sandbox MCP | `https://sandbox.mcp.luccilabs.xyz/<slug>` |
| Optional browser panel | `<MCP endpoint>/ui` |
| Health | `<MCP endpoint>/health` |
| Product-owned files and APIs | `<MCP endpoint>/files/...`, `<MCP endpoint>/api/...` |

The product URL is the complete MCP endpoint. Do not append `/mcp`, introduce product-specific public MCP subdomains, put environment names in product slugs, or use URL query parameters to select environments. The root hosts are directories, not combined MCP servers. Product website domains are separate and need not change.

MetaScraper is live at `/metascraper`. `launch` and `pilot` are reserved names only; there are no routes for them until their own MCP backends are ready. Unknown products return 404.

## Routing ownership

The shared route registry is `infra/mcp-gateway/registry.json` in the Meta-Scraper repository. Edit it and run `node infra/mcp-gateway/generate.mjs` to regenerate both gateway configurations. Preserve every other product's route. Never assign the shared domains to a product's Vercel project.

Gateways are the Vercel projects `lucci-mcp` and `lucci-mcp-sandbox` in the `lucci` team. Each product and environment runs in its own backend project. Gateways use fixed external rewrites: `/<slug>` to the backend's MCP route, and `/<slug>/:path*` to the backend's supporting routes. They preserve methods and response streams, disable response caching, and never choose an upstream from untrusted input.

Product code must generate absolute links using its environment's public prefix. Do not derive the public hostname from forwarded headers. Validate explicitly permitted Host and Origin values, including the known upstream origin. Keep browser requests under the product prefix. Do not publish internal Vercel URLs as connection URLs.

## Environment isolation

Production and sandbox require separate backend projects, databases, credentials, file storage, auth clients, callbacks and webhooks where applicable. Preview or development deployments must never receive production data credentials. Separate key prefixes alone are not the database isolation standard. Sandbox uses synthetic/test data and test-mode third-party credentials.

MetaScraper production uses `metascraper-photos` (`store_d242zyAA5zHTbBfa`); sandbox uses `metascraper-sandbox-photos` (`store_QresXSVfJ338hE3X`). Each resource is connected only to the primary deployment slot of its respective project. Vercel calls that slot `production` even in the separate sandbox project; the sandbox project's `APP_ENV` is `sandbox`. The product's production preview deployments have no production Redis credentials and fail closed if started without their own storage configuration.

Both databases use pay-as-you-go Upstash with automatic upgrades disabled. Images and metadata expire after 30 minutes; incomplete uploads after five minutes. Gateway projects contain no database credentials.

## ChatGPT and other MCP clients

Use `<Product Name>` for production and `<Product Name> Sandbox` for testing. Register each as a separate connection using its complete public endpoint. Keep distinct permissions and OAuth registrations where applicable. Preserve each product's own tools, skills and UI. Helpers that transport bytes for a widget stay app-only; ChatGPT attachments use the host-provided file descriptor.

For OAuth-enabled products, implement MCP authorization discovery and validate the full public resource URL. Route discovery under `/.well-known` using a mechanism Vercel supports; a generic external rewrite is not sufficient for its reserved `/.well-known` path. Do not weaken authentication or add cross-product token acceptance to make a gateway work. MetaScraper currently uses No Auth with unguessable expiring photo handles; that is not a blanket auth policy for other products.

## Deploy and verify

Always pin both `VERCEL_PROJECT_ID` and `VERCEL_ORG_ID`. A nested `.vercel/project.json` alone did not prevent the CLI from choosing the parent repository's project during this migration.

From this repository's root:

```sh
node infra/mcp-gateway/generate.mjs
npm --prefix mcp run check
node infra/mcp-gateway/deploy.mjs sandbox metascraper
node infra/mcp-gateway/deploy.mjs sandbox gateway
node infra/mcp-gateway/deploy.mjs production metascraper
node infra/mcp-gateway/deploy.mjs production gateway
node mcp/scripts/verify-environments.mjs
```

The deployment helper uploads an explicit allowlist of files from a fresh temporary directory; it does not upload secrets, user photos, dependencies or unrelated repository contents. The gateway and backend deploy independently; deploy only the component that changed. Confirm the printed project ID before interpreting a deployment as successful. CLI deployments use the working tree and do not commit or push it.

Acceptance must include TLS, initialization, tool and resource discovery, native widget behavior, prefixed supporting routes, a real download, and cross-environment denial. For MetaScraper, also test a file larger than 4.5 MB through multipart upload and streaming download. Keep existing endpoints functioning while clients migrate; use request forwarding rather than HTTP redirects for MCP POSTs. Legacy MetaScraper remains available at `https://mcp.metascraper.luccilabs.xyz/mcp`.
Loading
Loading