diff --git a/trello/README.md b/trello/README.md
new file mode 100644
index 0000000..44005c4
--- /dev/null
+++ b/trello/README.md
@@ -0,0 +1,30 @@
+Connect Trello to help Captain find the card for a bug or request, check its status and comments, and add a comment when another customer reports it.
+
+## Setup
+
+1. Go to [trello.com/apps/admin](https://trello.com/apps/admin), open (or create) a Power-Up, and under the **Trello Auth** tab select **Generate a new API Key**. Copy the API key.
+2. Generate a token for the Trello account Captain should act as by visiting this URL while signed in to that account, replacing `YOUR_API_KEY`, and selecting **Allow**:
+
+ `https://trello.com/1/authorize?expiration=never&scope=read,write&response_type=token&key=YOUR_API_KEY`
+
+ Use `scope=read` instead if you don't need Add Card Comment.
+3. Enter the API key and the token when installing.
+
+The token can see every board the Trello account can see. To limit what Captain reads, create the token from an account that is only a member of the boards you want to share.
+
+## Tools
+
+- **Search Cards:** Search cards by keyword across every board the account can see. Returns up to 10 cards with their board, list, due date, and card ID.
+- **Get Card:** Check one card's board, list, due date, labels, members, checklist progress, comment count, and description.
+- **List Card Comments:** Read the 10 most recent comments on a card.
+- **List Boards:** List up to 50 open boards with their board IDs.
+- **List Board Cards:** List the open lists on a board and the open cards in each, up to 20 lists and 10 cards per list.
+- **Add Card Comment:** Add a comment to a card, such as a note that another customer reported the same issue.
+
+Start with Search Cards, then use the card ID with the other tools.
+
+## Notes
+
+- The API key and token are sent in Trello's `Authorization: OAuth` header, not in the request URL.
+- Search matches whole words and word prefixes, so "dev" finds "development" but "velopment" does not.
+- Add Card Comment posts as the Trello account that created the token.
diff --git a/trello/toolset.yml b/trello/toolset.yml
new file mode 100644
index 0000000..aa900aa
--- /dev/null
+++ b/trello/toolset.yml
@@ -0,0 +1,146 @@
+version: 0.0.1
+kind: captain_toolset
+name: Trello
+author: chatwoot
+category: Others
+description: Search Trello cards, check their status and comments, and add comments to cards.
+
+secrets:
+ api_key:
+ label: Trello API key
+ type: password
+ required: true
+ api_token:
+ label: Trello API token
+ type: password
+ required: true
+
+tools:
+ - id: search_cards
+ title: Search Cards
+ description: Search Trello cards by keyword across every board the connected account can see. Use this first when a customer reports a bug or asks about a request, to find a matching card and its card ID. Supports Trello search operators such as board:name, list:name, label:name, and is:open.
+ http_method: GET
+ endpoint_url: https://api.trello.com/1/search?query={{ query | strip | url_encode }}&modelTypes=cards&cards_limit=10&card_fields=name,desc,closed,due,dateLastActivity,shortUrl&card_board=true&card_list=true&partial=true
+ auth_type: api_key
+ auth_config:
+ location: header
+ name: Authorization
+ key: 'OAuth oauth_consumer_key="${{ secrets.api_key }}", oauth_token="${{ secrets.api_token }}"'
+ param_schema:
+ - name: query
+ type: string
+ description: Keywords to search for, such as an error message or feature name. Keep it to a few distinctive words.
+ required: true
+ response_template: |-
+ {% if response.cards %}{% for card in response.cards limit: 10 %}- {{ card.name }} (card ID {{ card.id }}){% if card.closed %} · archived{% endif %}
+ Board: {{ card.board.name | default: "unknown" }} · List: {{ card.list.name | default: "unknown" }}{% if card.due %} · Due {{ card.due | slice: 0, 10 }}{% endif %} · Last activity {{ card.dateLastActivity | slice: 0, 10 }}
+ {{ card.shortUrl }}{% if card.desc and card.desc != "" %}
+ {{ card.desc | newline_to_br | replace: "
", " " | strip_newlines | truncate: 200 }}{% endif %}
+ {% else %}No cards found matching this search.
+ {% endfor %}{% else %}Trello returned an unexpected response.{% endif %}
+
+ - id: get_card
+ title: Get Card
+ description: Get one Trello card's details, including its board, list, due date, labels, members, checklist progress, and description. Use a card ID from Search Cards or List Board Cards.
+ http_method: GET
+ endpoint_url: https://api.trello.com/1/cards/{{ card_id | strip | url_encode }}?fields=name,desc,closed,due,dateLastActivity,shortUrl,labels,badges&board=true&board_fields=name&list=true&members=true&member_fields=fullName
+ auth_type: api_key
+ auth_config:
+ location: header
+ name: Authorization
+ key: 'OAuth oauth_consumer_key="${{ secrets.api_key }}", oauth_token="${{ secrets.api_token }}"'
+ param_schema:
+ - name: card_id
+ type: string
+ description: The Trello card ID from Search Cards or List Board Cards.
+ required: true
+ response_template: |-
+ {% if response.id %}{{ response.name }} (card ID {{ response.id }}){% if response.closed %} · archived{% endif %}
+ Board: {{ response.board.name | default: "unknown" }}
+ List: {{ response.list.name | default: "unknown" }}{% if response.due %}
+ Due: {{ response.due | slice: 0, 10 }}{% if response.badges.dueComplete %} (marked complete){% endif %}{% endif %}
+ Last activity: {{ response.dateLastActivity | slice: 0, 10 }}{% if response.labels.size > 0 %}
+ Labels: {% for label in response.labels limit: 10 %}{{ label.name | default: label.color }}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endif %}{% if response.members.size > 0 %}
+ Members: {% for member in response.members limit: 10 %}{{ member.fullName }}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endif %}{% if response.badges.checkItems > 0 %}
+ Checklist items done: {{ response.badges.checkItemsChecked }} of {{ response.badges.checkItems }}{% endif %}
+ Comments: {{ response.badges.comments | default: 0 }}
+ Link: {{ response.shortUrl }}{% if response.desc and response.desc != "" %}
+ Description: {{ response.desc | truncate: 1500 }}{% endif %}{% else %}Trello returned an unexpected response.{% endif %}
+
+ - id: list_card_comments
+ title: List Card Comments
+ description: Read the 10 most recent comments on a Trello card, such as progress updates from the team. Use a card ID from Search Cards or List Board Cards.
+ http_method: GET
+ endpoint_url: https://api.trello.com/1/cards/{{ card_id | strip | url_encode }}/actions?filter=commentCard
+ auth_type: api_key
+ auth_config:
+ location: header
+ name: Authorization
+ key: 'OAuth oauth_consumer_key="${{ secrets.api_key }}", oauth_token="${{ secrets.api_token }}"'
+ param_schema:
+ - name: card_id
+ type: string
+ description: The Trello card ID from Search Cards or List Board Cards.
+ required: true
+ response_template: |-
+ {% if response.size == 0 %}No comments on this card.{% elsif response[0].id %}{% for action in response limit: 10 %}- {{ action.date | slice: 0, 10 }} · {{ action.memberCreator.fullName | default: "Unknown member" }}: {{ action.data.text | newline_to_br | replace: "
", " " | strip_newlines | truncate: 500 }}
+ {% endfor %}{% else %}Trello returned an unexpected response.{% endif %}
+
+ - id: list_boards
+ title: List Boards
+ description: List the open Trello boards the connected account can see, with their board IDs. Use this to find the board ID that List Board Cards needs.
+ http_method: GET
+ endpoint_url: https://api.trello.com/1/members/me/boards?filter=open&fields=name,desc,shortUrl,dateLastActivity
+ auth_type: api_key
+ auth_config:
+ location: header
+ name: Authorization
+ key: 'OAuth oauth_consumer_key="${{ secrets.api_key }}", oauth_token="${{ secrets.api_token }}"'
+ response_template: |-
+ {% if response.size == 0 %}No open boards found.{% elsif response[0].id %}{% for board in response limit: 50 %}- {{ board.name }} (board ID {{ board.id }}){% if board.dateLastActivity %} · Last activity {{ board.dateLastActivity | slice: 0, 10 }}{% endif %}
+ {{ board.shortUrl }}{% if board.desc and board.desc != "" %}
+ {{ board.desc | newline_to_br | replace: "
", " " | strip_newlines | truncate: 150 }}{% endif %}
+ {% endfor %}{% if response.size > 50 %}Showing the first 50 of {{ response.size }} boards.{% endif %}{% else %}Trello returned an unexpected response.{% endif %}
+
+ - id: list_board_cards
+ title: List Board Cards
+ description: List the open lists on a Trello board and the open cards in each, to see what is planned, in progress, or done. Use a board ID from List Boards. Shows up to 20 lists and 10 cards per list.
+ http_method: GET
+ endpoint_url: https://api.trello.com/1/boards/{{ board_id | strip | url_encode }}/lists?filter=open&fields=name&cards=open&card_fields=name,due,shortUrl
+ auth_type: api_key
+ auth_config:
+ location: header
+ name: Authorization
+ key: 'OAuth oauth_consumer_key="${{ secrets.api_key }}", oauth_token="${{ secrets.api_token }}"'
+ param_schema:
+ - name: board_id
+ type: string
+ description: The Trello board ID from List Boards.
+ required: true
+ response_template: |-
+ {% if response.size == 0 %}No open lists on this board.{% elsif response[0].id %}{% for list in response limit: 20 %}{{ list.name }} ({{ list.cards.size | default: 0 }} open cards)
+ {% for card in list.cards limit: 10 %}- {{ card.name | truncate: 120 }} (card ID {{ card.id }}){% if card.due %} · Due {{ card.due | slice: 0, 10 }}{% endif %}
+ {% endfor %}{% if list.cards.size > 10 %}- and {{ list.cards.size | minus: 10 }} more cards
+ {% endif %}{% endfor %}{% if response.size > 20 %}Showing the first 20 of {{ response.size }} lists.{% endif %}{% else %}Trello returned an unexpected response.{% endif %}
+
+ - id: add_card_comment
+ title: Add Card Comment
+ description: Add a comment to a Trello card, for example to note that another customer reported the same issue, with a short summary. Use a card ID from Search Cards. Only add a comment when the conversation needs one.
+ http_method: POST
+ endpoint_url: https://api.trello.com/1/cards/{{ card_id | strip | url_encode }}/actions/comments?text={{ text | strip | url_encode }}
+ auth_type: api_key
+ auth_config:
+ location: header
+ name: Authorization
+ key: 'OAuth oauth_consumer_key="${{ secrets.api_key }}", oauth_token="${{ secrets.api_token }}"'
+ param_schema:
+ - name: card_id
+ type: string
+ description: The Trello card ID from Search Cards or List Board Cards.
+ required: true
+ - name: text
+ type: string
+ description: The comment text, up to about 1,000 characters. Trello renders Markdown.
+ required: true
+ response_template: |-
+ {% if response.id %}Added the comment to the card{% if response.data.card.name %} "{{ response.data.card.name }}"{% endif %}.{% else %}Trello returned an unexpected response.{% endif %}