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 +191 -191
- package/dist/client.d.ts +7 -17
- package/dist/client.js +66 -65
- package/dist/index.js +64 -66
- package/dist/types.d.ts +25 -69
- package/dist/types.js +1 -1
- package/package.json +48 -49
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?:
|
|
19
|
-
status?:
|
|
20
|
-
sentiment?:
|
|
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?:
|
|
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?:
|
|
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/
|
|
10
|
+
'User-Agent': 'FeedbackBasket-MCP/2.0.0',
|
|
11
11
|
},
|
|
12
|
-
timeout: 30000,
|
|
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
|
|
32
|
-
const
|
|
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
|
-
`
|
|
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:
|
|
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(([
|
|
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
|
|
86
|
-
? `
|
|
73
|
+
const priority = item.aiPriorityScore != null
|
|
74
|
+
? ` | Priority: ${item.aiPriorityScore >= 70 ? 'HIGH' : item.aiPriorityScore >= 40 ? 'MEDIUM' : 'LOW'} (${item.aiPriorityScore}/100)`
|
|
87
75
|
: '';
|
|
88
|
-
|
|
89
|
-
`**${category}
|
|
76
|
+
const lines = [
|
|
77
|
+
`**${category} | ${sentiment} | ${item.status}${priority}**`,
|
|
90
78
|
`Project: ${item.project.name}`,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
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:
|
|
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(([
|
|
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
|
|
141
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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.
|
|
159
|
+
`Total Bugs: ${stats.total}`,
|
|
155
160
|
`🔴 High: ${stats.bySeverity.high} | 🟡 Medium: ${stats.bySeverity.medium} | 🟢 Low: ${stats.bySeverity.low}`,
|
|
156
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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:
|
|
27
|
+
console.error('Usage: --api-key <key> or set FEEDBACKBASKET_API_KEY env var');
|
|
17
28
|
process.exit(1);
|
|
18
29
|
}
|
|
19
|
-
|
|
20
|
-
|
|
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,
|
|
25
|
-
// Create MCP server
|
|
34
|
+
const client = new FeedbackBasketClient(apiKey, baseUrl);
|
|
26
35
|
const server = new Server({
|
|
27
36
|
name: 'feedbackbasket-mcp',
|
|
28
|
-
version: '
|
|
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
|
|
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', '
|
|
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: ['
|
|
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
|
|
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
|
|
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
|
|
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: ['
|
|
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
|
|
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
|
|
124
|
+
description: 'Search bug report content',
|
|
112
125
|
},
|
|
113
126
|
limit: {
|
|
114
127
|
type: 'number',
|
|
115
|
-
description: 'Maximum number of results
|
|
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
|
|
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
|
|
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', '
|
|
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
|
|
176
|
-
if (typeof args.projectId === 'string')
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
217
|
-
process.on('
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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: '
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
|
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": "
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
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
|
+
}
|