From 2101076b3b035a2c2af7d9a485e183bbbf14ce37 Mon Sep 17 00:00:00 2001 From: Rohan Shah Date: Fri, 6 Nov 2026 13:09:48 -0800 Subject: [PATCH 1/4] feat(third_party): add FitMetrics plugin Co-authored-by: Cursor --- .cursor-plugin/marketplace.json | 5 ++ README.md | 1 + .../fitmetrics/.cursor-plugin/plugin.json | 49 ++++++++++++++ third_party/fitmetrics/CHANGELOG.md | 9 +++ third_party/fitmetrics/LICENSE | 21 ++++++ third_party/fitmetrics/README.md | 65 +++++++++++++++++++ third_party/fitmetrics/assets/logo.svg | 1 + third_party/fitmetrics/mcp.json | 11 ++++ 8 files changed, 162 insertions(+) create mode 100644 third_party/fitmetrics/.cursor-plugin/plugin.json create mode 100644 third_party/fitmetrics/CHANGELOG.md create mode 100644 third_party/fitmetrics/LICENSE create mode 100644 third_party/fitmetrics/README.md create mode 100644 third_party/fitmetrics/assets/logo.svg create mode 100644 third_party/fitmetrics/mcp.json diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index b69b642c16..153216eb7e 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -692,6 +692,11 @@ "name": "tesla", "source": "third_party/tesla", "description": "Read your Tesla cars and energy sites, and send commands you confirm." + }, + { + "name": "fitmetrics", + "source": "third_party/fitmetrics", + "description": "Review Trainerize clients and check-ins, and update macros, messages, and programs." } ] } diff --git a/README.md b/README.md index 2a769e3ec5..7894f0c6e3 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ Official Cursor plugins for popular developer tools, frameworks, and SaaS produc | `onepay` | [OnePay](third_party/onepay/) | Cursor | Integrations | Access your OnePay accounts: balances, transactions, credit, Pay Later, investments, crypto, and rewards. | | `questrade` | [Questrade](third_party/questrade/) | Cursor | Integrations | Read your Questrade portfolio, get live market data, and discuss potential trades. | | `tesla` | [Tesla](third_party/tesla/) | Cursor | Integrations | Read your Tesla cars and energy sites, and send commands you confirm. | +| `fitmetrics` | [FitMetrics](third_party/fitmetrics/) | Cursor | Integrations | Review Trainerize clients and check-ins, and update macros, messages, and programs. | Author values match each plugin’s `plugin.json` `author.name` (Cursor lists `plugins@cursor.com` in the manifest). ## Repository structure diff --git a/third_party/fitmetrics/.cursor-plugin/plugin.json b/third_party/fitmetrics/.cursor-plugin/plugin.json new file mode 100644 index 0000000000..fa49be676f --- /dev/null +++ b/third_party/fitmetrics/.cursor-plugin/plugin.json @@ -0,0 +1,49 @@ +{ + "name": "fitmetrics", + "displayName": "FitMetrics", + "version": "1.0.0", + "minClientVersions": { + "cursor": "3.13.0" + }, + "description": "Review Trainerize clients and check-ins, and update macros, messages, and programs.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "homepage": "https://app.fitmetrics.ai/docs/faq/connect-ai-tools-to-fitmetrics-via-mcp-claude-chatgpt-openclaw-more", + "repository": "https://github.com/cursor/plugins", + "license": "MIT", + "logo": "assets/logo.svg", + "keywords": [ + "fitmetrics", + "trainerize", + "abc fitness", + "coaching", + "fitness", + "nutrition", + "workouts", + "check-ins", + "mcp" + ], + "category": "integrations", + "tags": [ + "fitmetrics", + "fitness", + "coaching", + "mcp" + ], + "variables": { + "type": "object", + "properties": { + "FITMETRICS_API_KEY": { + "type": "string", + "title": "FitMetrics API key", + "description": "Key from FitMetrics Settings → Integrations → MCP → Create API Key → Other MCP app. Requires the Scale package." + } + }, + "required": [ + "FITMETRICS_API_KEY" + ] + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/fitmetrics/CHANGELOG.md b/third_party/fitmetrics/CHANGELOG.md new file mode 100644 index 0000000000..874b2ae180 --- /dev/null +++ b/third_party/fitmetrics/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this plugin will be documented here. + +## 1.0.0 — initial release + +- Added the `fitmetrics` MCP server pointing at FitMetrics' hosted Streamable HTTP endpoint (`https://app.fitmetrics.ai/mcp`). +- Auth uses a FitMetrics API key supplied by the user as an `Authorization: Bearer` header (`${FITMETRICS_API_KEY}`). +- Logo: FitMetrics' official mark, the `favicon.svg` from fitmetrics.ai. diff --git a/third_party/fitmetrics/LICENSE b/third_party/fitmetrics/LICENSE new file mode 100644 index 0000000000..ca2bba771c --- /dev/null +++ b/third_party/fitmetrics/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Cursor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/fitmetrics/README.md b/third_party/fitmetrics/README.md new file mode 100644 index 0000000000..59f0a95508 --- /dev/null +++ b/third_party/fitmetrics/README.md @@ -0,0 +1,65 @@ +# FitMetrics + +Cursor plugin that connects agents to [FitMetrics](https://www.fitmetrics.ai) through FitMetrics' official remote [Model Context Protocol](https://modelcontextprotocol.io/) server. + +Review your ABC Trainerize clients, check-ins, measurements, meals, and workouts, and update macros, messages, habits, and workout programs. FitMetrics is ABC Fitness's coaching add-on for Trainerize. + +## Install + +1. Open **Cursor Settings → Plugins**. +2. Search for **FitMetrics**. +3. Click **Install**, then set your FitMetrics API key (below). + +Or run `/add-plugin fitmetrics` in chat. + +## MCP + +```json +{ + "mcpServers": { + "fitmetrics": { + "type": "http", + "url": "https://app.fitmetrics.ai/mcp", + "headers": { + "Authorization": "Bearer ${FITMETRICS_API_KEY}" + } + } + } +} +``` + +Auth is a FitMetrics **API key** sent as `Authorization: Bearer`. In FitMetrics, open **Settings → Integrations → MCP**, click **Create API Key**, choose **Other MCP app**, then name the key and pick its coach and tool permissions. Set the key in **Dashboard → Plugins → Configure**. Do not commit the key. + +## Before you connect + +You need ABC Trainerize with the FitMetrics **Scale** package, the only package with MCP access. Coaches can create their own key, and it reaches only their assigned clients. Write actions stay off until an owner or admin turns them on. + +## What agents can do + +| Category | Capabilities | +| --- | --- | +| Clients | Morning briefing, client dossiers, measurements, habits, and tags | +| Check-ins | Review check-ins, and create and manage AI check-in templates and AI Forms | +| Nutrition | Meals and macros, including scheduled macro changes in Trainerize | +| Training | Workouts, cardio, 1:1 bookings, and building, copying, scheduling, and assigning programs | +| Messaging | Messages, scheduled and bulk messages, email, comments, and reactions | +| Automations | Create and manage FitMetrics automations | + +FitMetrics lists 359 tools (137 read, 222 write). The hosted runtime is the source of truth for tool names and schemas. + +## Notes + +- FitMetrics offers OAuth sign-in only to Claude and ChatGPT, and its dynamic client registration rejects Cursor's OAuth callback. This plugin uses the API key path FitMetrics documents for Cursor and other MCP apps. +- Changes made through the server are confirmed, scoped, and logged in FitMetrics. + +## Docs + +- Connect AI tools to FitMetrics via MCP: https://app.fitmetrics.ai/docs/faq/connect-ai-tools-to-fitmetrics-via-mcp-claude-chatgpt-openclaw-more +- ABC Trainerize x FitMetrics MCP: https://resources.trainerize.com/mcp +- Server URL: https://app.fitmetrics.ai/mcp + +Logo is FitMetrics' official mark, the `favicon.svg` from fitmetrics.ai. + +## License + +MIT diff --git a/third_party/fitmetrics/assets/logo.svg b/third_party/fitmetrics/assets/logo.svg new file mode 100644 index 0000000000..53c7ed8023 --- /dev/null +++ b/third_party/fitmetrics/assets/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/third_party/fitmetrics/mcp.json b/third_party/fitmetrics/mcp.json new file mode 100644 index 0000000000..53179659c2 --- /dev/null +++ b/third_party/fitmetrics/mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "fitmetrics": { + "type": "http", + "url": "https://app.fitmetrics.ai/mcp", + "headers": { + "Authorization": "Bearer ${FITMETRICS_API_KEY}" + } + } + } +} From fb4bf31dc8575ab46c8a10182a407d17a0da812a Mon Sep 17 00:00:00 2001 From: Santiago Medina Rolong Date: Fri, 6 Nov 2026 21:28:05 +0000 Subject: [PATCH 2/4] X MCP guide: find a contact's thread with participant_query To find or message someone the owner already chats with, the guide now starts with get_chat_conversations participant_query (their @handle or name, type direct, expansions participant_ids) instead of resolving their handle to an id and matching participants across the inbox. Send or reply also says X DMs go out only through send_chat_message, never an email or Slack draft tool, and that when a host holds the call for approval, the send repeats it with exactly the same arguments rather than re-encrypting. --- third_party/x/skills/x-api-mcp-guide/SKILL.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/third_party/x/skills/x-api-mcp-guide/SKILL.md b/third_party/x/skills/x-api-mcp-guide/SKILL.md index 5930489e8e..752e87c10a 100644 --- a/third_party/x/skills/x-api-mcp-guide/SKILL.md +++ b/third_party/x/skills/x-api-mcp-guide/SKILL.md @@ -138,7 +138,7 @@ Once per session: | Tool | Use | | --- | --- | -| `get_chat_conversations` | The inbox, `max_results` 1000 | +| `get_chat_conversations` | The inbox, `max_results` 1000. `participant_query` finds one person's thread ([Find someone's thread](#find-someones-thread)) | | `get_chat_conversation` | One thread, including `participant_ids` / `member_ids` | | `get_chat_conversation_events` | `data[].encoded_event` plus `meta.conversation_key_events`, newest first, `max_results` 100 | | `get_users_public_keys` | Signing keys for senders, all versions, up to 100 `ids` per call | @@ -163,6 +163,10 @@ For any set of events: Each message's `status` is `verified`, `unencrypted`, or `unverified` (an unverified message still has its text). Focus on the content. Don't comment on encryption unprompted or call plain messages odd. Answer plainly from `status` when asked about security, and mention `unverified` briefly before acting on that message. +### Find someone's thread + +To find the thread with a named person, for example to message them, call `get_chat_conversations` with `participant_query` set to their @handle or name, `type` `direct` for a 1:1, `max_results` 1000, and `expansions` `participant_ids` to check it's them. Don't scan the whole inbox or look the person up first. A page can be empty while `has_more` is true: keep paging with the same query. If nothing matches, try their @handle if you used a name, then message requests by `sender_id`. + ### Unread For "what's unread" or "anything new", don't scan every conversation. @@ -175,7 +179,7 @@ For "what's unread" or "anything new", don't scan every conversation. ### Read or summarize -Read every inbox conversation, not only the ones that look relevant: list with `get_chat_conversations` (`max_results` 1000, all pages), then decrypt each. Skip message requests unless asked; if `meta.has_message_requests` is true, end with one line offering them. To find a named person's thread, resolve their handle to an id and match participants, then fall back to message requests by `sender_id`. Never claim you checked everything unless you read every thread (and both request inboxes, if asked) to the end; otherwise say how many you read. Optionally `mark_chat_conversation_read` afterwards. +Read every inbox conversation, not only the ones that look relevant: list with `get_chat_conversations` (`max_results` 1000, all pages), then decrypt each. Skip message requests unless asked; if `meta.has_message_requests` is true, end with one line offering them. To find a named person's thread, see [Find someone's thread](#find-someones-thread). Never claim you checked everything unless you read every thread (and both request inboxes, if asked) to the end; otherwise say how many you read. Optionally `mark_chat_conversation_read` afterwards. ### Message requests @@ -187,7 +191,9 @@ Pending 1:1 requests aren't in `get_chat_conversations`. List, open, and accept ### Send or reply -The owner must approve the text unless they already told you to send or reply ("reply that I'll be there"). "Check my inbox" is not approval. +The owner must approve the text unless they already told you to send or reply ("reply that I'll be there"). "Check my inbox" is not approval. To message someone they already chat with, find the thread first ([Find someone's thread](#find-someones-thread)). + +X DMs go out only through `send_chat_message`; an email or Slack draft tool can't send them, so never route a DM there or tell the owner it needs one. If the host holds a `send_chat_message` call for the owner to approve (Grok Bot shows an X Chat draft card), call it again with exactly the same arguments, the same encrypted payload included (don't re-encrypt), once they press Send or tell you to send it. - **Thread with a conversation key:** fetch events and `conversation_key_events`, `session-encrypt` with the approved text, then `send_chat_message` with `id`, `message_id`, `encoded_message_create_event`, and `encoded_message_event_signature`. Confirm without dumping ciphertext. - **No conversation key yet** (first message, or only plain messages): get both users' public keys (`public_key` and `public_key_version`), `session-encrypt` with `prepare` set (same shape as `prepare-keys` input), call `add_conversation_keys` with its output **before** `send_chat_message`, and strip any `_local_*` fields. `add_conversation_keys` returning `UNAUTHORIZED_REQUESTING_USER` means you can't set keys there or the peer isn't on X Chat: stop and report. From fb77aaa734533f09e62b8b0205c66d9e661aa004 Mon Sep 17 00:00:00 2001 From: Santiago Medina Rolong Date: Fri, 6 Nov 2026 22:01:45 +0000 Subject: [PATCH 3/4] X MCP guide: step-by-step playbook to find or message a contact Turns the participant_query section into numbered steps: search with participant_query (their @username, or a word of their name) with type direct and expansions participant_ids, confirm the person from the expanded participant, keep paging while has_more is true, retry with the @username, then check message requests, and send through Send or reply with the match's id. --- third_party/x/skills/x-api-mcp-guide/SKILL.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/third_party/x/skills/x-api-mcp-guide/SKILL.md b/third_party/x/skills/x-api-mcp-guide/SKILL.md index 752e87c10a..bdaf0e90db 100644 --- a/third_party/x/skills/x-api-mcp-guide/SKILL.md +++ b/third_party/x/skills/x-api-mcp-guide/SKILL.md @@ -138,7 +138,7 @@ Once per session: | Tool | Use | | --- | --- | -| `get_chat_conversations` | The inbox, `max_results` 1000. `participant_query` finds one person's thread ([Find someone's thread](#find-someones-thread)) | +| `get_chat_conversations` | The inbox, `max_results` 1000. `participant_query` finds one person's thread ([Find or message a contact](#find-or-message-a-contact)) | | `get_chat_conversation` | One thread, including `participant_ids` / `member_ids` | | `get_chat_conversation_events` | `data[].encoded_event` plus `meta.conversation_key_events`, newest first, `max_results` 100 | | `get_users_public_keys` | Signing keys for senders, all versions, up to 100 `ids` per call | @@ -163,9 +163,15 @@ For any set of events: Each message's `status` is `verified`, `unencrypted`, or `unverified` (an unverified message still has its text). Focus on the content. Don't comment on encryption unprompted or call plain messages odd. Answer plainly from `status` when asked about security, and mention `unverified` briefly before acting on that message. -### Find someone's thread +### Find or message a contact -To find the thread with a named person, for example to message them, call `get_chat_conversations` with `participant_query` set to their @handle or name, `type` `direct` for a 1:1, `max_results` 1000, and `expansions` `participant_ids` to check it's them. Don't scan the whole inbox or look the person up first. A page can be empty while `has_more` is true: keep paging with the same query. If nothing matches, try their @handle if you used a name, then message requests by `sender_id`. +To find the thread with someone the owner already chats with, or to message them: + +1. `get_chat_conversations` with `participant_query` set to their @username (or a word of their name if you don't know it), `type` `direct`, `max_results` 1000, and `expansions` `participant_ids`. Don't list the whole inbox, and don't look the person up to get their id first. +2. Confirm it's them from the expanded participant's `username` and `name`. The match's `id` is the conversation id the other Chat tools take. +3. A page can be empty while `meta.has_more` is true: keep paging with `meta.next_token` and the same query until a match or `has_more` is false. +4. No match: retry with their @username if you searched by name, then check message requests by `sender_id`. Still nothing means they have no thread with the owner yet. +5. To message them, follow [Send or reply](#send-or-reply) with that `id`. ### Unread @@ -179,7 +185,7 @@ For "what's unread" or "anything new", don't scan every conversation. ### Read or summarize -Read every inbox conversation, not only the ones that look relevant: list with `get_chat_conversations` (`max_results` 1000, all pages), then decrypt each. Skip message requests unless asked; if `meta.has_message_requests` is true, end with one line offering them. To find a named person's thread, see [Find someone's thread](#find-someones-thread). Never claim you checked everything unless you read every thread (and both request inboxes, if asked) to the end; otherwise say how many you read. Optionally `mark_chat_conversation_read` afterwards. +Read every inbox conversation, not only the ones that look relevant: list with `get_chat_conversations` (`max_results` 1000, all pages), then decrypt each. Skip message requests unless asked; if `meta.has_message_requests` is true, end with one line offering them. To find a named person's thread, see [Find or message a contact](#find-or-message-a-contact). Never claim you checked everything unless you read every thread (and both request inboxes, if asked) to the end; otherwise say how many you read. Optionally `mark_chat_conversation_read` afterwards. ### Message requests @@ -191,7 +197,7 @@ Pending 1:1 requests aren't in `get_chat_conversations`. List, open, and accept ### Send or reply -The owner must approve the text unless they already told you to send or reply ("reply that I'll be there"). "Check my inbox" is not approval. To message someone they already chat with, find the thread first ([Find someone's thread](#find-someones-thread)). +The owner must approve the text unless they already told you to send or reply ("reply that I'll be there"). "Check my inbox" is not approval. To message someone they already chat with, find the thread first ([Find or message a contact](#find-or-message-a-contact)). X DMs go out only through `send_chat_message`; an email or Slack draft tool can't send them, so never route a DM there or tell the owner it needs one. If the host holds a `send_chat_message` call for the owner to approve (Grok Bot shows an X Chat draft card), call it again with exactly the same arguments, the same encrypted payload included (don't re-encrypt), once they press Send or tell you to send it. From e336b83ff2d6443eb7c3c3d7f7fbbab77eb35959 Mon Sep 17 00:00:00 2001 From: Santiago Medina Rolong Date: Fri, 6 Nov 2026 22:15:00 +0000 Subject: [PATCH 4/4] X plugin 2.11.1: changelog for the contact playbook and the X DM send rule --- third_party/x/.cursor-plugin/plugin.json | 2 +- third_party/x/CHANGELOG.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/third_party/x/.cursor-plugin/plugin.json b/third_party/x/.cursor-plugin/plugin.json index 0c174b3633..241da22f3e 100644 --- a/third_party/x/.cursor-plugin/plugin.json +++ b/third_party/x/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "x", "displayName": "X", - "version": "2.11.0", + "version": "2.11.1", "minClientVersions": { "cursor": "3.13.0" }, diff --git a/third_party/x/CHANGELOG.md b/third_party/x/CHANGELOG.md index 7c8df457dd..3b4736225e 100644 --- a/third_party/x/CHANGELOG.md +++ b/third_party/x/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this plugin will be documented here. +## 2.11.1 — Find a contact's chat, X DMs only via send_chat_message + +- A new "Find or message a contact" section in the guide: find someone's thread with `get_chat_conversations` `participant_query` (their @username, `type` `direct`, `expansions` `participant_ids` to confirm it's them), keep paging while `has_more` is true, and retry with their @username, instead of scanning the inbox or looking the person up first. +- X DMs go out only through `send_chat_message`, never an email or Slack draft tool. When the host holds a send for approval, as Grok Bot's X Chat draft card does, the send repeats it with exactly the same arguments, without re-encrypting. + ## 2.11.0 — Posting - Requested `tweet.write` so agents can publish posts and replies with `create_posts` on `https://api.x.com/mcp`.