# Wanted: the full picture, for agents > Wanted is a tour manager for independent musicians, run by an agent called Gus. Gus uses Qloo's taste graph to find the US cities, and the neighborhoods inside them, where an act's sound already has fans. He puts a WANTED poster up in those cities, fans sign it, and once a city proves itself he negotiates a date with a venue that fits, inside limits the artist set. The artist makes one decision: yes or no on an exact offer. Built for the Qloo Agentic Hackathon. Short version: https://trywanted.vercel.app/llms.txt ## Try it - App (works on a phone; on a laptop every tab floats over the live map): https://trywanted.vercel.app - Demo act with a guided tour from Gus: https://trywanted.vercel.app/a/maya-ortiz?tour=1 - A fan's poster page: https://trywanted.vercel.app/w/maya-ortiz/burlington-vt - Gus as an MCP connector: https://trywanted.vercel.app/api/mcp - Source (MIT): https://github.com/wafflebytes/wanted ## Who it's for Independent singer-songwriters and small bands in the US with roughly 5k to 80k monthly listeners, booking themselves into venues that hold 100 to 400 people. They have listeners scattered across the country and no agent. Their questions are: where do people actually want me, which venue in that city fits, and will anyone come. Today they answer them with guesswork, a streaming dashboard and cold emails. ## The loop 1. **Sound.** The artist pastes a Spotify, Apple Music, Bandcamp, SoundCloud or YouTube link, or types a name. Gus resolves the act and a few artists it sounds like into Qloo entities. 2. **Where you're wanted.** Every US metro is scored for that sound with Qloo's heatmap insights, then normalized against a panel of other acts so a city isn't hot just because it's big. A "sleeper" is a city with far more fans than its size predicts. 3. **Poster.** In a chosen city Gus puts up a WANTED poster. Fans sign it with their neighborhood. Signatures are proof of demand, kept per city and per neighborhood. 4. **Ask venues.** Once fifty fans sign in a city, Gus asks the venues that fit the act's taste for a date, skipping any closed that night. He trades letters with each venue's booker and stays inside the artist's limits: the lowest cut of ticket sales, the biggest deposit, the nights to avoid. 5. **One decision.** When a venue agrees, the artist sees the offer next to their own limits and says yes or no. A yes pays a test deposit that locks in that exact offer. 6. **Fill the venue, play, get paid.** Signers hear first and get first dibs on tickets. Booked nights join the tour route. Gus plans the show day, and afterwards checks the venue's ticket count against the scanner so the artist gets their full cut. ## Model, harness, context Gus follows Harrison Chase's split of an agent into a model, a harness and context, with no agent framework: - Context: Qloo's taste graph (cities, neighborhoods, venues, crowd, openers), his notes on the act (`src/server/gus/memory.ts`), and a short brief with the last few turns. - Harness (ours, `src/server/gus/ask.ts`): the tool loop and its budget, the grounding check, actions only a lookup backs, rules before the model, money rules on the server, the privacy gate on every prompt (`src/server/outbound.ts`). - Model: free OpenRouter models tried in order, with a cache and a rules fallback ("Gus lite") that says when it answers. The easiest part to swap. ## Gus works when no screen is open Gus doesn't wait for a tab. Any read of an act's deals advances his calls by the time that passed, and a cron sweep does the same for every act with a call in progress (`src/server/ambient.ts`, `src/app/api/cron/gus+api.ts`). When you come back, the app's Dynamic Island shows what he's doing right now: a call with its progress, an offer waiting on you, a show just booked. The demo act always has one of each, so the first visit lands mid-work. ## Asking Gus Tap Gus to see his jobs, or hold to talk. His answers stream: each lookup appears as he runs it, then a sentence or two and, when it fits, one thing to do next. He has eleven tools, all backed by Qloo or the act's own data (`src/server/gus/ask.ts`). He runs up to four rounds with up to four lookups at once, inside a 25 second budget: - `top_cities`: where the act ranks highest, or its sleepers. - `city_fit`: how one city scores, and why. - `rooms`: the venues in a city that fit the act's taste, and the nights each is open (Qloo hours). - `openers`: artists who share the crowd, compared with Qloo's analysis endpoint. - `crowd`: who the fans are and what else they love. - `poster_spots`: bars and cafes in the neighborhoods where the crowd is thickest. - `pick_night`: the best night to play a city, skipping nights the artist ruled out. - `fan_proof`: signatures by city and neighborhood (counts only). - `fan_draw`: the draw for one city (a fan who signed meets the artist at soundcheck). - `swap_options`: two cities that could take a tour stop's night instead. - `remember`: writes down something lasting the artist said about how they tour. Every answer is checked before it reaches the artist: any city, venue or number that no lookup returned (or that the artist didn't say) gets the reply thrown out, and a rules planner ("Gus lite") answers from the same lookups and says so (`src/server/gus/grounding.ts`). What Gus suggests next stays in place: opening a city opens its sheet, asking a venue opens the deal room, changing a limit happens right there with Undo. Nothing jumps to another page halfway through. ## Memory Gus remembers lasting things an artist tells him ("Won't play Mondays", "the van does five hours a day", "skip Boston"). The `remember` tool writes them down in the artist's words, twelve at most, with repeats merged (`src/server/gus/memory.ts`). He reads them before every answer, as untrusted text rather than instructions, and they count as grounded facts. The Logbook gets "Noted: ..." once per fact, and Settings lists everything he remembers, each with a strike to forget it. ## Reaching you How loudly Gus reaches an artist is decided in code from what's at stake, how soon it runs out and whether it can be undone, never by the model (`src/server/mandate.ts`): a Logbook line, a note, a question, a card to review, or a phone call in his own voice. Quiet hours run 10 pm to 9 am by default, calls are capped at one a week, and every call ends in the same choice as the card. ## The loops: every show brings fans to the next Story posters are drawn on the server (satori, resvg, sharp) as 1080x1920 JPEGs for Instagram and TikTok (`src/server/og.tsx`, `/img/story/{act}/{city}`): - The tour poster (`/img/story/maya-ortiz/tour`): every night on one story. Booked nights show their venue, the rest show how many signed, ending with "Sign for your city." - Booked (`?kind=booked`): the day a city gets its date. - Thank you (`?kind=thanks&n=184`): the morning after, how many came and how many signed to start it. - First dibs: once a city has a date, people who signed hold up to two tickets (four if they brought two friends) on the poster page, and the ticket link opens for them two days before the public. Nothing is charged until then, and Wanted's $1 a ticket comes from the venue, never the fans (`src/server/dibs.ts`). ## Money and trust - Gus can talk, but he can't pay on his own. Each offer carries a hash of its exact terms. A yes approves that hash only. If the terms change, the yes is void. - A yes mints a one-time pass for that amount, that payee, for 48 hours. The deposit is paid with the pass, idempotently (`src/server/deals.ts`, `src/server/deal-scope.ts`). - Deposits run on Stripe test mode when a test key is set, otherwise on Wanted's sandbox ledger. Never live money. - The venue side is a simulation: booking agents are stand-ins that answer from templates and policy. Labeled as such in the app and the API (`mode: "simulation"`). - Artist limits (the lowest cut of ticket sales, the biggest deposit, quiet hours) live in Gus's mandate and are enforced on the server, not in the prompt. ## Privacy Fan data never goes to Qloo or to a model. Signatures, names, neighborhoods and contacts stay in Wanted's database. Gus and the MCP connector see counts by city and neighborhood, never people. ## How Wanted uses Qloo All calls go through `src/server/qloo/api.ts`, with receipts kept for each answer. | What Wanted needs | Qloo call | Where it decides | |---|---|---| | The act and its sound | `/search`, entities by ID or Spotify ID | onboarding, `src/server/sound.ts` | | Every US city ranked for the crowd | `/v2/insights`, `filter.type=urn:heatmap` | the map, `src/server/score.ts` | | Neighborhoods inside a city | `urn:heatmap` scoped to the city | city sheet, poster spots | | Venues that fit the act's taste, with why and open nights | `urn:entity:place` with explainability and hours | city sheet, deals | | Where to put posters | `urn:entity:place` in top neighborhoods | posters | | Who the fans are | `urn:demographics`, cross-domain `podcast`, `tv_show`, `movie`, `brand`, `urn:tag` | city sheet, Fan Mail | | An opener who shares the crowd | `urn:entity:artist`, then `/v2/analysis/compare` | Gus's `openers` tool | ## Qloo against a plain chatbot Every city sheet has "Ask a chatbot instead?". It puts the same question (where's my crowd in this city, which venues) to a general model with no taste data, three samples with majority vote, then checks each answer: does the venue exist, is it in that city, is it still open, is it open that night, does it put on shows, and does Qloo rank that neighborhood for the sound (`src/server/without-qloo.ts`, `src/server/gus/baseline.ts`). The sheet shows one number, how much of the confident answer holds up, and the reason for each miss. Results are kept for a day per city. ## Gus in Claude or ChatGPT (MCP) `https://trywanted.vercel.app/api/mcp` is a remote MCP server: streamable HTTP, stateless, JSON responses, no sign-in. Add it as a custom connector. Every tool takes an optional `act` slug (default `maya-ortiz`). - `where_am_i_wanted`: ranked cities, with heat percentile, sleepers and signatures. Read-only. - `city_brief`: neighborhoods, fitting venues with likely size and open nights, signatures. Read-only. - `deals`: every date in talks, with terms and whether they sit inside the artist's limits. Read-only. - `ask_gus`: a question for Gus; he runs his own lookups and answers with what he checked. Read-only. - `ask_rooms`: sends Gus to ask a venue in a city. Starts a negotiation, never pays. In Claude, every tool answers as a card in the chat (an MCP App, `ui://wanted/gus-card-v3.html`): the ranked cities, one city with its neighborhoods and the venues' open nights, the deals, or Gus's reply with what he checked. Tapping a city opens it and "Send Gus to ask" calls `ask_rooms`, all without leaving the conversation. Other clients get the same answer as text and structured content. The server also lists starter prompts: `where_am_i_wanted`, `plan_a_night` and `whats_waiting`. There's no tool that approves or pays. That happens in the app, by the artist, against the exact offer. ## Public JSON API (demo act) - `GET /api/artist/maya-ortiz`: the act, its sound, opened cities. - `GET /api/artist/maya-ortiz/map`: city heat, sleepers, signatures by city. - `GET /api/artist/maya-ortiz/city/{metroId}`: neighborhoods, venues, crowd for one city. - `GET /api/artist/maya-ortiz/deals`: dates in talks, with full letter threads. - `GET /api/w/maya-ortiz/{metroId}`: a fan's poster page data, with first dibs once a date is held. - `GET /api/artist/maya-ortiz/plan`: the tour route and Gus's suggested changes. - `GET /api/artist/maya-ortiz/logbook`: everything Gus did, in plain words, and his mandate. - `POST /api/artist/maya-ortiz/without-qloo` with `{"focusCity":"burlington-vt"}`: the chatbot comparison. - `GET /api/health`: which providers are live. ## Built with - Web: Next.js 16 (what judges use). iOS: Expo, from the same codebase and the same server; TestFlight is next. - Server: shared TypeScript in `src/server`, served by the web app unchanged. libSQL (Turso) in production. - Models: OpenRouter for Gus's tool loop, his writing and his voice (a Fish Audio voice picked for him). - Taste: Qloo Insights API. - Payments: Stripe test mode or a sandbox ledger. ## Evals - Asking Gus (`evals/gus.md`, `npm run eval:gus`): 16 questions across everything he's asked to do, live model with no cache and live Qloo, through the same route the app uses. 16 of 16 answers passed. A pass means he ran the right lookup, offered a fitting next step, used plain words and stayed short. Separately, the server throws out any reply naming a place no lookup returned. Writing this eval caught three bugs that are now fixed: "Portland Maine" picked Portland, Oregon; Gus didn't know a poster needs fifty signatures; and he'd ask "want me to ask for a date?" without the button that does it. - Qloo against a plain chatbot (`evals/qloo-vs-chatbot.md`, `npm run eval:chatbot`): the same city question over 8 cities. 46% of its venues hold up, and most of the neighborhoods it names aren't where this sound's fans live by Qloo's ranking. - Agent gate (`evals/report.md`): 12 of 12 scenarios passed with no rejected tool calls. They cover approval not being delivery, changed terms needing review, stopping when nothing qualifies, and text from a venue not being able to grant authority. - Unit and policy tests: `npm test`, 236 passing (deal policy, approvals, the MCP protocol, ranking). - Production smoke: `npm run smoke:prod` checks every page, the JSON the pages need, the share images, the MCP connector and these text files, with time budgets. - Not measured yet: real artist outcomes, ticket sales, real venue replies. ## What's real and what's simulated - Real: Qloo calls, the city scores, neighborhoods, venues and crowds; fan signatures and first dibs holds; Gus's tool loop, memory and voice; the story posters; the MCP server; the approval and payment policy. - Simulated: venue booking agents and their replies, deposits (test money), and the demo act's signatures, which are flagged as demo.