For developers · MCP

Connect your agent to real local inventory and counter pay.

Ask Lucilla is a Model Context Protocol server for any AI agent. Connect Claude, ChatGPT or your own client (Muse coming soon) and it acts for one signed-in person in their own Ask Lucilla account: searches local demand, checks availability, books slots, claims deals for that person and, for an owner, runs the shop. Every permission is a checkbox the person can untick, and anything that moves money or reaches other people describes the consequence first and needs a second explicit call.

Endpoint: https://asklucilla.com/mcp

Connect in 30 seconds

Nothing to install. Paste one URL into the agent you already use.

Claude Code

claude mcp add --transport http ask-lucilla \
  https://asklucilla.com/mcp

Claude.ai and ChatGPT

Settings, then Connectors, then Add custom connector, and paste the URL below. Muse support is coming soon.

https://asklucilla.com/mcp

Cursor, Windsurf, any mcp.json

{
  "mcpServers": {
    "ask-lucilla": { "url": "https://asklucilla.com/mcp" }
  }
}

Clients that only speak stdio

Bridge with the public mcp-remote package. No Lucilla package needed.

npx mcp-remote https://asklucilla.com/mcp

Sign-in is OAuth 2.1 with PKCE and dynamic client registration; no API key to copy. Business owners get the owner tools automatically when the account they sign in with owns a listing.

What is available

33 tools, in two groups.

CUSTOMERS

16 tools

Ask Lucilla in plain language, search listings, check availability, hold codes and bookings, follow shops, cancel your own booking or report a problem, and set spending limits.

BUSINESS OWNERS

17 tools

Read your shop, listings, orders, calendar and leads. Edit listings, post, run keyword promos and follower drops, fulfil or cancel orders.

THE RULE

The person is always in the loop

Anything that moves money or reaches other people describes the consequence first and needs a second explicit call.

Settled on-chain

Pay with Ask Lucilla, on your own site.

USDC transfers and event ticket escrows run through smart contracts on Base. Accept USDC on your own site and the money moves wallet to wallet; Ask Lucilla never holds it.

01 · HOSTED

/pay hosted checkout

Send the customer to a hosted payment page and get the result back to your site.

02 · DROP-IN

A drop-in button

Add a button to your page that opens checkout. No Ask Lucilla fee on a sale.

03 · REST

REST API sessions

Create a checkout session, receive signed webhooks and refund when you need to.

Read the Pay with Ask Lucilla API reference

ENTERPRISE

API-key MCP access

Enterprise accounts can use API-key MCP access to run and manage local reward campaigns from their own agents.

Endpoints

Where it lives.

One MCP endpoint and one OAuth issuer, both on Google Cloud in us-east1. Everything a client needs is discoverable from the first 401.

MCP endpoint
https://asklucilla.com/mcpStreamable HTTP. JSON-RPC 2.0 over POST, one request per POST.
Protected resource metadata
https://asklucilla.com/mcp/.well-known/oauth-protected-resourceRFC 9728. Names the authorization server and the baseline scopes.
OAuth issuer
https://asklucilla.comRFC 8414 / OIDC issuer identifier. Also the audience every token is bound to.
Authorization server metadata
https://asklucilla.com/.well-known/oauth-authorization-serverThe same document is served at /.well-known/openid-configuration under the issuer.
Authorize
https://asklucilla.com/authorizeSign-in and consent screen. Opens in the person's browser.
Token
https://asklucilla.com/tokenauthorization_code and refresh_token grants. Public clients only (no client secret).
Revoke
https://asklucilla.com/revokeRFC 7009.
Register
https://asklucilla.com/registerRFC 7591 dynamic registration. Client ID Metadata Documents are preferred.
Scope catalog
https://asklucilla.com/scopesThe consent wording, machine-readable. Not part of any RFC.

Protocol versions

Newest first. A client that sends none is treated as 2025-03-26. An initialize request selects the legacy handshake era; the MCP-Protocol-Version header (or _meta) selects the stateless 2026-07-28 era.

2026-07-282025-11-252025-06-182025-03-26

Transport rules

  • POST only. GET and DELETE answer 405: there is no standalone SSE stream and no protocol-level session.
  • One JSON-RPC request per POST. Batches are refused with 400.
  • Request bodies over 512 KB are refused with 413.
  • Methods: initialize, server/discover, ping, tools/list, tools/call. Capabilities: tools only.

Authorization

The person is always in the loop.

