Skip to main content
Glama
Foto MCP Server
by thesven

Foto MCP Server (foto_mcp)

Version License: MIT Coverage Node Version MCP Compatible

An official Model Context Protocol (MCP) server for Foto (fotoapp.co / the.fotoapp.co), the photography-first social platform.

This MCP server equips AI assistants (Claude Desktop, Cursor, Zed, Windsurf, and custom agent runtimes) to seamlessly inspect local photos, extract camera EXIF metadata, select from Foto's official curated taxonomy, and publish single frames, multi-image series, or Instagram migrations directly to your chronological Foto feed.


Architecture Overview

flowchart TD
    Client["AI Assistant / MCP Client<br>(Claude Desktop, Cursor, Zed, Windsurf)"]
    
    subgraph Foto_MCP ["Foto MCP Server (foto_mcp)"]
        Tools["11 Native MCP Tools<br>& Resources / Prompts"]
        Proc["Sharp & Exifr Media Pipeline<br>• Max 1820px Zero-Crop<br>• Strips GPS & Thumbnails<br>• Extracts Camera & Lens EXIF"]
        Auth["Firebase REST Auth<br>• Local Token Cache (chmod 0600)<br>• Auto Token Refresh (60s buffer)"]
        Sync["Instagram Migration Engine<br>• Cleans Hashtags<br>• Maps Curated Tags<br>• Deduplicates Posts"]
    end
    
    subgraph Foto_Cloud ["Foto Cloud Infrastructure (foto-d963e)"]
        Storage["Firebase Storage<br>posts/{userId}/{imageId}_{ts}"]
        Sightengine["Sightengine Moderation<br>nudity-2.1 Check"]
        Firestore["Cloud Firestore<br>• posts/{postId}<br>• image-private-metadata/{id}<br>• user-profiles/{userId}<br>• tag_index/{tag}_{shard}"]
    end

    Client <-->|"Stdio Transport (JSON-RPC)"| Tools
    Tools --> Proc
    Tools --> Auth
    Tools --> Sync
    Proc --> Storage
    Storage --> Sightengine
    Sightengine --> Firestore

Related MCP server: woof

Core Features & Platform Philosophy

  • Zero-Crop Resolution Normalization: Automatically scales high-resolution camera exports to Foto's 1820px longest-edge standard (JPEG quality 80) while preserving the creator's exact original aspect ratio (3:2, 4:3, 1:1, 6:7, 65:24 panoramic, or 16:9). No forced cropping.

  • Privacy by Design (GPS Scrubbing): Automatically strips embedded GPS coordinates and thumbnail IFDs from binary files before upload to prevent wildlife and location tracking.

  • Technical EXIF Preservation: Parses camera body, lens model, ISO, aperture ($f$-number), shutter speed, and focal length into Foto's image-private-metadata system so technical details are preserved for photography enthusiasts.

  • Curated 303 Tag Taxonomy: Strictly enforces Foto's platform rule of 1 to 3 tags selected exclusively from the official whitelist of 303 curated photographic tags. Completely eliminates hashtag spam.

  • Smart AI Tag Suggester: Analyzes narrative descriptions, gear notes, or film stocks (e.g. Hasselblad 500C/M, Kodak Portra 400, Street) and recommends the best matching tags.

  • Multi-Image Series (Stories): Authors and publishes sequential multi-photo stories (2 to 5 images per post).

  • Automated Content Moderation: Automatically validates uploads against platform community safety rules and Sightengine moderation (sexual_activity <= 0.7, sexual_display <= 0.9).

  • Dry-Run Validation: Test image geometry, EXIF extraction, and tag formatting locally without modifying your account or making live network calls (dryRun: true).

  • Instagram Migration Engine (foto_sync_instagram): Seamlessly extracts posts from Instagram data exports, cleans out hashtag noise, strips GPS, suggests curated tags, and checks against already-published Foto posts.


Quick Start via npx (No Installation Required)

You can run foto_mcp directly without cloning or building locally:

