Claude Code
claude mcp add --transport http ask-lucilla \ https://asklucilla.com/mcp
For developers · MCP
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
claude mcp add --transport http ask-lucilla \ https://asklucilla.com/mcp
Settings, then Connectors, then Add custom connector, and paste the URL below. Muse support is coming soon.
https://asklucilla.com/mcp
{
"mcpServers": {
"ask-lucilla": { "url": "https://asklucilla.com/mcp" }
}
}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
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.
Read your shop, listings, orders, calendar and leads. Edit listings, post, run keyword promos and follower drops, fulfil or cancel orders.
Anything that moves money or reaches other people describes the consequence first and needs a second explicit call.
Settled on-chain
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.
Send the customer to a hosted payment page and get the result back to your site.
Add a button to your page that opens checkout. No Ask Lucilla fee on a sale.
Create a checkout session, receive signed webhooks and refund when you need to.
Read the Pay with Ask Lucilla API reference
Enterprise accounts can use API-key MCP access to run and manage local reward campaigns from their own agents.
Endpoints
One MCP endpoint and one OAuth issuer, both on Google Cloud in us-east1. Everything a client needs is discoverable from the first 401.
https://asklucilla.com/mcpStreamable HTTP. JSON-RPC 2.0 over POST, one request per POST.https://asklucilla.com/mcp/.well-known/oauth-protected-resourceRFC 9728. Names the authorization server and the baseline scopes.https://asklucilla.comRFC 8414 / OIDC issuer identifier. Also the audience every token is bound to.https://asklucilla.com/.well-known/oauth-authorization-serverThe same document is served at /.well-known/openid-configuration under the issuer.https://asklucilla.com/authorizeSign-in and consent screen. Opens in the person's browser.https://asklucilla.com/tokenauthorization_code and refresh_token grants. Public clients only (no client secret).https://asklucilla.com/revokeRFC 7009.https://asklucilla.com/registerRFC 7591 dynamic registration. Client ID Metadata Documents are preferred.https://asklucilla.com/scopesThe consent wording, machine-readable. Not part of any RFC.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.
initialize, server/discover, ping, tools/list, tools/call. Capabilities: tools only.Authorization
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.
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.
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.
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.
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.
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.
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.
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"Scopes
profile.readdeals.readdeals.claimorders.writeshop.readshop.writeshop.moneyspendTool reference
Every tool below is registered on the live server. Scope, whether it asks first, and its rate limit are shown with each.
ask_lucilladeals.readwriteasks first: no30/minTalk 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/minWho 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/minThe delivery address saved for agent purchases, or null.
{}get_spend_settingsprofile.readreadasks first: no100/minThe 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/minBookable 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/minOpen 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/minDeal 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/minEverything 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/minReserve 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/minFollow a business so its posts and follower drops reach this person. Idempotent, reversible.
{ "business_id": "conn_…" }unfollow_shopdeals.claimwriteasks first: no30/minStop following a business. Idempotent.
{ "business_id": "conn_…" }cancel_my_bookingorders.writewriteasks first: yes30/minCancel 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/minOpen 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/minSet 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/minChange 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/minSay 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 }my_shopshop.readreadasks first: no100/minThe 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/minThe 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/minOrders 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/minBlocked-off time, including busy time synced from Google Calendar (editable: false).
{}shop_leadsshop.readreadasks first: no100/minPeople 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/minTake 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/minBlock 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/minRemove an owner block. Google-synced blocks are refused.
{ "block_id": "blk_…" }create_keyword_promoshop.writewriteasks first: yes30/minSet 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/minPause or resume a keyword promo. Codes already issued stay valid.
{ "promo_id": "prm_…", "active": false }set_shop_brandingshop.writewriteasks first: no30/minSet or clear the logo or cover image by public https URL. Uploads nothing.
{ "logo_url": "https://…/logo.png" }publish_shop_postshop.writewriteasks first: yes3/minPublish 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/minTake a post down everywhere. Already-down is a success, not an error.
{ "post_id": "pst_…" }send_follower_dropshop.writewriteasks first: yes3/minPush 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/minMark 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/minCancel 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/minPropose 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 }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": "…" }
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
whoami, search, availability, codes, bookings, every shop_* read.
ask_lucilla, follow, listing status, blocks, promos, branding, take-down, own-order cancel and problem report.
reserve_listing. A hold creates an obligation to a real shop.
fulfil, shop cancel, propose time, spend settings, delivery address, purchase approval.
publish_shop_post and send_follower_drop. A drop is also one per shop per day.
Privacy and money
The rules below are enforced in the server, not promised in a policy. They hold for every client, including ours.
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.
No tool returns an email address or a phone number, including the person’s own.
Orders and leads name a customer as an @username and a city, nothing more.
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.
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.
The person can revoke any connected client from the You tab in the app. Every call is audited.
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 shopText CANCEL to stop texts.