OAuth 2.1 as the MCP authorization spec (revision 2026-07-28) describes it. No implicit grant, no password grant, no client credentials. Every token is minted for one person, one client, and this one server.

01 · DISCOVER

Start with a 401

Call the endpoint with no token. The WWW-Authenticate challenge carries the protected-resource metadata URL and the baseline scopes (profile.read deals.read). Follow it to the authorization server metadata.

02 · IDENTIFY

Bring a client_id

Preferred: a Client ID Metadata Document, where client_id is an https URL to your own JSON document. RFC 7591 dynamic registration at /register also works. Public clients only: there is no client secret.

03 · AUTHORIZE

Authorization code + PKCE

Redirect the person to /authorize with response_type=code, code_challenge_method=S256 (plain is refused), the scopes you need, and resource set to the MCP endpoint (RFC 8707). Add offline_access if you want a refresh token.

04 · SIGN IN

Password never reaches us

The consent page signs the person in with the Firebase Auth web SDK, directly against Google’s identity service: email and password, Google, or Apple. Our server only receives the resulting ID token, verifies it and discards it.

05 · CONSENT

One checkbox per permission

The screen names your client and the host the code will be sent to, then lists each requested scope in plain words with its consequence, all ticked. The person can untick any of them; the token is genuinely narrowed. They have 10 minutes to decide.

06 · EXCHANGE

Code for tokens

POST the code and code_verifier to /token within 60 seconds. You get a 30-minute access token bound to this server and, with offline_access, a refresh token that lasts 60 days of inactivity and is rotated on every use.

The first call

POST https://asklucilla.com/mcp
Content-Type: application/json

