Saleor Store
우리 컬렉션을 쇼핑하세요
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Make any food business orderable by AI agents.
Ordering is moving from screens to tool calls. DoorDash already ships an MCP server so agents in Claude or Slack can order team lunch without anyone opening an app. Aggregators can afford that. The independent bakery, the three-location cafe, the local delivery outfit can't, and they're about to become invisible to agents. Orderable is an open-source MCP server any food business can run (or have a dev run for them) that exposes its catalog, prices, live availability, ordering, and order tracking to any MCP-capable agent.README 전문 보기 ↗Make any food business orderable by AI agents.
Ordering is moving from screens to tool calls. DoorDash already ships an MCP server so agents in Claude or Slack can order team lunch without anyone opening an app. Aggregators can afford that. The independent bakery, the three-location cafe, the local delivery outfit can't, and they're about to become invisible to agents. Orderable is an open-source MCP server any food business can run (or have a dev run for them) that exposes its catalog, prices, live availability, ordering, and order tracking to any MCP-capable agent. Agents don't care about banners or loyalty points. They check three things: is availability accurate, does checkout work, does this fit the customer's spending policy. Orderable is built for exactly that: timestamped, machine-checkable data, quotes that lock prices, idempotent ordering, and honest allergens.
Built for B2B and office ordering first (team lunch, catering, recurring pantry orders), and for people ordering through their own agent.
Live demo: orderable-mcp.vercel.app · Docs: orderable-mcp.vercel.app/docs · Transcript: examples/team-lunch.md
| Open source | Free (hosted) | Pro (hosted) | |
|---|---|---|---|
| Price | $0, MIT, forever | $0 | $29/month |
| Orders | Unlimited | 50/month | 1,000/month included, then $0.05 each |
| Runs on | Your machine or cloud | Orderable cloud | Orderable cloud |
| MCP endpoint | Self-hosted | Hosted | Hosted |
| Chat apps | Telegram, Slack (self-run bots) | Telegram, Slack, connected for you | Same, plus new channels as they ship |
| Locations | Unlimited | 1 | Up to 3 |
No commission on any plan: customers pay the business directly. Only completed orders count; going over a limit never blocks an order. More than 3 locations or your own servers: talk to us. Hosted plans are in early access (request it).
Channels: MCP, Telegram and Slack are live. WhatsApp and iMessage are coming soon.
git clone https://github.com/Lameda12/OrderAble && cd OrderAble
npm install # builds to dist/
node dist/cli.js serve --stdio --adapter mock
That's a working server with two seeded Halifax businesses (Crumb & Co bakery, Northline Coffee). Poke at it in the MCP Inspector:
npx @modelcontextprotocol/inspector node dist/cli.js serve --stdio --adapter mock
Now make it yours:
node dist/cli.js init # wizard: restaurant | cafe | bakery | delivery → menu.yaml
node dist/cli.js validate # plain-English errors with line numbers
node dist/cli.js doctor # agent-readiness score out of 100 + top 3 fixes
node dist/cli.js serve --stdio
npm link puts orderable on your PATH so you can drop the node dist/cli.js. (The npm package name will be orderable-mcp; orderable on npm is an unrelated project.)
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"orderable": {
"command": "node",
"args": ["/absolute/path/to/OrderAble/dist/cli.js", "serve", "--stdio"],
"env": {
"ORDERABLE_ADAPTER": "mock",
"ORDERABLE_DB": "/absolute/path/to/orderable.db"
}
}
}
}
Use "ORDERABLE_ADAPTER": "file" and "ORDERABLE_MENU": "/path/to/menu.yaml" for your own menu.
Claude Code:
claude mcp add orderable -e ORDERABLE_ADAPTER=mock -- node /absolute/path/to/OrderAble/dist/cli.js serve --stdio
Hosted (Streamable HTTP): requires a bearer token. Without ORDERABLE_TOKEN, one is generated and printed.
ORDERABLE_TOKEN=change-me node dist/cli.js serve --http --port 3333
claude mcp add --transport http orderable http://127.0.0.1:3333/mcp --header "Authorization: Bearer change-me"
GET /health reports version, adapter and dry-run state. The HTTP handler is built on web-standard Request/Response (createHttpHandler from orderable-mcp/mcp), so it also runs on Vercel, Cloudflare Workers or Deno. The demo site does exactly that.
Every response carrying prices or availability includes as_of and ttl_seconds. Money is always integer minor units with an explicit currency. Every successful result carries a next_step hint; every error carries a code and suggested_next_tool.
| Tool | Use it to | Key inputs | Returns |
|---|---|---|---|
list_locations | Start here. Find locations near the customer | near {lat,lng}, postal_code, fulfillment | Locations with hours, open_now, next_open_at, distance_km, delivers_to_you |
search_menu | Find items with live availability | location_id, query, dietary[], exclude_allergens[], max_price, category, desired_time | Items with price, allergens (contains / may_contain / unknown), modifiers, lead time, availability |
get_item | Full detail before quoting or for allergies | item_id | Item, complete allergen map, required modifier groups, availability per location |
check_availability | Can these items be ready at that time? | location_id, item_ids[], desired_time, fulfillment, quantities | Per item: orderable, earliest_time, reason, stale |
plan_group_order | Feed a group within an all-in budget | location_id, headcount, budget_per_person, dietary_requirements {vegetarian: 3, …}, desired_time | Cart lines ready for quote_order, per-line coverage, estimate, unmet constraints, warnings |
quote_order | Price and lock a cart for 10 minutes | location_id, lines[], fulfillment, desired_time, delivery_address, headcount | quote_id, itemized subtotal, fees, tax, total, ETA, expires_at, policy check |
place_order | Turn a quote into an order | quote_id, idempotency_key, customer, , |
Resources: orderable://menu/{location_id} (full menu with availability) and orderable://policies (cancellation, refund, payment, delivery fees, the agent spending policy, and ordering rules), readable without tool calls.
Error codes: QUOTE_EXPIRED, QUOTE_NOT_FOUND, QUOTE_ALREADY_USED, PRICE_CHANGED, ITEM_SOLD_OUT, ITEM_NOT_FOUND, ITEM_UNAVAILABLE, INVALID_MODIFIERS, MINIMUM_NOT_MET, OUTSIDE_DELIVERY_ZONE, CLOSED, LEAD_TIME_NOT_MET, POLICY_BLOCKED, LOCATION_NOT_FOUND, FULFILLMENT_UNAVAILABLE, ORDER_NOT_FOUND, CANCELLATION_WINDOW_CLOSED, IDEMPOTENCY_KEY_REUSED, INVALID_REQUEST, ADAPTER_ERROR.
{
"error": {
"code": "LEAD_TIME_NOT_MET",
"message": "Chocolate Celebration Cake (8 in) needs 48h notice. Earliest: 2026-10-09T13:00:00.000Z",
"suggested_next_tool": "check_availability",
"details": { "problems": [{ "item_id": "crumb-chocolate-cake", "earliest_time": "2026-10-09T13:00:00.000Z" }] }
}
}
place_order only accepts a valid, unexpired, unused quote. Stock, prices and policy are re-checked at order time (ITEM_SOLD_OUT, PRICE_CHANGED, POLICY_BLOCKED).idempotency_key + quote returns the original order (idempotent_replay: true), including under concurrent retries. A key reused for a different quote is rejected.orderable.config.yaml: per-order cap, per-day cap, allowed locations, max headcount. Quotes show the policy result up front; blocked orders return machine-readable violations.DRY_RUN is on by default. Orders are recorded in SQLite but never sent to the adapter until the owner sets dry_run: false (or DRY_RUN=false).contains / may_contain / unknown) for every major allergen. Missing data is unknown, never "safe". plan_group_order flags shared-kitchen risk (e.g. gluten-free by recipe, may contain gluten).Customers and teams can order from inside the chat apps they already use. Orderable ships a Claude-powered ordering assistant (src/agent.ts) that calls the same nine MCP tools through a real MCP client, so every rule (quotes, policy caps, DRY_RUN, idempotency, honest allergens) applies in chat exactly as it does in Claude Desktop. It confirms the total before placing anything and asks for missing contact details instead of inventing them.
| Surface | How it connects | Run it |
|---|---|---|
| Telegram | Bot via @BotFather. DMs or groups. /start, /reset | orderable bot telegram (long polling, no public URL) or the /api/telegram webhook on Vercel |
| Slack | Slack app. @mention it in a channel (answers in a thread), DM it, or /order … | orderable bot slack (HTTP) or /api/slack/events on Vercel |
| Claude in Slack, claude.ai, any MCP client | Add Orderable as a remote MCP connector | https://<host>/api/mcp/<ORDERABLE_TOKEN> |
All bots need ANTHROPIC_API_KEY. The agent uses claude-opus-5-5 at medium effort with server-side refusal fallback enabled; override with ORDERABLE_AGENT_MODEL and ORDERABLE_AGENT_EFFORT. Conversation history is kept per chat (per thread in Slack channels) in memory, and starts fresh after 2 idle hours or when it gets long.
Message @BotFather, send /newbot, copy the token.
Run it locally (polling, nothing to expose):
TELEGRAM_BOT_TOKEN=123:abc ANTHROPIC_API_KEY=sk-ant-... node dist/cli.js bot telegram --adapter mock
Or on Vercel: set TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET (any random string) and ANTHROPIC_API_KEY on the project, then register the webhook once:
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
-d url=https://orderable-mcp.vercel.app/api/telegram \
-d secret_token=$TELEGRAM_WEBHOOK_SECRET
Get a manifest with your URLs filled in: node dist/cli.js bot slack-manifest https://your-host (self-hosted) or open https://<vercel-host>/api/slack/manifest.
api.slack.com/apps → Create New App → From a manifest → paste it → Install to Workspace.
Copy the Bot User OAuth Token (xoxb-…) and Signing Secret, then either:
SLACK_BOT_TOKEN=xoxb-... SLACK_SIGNING_SECRET=... ANTHROPIC_API_KEY=sk-ant-... \
node dist/cli.js bot slack --port 3334 # expose /slack/events and /slack/commands publicly
or set the same three variables on the Vercel project (endpoints /api/slack/events and /api/slack/commands).
Requests are verified with Slack's signing secret (5-minute replay window), acknowledged inside Slack's 3-second limit, and deduplicated by event id. Scopes: app_mentions:read, chat:write, commands, im:history, reactions:write, users:read (used to pre-fill the customer's name).
Clients that only take a URL can put the token in the path: https://<host>/mcp/<ORDERABLE_TOKEN> for orderable serve --http, or https://<vercel-host>/api/mcp/<token> on the site. Treat that URL as a secret. The public demo is https://orderable-mcp.vercel.app/api/mcp/orderable-demo (fake businesses, DRY_RUN on).
Owners don't need to touch YAML. Run Orderable in owner mode and connect it to Claude Desktop (or any MCP agent that can read images):
node dist/cli.js serve --stdio --owner # owner tools on; customers never get them
Then say "Set up my bakery. Here's the menu" and attach a photo. The assistant reads it and calls five owner tools: owner_get_setup, owner_save_business, owner_upsert_items, owner_remove_items and owner_update_stock. Orderable validates everything with the same rules as orderable validate before saving, never writes an invalid menu, and tells the assistant which items still need allergen info so it asks instead of guessing. Over HTTP, a separate ORDERABLE_OWNER_TOKEN unlocks the owner tools; the normal ORDERABLE_TOKEN never does.
Stock changes work in plain words from Telegram, Slack, the assistant, or the terminal: orderable stock "out of butter croissants". Allow-list who may do it with ORDERABLE_OWNER_TELEGRAM_IDS and ORDERABLE_OWNER_SLACK_IDS.
One deployment can serve many restaurants. Each account gets two secret URLs: an owner URL that unlocks the owner tools, and an agent URL for customers' agents. Menus, quotes and orders live in Postgres, so it works on serverless hosts where every request may land on a fresh instance. Tokens are stored only as SHA-256 hashes, and new accounts start in test mode (orders are recorded, nothing is sent to the kitchen).
We use Neon (serverless Postgres). On Vercel, add Neon from the Marketplace and it sets DATABASE_URL for you; elsewhere, paste Neon's pooled connection string (the host with -pooler, ?sslmode=require). Any Postgres 14+ works.
export DATABASE_URL="postgres://...-pooler.../neondb?sslmode=require"
node dist/cli.js accounts migrate # once; safe to re-run
node dist/cli.js accounts create "Rosie's Bakeshop" # prints the owner URL and agent URL
node dist/cli.js accounts list
node dist/cli.js accounts token <id> owner # rotate a lost URL (old one stops working)
node dist/cli.js accounts live <id> on # leave test mode
node dist/cli.js serve --http --hosted # self-hosted; the Vercel site does this at /api/mcp/<token>
The owner pastes their owner URL into Claude (or any MCP client) and sets up by talking. Billing is not built yet.
Owners aren't developers, so the menu file uses dollars, "07:00-18:00" hours, "48h" lead times and two short allergen lists. orderable init writes a starter file with examples for your business type: bakeries get lead-time cakes and a daily sell-out item, cafes get drink modifiers, restaurants get catering trays.
items:
- id: chocolate-cake
name: Chocolate Celebration Cake (8 in)
category: cakes
price: 58.00
serves: 12
lead_time: 48h # agents won't promise it sooner
dietary: [vegetarian]
allergens:
contains: [wheat, gluten, milk, egg, soy]
may_contain: [tree_nut, peanut]
stock: in_stock # or { status: low, quantity: 6 }
stock_updated_at: live # or a timestamp; agents drop stale merchants
$ orderable validate
✗ error menu.yaml:76 Country Sourdough is in category "bread", which isn't defined under categories:.
! warning menu.yaml:54 Butter Croissant has no allergen info, agents will treat it as unknown.
$ orderable doctor
Agent readiness 94/100
~ Allergen coverage 21/25 31/37 items declare allergens
✓ Stock tracked 15/15 37/37 items have a stock status
✓ Availability freshness 10/10 Stock data is fresh
✓ Opening hours 15/15 4/4 locations have hours
✓ Delivery zones 10/10 2/2 delivery locations have a checkable zone
✓ Quote-to-order flow 20/20 Quoted and placed a dry-run order for Butter Croissant
~ Item descriptions 3/5 19/37 items have a description
Top fixes
1. Add allergens to Seasonal Fruit Galette, … (+4)
The file adapter hot-reloads menu.yaml when it changes, so updating stock is just saving the file.
src/
server.ts Node entry points: stdio + HTTP, adapter/store wiring
mcp.ts Transport-agnostic MCP server: tools, resources, web-standard HTTP handler
service.ts Ordering engine: hours, lead times, zones, stock, quotes, policy, idempotency
schema.ts Canonical domain model (Zod)
tools/ One file per tool (input + output Zod schemas, LLM-facing descriptions)
adapters/
types.ts Adapter interface
local.ts Shared base for catalog-backed adapters (simulated order lifecycle)
file.ts menu.yaml adapter (default)
mock.ts Seeded Crumb & Co + Northline Coffee
square.ts Square (v0.2, experimental: locations + catalog read; ordering TODO)
policy.ts Spending limits and checks
store.ts SQLite persistence (better-sqlite3); store-memory.ts for serverless
lint.ts `validate` rules
doctor.ts `doctor` scoring
agent.ts Claude chat agent that orders through the MCP tools
channels/ telegram.ts (webhook + polling), slack.ts (events, /order, manifest)
cli.ts orderable init | validate | doctor | serve | bot
An adapter translates between a POS and the canonical model. Orderable handles quoting rules, hours, lead times, zones, policy, idempotency and persistence, so an adapter stays small. Implement Adapter from src/adapters/types.ts:
interface Adapter {
readonly name: string;
listBusinesses(): Promise<Business[]>;
listLocations(): Promise<Location[]>;
getMenu(locationId: string): Promise<Menu>;
getItem(itemId: string): Promise<Item | null>;
getAvailability(locationId: string, itemIds: string[]): Promise<StockLevel[]>;
quote(req: PricingRequest): Promise<PricingResult>; // price + validate modifiers
placeOrder(req: AdapterOrderRequest): Promise<AdapterOrderResult>;
getOrderStatus(externalId: string, order: Order): Promise<AdapterOrderStatus>;
cancelOrder(externalId: string, reason: string, order: Order): Promise<{ cancelled: boolean; message?: string }>;
}
Guidelines:
allergenMap(undefined) (all unknown). If it doesn't track stock, return status: "unknown".as_of is when the POS last confirmed the data, not when you read it from a cache.amount is cents).OrderableError for expected failures (ITEM_NOT_FOUND, INVALID_MODIFIERS); anything else becomes ADAPTER_ERROR.order_id as the POS idempotency key in placeOrder, so a retried send never duplicates on the POS side either.LocalCatalogAdapter and only supply a Catalog. resolveModifiers and computeTotals in pricing.ts are reusable.createAdapter (src/server.ts) and add a test that runs the quote → order happy path against it.npm test # vitest: 87 tests (schema, ordering, idempotency, policy, group orders, MCP surface, HTTP auth, file adapter, CLI rules, chat agent, Telegram, Slack)
npm run typecheck
npm run example # regenerate examples/team-lunch.md from a real run
npm run inspect # MCP Inspector against the mock adapter
The demo site lives in web/ (Next.js, deployed on Vercel), with docs at /docs (the tool reference is generated from the server's schemas) and legal pages at /terms, /privacy, /refunds, /cookies, /accessibility and /contact. Business details for those pages are in web/lib/site.ts. It runs the real server in a serverless function: /api/mcp is a live Streamable HTTP endpoint backed by the mock adapter (demo token is printed on the site) and /api/demo runs the team-lunch scenario end to end.
plan_group_order propose swaps when an item sells out after quotingMIT
confirm: truepayment_methodOrder, idempotent_replay, payment instructions (never card data) |
get_order_status | Track an order | order_id | Status, timestamped timeline, ETA, cancellable_until |
cancel_order | Cancel within the merchant's window | order_id, reason | Result and timeline |