meni.ge MCP Server β AI / GPT Integration Reference
This document is designed for AI assistants, GPT bots, and automated integrations. It provides exact protocol details, tool schemas, and authentication flow.
Server Information
| 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 |
Authentication
All tool calls require an Authorization header.
Option 1: User MCP API Key (recommended)
Authorization: Bearer mk_XXXXXXXXXXXX... (64 hex characters)
Scoped to a specific user account. The bot sees only that user's data.
Option 2: Cognito JWT Token
Authorization: Bearer eyJraWQi... (JWT id_token)
Obtained via POST /auth/login. Expires in 1 hour.
Option 3: Admin API Key
Authorization: Bearer <admin_api_key>
Full access to all data. Only for system administrators.
Obtaining a JWT Token
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"
}
MCP Protocol
Initialize
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" }
}
}
List Tools
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
Call Tool
{
"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
}
}
Endpoints
| 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.
Complete Tool Reference
Access Levels
- USER β Available to all authenticated users (data scoped to own account)
- ADMIN β Available only to administrators (full access)
Self-Service Tools
whoami
Access: USER β Arguments: none
Returns: userId, email, userRole, authMethod
my_profile
Access: USER β Arguments: none
Returns: full user profile from S3
update_my_profile
Access: USER
| Argument | Type | Required | Description |
|---|---|---|---|
fields |
object | yes | Key-value pairs to update in profile |
my_locations
Access: USER β Arguments: none
Returns: array of user's locations
my_orders
Access: USER
| Argument | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Maximum number of results |
my_images
Access: USER
| Argument | Type | Required | Description |
|---|---|---|---|
type |
string | no | Enum: all, menu-photos, category-photos, locations |
User Profiles
list_users
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Maximum number of results |
get_user_profile
Access: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | yes | The user ID |
update_user_profile
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | yes | The user ID |
fields |
object | yes | Key-value pairs to update |
search_user_by_email
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
email |
string | yes | Email address to search |
Locations
list_locations
Access: USER (own) / ADMIN (any)
| Argument | Type | Required | Description |
|---|---|---|---|
userId |
string | yes | Cognito user sub |
get_location_profile
Access: 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_profile
Access: 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_menu
Access: 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).
Menu Items
list_menu_items
Access: 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_item
Access: 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_item
Access: 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_item
Access: 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_category
Access: 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_category
Access: 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_item
Access: 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_categories
Access: 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_category
Access: 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) |
Orders
list_orders
Access: 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_order
Access: 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 |
Domains
check_domain_availability
Access: 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_domain
Access: 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_domains
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
prefix |
string | no | Filter domains by prefix |
resolve_domain
Access: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain to resolve |
CDN
get_cdn_profile
Access: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain |
get_cdn_menu
Access: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain |
language |
string | yes | Language code (e.g., en, ru, ka) |
list_cdn_files
Access: USER
| Argument | Type | Required | Description |
|---|---|---|---|
domain |
string | yes | The domain |
invalidate_cdn_cache
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
paths |
array of strings | yes | CDN paths, e.g. ["/locations/DOMAIN/*"] |
Images
list_user_images
Access: 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_url
Access: 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_image
Access: 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 (Low-level Storage)
s3_read
Access: 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_write
Access: 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_list
Access: 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_delete
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
bucket |
string | yes | Enum: data.meni, cdn.meni.ge, o.meni.ge |
key |
string | yes | S3 object key |
Cognito (User Management)
cognito_list_users
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
filter |
string | no | Cognito filter expression |
limit |
integer | no | Maximum number of results |
cognito_get_user
Access: ADMIN
| Argument | Type | Required | Description |
|---|---|---|---|
username |
string | yes | Cognito username (sub UUID) |
System
get_system_stats
Access: ADMIN β Arguments: none
Returns: user count, location count, domain count, etc.
S3 Bucket Reference
| 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 |
Access Control
- Regular users can only access their own data (enforced by userId)
- S3 access for regular users is restricted to
users/{userId}/prefix - Admin-only tools:
list_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_stats - For location/menu tools,
userIdis auto-resolved for regular users (only admins need to specify it) - API key management (
/api/keys) requires Cognito JWT β API keys cannot manage themselves
Error Handling
| 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 |
Tool Error Messages
| 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 |
Example Session
β 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\"}"}]}}
Client Configuration
Claude Desktop
{
"mcpServers": {
"meni": {
"url": "https://api.meni.ge/mcp",
"headers": {
"Authorization": "Bearer <API_KEY>"
}
}
}
}
cURL
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":{}}}'
Python
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"])