{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://asklucilla.com/mcp/.well-known/oauth-protected-resource",
  scope="profile.read deals.read"

Lifetimes

Access token
30 minutes
Refresh token
60 days of inactivity, rotated on every use
Authorization code
60 seconds, single use
Sign-in and consent window
10 minutes
confirm_token
10 minutes, single use, bound to one tool
availability_token
a few minutes, re-verified when the order is written

Scopes

Eight permissions, in the words the person sees.

profile.read
See who you are
Your @username, display name and city. Not your phone number or email.
deals.read
Look around for you
Search deals, shops and listings near you, and read the codes and bookings you already hold.
deals.claim
Claim deals and reserve things
Take a deal or hold a booking in your name. This creates a real obligation to a shop, and some holds cannot be undone without contacting them. Includes deals.read.
orders.write
Manage your own orders
Cancel one of your orders, or report a problem with it. Only orders you placed yourself. Includes deals.read.
shop.read
Read your business
Your listings, incoming orders, leads and numbers for the business you own. Customer contact details stay hidden.
shop.write
moves money or reaches people
Run your business
Create and edit listings, deals, posts, hours and branding, and message your followers. Posts and follower messages reach real people and cannot be unsent. Includes shop.read.
shop.money
moves money or reaches people
Handle your business money
Fulfil an order, which CHARGES the customer, cancel an order, which REFUNDS them in full, and act on disputes. This moves real money. Includes shop.read.
spend
moves money or reaches people
Spend your money
Buy things for you, up to the limit you set in the app. This spends real money from your wallet.

Tool reference

Every tool, grouped by who it is for.

Every tool below is registered on the live server. Scope, whether it asks first, and its rate limit are shown with each.

Customers

ask_lucilladeals.readwriteasks first: no30/min

Talk to Ask Lucilla in plain language, exactly as a person texting the agent would. Returns the agent's reply as text. The agent asks for a YES before it publishes, books or spends; pass the user's answer back through this same tool. Needs a verified phone on the account (reply carries needs_phone).

{ "text": "bagels near me under $15", "lat": 43.65, "lng": -79.38 }
whoamiprofile.readreadasks first: no100/min

Who this session belongs to: @username, city, whether they run a business, and exactly which permissions they granted. Call it first. Returns no email or phone number.

{}
get_delivery_addressprofile.readreadasks first: no100/min

The delivery address saved for agent purchases, or null.

{}
get_spend_settingsprofile.readreadasks first: no100/min

The spending limits this person set for their agent and how much of the month is left. Amounts in cents. enabled: false means agent purchases are off entirely.

{}
search_listingsdeals.readreadasks first: no100/min

Bookable and buyable listings near a point: tables, appointments, class seats, pickup orders, products. Requires lat and lng; filter by listing_type (service, product, food). Radius 0.5 to 50 km, up to 25 rows.

{ "lat": 43.65, "lng": -79.38, "radius_km": 3, "listing_type": "food" }
get_listing_availabilitydeals.readreadasks first: no100/min

Open times for one listing plus the short-lived availability_token that reserve_listing requires. Window up to 31 days.

{ "item_id": "itm_…", "quantity": 2 }
my_saved_codesdeals.readreadasks first: no100/min

Deal codes this person holds: what for, which shop, active, redeemed or expired. The single-use burn link is never returned.

{ "status": "active" }
my_bookingsdeals.readreadasks first: no100/min

Everything this person has reserved, ordered or booked, with the order_id and code the counter asks for. The redeem token is never returned.

{ "active_only": true }
reserve_listingdeals.claimwriteasks first: yes6/min

Reserve or order a listing in this person's name. Creates a real obligation to a shop. Needs a fresh availability_token. Takes no payment: pay-at-counter listings confirm immediately; card listings come back held and are paid in the app.

{ "item_id": "itm_…", "availability_token": "…", "slot_start_ms": 1790000000000, "quantity": 2 }
follow_shopdeals.claimwriteasks first: no30/min

Follow a business so its posts and follower drops reach this person. Idempotent, reversible.

{ "business_id": "conn_…" }
unfollow_shopdeals.claimwriteasks first: no30/min

Stop following a business. Idempotent.

{ "business_id": "conn_…" }
cancel_my_bookingorders.writewriteasks first: yes30/min

Cancel one of this person's own bookings. The first call says whether the deposit is refunded or forfeited, and for how much.

{ "order_id": "ABC123", "reason": "Can't make it" }
report_order_problemorders.writewriteasks first: yes30/min

Open a dispute on an order this person paid for or reserved (not_served, not_as_described, charged_wrong, business_closed, other). Holds the shop's payout while Lucilla reviews it.

{ "order_id": "ABC123", "reason": "not_served" }
set_delivery_addressspendwriteasks first: no10/min

Set or clear the address anything the agent buys ships to. Canada and the US only.

{ "name": "…", "line1": "12 King St W", "city": "Hamilton", "region": "ON", "postal_code": "L8P 1A1", "country_code": "CA" }
set_spend_settingsspendwriteasks first: when widening10/min

Change the agent's spending limits (cents). Turning purchases on or raising any cap confirms first; turning off or lowering applies immediately.

{ "monthly_cap_cents": 5000 }
approve_agent_purchasespendwriteasks first: when approving10/min

Say yes or no to a purchase parked over the approval threshold. Approving does not charge by itself; caps are re-checked at payment. Declining applies immediately.

{ "purchase_id": "pur_…", "approve": true }

Business owners

my_shopshop.readreadasks first: no100/min

The business this person runs: id, name, handle, category, city, live or paused, whether it can take a card, followers, trust tier. Call before any other shop tool. business_id is optional on every shop tool.

{}
shop_listingsshop.readreadasks first: no100/min

The business's own listings with status, price, pay mode and booked, fulfilled and cancelled counts. Includes drafts and paused.

{ "status": "live" }
shop_ordersshop.readreadasks first: no100/min

Orders and bookings in a time window, with order_id, code, slot, money and whether prepaid. The customer appears as an @username only.

{ "from_ms": 1790000000000, "to_ms": 1790086400000, "status": "confirmed" }
shop_calendarshop.readreadasks first: no100/min

Blocked-off time, including busy time synced from Google Calendar (editable: false).

{}
shop_leadsshop.readreadasks first: no100/min

People who claimed a deal and consented to be contacted, as @usernames with city and what they claimed. Raw email and phone are not available here.

{}
set_listing_statusshop.writewriteasks first: no30/min

Take a listing live, pause it or return it to draft. Pausing stops new bookings and leaves existing ones alone.

{ "item_id": "itm_…", "status": "paused" }
block_shop_timeshop.writewriteasks first: no30/min

Block a span so nothing can be booked in it. Does not cancel bookings already inside it. At most 31 days per block.

{ "start_ms": 1790000000000, "end_ms": 1790086400000, "reason": "Closed Monday" }
unblock_shop_timeshop.writewriteasks first: no30/min

Remove an owner block. Google-synced blocks are refused.

{ "block_id": "blk_…" }
create_keyword_promoshop.writewriteasks first: yes30/min

Set up a text-to-claim offer: customers text a keyword and get a code. Publishes a public offer the business must honour. Audience, frequency, cooldown and supply cap are enforced at claim time.

{ "deal_label": "free coffee with any bagel", "keyword": "BAGEL", "duration_days": 7 }
set_keyword_promo_activeshop.writewriteasks first: no30/min

Pause or resume a keyword promo. Codes already issued stay valid.

{ "promo_id": "prm_…", "active": false }
set_shop_brandingshop.writewriteasks first: no30/min

Set or clear the logo or cover image by public https URL. Uploads nothing.

{ "logo_url": "https://…/logo.png" }
publish_shop_postshop.writewriteasks first: yes3/min

Publish a post to followers' feeds. Public, reaches real people, can be taken down but not un-seen. The first call returns the exact text and the follower count.

{ "title": "New lunch menu", "content": "…", "type": "announcement" }
take_shop_post_downshop.writewriteasks first: no30/min

Take a post down everywhere. Already-down is a success, not an error.

{ "post_id": "pst_…" }
send_follower_dropshop.writewriteasks first: yes3/min

Push a one-line message (1 to 140 characters) to every follower's phone. Cannot be unsent. One drop per shop per day. Consent and quiet hours are re-checked per person at send time.

{ "text": "Fresh bagels until 2pm, 20% off with code BAGEL" }
fulfil_ordershop.moneywriteasks first: yes10/min

Mark an order fulfilled. Captures the prepaid amount and starts the payout clock. The first call states the exact amount. A staff member cannot fulfil their own order.

{ "order_ref": "ABC123" }
cancel_order_as_shopshop.moneywriteasks first: yes10/min

Cancel on the business's behalf. Always a full refund to whoever paid; the customer is told.

{ "order_ref": "ABC123", "reason": "Kitchen closed" }
propose_order_timeshop.moneywriteasks first: yes10/min

Propose a new time for a confirmed booking and tell the customer, who may accept, ask for another time, or cancel for a full refund. Owner or admin only.

{ "order_ref": "ABC123", "new_start_ms": 1790010000000 }

Money and reach need a second call

Tools marked “asks first” first return the consequence in plain words and a confirm_token (10 minutes, single use, bound to one tool). Show it to the person. Only if they say yes, call again with the token.

// 1. Describe. Nothing changes.
tools/call  fulfil_order  { "order_ref": "ABC123" }
// 2. Show the consequence. If yes:
tools/call  fulfil_order  { "order_ref": "ABC123", "confirm_token": "…" }

Refusals are results, not transport errors

A refusal such as a missing scope or a rate limit comes back as a structured result an agent can read and act on. Missing scope answers 403 insufficient_scope; too many calls answer 429 with Retry-After.

Rate limits

Per person, per client, per tool.

100 per minute

Reads

whoami, search, availability, codes, bookings, every shop_* read.

30 per minute

Ordinary writes

ask_lucilla, follow, listing status, blocks, promos, branding, take-down, own-order cancel and problem report.

6 per minute

Reservations

reserve_listing. A hold creates an obligation to a real shop.

10 per minute

Money

fulfil, shop cancel, propose time, spend settings, delivery address, purchase approval.

3 per minute

Broadcast

publish_shop_post and send_follower_drop. A drop is also one per shop per day.

Privacy and money

What never crosses this surface.

The rules below are enforced in the server, not promised in a policy. They hold for every client, including ours.

01

No passwords reach our server

Sign-in happens in the person’s browser against Google’s identity service. We receive a short-lived ID token, verify it, and discard it. The MCP client only ever sees tokens we mint.

02

No email or phone, ever

No tool returns an email address or a phone number, including the person’s own.

03

A business never sees identities

Orders and leads name a customer as an @username and a city, nothing more.

04

Lucilla does not hold funds

Card payments run through Stripe with the business as the merchant of record. A deposit is held on the customer’s card and captured at fulfilment; a shop-side cancel always refunds it in full to whoever paid.

05

An agent cannot raise its own ceiling

Agent purchases are off until the person turns them on in the app. Widening any cap confirms first; approving a parked purchase does not charge, and caps are re-checked at the moment of paying.

06

Disconnect in one tap

The person can revoke any connected client from the You tab in the app. Every call is audited.

Text to list your shop.

One text. Ask Lucilla reads your website and Google listing and asks only for what is missing.

+1 (314) 237-4046 Text to list your shop

Text CANCEL to stop texts.