npx -y github:thesven/foto_mcp#v1.0.0

Client Configuration

Claude Desktop

Add this to your Claude Desktop configuration file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

  • Linux: ~/.config/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "foto": {
      "command": "npx",
      "args": ["-y", "github:thesven/foto_mcp#v1.0.0"],
      "env": {
        "FOTO_EMAIL": "your_email@example.com",
        "FOTO_PASSWORD": "your_password"
      }
    }
  }
}

Cursor

Add to .cursor/mcp.json or your Global Cursor MCP settings:

{
  "mcpServers": {
    "foto": {
      "command": "npx",
      "args": ["-y", "github:thesven/foto_mcp#v1.0.0"]
    }
  }
}

Zed Editor

Add to ~/.config/zed/settings.json:

{
  "experimental.model_context_protocol": {
    "servers": {
      "foto": {
        "command": "npx",
        "args": ["-y", "github:thesven/foto_mcp#v1.0.0"]
      }
    }
  }
}

Windsurf / Codeium

Add to ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "foto": {
      "command": "npx",
      "args": ["-y", "github:thesven/foto_mcp#v1.0.0"]
    }
  }
}

Authentication & Credentials Security

Foto MCP supports two flexible authentication mechanisms:

Option 1: In-Chat via foto_login (Recommended)

Prompt your AI assistant in chat:

"Log in to my Foto account with email you@example.com and password your_password."

The foto_login tool:

  1. Connects directly to Google Identity Toolkit REST endpoints.

  2. Securely caches session tokens locally in ~/.config/foto-mcp/credentials.json with user-only permissions (0600).

  3. Automatically refreshes expired tokens in the background before subsequent tool calls.

Option 2: Environment Variables

For automated runtimes, provide credentials in your environment:

export FOTO_EMAIL="you@example.com"
export FOTO_PASSWORD="your_password"

Complete MCP Tool Suite (11 Tools)

Tool

Purpose

Key Arguments

foto_auth_status

Inspects active session, local token validity, and remaining minutes.

None

foto_login

In-chat authentication via email and password; caches tokens locally.

email (string), password (string)

foto_logout

Clears local cached session tokens and in-memory credentials.

None

foto_inspect_image

Inspects local image: returns dimensions, photographic aspect ratio (e.g. 3:2, 6:7), GPS status, and camera/lens EXIF.

filePath (string)

foto_suggest_tags

Suggests 1 to 3 valid tags from the 303 curated tags list based on caption, gear notes, or keywords.

caption? (string), cameraInfo? (string), keywords? (string[])

foto_search_tags

Searches the 303 curated tags whitelist by prefix or keyword.

query (string), limit? (number, default: 20)

foto_draft_post

Validates a post draft before publishing: checks tag whitelist, calculates downsampled dimensions, and reviews EXIF summary.

filePath (string), caption (string), tags (string[])

foto_publish_post

Normalizes image (1820px), scrubs GPS, uploads to Storage, passes Sightengine moderation, and creates public Firestore post.

filePath (string), caption (string), tags (string[]), dryRun? (boolean)

foto_publish_series

Publishes a multi-image sequential Series (2 to 5 photos) under one post.

filePaths (string[], min: 2, max: 5), caption (string), tags (string[]), dryRun? (boolean)

foto_get_user_profile

Retrieves current user's profile info, bio, and published post stats.

None

foto_sync_instagram

Migrates posts from an Instagram HTML export: strips hashtags, maps curated tags, scrubs GPS, and checks against published posts.

htmlFilePath (string), mediaDir? (string), maxPosts? (number), dryRun? (boolean)


MCP Resources & Prompts

Resources

  • foto://curated-tags: Exposes the complete JSON array of all 303 curated tags permitted on Foto. AI clients can read and cache this resource to ensure autocomplete and zero invalid tag errors.

Prompts

  • curate_foto_post: A structured prompt template guiding the model to inspect EXIF metadata, select canonical tags, formulate a caption focusing on narrative rather than hashtag spam, and preview drafts with foto_draft_post.


