| name | helpscout-navigator |
| description | Use when searching HelpScout tickets, customers, or organizations. Provides correct tool selection, required sequencing, and prevents common mistakes. Triggers on "search helpscout", "find tickets", "check support inbox", "helpscout conversations", "look up customer", "find organization", "customer history". |
HelpScout Navigation
Guide for correctly using HelpScout MCP tools. Prevents common mistakes and ensures complete search results.
First Step: Diagnose Setup
Follow these steps IN ORDER. Do not skip ahead.
Step 1: Check if MCP Tools are Available
Look for these tools in your available tools:
mcp__helpscout__searchInboxes
mcp__helpscout__searchConversations
mcp__helpscout__comprehensiveConversationSearch
If tools ARE available: ✅ Skip to "Critical Rules" section. You're ready to go.
If tools are NOT available: Continue to Step 2.
Step 2: Check if Credentials are Set
Run this command:
echo "HELPSCOUT_APP_ID: ${HELPSCOUT_APP_ID:+[SET]}" && echo "HELPSCOUT_APP_SECRET: ${HELPSCOUT_APP_SECRET:+[SET]}"
If both show [SET]: Credentials exist but MCP didn't start. Go to Step 4.
If either is blank: Credentials are missing. Go to Step 3.
Step 3: Set Up Credentials
Tell the user:
HelpScout credentials are not configured.
Get your credentials:
- Go to HelpScout → Your Profile → My Apps
- Create a new app (or use existing)
- Copy the App ID and App Secret
Add to your shell profile (~/.zshrc or ~/.bashrc):
export HELPSCOUT_APP_ID="your-app-id-here"
export HELPSCOUT_APP_SECRET="your-app-secret-here"
Then go to Step 4.
Step 4: Restart Correctly (IMPORTANT)
⚠️ This is where most people get stuck.
The MCP server inherits environment variables from Claude Code's process. If Claude Code was started before the credentials were set, it won't have them.
Tell the user:
You must restart BOTH your terminal AND Claude Code:
- Quit Claude Code completely (not just close the window)
- Close your terminal completely (not just the tab)
- Open a new terminal (this loads your updated
.zshrc)
- Start Claude Code from this new terminal
claude
The HelpScout MCP server will now start with the correct credentials.
Do not proceed with HelpScout operations until the MCP tools are available.
Overview
The HelpScout MCP server provides 17 tools for searching and retrieving support conversations. However, using them incorrectly leads to missed tickets and incomplete results. This skill ensures you use the right tool in the right order.
Core problems this skill solves:
- Users forget to call
searchInboxes first (required for inbox-scoped searches)
searchConversations defaults to "active" status only (misses closed/pending)
- Users don't know which search tool to use for their query type
- Users don't know the customer/org lookup tools for account investigation
- Users try conversation search when they should use customer or org tools
Critical Rules (MUST READ FIRST)
Rule 1: ALWAYS Call searchInboxes First
When a user mentions an inbox by name, you MUST look up the inbox ID before searching.
| User Says | You MUST Do First |
|---|
| "Search the support inbox" | searchInboxes(query: "support") |
| "Find tickets in sales mailbox" | searchInboxes(query: "sales") |
| "Check the billing inbox" | searchInboxes(query: "billing") |
Why: All inbox-scoped searches require an inbox ID (numeric), not a name.
Rule 2: searchConversations Defaults to "Active" Only
DANGER: searchConversations without a status parameter only returns active tickets.
| What You Want | WRONG Tool | RIGHT Tool |
|---|
| "Find tickets about billing" | searchConversations(query: "billing") | comprehensiveConversationSearch(searchTerms: ["billing"]) |
| "Search for refund requests" | searchConversations(query: "refund") | comprehensiveConversationSearch(searchTerms: ["refund"]) |
Use searchConversations ONLY when:
- Listing recent tickets (no keyword search)
- You explicitly want only active tickets
- Combined with explicit status parameter
Use comprehensiveConversationSearch when:
- Searching by keywords
- You want to find tickets regardless of status
- Default behavior (searches active + pending + closed)
Rule 3: structuredConversationFilter is for AFTER Discovery
This tool requires IDs from previous searches. Do NOT use for initial searches.
| Use Case | Correct Approach |
|---|
| Find ticket #42839 | structuredConversationFilter(conversationNumber: 42839) |
| Find John's assigned tickets | First: get John's ID. Then: structuredConversationFilter(assignedTo: <johnId>) |
| Find customer 12345's history | structuredConversationFilter(customerIds: [12345]) |
Decision Tree: Which Tool to Use
digraph decision {
rankdir=TB;
node [shape=box, style=rounded];
start [label="Start", shape=ellipse];
inbox_q [label="User mentions\ninbox name?", shape=diamond];
searchInboxes [label="searchInboxes\n(ALWAYS FIRST)", style="bold,filled", fillcolor="#ffcccc"];
keyword_q [label="Keyword\nsearch?", shape=diamond];
comprehensive [label="comprehensiveConversationSearch\n(multi-status)", style="bold,filled", fillcolor="#ccffcc"];
listing_q [label="Listing by\ntime/status?", shape=diamond];
searchConv [label="searchConversations"];
complex_q [label="Complex filters?\n(domain, tags)", shape=diamond];
advanced [label="advancedConversationSearch"];
has_ids [label="Have IDs from\nprior search?", shape=diamond];
structured [label="structuredConversationFilter"];
thread_q [label="Need full\nthread?", shape=diamond];
getThreads [label="getThreads"];
summary_q [label="Need quick\nsummary?", shape=diamond];
getSummary [label="getConversationSummary"];
start -> inbox_q;
inbox_q -> searchInboxes [label="yes"];
inbox_q -> keyword_q [label="no"];
searchInboxes -> keyword_q;
keyword_q -> comprehensive [label="yes"];
keyword_q -> listing_q [label="no"];
listing_q -> searchConv [label="yes"];
listing_q -> complex_q [label="no"];
complex_q -> advanced [label="yes"];
complex_q -> has_ids [label="no"];
has_ids -> structured [label="yes"];
has_ids -> thread_q [label="no"];
thread_q -> getThreads [label="yes"];
thread_q -> summary_q [label="no"];
summary_q -> getSummary [label="yes"];
}
Quick Decision Matrix
| I want to... | Use this tool | Required first? |
|---|
| Search by keywords | comprehensiveConversationSearch | searchInboxes if inbox mentioned |
| List recent tickets (single status) | searchConversations | searchInboxes if inbox mentioned |
| List recent tickets (ALL statuses) | structuredConversationFilter(sortBy: "waitingSince", status: "all") | searchInboxes if inbox mentioned |
| Find tickets by email domain | advancedConversationSearch | searchInboxes if inbox mentioned |
| Look up ticket #12345 | structuredConversationFilter | None |
| Get all tickets from customer X | structuredConversationFilter | Need customer ID from prior search |
| Read full conversation | getThreads | Need conversation ID |
| Get quick overview | getConversationSummary | Need conversation ID |
| List all inboxes | listAllInboxes | None |
| Get current server time | getServerTime | None |
| Look up a customer by email | searchCustomersByEmail | None |
| Browse customers | listCustomers | None |
| Get full customer profile | getCustomer | Need customer ID |
| Get customer contact details | getCustomerContacts | Need customer ID |
| Browse organizations | listOrganizations | None |
| Get organization details | getOrganization | Need organization ID |
| See who is in an organization | getOrganizationMembers | Need organization ID |
| See org's support history | getOrganizationConversations | Need organization ID |
Tool Reference Summary
| Tool | Purpose | Key Limitation |
|---|
searchInboxes | Get inbox ID from name | ALWAYS call first when inbox mentioned |
listAllInboxes | List all available inboxes | Helper for discovery |
searchConversations | List tickets by time/status | Defaults to ACTIVE ONLY |
comprehensiveConversationSearch | Keyword search across statuses | Preferred for content search |
advancedConversationSearch | Complex filters (domain, tags) | For boolean logic |
structuredConversationFilter | ID-based lookup | Requires IDs from prior search |
getConversationSummary | Quick overview | Needs conversation ID |
getThreads | Full message history | Needs conversation ID |
getServerTime | Current timestamp | For time-relative searches |
listCustomers | Browse customers by name or query | Page-based v2 API |
searchCustomersByEmail | Find customer by email | Uses v3 API with cursor pagination |
getCustomer | Full customer profile with contacts | Includes embedded sub-resources |
getCustomerContacts | All contact channels for a customer | Parallel sub-resource lookups |
listOrganizations | Browse organizations | Sortable by activity, size, name |
getOrganization | Organization profile with counts | Optional customer/conversation counts |
getOrganizationMembers | Customers in an organization | 50 per page |
getOrganizationConversations | Support history for an organization | 50 per page |
See references/tool-reference.md for complete parameter documentation.
Common Workflows
Workflow 1: Search Inbox X for Keyword Y
User: "Search the support inbox for billing issues"
Steps:
-
Look up inbox ID:
searchInboxes(query: "support")
Result: { id: 359402, name: "Support" }
-
Search with inbox scope:
comprehensiveConversationSearch(
searchTerms: ["billing"],
inboxId: "359402"
)
Workflow 2: Show Recent Tickets in Inbox X
User: "Show me recent tickets in the sales inbox"
Steps:
-
Look up inbox ID:
searchInboxes(query: "sales")
-
List recent (no keyword = use searchConversations):
searchConversations(
inboxId: "359402",
sort: "createdAt",
order: "desc",
limit: 20
)
Workflow 3: Find Ticket #12345
User: "Show me ticket 12345"
Steps:
-
Direct lookup (no inbox lookup needed):
structuredConversationFilter(conversationNumber: 12345)
-
Get details:
getConversationSummary(conversationId: "<id from step 1>")
Workflow 4: Find All Tickets from Domain
User: "Find tickets from @acme.com"
Steps:
- Use advanced search with domain filter:
advancedConversationSearch(emailDomain: "acme.com")
Workflow 5: List All Recent Tickets Across ALL Statuses
User: "Show me recent tickets from the last 30 days" (no specific status mentioned)
Steps:
- Use
structuredConversationFilter with a unique sortBy value:
structuredConversationFilter(
sortBy: "waitingSince", // Required: unique sortBy enables all-status queries
status: "all", // Includes active, pending, closed, spam
sortOrder: "desc",
limit: 50,
createdAfter: "2024-01-01T00:00:00Z" // Optional: date filter
)
Why this works:
structuredConversationFilter supports status: "all" (unlike searchConversations)
- BUT it requires at least one "unique field" - using
sortBy: "waitingSince" satisfies this
- Other unique sortBy values:
customerName, customerEmail
Common mistake: Using searchConversations(status: "all") - this FAILS because searchConversations only accepts specific statuses (active/pending/closed/spam), not "all".
Workflow 6: Get Full Conversation Thread
User: "Show me the full thread for conversation 12345678"
Steps:
- Get all messages:
getThreads(conversationId: "12345678", limit: 200)
Workflow 7: Customer Investigation by Email
User: "Look up the customer jane@acme.com and show their history"
Steps:
-
Find customer by email:
searchCustomersByEmail(email: "jane@acme.com")
Result: { id: 12345, firstName: "Jane", organizationId: 456 }
-
Get full profile:
getCustomer(customerId: "12345")
-
Get their conversations:
structuredConversationFilter(customerIds: [12345], status: "all", sortBy: "createdAt")
Workflow 8: Organization Account Review
User: "Show me everything about the Acme Corp account"
Steps:
-
Find the organization:
listOrganizations(sortField: "name")
Find "Acme Corp" in results, get org ID.
-
Get organization details:
getOrganization(organizationId: "456", includeCounts: true)
-
See who is in the org:
getOrganizationMembers(organizationId: "456")
-
See their support history:
getOrganizationConversations(organizationId: "456")
Anti-Patterns (What NOT to Do)
| Mistake | Why It Fails | Correct Approach |
|---|
searchConversations(query: "billing") without status | Returns active only, misses 80%+ of tickets | comprehensiveConversationSearch(searchTerms: ["billing"]) |
searchConversations(inboxId: "Support") | Inbox ID must be numeric, not name | First: searchInboxes(query: "Support") |
searchConversations(status: "all") | "all" is NOT a valid status for this tool | Use structuredConversationFilter(sortBy: "waitingSince", status: "all") |
structuredConversationFilter as first search | Requires IDs you don't have yet | Start with comprehensiveConversationSearch |
structuredConversationFilter without unique field | Tool requires a unique field to work | Add sortBy: "waitingSince" or provide assignedTo/customerIds/conversationNumber |
Skipping searchInboxes when user mentions inbox | API requires numeric inbox ID | ALWAYS lookup first |
Using searchConversations for keyword search | Misses closed/pending tickets | Use comprehensiveConversationSearch |
See references/common-mistakes.md for more anti-patterns.
Quick Reference Card
searchInboxes(query: "support")
comprehensiveConversationSearch(
searchTerms: ["billing", "refund"],
inboxId: "359402",
timeframeDays: 60
)
searchConversations(
inboxId: "359402",
status: "active",
sort: "createdAt",
order: "desc"
)
structuredConversationFilter(
sortBy: "waitingSince",
status: "all",
sortOrder: "desc",
limit: 50
)
structuredConversationFilter(conversationNumber: 12345)
advancedConversationSearch(emailDomain: "acme.com")
getThreads(conversationId: "12345678")
getConversationSummary(conversationId: "12345678")
searchCustomersByEmail(email: "jane@acme.com")
getCustomer(customerId: "12345")
getCustomerContacts(customerId: "12345")
listOrganizations(sortField: "conversationCount", sortOrder: "desc")
getOrganization(organizationId: "456", includeCounts: true)
getOrganizationMembers(organizationId: "456")
getOrganizationConversations(organizationId: "456")
Common Mistakes Checklist
Before executing a HelpScout search, verify: