@centry-digital/bukku-mcp 1.1.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/build/config/env.d.ts +1 -0
- package/build/config/env.d.ts.map +1 -0
- package/build/config/env.js +2 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +1813 -44
- package/build/tools/custom/account-tools.d.ts +2 -1
- package/build/tools/custom/account-tools.d.ts.map +1 -0
- package/build/tools/custom/account-tools.js +2 -2
- package/build/tools/custom/contact-archive.d.ts +2 -1
- package/build/tools/custom/contact-archive.d.ts.map +1 -0
- package/build/tools/custom/contact-archive.js +2 -2
- package/build/tools/custom/control-panel-archive.d.ts +2 -1
- package/build/tools/custom/control-panel-archive.d.ts.map +1 -0
- package/build/tools/custom/control-panel-archive.js +2 -2
- package/build/tools/custom/file-upload.d.ts +2 -1
- package/build/tools/custom/file-upload.d.ts.map +1 -0
- package/build/tools/custom/file-upload.js +2 -2
- package/build/tools/custom/journal-entry-tools.d.ts +2 -1
- package/build/tools/custom/journal-entry-tools.d.ts.map +1 -0
- package/build/tools/custom/journal-entry-tools.js +2 -3
- package/build/tools/custom/location-tools.d.ts +2 -1
- package/build/tools/custom/location-tools.d.ts.map +1 -0
- package/build/tools/custom/location-tools.js +2 -2
- package/build/tools/custom/product-archive.d.ts +2 -1
- package/build/tools/custom/product-archive.d.ts.map +1 -0
- package/build/tools/custom/product-archive.js +2 -2
- package/build/tools/custom/reference-data.d.ts +3 -2
- package/build/tools/custom/reference-data.d.ts.map +1 -0
- package/build/tools/custom/reference-data.js +2 -2
- package/build/tools/factory.d.ts +2 -2
- package/build/tools/factory.d.ts.map +1 -0
- package/build/tools/factory.js +2 -2
- package/build/tools/registry.d.ts +2 -1
- package/build/tools/registry.d.ts.map +1 -0
- package/build/tools/registry.js +3 -41
- package/package.json +9 -22
- package/LICENSE +0 -21
- package/README.md +0 -269
- package/build/client/bukku-client.d.ts +0 -62
- package/build/client/bukku-client.js +0 -195
- package/build/errors/transform.d.ts +0 -14
- package/build/errors/transform.js +0 -141
- package/build/errors/transform.test.d.ts +0 -1
- package/build/errors/transform.test.js +0 -101
- package/build/tools/cache/reference-cache.d.ts +0 -42
- package/build/tools/cache/reference-cache.js +0 -63
- package/build/tools/configs/account.d.ts +0 -17
- package/build/tools/configs/account.js +0 -28
- package/build/tools/configs/bank-money-in.d.ts +0 -10
- package/build/tools/configs/bank-money-in.js +0 -22
- package/build/tools/configs/bank-money-out.d.ts +0 -10
- package/build/tools/configs/bank-money-out.js +0 -22
- package/build/tools/configs/bank-transfer.d.ts +0 -11
- package/build/tools/configs/bank-transfer.js +0 -23
- package/build/tools/configs/contact-group.d.ts +0 -11
- package/build/tools/configs/contact-group.js +0 -19
- package/build/tools/configs/contact.d.ts +0 -14
- package/build/tools/configs/contact.js +0 -25
- package/build/tools/configs/delivery-order.d.ts +0 -8
- package/build/tools/configs/delivery-order.js +0 -20
- package/build/tools/configs/file.d.ts +0 -18
- package/build/tools/configs/file.js +0 -26
- package/build/tools/configs/goods-received-note.d.ts +0 -8
- package/build/tools/configs/goods-received-note.js +0 -20
- package/build/tools/configs/journal-entry.d.ts +0 -14
- package/build/tools/configs/journal-entry.js +0 -26
- package/build/tools/configs/location.d.ts +0 -20
- package/build/tools/configs/location.js +0 -28
- package/build/tools/configs/product-bundle.d.ts +0 -18
- package/build/tools/configs/product-bundle.js +0 -29
- package/build/tools/configs/product-group.d.ts +0 -14
- package/build/tools/configs/product-group.js +0 -22
- package/build/tools/configs/product.d.ts +0 -24
- package/build/tools/configs/product.js +0 -35
- package/build/tools/configs/purchase-bill.d.ts +0 -9
- package/build/tools/configs/purchase-bill.js +0 -21
- package/build/tools/configs/purchase-credit-note.d.ts +0 -8
- package/build/tools/configs/purchase-credit-note.js +0 -20
- package/build/tools/configs/purchase-order.d.ts +0 -8
- package/build/tools/configs/purchase-order.js +0 -20
- package/build/tools/configs/purchase-payment.d.ts +0 -8
- package/build/tools/configs/purchase-payment.js +0 -20
- package/build/tools/configs/purchase-refund.d.ts +0 -8
- package/build/tools/configs/purchase-refund.js +0 -20
- package/build/tools/configs/sales-credit-note.d.ts +0 -8
- package/build/tools/configs/sales-credit-note.js +0 -20
- package/build/tools/configs/sales-invoice.d.ts +0 -8
- package/build/tools/configs/sales-invoice.js +0 -20
- package/build/tools/configs/sales-order.d.ts +0 -8
- package/build/tools/configs/sales-order.js +0 -20
- package/build/tools/configs/sales-payment.d.ts +0 -8
- package/build/tools/configs/sales-payment.js +0 -20
- package/build/tools/configs/sales-quote.d.ts +0 -8
- package/build/tools/configs/sales-quote.js +0 -20
- package/build/tools/configs/sales-refund.d.ts +0 -8
- package/build/tools/configs/sales-refund.js +0 -20
- package/build/tools/configs/tag-group.d.ts +0 -11
- package/build/tools/configs/tag-group.js +0 -22
- package/build/tools/configs/tag.d.ts +0 -11
- package/build/tools/configs/tag.js +0 -22
- package/build/tools/validation/double-entry.d.ts +0 -46
- package/build/tools/validation/double-entry.js +0 -66
- package/build/types/api-responses.d.ts +0 -21
- package/build/types/api-responses.js +0 -6
- package/build/types/bukku.d.ts +0 -93
- package/build/types/bukku.js +0 -11
- package/build/utils/logger.d.ts +0 -6
- package/build/utils/logger.js +0 -8
package/README.md
DELETED
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
# Bukku MCP Server
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@centry-digital/bukku-mcp)
|
|
4
|
-
|
|
5
|
-
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that connects AI assistants to [Bukku](https://bukku.my), a Malaysian accounting platform. This gives your AI the ability to read, create, and manage your accounting data — invoices, bills, payments, contacts, products, and more.
|
|
6
|
-
|
|
7
|
-
## What can it do?
|
|
8
|
-
|
|
9
|
-
With this MCP server connected, you can ask your AI things like:
|
|
10
|
-
|
|
11
|
-
- "List my unpaid sales invoices"
|
|
12
|
-
- "Create an invoice for RM 5,000 to Acme Corp for consulting services"
|
|
13
|
-
- "Show me all purchase bills from last month"
|
|
14
|
-
- "Record a bank transfer of RM 10,000 from Maybank to CIMB"
|
|
15
|
-
- "Create a new contact for my supplier"
|
|
16
|
-
- "Upload this receipt and attach it to the purchase bill"
|
|
17
|
-
|
|
18
|
-
The server exposes **173 tools** covering the full Bukku API:
|
|
19
|
-
|
|
20
|
-
| Category | Tools | What you can do |
|
|
21
|
-
|----------|-------|-----------------|
|
|
22
|
-
| **Sales** | 42 | Quotes, orders, delivery orders, invoices, credit notes, payments, refunds |
|
|
23
|
-
| **Purchases** | 36 | Purchase orders, bills, credit notes, goods received notes, payments, refunds |
|
|
24
|
-
| **Banking** | 18 | Money in, money out, bank transfers |
|
|
25
|
-
| **Contacts** | 12 | Customers, suppliers, contact groups |
|
|
26
|
-
| **Products** | 18 | Products, product bundles, product groups |
|
|
27
|
-
| **Accounting** | 13 | Journal entries, chart of accounts |
|
|
28
|
-
| **Files** | 3 | Upload and manage file attachments |
|
|
29
|
-
| **Organisation** | 21 | Locations, tags, tag groups |
|
|
30
|
-
| **Reference Data** | 10 | Tax codes, currencies, payment methods, terms, and more |
|
|
31
|
-
|
|
32
|
-
## Quick Start
|
|
33
|
-
|
|
34
|
-
Get up and running in under 2 minutes.
|
|
35
|
-
|
|
36
|
-
### Prerequisites
|
|
37
|
-
|
|
38
|
-
- [Node.js](https://nodejs.org) v18 or later
|
|
39
|
-
- A [Bukku](https://bukku.my) account with API access enabled
|
|
40
|
-
- An AI client that supports MCP (e.g. [Claude Desktop](https://claude.ai/download), [Claude Code](https://docs.anthropic.com/en/docs/claude-code))
|
|
41
|
-
|
|
42
|
-
### Step 1: Get your Bukku API token
|
|
43
|
-
|
|
44
|
-
1. Log into your Bukku account
|
|
45
|
-
2. Go to **Control Panel > Integrations > API Access**
|
|
46
|
-
3. Generate a new API token (or copy your existing one)
|
|
47
|
-
4. Note your company subdomain — e.g. `mycompany` from `mycompany.bukku.my`
|
|
48
|
-
|
|
49
|
-
### Step 2: Add to your AI client
|
|
50
|
-
|
|
51
|
-
For Claude Desktop, open your config file:
|
|
52
|
-
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
53
|
-
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
54
|
-
|
|
55
|
-
Add this configuration:
|
|
56
|
-
|
|
57
|
-
```json
|
|
58
|
-
{
|
|
59
|
-
"mcpServers": {
|
|
60
|
-
"bukku": {
|
|
61
|
-
"command": "npx",
|
|
62
|
-
"args": ["-y", "@centry-digital/bukku-mcp"],
|
|
63
|
-
"env": {
|
|
64
|
-
"BUKKU_API_TOKEN": "your-token-here",
|
|
65
|
-
"BUKKU_COMPANY_SUBDOMAIN": "your-subdomain"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Step 3: Restart your AI client
|
|
73
|
-
|
|
74
|
-
Quit and reopen Claude Desktop. That's it — you're ready to go!
|
|
75
|
-
|
|
76
|
-
## Installation
|
|
77
|
-
|
|
78
|
-
### npx (recommended)
|
|
79
|
-
|
|
80
|
-
The quickest way to use the server is with `npx`. No installation needed — it downloads and runs the latest version automatically:
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
npx @centry-digital/bukku-mcp
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
This is what the Quick Start configuration uses. `npx` ensures you're always running the latest version without manual updates.
|
|
87
|
-
|
|
88
|
-
### npm global install
|
|
89
|
-
|
|
90
|
-
If you prefer a persistent installation:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
npm install -g @centry-digital/bukku-mcp
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Then update your AI client configuration to use the installed command instead:
|
|
97
|
-
|
|
98
|
-
```json
|
|
99
|
-
{
|
|
100
|
-
"mcpServers": {
|
|
101
|
-
"bukku": {
|
|
102
|
-
"command": "bukku-mcp",
|
|
103
|
-
"env": {
|
|
104
|
-
"BUKKU_API_TOKEN": "your-token-here",
|
|
105
|
-
"BUKKU_COMPANY_SUBDOMAIN": "your-subdomain"
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Configuration
|
|
113
|
-
|
|
114
|
-
### Environment Variables
|
|
115
|
-
|
|
116
|
-
| Variable | Required | Description |
|
|
117
|
-
|----------|----------|-------------|
|
|
118
|
-
| `BUKKU_API_TOKEN` | Yes | Your Bukku API token from Control Panel > Integrations > API Access |
|
|
119
|
-
| `BUKKU_COMPANY_SUBDOMAIN` | Yes | Your company subdomain (e.g. `mycompany` from `mycompany.bukku.my`) |
|
|
120
|
-
|
|
121
|
-
### Claude Desktop
|
|
122
|
-
|
|
123
|
-
Open your configuration file:
|
|
124
|
-
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
125
|
-
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
126
|
-
|
|
127
|
-
**Using npx (recommended):**
|
|
128
|
-
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"mcpServers": {
|
|
132
|
-
"bukku": {
|
|
133
|
-
"command": "npx",
|
|
134
|
-
"args": ["-y", "@centry-digital/bukku-mcp"],
|
|
135
|
-
"env": {
|
|
136
|
-
"BUKKU_API_TOKEN": "your-token-here",
|
|
137
|
-
"BUKKU_COMPANY_SUBDOMAIN": "your-subdomain"
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
**If you installed globally:**
|
|
145
|
-
|
|
146
|
-
```json
|
|
147
|
-
{
|
|
148
|
-
"mcpServers": {
|
|
149
|
-
"bukku": {
|
|
150
|
-
"command": "bukku-mcp",
|
|
151
|
-
"env": {
|
|
152
|
-
"BUKKU_API_TOKEN": "your-token-here",
|
|
153
|
-
"BUKKU_COMPANY_SUBDOMAIN": "your-subdomain"
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
After updating the config, restart Claude Desktop.
|
|
161
|
-
|
|
162
|
-
### Claude Code
|
|
163
|
-
|
|
164
|
-
Add to `.claude/settings.json` in your home directory or project:
|
|
165
|
-
|
|
166
|
-
**Using npx (recommended):**
|
|
167
|
-
|
|
168
|
-
```json
|
|
169
|
-
{
|
|
170
|
-
"mcpServers": {
|
|
171
|
-
"bukku": {
|
|
172
|
-
"command": "npx",
|
|
173
|
-
"args": ["-y", "@centry-digital/bukku-mcp"],
|
|
174
|
-
"env": {
|
|
175
|
-
"BUKKU_API_TOKEN": "your-token-here",
|
|
176
|
-
"BUKKU_COMPANY_SUBDOMAIN": "your-subdomain"
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
**If you installed globally:**
|
|
184
|
-
|
|
185
|
-
```json
|
|
186
|
-
{
|
|
187
|
-
"mcpServers": {
|
|
188
|
-
"bukku": {
|
|
189
|
-
"command": "bukku-mcp",
|
|
190
|
-
"env": {
|
|
191
|
-
"BUKKU_API_TOKEN": "your-token-here",
|
|
192
|
-
"BUKKU_COMPANY_SUBDOMAIN": "your-subdomain"
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Other MCP Clients
|
|
200
|
-
|
|
201
|
-
Any client that supports the [MCP stdio transport](https://modelcontextprotocol.io/docs/concepts/transports) can use this server. Use the `npx @centry-digital/bukku-mcp` command with the two environment variables shown above.
|
|
202
|
-
|
|
203
|
-
## Troubleshooting
|
|
204
|
-
|
|
205
|
-
**"Configuration Error" on startup**
|
|
206
|
-
- Check that both `BUKKU_API_TOKEN` and `BUKKU_COMPANY_SUBDOMAIN` are set in your client config
|
|
207
|
-
- Verify the environment variables are inside the `"env"` object
|
|
208
|
-
|
|
209
|
-
**"Token validation failed"**
|
|
210
|
-
- Your API token may be invalid or expired
|
|
211
|
-
- Log into Bukku and regenerate your token at Control Panel > Integrations > API Access
|
|
212
|
-
|
|
213
|
-
**Server doesn't appear in your AI client**
|
|
214
|
-
- Verify your configuration JSON syntax is correct (no trailing commas)
|
|
215
|
-
- Make sure you've restarted your AI client after editing the config
|
|
216
|
-
- Check that Node.js v18 or later is installed: `node --version`
|
|
217
|
-
|
|
218
|
-
**"Could not resolve package" with npx**
|
|
219
|
-
- Check that you have Node.js v18 or later installed
|
|
220
|
-
- Verify your network connection and proxy settings if applicable
|
|
221
|
-
- Try running `npm view @centry-digital/bukku-mcp` to confirm the package is accessible
|
|
222
|
-
|
|
223
|
-
**Permission errors with global install**
|
|
224
|
-
- Consider using `npx` instead (no installation needed)
|
|
225
|
-
- Or fix npm permissions: [https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally)
|
|
226
|
-
|
|
227
|
-
## Development
|
|
228
|
-
|
|
229
|
-
Want to contribute or run from source? Here's how to set up your development environment.
|
|
230
|
-
|
|
231
|
-
### Clone and build
|
|
232
|
-
|
|
233
|
-
```bash
|
|
234
|
-
git clone https://github.com/centry-digital/bukku-mcp.git
|
|
235
|
-
cd bukku-mcp
|
|
236
|
-
npm install
|
|
237
|
-
npm run build
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Commands
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
npm run build # Compile TypeScript
|
|
244
|
-
npm test # Run tests
|
|
245
|
-
npm start # Start server (requires env vars)
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### Project structure
|
|
249
|
-
|
|
250
|
-
```
|
|
251
|
-
src/
|
|
252
|
-
├── client/ # Bukku API HTTP client
|
|
253
|
-
├── config/ # Environment validation
|
|
254
|
-
├── errors/ # Error handling and transformation
|
|
255
|
-
├── tools/ # MCP tool definitions (one folder per category)
|
|
256
|
-
│ ├── sales/
|
|
257
|
-
│ ├── purchases/
|
|
258
|
-
│ ├── banking/
|
|
259
|
-
│ ├── contacts/
|
|
260
|
-
│ ├── products/
|
|
261
|
-
│ ├── accounting/
|
|
262
|
-
│ ├── files/
|
|
263
|
-
│ └── ...
|
|
264
|
-
└── index.ts # Server entry point
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
## License
|
|
268
|
-
|
|
269
|
-
MIT
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { Env } from "../config/env.js";
|
|
2
|
-
/**
|
|
3
|
-
* HTTP client for Bukku API.
|
|
4
|
-
* Handles authentication via Bearer token and Company-Subdomain header.
|
|
5
|
-
* Base URL: https://api.bukku.my
|
|
6
|
-
*/
|
|
7
|
-
export declare class BukkuClient {
|
|
8
|
-
private readonly baseUrl;
|
|
9
|
-
private readonly token;
|
|
10
|
-
private readonly subdomain;
|
|
11
|
-
constructor(env: Env);
|
|
12
|
-
/**
|
|
13
|
-
* Build headers for all requests.
|
|
14
|
-
* CRITICAL: Never log the actual token value - use "Bearer ***" for debugging.
|
|
15
|
-
*/
|
|
16
|
-
private getHeaders;
|
|
17
|
-
/**
|
|
18
|
-
* Map file extensions to MIME types for common file types.
|
|
19
|
-
* Returns null for unknown extensions.
|
|
20
|
-
*/
|
|
21
|
-
private getMimeType;
|
|
22
|
-
/**
|
|
23
|
-
* Build URL with query parameters.
|
|
24
|
-
*/
|
|
25
|
-
private buildUrl;
|
|
26
|
-
/**
|
|
27
|
-
* GET request with optional query parameters.
|
|
28
|
-
*/
|
|
29
|
-
get(path: string, params?: Record<string, string | number | undefined>): Promise<unknown>;
|
|
30
|
-
/**
|
|
31
|
-
* POST request with JSON body.
|
|
32
|
-
*/
|
|
33
|
-
post(path: string, body: unknown): Promise<unknown>;
|
|
34
|
-
/**
|
|
35
|
-
* PUT request with JSON body.
|
|
36
|
-
*/
|
|
37
|
-
put(path: string, body: unknown): Promise<unknown>;
|
|
38
|
-
/**
|
|
39
|
-
* PATCH request with JSON body (for status updates).
|
|
40
|
-
*/
|
|
41
|
-
patch(path: string, body: unknown): Promise<unknown>;
|
|
42
|
-
/**
|
|
43
|
-
* DELETE request.
|
|
44
|
-
*/
|
|
45
|
-
delete(path: string): Promise<void>;
|
|
46
|
-
/**
|
|
47
|
-
* POST multipart/form-data request for file uploads.
|
|
48
|
-
* Reads file from disk and sends as multipart form data.
|
|
49
|
-
* CRITICAL: Does NOT manually set Content-Type - fetch sets it automatically with boundary.
|
|
50
|
-
*
|
|
51
|
-
* @param path - API endpoint path
|
|
52
|
-
* @param filePath - Absolute path to file on disk
|
|
53
|
-
* @returns API response
|
|
54
|
-
*/
|
|
55
|
-
postMultipart(path: string, filePath: string): Promise<unknown>;
|
|
56
|
-
/**
|
|
57
|
-
* Validate token on startup by making a lightweight API call.
|
|
58
|
-
* Uses GET /contacts with page_size=1 to verify authentication.
|
|
59
|
-
* Exits process if token is invalid (401).
|
|
60
|
-
*/
|
|
61
|
-
validateToken(): Promise<void>;
|
|
62
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { log } from "../utils/logger.js";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { basename, extname } from "node:path";
|
|
4
|
-
/**
|
|
5
|
-
* HTTP client for Bukku API.
|
|
6
|
-
* Handles authentication via Bearer token and Company-Subdomain header.
|
|
7
|
-
* Base URL: https://api.bukku.my
|
|
8
|
-
*/
|
|
9
|
-
export class BukkuClient {
|
|
10
|
-
baseUrl = "https://api.bukku.my";
|
|
11
|
-
token;
|
|
12
|
-
subdomain;
|
|
13
|
-
constructor(env) {
|
|
14
|
-
this.token = env.BUKKU_API_TOKEN;
|
|
15
|
-
this.subdomain = env.BUKKU_COMPANY_SUBDOMAIN;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Build headers for all requests.
|
|
19
|
-
* CRITICAL: Never log the actual token value - use "Bearer ***" for debugging.
|
|
20
|
-
*/
|
|
21
|
-
getHeaders(includeContentType = false) {
|
|
22
|
-
const headers = {
|
|
23
|
-
Authorization: `Bearer ${this.token}`,
|
|
24
|
-
"Company-Subdomain": this.subdomain,
|
|
25
|
-
Accept: "application/json",
|
|
26
|
-
};
|
|
27
|
-
if (includeContentType) {
|
|
28
|
-
headers["Content-Type"] = "application/json";
|
|
29
|
-
}
|
|
30
|
-
return headers;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Map file extensions to MIME types for common file types.
|
|
34
|
-
* Returns null for unknown extensions.
|
|
35
|
-
*/
|
|
36
|
-
getMimeType(extension) {
|
|
37
|
-
const mimeMap = {
|
|
38
|
-
".pdf": "application/pdf",
|
|
39
|
-
".png": "image/png",
|
|
40
|
-
".jpg": "image/jpeg",
|
|
41
|
-
".jpeg": "image/jpeg",
|
|
42
|
-
".gif": "image/gif",
|
|
43
|
-
".txt": "text/plain",
|
|
44
|
-
".csv": "text/csv",
|
|
45
|
-
".json": "application/json",
|
|
46
|
-
".xml": "application/xml",
|
|
47
|
-
".zip": "application/zip",
|
|
48
|
-
};
|
|
49
|
-
return mimeMap[extension.toLowerCase()] || null;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Build URL with query parameters.
|
|
53
|
-
*/
|
|
54
|
-
buildUrl(path, params) {
|
|
55
|
-
const url = new URL(path, this.baseUrl);
|
|
56
|
-
if (params) {
|
|
57
|
-
for (const [key, value] of Object.entries(params)) {
|
|
58
|
-
if (value !== undefined) {
|
|
59
|
-
url.searchParams.append(key, String(value));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return url.toString();
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* GET request with optional query parameters.
|
|
67
|
-
*/
|
|
68
|
-
async get(path, params) {
|
|
69
|
-
const url = this.buildUrl(path, params);
|
|
70
|
-
const response = await fetch(url, {
|
|
71
|
-
method: "GET",
|
|
72
|
-
headers: this.getHeaders(),
|
|
73
|
-
});
|
|
74
|
-
if (!response.ok) {
|
|
75
|
-
throw response;
|
|
76
|
-
}
|
|
77
|
-
return response.json();
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* POST request with JSON body.
|
|
81
|
-
*/
|
|
82
|
-
async post(path, body) {
|
|
83
|
-
const url = this.buildUrl(path);
|
|
84
|
-
const response = await fetch(url, {
|
|
85
|
-
method: "POST",
|
|
86
|
-
headers: this.getHeaders(true),
|
|
87
|
-
body: JSON.stringify(body),
|
|
88
|
-
});
|
|
89
|
-
if (!response.ok) {
|
|
90
|
-
throw response;
|
|
91
|
-
}
|
|
92
|
-
return response.json();
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* PUT request with JSON body.
|
|
96
|
-
*/
|
|
97
|
-
async put(path, body) {
|
|
98
|
-
const url = this.buildUrl(path);
|
|
99
|
-
const response = await fetch(url, {
|
|
100
|
-
method: "PUT",
|
|
101
|
-
headers: this.getHeaders(true),
|
|
102
|
-
body: JSON.stringify(body),
|
|
103
|
-
});
|
|
104
|
-
if (!response.ok) {
|
|
105
|
-
throw response;
|
|
106
|
-
}
|
|
107
|
-
return response.json();
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* PATCH request with JSON body (for status updates).
|
|
111
|
-
*/
|
|
112
|
-
async patch(path, body) {
|
|
113
|
-
const url = this.buildUrl(path);
|
|
114
|
-
const response = await fetch(url, {
|
|
115
|
-
method: "PATCH",
|
|
116
|
-
headers: this.getHeaders(true),
|
|
117
|
-
body: JSON.stringify(body),
|
|
118
|
-
});
|
|
119
|
-
if (!response.ok) {
|
|
120
|
-
throw response;
|
|
121
|
-
}
|
|
122
|
-
return response.json();
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* DELETE request.
|
|
126
|
-
*/
|
|
127
|
-
async delete(path) {
|
|
128
|
-
const url = this.buildUrl(path);
|
|
129
|
-
const response = await fetch(url, {
|
|
130
|
-
method: "DELETE",
|
|
131
|
-
headers: this.getHeaders(),
|
|
132
|
-
});
|
|
133
|
-
if (!response.ok) {
|
|
134
|
-
throw response;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* POST multipart/form-data request for file uploads.
|
|
139
|
-
* Reads file from disk and sends as multipart form data.
|
|
140
|
-
* CRITICAL: Does NOT manually set Content-Type - fetch sets it automatically with boundary.
|
|
141
|
-
*
|
|
142
|
-
* @param path - API endpoint path
|
|
143
|
-
* @param filePath - Absolute path to file on disk
|
|
144
|
-
* @returns API response
|
|
145
|
-
*/
|
|
146
|
-
async postMultipart(path, filePath) {
|
|
147
|
-
const url = this.buildUrl(path);
|
|
148
|
-
// Read file from disk
|
|
149
|
-
const fileBuffer = await readFile(filePath);
|
|
150
|
-
const fileName = basename(filePath);
|
|
151
|
-
const fileExtension = extname(filePath);
|
|
152
|
-
// Determine MIME type, fallback to generic binary
|
|
153
|
-
const mimeType = this.getMimeType(fileExtension) || "application/octet-stream";
|
|
154
|
-
// Create File object and FormData
|
|
155
|
-
const file = new File([fileBuffer], fileName, { type: mimeType });
|
|
156
|
-
const form = new FormData();
|
|
157
|
-
form.append("file", file);
|
|
158
|
-
// Get auth headers WITHOUT Content-Type (fetch sets it with boundary)
|
|
159
|
-
const headers = this.getHeaders(false);
|
|
160
|
-
const response = await fetch(url, {
|
|
161
|
-
method: "POST",
|
|
162
|
-
headers,
|
|
163
|
-
body: form,
|
|
164
|
-
});
|
|
165
|
-
if (!response.ok) {
|
|
166
|
-
throw response;
|
|
167
|
-
}
|
|
168
|
-
return response.json();
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Validate token on startup by making a lightweight API call.
|
|
172
|
-
* Uses GET /contacts with page_size=1 to verify authentication.
|
|
173
|
-
* Exits process if token is invalid (401).
|
|
174
|
-
*/
|
|
175
|
-
async validateToken() {
|
|
176
|
-
try {
|
|
177
|
-
await this.get("/contacts", { page_size: 1 });
|
|
178
|
-
log("Token validated successfully");
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
if (error instanceof Response && error.status === 401) {
|
|
182
|
-
log("Authentication Error\n");
|
|
183
|
-
log("The provided BUKKU_API_TOKEN is invalid or expired.\n");
|
|
184
|
-
log("Please check:");
|
|
185
|
-
log(" 1. Token is copied correctly (no extra spaces)");
|
|
186
|
-
log(" 2. API Access is enabled in Bukku Control Panel -> Integrations");
|
|
187
|
-
log(" 3. Token has not been revoked or regenerated\n");
|
|
188
|
-
process.exit(1);
|
|
189
|
-
}
|
|
190
|
-
// For other errors, log and exit
|
|
191
|
-
log("Failed to validate token:", error);
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP to MCP Error Transformation
|
|
3
|
-
* Converts HTTP error responses into conversational MCP error messages
|
|
4
|
-
*/
|
|
5
|
-
export interface MCPErrorResponse {
|
|
6
|
-
isError: true;
|
|
7
|
-
content: Array<{
|
|
8
|
-
type: 'text';
|
|
9
|
-
text: string;
|
|
10
|
-
}>;
|
|
11
|
-
[key: string]: unknown;
|
|
12
|
-
}
|
|
13
|
-
export declare function transformHttpError(status: number | null, body: unknown, operation: string): MCPErrorResponse;
|
|
14
|
-
export declare function transformNetworkError(error: unknown, operation: string): MCPErrorResponse;
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP to MCP Error Transformation
|
|
3
|
-
* Converts HTTP error responses into conversational MCP error messages
|
|
4
|
-
*/
|
|
5
|
-
export function transformHttpError(status, body, operation) {
|
|
6
|
-
// Handle authentication errors (401)
|
|
7
|
-
if (status === 401) {
|
|
8
|
-
return {
|
|
9
|
-
isError: true,
|
|
10
|
-
content: [
|
|
11
|
-
{
|
|
12
|
-
type: 'text',
|
|
13
|
-
text: `Bukku authentication failed for "${operation}". The BUKKU_API_TOKEN environment variable is either missing or invalid. Please check your token and restart the server with the correct credentials.`,
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
// Handle permission errors (403)
|
|
19
|
-
if (status === 403) {
|
|
20
|
-
return {
|
|
21
|
-
isError: true,
|
|
22
|
-
content: [
|
|
23
|
-
{
|
|
24
|
-
type: 'text',
|
|
25
|
-
text: `You don't have permission to "${operation}". Please check your Bukku account permissions and ensure you have access to this resource.`,
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
// Handle not found errors (404)
|
|
31
|
-
if (status === 404) {
|
|
32
|
-
return {
|
|
33
|
-
isError: true,
|
|
34
|
-
content: [
|
|
35
|
-
{
|
|
36
|
-
type: 'text',
|
|
37
|
-
text: `I couldn't find that item when trying to "${operation}". Try listing the available items first to see what's accessible.`,
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
// Handle validation errors (400, 422)
|
|
43
|
-
if (status === 400 || status === 422) {
|
|
44
|
-
const parsedBody = body;
|
|
45
|
-
const errors = parsedBody?.errors;
|
|
46
|
-
if (errors && typeof errors === 'object') {
|
|
47
|
-
// Multiple validation errors - show all at once
|
|
48
|
-
const errorMessages = Object.entries(errors)
|
|
49
|
-
.map(([field, messages]) => ` - ${field}: ${messages.join(', ')}`)
|
|
50
|
-
.join('\n');
|
|
51
|
-
return {
|
|
52
|
-
isError: true,
|
|
53
|
-
content: [
|
|
54
|
-
{
|
|
55
|
-
type: 'text',
|
|
56
|
-
text: `Validation failed for "${operation}":\n${errorMessages}\n\nPlease fix these issues and try again.`,
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
// Single error message
|
|
63
|
-
const message = parsedBody?.message || 'Invalid request';
|
|
64
|
-
return {
|
|
65
|
-
isError: true,
|
|
66
|
-
content: [
|
|
67
|
-
{
|
|
68
|
-
type: 'text',
|
|
69
|
-
text: `${message} when trying to "${operation}". Please check your input and try again.`,
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// Handle service unavailable (503)
|
|
76
|
-
if (status === 503) {
|
|
77
|
-
return {
|
|
78
|
-
isError: true,
|
|
79
|
-
content: [
|
|
80
|
-
{
|
|
81
|
-
type: 'text',
|
|
82
|
-
text: `Bukku is temporarily unavailable while trying to "${operation}". Please try again in a few moments.`,
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
// Handle server errors (500+)
|
|
88
|
-
if (status !== null && status >= 500) {
|
|
89
|
-
// Include response body for debugging server errors
|
|
90
|
-
// Often these contain validation errors that should have been 400s
|
|
91
|
-
const parsedBody = body;
|
|
92
|
-
const bodyText = body ? `\n\nServer response: ${JSON.stringify(parsedBody, null, 2)}` : '';
|
|
93
|
-
return {
|
|
94
|
-
isError: true,
|
|
95
|
-
content: [
|
|
96
|
-
{
|
|
97
|
-
type: 'text',
|
|
98
|
-
text: `An unexpected error occurred on Bukku's servers while trying to "${operation}". Please try again, and if the issue persists, contact Bukku support.${bodyText}`,
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
// Fallback for unknown status codes
|
|
104
|
-
return {
|
|
105
|
-
isError: true,
|
|
106
|
-
content: [
|
|
107
|
-
{
|
|
108
|
-
type: 'text',
|
|
109
|
-
text: `An error occurred while trying to "${operation}". Please check your request and try again.`,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
export function transformNetworkError(error, operation) {
|
|
115
|
-
// Check if it's a network-related error
|
|
116
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
117
|
-
if (errorMessage.includes('fetch') ||
|
|
118
|
-
errorMessage.includes('connect') ||
|
|
119
|
-
errorMessage.includes('network') ||
|
|
120
|
-
error instanceof TypeError) {
|
|
121
|
-
return {
|
|
122
|
-
isError: true,
|
|
123
|
-
content: [
|
|
124
|
-
{
|
|
125
|
-
type: 'text',
|
|
126
|
-
text: `Couldn't connect to Bukku while trying to "${operation}". Please check your internet connection and ensure the Bukku API is accessible.`,
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
// Fallback for unknown errors
|
|
132
|
-
return {
|
|
133
|
-
isError: true,
|
|
134
|
-
content: [
|
|
135
|
-
{
|
|
136
|
-
type: 'text',
|
|
137
|
-
text: `An unexpected error occurred while trying to "${operation}": ${errorMessage}. Please try again.`,
|
|
138
|
-
},
|
|
139
|
-
],
|
|
140
|
-
};
|
|
141
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|