integrate-sdk 0.1.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/LICENSE +22 -0
- package/README.md +639 -0
- package/dist/client.d.ts +108 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/config/types.d.ts +34 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +440 -0
- package/dist/plugins/generic.d.ts +79 -0
- package/dist/plugins/generic.d.ts.map +1 -0
- package/dist/plugins/github.d.ts +51 -0
- package/dist/plugins/github.d.ts.map +1 -0
- package/dist/plugins/gmail.d.ts +48 -0
- package/dist/plugins/gmail.d.ts.map +1 -0
- package/dist/plugins/types.d.ts +58 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/protocol/jsonrpc.d.ts +32 -0
- package/dist/protocol/jsonrpc.d.ts.map +1 -0
- package/dist/protocol/messages.d.ts +130 -0
- package/dist/protocol/messages.d.ts.map +1 -0
- package/dist/transport/http-session.d.ts +67 -0
- package/dist/transport/http-session.d.ts.map +1 -0
- package/dist/transport/http-stream.d.ts +69 -0
- package/dist/transport/http-stream.d.ts.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Integrate SDK 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.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
# Integrate SDK
|
|
2
|
+
|
|
3
|
+
A type-safe TypeScript SDK for building MCP (Model Context Protocol) clients with plugin-based OAuth provider configuration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔌 **Plugin-Based Architecture** - Enable only the tools you need with a BetterAuth-inspired plugin pattern
|
|
8
|
+
- 🔒 **Type-Safe Configuration** - Full TypeScript support with IntelliSense for tools and configurations
|
|
9
|
+
- 🌊 **HTTP Streaming** - Real-time bidirectional communication via HTTP streaming with newline-delimited JSON (NDJSON)
|
|
10
|
+
- 🔐 **OAuth Support** - Built-in OAuth configuration for multiple providers
|
|
11
|
+
- 🛠️ **Extensible** - Easy to create custom plugins for any OAuth provider or tool set
|
|
12
|
+
- 📦 **Zero Dependencies** - Lightweight with no external runtime dependencies
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun add integrate-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { createMCPClient, githubPlugin, gmailPlugin } from 'integrate-sdk';
|
|
24
|
+
|
|
25
|
+
// Create a client with plugins
|
|
26
|
+
const client = createMCPClient({
|
|
27
|
+
serverUrl: 'http://localhost:3000/mcp',
|
|
28
|
+
plugins: [
|
|
29
|
+
githubPlugin({
|
|
30
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
31
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
32
|
+
scopes: ['repo', 'user'],
|
|
33
|
+
}),
|
|
34
|
+
gmailPlugin({
|
|
35
|
+
clientId: process.env.GMAIL_CLIENT_ID!,
|
|
36
|
+
clientSecret: process.env.GMAIL_CLIENT_SECRET!,
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Connect to the server
|
|
42
|
+
await client.connect();
|
|
43
|
+
|
|
44
|
+
// Call tools
|
|
45
|
+
const result = await client.callTool('github/createIssue', {
|
|
46
|
+
repo: 'owner/repo',
|
|
47
|
+
title: 'Bug report',
|
|
48
|
+
body: 'Description of the bug',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log('Issue created:', result);
|
|
52
|
+
|
|
53
|
+
// Disconnect when done
|
|
54
|
+
await client.disconnect();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Built-in Plugins
|
|
58
|
+
|
|
59
|
+
### GitHub Plugin
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { createMCPClient, githubPlugin } from 'integrate-sdk';
|
|
63
|
+
|
|
64
|
+
const client = createMCPClient({
|
|
65
|
+
serverUrl: 'http://localhost:3000/mcp',
|
|
66
|
+
plugins: [
|
|
67
|
+
githubPlugin({
|
|
68
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
69
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
70
|
+
scopes: ['repo', 'user', 'read:org'], // Optional, defaults to ['repo', 'user']
|
|
71
|
+
redirectUri: 'http://localhost:3000/callback', // Optional
|
|
72
|
+
}),
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Available Tools:**
|
|
78
|
+
- `github/createIssue`
|
|
79
|
+
- `github/listIssues`
|
|
80
|
+
- `github/getIssue`
|
|
81
|
+
- `github/updateIssue`
|
|
82
|
+
- `github/closeIssue`
|
|
83
|
+
- `github/createPullRequest`
|
|
84
|
+
- `github/listPullRequests`
|
|
85
|
+
- `github/getPullRequest`
|
|
86
|
+
- `github/mergePullRequest`
|
|
87
|
+
- `github/listRepositories`
|
|
88
|
+
- `github/getRepository`
|
|
89
|
+
- `github/createRepository`
|
|
90
|
+
- And more...
|
|
91
|
+
|
|
92
|
+
### Gmail Plugin
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { createMCPClient, gmailPlugin } from 'integrate-sdk';
|
|
96
|
+
|
|
97
|
+
const client = createMCPClient({
|
|
98
|
+
serverUrl: 'http://localhost:3000/mcp',
|
|
99
|
+
plugins: [
|
|
100
|
+
gmailPlugin({
|
|
101
|
+
clientId: process.env.GMAIL_CLIENT_ID!,
|
|
102
|
+
clientSecret: process.env.GMAIL_CLIENT_SECRET!,
|
|
103
|
+
scopes: [ // Optional, defaults to common Gmail scopes
|
|
104
|
+
'https://www.googleapis.com/auth/gmail.send',
|
|
105
|
+
'https://www.googleapis.com/auth/gmail.readonly',
|
|
106
|
+
],
|
|
107
|
+
}),
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Available Tools:**
|
|
113
|
+
- `gmail/sendEmail`
|
|
114
|
+
- `gmail/listEmails`
|
|
115
|
+
- `gmail/getEmail`
|
|
116
|
+
- `gmail/deleteEmail`
|
|
117
|
+
- `gmail/searchEmails`
|
|
118
|
+
- `gmail/markAsRead`
|
|
119
|
+
- `gmail/markAsUnread`
|
|
120
|
+
- `gmail/listLabels`
|
|
121
|
+
- `gmail/createLabel`
|
|
122
|
+
- And more...
|
|
123
|
+
|
|
124
|
+
## Creating Custom Plugins
|
|
125
|
+
|
|
126
|
+
### Using Generic OAuth Plugin
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { createMCPClient, genericOAuthPlugin } from 'integrate-sdk';
|
|
130
|
+
|
|
131
|
+
const slackPlugin = genericOAuthPlugin({
|
|
132
|
+
id: 'slack',
|
|
133
|
+
provider: 'slack',
|
|
134
|
+
clientId: process.env.SLACK_CLIENT_ID!,
|
|
135
|
+
clientSecret: process.env.SLACK_CLIENT_SECRET!,
|
|
136
|
+
scopes: ['chat:write', 'channels:read', 'users:read'],
|
|
137
|
+
tools: [
|
|
138
|
+
'slack/sendMessage',
|
|
139
|
+
'slack/listChannels',
|
|
140
|
+
'slack/getChannel',
|
|
141
|
+
'slack/inviteUser',
|
|
142
|
+
],
|
|
143
|
+
redirectUri: 'http://localhost:3000/callback',
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const client = createMCPClient({
|
|
147
|
+
serverUrl: 'http://localhost:3000/mcp',
|
|
148
|
+
plugins: [slackPlugin],
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Creating a Simple Plugin (No OAuth)
|
|
153
|
+
|
|
154
|
+
For tools that don't require OAuth:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { createSimplePlugin } from 'integrate-sdk';
|
|
158
|
+
|
|
159
|
+
const mathPlugin = createSimplePlugin({
|
|
160
|
+
id: 'math',
|
|
161
|
+
tools: ['math/add', 'math/subtract', 'math/multiply', 'math/divide'],
|
|
162
|
+
onInit: async (client) => {
|
|
163
|
+
console.log('Math plugin initialized');
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Creating a Custom Plugin from Scratch
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import type { MCPPlugin } from 'integrate-sdk';
|
|
172
|
+
|
|
173
|
+
export function customPlugin(config: CustomConfig): MCPPlugin {
|
|
174
|
+
return {
|
|
175
|
+
id: 'custom',
|
|
176
|
+
tools: ['custom/tool1', 'custom/tool2'],
|
|
177
|
+
oauth: {
|
|
178
|
+
provider: 'custom-provider',
|
|
179
|
+
clientId: config.clientId,
|
|
180
|
+
clientSecret: config.clientSecret,
|
|
181
|
+
scopes: config.scopes,
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
async onInit(client) {
|
|
185
|
+
// Called when plugin is initialized
|
|
186
|
+
console.log('Custom plugin initialized');
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
async onBeforeConnect(client) {
|
|
190
|
+
// Called before connecting to server
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
async onAfterConnect(client) {
|
|
194
|
+
// Called after successful connection
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
async onDisconnect(client) {
|
|
198
|
+
// Called when disconnecting
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Advanced Usage
|
|
205
|
+
|
|
206
|
+
### Accessing OAuth Configurations
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// Get OAuth config for a specific plugin
|
|
210
|
+
const githubOAuth = client.getOAuthConfig('github');
|
|
211
|
+
console.log('GitHub OAuth scopes:', githubOAuth?.scopes);
|
|
212
|
+
|
|
213
|
+
// Get all OAuth configs
|
|
214
|
+
const allConfigs = client.getAllOAuthConfigs();
|
|
215
|
+
for (const [pluginId, config] of allConfigs) {
|
|
216
|
+
console.log(`${pluginId}: ${config.provider}`);
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Listing Available Tools
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
await client.connect();
|
|
224
|
+
|
|
225
|
+
// Get all enabled tools (filtered by plugins)
|
|
226
|
+
const enabledTools = client.getEnabledTools();
|
|
227
|
+
console.log('Enabled tools:', enabledTools.map(t => t.name));
|
|
228
|
+
|
|
229
|
+
// Get all available tools from server
|
|
230
|
+
const allTools = client.getAvailableTools();
|
|
231
|
+
console.log('All tools:', allTools.map(t => t.name));
|
|
232
|
+
|
|
233
|
+
// Get a specific tool
|
|
234
|
+
const tool = client.getTool('github/createIssue');
|
|
235
|
+
console.log('Tool schema:', tool?.inputSchema);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Handling Messages and Notifications
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// Listen for server messages and notifications
|
|
242
|
+
const unsubscribe = client.onMessage((message) => {
|
|
243
|
+
console.log('Received message:', message);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Unsubscribe when done
|
|
247
|
+
unsubscribe();
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Error Handling
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
try {
|
|
254
|
+
await client.connect();
|
|
255
|
+
const result = await client.callTool('github/createIssue', {
|
|
256
|
+
repo: 'owner/repo',
|
|
257
|
+
title: 'Bug report',
|
|
258
|
+
});
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error.message.includes('not enabled')) {
|
|
261
|
+
console.error('Tool is not enabled. Add the appropriate plugin.');
|
|
262
|
+
} else if (error.message.includes('not available')) {
|
|
263
|
+
console.error('Tool is not available on the server.');
|
|
264
|
+
} else {
|
|
265
|
+
console.error('Unexpected error:', error);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Custom Headers and Timeouts
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const client = createMCPClient({
|
|
274
|
+
serverUrl: 'http://localhost:3000/mcp',
|
|
275
|
+
plugins: [/* ... */],
|
|
276
|
+
|
|
277
|
+
// Custom headers
|
|
278
|
+
headers: {
|
|
279
|
+
'Authorization': 'Bearer token',
|
|
280
|
+
'X-Custom-Header': 'value',
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
// Request timeout (default: 30000ms)
|
|
284
|
+
timeout: 60000,
|
|
285
|
+
|
|
286
|
+
// Custom client info
|
|
287
|
+
clientInfo: {
|
|
288
|
+
name: 'my-app',
|
|
289
|
+
version: '1.0.0',
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## API Reference
|
|
295
|
+
|
|
296
|
+
### `createMCPClient(config)`
|
|
297
|
+
|
|
298
|
+
Creates a new MCP client instance.
|
|
299
|
+
|
|
300
|
+
**Parameters:**
|
|
301
|
+
- `config.serverUrl` (string): URL of the MCP server
|
|
302
|
+
- `config.plugins` (MCPPlugin[]): Array of plugins to enable
|
|
303
|
+
- `config.headers` (object, optional): Custom HTTP headers
|
|
304
|
+
- `config.timeout` (number, optional): Request timeout in milliseconds
|
|
305
|
+
- `config.clientInfo` (object, optional): Client name and version
|
|
306
|
+
|
|
307
|
+
**Returns:** `MCPClient` instance
|
|
308
|
+
|
|
309
|
+
### `MCPClient` Methods
|
|
310
|
+
|
|
311
|
+
- `connect()`: Connect to the MCP server
|
|
312
|
+
- `disconnect()`: Disconnect from the server
|
|
313
|
+
- `callTool(name, args)`: Invoke a tool by name
|
|
314
|
+
- `getTool(name)`: Get tool definition by name
|
|
315
|
+
- `getEnabledTools()`: Get all enabled tools
|
|
316
|
+
- `getAvailableTools()`: Get all available tools
|
|
317
|
+
- `getOAuthConfig(pluginId)`: Get OAuth config for a plugin
|
|
318
|
+
- `getAllOAuthConfigs()`: Get all OAuth configurations
|
|
319
|
+
- `onMessage(handler)`: Register a message handler
|
|
320
|
+
- `isConnected()`: Check if connected
|
|
321
|
+
- `isInitialized()`: Check if initialized
|
|
322
|
+
|
|
323
|
+
## Architecture
|
|
324
|
+
|
|
325
|
+
The SDK is built with a modular architecture:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
integrate-sdk/
|
|
329
|
+
├── src/
|
|
330
|
+
│ ├── client.ts # Main MCPClient class
|
|
331
|
+
│ ├── index.ts # Public exports
|
|
332
|
+
│ ├── config/
|
|
333
|
+
│ │ └── types.ts # Configuration types
|
|
334
|
+
│ ├── transport/
|
|
335
|
+
│ │ └── http-stream.ts # HTTP streaming transport (NDJSON)
|
|
336
|
+
│ ├── protocol/
|
|
337
|
+
│ │ ├── messages.ts # MCP message types
|
|
338
|
+
│ │ └── jsonrpc.ts # JSON-RPC implementation
|
|
339
|
+
│ └── plugins/
|
|
340
|
+
│ ├── types.ts # Plugin interface
|
|
341
|
+
│ ├── github.ts # GitHub plugin
|
|
342
|
+
│ ├── gmail.ts # Gmail plugin
|
|
343
|
+
│ └── generic.ts # Generic OAuth plugin
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Transport Layer:**
|
|
347
|
+
The SDK uses HTTP streaming with newline-delimited JSON (NDJSON) for bidirectional communication:
|
|
348
|
+
- Single persistent HTTP connection
|
|
349
|
+
- Messages sent as JSON followed by newline (`\n`)
|
|
350
|
+
- Automatic heartbeat to keep connection alive
|
|
351
|
+
- Compatible with MCP's `StreamableHTTPServer`
|
|
352
|
+
|
|
353
|
+
## MCP Server Requirements
|
|
354
|
+
|
|
355
|
+
Your MCP server should implement HTTP streaming transport compatible with MCP's `StreamableHTTPServer`:
|
|
356
|
+
|
|
357
|
+
- A single streaming endpoint (e.g., `POST /api/v1/mcp`) that:
|
|
358
|
+
- Accepts HTTP POST with streaming request body (NDJSON format)
|
|
359
|
+
- Returns streaming response body (NDJSON format)
|
|
360
|
+
- Supports bidirectional communication over a single persistent connection
|
|
361
|
+
- Messages are newline-delimited JSON (one JSON object per line)
|
|
362
|
+
|
|
363
|
+
And support these MCP protocol methods:
|
|
364
|
+
- `initialize` - Initialize the protocol connection
|
|
365
|
+
- `tools/list` - List available tools
|
|
366
|
+
- `tools/call` - Invoke a tool
|
|
367
|
+
|
|
368
|
+
**Example Go server setup:**
|
|
369
|
+
```go
|
|
370
|
+
httpServer := server.NewStreamableHTTPServer(s,
|
|
371
|
+
server.WithEndpointPath("/api/v1/mcp"),
|
|
372
|
+
server.WithHeartbeatInterval(30*time.Second),
|
|
373
|
+
server.WithStateLess(false),
|
|
374
|
+
)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## TypeScript Support
|
|
378
|
+
|
|
379
|
+
The SDK is built with TypeScript and provides full type safety:
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
import { createMCPClient, githubPlugin } from 'integrate-sdk';
|
|
383
|
+
import type { MCPToolCallResponse } from 'integrate-sdk';
|
|
384
|
+
|
|
385
|
+
const client = createMCPClient({
|
|
386
|
+
serverUrl: 'http://localhost:3000/mcp',
|
|
387
|
+
plugins: [
|
|
388
|
+
githubPlugin({
|
|
389
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
390
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
391
|
+
}),
|
|
392
|
+
],
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Full type inference and IntelliSense support
|
|
396
|
+
await client.connect();
|
|
397
|
+
const result: MCPToolCallResponse = await client.callTool('github/createIssue', {
|
|
398
|
+
repo: 'owner/repo',
|
|
399
|
+
title: 'Bug report',
|
|
400
|
+
});
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
# Test Suite
|
|
404
|
+
|
|
405
|
+
Comprehensive test suite for the Integrate SDK.
|
|
406
|
+
|
|
407
|
+
## Test Structure
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
tests/
|
|
411
|
+
├── protocol/ # Protocol and JSON-RPC tests
|
|
412
|
+
│ └── jsonrpc.test.ts
|
|
413
|
+
├── plugins/ # Plugin system tests
|
|
414
|
+
│ └── plugin-system.test.ts
|
|
415
|
+
├── client/ # Client functionality tests
|
|
416
|
+
│ └── client.test.ts
|
|
417
|
+
├── integration/ # Integration tests with mock server
|
|
418
|
+
│ ├── mock-server.ts
|
|
419
|
+
│ └── integration.test.ts
|
|
420
|
+
├── setup.ts # Test setup and utilities
|
|
421
|
+
└── README.md # This file
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Running Tests
|
|
425
|
+
|
|
426
|
+
### All Tests
|
|
427
|
+
```bash
|
|
428
|
+
bun test
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Unit Tests Only
|
|
432
|
+
```bash
|
|
433
|
+
bun run test:unit
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Integration Tests Only
|
|
437
|
+
```bash
|
|
438
|
+
bun run test:integration
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Watch Mode
|
|
442
|
+
```bash
|
|
443
|
+
bun run test:watch
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### With Coverage
|
|
447
|
+
```bash
|
|
448
|
+
bun run test:coverage
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Test Categories
|
|
452
|
+
|
|
453
|
+
### 1. Protocol Tests (`tests/protocol/`)
|
|
454
|
+
Tests for JSON-RPC 2.0 protocol implementation:
|
|
455
|
+
- Request/response formatting
|
|
456
|
+
- Notification handling
|
|
457
|
+
- Error responses
|
|
458
|
+
- Message parsing and serialization
|
|
459
|
+
- ID generation
|
|
460
|
+
|
|
461
|
+
### 2. Plugin System Tests (`tests/plugins/`)
|
|
462
|
+
Tests for the plugin architecture:
|
|
463
|
+
- GitHub plugin configuration
|
|
464
|
+
- Gmail plugin configuration
|
|
465
|
+
- Generic OAuth plugin creation
|
|
466
|
+
- Simple plugin creation
|
|
467
|
+
- OAuth config type guards
|
|
468
|
+
- Plugin lifecycle hooks
|
|
469
|
+
|
|
470
|
+
### 3. Client Tests (`tests/client/`)
|
|
471
|
+
Tests for the main MCP client:
|
|
472
|
+
- Client creation and configuration
|
|
473
|
+
- Plugin initialization
|
|
474
|
+
- OAuth configuration management
|
|
475
|
+
- Tool management
|
|
476
|
+
- Connection state tracking
|
|
477
|
+
- Error handling
|
|
478
|
+
- Message handlers
|
|
479
|
+
|
|
480
|
+
### 4. Integration Tests (`tests/integration/`)
|
|
481
|
+
End-to-end tests with a mock MCP server:
|
|
482
|
+
- Connection establishment
|
|
483
|
+
- Tool discovery
|
|
484
|
+
- Tool filtering by plugins
|
|
485
|
+
- Tool invocation
|
|
486
|
+
- Plugin lifecycle hooks
|
|
487
|
+
- Concurrent requests
|
|
488
|
+
- Error scenarios
|
|
489
|
+
- Connection timeout
|
|
490
|
+
|
|
491
|
+
## Mock Server
|
|
492
|
+
|
|
493
|
+
The integration tests use a mock MCP server that:
|
|
494
|
+
- Implements HTTP streaming with NDJSON
|
|
495
|
+
- Supports `initialize`, `tools/list`, and `tools/call` methods
|
|
496
|
+
- Returns configurable tools
|
|
497
|
+
- Handles heartbeat/ping messages
|
|
498
|
+
- Runs on a random port to avoid conflicts
|
|
499
|
+
|
|
500
|
+
Example usage:
|
|
501
|
+
```typescript
|
|
502
|
+
import { MockMCPServer } from './tests/integration/mock-server';
|
|
503
|
+
|
|
504
|
+
const server = new MockMCPServer({
|
|
505
|
+
port: 3456,
|
|
506
|
+
tools: [
|
|
507
|
+
{
|
|
508
|
+
name: 'test/echo',
|
|
509
|
+
description: 'Echo test tool',
|
|
510
|
+
inputSchema: { /* ... */ }
|
|
511
|
+
}
|
|
512
|
+
]
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
await server.start();
|
|
516
|
+
// Run tests...
|
|
517
|
+
await server.stop();
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## Writing New Tests
|
|
521
|
+
|
|
522
|
+
### Unit Test Example
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
import { describe, test, expect } from "bun:test";
|
|
526
|
+
import { myFunction } from "../../src/module.js";
|
|
527
|
+
|
|
528
|
+
describe("My Module", () => {
|
|
529
|
+
test("does something correctly", () => {
|
|
530
|
+
const result = myFunction("input");
|
|
531
|
+
expect(result).toBe("expected output");
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Integration Test Example
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
|
|
540
|
+
import { createMCPClient } from "../../src/client.js";
|
|
541
|
+
import { MockMCPServer } from "./mock-server.js";
|
|
542
|
+
|
|
543
|
+
describe("Integration Test", () => {
|
|
544
|
+
let server: MockMCPServer;
|
|
545
|
+
|
|
546
|
+
beforeAll(async () => {
|
|
547
|
+
server = new MockMCPServer({ port: 3456 });
|
|
548
|
+
await server.start();
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
afterAll(async () => {
|
|
552
|
+
await server.stop();
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
test("connects and calls tool", async () => {
|
|
556
|
+
const client = createMCPClient({
|
|
557
|
+
serverUrl: server.getUrl(),
|
|
558
|
+
plugins: [/* ... */],
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
await client.connect();
|
|
562
|
+
const result = await client.callTool("test/tool");
|
|
563
|
+
expect(result).toBeDefined();
|
|
564
|
+
await client.disconnect();
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## Test Coverage
|
|
570
|
+
|
|
571
|
+
The test suite covers:
|
|
572
|
+
- ✅ JSON-RPC protocol implementation
|
|
573
|
+
- ✅ Plugin system and configuration
|
|
574
|
+
- ✅ Client initialization and lifecycle
|
|
575
|
+
- ✅ Tool discovery and filtering
|
|
576
|
+
- ✅ Tool invocation
|
|
577
|
+
- ✅ OAuth configuration management
|
|
578
|
+
- ✅ Error handling
|
|
579
|
+
- ✅ Connection management
|
|
580
|
+
- ✅ Concurrent requests
|
|
581
|
+
- ✅ Plugin lifecycle hooks
|
|
582
|
+
|
|
583
|
+
## Debugging Tests
|
|
584
|
+
|
|
585
|
+
Run tests with debug output:
|
|
586
|
+
```bash
|
|
587
|
+
DEBUG=1 bun test
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Run specific test file:
|
|
591
|
+
```bash
|
|
592
|
+
bun test tests/protocol/jsonrpc.test.ts
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Run specific test:
|
|
596
|
+
```bash
|
|
597
|
+
bun test -t "creates valid JSON-RPC request"
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
## Continuous Integration
|
|
601
|
+
|
|
602
|
+
Tests run automatically on:
|
|
603
|
+
- Push to `main` or `develop` branches
|
|
604
|
+
- Pull requests to `main` or `develop`
|
|
605
|
+
|
|
606
|
+
The CI pipeline runs:
|
|
607
|
+
1. Type checking
|
|
608
|
+
2. Unit tests
|
|
609
|
+
3. Integration tests
|
|
610
|
+
4. Build verification
|
|
611
|
+
|
|
612
|
+
See `.github/workflows/test.yml` for details.
|
|
613
|
+
|
|
614
|
+
## Common Issues
|
|
615
|
+
|
|
616
|
+
### Port Already in Use
|
|
617
|
+
Integration tests use port 3456. If this conflicts, modify `MockMCPServer` constructor.
|
|
618
|
+
|
|
619
|
+
### Test Timeouts
|
|
620
|
+
Integration tests have 10s timeout. Increase if needed:
|
|
621
|
+
```typescript
|
|
622
|
+
test("my test", async () => {
|
|
623
|
+
// test code
|
|
624
|
+
}, 20000); // 20 second timeout
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### Console Logs
|
|
628
|
+
Console logs are suppressed during tests unless `DEBUG=1` is set.
|
|
629
|
+
|
|
630
|
+
## Contributing
|
|
631
|
+
|
|
632
|
+
When adding new features:
|
|
633
|
+
1. Write unit tests for individual components
|
|
634
|
+
2. Write integration tests for end-to-end flows
|
|
635
|
+
3. Ensure all tests pass before submitting PR
|
|
636
|
+
4. Maintain test coverage above 80%
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
|