feedbackbasket-mcp-server 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,192 +1,192 @@
1
- # FeedbackBasket MCP Server
2
-
3
- Model Context Protocol (MCP) server for FeedbackBasket that allows AI assistants to query your project feedback and bug reports.
4
-
5
- ## Features
6
-
7
- - 🔍 **List Projects** - View all your FeedbackBasket projects with summary statistics
8
- - 📝 **Get Feedback** - Retrieve feedback with filtering by category, status, sentiment, and more
9
- - 🐛 **Get Bug Reports** - Specifically fetch bug reports with severity classification
10
- - 🔎 **Search Feedback** - Search across all feedback content using text queries
11
- - 🔐 **Secure Authentication** - Uses API keys with project-level access control
12
- - 📊 **Rich Statistics** - Get counts by category, status, sentiment, and severity
13
-
14
- ## Installation & Setup
15
-
16
- ### 1. Generate API Key
17
-
18
- First, you'll need to create an API key from your FeedbackBasket account:
19
-
20
- 1. **Visit** [feedbackbasket.com](https://feedbackbasket.com) and log into your account
21
- 2. **Navigate** to your Account Settings (click your profile → Account)
22
- 3. **Find** the "MCP API Keys" section
23
- 4. **Click** "New API Key"
24
- 5. **Name your key** (e.g., "Claude Desktop", "Cursor IDE", "Windsurf")
25
- 6. **Select projects** you want to grant access to (or leave empty for all projects)
26
- 7. **Copy the generated API key** (starts with `fb_key_`) - you'll only see the full key once!
27
-
28
- > 💡 **Tip**: You can manage your API keys anytime from your account settings at [feedbackbasket.com/account](https://feedbackbasket.com/account)
29
-
30
- ### 2. Configure Your Editor
31
-
32
- Add the MCP server to your editor's configuration:
33
-
34
- #### Quick Setup (All Editors)
35
- ```json
36
- {
37
- "mcpServers": {
38
- "feedbackbasket": {
39
- "command": "npx",
40
- "args": ["-y", "@feedbackbasket/mcp-server@latest", "--api-key", "fb_key_your_api_key_here"]
41
- }
42
- }
43
- }
44
- ```
45
-
46
- #### Detailed Examples
47
-
48
- #### Claude Desktop
49
- ```json
50
- {
51
- "mcpServers": {
52
- "feedbackbasket": {
53
- "command": "npx",
54
- "args": [
55
- "-y",
56
- "@feedbackbasket/mcp-server@latest",
57
- "--api-key",
58
- "fb_key_your_api_key_here"
59
- ]
60
- }
61
- }
62
- }
63
- ```
64
-
65
- #### Cursor/Windsurf
66
- ```json
67
- {
68
- "mcpServers": {
69
- "feedbackbasket": {
70
- "command": "npx",
71
- "args": [
72
- "-y",
73
- "@feedbackbasket/mcp-server@latest",
74
- "--api-key",
75
- "fb_key_your_api_key_here"
76
- ]
77
- }
78
- }
79
- }
80
- ```
81
-
82
- #### Environment Variable (Alternative)
83
- ```json
84
- {
85
- "mcpServers": {
86
- "feedbackbasket": {
87
- "command": "npx",
88
- "args": ["-y", "@feedbackbasket/mcp-server@latest"],
89
- "env": {
90
- "FEEDBACKBASKET_API_KEY": "fb_key_your_api_key_here"
91
- }
92
- }
93
- }
94
- }
95
- ```
96
-
97
- ## Usage Examples
98
-
99
- Once configured, you can ask your AI assistant:
100
-
101
- ### Project Overview
102
- - "Show me all my FeedbackBasket projects"
103
- - "What projects do I have and how much feedback do they have?"
104
-
105
- ### Bug Reports
106
- - "Show me all bug reports from my projects"
107
- - "Get high severity bug reports that are still pending"
108
- - "Find bug reports for my main website project"
109
-
110
- ### Feedback Analysis
111
- - "Show me negative feedback from the last week"
112
- - "Get all feature requests from my mobile app project"
113
- - "Search for feedback containing 'login error'"
114
-
115
- ### Filtering & Search
116
- - "Show me reviewed feedback with positive sentiment"
117
- - "Find all pending bug reports with high severity"
118
- - "Search for feedback about 'payment issues' in my e-commerce project"
119
-
120
- ## Available Tools
121
-
122
- ### `list_projects`
123
- Lists all projects accessible by your API key with summary statistics.
124
-
125
- ### `get_feedback`
126
- Retrieves feedback with optional filtering:
127
- - `projectId` - Filter by specific project
128
- - `category` - Filter by BUG, FEATURE, or REVIEW
129
- - `status` - Filter by PENDING, REVIEWED, or DONE
130
- - `sentiment` - Filter by POSITIVE, NEGATIVE, or NEUTRAL
131
- - `search` - Text search in feedback content
132
- - `limit` - Number of results (max 100)
133
- - `includeNotes` - Include internal notes
134
-
135
- ### `get_bug_reports`
136
- Specifically fetches bug reports with computed severity:
137
- - `projectId` - Filter by specific project
138
- - `status` - Filter by bug status
139
- - `severity` - Filter by high, medium, or low severity
140
- - `search` - Text search in bug reports
141
- - `limit` - Number of results (max 100)
142
- - `includeNotes` - Include internal notes
143
-
144
- ### `search_feedback`
145
- Searches feedback content across all accessible projects:
146
- - `query` - Search text (required)
147
- - `projectId` - Limit to specific project
148
- - `category` - Filter by category
149
- - `limit` - Number of results (max 50)
150
-
151
- ## Security & Privacy
152
-
153
- - ✅ **Read-only access** - MCP server can only read data, never modify
154
- - ✅ **Project-level permissions** - Grant access only to specific projects
155
- - ✅ **API key authentication** - Secure token-based authentication
156
- - ✅ **Usage tracking** - Monitor API key usage in your dashboard
157
- - ✅ **Revokable access** - Instantly deactivate API keys if needed
158
- - ✅ **Subscription limits respected** - Only returns feedback within your plan limits
159
-
160
- ## Troubleshooting
161
-
162
- ### "Invalid or missing API key"
163
- - Check that your API key starts with `fb_key_`
164
- - Ensure the API key is still active in your dashboard
165
- - Verify the API key has access to at least one project
166
-
167
- ### "No projects found"
168
- - Make sure your API key has been granted access to projects
169
- - Check that you have projects in your FeedbackBasket account
170
-
171
- ### "Authentication failed"
172
- - Verify your API key hasn't been revoked
173
- - Check that the API key format is correct (71 characters, starts with `fb_key_`)
174
-
175
- ### Installation Issues
176
- - Ensure you have Node.js installed
177
- - Try clearing npx cache: `npx clear-npx-cache`
178
- - Check your internet connection for package downloads
179
-
180
- ## API Key Management
181
-
182
- Visit your [FeedbackBasket dashboard](https://feedbackbasket.com/dashboard/settings) to:
183
- - Generate new API keys
184
- - Manage project access for existing keys
185
- - View usage statistics
186
- - Deactivate or delete keys
187
- - Monitor key activity
188
-
189
- ## Support
190
-
191
- - 🐛 Issues: [GitHub Issues](https://github.com/deifos/feedbackbasket-mcp/issues)
1
+ # FeedbackBasket MCP Server
2
+
3
+ Model Context Protocol (MCP) server for FeedbackBasket that allows AI assistants to query your project feedback and bug reports.
4
+
5
+ ## Features
6
+
7
+ - 🔍 **List Projects** - View all your FeedbackBasket projects with summary statistics
8
+ - 📝 **Get Feedback** - Retrieve feedback with filtering by category, status, sentiment, and more
9
+ - 🐛 **Get Bug Reports** - Specifically fetch bug reports with severity classification
10
+ - 🔎 **Search Feedback** - Search across all feedback content using text queries
11
+ - 🔐 **Secure Authentication** - Uses API keys with project-level access control
12
+ - 📊 **Rich Statistics** - Get counts by category, status, sentiment, and severity
13
+
14
+ ## Installation & Setup
15
+
16
+ ### 1. Generate API Key
17
+
18
+ First, you'll need to create an API key from your FeedbackBasket account:
19
+
20
+ 1. **Visit** [feedbackbasket.com](https://feedbackbasket.com) and log into your account
21
+ 2. **Navigate** to your Account Settings (click your profile → Account)
22
+ 3. **Find** the "MCP API Keys" section
23
+ 4. **Click** "New API Key"
24
+ 5. **Name your key** (e.g., "Claude Desktop", "Cursor IDE", "Windsurf")
25
+ 6. **Select projects** you want to grant access to (or leave empty for all projects)
26
+ 7. **Copy the generated API key** (starts with `fb_key_`) - you'll only see the full key once!
27
+
28
+ > 💡 **Tip**: You can manage your API keys anytime from your account settings at [feedbackbasket.com/account](https://feedbackbasket.com/account)
29
+
30
+ ### 2. Configure Your Editor
31
+
32
+ Add the MCP server to your editor's configuration:
33
+
34
+ #### Quick Setup (All Editors)
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "feedbackbasket": {
39
+ "command": "npx",
40
+ "args": ["-y", "@feedbackbasket/mcp-server@latest", "--api-key", "fb_key_your_api_key_here"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ #### Detailed Examples
47
+
48
+ #### Claude Desktop
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "feedbackbasket": {
53
+ "command": "npx",
54
+ "args": [
55
+ "-y",
56
+ "@feedbackbasket/mcp-server@latest",
57
+ "--api-key",
58
+ "fb_key_your_api_key_here"
59
+ ]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ #### Cursor/Windsurf
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "feedbackbasket": {
70
+ "command": "npx",
71
+ "args": [
72
+ "-y",
73
+ "@feedbackbasket/mcp-server@latest",
74
+ "--api-key",
75
+ "fb_key_your_api_key_here"
76
+ ]
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ #### Environment Variable (Alternative)
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "feedbackbasket": {
87
+ "command": "npx",
88
+ "args": ["-y", "@feedbackbasket/mcp-server@latest"],
89
+ "env": {
90
+ "FEEDBACKBASKET_API_KEY": "fb_key_your_api_key_here"
91
+ }
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## Usage Examples
98
+
99
+ Once configured, you can ask your AI assistant:
100
+
101
+ ### Project Overview
102
+ - "Show me all my FeedbackBasket projects"
103
+ - "What projects do I have and how much feedback do they have?"
104
+
105
+ ### Bug Reports
106
+ - "Show me all bug reports from my projects"
107
+ - "Get high severity bug reports that are still pending"
108
+ - "Find bug reports for my main website project"
109
+
110
+ ### Feedback Analysis
111
+ - "Show me negative feedback from the last week"
112
+ - "Get all feature requests from my mobile app project"
113
+ - "Search for feedback containing 'login error'"
114
+
115
+ ### Filtering & Search
116
+ - "Show me reviewed feedback with positive sentiment"
117
+ - "Find all pending bug reports with high severity"
118
+ - "Search for feedback about 'payment issues' in my e-commerce project"
119
+
120
+ ## Available Tools
121
+
122
+ ### `list_projects`
123
+ Lists all projects accessible by your API key with summary statistics.
124
+
125
+ ### `get_feedback`
126
+ Retrieves feedback with optional filtering:
127
+ - `projectId` - Filter by specific project
128
+ - `category` - Filter by BUG, FEATURE, or REVIEW
129
+ - `status` - Filter by PENDING, REVIEWED, or DONE
130
+ - `sentiment` - Filter by POSITIVE, NEGATIVE, or NEUTRAL
131
+ - `search` - Text search in feedback content
132
+ - `limit` - Number of results (max 100)
133
+ - `includeNotes` - Include internal notes
134
+
135
+ ### `get_bug_reports`
136
+ Specifically fetches bug reports with computed severity:
137
+ - `projectId` - Filter by specific project
138
+ - `status` - Filter by bug status
139
+ - `severity` - Filter by high, medium, or low severity
140
+ - `search` - Text search in bug reports
141
+ - `limit` - Number of results (max 100)
142
+ - `includeNotes` - Include internal notes
143
+
144
+ ### `search_feedback`
145
+ Searches feedback content across all accessible projects:
146
+ - `query` - Search text (required)
147
+ - `projectId` - Limit to specific project
148
+ - `category` - Filter by category
149
+ - `limit` - Number of results (max 50)
150
+
151
+ ## Security & Privacy
152
+
153
+ - ✅ **Read-only access** - MCP server can only read data, never modify
154
+ - ✅ **Project-level permissions** - Grant access only to specific projects
155
+ - ✅ **API key authentication** - Secure token-based authentication
156
+ - ✅ **Usage tracking** - Monitor API key usage in your dashboard
157
+ - ✅ **Revokable access** - Instantly deactivate API keys if needed
158
+ - ✅ **Subscription limits respected** - Only returns feedback within your plan limits
159
+
160
+ ## Troubleshooting
161
+
162
+ ### "Invalid or missing API key"
163
+ - Check that your API key starts with `fb_key_`
164
+ - Ensure the API key is still active in your dashboard
165
+ - Verify the API key has access to at least one project
166
+
167
+ ### "No projects found"
168
+ - Make sure your API key has been granted access to projects
169
+ - Check that you have projects in your FeedbackBasket account
170
+
171
+ ### "Authentication failed"
172
+ - Verify your API key hasn't been revoked
173
+ - Check that the API key format is correct (71 characters, starts with `fb_key_`)
174
+
175
+ ### Installation Issues
176
+ - Ensure you have Node.js installed
177
+ - Try clearing npx cache: `npx clear-npx-cache`
178
+ - Check your internet connection for package downloads
179
+
180
+ ## API Key Management
181
+
182
+ Visit your [FeedbackBasket dashboard](https://feedbackbasket.com/dashboard/settings) to:
183
+ - Generate new API keys
184
+ - Manage project access for existing keys
185
+ - View usage statistics
186
+ - Deactivate or delete keys
187
+ - Monitor key activity
188
+
189
+ ## Support
190
+
191
+ - 🐛 Issues: [GitHub Issues](https://github.com/deifos/feedbackbasket-mcp/issues)
192
192
  - 📖 Docs: [FeedbackBasket Documentation](https://feedbackbasket.com/docs)
package/dist/client.d.ts CHANGED
@@ -1,24 +1,19 @@
1
1
  export declare class FeedbackBasketClient {
2
2
  private api;
3
3
  constructor(apiKey: string, baseUrl?: string);
4
- /**
5
- * List all projects accessible by the API key
6
- */
7
4
  listProjects(): Promise<{
8
5
  content: Array<{
9
6
  type: string;
10
7
  text: string;
11
8
  }>;
12
9
  }>;
13
- /**
14
- * Get feedback for projects
15
- */
16
10
  getFeedback(params?: {
17
11
  projectId?: string;
18
- category?: 'BUG' | 'FEATURE' | 'REVIEW';
19
- status?: 'PENDING' | 'REVIEWED' | 'DONE';
20
- sentiment?: 'POSITIVE' | 'NEGATIVE' | 'NEUTRAL';
12
+ category?: string;
13
+ status?: string;
14
+ sentiment?: string;
21
15
  limit?: number;
16
+ offset?: number;
22
17
  search?: string;
23
18
  includeNotes?: boolean;
24
19
  }): Promise<{
@@ -27,14 +22,12 @@ export declare class FeedbackBasketClient {
27
22
  text: string;
28
23
  }>;
29
24
  }>;
30
- /**
31
- * Get bug reports specifically
32
- */
33
25
  getBugReports(params?: {
34
26
  projectId?: string;
35
- status?: 'PENDING' | 'REVIEWED' | 'DONE';
27
+ status?: string;
36
28
  severity?: 'high' | 'medium' | 'low';
37
29
  limit?: number;
30
+ offset?: number;
38
31
  search?: string;
39
32
  includeNotes?: boolean;
40
33
  }): Promise<{
@@ -43,12 +36,9 @@ export declare class FeedbackBasketClient {
43
36
  text: string;
44
37
  }>;
45
38
  }>;
46
- /**
47
- * Search feedback across all accessible projects
48
- */
49
39
  searchFeedback(query: string, options?: {
50
40
  projectId?: string;
51
- category?: 'BUG' | 'FEATURE' | 'REVIEW';
41
+ category?: string;
52
42
  limit?: number;
53
43
  }): Promise<{
54
44
  content: Array<{
package/dist/client.js CHANGED
@@ -7,14 +7,11 @@ export class FeedbackBasketClient {
7
7
  headers: {
8
8
  'Authorization': `Bearer ${apiKey}`,
9
9
  'Content-Type': 'application/json',
10
- 'User-Agent': 'FeedbackBasket-MCP/1.0.0',
10
+ 'User-Agent': 'FeedbackBasket-MCP/2.0.0',
11
11
  },
12
- timeout: 30000, // 30 second timeout
12
+ timeout: 30000,
13
13
  });
14
14
  }
15
- /**
16
- * List all projects accessible by the API key
17
- */
18
15
  async listProjects() {
19
16
  try {
20
17
  const response = await this.api.post('/projects', {});
@@ -28,27 +25,21 @@ export class FeedbackBasketClient {
28
25
  };
29
26
  }
30
27
  const projectList = projects.map(project => {
31
- const totalFeedback = project.stats.totalFeedback;
32
- const pendingCount = project.stats.byStatus.PENDING;
33
- const bugCount = project.stats.byCategory.BUG;
28
+ const openCount = project.byStatus['OPEN'] || 0;
29
+ const bugCount = project.byCategory['BUG'] || 0;
34
30
  return [
35
31
  `**${project.name}**`,
32
+ ` ID: ${project.id}`,
36
33
  ` URL: ${project.url}`,
37
- ` Total Feedback: ${totalFeedback}`,
38
- ` Pending: ${pendingCount} | Bugs: ${bugCount}`,
39
- ` Created: ${new Date(project.createdAt).toLocaleDateString()}`,
34
+ ` Total Feedback: ${project.totalFeedback}`,
35
+ ` Open: ${openCount} | Bugs: ${bugCount}`,
40
36
  ''
41
37
  ].join('\n');
42
38
  }).join('\n');
43
- const summary = [
44
- `# FeedbackBasket Projects (${projects.length} total)\n`,
45
- projectList,
46
- `\n*API Key: ${response.data.apiKeyInfo.name} (${response.data.apiKeyInfo.usageCount} uses)*`
47
- ].join('\n');
48
39
  return {
49
40
  content: [{
50
41
  type: 'text',
51
- text: summary
42
+ text: `# FeedbackBasket Projects (${projects.length} total)\n\n${projectList}`
52
43
  }]
53
44
  };
54
45
  }
@@ -56,9 +47,6 @@ export class FeedbackBasketClient {
56
47
  throw this.handleError('Failed to fetch projects', error);
57
48
  }
58
49
  }
59
- /**
60
- * Get feedback for projects
61
- */
62
50
  async getFeedback(params = {}) {
63
51
  try {
64
52
  const response = await this.api.post('/feedback', {
@@ -69,7 +57,7 @@ export class FeedbackBasketClient {
69
57
  const feedback = response.data.feedback;
70
58
  if (feedback.length === 0) {
71
59
  const filters = Object.entries(params)
72
- .filter(([_, value]) => value !== undefined)
60
+ .filter(([, value]) => value !== undefined)
73
61
  .map(([key, value]) => `${key}: ${value}`)
74
62
  .join(', ');
75
63
  return {
@@ -82,29 +70,38 @@ export class FeedbackBasketClient {
82
70
  const feedbackList = feedback.map(item => {
83
71
  const category = item.category || 'UNCATEGORIZED';
84
72
  const sentiment = item.sentiment || 'UNKNOWN';
85
- const confidenceText = item.categoryConfidence
86
- ? ` (${Math.round(item.categoryConfidence * 100)}% confidence)`
73
+ const priority = item.aiPriorityScore != null
74
+ ? ` | Priority: ${item.aiPriorityScore >= 70 ? 'HIGH' : item.aiPriorityScore >= 40 ? 'MEDIUM' : 'LOW'} (${item.aiPriorityScore}/100)`
87
75
  : '';
88
- return [
89
- `**${category}${confidenceText} | ${sentiment} | ${item.status}**`,
76
+ const lines = [
77
+ `**${category} | ${sentiment} | ${item.status}${priority}**`,
90
78
  `Project: ${item.project.name}`,
91
- `Content: ${item.content.length > 100 ? item.content.substring(0, 100) + '...' : item.content}`,
92
- item.email ? `Email: ${item.email}` : '',
93
- item.notes && params.includeNotes ? `Notes: ${item.notes}` : '',
94
- `Created: ${new Date(item.createdAt).toLocaleDateString()}`,
95
- ''
96
- ].filter(Boolean).join('\n');
79
+ ];
80
+ if (item.aiSummary)
81
+ lines.push(`Summary: ${item.aiSummary}`);
82
+ lines.push(`Content: ${item.content.length > 150 ? item.content.substring(0, 150) + '...' : item.content}`);
83
+ if (item.email)
84
+ lines.push(`Email: ${item.email}`);
85
+ if (item.pageUrl)
86
+ lines.push(`Page: ${item.pageUrl}`);
87
+ if (item.browser || item.os)
88
+ lines.push(`Browser: ${[item.browser, item.os, item.device].filter(Boolean).join(' | ')}`);
89
+ if (item.notes && item.notes.length > 0) {
90
+ lines.push(`Notes: ${item.notes.map(n => `[${n.author.name}] ${n.content}`).join('; ')}`);
91
+ }
92
+ lines.push(`Created: ${new Date(item.createdAt).toLocaleDateString()}`);
93
+ lines.push('');
94
+ return lines.join('\n');
97
95
  }).join('\n');
98
- const summary = [
99
- `# Feedback Results (${feedback.length} of ${response.data.pagination.totalCount})\n`,
100
- feedbackList,
101
- response.data.pagination.hasMore ? `\n*Showing first ${feedback.length} results. Use offset parameter to get more.*` : '',
102
- `\n*API Key: ${response.data.apiKeyInfo.name}*`
103
- ].join('\n');
96
+ const { pagination } = response.data;
104
97
  return {
105
98
  content: [{
106
99
  type: 'text',
107
- text: summary
100
+ text: [
101
+ `# Feedback Results (${feedback.length} of ${pagination.totalCount})\n`,
102
+ feedbackList,
103
+ pagination.hasMore ? `\n*Showing ${feedback.length} results. Use offset: ${pagination.offset + pagination.limit} to get more.*` : '',
104
+ ].join('\n')
108
105
  }]
109
106
  };
110
107
  }
@@ -112,9 +109,6 @@ export class FeedbackBasketClient {
112
109
  throw this.handleError('Failed to fetch feedback', error);
113
110
  }
114
111
  }
115
- /**
116
- * Get bug reports specifically
117
- */
118
112
  async getBugReports(params = {}) {
119
113
  try {
120
114
  const response = await this.api.post('/feedback/bugs', {
@@ -125,7 +119,7 @@ export class FeedbackBasketClient {
125
119
  const bugReports = response.data.bugReports;
126
120
  if (bugReports.length === 0) {
127
121
  const filters = Object.entries(params)
128
- .filter(([_, value]) => value !== undefined)
122
+ .filter(([, value]) => value !== undefined)
129
123
  .map(([key, value]) => `${key}: ${value}`)
130
124
  .join(', ');
131
125
  return {
@@ -137,36 +131,46 @@ export class FeedbackBasketClient {
137
131
  }
138
132
  const bugList = bugReports.map(bug => {
139
133
  const severityEmoji = bug.severity === 'high' ? '🔴' : bug.severity === 'medium' ? '🟡' : '🟢';
140
- const statusEmoji = bug.status === 'PENDING' ? '⏳' : bug.status === 'REVIEWED' ? '👁️' : '✅';
141
- return [
134
+ const statusMap = {
135
+ OPEN: '⏳', UNDER_REVIEW: '👁️', PLANNED: '📋',
136
+ IN_PROGRESS: '🔨', COMPLETE: '✅', CLOSED: '🔒'
137
+ };
138
+ const statusEmoji = statusMap[bug.status] || '❓';
139
+ const lines = [
142
140
  `${severityEmoji} **${bug.severity.toUpperCase()} SEVERITY** ${statusEmoji} ${bug.status}`,
143
141
  `Project: ${bug.project.name}`,
144
- `Bug: ${bug.content.length > 150 ? bug.content.substring(0, 150) + '...' : bug.content}`,
145
- bug.email ? `Reported by: ${bug.email}` : '',
146
- bug.notes && params.includeNotes ? `Notes: ${bug.notes}` : '',
147
- `Reported: ${new Date(bug.createdAt).toLocaleDateString()}`,
148
- ''
149
- ].filter(Boolean).join('\n');
142
+ ];
143
+ if (bug.aiSummary)
144
+ lines.push(`Summary: ${bug.aiSummary}`);
145
+ lines.push(`Bug: ${bug.content.length > 150 ? bug.content.substring(0, 150) + '...' : bug.content}`);
146
+ if (bug.email)
147
+ lines.push(`Reported by: ${bug.email}`);
148
+ if (bug.pageUrl)
149
+ lines.push(`Page: ${bug.pageUrl}`);
150
+ if (bug.browser || bug.os)
151
+ lines.push(`Browser: ${[bug.browser, bug.os, bug.device].filter(Boolean).join(' | ')}`);
152
+ lines.push(`Reported: ${new Date(bug.createdAt).toLocaleDateString()}`);
153
+ lines.push('');
154
+ return lines.join('\n');
150
155
  }).join('\n');
151
156
  const stats = response.data.stats;
152
157
  const statsText = [
153
158
  `## Bug Statistics`,
154
- `Total Bugs: ${stats.totalBugs}`,
159
+ `Total Bugs: ${stats.total}`,
155
160
  `🔴 High: ${stats.bySeverity.high} | 🟡 Medium: ${stats.bySeverity.medium} | 🟢 Low: ${stats.bySeverity.low}`,
156
- `⏳ Pending: ${stats.byStatus.pending} | 👁️ Reviewed: ${stats.byStatus.reviewed} | Done: ${stats.byStatus.done}`,
161
+ `Open: ${stats.byStatus['OPEN'] || 0} | Under Review: ${stats.byStatus['UNDER_REVIEW'] || 0} | In Progress: ${stats.byStatus['IN_PROGRESS'] || 0} | Complete: ${stats.byStatus['COMPLETE'] || 0}`,
157
162
  ''
158
163
  ].join('\n');
159
- const summary = [
160
- `# Bug Reports (${bugReports.length} of ${response.data.pagination.totalCount})\n`,
161
- statsText,
162
- bugList,
163
- response.data.pagination.hasMore ? `\n*Showing first ${bugReports.length} results. Use offset parameter to get more.*` : '',
164
- `\n*API Key: ${response.data.apiKeyInfo.name}*`
165
- ].join('\n');
164
+ const { pagination } = response.data;
166
165
  return {
167
166
  content: [{
168
167
  type: 'text',
169
- text: summary
168
+ text: [
169
+ `# Bug Reports (${bugReports.length} of ${pagination.totalCount})\n`,
170
+ statsText,
171
+ bugList,
172
+ pagination.hasMore ? `\n*Showing ${bugReports.length} results. Use offset: ${pagination.offset + pagination.limit} to get more.*` : '',
173
+ ].join('\n')
170
174
  }]
171
175
  };
172
176
  }
@@ -174,9 +178,6 @@ export class FeedbackBasketClient {
174
178
  throw this.handleError('Failed to fetch bug reports', error);
175
179
  }
176
180
  }
177
- /**
178
- * Search feedback across all accessible projects
179
- */
180
181
  async searchFeedback(query, options = {}) {
181
182
  return this.getFeedback({
182
183
  search: query,
@@ -188,7 +189,7 @@ export class FeedbackBasketClient {
188
189
  handleError(message, error) {
189
190
  if (error instanceof AxiosError) {
190
191
  const status = error.response?.status;
191
- const responseMessage = error.response?.data?.message || error.message;
192
+ const responseMessage = error.response?.data?.error || error.response?.data?.message || error.message;
192
193
  if (status === 401) {
193
194
  return new Error(`Authentication failed: ${responseMessage}. Check your API key.`);
194
195
  }
@@ -196,7 +197,7 @@ export class FeedbackBasketClient {
196
197
  return new Error(`Access denied: ${responseMessage}. Check your API key permissions.`);
197
198
  }
198
199
  else if (status === 429) {
199
- return new Error(`Rate limit exceeded: ${responseMessage}. Please try again later.`);
200
+ return new Error(`Rate limit exceeded. Please try again later.`);
200
201
  }
201
202
  else {
202
203
  return new Error(`${message}: ${responseMessage} (HTTP ${status})`);
package/dist/index.js CHANGED
@@ -2,41 +2,49 @@
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
- import { program } from 'commander';
6
5
  import { FeedbackBasketClient } from './client.js';
7
- // Parse command line arguments
8
- program
9
- .option('--api-key <key>', 'FeedbackBasket API key')
10
- .option('--base-url <url>', 'Base URL for FeedbackBasket API', 'https://feedbackbasket.com')
11
- .parse();
12
- const options = program.opts();
6
+ function parseArgs(args) {
7
+ let apiKey;
8
+ let baseUrl;
9
+ for (let i = 0; i < args.length; i++) {
10
+ const arg = args[i] ?? '';
11
+ if (arg.startsWith('--api-key')) {
12
+ const eqIdx = arg.indexOf('=');
13
+ apiKey = eqIdx > 0 ? arg.slice(eqIdx + 1) : args[++i];
14
+ }
15
+ else if (arg.startsWith('--base-url')) {
16
+ const eqIdx = arg.indexOf('=');
17
+ baseUrl = eqIdx > 0 ? arg.slice(eqIdx + 1) : args[++i];
18
+ }
19
+ }
20
+ return { apiKey, baseUrl };
21
+ }
22
+ const options = parseArgs(process.argv.slice(2));
13
23
  const apiKey = options.apiKey || process.env.FEEDBACKBASKET_API_KEY;
24
+ const baseUrl = options.baseUrl || 'https://feedbackbasket.com';
14
25
  if (!apiKey) {
15
26
  console.error('Error: API key required.');
16
- console.error('Usage: Use --api-key option or set FEEDBACKBASKET_API_KEY environment variable');
27
+ console.error('Usage: --api-key <key> or set FEEDBACKBASKET_API_KEY env var');
17
28
  process.exit(1);
18
29
  }
19
- // Validate API key format
20
- if (!/^fb_key_[a-f0-9]{64}$/.test(apiKey)) {
21
- console.error('Error: Invalid API key format. API keys should start with "fb_key_" followed by 64 hexadecimal characters.');
30
+ if (!apiKey.startsWith('fb_key_')) {
31
+ console.error('Error: Invalid API key format. Keys should start with "fb_key_".');
22
32
  process.exit(1);
23
33
  }
24
- const client = new FeedbackBasketClient(apiKey, options.baseUrl);
25
- // Create MCP server
34
+ const client = new FeedbackBasketClient(apiKey, baseUrl);
26
35
  const server = new Server({
27
36
  name: 'feedbackbasket-mcp',
28
- version: '1.0.0',
37
+ version: '2.0.0',
29
38
  capabilities: {
30
39
  tools: {},
31
40
  },
32
41
  });
33
- // List available tools
34
42
  server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
35
43
  return {
36
44
  tools: [
37
45
  {
38
46
  name: 'list_projects',
39
- description: 'List all FeedbackBasket projects accessible by your API key with summary statistics',
47
+ description: 'List all FeedbackBasket projects accessible by your API key with summary statistics including feedback counts by status and category',
40
48
  inputSchema: {
41
49
  type: 'object',
42
50
  properties: {},
@@ -45,7 +53,7 @@ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
45
53
  },
46
54
  {
47
55
  name: 'get_feedback',
48
- description: 'Get feedback from your FeedbackBasket projects with filtering options',
56
+ description: 'Get feedback from your FeedbackBasket projects with filtering. Returns AI analysis (summary, priority, category, sentiment), page URL, browser info, and optional notes.',
49
57
  inputSchema: {
50
58
  type: 'object',
51
59
  properties: {
@@ -55,12 +63,12 @@ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
55
63
  },
56
64
  category: {
57
65
  type: 'string',
58
- enum: ['BUG', 'FEATURE', 'REVIEW'],
66
+ enum: ['BUG', 'FEATURE_REQUEST', 'IMPROVEMENT', 'QUESTION'],
59
67
  description: 'Filter by feedback category',
60
68
  },
61
69
  status: {
62
70
  type: 'string',
63
- enum: ['PENDING', 'REVIEWED', 'DONE'],
71
+ enum: ['OPEN', 'UNDER_REVIEW', 'PLANNED', 'IN_PROGRESS', 'COMPLETE', 'CLOSED'],
64
72
  description: 'Filter by feedback status',
65
73
  },
66
74
  sentiment: {
@@ -74,13 +82,18 @@ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
74
82
  },
75
83
  limit: {
76
84
  type: 'number',
77
- description: 'Maximum number of results to return (default: 20, max: 100)',
85
+ description: 'Maximum number of results (default: 20, max: 100)',
78
86
  minimum: 1,
79
87
  maximum: 100,
80
88
  },
89
+ offset: {
90
+ type: 'number',
91
+ description: 'Offset for pagination (default: 0)',
92
+ minimum: 0,
93
+ },
81
94
  includeNotes: {
82
95
  type: 'boolean',
83
- description: 'Include internal notes in the response (default: false)',
96
+ description: 'Include internal team notes (default: false)',
84
97
  },
85
98
  },
86
99
  additionalProperties: false,
@@ -88,7 +101,7 @@ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
88
101
  },
89
102
  {
90
103
  name: 'get_bug_reports',
91
- description: 'Get bug reports specifically from your FeedbackBasket projects',
104
+ description: 'Get bug reports from your FeedbackBasket projects with severity classification (high/medium/low based on sentiment) and statistics',
92
105
  inputSchema: {
93
106
  type: 'object',
94
107
  properties: {
@@ -98,27 +111,32 @@ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
98
111
  },
99
112
  status: {
100
113
  type: 'string',
101
- enum: ['PENDING', 'REVIEWED', 'DONE'],
114
+ enum: ['OPEN', 'UNDER_REVIEW', 'PLANNED', 'IN_PROGRESS', 'COMPLETE', 'CLOSED'],
102
115
  description: 'Filter by bug status',
103
116
  },
104
117
  severity: {
105
118
  type: 'string',
106
119
  enum: ['high', 'medium', 'low'],
107
- description: 'Filter by computed severity (based on sentiment: negative=high, neutral=medium, positive=low)',
120
+ description: 'Filter by severity (high=negative sentiment, medium=neutral, low=positive)',
108
121
  },
109
122
  search: {
110
123
  type: 'string',
111
- description: 'Search bug report content for specific text',
124
+ description: 'Search bug report content',
112
125
  },
113
126
  limit: {
114
127
  type: 'number',
115
- description: 'Maximum number of results to return (default: 20, max: 100)',
128
+ description: 'Maximum number of results (default: 20, max: 100)',
116
129
  minimum: 1,
117
130
  maximum: 100,
118
131
  },
132
+ offset: {
133
+ type: 'number',
134
+ description: 'Offset for pagination',
135
+ minimum: 0,
136
+ },
119
137
  includeNotes: {
120
138
  type: 'boolean',
121
- description: 'Include internal notes in the response (default: false)',
139
+ description: 'Include internal team notes (default: false)',
122
140
  },
123
141
  },
124
142
  additionalProperties: false,
@@ -126,21 +144,21 @@ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
126
144
  },
127
145
  {
128
146
  name: 'search_feedback',
129
- description: 'Search for feedback across all accessible projects using text search',
147
+ description: 'Search for feedback across all accessible projects using text search. Useful for finding specific issues or topics.',
130
148
  inputSchema: {
131
149
  type: 'object',
132
150
  properties: {
133
151
  query: {
134
152
  type: 'string',
135
- description: 'Search query to find in feedback content',
153
+ description: 'Search text to find in feedback content',
136
154
  },
137
155
  projectId: {
138
156
  type: 'string',
139
- description: 'Limit search to specific project',
157
+ description: 'Limit search to a specific project',
140
158
  },
141
159
  category: {
142
160
  type: 'string',
143
- enum: ['BUG', 'FEATURE', 'REVIEW'],
161
+ enum: ['BUG', 'FEATURE_REQUEST', 'IMPROVEMENT', 'QUESTION'],
144
162
  description: 'Filter search results by category',
145
163
  },
146
164
  limit: {
@@ -157,7 +175,6 @@ server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
157
175
  ],
158
176
  };
159
177
  });
160
- // Handle tool calls
161
178
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
162
179
  const { name, arguments: args } = request.params;
163
180
  try {
@@ -172,17 +189,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
172
189
  if (!args || typeof args !== 'object' || !('query' in args) || typeof args.query !== 'string') {
173
190
  throw new Error('Search query is required');
174
191
  }
175
- const searchOptions = {};
176
- if (typeof args.projectId === 'string') {
177
- searchOptions.projectId = args.projectId;
178
- }
179
- if (typeof args.category === 'string' && ['BUG', 'FEATURE', 'REVIEW'].includes(args.category)) {
180
- searchOptions.category = args.category;
181
- }
182
- if (typeof args.limit === 'number') {
183
- searchOptions.limit = args.limit;
184
- }
185
- return await client.searchFeedback(args.query, searchOptions);
192
+ const searchOpts = {};
193
+ if (typeof args.projectId === 'string')
194
+ searchOpts.projectId = args.projectId;
195
+ if (typeof args.category === 'string')
196
+ searchOpts.category = args.category;
197
+ if (typeof args.limit === 'number')
198
+ searchOpts.limit = args.limit;
199
+ return await client.searchFeedback(args.query, searchOpts);
186
200
  default:
187
201
  throw new Error(`Unknown tool: ${name}`);
188
202
  }
@@ -190,41 +204,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
190
204
  catch (error) {
191
205
  const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
192
206
  return {
193
- content: [
194
- {
195
- type: 'text',
196
- text: `Error: ${errorMessage}`,
197
- },
198
- ],
207
+ content: [{ type: 'text', text: `Error: ${errorMessage}` }],
199
208
  isError: true,
200
209
  };
201
210
  }
202
211
  });
203
- // Start the server
204
212
  async function main() {
205
213
  try {
206
214
  const transport = new StdioServerTransport();
207
215
  await server.connect(transport);
208
- // Server is now running and will handle requests via stdio
209
- console.error('FeedbackBasket MCP server started successfully');
216
+ console.error('FeedbackBasket MCP server v2.0.0 started');
210
217
  }
211
218
  catch (error) {
212
219
  console.error('Failed to start MCP server:', error);
213
220
  process.exit(1);
214
221
  }
215
222
  }
216
- // Handle graceful shutdown
217
- process.on('SIGINT', () => {
218
- console.error('Received SIGINT, shutting down gracefully...');
219
- process.exit(0);
220
- });
221
- process.on('SIGTERM', () => {
222
- console.error('Received SIGTERM, shutting down gracefully...');
223
- process.exit(0);
223
+ process.on('SIGINT', () => process.exit(0));
224
+ process.on('SIGTERM', () => process.exit(0));
225
+ main().catch((error) => {
226
+ console.error('Fatal error:', error);
227
+ process.exit(1);
224
228
  });
225
- if (import.meta.url === `file://${process.argv[1]}`) {
226
- main().catch((error) => {
227
- console.error('Fatal error:', error);
228
- process.exit(1);
229
- });
230
- }
package/dist/types.d.ts CHANGED
@@ -4,62 +4,45 @@ export interface Project {
4
4
  url: string;
5
5
  description?: string;
6
6
  createdAt: string;
7
- updatedAt: string;
8
- stats: {
9
- totalFeedback: number;
10
- byCategory: {
11
- BUG: number;
12
- FEATURE: number;
13
- REVIEW: number;
14
- UNKNOWN: number;
15
- };
16
- byStatus: {
17
- PENDING: number;
18
- REVIEWED: number;
19
- DONE: number;
20
- };
21
- };
7
+ totalFeedback: number;
8
+ byStatus: Record<string, number>;
9
+ byCategory: Record<string, number>;
22
10
  }
23
11
  export interface Feedback {
24
12
  id: string;
25
13
  content: string;
26
- email?: string;
27
- status: 'PENDING' | 'REVIEWED' | 'DONE';
28
- notes?: string;
29
- category?: 'BUG' | 'FEATURE' | 'REVIEW';
30
- categorySource: 'manual' | 'ai';
31
- categoryConfidence?: number;
32
- sentiment?: 'POSITIVE' | 'NEGATIVE' | 'NEUTRAL';
33
- sentimentSource: 'manual' | 'ai';
34
- sentimentConfidence?: number;
35
- isAiAnalyzed: boolean;
36
- aiAnalyzedAt?: string;
37
- aiReasoning?: string;
14
+ email?: string | null;
15
+ status: 'OPEN' | 'UNDER_REVIEW' | 'PLANNED' | 'IN_PROGRESS' | 'COMPLETE' | 'CLOSED';
16
+ category?: 'BUG' | 'FEATURE_REQUEST' | 'IMPROVEMENT' | 'QUESTION' | null;
17
+ sentiment?: 'POSITIVE' | 'NEGATIVE' | 'NEUTRAL' | null;
18
+ aiSummary?: string | null;
19
+ aiPriorityScore?: number | null;
20
+ reasoning?: string | null;
21
+ pageUrl?: string | null;
22
+ browser?: string | null;
23
+ os?: string | null;
24
+ device?: string | null;
25
+ language?: string | null;
38
26
  project: {
39
27
  id: string;
40
28
  name: string;
41
- url: string;
42
29
  };
30
+ notes?: Array<{
31
+ id: string;
32
+ content: string;
33
+ createdAt: string;
34
+ author: {
35
+ name: string;
36
+ };
37
+ }>;
43
38
  createdAt: string;
44
- updatedAt: string;
45
39
  }
46
40
  export interface BugReport extends Feedback {
47
41
  severity: 'high' | 'medium' | 'low';
48
42
  }
49
- export interface FeedbackBasketResponse<T> {
50
- data?: T;
51
- error?: {
52
- message: string;
53
- code?: string;
54
- };
55
- }
56
43
  export interface ProjectsResponse {
57
44
  projects: Project[];
58
45
  totalProjects: number;
59
- apiKeyInfo: {
60
- name: string;
61
- usageCount: number;
62
- };
63
46
  }
64
47
  export interface FeedbackResponse {
65
48
  feedback: Feedback[];
@@ -68,50 +51,23 @@ export interface FeedbackResponse {
68
51
  limit: number;
69
52
  offset: number;
70
53
  hasMore: boolean;
71
- nextOffset: number | null;
72
- };
73
- filters: {
74
- projectId?: string;
75
- category?: string;
76
- status?: string;
77
- sentiment?: string;
78
- search?: string;
79
- };
80
- apiKeyInfo: {
81
- name: string;
82
- usageCount: number;
83
54
  };
84
55
  }
85
56
  export interface BugReportsResponse {
86
57
  bugReports: BugReport[];
87
58
  stats: {
88
- totalBugs: number;
59
+ total: number;
89
60
  bySeverity: {
90
61
  high: number;
91
62
  medium: number;
92
63
  low: number;
93
64
  };
94
- byStatus: {
95
- pending: number;
96
- reviewed: number;
97
- done: number;
98
- };
65
+ byStatus: Record<string, number>;
99
66
  };
100
67
  pagination: {
101
68
  totalCount: number;
102
69
  limit: number;
103
70
  offset: number;
104
71
  hasMore: boolean;
105
- nextOffset: number | null;
106
- };
107
- filters: {
108
- projectId?: string;
109
- status?: string;
110
- severity?: string;
111
- search?: string;
112
- };
113
- apiKeyInfo: {
114
- name: string;
115
- usageCount: number;
116
72
  };
117
73
  }
package/dist/types.js CHANGED
@@ -1,2 +1,2 @@
1
- // Shared types for the MCP server based on actual database schema
1
+ // Shared types for the MCP server aligned with FeedbackBasket v3
2
2
  export {};
package/package.json CHANGED
@@ -1,49 +1,48 @@
1
- {
2
- "name": "feedbackbasket-mcp-server",
3
- "version": "1.0.0",
4
- "description": "MCP server for FeedbackBasket project data access",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "bin": {
8
- "feedbackbasket-mcp-server": "dist/index.js"
9
- },
10
- "scripts": {
11
- "build": "tsc",
12
- "start": "node dist/index.js",
13
- "dev": "tsx src/index.ts",
14
- "prepublishOnly": "npm run build"
15
- },
16
- "keywords": [
17
- "mcp",
18
- "feedbackbasket",
19
- "feedback",
20
- "projects",
21
- "ai-assistant",
22
- "claude-desktop",
23
- "cursor"
24
- ],
25
- "author": "deifosv",
26
- "license": "MIT",
27
- "repository": {
28
- "type": "git",
29
- "url": "https://github.com/deifos/feedbackbasket-mcp.git"
30
- },
31
- "homepage": "https://feedbackbasket.com",
32
- "bugs": {
33
- "url": "https://github.com/deifos/feedbackbasket-mcp/issues"
34
- },
35
- "dependencies": {
36
- "@modelcontextprotocol/sdk": "^0.4.0",
37
- "axios": "^1.6.0",
38
- "commander": "^11.0.0"
39
- },
40
- "devDependencies": {
41
- "@types/node": "^20.0.0",
42
- "typescript": "^5.0.0",
43
- "tsx": "^4.0.0"
44
- },
45
- "files": [
46
- "dist/**/*",
47
- "README.md"
48
- ]
49
- }
1
+ {
2
+ "name": "feedbackbasket-mcp-server",
3
+ "version": "2.0.0",
4
+ "description": "MCP server for FeedbackBasket project data access",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "feedbackbasket-mcp-server": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "node node_modules/typescript/bin/tsc",
12
+ "start": "node dist/index.js",
13
+ "dev": "tsx src/index.ts",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "feedbackbasket",
19
+ "feedback",
20
+ "projects",
21
+ "ai-assistant",
22
+ "claude-desktop",
23
+ "cursor"
24
+ ],
25
+ "author": "deifosv",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/deifos/feedbackbasket-mcp.git"
30
+ },
31
+ "homepage": "https://feedbackbasket.com",
32
+ "bugs": {
33
+ "url": "https://github.com/deifos/feedbackbasket-mcp/issues"
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^0.4.0",
37
+ "axios": "^1.6.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^20.0.0",
41
+ "typescript": "^5.0.0",
42
+ "tsx": "^4.0.0"
43
+ },
44
+ "files": [
45
+ "dist/**/*",
46
+ "README.md"
47
+ ]
48
+ }