Example Usage with an AI Agent

1. Inspecting a Photo

"Can you inspect the photo at ~/Photos/Redwoods.jpg and check its camera settings?"

{
  "originalWidth": 4000,
  "originalHeight": 2667,
  "aspectRatioFormatted": "3:2 (Landscape)",
  "hasGps": true,
  "exif": {
    "Make": "Hasselblad",
    "Model": "500 C/M",
    "LensModel": "Zeiss Planar 80mm f/2.8",
    "ISO": 400,
    "FNumber": 4,
    "ExposureTime": "1/125s",
    "FocalLength": 80
  }
}

2. Auto-Suggesting Foto Curated Tags

"What Foto tags would fit this redwood shot?"

{
  "suggestedTags": ["Medium Format", "Analog", "Forest"],
  "explanation": "Detected Medium Format context; Detected analog film gear; Detected forest environment"
}

3. Publishing with Local Dry-Run Test

"Let's test publishing it with a dry run first."

Claude runs foto_publish_post with dryRun: true:

{
  "success": true,
  "dryRun": true,
  "postId": "20261109161630-SIMULATED_USER-abc12345",
  "dimensions": { "width": 1820, "height": 1213, "aspectRatio": 1.5 },
  "hasStrippedGps": true,
  "tags": ["Medium Format", "Analog", "Forest"],
  "message": "[DRY RUN] Post successfully validated! Image prepared to 1820x1213px. GPS scrubbed: true. 0 API calls made."
}

4. Publishing a Multi-Image Series

"Publish these 3 shots as a sequential series: frame1.jpg, frame2.jpg, frame3.jpg"

Claude validates boundaries (2 to 5 images), extracts metadata for each, and publishes the multi-image post.

5. Instagram Data Export Migration

"Analyze my Instagram export at ~/Downloads/instagram_export/posts_1.html and preview what can be migrated."

Claude runs foto_sync_instagram:

{
  "dryRun": true,
  "totalParsed": 15,
  "readyToPublish": 12,
  "alreadyPublishedCount": 3,
  "candidates": [
    {
      "post": { "title": "Red Fox in Winter Snow", "hashtags": ["fox", "wildlife", "winter"] },
      "suggestedTags": ["Wildlife", "Animals", "Winter"],
      "gpsScrubbed": true,
      "status": "READY"
    }
  ]
}

Build from Source & Developer Guide

Prerequisites

  • Node.js v18.0.0 or later

  • npm v9 or later

git clone https://github.com/thesven/foto_mcp.git
cd foto_mcp
npm install
npm run build

Running Test Suite with >90% Coverage

The test suite includes end-to-end integration flows and modular unit test suites across all components.

# Run tests and verify >90% coverage
npm test

# Generate HTML coverage report
npm run test:coverage

Current test coverage breakdown:

