MCP 서버

Kirah Local Services

io.github.kirubel/kirah-local-services
비즈니스 및 운영 공개 · 연결 가능 MCP 2026-07-28

이 MCP로 할 수 있는 일

Searches local businesses and services, checks availability, and creates, tracks, reschedules, or cancels service bookings.

cancel_booking
Cancel booking
Cancels one grant-authorized booking after explicit user confirmation. Reusing idempotency_key makes retries safe. Kirah refuses cancellations that could refund a charged deposit or restore a paid plan session; an allowed cancellation has no undo.
파괴적 작업 외부 접근 가능
입력 스키마
{'type': 'object', 'required': ['tenant', 'booking_ref', 'grant', 'idempotency_key'], 'properties': {'grant': {'type': 'string', 'maxLength': 2048, 'minLength': 1, 'description': "The capability token from this booking's create_booking response (confirmation.grant). Required: without it there is no way to establish that you are the agent that made this booking. It must carry the `booking:cancel` scope. Tampered, wrong-booking, wrong-tenant, revoked and insufficiently-scoped grants all answer invalid_grant, indistinguishably; an authentic grant past the appointment start answers expired_grant."}, 'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}, 'booking_ref': {'type': 'string', 'maxLength': 200, 'minLength': 16, 'description': 'The reference create_booking returned for the booking you want to CANCEL. It must be the SAME booking the supplied grant was issued for; a mismatch answers invalid_grant, indistinguishably from a forged grant.'}, 'agent_metadata': {'type': 'object', 'properties': {'source': {'type': 'string', 'maxLength': 120}, 'request_id': {'type': 'string', 'maxLength': 120}}, 'additionalProperties': False}, 'idempotency_key': {'type': 'string', 'maxLength': 200, 'minLength': 16, 'description': 'REQUIRED — unlike create_booking, this action cannot derive one, because "cancel booking X" is byte-identical whether it is a transport retry or a deliberate second call. Omitting it answers missing_idempotency_key. Reuse the SAME key when retrying the SAME cancellation: the replay returns the original result with idempotent_replay:true, cancels nothing and sends no second notice. The same key against a different booking_ref answers idempotency_conflict.'}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'required': ['outcome'], 'properties': {'demo': {'type': 'boolean'}, 'grant': {'type': 'string', 'maxLength': 2048}, 'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'end_iso': {'type': 'string', 'format': 'date-time'}, 'missing': {'type': 'array', 'items': {'type': 'string'}}, 'outcome': {'enum': ['ok', 'booked', 'submitted', 'payment_required', 'intake_required', 'slot_taken', 'not_bookable', 'rescheduled', 'cancelled', 'validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'notified': {'type': 'string'}, 'start_iso': {'type': 'string', 'format': 'date-time'}, 'policy_fee': {'type': 'string'}, 'request_id': {'type': 'string'}, 'booking_ref': {'type': 'string'}, 'booking_url': {'type': 'string'}, 'demo_notice': {'type': 'string'}, 'intake_form': {'allOf': [{'type': 'object', 'required': ['id', 'name', 'required', 'fields'], 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'fields': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'label', 'type', 'required'], 'properties': {'id': {'type': 'string'}, 'type': {'enum': ['text', 'textarea', 'boolean', 'select', 'date'], 'type': 'string'}, 'label': {'type': 'string'}, 'consent': {'type': 'boolean'}, 'options': {'type': 'array', 'items': {'type': 'string'}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}]}, 'payment_url': {'type': 'string'}, 'confirmation': {'type': 'object', 'properties': {'grant': {'type': 'string', 'maxLength': 2048}, 'ics_url': {'type': 'string'}, 'manage_url_sent_to': {'type': 'string'}}, 'additionalProperties': False}, 'service_name': {'type': 'string'}, 'deposit_cents': {'type': 'integer'}, 'provider_name': {'type': 'string'}, 'status_reason': {'type': 'string'}, 'appointment_id': {'type': 'string'}, 'policy_message': {'type': 'string'}, 'retry_after_sec': {'type': 'integer'}, 'cancel_fee_cents': {'type': 'integer'}, 'already_cancelled': {'type': 'boolean'}, 'idempotent_replay': {'type': 'boolean'}}, 'additionalProperties': False, 'x-channel-adapter-failures': {'financial_action_unsupported': 'A channel adapter that forbids financial effects refused a cancellation that could refund a charged deposit or restore a paid plan credit. The booking is unchanged; the customer uses the secure manage link or contacts the business.'}}
create_booking
Create booking
Creates one guest booking for an exact service, provider and ISO-8601 start time after explicit user confirmation. Reusing idempotency_key makes retries safe. Kirah accepts no payment credentials through MCP; a deposit-required service may return a Kirah-hosted human payment continuation, but this call transfers no funds.
파괴적 작업 외부 접근 가능
입력 스키마
{'type': 'object', 'required': ['tenant', 'service_id', 'start_iso', 'client'], 'properties': {'client': {'type': 'object', 'required': ['name', 'email'], 'properties': {'name': {'type': 'string', 'maxLength': 200}, 'email': {'type': 'string', 'format': 'email', 'maxLength': 200}, 'phone': {'type': 'string', 'maxLength': 60}}, 'additionalProperties': False}, 'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}, 'start_iso': {'type': 'string', 'format': 'date-time'}, 'service_id': {'type': 'string', 'maxLength': 200}, 'provider_id': {'type': 'string', 'maxLength': 200}, 'agent_metadata': {'type': 'object', 'properties': {'source': {'type': 'string', 'maxLength': 120}, 'request_id': {'type': 'string', 'maxLength': 120}}, 'additionalProperties': False}, 'idempotency_key': {'type': 'string', 'maxLength': 200, 'minLength': 16}, 'intake_responses': {'type': 'object', 'description': "Answers to the service's intake form (2.8), keyed by intake field id as advertised in list_services' service.intake_form.fields[].id. FLAT only: values must be a string, a boolean, or a finite number — arrays, nested objects and nulls are rejected. Unknown keys are ignored by the booking engine. Answers are validated against the LIVE form; a required plain boolean accepts an explicit true OR false, a consent boolean accepts only true, and a select must exactly match one advertised option. Omit this field entirely when the service has no intake form.", 'maxProperties': 40, 'propertyNames': {'type': 'string', 'maxLength': 80, 'minLength': 1}, 'additionalProperties': {'type': ['string', 'boolean', 'number'], 'maxLength': 4000}}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'required': ['outcome'], 'properties': {'demo': {'type': 'boolean'}, 'grant': {'type': 'string', 'maxLength': 2048}, 'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'end_iso': {'type': 'string', 'format': 'date-time'}, 'missing': {'type': 'array', 'items': {'type': 'string'}}, 'outcome': {'enum': ['ok', 'booked', 'submitted', 'payment_required', 'intake_required', 'slot_taken', 'not_bookable', 'rescheduled', 'cancelled', 'validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'notified': {'type': 'string'}, 'start_iso': {'type': 'string', 'format': 'date-time'}, 'policy_fee': {'type': 'string'}, 'request_id': {'type': 'string'}, 'booking_ref': {'type': 'string'}, 'booking_url': {'type': 'string'}, 'demo_notice': {'type': 'string'}, 'intake_form': {'allOf': [{'type': 'object', 'required': ['id', 'name', 'required', 'fields'], 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'fields': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'label', 'type', 'required'], 'properties': {'id': {'type': 'string'}, 'type': {'enum': ['text', 'textarea', 'boolean', 'select', 'date'], 'type': 'string'}, 'label': {'type': 'string'}, 'consent': {'type': 'boolean'}, 'options': {'type': 'array', 'items': {'type': 'string'}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}]}, 'payment_url': {'type': 'string'}, 'confirmation': {'type': 'object', 'properties': {'grant': {'type': 'string', 'maxLength': 2048}, 'ics_url': {'type': 'string'}, 'manage_url_sent_to': {'type': 'string'}}, 'additionalProperties': False}, 'service_name': {'type': 'string'}, 'deposit_cents': {'type': 'integer'}, 'provider_name': {'type': 'string'}, 'status_reason': {'type': 'string'}, 'appointment_id': {'type': 'string'}, 'policy_message': {'type': 'string'}, 'retry_after_sec': {'type': 'integer'}, 'cancel_fee_cents': {'type': 'integer'}, 'already_cancelled': {'type': 'boolean'}, 'idempotent_replay': {'type': 'boolean'}}, 'additionalProperties': False, 'x-channel-adapter-failures': {'financial_action_unsupported': 'A channel adapter that forbids financial effects refused a cancellation that could refund a charged deposit or restore a paid plan credit. The booking is unchanged; the customer uses the secure manage link or contacts the business.'}}
find_available_services
Find available services (earliest openings)
Checks up to five top matching eligible Kirah businesses for earliest current availability within an exact ISO-8601 window of up to 14 days. Returns checked status and a partial-coverage flag.
읽기 전용 외부 접근 가능
입력 스키마
{'type': 'object', 'required': ['query'], 'properties': {'query': {'type': 'string', 'maxLength': 200, 'minLength': 1, 'description': "The consumer's service need in plain words — identical semantics to search_businesses.query."}, 'location': {'type': 'string', 'maxLength': 120, 'description': "Optional place text ('City, ST', ZIP, bare city/state) OR explicit coordinates 'lat,lng' — identical semantics to search_businesses.location."}, 'tenant_mode': {'enum': ['real_only', 'include_demos', 'demos_only'], 'type': 'string', 'default': 'include_demos', 'description': 'The authoritative demo eligibility filter, with the same semantics as search_businesses. include_demos is the default and returns real and demo candidates; real_only excludes demos; demos_only returns only clearly labeled demonstration candidates.'}, 'include_demo': {'type': 'boolean', 'description': "Back-compat alias: true is equivalent to tenant_mode 'include_demos' and false to 'real_only'. When omitted, tenant_mode defaults to include_demos. Every demo candidate carries demo:true and a demo_notice. Supplying both include_demo and tenant_mode with disagreeing demo-inclusion is invalid_tenant_mode."}, 'radius_miles': {'type': 'integer', 'maximum': 500, 'minimum': 1, 'description': 'Radius in miles (default 25) — operative when the location resolves to coordinates (2.6 semantics).'}, 'earliest_after': {'type': 'string', 'format': 'date-time', 'description': 'Exact ISO 8601 instant the window opens (default: now). Natural-language times are rejected (invalid_earliest_after).'}, 'candidate_limit': {'type': 'integer', 'default': 3, 'maximum': 5, 'minimum': 1, 'description': 'How many top-matching businesses get LIVE availability checks (invalid_candidate_limit outside 1-5). This bounds the whole fan-out: businesses beyond it are never touched, and partial:true says so.'}, 'earliest_before': {'type': 'string', 'format': 'date-time', 'description': 'Exact ISO 8601 instant the window closes (default: earliest_after + 14 days). Must be after earliest_after and at most 14 days after it (invalid_earliest_before).'}, 'max_price_cents': {'type': 'integer', 'maximum': 10000000, 'minimum': 0, 'description': 'Only candidate services with a parseable price at or under this amount are considered.'}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'anyOf': [{'type': 'object', 'required': ['outcome', 'earliest_after', 'earliest_before', 'partial', 'candidates'], 'properties': {'outcome': {'const': 'ok'}, 'partial': {'type': 'boolean'}, 'candidates': {'type': 'array', 'items': {'type': 'object', 'properties': {'demo': {'type': 'boolean'}, 'match': {'type': 'object', 'properties': {'score': {'type': 'integer'}, 'evidence': {'type': 'array', 'items': {'type': 'string'}}, 'match_type': {'enum': ['exact', 'ontology', 'description'], 'type': 'string'}}, 'additionalProperties': False}, 'checked': {'type': 'boolean'}, 'service': {'type': 'object', 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'price_cents': {'type': ['integer', 'null']}, 'price_label': {'type': 'string'}, 'duration_minutes': {'type': 'integer'}}, 'additionalProperties': False}, 'location': {'type': 'object', 'properties': {'city': {'type': ['string', 'null']}, 'match': {'enum': ['postal', 'city', 'market', 'region', 'coordinates'], 'type': 'string'}, 'region': {'type': ['string', 'null']}, 'postal_code': {'type': ['string', 'null']}, 'distance_miles': {'type': 'number'}}, 'additionalProperties': False}, 'demo_notice': {'type': 'string'}, 'tenant_name': {'type': 'string'}, 'tenant_slug': {'type': 'string'}, 'earliest_slot': {'type': ['object', 'null'], 'properties': {'end_iso': {'type': 'string', 'format': 'date-time'}, 'start_iso': {'type': 'string', 'format': 'date-time'}}, 'additionalProperties': False}}, 'additionalProperties': False}}, 'demo_notice': {'type': 'string'}, 'distance_note': {'type': 'string'}, 'earliest_after': {'type': 'string', 'format': 'date-time'}, 'earliest_before': {'type': 'string', 'format': 'date-time'}, 'demo_exclusion_note': {'type': 'string'}, 'demo_businesses_excluded': {'type': 'integer', 'minimum': 1}, 'demo_businesses_excluded_is_exhaustive': {'type': 'boolean'}}, 'additionalProperties': False}, {'type': 'object', 'required': ['outcome', 'reason', 'detail'], 'properties': {'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'outcome': {'enum': ['validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'retry_after_sec': {'type': 'integer'}}, 'additionalProperties': False}]}
get_availability
Get availability
Returns current open times for one exact service and optional provider over a YYYY-MM-DD range of up to 31 days, with at most 20 rows. Approval-mode times are preferences rather than holds.
읽기 전용
입력 스키마
{'type': 'object', 'required': ['tenant', 'service_id', 'date_range'], 'properties': {'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}, 'date_range': {'type': 'object', 'required': ['start', 'end'], 'properties': {'end': {'type': 'string', 'format': 'date'}, 'start': {'type': 'string', 'format': 'date'}}, 'additionalProperties': False}, 'service_id': {'type': 'string', 'maxLength': 200}, 'provider_id': {'type': 'string', 'maxLength': 200}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'anyOf': [{'type': 'object', 'required': ['outcome', 'timezone', 'slots'], 'properties': {'slots': {'type': 'array', 'items': {'type': 'object', 'required': ['start_iso', 'end_iso', 'provider_id'], 'properties': {'end_iso': {'type': 'string', 'format': 'date-time'}, 'start_iso': {'type': 'string', 'format': 'date-time'}, 'provider_id': {'type': 'string'}, 'provider_name': {'type': 'string'}}, 'additionalProperties': False}, 'maxItems': 20}, 'notice': {'type': 'string'}, 'outcome': {'enum': ['ok'], 'type': 'string'}, 'bookable': {'type': 'boolean'}, 'timezone': {'type': 'string'}, 'booking_mode': {'enum': ['instant', 'approval_required'], 'type': 'string'}, 'status_reason': {'enum': ['suspended', 'agent_booking_disabled', 'no_booking_section'], 'type': 'string'}}, 'additionalProperties': False}, {'type': 'object', 'required': ['outcome', 'reason', 'detail'], 'properties': {'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'outcome': {'enum': ['validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'retry_after_sec': {'type': 'integer'}}, 'additionalProperties': False}]}
get_booking_status
Get booking status
Returns the current status, time, service and provider for one grant-authorized booking reference. Client contact details and intake responses are excluded.
읽기 전용
입력 스키마
{'type': 'object', 'required': ['tenant', 'booking_ref'], 'properties': {'grant': {'type': 'string', 'maxLength': 4096, 'minLength': 1, 'description': 'The booking-bound capability token returned by create_booking. It must match this tenant and booking_ref and remain unexpired and unrevoked.'}, 'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}, 'booking_ref': {'type': 'string', 'maxLength': 200, 'minLength': 16, 'description': 'The reference create_booking returned (its idempotency key). Tampered, unrelated, cross-tenant or never-committed references answer one indistinguishable booking_not_found.'}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'anyOf': [{'type': 'object', 'required': ['outcome', 'booking'], 'properties': {'booking': {'type': 'object', 'required': ['booking_ref', 'status', 'start_iso', 'end_iso', 'timezone', 'service_name'], 'properties': {'status': {'type': 'string'}, 'end_iso': {'type': ['string', 'null'], 'format': 'date-time'}, 'timezone': {'type': 'string'}, 'start_iso': {'type': ['string', 'null'], 'format': 'date-time'}, 'booking_ref': {'type': 'string'}, 'service_name': {'type': 'string'}, 'provider_name': {'type': 'string'}}, 'additionalProperties': False}, 'outcome': {'enum': ['ok'], 'type': 'string'}}, 'additionalProperties': False}, {'type': 'object', 'required': ['outcome', 'reason', 'detail'], 'properties': {'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'outcome': {'enum': ['validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'retry_after_sec': {'type': 'integer'}}, 'additionalProperties': False}]}
get_reschedule_options
Get reschedule options
Returns up to five real alternate times and the current fee or approval consequences for one grant-authorized booking. It does not hold, move or cancel the appointment.
읽기 전용
입력 스키마
{'type': 'object', 'required': ['tenant', 'booking_ref', 'grant'], 'properties': {'grant': {'type': 'string', 'maxLength': 2048, 'minLength': 1, 'description': "The capability token from this booking's create_booking response (confirmation.grant). Required: without it there is no way to establish that you are the agent that made this booking. Tampered, wrong-booking, wrong-tenant and revoked grants all answer invalid_grant, indistinguishably; an authentic grant past the appointment start answers expired_grant. These are the GRANT codes, not the payment page's invalid_token/expired_token — through contract 2.11 this action answered the latter, whose prose wrongly told callers their payment link was broken."}, 'limit': {'type': 'integer', 'default': 2, 'maximum': 5, 'minimum': 1, 'description': 'How many options to return. Default 2, hard maximum 5; a larger value is CLAMPED to 5 rather than rejected. The ceiling is deliberate: the primary consumer is a voice agent reading options aloud, and five spoken times is more than a caller can hold in their head.'}, 'latest': {'type': 'string', 'format': 'date-time', 'description': 'Optional exact ISO 8601 instant — the latest replacement time to consider. Default: now + 14 days. Must be after `earliest` and at most 31 days after it; anything else answers invalid_latest. Supply it explicitly whenever `earliest` is more than 14 days out.'}, 'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}, 'earliest': {'type': 'string', 'format': 'date-time', 'description': 'Optional exact ISO 8601 instant — the earliest replacement time to consider. Default: now + 2 hours (a slot the customer cannot physically reach is not an option). Natural-language times are not accepted.'}, 'booking_ref': {'type': 'string', 'maxLength': 200, 'minLength': 16, 'description': 'The reference create_booking returned (its idempotency key). It must be the SAME booking the supplied grant was issued for; a mismatch answers invalid_grant, indistinguishably from a forged grant.'}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'anyOf': [{'type': 'object', 'required': ['outcome', 'booking', 'options', 'consequences'], 'properties': {'booking': {'type': 'object', 'required': ['service_name', 'provider_name', 'current_start_iso', 'current_end_iso', 'timezone'], 'properties': {'timezone': {'type': 'string'}, 'service_name': {'type': 'string'}, 'provider_name': {'type': ['string', 'null']}, 'current_end_iso': {'type': ['string', 'null'], 'format': 'date-time'}, 'current_start_iso': {'type': ['string', 'null'], 'format': 'date-time'}}, 'additionalProperties': False}, 'options': {'type': 'array', 'items': {'type': 'object', 'required': ['start_iso', 'end_iso', 'provider_id'], 'properties': {'end_iso': {'type': 'string', 'format': 'date-time'}, 'start_iso': {'type': 'string', 'format': 'date-time'}, 'provider_id': {'type': 'string'}, 'provider_name': {'type': 'string'}}, 'additionalProperties': False}, 'maxItems': 5}, 'outcome': {'enum': ['ok'], 'type': 'string'}, 'consequences': {'type': 'object', 'required': ['reschedule_fee_cents', 'cancel_fee_cents', 'requires_approval', 'approval_reason'], 'properties': {'approval_reason': {'type': ['string', 'null']}, 'cancel_fee_cents': {'type': 'integer', 'minimum': 0}, 'requires_approval': {'type': 'boolean'}, 'reschedule_fee_cents': {'type': 'integer', 'minimum': 0}}, 'additionalProperties': False}}, 'additionalProperties': False}, {'type': 'object', 'required': ['outcome', 'reason', 'detail'], 'properties': {'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'outcome': {'enum': ['validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'retry_after_sec': {'type': 'integer'}}, 'additionalProperties': False}]}
list_services
List services
Returns one published Kirah business catalog with current services, providers, timezone, prices, durations, deposit requirements, intake-question definitions and derived booking capabilities.
읽기 전용
입력 스키마
{'type': 'object', 'required': ['tenant'], 'properties': {'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'anyOf': [{'type': 'object', 'required': ['outcome', 'tenant', 'services', 'providers'], 'properties': {'tenant': {'type': 'object', 'required': ['name', 'capabilities'], 'properties': {'name': {'type': 'string'}, 'timezone': {'type': 'string'}, 'capabilities': {'type': 'object', 'required': ['native_booking', 'booking_mode', 'bookable'], 'properties': {'intake': {'type': 'boolean'}, 'add_ons': {'type': 'boolean'}, 'bookable': {'type': 'boolean'}, 'deposits': {'type': 'boolean'}, 'booking_mode': {'enum': ['instant', 'approval_required', 'external_booking_mode'], 'type': 'string'}, 'agent_booking': {'type': 'boolean'}, 'multi_service': {'type': 'boolean'}, 'status_reason': {'enum': ['suspended', 'agent_booking_disabled', 'no_booking_section'], 'type': 'string'}, 'native_booking': {'type': 'boolean'}, 'provider_selection': {'type': 'boolean'}}, 'additionalProperties': False}}, 'additionalProperties': False}, 'outcome': {'enum': ['ok'], 'type': 'string'}, 'services': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'name', 'duration_minutes', 'price_label'], 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'add_ons': {'type': 'array', 'items': {'type': 'object', 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'price_cents': {'type': 'integer'}, 'duration_minutes': {'type': 'integer'}}, 'additionalProperties': False}}, 'intake_form': {'allOf': [{'type': 'object', 'required': ['id', 'name', 'required', 'fields'], 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'fields': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'label', 'type', 'required'], 'properties': {'id': {'type': 'string'}, 'type': {'enum': ['text', 'textarea', 'boolean', 'select', 'date'], 'type': 'string'}, 'label': {'type': 'string'}, 'consent': {'type': 'boolean'}, 'options': {'type': 'array', 'items': {'type': 'string'}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}]}, 'price_cents': {'type': ['integer', 'null']}, 'price_label': {'type': 'string'}, 'deposit_cents': {'type': 'integer'}, 'intake_required': {'type': 'boolean'}, 'duration_minutes': {'type': 'integer'}}, 'additionalProperties': False}}, 'providers': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'service_ids'], 'properties': {'id': {'type': 'string'}, 'bio': {'type': 'string'}, 'name': {'type': 'string'}, 'service_ids': {'type': 'array', 'items': {'type': 'string'}}}, 'additionalProperties': False}}}, 'additionalProperties': False}, {'type': 'object', 'required': ['outcome', 'reason', 'detail'], 'properties': {'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'outcome': {'enum': ['validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'retry_after_sec': {'type': 'integer'}}, 'additionalProperties': False}]}
reschedule_booking
Reschedule booking
Atomically moves one grant-authorized booking to a real open time after explicit user confirmation. Reusing idempotency_key makes retries safe. Failure leaves the original booking unchanged; success returns a rotated grant.
파괴적 작업 외부 접근 가능
입력 스키마
{'type': 'object', 'required': ['tenant', 'booking_ref', 'grant', 'start_iso', 'idempotency_key'], 'properties': {'grant': {'type': 'string', 'maxLength': 2048, 'minLength': 1, 'description': "The capability token from this booking's create_booking response (confirmation.grant). Required: without it there is no way to establish that you are the agent that made this booking. It must carry the `booking:reschedule` scope. Tampered, wrong-booking, wrong-tenant, revoked and insufficiently-scoped grants all answer invalid_grant, indistinguishably; an authentic grant past the appointment start answers expired_grant. These are the GRANT codes, not the payment page's invalid_token/expired_token — through contract 2.11 this action answered the latter, whose prose wrongly told callers their payment link was broken."}, 'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}, 'start_iso': {'type': 'string', 'format': 'date-time', 'description': 'The exact ISO 8601 instant to move the appointment to. Natural-language times are not accepted. You may supply it DIRECTLY — calling get_reschedule_options first is optional, not required — because the instant is validated against real availability by the same slot engine create_booking uses. A time that is not genuinely open answers slot_taken with the original booking untouched; a time further ahead than this business books answers beyond_booking_horizon.'}, 'booking_ref': {'type': 'string', 'maxLength': 200, 'minLength': 16, 'description': 'The reference create_booking returned for the booking you want to MOVE. It must be the SAME booking the supplied grant was issued for; a mismatch answers invalid_grant, indistinguishably from a forged grant. A successful move does not change it.'}, 'provider_id': {'type': 'string', 'maxLength': 200, 'description': "OPTIONAL. Move the booking to a different provider. Omit it and the booking keeps the provider it is already on — the customer booked a person, not a room. An explicit provider is honored only when that provider exists in this tenant's catalog (else unknown_provider) AND performs the booked service (else provider_service_mismatch). The service and its duration never change."}, 'agent_metadata': {'type': 'object', 'properties': {'source': {'type': 'string', 'maxLength': 120}, 'request_id': {'type': 'string', 'maxLength': 120}}, 'additionalProperties': False}, 'idempotency_key': {'type': 'string', 'maxLength': 200, 'minLength': 16, 'description': 'REQUIRED — unlike create_booking, this action cannot derive one, because "move booking X to time T" is byte-identical whether it is a retry or a deliberate second move. Omitting it answers missing_idempotency_key. Reuse the SAME key when retrying the SAME move: the replay returns the original result with idempotent_replay:true and moves nothing. The same key with a different start_iso or provider_id answers idempotency_conflict.'}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'required': ['outcome'], 'properties': {'demo': {'type': 'boolean'}, 'grant': {'type': 'string', 'maxLength': 2048}, 'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'end_iso': {'type': 'string', 'format': 'date-time'}, 'missing': {'type': 'array', 'items': {'type': 'string'}}, 'outcome': {'enum': ['ok', 'booked', 'submitted', 'payment_required', 'intake_required', 'slot_taken', 'not_bookable', 'rescheduled', 'cancelled', 'validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'notified': {'type': 'string'}, 'start_iso': {'type': 'string', 'format': 'date-time'}, 'policy_fee': {'type': 'string'}, 'request_id': {'type': 'string'}, 'booking_ref': {'type': 'string'}, 'booking_url': {'type': 'string'}, 'demo_notice': {'type': 'string'}, 'intake_form': {'allOf': [{'type': 'object', 'required': ['id', 'name', 'required', 'fields'], 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'fields': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'label', 'type', 'required'], 'properties': {'id': {'type': 'string'}, 'type': {'enum': ['text', 'textarea', 'boolean', 'select', 'date'], 'type': 'string'}, 'label': {'type': 'string'}, 'consent': {'type': 'boolean'}, 'options': {'type': 'array', 'items': {'type': 'string'}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}}, 'required': {'type': 'boolean'}}, 'additionalProperties': False}]}, 'payment_url': {'type': 'string'}, 'confirmation': {'type': 'object', 'properties': {'grant': {'type': 'string', 'maxLength': 2048}, 'ics_url': {'type': 'string'}, 'manage_url_sent_to': {'type': 'string'}}, 'additionalProperties': False}, 'service_name': {'type': 'string'}, 'deposit_cents': {'type': 'integer'}, 'provider_name': {'type': 'string'}, 'status_reason': {'type': 'string'}, 'appointment_id': {'type': 'string'}, 'policy_message': {'type': 'string'}, 'retry_after_sec': {'type': 'integer'}, 'cancel_fee_cents': {'type': 'integer'}, 'already_cancelled': {'type': 'boolean'}, 'idempotent_replay': {'type': 'boolean'}}, 'additionalProperties': False, 'x-channel-adapter-failures': {'financial_action_unsupported': 'A channel adapter that forbids financial effects refused a cancellation that could refund a charged deposit or restore a paid plan credit. The booking is unchanged; the customer uses the secure manage link or contacts the business.'}}
search_businesses
Search Kirah businesses
Searches Kirah's eligible public business directory by service need or exact Kirah Agent Address, with optional coarse location, price and demo-mode filters. Demo results are explicitly labeled.
읽기 전용 외부 접근 가능
입력 스키마
{'type': 'object', 'anyOf': [{'required': ['query']}, {'required': ['agent_address']}], 'properties': {'limit': {'type': 'integer', 'default': 5, 'maximum': 10, 'minimum': 1}, 'query': {'type': 'string', 'maxLength': 200, 'minLength': 1, 'description': "The consumer's need in plain words (e.g. 'lower back tightness', 'prenatal massage'). Matched deterministically against real service catalogs, expanded by the discovery ontology."}, 'cursor': {'type': 'string', 'maxLength': 300, 'description': 'Opaque next_cursor value from a previous search_businesses response with the SAME query/filters: resumes after that tenant in the stable ordering. A cursor that does not decode, or that names a tenant not present in the current ordering, is invalid_cursor.'}, 'location': {'type': 'string', 'maxLength': 120, 'description': "Optional place text ('City, ST', ZIP, bare city/state) OR explicit coordinates 'lat,lng'. With resolvable coordinates, radius_miles applies as real distance; otherwise matching is city/ZIP/metro-market text equality."}, 'tenant_mode': {'enum': ['real_only', 'include_demos', 'demos_only'], 'type': 'string', 'default': 'include_demos', 'description': 'The authoritative eligibility filter for demo tenants. include_demos (default) returns real and demo businesses together; every demo is flagged demo:true and carries a clear notice. real_only excludes every demo business; demos_only returns ONLY demo businesses. A discovery-disabled, inactive, or otherwise ineligible tenant never appears in any mode — not even when its catalog uniquely matches the query.'}, 'include_demo': {'type': 'boolean', 'description': "Back-compat alias: true is equivalent to tenant_mode 'include_demos' and false to 'real_only'. When omitted, tenant_mode defaults to include_demos. Every demo result carries demo:true and a demo_notice. Supplying both include_demo and tenant_mode with disagreeing demo-inclusion is invalid_tenant_mode."}, 'radius_miles': {'type': 'integer', 'maximum': 500, 'minimum': 1, 'description': 'Radius in miles (default 25) — OPERATIVE when the location resolves to coordinates (2.6); otherwise answered with a truthful distance_note instead of a fabricated distance.'}, 'agent_address': {'type': 'string', 'pattern': '^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?@[Kk][Ii][Rr][Aa][Hh]\\.[Aa][Ii]$', 'maxLength': 253, 'description': 'Exact public locator `<handle>@kirah.ai`; case-insensitive ASCII only. A locator, never authentication or owner authority. Reserved, malformed, unlisted, disabled, non-bookable, and tenant_mode-ineligible addresses do not resolve. Exact mode never fuzzy-matches a near miss.'}, 'max_price_cents': {'type': 'integer', 'maximum': 10000000, 'minimum': 0, 'description': 'Only services with a parseable price at or under this amount are returned; unpriced services are excluded when this is set.'}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'anyOf': [{'type': 'object', 'required': ['outcome', 'location_precision', 'businesses', 'is_exhaustive'], 'properties': {'outcome': {'enum': ['ok'], 'type': 'string'}, 'businesses': {'type': 'array', 'items': {'type': 'object', 'required': ['tenant_slug', 'tenant_name', 'demo', 'listed', 'matched_services', 'score', 'service_match', 'agent_bookable'], 'properties': {'url': {'type': 'string'}, 'demo': {'type': 'boolean'}, 'score': {'type': 'integer'}, 'listed': {'enum': [True], 'type': 'boolean'}, 'location': {'type': 'object', 'properties': {'city': {'type': ['string', 'null']}, 'match': {'enum': ['postal', 'city', 'market', 'region', 'coordinates'], 'type': 'string'}, 'region': {'type': ['string', 'null']}, 'postal_code': {'type': ['string', 'null']}, 'distance_miles': {'type': 'number'}}, 'additionalProperties': False}, 'resolution': {'enum': ['exact_agent_address'], 'type': 'string'}, 'demo_notice': {'type': 'string'}, 'tenant_name': {'type': 'string'}, 'tenant_slug': {'type': 'string'}, 'agent_address': {'type': 'string'}, 'service_match': {'type': 'boolean'}, 'agent_bookable': {'type': 'boolean'}, 'authority_mode': {'enum': ['instant', 'approval_required', None], 'type': ['string', 'null']}, 'matched_services': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'name', 'match_type'], 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'match_type': {'enum': ['exact', 'ontology', 'description'], 'type': 'string'}, 'price_cents': {'type': ['integer', 'null']}, 'price_label': {'type': 'string'}, 'match_evidence': {'type': 'array', 'items': {'type': 'string'}, 'maxItems': 4}, 'duration_minutes': {'type': 'integer'}}, 'additionalProperties': False}, 'maxItems': 3, 'minItems': 0}, 'agent_discoverable': {'type': 'boolean'}}, 'additionalProperties': False}, 'maxItems': 10}, 'resolution': {'type': 'object', 'required': ['type', 'agent_address', 'exact', 'found'], 'properties': {'type': {'enum': ['agent_address'], 'type': 'string'}, 'exact': {'enum': [True], 'type': 'boolean'}, 'found': {'type': 'boolean'}, 'agent_address': {'type': 'string'}}, 'additionalProperties': False}, 'demo_notice': {'type': 'string'}, 'next_cursor': {'type': 'string'}, 'distance_note': {'type': 'string'}, 'is_exhaustive': {'type': 'boolean'}, 'location_precision': {'enum': ['coordinates', 'city_zip_market', 'none'], 'type': 'string'}, 'demo_exclusion_note': {'type': 'string'}, 'demo_businesses_excluded': {'type': 'integer', 'minimum': 1}, 'demo_businesses_excluded_is_exhaustive': {'type': 'boolean'}}, 'additionalProperties': False}, {'type': 'object', 'required': ['outcome', 'reason', 'detail'], 'properties': {'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'outcome': {'enum': ['validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'retry_after_sec': {'type': 'integer'}}, 'additionalProperties': False}]}
search_services
Search services
Ranks up to five services in one published Kirah business catalog against a short free-text query. Returns catalog-grounded candidate identifiers, names, scores and match evidence.
읽기 전용
입력 스키마
{'type': 'object', 'required': ['tenant', 'query'], 'properties': {'query': {'type': 'string', 'maxLength': 200, 'minLength': 1}, 'tenant': {'type': 'string', 'maxLength': 253, 'description': "The tenant to operate on: the provider.tenant_slug value from the tenant's /.well-known/kirah.json manifest (the tenant's full domain, e.g. <slug>.kirah.ai)."}}, 'additionalProperties': False}
출력 스키마
{'type': 'object', 'anyOf': [{'type': 'object', 'required': ['outcome', 'candidates'], 'properties': {'outcome': {'enum': ['ok'], 'type': 'string'}, 'candidates': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'name', 'score'], 'properties': {'id': {'type': 'string'}, 'name': {'type': 'string'}, 'score': {'type': 'number'}}, 'additionalProperties': False}, 'maxItems': 5}}, 'additionalProperties': False}, {'type': 'object', 'required': ['outcome', 'reason', 'detail'], 'properties': {'detail': {'type': 'string'}, 'reason': {'type': 'string'}, 'outcome': {'enum': ['validation_error', 'rate_limited', 'internal_error'], 'type': 'string'}, 'retry_after_sec': {'type': 'integer'}}, 'additionalProperties': False}]}
추가됨
create_booking
2026년 9월 17일 12:42 PM
추가됨
cancel_booking
2026년 9월 17일 12:42 PM
추가됨
reschedule_booking
2026년 9월 17일 12:42 PM
추가됨
get_reschedule_options
2026년 9월 17일 12:42 PM
추가됨
get_booking_status
2026년 9월 17일 12:42 PM
추가됨
find_available_services
2026년 9월 17일 12:42 PM
추가됨
search_businesses
2026년 9월 17일 12:42 PM
추가됨
get_availability
2026년 9월 17일 12:42 PM
추가됨
search_services
2026년 9월 17일 12:42 PM
추가됨
list_services
2026년 9월 17일 12:42 PM