Your agent asks a question about a real place. A nearby person takes a live photo and answers it.
Site · Worker app · Connect · Deploy · Status
The kind of photo Eyes returns: is the cafe open right now? The sign says yes.
Agents can read the internet but cannot see the street. Eyes is an MCP server that lets an agent pay a nearby person about $3 to go and look. Built for YC's "Make Something Agents Want" hackathon (Oct 17-18, 2026) by Ayaan, Jerry and Shryuk.
- MCP tools: 13 model-visible tools to quote, ask, wait, accept answers, review classifier refusals and manage tasks and money; plus app-only
confirm_ask. REST and non-UI automation retain direct dispatch. - ChatGPT and Claude apps: configured at the dedicated MCP host; OAuth Continue hands a new key to the client automatically. Saving it is optional for direct API use.
- iMessage dispatch: the 3 nearest workers get a text; first YES wins.
- Email channel: reply EMAIL in your existing Eyes iMessage conversation for a link to send your first email; offers, links and sign-in by email, with mobile still required.
- Photo checks: GPS within 150 m, taken live and narrowly moderated; no answer-correctness judgment.
- Escrow: bounty plus $0.50 fee held on ask; refunded if no answer.
- Prepaid balance:
get_balance.top_up_urlopens Stripe Checkout to add funds; payments are in test mode. - Safety: rules and an AI check refuse private-person, home, vehicle or face-targeted requests; this is separate from photo moderation and is not a blanket ban on faces in photos.
- Classifier review:
report_classifierandget_classifier_report; internal Approve/Reject controls, with approval allowing only an explicit retry of the same agent's exact request for 24 hours—not automatic dispatch or charging. - Owner controls: spend caps, allowed task types, key rotation.
- Worker location: HERE links, a Shortcut or the iPhone app.
- Ops dashboard: internal.eyes.pictures: tasks, money, reviews, tickets, live map.
-
Get a key: no dashboard needed; the test balance is $20.
curl -X POST https://api.eyes.pictures/v1/signup \ -H "content-type: application/json" -d '{"name":"my-agent"}'
-
Connect: from Claude Code (ChatGPT, claude.ai, Claude Desktop and Cursor are in docs/CONNECT.md):
claude mcp add --transport http eyes https://mcp.eyes.pictures/mcp \ --header "Authorization: Bearer $EYES_API_KEY" -
Ask:
claude -p 'Use quote_task to check the line at the Ferry Building, San Francisco'
Prefer the Manufact CLI? npx -y [email protected] client connect eyes https://mcp.eyes.pictures/mcp --protocol legacy -H "Authorization: Bearer $EYES_API_KEY". To be a worker, open https://app.eyes.pictures on an iPhone. Until workers have opted in near a place, ask_eyes there returns no_workers_nearby and charges nothing. https://api.eyes.pictures/status says which parts are live.
MCP is live at https://mcp.eyes.pictures/mcp, with ChatGPT and Claude configured there. OAuth issuer and card origins match each host. REST signup, balance, uploads and webhooks stay at https://api.eyes.pictures; legacy api.eyes.pictures/mcp connectors remain supported. This does not mean every third-party connector has migrated. See OAuth setup.
Stripe's provider integration is live, but payments and the $20 signup credit are test-only: no real money moves. Marketplace version 1.0.3 is a configured draft with preflight complete, not submitted or published; legal owner attestations and test-mode limits remain. The new ChatGPT card protocol and live API are verified. Follow-up in-chat visual checks on the operator's test accounts were quota-blocked on 2026-10-18, not unavailable for all users; see STATUS.
In ChatGPT and Claude connectors, the model prepares a read-only draft and the user confirms in the card before Eyes sends or escrows anything. REST, CLI and non-UI MCP can dispatch directly. All dispatches call one EyesService, and each money move is one plpgsql function, so it runs in one transaction (ADR-003). The full flow is in docs/SPEC.md.
Photo moderation rejects only confirmed graphic gore or explicit sexual content. Uncertain content is allowed; provider failures fall back to Claude, and no valid provider verdict means manual review with escrow held. The agent decides whether the answer is correct.
sequenceDiagram
participant A as Agent (MCP / REST)
participant E as Eyes API (Manufact Cloud)
participant W as Worker iPhone (iMessage)
participant C as Capture page (app.eyes.pictures)
A->>E: quote_task, then ask_eyes
E->>E: safety check, escrow $3.50 (InsForge Postgres)
E->>W: offer text (Inkbox)
W->>E: YES (first YES wins)
E->>W: capture link
W->>C: live photo + GPS
C->>E: upload (InsForge storage)
E->>E: GPS, freshness, moderation (clef, Claude on provider failure)
A->>E: await_answer (long poll, 55 s per call)
E-->>A: answer, photo, checks
E->>W: $3 Stripe Connect transfer + paid text
apps/mcp/: API server: MCP tools, REST, webhooks, OAuth,/status.apps/edge/: Cloudflare Workers: thin API forwarder, cron, uptime monitor—not the MCP/API server host.apps/web/: landing site, worker app and the ops dashboard (internal.eyes.pictures).apps/mobile/: Expo iPhone app that sends background location.packages/: core, engine, db, comms, payments, safety, verify, ui.cli/: theeyesCLI and generated agent docs (llms-full.txt, OpenAPI).scripts/: live smoke, owner link, task resolve, migrations, intent check.evals/,e2e/: safety evals and the Playwright golden path.intent/: why each file exists (INDEX), ADRs, verdicts.
- Connect from ChatGPT, Claude, Claude Code and Cursor
- Deploy, live smoke, roll back, alerts
- Spec
- Status and live verification
- Plan
- Demo runbook
- Decisions (ADRs)
- Agent docs: llms.txt
- Report a bug
To develop locally you need Node 26, pnpm and Docker: pnpm install && pnpm verify:fast runs lint, typecheck, unit, contract, safety fixtures and the intent check without a database. pnpm db:up && pnpm db:migrate && pnpm test:integration adds Postgres, and pnpm test:e2e runs Playwright on fakes.
Every outside provider and service Eyes uses, and how. "Live" means the deployed server at api.eyes.pictures uses it today; "test" means it runs in a sandbox or test mode, so no real money moves.
| Provider | What Eyes uses it for | Where in the repo | Status |
|---|---|---|---|
| Manufact Cloud | Hosts the actual MCP/API server (eyes-prod), including REST, webhooks, OAuth and the ops dashboard; origin https://warm-spark-aocdd.run.mcp-use.com, public MCP https://mcp.eyes.pictures/mcp |
apps/mcp, Dockerfile, docs/DEPLOY.md §2 | live |
| Cloudflare Workers | eyes-api is a thin forwarder for mcp.eyes.pictures, api.eyes.pictures and internal.eyes.pictures to Manufact; static Workers serve eyes.pictures and app.eyes.pictures |
apps/edge/api, apps/web/landing, apps/web/worker | live |
| Manufact gateway (optional) | https://dark-steel-ofaoa.run.mcp-use.com/mcp proxies the public MCP endpoint for gateway analytics; this is separate from the Manufact server host |
docs/CONNECT.md | optional |
| Cloudflare Containers + Durable Objects | Previous API hosting path; not the current production origin | apps/edge/api/worker.js | legacy |
| Cloudflare KV + Cron | eyes-monitor checks /healthz every minute, keeps state in KV, texts an alert after two failures |
apps/edge/monitor | live |
| Cloudflare DNS | eyes.pictures zone and custom domains, including the dedicated MCP host |
wrangler configs above | live |
Cloudflare Workers AI (clef-omni) |
First-pass task safety classifier; photo moderation rejects only confirmed graphic gore or explicit sexual content | packages/safety, packages/verify | live |
| Cloudflare Browser Integrity rule | Zone security setting on the domains | Cloudflare dashboard only, not in the repo | live (not verifiable from the repo) |
| InsForge | Postgres with RPC functions and RLS for the ledger, claims and expiry; migrations through the migrations endpoint | packages/db, scripts/insforge-migrate.ts | live |
| InsForge Storage | Public photos bucket for captures; faces alone do not reject or delete photos (photo policy) |
packages/db | live |
| InsForge Sites | Earlier host of the worker web (*.insforge.site); replaced by app.eyes.pictures |
docs/DEPLOY.md §3 | earlier |
| Stripe | Checkout top-ups, Connect Express onboarding, $3 transfers on verified answers, transfer reversals on confirmed fraud, signed webhooks | packages/payments | test mode |
| Inkbox | Shared iMessage line: inbound webhook, offer and paid texts, START/STOP/HELP/HERE replies, texts with location codes and the app link; uptime alerts; first-contact owner/ops email fallback when Primitive refuses delivery | packages/comms/src/inkbox.ts | live |
| Inkbox (limitation) | An iMessage "Share My Location" arrives as a lone U+FFFD, so Eyes replies with a HERE link instead (ADR-037) | packages/comms | live |
| Primitive | Outbound DNS verified and one owner Gmail delivery confirmed; unrestricted sending is still off, first-contact fallback uses Inkbox, and external custom-alias SPF remains unresolved | packages/comms/src/primitive.ts | limited live verification |
| Anthropic Claude | Fallback for task-safety or photo provider failures only; used alone without Cloudflare | packages/safety, packages/verify | live |
| OpenStreetMap Nominatim | Geocodes place names in quote_task and ask_eyes |
apps/mcp/src/geo.ts | live |
| OpenStreetMap tiles + Leaflet (unpkg) | Maps on the ops dashboard and live map | apps/web/dashboard | live |
| Apple Maps / Google Maps links | Reads coordinates from map links a worker texts in; the capture page links to Google Maps directions | packages/core/src/util.ts, apps/web/worker/src/capture.ts | live |
| mcp-use | MCP server framework for tools and widgets; Manufact CLI connect path on the landing page | apps/mcp, apps/web/landing | live |
| OpenAI Apps SDK / MCP Apps | ChatGPT app and Claude connector cards (quote, task, balance) with host-matched OAuth and card origins | apps/mcp, docs/CONNECT.md | supported |
| Expo / React Native + Apple | iPhone app with persistent connection across task photos; current distribution is ad hoc, not TestFlight | apps/mobile | ad hoc |
| ClickHouse | Optional copy of the ops event log and Trends charts; Postgres stays the source of truth | apps/web/dashboard/src/clickhouse.ts | optional, off on the host |
| Astro, Tailwind, GSAP | Landing and worker web; Better-T-Stack conventions | apps/web | live |
| Playwright, Vitest, Biome | E2E, unit/contract/safety/integration tests, lint and format | e2e, vitest.config.ts, biome.json | test |
| GitHub | Code, PRs, issues; Actions run CI, live smoke and the safety LLM eval | .github/workflows | live |
.env.example also lists keys for Armature analytics and Blaxel sandboxes; no code uses them yet.