This document is designed for AI assistants, GPT bots, and automated integrations. It provides exact protocol details, tool schemas, and authentication flow.
| Property | Value |
|---|---|
| Server Name | meni-user-data-mcp |
| Protocol | MCP 2024-11-05 (Streamable HTTP) |
| Transport | HTTP POST (stateless) |
| Base URL | https://api.meni.ge/mcp |
| Content-Type | application/json |
| Wire Format | JSON-RPC 2.0 |
All tool calls require an Authorization header.
Authorization: Bearer mk_XXXXXXXXXXXX... (64 hex characters)
Scoped to a specific user account. The bot sees only that user's data.
Authorization: Bearer eyJraWQi... (JWT id_token)
Obtained via POST /auth/login. Expires in 1 hour.
Authorization: Bearer <admin_api_key>
Full access to all data. Only for system administrators.
POST https://api.meni.ge/mcp/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
Response:
{
"idToken": "eyJraWQi...",
"accessToken": "eyJraWQi...",
"refreshToken": "eyJjdHki...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
POST https://api.meni.ge/mcp
Authorization: Bearer <token>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "my-bot", "version": "1.0" }
}
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": { "tools": {} },
"serverInfo": { "name": "meni-user-data-mcp", "version": "1.0.0" }
}
}
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "tool_name",
"arguments": { ... }
}
}
Response on success:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{ "type": "text", "text": "{\"key\": \"value\"}" }
]
}
}
Response on error:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{ "type": "text", "text": "error message" }
],
"isError": true
}
}
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/ |
No | Server info + tool list |
POST |
/ |
Yes | MCP JSON-RPC protocol |
GET |
/health |
No | Health check → {"status":"ok"} |
POST |
/auth/login |
No | Login → JWT tokens |
GET |
/api/keys |
JWT only | List user's API keys |
POST |
/api/keys |
JWT only | Generate new API key |
DELETE |
/api/keys/{keyId} |
JWT only | Revoke API key |
All paths are relative to
https://api.meni.ge/mcp.
whoamiAccess: USER — Arguments: none
Returns: userId, email, userRole, authMethod
my_profileAccess: USER — Arguments: none
Returns: full user profile from S3
update_my_profileAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
fields |
object | yes | Key-value pairs to update in profile |
my_locationsAccess: USER — Arguments: none
Returns: array of user's locations
my_ordersAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Maximum number of results |
my_imagesAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
type |
string | no | Enum: all, menu-photos, category-photos, locations |
list_usersAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Maximum number of results |
get_user_profileAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | yes | The user ID |
update_user_profileAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | yes | The user ID |
fields |
object | yes | Key-value pairs to update |
search_user_by_emailAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
email |
string | yes | Email address to search |
list_locationsAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | yes | Cognito user sub |
get_location_profileAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
Returns: status, displayName, phone, address, facebookUrl, instagramUrl, domainName, settings (currency, country, defaultLanguage, publicMenuEnabled, serviceChargeEnabled), workingHours.
update_location_profileAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
fields |
object | yes | Fields to update: displayName, phone, address, facebookUrl, instagramUrl, status, settings, workingHours |
Changes are synced to CDN automatically. Do NOT use this to change domainName — use set_location_domain instead.
get_location_menuAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
language |
string | no | Language code (e.g. en, ru, ka) for localized names. Omit for all translations |
Returns full menu with categories and enriched items (names, prices, translations).
list_menu_itemsAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
categoryId |
string | no | Category ID — recommended for detailed item data |
With categoryId: returns full item data (name, price, translations). Without: returns item/category ID pairs.
get_menu_itemAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
categoryId |
string | no | Category ID (optional — omit to search all categories) |
itemId |
string | yes | Menu item ID |
Returns full details: name, description, price, translations, tags, variantGroups, addons, image info.
update_menu_itemAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
categoryId |
string | no | Category ID (optional — omit to auto-find) |
itemId |
string | yes | Menu item ID |
fields |
object | yes | Fields to merge: name, description, price, status, tags, variantGroups, addons, nameTranslations, descriptionTranslations, locationPrices, sortOrder |
Changes sync automatically to CDN and userprofile.json.
create_menu_itemAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
categoryId |
string | yes | Category ID to put the item in |
itemId |
string | yes | Unique item ID |
name |
string | yes | Item name (primary language) |
price |
number | yes | Price |
nameTranslations |
object | no | Translations: {ka, ru, uk, ...} |
description |
string | no | Item description |
status |
string | no | Enum: active, paused, archived (default: active) |
sortOrder |
number | no | Sort order (auto-assigned if omitted) |
create_menu_categoryAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
categoryId |
string | yes | Unique category ID |
name |
string | yes | Category name (primary language) |
nameTranslations |
object | no | Translations: {ka, ru, uk, ...} |
status |
string | no | Enum: active, paused, archived (default: active) |
sortOrder |
number | no | Sort order (auto-assigned if omitted) |
update_menu_categoryAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
categoryId |
string | yes | Category ID to update |
name |
string | no | New category name |
nameTranslations |
object | no | Translations to merge: {ka, ru, uk, ...} |
status |
string | no | Enum: active, paused, archived |
sortOrder |
number | no | New sort order |
Updates group-item.json (global + user), menu.location.json, and userprofile.json.
move_menu_itemAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
itemId |
string | yes | Item ID to move |
fromCategoryId |
string | yes | Source category ID |
toCategoryId |
string | yes | Destination category ID |
merge_categoriesAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
sourceCategoryId |
string | yes | Category to merge FROM (deleted after merge) |
targetCategoryId |
string | yes | Category to merge INTO (receives all items) |
keepSourceName |
boolean | no | If true, use source category name (default: keep target name) |
Atomic operation: moves all items, merges nameTranslations, deletes source category.
delete_menu_categoryAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (admin only, auto-resolved for regular users) |
locationId |
string | yes | Location ID |
categoryId |
string | yes | Category ID to delete |
force |
boolean | no | If true, also delete all items (default: false — fail if has items) |
list_ordersAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | no | User ID (reads from data.meni) |
domain |
string | no | Domain name (reads from o.meni.ge) |
limit |
integer | no | Max orders (default 50) |
get_orderAccess: USER (own domain) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | no | Domain name (e.g. UKRAINOCHKA) |
locationId |
string | no | Location ID — used to resolve domain if domain is not provided |
orderId |
string | yes | Order ID |
check_domain_availabilityAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domainName |
string | yes | Domain name to check (e.g. my-restaurant) |
currentDomainName |
string | no | Current domain of this location (own domain is always available) |
Returns available: true/false with reason. Validates format: 3-63 chars, lowercase alphanumeric and hyphens.
set_location_domainAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
locationId |
string | yes | Location ID |
domainName |
string | yes | New domain name (3-63 chars, lowercase alphanumeric and hyphens) |
Validates availability, updates profile, renames CDN files, and updates domain mappings.
list_domainsAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
prefix |
string | no | Filter domains by prefix |
resolve_domainAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain to resolve |
get_cdn_profileAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain |
get_cdn_menuAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain |
language |
string | yes | Language code (e.g., en, ru, ka) |
list_cdn_filesAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain |
invalidate_cdn_cacheAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
paths |
array of strings | yes | CDN paths, e.g. ["/locations/DOMAIN/*"] |
list_user_imagesAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | yes | Cognito user sub |
type |
string | no | Enum: all, menu-photos, category-photos, locations |
get_image_upload_urlAccess: USER
| Argument | Type | Required | Description |
|---|---|---|---|
type |
string | yes | Enum: menu-photos, category-photos, locations |
itemId |
string | no | Menu item ID or category ID (required for menu-photos, category-photos) |
locationId |
string | no | Location ID (required for locations type — hero/logo images) |
filename |
string | no | Filename (default: original.jpg) |
contentType |
string | no | Enum: image/jpeg, image/png, image/webp (default: image/jpeg) |
Returns a presigned URL valid for 15 minutes. After uploading, the image processing pipeline automatically generates thumbnails and syncs to CDN.
delete_imageAccess: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
key |
string | yes | Full S3 key in i.meni.ge, e.g. users/{userId}/menu-photos/{itemId}/original.jpg |
s3_readAccess: USER (own prefix) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
bucket |
string | yes | Enum: data.meni, cdn.meni.ge, i.meni.ge, o.meni.ge |
key |
string | yes | S3 object key |
s3_writeAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
bucket |
string | yes | Enum: data.meni, cdn.meni.ge, o.meni.ge |
key |
string | yes | S3 object key |
data |
object | yes | JSON data to write |
s3_listAccess: USER (own prefix) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
bucket |
string | yes | Enum: data.meni, cdn.meni.ge, i.meni.ge, o.meni.ge |
prefix |
string | yes | Key prefix to list |
limit |
integer | no | Max keys (default 100) |
s3_deleteAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
bucket |
string | yes | Enum: data.meni, cdn.meni.ge, o.meni.ge |
key |
string | yes | S3 object key |
cognito_list_usersAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
filter |
string | no | Cognito filter expression |
limit |
integer | no | Maximum number of results |
cognito_get_userAccess: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
username |
string | yes | Cognito username (sub UUID) |
get_system_statsAccess: ADMIN — Arguments: none
Returns: user count, location count, domain count, etc.
| Bucket | Purpose | Example Keys |
|---|---|---|
data.meni |
Private user data | users/{userId}/profile.json |
cdn.meni.ge |
Published CDN data | {domain}/menu-en.json |
i.meni.ge |
Images | users/{userId}/menu-photos/... |
o.meni.ge |
Orders | {domain}/orders/{orderId}.json |
users/{userId}/ prefixlist_users, search_user_by_email, update_user_profile, list_domains, invalidate_cdn_cache, s3_write, s3_delete, cognito_list_users, cognito_get_user, get_system_statsuserId is auto-resolved for regular users (only admins need to specify it)/api/keys) requires Cognito JWT — API keys cannot manage themselves| HTTP Status | Meaning |
|---|---|
200 |
Success (check result.isError for tool-level errors) |
400 |
Invalid JSON or missing method |
401 |
Missing or invalid authentication |
405 |
Wrong HTTP method |
500 |
Internal server error |
| Error | Meaning |
|---|---|
🔒 admin access required |
Tool requires admin role |
🔒 access denied: you can only access your own data |
User tried to access another user's data |
missing required argument: <name> |
Required parameter not provided |
unknown tool: <name> |
Tool name not recognized |
→ POST / {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"bot","version":"1.0"}}}
← 200 {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"meni-user-data-mcp","version":"1.0.0"}}}
→ POST / {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"whoami","arguments":{}}}
← 200 {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"userId\":\"abc-123\",\"email\":\"user@example.com\"}"}]}}
{
"mcpServers": {
"meni": {
"url": "https://api.meni.ge/mcp",
"headers": {
"Authorization": "Bearer <API_KEY>"
}
}
}
}
curl -X POST https://api.meni.ge/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <API_KEY>" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'
import requests
resp = requests.post("https://api.meni.ge/mcp",
headers={"Authorization": "Bearer <API_KEY>", "Content-Type": "application/json"},
json={"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": "my_profile", "arguments": {}}})
print(resp.json()["result"]["content"][0]["text"])