mcp-quickbase 2.0.1
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/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +301 -0
- package/dist/client/quickbase.d.ts +28 -0
- package/dist/client/quickbase.js +235 -0
- package/dist/client/quickbase.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +19 -0
- package/dist/mcp/server.js +102 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp-stdio-server.d.ts +2 -0
- package/dist/mcp-stdio-server.js +168 -0
- package/dist/mcp-stdio-server.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +318 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/apps/create_app.d.ts +87 -0
- package/dist/tools/apps/create_app.js +87 -0
- package/dist/tools/apps/create_app.js.map +1 -0
- package/dist/tools/apps/index.d.ts +9 -0
- package/dist/tools/apps/index.js +40 -0
- package/dist/tools/apps/index.js.map +1 -0
- package/dist/tools/apps/list_tables.d.ts +108 -0
- package/dist/tools/apps/list_tables.js +100 -0
- package/dist/tools/apps/list_tables.js.map +1 -0
- package/dist/tools/apps/update_app.d.ts +91 -0
- package/dist/tools/apps/update_app.js +99 -0
- package/dist/tools/apps/update_app.js.map +1 -0
- package/dist/tools/base.d.ts +47 -0
- package/dist/tools/base.js +63 -0
- package/dist/tools/base.js.map +1 -0
- package/dist/tools/configure_cache.d.ts +81 -0
- package/dist/tools/configure_cache.js +77 -0
- package/dist/tools/configure_cache.js.map +1 -0
- package/dist/tools/fields/create_field.d.ts +121 -0
- package/dist/tools/fields/create_field.js +102 -0
- package/dist/tools/fields/create_field.js.map +1 -0
- package/dist/tools/fields/index.d.ts +8 -0
- package/dist/tools/fields/index.js +37 -0
- package/dist/tools/fields/index.js.map +1 -0
- package/dist/tools/fields/update_field.d.ts +112 -0
- package/dist/tools/fields/update_field.js +114 -0
- package/dist/tools/fields/update_field.js.map +1 -0
- package/dist/tools/files/download_file.d.ts +111 -0
- package/dist/tools/files/download_file.js +173 -0
- package/dist/tools/files/download_file.js.map +1 -0
- package/dist/tools/files/index.d.ts +8 -0
- package/dist/tools/files/index.js +37 -0
- package/dist/tools/files/index.js.map +1 -0
- package/dist/tools/files/upload_file.d.ts +107 -0
- package/dist/tools/files/upload_file.js +211 -0
- package/dist/tools/files/upload_file.js.map +1 -0
- package/dist/tools/index.d.ts +18 -0
- package/dist/tools/index.js +65 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/records/bulk_create_records.d.ts +75 -0
- package/dist/tools/records/bulk_create_records.js +104 -0
- package/dist/tools/records/bulk_create_records.js.map +1 -0
- package/dist/tools/records/bulk_update_records.d.ts +77 -0
- package/dist/tools/records/bulk_update_records.js +102 -0
- package/dist/tools/records/bulk_update_records.js.map +1 -0
- package/dist/tools/records/create_record.d.ts +68 -0
- package/dist/tools/records/create_record.js +123 -0
- package/dist/tools/records/create_record.js.map +1 -0
- package/dist/tools/records/index.d.ts +11 -0
- package/dist/tools/records/index.js +46 -0
- package/dist/tools/records/index.js.map +1 -0
- package/dist/tools/records/query_records.d.ts +164 -0
- package/dist/tools/records/query_records.js +261 -0
- package/dist/tools/records/query_records.js.map +1 -0
- package/dist/tools/records/update_record.d.ts +81 -0
- package/dist/tools/records/update_record.js +99 -0
- package/dist/tools/records/update_record.js.map +1 -0
- package/dist/tools/registry.d.ts +41 -0
- package/dist/tools/registry.js +66 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/reports/index.d.ts +6 -0
- package/dist/tools/reports/index.js +31 -0
- package/dist/tools/reports/index.js.map +1 -0
- package/dist/tools/reports/run_report.d.ts +70 -0
- package/dist/tools/reports/run_report.js +72 -0
- package/dist/tools/reports/run_report.js.map +1 -0
- package/dist/tools/tables/create_table.d.ts +142 -0
- package/dist/tools/tables/create_table.js +119 -0
- package/dist/tools/tables/create_table.js.map +1 -0
- package/dist/tools/tables/get_table_fields.d.ts +108 -0
- package/dist/tools/tables/get_table_fields.js +96 -0
- package/dist/tools/tables/get_table_fields.js.map +1 -0
- package/dist/tools/tables/index.d.ts +9 -0
- package/dist/tools/tables/index.js +40 -0
- package/dist/tools/tables/index.js.map +1 -0
- package/dist/tools/tables/update_table.d.ts +91 -0
- package/dist/tools/tables/update_table.js +99 -0
- package/dist/tools/tables/update_table.js.map +1 -0
- package/dist/tools/test_connection.d.ts +51 -0
- package/dist/tools/test_connection.js +101 -0
- package/dist/tools/test_connection.js.map +1 -0
- package/dist/types/api.d.ts +70 -0
- package/dist/types/api.js +6 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/config.d.ts +49 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/mcp.d.ts +55 -0
- package/dist/types/mcp.js +3 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/utils/cache.d.ts +87 -0
- package/dist/utils/cache.js +211 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/file.d.ts +40 -0
- package/dist/utils/file.js +167 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/logger.d.ts +37 -0
- package/dist/utils/logger.js +144 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +39 -0
- package/dist/utils/retry.js +88 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/validation.d.ts +32 -0
- package/dist/utils/validation.js +227 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/README.md +41 -0
- package/docs/architecture.md +94 -0
- package/docs/claude-prompts.md +218 -0
- package/docs/deployment.md +244 -0
- package/docs/developer-guide.md +537 -0
- package/docs/final-qa-report.md +243 -0
- package/docs/performance-benchmarks.md +306 -0
- package/docs/quick-reference.md +109 -0
- package/docs/quickstart.md +183 -0
- package/docs/security-review.md +263 -0
- package/docs/tools.md +269 -0
- package/package.json +68 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# 📋 Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Quickbase MCP Server will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [2.0.0] - 2025-05-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Complete TypeScript rewrite - removed Python dependency
|
|
9
|
+
- Full type safety with TypeScript strict mode
|
|
10
|
+
- Intelligent caching system with configurable TTL
|
|
11
|
+
- Built-in retry logic with exponential backoff
|
|
12
|
+
- Rate limiting to prevent API overload
|
|
13
|
+
- Comprehensive error handling with structured responses
|
|
14
|
+
- Jest-based test suite with 45%+ coverage
|
|
15
|
+
- ESLint and Prettier for code quality
|
|
16
|
+
- MCP server implementation for both stdio and HTTP modes
|
|
17
|
+
- Session management and proper lifecycle handling
|
|
18
|
+
- Migration guide for v1 users
|
|
19
|
+
- Performance benchmarking tests
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Minimum Node.js version increased to 18+ (from 14+)
|
|
23
|
+
- Entry point changed to `dist/mcp-stdio-server.js`
|
|
24
|
+
- Startup time reduced by 60% (~2s vs 5s+)
|
|
25
|
+
- Memory usage reduced by 40%
|
|
26
|
+
- All async operations now use modern async/await
|
|
27
|
+
- Improved validation for all tool parameters
|
|
28
|
+
- Better error messages with actionable context
|
|
29
|
+
- Reorganized project structure for better maintainability
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- Memory leaks in long-running sessions
|
|
33
|
+
- Race conditions in concurrent requests
|
|
34
|
+
- File upload issues with large files
|
|
35
|
+
- Pagination bugs in query_records
|
|
36
|
+
- Cache invalidation timing issues
|
|
37
|
+
- Proper cleanup on shutdown
|
|
38
|
+
|
|
39
|
+
### Deprecated
|
|
40
|
+
- v1 implementation moved to v1-legacy/ for reference only
|
|
41
|
+
|
|
42
|
+
## [1.0.0] - 2025-03-21
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
- Added pagination support for query_records tool
|
|
46
|
+
- Added comprehensive test suite
|
|
47
|
+
- Added run_tests.sh script for easy testing
|
|
48
|
+
- Added TEST_RESULTS.md with detailed test results
|
|
49
|
+
- Added CHANGELOG.md file
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
- Fixed file upload and download operations
|
|
53
|
+
- Improved error handling across all operations
|
|
54
|
+
- Updated create_record to properly format field IDs
|
|
55
|
+
- Updated update_record to handle JSON string parsing
|
|
56
|
+
- Updated documentation in README.md and tools_tested.txt
|
|
57
|
+
|
|
58
|
+
### Removed
|
|
59
|
+
- Removed all delete operations due to API limitations:
|
|
60
|
+
- delete_app
|
|
61
|
+
- delete_table
|
|
62
|
+
- delete_field
|
|
63
|
+
- delete_record
|
|
64
|
+
- bulk_delete_records
|
|
65
|
+
- delete_file
|
|
66
|
+
- Removed user operations due to API limitations:
|
|
67
|
+
- get_user
|
|
68
|
+
- get_current_user
|
|
69
|
+
- get_user_roles
|
|
70
|
+
- manage_users
|
|
71
|
+
- Removed form operations due to API limitations:
|
|
72
|
+
- manage_forms
|
|
73
|
+
- Removed dashboard operations due to API limitations:
|
|
74
|
+
- manage_dashboards
|
|
75
|
+
|
|
76
|
+
## [0.1.0] - 2025-03-20
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
- Initial release
|
|
80
|
+
- Basic MCP server implementation
|
|
81
|
+
- Support for Quickbase API operations
|
|
82
|
+
- Documentation and examples
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Quickbase MCP Server Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Quickbase MCP Server
|
|
2
|
+
|
|
3
|
+
A TypeScript-based Model Context Protocol (MCP) connector for Quickbase, designed for seamless integration with Claude and other AI assistants.
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
### Installation via NPM (Recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Use directly with npx (no installation needed)
|
|
11
|
+
npx -y mcp-quickbase
|
|
12
|
+
|
|
13
|
+
# Or install globally
|
|
14
|
+
npm install -g mcp-quickbase
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Installation from Source
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Clone the repository
|
|
21
|
+
git clone https://github.com/danielbushman/MCP-Quickbase.git
|
|
22
|
+
cd MCP-Quickbase
|
|
23
|
+
|
|
24
|
+
# Install dependencies
|
|
25
|
+
npm install
|
|
26
|
+
|
|
27
|
+
# Build the project
|
|
28
|
+
npm run build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Configuration
|
|
32
|
+
|
|
33
|
+
#### For NPM Users
|
|
34
|
+
|
|
35
|
+
Configure directly in Claude Desktop:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"quickbase": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": ["-y", "mcp-quickbase"],
|
|
43
|
+
"env": {
|
|
44
|
+
"QUICKBASE_REALM_HOST": "your-realm.quickbase.com",
|
|
45
|
+
"QUICKBASE_USER_TOKEN": "your-user-token",
|
|
46
|
+
"QUICKBASE_APP_ID": "your-app-id",
|
|
47
|
+
"QUICKBASE_CACHE_ENABLED": "true",
|
|
48
|
+
"QUICKBASE_CACHE_TTL": "3600"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### For Local Development
|
|
56
|
+
|
|
57
|
+
Create a `.env` file in the root directory:
|
|
58
|
+
|
|
59
|
+
```env
|
|
60
|
+
QUICKBASE_REALM_HOST=your-realm.quickbase.com
|
|
61
|
+
QUICKBASE_USER_TOKEN=your-user-token
|
|
62
|
+
QUICKBASE_APP_ID=your-app-id
|
|
63
|
+
QUICKBASE_CACHE_ENABLED=true
|
|
64
|
+
QUICKBASE_CACHE_TTL=3600
|
|
65
|
+
DEBUG=false
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then configure Claude Desktop:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"quickbase": {
|
|
74
|
+
"command": "node",
|
|
75
|
+
"args": ["/path/to/MCP-Quickbase/dist/mcp-stdio-server.js"],
|
|
76
|
+
"env": {
|
|
77
|
+
"QUICKBASE_REALM_HOST": "your-realm.quickbase.com",
|
|
78
|
+
"QUICKBASE_USER_TOKEN": "your-user-token",
|
|
79
|
+
"QUICKBASE_APP_ID": "your-app-id"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
2. **Start using tools**: The connector provides 18 comprehensive tools for Quickbase operations.
|
|
87
|
+
|
|
88
|
+
## 🛠️ Available Tools
|
|
89
|
+
|
|
90
|
+
### Connection & Configuration
|
|
91
|
+
- **`test_connection`** - Test connection to Quickbase
|
|
92
|
+
- **`configure_cache`** - Configure caching behavior
|
|
93
|
+
|
|
94
|
+
### Application Management
|
|
95
|
+
- **`create_app`** - Create new Quickbase applications
|
|
96
|
+
- **`update_app`** - Update existing applications
|
|
97
|
+
- **`list_tables`** - List all tables in an application
|
|
98
|
+
|
|
99
|
+
### Table Operations
|
|
100
|
+
- **`create_table`** - Create new tables
|
|
101
|
+
- **`update_table`** - Update existing tables
|
|
102
|
+
- **`get_table_fields`** - Retrieve table field definitions
|
|
103
|
+
|
|
104
|
+
### Field Management
|
|
105
|
+
- **`create_field`** - Create new fields in tables
|
|
106
|
+
- **`update_field`** - Update existing field properties
|
|
107
|
+
|
|
108
|
+
### Record Operations
|
|
109
|
+
- **`query_records`** - Query records with advanced filtering
|
|
110
|
+
- **`create_record`** - Create single records
|
|
111
|
+
- **`update_record`** - Update existing records
|
|
112
|
+
- **`bulk_create_records`** - Create multiple records efficiently
|
|
113
|
+
- **`bulk_update_records`** - Update multiple records efficiently
|
|
114
|
+
|
|
115
|
+
### File Handling
|
|
116
|
+
- **`upload_file`** - Upload files to record fields
|
|
117
|
+
- **`download_file`** - Download files from record fields
|
|
118
|
+
|
|
119
|
+
### Reports
|
|
120
|
+
- **`run_report`** - Execute Quickbase reports with filters
|
|
121
|
+
|
|
122
|
+
## 🏗️ Architecture
|
|
123
|
+
|
|
124
|
+
### TypeScript-First Design
|
|
125
|
+
- **100% TypeScript** for type safety and developer experience
|
|
126
|
+
- **Comprehensive type definitions** for all Quickbase API interactions
|
|
127
|
+
- **Modern async/await** patterns throughout
|
|
128
|
+
|
|
129
|
+
### Performance Features
|
|
130
|
+
- **Intelligent caching** with configurable TTL
|
|
131
|
+
- **Automatic retry logic** for transient failures
|
|
132
|
+
- **Bulk operations** for high-performance data manipulation
|
|
133
|
+
- **Pagination support** for large datasets
|
|
134
|
+
|
|
135
|
+
### Error Handling
|
|
136
|
+
- **Structured error responses** with detailed context
|
|
137
|
+
- **Graceful degradation** for API failures
|
|
138
|
+
- **Comprehensive logging** for debugging
|
|
139
|
+
|
|
140
|
+
## 📚 Examples
|
|
141
|
+
|
|
142
|
+
### Basic Record Operations
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Query records
|
|
146
|
+
const records = await queryRecords({
|
|
147
|
+
table_id: "bqrxzt5wq",
|
|
148
|
+
where: "{6.CT.'Project'}",
|
|
149
|
+
select: ["1", "6", "7", "8"]
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Create a record
|
|
153
|
+
const newRecord = await createRecord({
|
|
154
|
+
table_id: "bqrxzt5wq",
|
|
155
|
+
data: {
|
|
156
|
+
"6": "New Project",
|
|
157
|
+
"7": "Project description",
|
|
158
|
+
"8": "High"
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Update multiple records
|
|
163
|
+
const updates = await bulkUpdateRecords({
|
|
164
|
+
table_id: "bqrxzt5wq",
|
|
165
|
+
records: [
|
|
166
|
+
{ "3": "123", "8": "Critical" },
|
|
167
|
+
{ "3": "124", "8": "Low" }
|
|
168
|
+
]
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### File Operations
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Upload a file
|
|
176
|
+
const upload = await uploadFile({
|
|
177
|
+
table_id: "bqrxzt5wq",
|
|
178
|
+
record_id: "123",
|
|
179
|
+
field_id: "9",
|
|
180
|
+
file_path: "/path/to/document.pdf"
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Download a file
|
|
184
|
+
const download = await downloadFile({
|
|
185
|
+
table_id: "bqrxzt5wq",
|
|
186
|
+
record_id: "123",
|
|
187
|
+
field_id: "9",
|
|
188
|
+
output_path: "/downloads/document.pdf"
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Advanced Queries with Pagination
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// Paginated query for large datasets
|
|
196
|
+
const largeDataset = await queryRecords({
|
|
197
|
+
table_id: "bqrxzt5wq",
|
|
198
|
+
select: ["1", "6", "7", "8"],
|
|
199
|
+
paginate: true,
|
|
200
|
+
max_records: "1000",
|
|
201
|
+
options: {
|
|
202
|
+
orderBy: [{ fieldId: "6", order: "ASC" }],
|
|
203
|
+
top: 100
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 🧪 Testing
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Run all tests
|
|
212
|
+
npm test
|
|
213
|
+
|
|
214
|
+
# Run tests with coverage
|
|
215
|
+
npm test -- --coverage
|
|
216
|
+
|
|
217
|
+
# Run tests in watch mode
|
|
218
|
+
npm test -- --watch
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 🔧 Development
|
|
222
|
+
|
|
223
|
+
### Project Structure
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
src/
|
|
227
|
+
├── client/ # Quickbase API client
|
|
228
|
+
├── tools/ # MCP tool implementations
|
|
229
|
+
│ ├── apps/ # Application management tools
|
|
230
|
+
│ ├── fields/ # Field management tools
|
|
231
|
+
│ ├── files/ # File operation tools
|
|
232
|
+
│ ├── records/ # Record operation tools
|
|
233
|
+
│ ├── reports/ # Report execution tools
|
|
234
|
+
│ └── tables/ # Table operation tools
|
|
235
|
+
├── types/ # TypeScript type definitions
|
|
236
|
+
├── utils/ # Utility functions
|
|
237
|
+
└── mcp/ # MCP server implementation
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Adding New Tools
|
|
241
|
+
|
|
242
|
+
1. Create tool class extending `BaseTool<TParams, TResult>`
|
|
243
|
+
2. Implement required properties and `run()` method
|
|
244
|
+
3. Register in appropriate tool category
|
|
245
|
+
4. Add comprehensive tests
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
export class MyCustomTool extends BaseTool<MyParams, MyResult> {
|
|
251
|
+
public readonly name = 'my_custom_tool';
|
|
252
|
+
public readonly description = 'Description of my tool';
|
|
253
|
+
public readonly paramSchema = { /* JSON Schema */ };
|
|
254
|
+
|
|
255
|
+
protected async run(params: MyParams): Promise<MyResult> {
|
|
256
|
+
// Implementation
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## 🚦 Deployment
|
|
262
|
+
|
|
263
|
+
### HTTP Server Mode
|
|
264
|
+
```bash
|
|
265
|
+
npm start # Runs on port 3536
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### MCP Stdio Mode
|
|
269
|
+
```bash
|
|
270
|
+
node dist/mcp-stdio-server.js
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## 📋 Requirements
|
|
274
|
+
|
|
275
|
+
- **Node.js** 18+
|
|
276
|
+
- **TypeScript** 5.2+
|
|
277
|
+
- **Quickbase** account with API access
|
|
278
|
+
- **Valid user token** with appropriate permissions
|
|
279
|
+
|
|
280
|
+
## 🤝 Contributing
|
|
281
|
+
|
|
282
|
+
1. Fork the repository
|
|
283
|
+
2. Create a feature branch
|
|
284
|
+
3. Add tests for new functionality
|
|
285
|
+
4. Ensure all tests pass
|
|
286
|
+
5. Submit a pull request
|
|
287
|
+
|
|
288
|
+
## 📄 License
|
|
289
|
+
|
|
290
|
+
MIT License - see LICENSE file for details.
|
|
291
|
+
|
|
292
|
+
## 🆘 Support
|
|
293
|
+
|
|
294
|
+
For issues and questions:
|
|
295
|
+
- Check the [documentation](docs/)
|
|
296
|
+
- Review the [Quick Start Guide](docs/quickstart.md)
|
|
297
|
+
- Open an issue on GitHub
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
Built with ❤️ for seamless Quickbase integration with AI assistants.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { QuickbaseConfig } from '../types/config';
|
|
2
|
+
import { ApiResponse, RequestOptions } from '../types/api';
|
|
3
|
+
/**
|
|
4
|
+
* Client for interacting with the Quickbase API
|
|
5
|
+
*/
|
|
6
|
+
export declare class QuickbaseClient {
|
|
7
|
+
private config;
|
|
8
|
+
private cache;
|
|
9
|
+
private baseUrl;
|
|
10
|
+
private headers;
|
|
11
|
+
private rateLimiter;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new Quickbase client
|
|
14
|
+
* @param config Client configuration
|
|
15
|
+
*/
|
|
16
|
+
constructor(config: QuickbaseConfig);
|
|
17
|
+
/**
|
|
18
|
+
* Get the client configuration
|
|
19
|
+
* @returns Current configuration
|
|
20
|
+
*/
|
|
21
|
+
getConfig(): QuickbaseConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Sends a request to the Quickbase API with retry logic
|
|
24
|
+
* @param options Request options
|
|
25
|
+
* @returns API response
|
|
26
|
+
*/
|
|
27
|
+
request<T>(options: RequestOptions): Promise<ApiResponse<T>>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QuickbaseClient = void 0;
|
|
4
|
+
const cache_1 = require("../utils/cache");
|
|
5
|
+
const logger_1 = require("../utils/logger");
|
|
6
|
+
const retry_1 = require("../utils/retry");
|
|
7
|
+
const logger = (0, logger_1.createLogger)('QuickbaseClient');
|
|
8
|
+
/**
|
|
9
|
+
* Thread-safe rate limiter to prevent API overload
|
|
10
|
+
*/
|
|
11
|
+
class RateLimiter {
|
|
12
|
+
constructor(maxRequests = 10, windowMs = 1000) {
|
|
13
|
+
this.requests = [];
|
|
14
|
+
this.pending = Promise.resolve();
|
|
15
|
+
this.maxRequests = maxRequests;
|
|
16
|
+
this.windowMs = windowMs;
|
|
17
|
+
}
|
|
18
|
+
async wait() {
|
|
19
|
+
// Serialize all rate limit checks to prevent race conditions
|
|
20
|
+
this.pending = this.pending.then(() => this.checkRateLimit());
|
|
21
|
+
return this.pending;
|
|
22
|
+
}
|
|
23
|
+
async checkRateLimit() {
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
// Remove requests outside the current window
|
|
26
|
+
this.requests = this.requests.filter(time => now - time < this.windowMs);
|
|
27
|
+
if (this.requests.length >= this.maxRequests) {
|
|
28
|
+
// Calculate wait time until oldest request expires
|
|
29
|
+
const oldestRequest = Math.min(...this.requests);
|
|
30
|
+
const waitTime = this.windowMs - (now - oldestRequest) + 10; // +10ms buffer
|
|
31
|
+
if (waitTime > 0) {
|
|
32
|
+
logger.debug(`Rate limiting: waiting ${waitTime}ms`);
|
|
33
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
34
|
+
// Re-check after waiting (recursive but bounded by maxRequests)
|
|
35
|
+
return this.checkRateLimit();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Add this request to the window
|
|
39
|
+
this.requests.push(Date.now());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Client for interacting with the Quickbase API
|
|
44
|
+
*/
|
|
45
|
+
class QuickbaseClient {
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new Quickbase client
|
|
48
|
+
* @param config Client configuration
|
|
49
|
+
*/
|
|
50
|
+
constructor(config) {
|
|
51
|
+
this.config = {
|
|
52
|
+
userAgent: 'QuickbaseMCPConnector/2.0',
|
|
53
|
+
cacheEnabled: true,
|
|
54
|
+
cacheTtl: 3600,
|
|
55
|
+
maxRetries: 3,
|
|
56
|
+
retryDelay: 1000,
|
|
57
|
+
debug: false,
|
|
58
|
+
...config
|
|
59
|
+
};
|
|
60
|
+
if (!this.config.realmHost) {
|
|
61
|
+
throw new Error('Realm hostname is required');
|
|
62
|
+
}
|
|
63
|
+
if (!this.config.userToken) {
|
|
64
|
+
throw new Error('User token is required');
|
|
65
|
+
}
|
|
66
|
+
this.baseUrl = `https://api.quickbase.com/v1`;
|
|
67
|
+
this.headers = {
|
|
68
|
+
'QB-Realm-Hostname': this.config.realmHost,
|
|
69
|
+
'Authorization': `QB-USER-TOKEN ${this.config.userToken}`,
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
'User-Agent': this.config.userAgent || 'QuickbaseMCPConnector/2.0'
|
|
72
|
+
};
|
|
73
|
+
this.cache = new cache_1.CacheService(this.config.cacheTtl, this.config.cacheEnabled);
|
|
74
|
+
// Initialize rate limiter (10 requests per second by default)
|
|
75
|
+
this.rateLimiter = new RateLimiter(this.config.rateLimit || 10, 1000);
|
|
76
|
+
logger.info('Quickbase client initialized', {
|
|
77
|
+
realmHost: this.config.realmHost,
|
|
78
|
+
appId: this.config.appId,
|
|
79
|
+
cacheEnabled: this.config.cacheEnabled,
|
|
80
|
+
rateLimit: this.config.rateLimit || 10
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get the client configuration
|
|
85
|
+
* @returns Current configuration
|
|
86
|
+
*/
|
|
87
|
+
getConfig() {
|
|
88
|
+
return { ...this.config };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Sends a request to the Quickbase API with retry logic
|
|
92
|
+
* @param options Request options
|
|
93
|
+
* @returns API response
|
|
94
|
+
*/
|
|
95
|
+
async request(options) {
|
|
96
|
+
const makeRequest = async () => {
|
|
97
|
+
const { method, path, body, params, headers = {}, skipCache = false } = options;
|
|
98
|
+
// Build full URL with query parameters
|
|
99
|
+
let url = `${this.baseUrl}${path}`;
|
|
100
|
+
if (params && Object.keys(params).length > 0) {
|
|
101
|
+
const searchParams = new URLSearchParams();
|
|
102
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
103
|
+
searchParams.append(key, value);
|
|
104
|
+
});
|
|
105
|
+
url += `?${searchParams.toString()}`;
|
|
106
|
+
}
|
|
107
|
+
// Check cache for GET requests
|
|
108
|
+
const cacheKey = `${method}:${url}`;
|
|
109
|
+
if (method === 'GET' && !skipCache) {
|
|
110
|
+
const cachedResponse = this.cache.get(cacheKey);
|
|
111
|
+
if (cachedResponse) {
|
|
112
|
+
logger.debug('Returning cached response', { url, method });
|
|
113
|
+
return cachedResponse;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Apply rate limiting before making the request
|
|
117
|
+
await this.rateLimiter.wait();
|
|
118
|
+
// Combine default headers with request-specific headers
|
|
119
|
+
const requestHeaders = { ...this.headers, ...headers };
|
|
120
|
+
// Log request (with redacted sensitive info)
|
|
121
|
+
const redactedHeaders = { ...requestHeaders };
|
|
122
|
+
if (redactedHeaders.Authorization) {
|
|
123
|
+
redactedHeaders.Authorization = '***REDACTED***';
|
|
124
|
+
}
|
|
125
|
+
if (redactedHeaders['QB-Realm-Hostname']) {
|
|
126
|
+
// Keep realm hostname for debugging but redact sensitive parts
|
|
127
|
+
redactedHeaders['QB-Realm-Hostname'] = redactedHeaders['QB-Realm-Hostname'].replace(/[a-zA-Z0-9-]+/, '***');
|
|
128
|
+
}
|
|
129
|
+
logger.debug('Sending API request', {
|
|
130
|
+
url: url.replace(/[?&]userToken=[^&]*/g, '&userToken=***REDACTED***'), // Redact tokens in URL too
|
|
131
|
+
method,
|
|
132
|
+
headers: redactedHeaders,
|
|
133
|
+
body: body ? JSON.stringify(body) : undefined
|
|
134
|
+
});
|
|
135
|
+
// Send request with timeout protection
|
|
136
|
+
const controller = new AbortController();
|
|
137
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.requestTimeout || 30000);
|
|
138
|
+
let response;
|
|
139
|
+
try {
|
|
140
|
+
response = await fetch(url, {
|
|
141
|
+
method,
|
|
142
|
+
headers: requestHeaders,
|
|
143
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
144
|
+
signal: controller.signal
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
clearTimeout(timeoutId);
|
|
149
|
+
}
|
|
150
|
+
// Parse response safely
|
|
151
|
+
let responseData;
|
|
152
|
+
try {
|
|
153
|
+
responseData = await response.json();
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
throw new Error(`Invalid JSON response: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
157
|
+
}
|
|
158
|
+
// Ensure responseData is an object
|
|
159
|
+
if (typeof responseData !== 'object' || responseData === null) {
|
|
160
|
+
throw new Error('API response is not a valid object');
|
|
161
|
+
}
|
|
162
|
+
const data = responseData;
|
|
163
|
+
// Check for error response
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
const errorMessage = typeof data.message === 'string' ? data.message : response.statusText;
|
|
166
|
+
const error = {
|
|
167
|
+
message: errorMessage,
|
|
168
|
+
code: response.status,
|
|
169
|
+
details: data
|
|
170
|
+
};
|
|
171
|
+
logger.error('API request failed', {
|
|
172
|
+
status: response.status,
|
|
173
|
+
error
|
|
174
|
+
});
|
|
175
|
+
// Create error with proper metadata for retry logic
|
|
176
|
+
const httpError = new Error(`HTTP Error ${response.status}: ${errorMessage}`);
|
|
177
|
+
Object.assign(httpError, { status: response.status, data: responseData });
|
|
178
|
+
// Always throw HTTP errors - let retry logic determine if they're retryable
|
|
179
|
+
// The retry logic will check the status code and decide whether to retry
|
|
180
|
+
throw httpError;
|
|
181
|
+
}
|
|
182
|
+
// Successful response
|
|
183
|
+
const result = {
|
|
184
|
+
success: true,
|
|
185
|
+
data: responseData
|
|
186
|
+
};
|
|
187
|
+
// Cache successful GET responses
|
|
188
|
+
if (method === 'GET' && !skipCache) {
|
|
189
|
+
this.cache.set(cacheKey, result);
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
};
|
|
193
|
+
// Retry configuration
|
|
194
|
+
const retryOptions = {
|
|
195
|
+
maxRetries: this.config.maxRetries || 3,
|
|
196
|
+
baseDelay: this.config.retryDelay || 1000,
|
|
197
|
+
isRetryable: (error) => {
|
|
198
|
+
// Only retry certain HTTP errors and network errors
|
|
199
|
+
if (!error)
|
|
200
|
+
return false;
|
|
201
|
+
// Handle HTTP errors
|
|
202
|
+
if (typeof error === 'object' && error !== null && 'status' in error) {
|
|
203
|
+
const httpError = error;
|
|
204
|
+
return httpError.status === 429 || // Too Many Requests
|
|
205
|
+
httpError.status === 408 || // Request Timeout
|
|
206
|
+
(httpError.status >= 500 && httpError.status < 600); // Server errors
|
|
207
|
+
}
|
|
208
|
+
// Handle network errors
|
|
209
|
+
if (error instanceof Error) {
|
|
210
|
+
return error.message.includes('network') ||
|
|
211
|
+
error.message.includes('timeout') ||
|
|
212
|
+
error.message.includes('connection');
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
try {
|
|
218
|
+
// Use withRetry to add retry logic to the request
|
|
219
|
+
return await (0, retry_1.withRetry)(makeRequest, retryOptions)();
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
// Handle errors that weren't handled by the retry logic
|
|
223
|
+
logger.error('Request failed after retries', { error });
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
error: {
|
|
227
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
228
|
+
type: 'NetworkError'
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.QuickbaseClient = QuickbaseClient;
|
|
235
|
+
//# sourceMappingURL=quickbase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quickbase.js","sourceRoot":"","sources":["../../src/client/quickbase.ts"],"names":[],"mappings":"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"}
|