-----------------|---------|----------|---------|---------|
File             | % Stmts | % Branch | % Funcs | % Lines |
-----------------|---------|----------|---------|---------|
All files        |   97.71 |    84.55 |   96.07 |   97.71 |
 src/config.ts   |     100 |      100 |     100 |     100 |
 src/foto-client |     100 |     92.3 |     100 |     100 |
 src/tags.ts     |     100 |      100 |     100 |     100 |
 src/api/*       |   98.24 |    86.62 |     100 |   98.24 |
 src/media/*     |   94.88 |    63.93 |      80 |   94.88 |
 src/sync/*      |   98.71 |    86.56 |     100 |   98.71 |
 src/index.ts    |   92.94 |    80.64 |      50 |   92.94 |
-----------------|---------|----------|---------|---------|

Troubleshooting & FAQ

Session Expiration

  • If you see "Stored credentials invalid" or "Authentication token expired", simply prompt the assistant: "Log in to my Foto account with email ... and password ..." or call foto_login.

  • To completely reset all local stored sessions, call foto_logout or delete ~/.config/foto-mcp/credentials.json.

Content Moderation Rejection

  • Foto automatically evaluates photos using Sightengine (nudity-2.1). If an image exceeds platform safety limits (sexual activity > 0.7 or sexual display > 0.9), it will be blocked to protect your account standing.

Image Format Support

  • Foto exclusively allows still photography (JPEG, PNG, WebP, TIFF, HEIC). Animated GIFs and video files are rejected by the media pipeline.


License

This project is licensed under the MIT License.

Available Tools

11 tools
foto_auth_statusA

Check the current authentication status with Foto app (logged-in user, token validity).

Inputs: no named parameters.

foto_draft_postA

Preview and validate a post draft without publishing. Validates tag constraints (1-3 from whitelist), formats EXIF summary, and checks image parameters.

Inputs: tags (required), caption (required), filePath (required).

foto_get_user_profileA

Retrieve profile details and stats for the authenticated Foto user (username, bio, post count, following/followers).

Inputs: no named parameters.

foto_inspect_imageA

Inspect a local image file without uploading: extracts technical EXIF (camera, lens, ISO, shutter, aperture), dimensions, aspect ratio, and checks for GPS presence.

Inputs: filePath (required).

foto_loginA

Authenticate with Foto app using your account email and password. Securely stores credentials locally.

Inputs: email (required), password (required).

foto_logoutA

Log out of Foto app and delete stored credentials from local disk.

Inputs: no named parameters.

foto_publish_postA

Publish a single photo post to Foto. Automatically normalizes resolution (max 1820px, JPEG 80%), scrubs GPS for privacy, uploads to Storage, passes moderation, and creates the post. Use dryRun=true to test locally without publishing.

Inputs: tags (required), caption (required), filePath (required), dryRun.

foto_publish_seriesB

Publish a multi-image Series (2 to 5 photos) under a single post to Foto. Use dryRun=true to validate locally without uploading.

Inputs: tags (required), caption (required), filePaths (required), dryRun.

foto_search_tagsA

Search Foto's official 303 curated allowed tags whitelist by keyword or prefix.

Inputs: query (required), limit.

foto_suggest_tagsA

Suggest 1 to 3 valid tags from Foto's official 303 curated tags list based on caption, camera/film notes, or subject keywords.

Inputs: caption, keywords, cameraInfo.

foto_sync_instagramA

Synchronize posts from an Instagram data export (HTML/JSON) to Foto. Cleans hashtags, suggests 1-3 curated tags, normalizes dimensions, scrubs GPS, and checks against already-published Foto posts. Default dryRun=true validates everything without publishing.

Inputs: htmlFilePath (required), dryRun, maxPosts, mediaDir.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 11 tools

    Disambiguation4/5

    Each tool targets a fairly clear stage of the workflow (auth, inspect, tag, draft, publish, sync). Minor overlap exists between foto_draft_post and foto_publish_post with dryRun=true, and between foto_suggest_tags and foto_search_tags, but descriptions do enough to separate them.

    Naming Consistency4/5

    All tools share a consistent foto_ prefix and mostly follow a verb_noun pattern (foto_inspect_image, foto_publish_post, foto_sync_instagram). Minor deviations: foto_auth_status is noun-phrase and foto_get_user_profile uniquely uses a get_ verb.

    Tool Count5/5

    Eleven tools is well within a healthy scope and each maps to a distinct step: auth lifecycle (3), local inspection, tag discovery (2), draft, publishing (2), profile, and import sync.

    Completeness5/5

    Covers the full publish lifecycle from login and local validation through single/series publishing and Instagram import, plus read-only profile. The only notable gap is post management after publishing (edit/delete/list own posts), which agents can work around for now.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to browse and search local photo galleries without uploading photos, using metadata-only indexing and local thumbnail serving.
      8
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to access, tag, describe, and manage a Flickr photo library, including automated batch processing.
      61 npm
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI agents to search, list, preview, and export photos from Apple Photos, Google Photos Picker sessions, and local Google Takeout archives using native search and metadata inspection.
      10 npm
      MIT