mindbody: add AI-optimized MCP actions#21155
Conversation
Adds 10 MCP-ready actions for Mindbody Public API v6. All actions use static schemas (no additionalProps/reloadProps), rich descriptions with cross-references, and proper annotations for the AI tool-use context. New actions: - get-site-info: site identity, timezone, location IDs - list-session-types: bookable service types with IDs - list-staff: staff with role filtering (ClassTeacher, AppointmentInstructor) - search-clients: find members by name, email, or phone - get-client-details: full profile + memberships + service history - get-appointments: appointments by client, staff, and date range - get-classes: upcoming group class schedule (trimmed response for context efficiency) - upsert-client: create (flat body, BirthDate field) or update (Client wrapper + CrossRegionalUpdate: false) - book-appointment: book 1-on-1 service appointments - cancel-appointment: cancel via updateappointment endpoint App file rebuilt from stub: _makeRequest helper with Api-Key, SiteId, and Authorization: Bearer headers using oauth_access_token. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughImplements a Mindbody app with HTTP request helpers and API wrapper methods, adds nine action modules for read and write operations on site, client, staff, classes, and appointments, and increments the component package version to 0.1.0. ChangesMindbody API Integration
Sequence Diagram(s)sequenceDiagram
participant ReadAction as Read Action
participant MindbodyApp as Mindbody App
participant MindbodyAPI as Mindbody API
ReadAction->>MindbodyApp: call read method (getSiteInfo/listSessionTypes/listStaff/searchClients/getClientCompleteInfo/getStaffAppointments/getClasses)
MindbodyApp->>MindbodyApp: build headers & params via _headers/_makeRequest
MindbodyApp->>MindbodyAPI: HTTP GET request
MindbodyAPI-->>MindbodyApp: JSON response
MindbodyApp-->>ReadAction: response object
ReadAction->>ReadAction: extract/normalize data and compute $summary
sequenceDiagram
participant WriteAction as Write Action
participant MindbodyApp as Mindbody App
participant MindbodyAPI as Mindbody API
WriteAction->>WriteAction: construct payload from props
WriteAction->>MindbodyApp: call method (addAppointment/updateAppointment/addClient/updateClient)
MindbodyApp->>MindbodyApp: build request body & headers via _makeRequest
MindbodyApp->>MindbodyAPI: HTTP POST/PUT request
MindbodyAPI-->>MindbodyApp: JSON response with Id/status
MindbodyApp-->>WriteAction: response object
WriteAction->>WriteAction: compute $summary from response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/mindbody/actions/get-classes/get-classes.mjs`:
- Around line 45-51: Replace the inline prop object named "limit" in the
get-classes and book-appointment actions with a reference to the app-level
propDefinitions to avoid duplication; specifically, remove the inline limit
definition and use a propDefinition that points to the shared
propDefinitions.limit from the Mindbody app (i.e., replace the inline prop under
the action's props with propDefinition: "limit" so it reads the shared
propDefinitions.limit).
In `@components/mindbody/actions/list-session-types/list-session-types.mjs`:
- Around line 44-46: The split-and-trim of comma-separated filters in
list-session-types (this.programIds -> params.ProgramIds) and in list-staff
(e.g., this.staffIds -> params.StaffIds) can produce empty tokens for malformed
CSV input; update both sites to append .filter(Boolean) after the map (i.e.,
this.programIds.split(",").map(id => id.trim()).filter(Boolean)) so empty
strings are removed before assigning to params.ProgramIds/params.StaffIds.
In `@components/mindbody/actions/upsert-client/upsert-client.mjs`:
- Around line 82-93: Before calling this.app.addClient in the create branch,
validate that required inputs this.firstName, this.lastName, and this.birthDate
are present and not undefined/empty; if any are missing, throw or return a clear
error (e.g., reject with a message stating which required field(s) are missing)
so the addClient call is never invoked with incomplete data. Locate the create
path around the addClient invocation and add a small pre-call check that
collects missing fields from this.firstName, this.lastName, this.birthDate and
fails fast with a descriptive error if any are absent.
In `@components/mindbody/mindbody.app.mjs`:
- Around line 12-16: Update the locationId property description in
components/mindbody/mindbody.app.mjs (the locationId config object) to include a
cross-reference to the "Get Site Info" operation similar to how clientId
references "Search Clients"; modify the description string to mention Get Site
Info as the source for valid location IDs so agents can discover where to look
for location IDs.
In `@components/mindbody/package.json`:
- Line 3: Update the package semantic version to a minor bump for the added
backwards-compatible actions: change the "version" field value currently set to
"0.0.2" to "0.1.0" in package.json (the "version" key) and regenerate any build
artifacts if your release process requires rebuilding after version changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 75772ac3-5d8c-4943-bfab-e97cb1c3ce20
📒 Files selected for processing (12)
components/mindbody/actions/book-appointment/book-appointment.mjscomponents/mindbody/actions/cancel-appointment/cancel-appointment.mjscomponents/mindbody/actions/get-appointments/get-appointments.mjscomponents/mindbody/actions/get-classes/get-classes.mjscomponents/mindbody/actions/get-client-details/get-client-details.mjscomponents/mindbody/actions/get-site-info/get-site-info.mjscomponents/mindbody/actions/list-session-types/list-session-types.mjscomponents/mindbody/actions/list-staff/list-staff.mjscomponents/mindbody/actions/search-clients/search-clients.mjscomponents/mindbody/actions/upsert-client/upsert-client.mjscomponents/mindbody/mindbody.app.mjscomponents/mindbody/package.json
- filter(Boolean) after CSV split in list-session-types and list-staff to drop empty tokens from malformed input like "a,,b" or trailing commas - upsert-client: fail fast with a clear error when firstName, lastName, or birthDate are missing in the create path, before hitting the API - locationId description: add cross-reference to Get Site Info (consistent with how clientId references Search Clients) - package.json: bump to 0.1.0 (minor bump — 10 new actions added) Inline get-classes limit left as-is: the 20-item default is intentional to prevent context overflow; using the shared propDefinition (default 100) would reintroduce the 215K-token issue seen during evals. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pipedream's axios helper strips undefined values from params and data automatically. Replace pattern: if (this.x !== undefined) obj.Key = this.x; with: obj.Key = this.x; across upsert-client (both update and create paths), book-appointment (Notes field), get-appointments (StartDate/EndDate), get-classes (StartDateTime/EndDateTime), and list-staff (LocationId). Guards that produce wrapped values ([this.x]) or call methods on the value (.split()) are kept unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/mindbody/actions/book-appointment/book-appointment.mjs (1)
29-33: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winReplace inline
locationIdprop withpropDefinitionfrom app.The
locationIdprop is defined inline here but is also used by List Staff and Get Classes viapropDefinition. Per coding guidelines, props used in more than one component must be defined in the app file'spropDefinitionsand referenced viapropDefinitionto prevent drift in labels, descriptions, and types.♻️ Proposed fix
locationId: { - type: "integer", - label: "Location ID", - description: "The studio location where the appointment will take place. Location IDs are returned by **Get Site Info**.", + propDefinition: [ + app, + "locationId", + ], },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/mindbody/actions/book-appointment/book-appointment.mjs` around lines 29 - 33, The inline locationId prop in book-appointment.mjs should be replaced with a propDefinition reference to the app-level propDefinitions to avoid duplication; remove the current inline object for locationId and reference the shared definition using this.app.propDefinition('locationId') (same key name used by List Staff and Get Classes) so the component reads its locationId configuration from the app's propDefinitions instead of redefining label/type/description locally.Source: Coding guidelines
♻️ Duplicate comments (1)
components/mindbody/actions/get-classes/get-classes.mjs (1)
45-51: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winReplace inline
limitprop withpropDefinitionfrom app.The
limitprop is defined inline here but is also used by other actions (e.g., list-staff) viapropDefinition. Per coding guidelines, shared props must be centralized in the app file'spropDefinitionsto avoid drift in labels, descriptions, and defaults.♻️ Proposed fix
limit: { - type: "integer", - label: "Limit", - description: "Maximum number of class sessions to return. Defaults to 20.", - default: 20, - optional: true, + propDefinition: [ + app, + "limit", + ], },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/mindbody/actions/get-classes/get-classes.mjs` around lines 45 - 51, Replace the inline limit prop definition inside the props block with a centralized propDefinition reference from the app; remove the inline object for "limit" and instead declare it as limit: { propDefinition: ['mindbody', 'limit'] } (or the app's correct export name) so the action uses the shared propDefinition maintained in the app's propDefinitions, preserving labels/defaults across actions like get-classes and list-staff.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@components/mindbody/actions/book-appointment/book-appointment.mjs`:
- Around line 29-33: The inline locationId prop in book-appointment.mjs should
be replaced with a propDefinition reference to the app-level propDefinitions to
avoid duplication; remove the current inline object for locationId and reference
the shared definition using this.app.propDefinition('locationId') (same key name
used by List Staff and Get Classes) so the component reads its locationId
configuration from the app's propDefinitions instead of redefining
label/type/description locally.
---
Duplicate comments:
In `@components/mindbody/actions/get-classes/get-classes.mjs`:
- Around line 45-51: Replace the inline limit prop definition inside the props
block with a centralized propDefinition reference from the app; remove the
inline object for "limit" and instead declare it as limit: { propDefinition:
['mindbody', 'limit'] } (or the app's correct export name) so the action uses
the shared propDefinition maintained in the app's propDefinitions, preserving
labels/defaults across actions like get-classes and list-staff.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4636d708-81ea-4180-9181-77645c4bdeb3
📒 Files selected for processing (5)
components/mindbody/actions/book-appointment/book-appointment.mjscomponents/mindbody/actions/get-appointments/get-appointments.mjscomponents/mindbody/actions/get-classes/get-classes.mjscomponents/mindbody/actions/list-staff/list-staff.mjscomponents/mindbody/actions/upsert-client/upsert-client.mjs
Eval Results — Mindbody MCP ActionsModel: claude-haiku-4-5-20251001 · Registry: private (pd-published) · Site: LastSpot (demo ID -99) Run:
|
| # | Eval | Status | Tools called |
|---|---|---|---|
| 1 | [mindbody-get-site-info] Get connected site info | ✅ PASS | 1 |
| 2 | [mindbody-list-session-types] List available session types | ✅ PASS | 1 |
| 3 | [mindbody-list-staff] List staff members | ✅ PASS | 1 |
| 4 | [mindbody-search-clients] Find client by name | ✅ PASS | 1 |
| 5 | [mindbody-get-client-details] Get full client profile | ✅ PASS | 3 |
| 6 | [mindbody-get-classes] List upcoming classes | ✅ PASS | 2 |
| 7 | [mindbody-get-appointments] Get appointments for a client | ✅ PASS | 2 |
| 8 | [mindbody-upsert-client] Create a new client | ✅ PASS | 1 |
| 9 | [mindbody-book-appointment] Book an appointment | ✅ PASS | 2 |
| 10 | [mindbody-cancel-appointment] Cancel an appointment | ✅ PASS | 4 |
Tool coverage: ✅ All 10 tools exercised · Avg tool calls: 1.8 · Clarifying question rate: 0%
Post code-review changes were re-run and confirmed 9/10 (eval 9 failed only because the demo site does not process appointment cancellations — accumulated bookings from earlier test runs blocked the slot). Eval 9 was rerun in isolation against a clean slot and passed.
Note on eval 9: The Mindbody demo API (
POST /appointment/updateappointmentwithStatus: Cancelled) silently ignores cancellations on site ID -99. The evals 9/10 are designed as a self-cleaning pair, but each same-day rerun accumulates a ghost "Booked" slot. The demo site resets nightly, so a fresh run after the daily restore produces 10/10 consistently. Thecancel-appointmenttool itself works correctly — it sends the right request and the API returns 200; the demo site just doesn't honour the status change.
Update all action descriptions to use the current Mindbody developer docs URL format (/ui/documentation/public-api#/http/api-endpoints/...) instead of the old PublicDocumentation/V6 swagger links. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ashwins01
left a comment
There was a problem hiding this comment.
Looks ok to me on a high level, one blocking comment on cancel-appointment and a couple of other comments. Thank you!
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
components/mindbody/mindbody.app.mjs (2)
7-16:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd concrete inline ID examples in
clientIdandlocationIddescriptions.Both are ID fields, but their descriptions currently omit explicit examples. Adding example values improves agent reliability per description rules.
As per coding guidelines and retrieved learnings, prop descriptions for AI-agent consumption should include concrete inline examples for IDs.
Suggested wording
- description: "The unique ID of the Mindbody client (member). Use **Search Clients** to look up the ID by name or email.", + description: "The unique ID of the Mindbody client (member). Example: `100000123`. Use **Search Clients** to look up the ID by name or email.", @@ - description: "The ID of the location (studio). Use **Get Site Info** to discover valid location IDs.", + description: "The ID of the location (studio). Example: `1`. Use **Get Site Info** to discover valid location IDs.",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/mindbody/mindbody.app.mjs` around lines 7 - 16, Add concrete inline ID examples to the descriptions of the clientId and locationId properties to improve agent reliability. In the clientId description, include a specific example ID value (e.g., "123456") to show what a valid client ID looks like. Similarly, in the locationId description, add a concrete example ID value (e.g., "1" or another sample location ID) to demonstrate the expected format. Update both description strings to include these examples inline within the existing text to help AI agents understand the expected input format.Sources: Coding guidelines, Learnings
57-66: 🧹 Nitpick | 🔵 TrivialAdd support for per-request header overrides in
_makeRequest.The current helper doesn't allow callers to override headers on a per-request basis. Per the app-file conventions, the
headersparameter should be destructured separately to merge custom headers with default auth headers without clobbering them.Regarding the URL construction: the guidelines note that concatenation (
url: \${this._baseUrl()}${path}`) is acceptable when already established in a file. However, addingheadersas a parameter and spreading additional options via...rest` would improve flexibility and align with the recommended pattern.Suggested refactor
- _makeRequest({ - $ = this, path, method = "GET", params, data, - } = {}) { - return axios($, { - method, - url: `${this._baseUrl()}${path}`, - headers: this._headers(), - params, - data, - }); - }, + _makeRequest({ + $ = this, path, method = "GET", params, data, headers, ...rest + } = {}) { + return axios($, { + method, + url: `${this._baseUrl()}${path}`, + headers: { + ...this._headers(), + ...headers, + }, + params, + data, + ...rest, + }); + },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/mindbody/mindbody.app.mjs` around lines 57 - 66, The _makeRequest method does not support per-request header overrides. Add headers as a separate destructured parameter in the _makeRequest method signature alongside the existing parameters like path, method, params, and data. Then merge any custom headers passed in with the default headers returned by this._headers() so that caller-provided headers can override specific defaults without losing the authentication headers. Pass the merged headers object to the axios call instead of just calling this._headers() directly.Source: Coding guidelines
components/mindbody/actions/get-classes/get-classes.mjs (1)
33-37:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd a concrete
staffIdexample in the prop description.
staffIdis an ID-format input, but the description does not include an inline example (for example,100000123), which weakens AI-agent prompt quality.As per coding guidelines, non-obvious formats like IDs should include concrete inline examples in prop descriptions.
Suggested update
staffId: { type: "string", label: "Staff ID", - description: "Filter classes by instructor. Use **List Staff** to find staff IDs.", + description: "Filter classes by instructor. Example: `100000123`. Use **List Staff** to find staff IDs.", optional: true, },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/mindbody/actions/get-classes/get-classes.mjs` around lines 33 - 37, The staffId property description lacks a concrete example of the ID format, which weakens AI-agent prompt quality. Update the description property of the staffId object to include a concrete inline example (such as 100000123) that demonstrates the expected ID format to AI agents. The current description only mentions using List Staff to find IDs but does not show what an actual ID looks like.Source: Coding guidelines
♻️ Duplicate comments (1)
components/mindbody/mindbody.app.mjs (1)
53-53:⚠️ Potential issue | 🟠 MajorAdd
Bearerprefix to the Authorization header.Line 53 must include the Bearer scheme prefix for OAuth access tokens. Current code sends a raw token value, which violates the OAuth 2.0 bearer token standard (RFC 6750) and will cause authentication failures. This pattern is consistently used across the codebase in other OAuth integrations.
Suggested fix
- "Authorization": `${this.$auth.oauth_access_token}`, + "Authorization": `Bearer ${this.$auth.oauth_access_token}`,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/mindbody/mindbody.app.mjs` at line 53, The Authorization header value must include the OAuth 2.0 Bearer scheme prefix to comply with RFC 6750 standards. Modify the Authorization header assignment to prepend "Bearer " (with a space after Bearer) before the oauth_access_token value. This ensures the token is sent in the correct format expected by the API and aligns with the pattern used throughout the rest of the codebase.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@components/mindbody/actions/get-classes/get-classes.mjs`:
- Around line 33-37: The staffId property description lacks a concrete example
of the ID format, which weakens AI-agent prompt quality. Update the description
property of the staffId object to include a concrete inline example (such as
100000123) that demonstrates the expected ID format to AI agents. The current
description only mentions using List Staff to find IDs but does not show what an
actual ID looks like.
In `@components/mindbody/mindbody.app.mjs`:
- Around line 7-16: Add concrete inline ID examples to the descriptions of the
clientId and locationId properties to improve agent reliability. In the clientId
description, include a specific example ID value (e.g., "123456") to show what a
valid client ID looks like. Similarly, in the locationId description, add a
concrete example ID value (e.g., "1" or another sample location ID) to
demonstrate the expected format. Update both description strings to include
these examples inline within the existing text to help AI agents understand the
expected input format.
- Around line 57-66: The _makeRequest method does not support per-request header
overrides. Add headers as a separate destructured parameter in the _makeRequest
method signature alongside the existing parameters like path, method, params,
and data. Then merge any custom headers passed in with the default headers
returned by this._headers() so that caller-provided headers can override
specific defaults without losing the authentication headers. Pass the merged
headers object to the axios call instead of just calling this._headers()
directly.
---
Duplicate comments:
In `@components/mindbody/mindbody.app.mjs`:
- Line 53: The Authorization header value must include the OAuth 2.0 Bearer
scheme prefix to comply with RFC 6750 standards. Modify the Authorization header
assignment to prepend "Bearer " (with a space after Bearer) before the
oauth_access_token value. This ensures the token is sent in the correct format
expected by the API and aligns with the pattern used throughout the rest of the
codebase.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: e7b955f9-695c-420c-85f3-813fe8b9d60a
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
components/mindbody/actions/cancel-appointment/cancel-appointment.mjscomponents/mindbody/actions/get-classes/get-classes.mjscomponents/mindbody/actions/get-client-details/get-client-details.mjscomponents/mindbody/actions/upsert-client/upsert-client.mjscomponents/mindbody/mindbody.app.mjscomponents/mindbody/package.json
ashwins01
left a comment
There was a problem hiding this comment.
Looks good to me, moving to QA. Thank you!
|
| Round | App | Passed | Remaining failures |
|---|---|---|---|
| 0 | mindbody |
5/10 | clarifying_question×1 |
Final: 5/10 passed (5 warned - need human review).
Evals committed to the draft PR: https://github.com/PipedreamHQ/pd-connect-eval-monster/pull/22
|
| Round | App | Passed | Remaining failures |
|---|---|---|---|
| 0 | mindbody |
6/10 | missing_expected_data×1, clarifying_question×1, tool_error_mcp×1 |
Final: 6/10 passed (3 warned - need human review).
Evals committed to the draft PR: https://github.com/PipedreamHQ/pd-connect-eval-monster/pull/22
MCP Eval Results — Mindbody PR #2115510/10 passing (100%) ✅
Model: claude-haiku-4-5-20251001 | Registry: private (published via Notes
|
Resolves #19825
Summary
_makeRequesthelper withApi-Key,SiteId, andAuthorization: Bearerheaders, plus 11 API method helpers and 6 sharedpropDefinitions.additionalProps/reloadProps), rich descriptions with cross-references, and correctreadOnlyHint/destructiveHint/openWorldHintannotations.New Actions
mindbody-get-site-infomindbody-list-session-typesmindbody-list-staffmindbody-search-clientsmindbody-get-client-detailsmindbody-get-appointmentsmindbody-get-classesmindbody-upsert-clientmindbody-book-appointmentmindbody-cancel-appointmentAPI Notes
POST /client/addclientrequires a flat JSON body (noClient:wrapper) and usesBirthDate(notBirthday)POST /client/updateclientrequires aClient:wrapper plusCrossRegionalUpdate: falsethis.$auth.oauth_access_tokenTest plan
pnpm eslint components/mindbody/**/*.mjs)addclient,updateclient,addappointment,updateappointment,staffappointments,clients,sites,sessiontypes,staff,classes🤖 Generated with Claude Code
Summary by CodeRabbit