Inspiration

People delegate the boring parts of shopping, but the boring part that a plain API integration can never capture is the haggle. A price is one number; a real deal is price times quantity, which freebie the seller throws in, whether shipping is waived, and which coupon stacks on top. We wanted the shopper's browser agent to work that whole space on the site the user is already looking at, and still leave the final yes/no to a human.

What it does

Agent Market is a multi-vendor storefront with 95k+ real products. Every page registers storefront tools on document.modelContext (WebMCP): search, product details, cart, add-ons, coupons, and a full negotiation set. A buyer's agent opens a negotiation and haggles a multi-dimensional deal against the store's agent over several rounds. When they settle, the page shows a Confirm dialog and nothing is bought until the human confirms the price. The same tools work in a WebMCP browser (ChatGPT's in-app browser, Chrome with the flag) or drive an in-page agent loop that needs no API key from the user.

How we built it

  • Frontend: Next.js 15 / React 19 / TypeScript on Vercel. StorefrontTools registers every tool on document.modelContext; lib/agent-loop.ts is a tool-agnostic agent harness that discovers tools from the registry and runs a hand-rolled chat-completions loop.
  • Backend: Spring Boot (Java) on Render, Azure SQL, Flyway, Hibernate.
  • One mutation path: every state change (buyer, seller, or the server-side auto-negotiator) goes through OffersService.applyTurn, a pure function that is the only code allowed to decide a negotiation transition. It enforces the two hard economic limits (buyer total <= budget, seller net >= floor); which freebie and how much to concede is agent judgment.
  • Human gate: accepting mints an HMAC confirm token; the order is placed only when the human clicks Confirm.
  • Model: Google Gemini through the OpenAI-compatible endpoint, key server-side only.

Challenges we ran into

  • Gemini "thinking" models and tool history. Gemini 3.x rejects a multi-turn tool conversation unless each function call's thought_signature is echoed back. The OpenAI-compat shim dropped it, so turn 2 always 400'd. We now capture tool_calls[].extra_content on the way in and replay it on the way out.
  • Small models stall on long tool loops. Asking a fast model to drive search then offer then counter then counter then accept meant it derailed a turn later every time. We collapsed the buyer's negotiation into a single deterministic negotiate tool: the server plays both sides of the haggle, persists every round so the transcript is real, and returns a confirm token. The agent's job is now two calls, and reaching the confirm step no longer depends on the model finishing a loop.
  • Optimistic concurrency vs agent reliability. A strict round guard made the agent stall forever when its round number drifted. We made the guard opt-in for proposals and mandatory only for the binding accept.

What we learned

WebMCP's value is not "expose an API"; it is that the agent operates the same surface the human sees, so the human can step in on any turn. And the moment a flow depends on an unreliable model completing N steps, move the deterministic part

What we learned

WebMCP's value is not "expose an API"; it is that the agent operates the same surface the human sees, so the human can step in on any turn. And the moment a flow depends on an unreliable model completing N steps, move the deterministic part to the server and leave the model the judgment call.

What's next

Seller-side takeover controls, agent-negotiated bundles across multiple sellers, and a browser-vs-browser two-agent mode for a fully agentic negotiation.

Built With

Share this project:

Updates

Submission history