cowork-os 0.3.21 → 0.3.23

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.
Files changed (170) hide show
  1. package/README.md +293 -6
  2. package/connectors/README.md +20 -0
  3. package/connectors/asana-mcp/README.md +24 -0
  4. package/connectors/asana-mcp/dist/index.js +427 -0
  5. package/connectors/asana-mcp/package.json +15 -0
  6. package/connectors/asana-mcp/src/index.ts +553 -0
  7. package/connectors/asana-mcp/tsconfig.json +13 -0
  8. package/connectors/hubspot-mcp/README.md +35 -0
  9. package/connectors/hubspot-mcp/dist/index.js +454 -0
  10. package/connectors/hubspot-mcp/package.json +15 -0
  11. package/connectors/hubspot-mcp/src/index.ts +562 -0
  12. package/connectors/hubspot-mcp/tsconfig.json +13 -0
  13. package/connectors/jira-mcp/README.md +49 -0
  14. package/connectors/jira-mcp/dist/index.js +588 -0
  15. package/connectors/jira-mcp/package.json +15 -0
  16. package/connectors/jira-mcp/src/index.ts +711 -0
  17. package/connectors/jira-mcp/tsconfig.json +13 -0
  18. package/connectors/linear-mcp/README.md +22 -0
  19. package/connectors/linear-mcp/dist/index.js +402 -0
  20. package/connectors/linear-mcp/package.json +15 -0
  21. package/connectors/linear-mcp/src/index.ts +522 -0
  22. package/connectors/linear-mcp/tsconfig.json +13 -0
  23. package/connectors/okta-mcp/README.md +24 -0
  24. package/connectors/okta-mcp/dist/index.js +411 -0
  25. package/connectors/okta-mcp/package.json +15 -0
  26. package/connectors/okta-mcp/src/index.ts +520 -0
  27. package/connectors/okta-mcp/tsconfig.json +13 -0
  28. package/connectors/salesforce-mcp/README.md +47 -0
  29. package/connectors/salesforce-mcp/dist/index.js +584 -0
  30. package/connectors/salesforce-mcp/package.json +15 -0
  31. package/connectors/salesforce-mcp/src/index.ts +722 -0
  32. package/connectors/salesforce-mcp/tsconfig.json +13 -0
  33. package/connectors/servicenow-mcp/README.md +26 -0
  34. package/connectors/servicenow-mcp/dist/index.js +400 -0
  35. package/connectors/servicenow-mcp/package.json +15 -0
  36. package/connectors/servicenow-mcp/src/index.ts +500 -0
  37. package/connectors/servicenow-mcp/tsconfig.json +13 -0
  38. package/connectors/templates/mcp-connector/README.md +31 -0
  39. package/connectors/templates/mcp-connector/package.json +15 -0
  40. package/connectors/templates/mcp-connector/src/index.ts +330 -0
  41. package/connectors/templates/mcp-connector/tsconfig.json +13 -0
  42. package/connectors/zendesk-mcp/README.md +40 -0
  43. package/connectors/zendesk-mcp/dist/index.js +431 -0
  44. package/connectors/zendesk-mcp/package.json +15 -0
  45. package/connectors/zendesk-mcp/src/index.ts +543 -0
  46. package/connectors/zendesk-mcp/tsconfig.json +13 -0
  47. package/dist/electron/electron/agent/daemon.js +25 -0
  48. package/dist/electron/electron/agent/executor.js +181 -26
  49. package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
  50. package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
  51. package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
  52. package/dist/electron/electron/agent/llm/index.js +11 -1
  53. package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
  54. package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
  55. package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
  56. package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
  57. package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
  58. package/dist/electron/electron/agent/llm/provider-factory.js +318 -4
  59. package/dist/electron/electron/agent/llm/types.js +66 -1
  60. package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
  61. package/dist/electron/electron/agent/tools/box-tools.js +231 -0
  62. package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
  63. package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
  64. package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
  65. package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
  66. package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
  67. package/dist/electron/electron/agent/tools/registry.js +541 -0
  68. package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
  69. package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
  70. package/dist/electron/electron/agent/tools/x-tools.js +1 -1
  71. package/dist/electron/electron/gateway/index.js +1 -0
  72. package/dist/electron/electron/gateway/router.js +123 -143
  73. package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
  74. package/dist/electron/electron/ipc/handlers.js +627 -158
  75. package/dist/electron/electron/main.js +63 -0
  76. package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
  77. package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
  78. package/dist/electron/electron/memory/MemoryService.js +1 -1
  79. package/dist/electron/electron/preload.js +74 -1
  80. package/dist/electron/electron/settings/box-manager.js +54 -0
  81. package/dist/electron/electron/settings/dropbox-manager.js +54 -0
  82. package/dist/electron/electron/settings/google-drive-manager.js +54 -0
  83. package/dist/electron/electron/settings/notion-manager.js +56 -0
  84. package/dist/electron/electron/settings/onedrive-manager.js +54 -0
  85. package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
  86. package/dist/electron/electron/utils/box-api.js +153 -0
  87. package/dist/electron/electron/utils/dropbox-api.js +144 -0
  88. package/dist/electron/electron/utils/env-migration.js +19 -0
  89. package/dist/electron/electron/utils/google-drive-api.js +152 -0
  90. package/dist/electron/electron/utils/notion-api.js +103 -0
  91. package/dist/electron/electron/utils/onedrive-api.js +113 -0
  92. package/dist/electron/electron/utils/sharepoint-api.js +109 -0
  93. package/dist/electron/electron/utils/validation.js +82 -3
  94. package/dist/electron/electron/utils/x-cli.js +1 -1
  95. package/dist/electron/shared/channelMessages.js +284 -3
  96. package/dist/electron/shared/llm-provider-catalog.js +198 -0
  97. package/dist/electron/shared/types.js +88 -1
  98. package/package.json +12 -2
  99. package/src/electron/agent/executor.ts +205 -28
  100. package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
  101. package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
  102. package/src/electron/agent/llm/groq-provider.ts +39 -0
  103. package/src/electron/agent/llm/index.ts +5 -0
  104. package/src/electron/agent/llm/kimi-provider.ts +39 -0
  105. package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
  106. package/src/electron/agent/llm/openai-compatible.ts +133 -0
  107. package/src/electron/agent/llm/openai-oauth.ts +2 -1
  108. package/src/electron/agent/llm/openrouter-provider.ts +2 -1
  109. package/src/electron/agent/llm/provider-factory.ts +414 -6
  110. package/src/electron/agent/llm/types.ts +90 -1
  111. package/src/electron/agent/llm/xai-provider.ts +39 -0
  112. package/src/electron/agent/tools/box-tools.ts +239 -0
  113. package/src/electron/agent/tools/builtin-settings.ts +34 -0
  114. package/src/electron/agent/tools/dropbox-tools.ts +237 -0
  115. package/src/electron/agent/tools/google-drive-tools.ts +228 -0
  116. package/src/electron/agent/tools/notion-tools.ts +330 -0
  117. package/src/electron/agent/tools/onedrive-tools.ts +217 -0
  118. package/src/electron/agent/tools/registry.ts +565 -0
  119. package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
  120. package/src/electron/agent/tools/shell-tools.ts +11 -3
  121. package/src/electron/agent/tools/x-tools.ts +1 -1
  122. package/src/electron/database/SecureSettingsRepository.ts +7 -1
  123. package/src/electron/gateway/index.ts +1 -0
  124. package/src/electron/gateway/router.ts +134 -149
  125. package/src/electron/ipc/canvas-handlers.ts +10 -0
  126. package/src/electron/ipc/handlers.ts +673 -153
  127. package/src/electron/main.ts +35 -0
  128. package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
  129. package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
  130. package/src/electron/memory/MemoryService.ts +5 -1
  131. package/src/electron/preload.ts +167 -4
  132. package/src/electron/settings/box-manager.ts +58 -0
  133. package/src/electron/settings/dropbox-manager.ts +58 -0
  134. package/src/electron/settings/google-drive-manager.ts +58 -0
  135. package/src/electron/settings/notion-manager.ts +60 -0
  136. package/src/electron/settings/onedrive-manager.ts +58 -0
  137. package/src/electron/settings/sharepoint-manager.ts +58 -0
  138. package/src/electron/utils/box-api.ts +184 -0
  139. package/src/electron/utils/dropbox-api.ts +171 -0
  140. package/src/electron/utils/env-migration.ts +22 -0
  141. package/src/electron/utils/google-drive-api.ts +183 -0
  142. package/src/electron/utils/notion-api.ts +126 -0
  143. package/src/electron/utils/onedrive-api.ts +137 -0
  144. package/src/electron/utils/sharepoint-api.ts +132 -0
  145. package/src/electron/utils/validation.ts +102 -1
  146. package/src/electron/utils/x-cli.ts +1 -1
  147. package/src/renderer/App.tsx +20 -2
  148. package/src/renderer/components/BoxSettings.tsx +203 -0
  149. package/src/renderer/components/BrowserView.tsx +101 -0
  150. package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
  151. package/src/renderer/components/CanvasPreview.tsx +68 -1
  152. package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
  153. package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
  154. package/src/renderer/components/ConnectorsSettings.tsx +397 -0
  155. package/src/renderer/components/DropboxSettings.tsx +202 -0
  156. package/src/renderer/components/GoogleDriveSettings.tsx +201 -0
  157. package/src/renderer/components/MCPSettings.tsx +56 -0
  158. package/src/renderer/components/MainContent.tsx +270 -34
  159. package/src/renderer/components/NotionSettings.tsx +231 -0
  160. package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
  161. package/src/renderer/components/OnboardingModal.tsx +70 -1
  162. package/src/renderer/components/OneDriveSettings.tsx +212 -0
  163. package/src/renderer/components/Settings.tsx +611 -8
  164. package/src/renderer/components/SharePointSettings.tsx +224 -0
  165. package/src/renderer/components/Sidebar.tsx +25 -9
  166. package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
  167. package/src/renderer/styles/index.css +438 -25
  168. package/src/shared/channelMessages.ts +367 -4
  169. package/src/shared/llm-provider-catalog.ts +217 -0
  170. package/src/shared/types.ts +226 -1
@@ -0,0 +1,562 @@
1
+ import * as readline from 'readline';
2
+
3
+ // ==================== MCP Types ====================
4
+
5
+ type JSONRPCId = string | number;
6
+
7
+ type JSONRPCRequest = {
8
+ jsonrpc: '2.0';
9
+ id: JSONRPCId;
10
+ method: string;
11
+ params?: Record<string, any>;
12
+ };
13
+
14
+ type JSONRPCNotification = {
15
+ jsonrpc: '2.0';
16
+ method: string;
17
+ params?: Record<string, any>;
18
+ };
19
+
20
+ type JSONRPCResponse = {
21
+ jsonrpc: '2.0';
22
+ id: JSONRPCId;
23
+ result?: any;
24
+ error?: { code: number; message: string; data?: any };
25
+ };
26
+
27
+ type MCPToolProperty = {
28
+ type: string;
29
+ description?: string;
30
+ enum?: string[];
31
+ default?: any;
32
+ items?: MCPToolProperty;
33
+ properties?: Record<string, MCPToolProperty>;
34
+ required?: string[];
35
+ };
36
+
37
+ type MCPTool = {
38
+ name: string;
39
+ description?: string;
40
+ inputSchema: {
41
+ type: 'object';
42
+ properties?: Record<string, MCPToolProperty>;
43
+ required?: string[];
44
+ additionalProperties?: boolean;
45
+ };
46
+ };
47
+
48
+ type MCPServerInfo = {
49
+ name: string;
50
+ version: string;
51
+ protocolVersion?: string;
52
+ capabilities?: {
53
+ tools?: { listChanged?: boolean };
54
+ };
55
+ };
56
+
57
+ const PROTOCOL_VERSION = '2024-11-05';
58
+
59
+ const MCP_METHODS = {
60
+ INITIALIZE: 'initialize',
61
+ INITIALIZED: 'notifications/initialized',
62
+ SHUTDOWN: 'shutdown',
63
+ TOOLS_LIST: 'tools/list',
64
+ TOOLS_CALL: 'tools/call',
65
+ } as const;
66
+
67
+ const MCP_ERROR_CODES = {
68
+ PARSE_ERROR: -32700,
69
+ INVALID_REQUEST: -32600,
70
+ METHOD_NOT_FOUND: -32601,
71
+ INVALID_PARAMS: -32602,
72
+ INTERNAL_ERROR: -32603,
73
+ SERVER_NOT_INITIALIZED: -32002,
74
+ } as const;
75
+
76
+ // ==================== HubSpot Client ====================
77
+
78
+ type HubSpotConfig = {
79
+ baseUrl: string;
80
+ accessToken?: string;
81
+ clientId?: string;
82
+ clientSecret?: string;
83
+ refreshToken?: string;
84
+ };
85
+
86
+ type RequestMeta = {
87
+ durationMs: number;
88
+ vendorRequestId?: string;
89
+ baseUrl: string;
90
+ };
91
+
92
+ type RequestResult = {
93
+ data: any;
94
+ meta: RequestMeta;
95
+ nextCursor?: string;
96
+ };
97
+
98
+ class HubSpotClient {
99
+ constructor(private config: HubSpotConfig) {}
100
+
101
+ async health(): Promise<RequestResult> {
102
+ return this.requestJson('GET', 'account-info/v3/details');
103
+ }
104
+
105
+ async searchObjects(objectType: string, payload: Record<string, any>): Promise<RequestResult> {
106
+ return this.requestJson('POST', `crm/v3/objects/${encodeURIComponent(objectType)}/search`, payload);
107
+ }
108
+
109
+ async getObject(objectType: string, objectId: string, properties?: string[]): Promise<RequestResult> {
110
+ const params = new URLSearchParams();
111
+ if (properties && properties.length > 0) {
112
+ params.set('properties', properties.join(','));
113
+ }
114
+ const query = params.toString();
115
+ return this.requestJson('GET', `crm/v3/objects/${encodeURIComponent(objectType)}/${encodeURIComponent(objectId)}${query ? `?${query}` : ''}`);
116
+ }
117
+
118
+ async createObject(objectType: string, properties: Record<string, any>): Promise<RequestResult> {
119
+ return this.requestJson('POST', `crm/v3/objects/${encodeURIComponent(objectType)}`, { properties });
120
+ }
121
+
122
+ async updateObject(objectType: string, objectId: string, properties: Record<string, any>): Promise<RequestResult> {
123
+ return this.requestJson('PATCH', `crm/v3/objects/${encodeURIComponent(objectType)}/${encodeURIComponent(objectId)}`, { properties });
124
+ }
125
+
126
+ private getBaseUrl(): string {
127
+ return this.config.baseUrl.replace(/\/$/, '');
128
+ }
129
+
130
+ private getAuthHeader(): string {
131
+ if (!this.config.accessToken) {
132
+ throw new Error('HUBSPOT_ACCESS_TOKEN is required');
133
+ }
134
+ return `Bearer ${this.config.accessToken}`;
135
+ }
136
+
137
+ private async requestJson(method: string, path: string, body?: any): Promise<RequestResult> {
138
+ const start = Date.now();
139
+ const url = `${this.getBaseUrl()}/${path.replace(/^\//, '')}`;
140
+
141
+ const res = await fetch(url, {
142
+ method,
143
+ headers: {
144
+ Authorization: await this.ensureAuthHeader(),
145
+ 'Content-Type': 'application/json',
146
+ 'User-Agent': 'CoWork-HubSpot-Connector/0.1.0',
147
+ },
148
+ body: body ? JSON.stringify(body) : undefined,
149
+ });
150
+
151
+ const durationMs = Date.now() - start;
152
+ const vendorRequestId = res.headers.get('x-hubspot-request-id') || undefined;
153
+
154
+ if (res.status === 401 && this.canRefresh()) {
155
+ await this.refreshAccessToken();
156
+ return this.requestJson(method, path, body);
157
+ }
158
+
159
+ if (!res.ok) {
160
+ const message = await res.text();
161
+ throw new Error(message || `HubSpot API error (${res.status})`);
162
+ }
163
+
164
+ let data: any = null;
165
+ if (res.status !== 204) {
166
+ data = await res.json();
167
+ }
168
+
169
+ const nextCursor = data?.paging?.next?.after;
170
+
171
+ return {
172
+ data,
173
+ meta: {
174
+ durationMs,
175
+ vendorRequestId,
176
+ baseUrl: this.config.baseUrl,
177
+ },
178
+ nextCursor,
179
+ };
180
+ }
181
+
182
+ private canRefresh(): boolean {
183
+ return Boolean(this.config.clientId && this.config.clientSecret && this.config.refreshToken);
184
+ }
185
+
186
+ private async ensureAuthHeader(): Promise<string> {
187
+ if (this.config.accessToken) {
188
+ return `Bearer ${this.config.accessToken}`;
189
+ }
190
+ if (!this.canRefresh()) {
191
+ throw new Error('HUBSPOT_ACCESS_TOKEN is required');
192
+ }
193
+ await this.refreshAccessToken();
194
+ if (!this.config.accessToken) {
195
+ throw new Error('Failed to refresh HubSpot access token');
196
+ }
197
+ return `Bearer ${this.config.accessToken}`;
198
+ }
199
+
200
+ private async refreshAccessToken(): Promise<void> {
201
+ if (!this.canRefresh()) {
202
+ throw new Error('Missing HubSpot refresh credentials');
203
+ }
204
+
205
+ const params = new URLSearchParams({
206
+ grant_type: 'refresh_token',
207
+ client_id: this.config.clientId as string,
208
+ client_secret: this.config.clientSecret as string,
209
+ refresh_token: this.config.refreshToken as string,
210
+ });
211
+
212
+ const res = await fetch('https://api.hubapi.com/oauth/v1/token', {
213
+ method: 'POST',
214
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
215
+ body: params.toString(),
216
+ });
217
+
218
+ if (!res.ok) {
219
+ const text = await res.text();
220
+ throw new Error(`HubSpot OAuth refresh failed: ${text}`);
221
+ }
222
+
223
+ const data = await res.json();
224
+ if (!data.access_token) {
225
+ throw new Error('HubSpot OAuth refresh returned no access_token');
226
+ }
227
+
228
+ this.config.accessToken = data.access_token;
229
+ if (data.refresh_token) {
230
+ this.config.refreshToken = data.refresh_token;
231
+ }
232
+ }
233
+ }
234
+
235
+ // ==================== MCP Stdio Server ====================
236
+
237
+ type ToolProvider = {
238
+ getTools(): MCPTool[];
239
+ executeTool(name: string, args: Record<string, any>): Promise<any>;
240
+ };
241
+
242
+ class StdioMCPServer {
243
+ private initialized = false;
244
+ private rl: readline.Interface | null = null;
245
+
246
+ constructor(
247
+ private toolProvider: ToolProvider,
248
+ private serverInfo: MCPServerInfo
249
+ ) {}
250
+
251
+ start(): void {
252
+ this.rl = readline.createInterface({
253
+ input: process.stdin,
254
+ output: process.stdout,
255
+ terminal: false,
256
+ });
257
+
258
+ this.rl.on('line', (line) => this.handleLine(line));
259
+ this.rl.on('close', () => this.stop());
260
+
261
+ process.on('SIGINT', () => this.stop());
262
+ process.on('SIGTERM', () => this.stop());
263
+ }
264
+
265
+ stop(): void {
266
+ if (this.rl) {
267
+ this.rl.close();
268
+ this.rl = null;
269
+ }
270
+ process.exit(0);
271
+ }
272
+
273
+ private handleLine(line: string): void {
274
+ const trimmed = line.trim();
275
+ if (!trimmed) return;
276
+
277
+ try {
278
+ const message = JSON.parse(trimmed);
279
+ this.handleMessage(message);
280
+ } catch {
281
+ this.sendError(0, MCP_ERROR_CODES.PARSE_ERROR, 'Parse error');
282
+ }
283
+ }
284
+
285
+ private async handleMessage(message: any): Promise<void> {
286
+ if ('id' in message && message.id !== null) {
287
+ await this.handleRequest(message as JSONRPCRequest);
288
+ return;
289
+ }
290
+
291
+ if ('method' in message) {
292
+ await this.handleNotification(message as JSONRPCNotification);
293
+ }
294
+ }
295
+
296
+ private async handleRequest(request: JSONRPCRequest): Promise<void> {
297
+ const { id, method, params } = request;
298
+
299
+ try {
300
+ let result: any;
301
+
302
+ switch (method) {
303
+ case MCP_METHODS.INITIALIZE:
304
+ result = this.handleInitialize(params);
305
+ break;
306
+ case MCP_METHODS.TOOLS_LIST:
307
+ this.requireInitialized();
308
+ result = this.handleToolsList();
309
+ break;
310
+ case MCP_METHODS.TOOLS_CALL:
311
+ this.requireInitialized();
312
+ result = await this.handleToolsCall(params);
313
+ break;
314
+ case MCP_METHODS.SHUTDOWN:
315
+ result = this.handleShutdown();
316
+ break;
317
+ default:
318
+ throw this.createError(MCP_ERROR_CODES.METHOD_NOT_FOUND, `Method not found: ${method}`);
319
+ }
320
+
321
+ this.sendResult(id, result);
322
+ } catch (error: any) {
323
+ if (error.code !== undefined) {
324
+ this.sendError(id, error.code, error.message, error.data);
325
+ } else {
326
+ this.sendError(id, MCP_ERROR_CODES.INTERNAL_ERROR, error?.message || 'Internal error');
327
+ }
328
+ }
329
+ }
330
+
331
+ private async handleNotification(notification: JSONRPCNotification): Promise<void> {
332
+ const { method } = notification;
333
+
334
+ if (method === MCP_METHODS.INITIALIZED) {
335
+ this.initialized = true;
336
+ }
337
+ }
338
+
339
+ private handleInitialize(_params: any): {
340
+ protocolVersion: string;
341
+ capabilities: MCPServerInfo['capabilities'];
342
+ serverInfo: MCPServerInfo;
343
+ } {
344
+ if (this.initialized) {
345
+ throw this.createError(MCP_ERROR_CODES.INVALID_REQUEST, 'Already initialized');
346
+ }
347
+
348
+ return {
349
+ protocolVersion: PROTOCOL_VERSION,
350
+ capabilities: this.serverInfo.capabilities,
351
+ serverInfo: this.serverInfo,
352
+ };
353
+ }
354
+
355
+ private handleToolsList(): { tools: MCPTool[] } {
356
+ return { tools: this.toolProvider.getTools() };
357
+ }
358
+
359
+ private async handleToolsCall(params: any): Promise<any> {
360
+ const { name, arguments: args } = params || {};
361
+ if (!name) {
362
+ throw this.createError(MCP_ERROR_CODES.INVALID_PARAMS, 'Tool name is required');
363
+ }
364
+
365
+ try {
366
+ const result = await this.toolProvider.executeTool(name, args || {});
367
+
368
+ if (typeof result === 'string') {
369
+ return { content: [{ type: 'text', text: result }] };
370
+ }
371
+
372
+ if (result && typeof result === 'object') {
373
+ if (result.content && Array.isArray(result.content)) {
374
+ return result;
375
+ }
376
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
377
+ }
378
+
379
+ return { content: [{ type: 'text', text: String(result) }] };
380
+ } catch (error: any) {
381
+ return {
382
+ content: [{ type: 'text', text: `Error: ${error?.message || 'Tool failed'}` }],
383
+ isError: true,
384
+ };
385
+ }
386
+ }
387
+
388
+ private handleShutdown(): Record<string, never> {
389
+ setImmediate(() => this.stop());
390
+ return {};
391
+ }
392
+
393
+ private sendResult(id: JSONRPCId, result: any): void {
394
+ const response: JSONRPCResponse = { jsonrpc: '2.0', id, result };
395
+ this.sendMessage(response);
396
+ }
397
+
398
+ private sendError(id: JSONRPCId, code: number, message: string, data?: any): void {
399
+ const response: JSONRPCResponse = {
400
+ jsonrpc: '2.0',
401
+ id,
402
+ error: { code, message, data },
403
+ };
404
+ this.sendMessage(response);
405
+ }
406
+
407
+ private sendMessage(message: JSONRPCResponse | JSONRPCNotification): void {
408
+ process.stdout.write(JSON.stringify(message) + '\n');
409
+ }
410
+
411
+ private requireInitialized(): void {
412
+ if (!this.initialized) {
413
+ throw this.createError(MCP_ERROR_CODES.SERVER_NOT_INITIALIZED, 'Server not initialized');
414
+ }
415
+ }
416
+
417
+ private createError(code: number, message: string, data?: any): { code: number; message: string; data?: any } {
418
+ return { code, message, data };
419
+ }
420
+ }
421
+
422
+ // ==================== Tool Definitions ====================
423
+
424
+ const CONNECTOR_PREFIX = 'hubspot';
425
+ const DEFAULT_BASE_URL = 'https://api.hubapi.com';
426
+
427
+ const tools: MCPTool[] = [
428
+ {
429
+ name: `${CONNECTOR_PREFIX}.health`,
430
+ description: 'Check connector health and authentication status',
431
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
432
+ },
433
+ {
434
+ name: `${CONNECTOR_PREFIX}.search_objects`,
435
+ description: 'Search CRM objects with HubSpot search API',
436
+ inputSchema: {
437
+ type: 'object',
438
+ properties: {
439
+ objectType: { type: 'string', description: 'Object type (e.g., contacts, companies, deals)' },
440
+ filterGroups: { type: 'array', description: 'HubSpot filterGroups array', items: { type: 'object' } },
441
+ properties: { type: 'array', description: 'Properties to return', items: { type: 'string' } },
442
+ limit: { type: 'number', description: 'Max results' },
443
+ after: { type: 'string', description: 'Paging cursor' },
444
+ },
445
+ required: ['objectType'],
446
+ additionalProperties: false,
447
+ },
448
+ },
449
+ {
450
+ name: `${CONNECTOR_PREFIX}.get_object`,
451
+ description: 'Fetch a CRM object by ID',
452
+ inputSchema: {
453
+ type: 'object',
454
+ properties: {
455
+ objectType: { type: 'string', description: 'Object type (e.g., contacts, companies, deals)' },
456
+ id: { type: 'string', description: 'Object ID' },
457
+ properties: { type: 'array', description: 'Properties to return', items: { type: 'string' } },
458
+ },
459
+ required: ['objectType', 'id'],
460
+ additionalProperties: false,
461
+ },
462
+ },
463
+ {
464
+ name: `${CONNECTOR_PREFIX}.create_object`,
465
+ description: 'Create a CRM object',
466
+ inputSchema: {
467
+ type: 'object',
468
+ properties: {
469
+ objectType: { type: 'string', description: 'Object type (e.g., contacts, companies, deals)' },
470
+ properties: { type: 'object', description: 'Properties for creation' },
471
+ },
472
+ required: ['objectType', 'properties'],
473
+ additionalProperties: false,
474
+ },
475
+ },
476
+ {
477
+ name: `${CONNECTOR_PREFIX}.update_object`,
478
+ description: 'Update a CRM object',
479
+ inputSchema: {
480
+ type: 'object',
481
+ properties: {
482
+ objectType: { type: 'string', description: 'Object type (e.g., contacts, companies, deals)' },
483
+ id: { type: 'string', description: 'Object ID' },
484
+ properties: { type: 'object', description: 'Properties for update' },
485
+ },
486
+ required: ['objectType', 'id', 'properties'],
487
+ additionalProperties: false,
488
+ },
489
+ },
490
+ ];
491
+
492
+ const config: HubSpotConfig = {
493
+ baseUrl: process.env.HUBSPOT_BASE_URL || DEFAULT_BASE_URL,
494
+ accessToken: process.env.HUBSPOT_ACCESS_TOKEN,
495
+ clientId: process.env.HUBSPOT_CLIENT_ID,
496
+ clientSecret: process.env.HUBSPOT_CLIENT_SECRET,
497
+ refreshToken: process.env.HUBSPOT_REFRESH_TOKEN,
498
+ };
499
+
500
+ const client = new HubSpotClient(config);
501
+
502
+ const handlers: Record<string, (args: Record<string, any>) => Promise<any>> = {
503
+ [`${CONNECTOR_PREFIX}.health`]: async () => buildEnvelope(await client.health()),
504
+ [`${CONNECTOR_PREFIX}.search_objects`]: async (args) => {
505
+ const payload: Record<string, any> = {};
506
+ if (args.filterGroups) payload.filterGroups = args.filterGroups;
507
+ if (args.properties) payload.properties = args.properties;
508
+ if (args.limit) payload.limit = args.limit;
509
+ if (args.after) payload.after = args.after;
510
+ const result = await client.searchObjects(args.objectType, payload);
511
+ return buildEnvelope(result);
512
+ },
513
+ [`${CONNECTOR_PREFIX}.get_object`]: async (args) => {
514
+ const result = await client.getObject(args.objectType, args.id, args.properties);
515
+ return buildEnvelope(result);
516
+ },
517
+ [`${CONNECTOR_PREFIX}.create_object`]: async (args) => {
518
+ const result = await client.createObject(args.objectType, args.properties || {});
519
+ return buildEnvelope(result);
520
+ },
521
+ [`${CONNECTOR_PREFIX}.update_object`]: async (args) => {
522
+ const result = await client.updateObject(args.objectType, args.id, args.properties || {});
523
+ return buildEnvelope(result);
524
+ },
525
+ };
526
+
527
+ const toolProvider: ToolProvider = {
528
+ getTools: () => tools,
529
+ executeTool: async (name, args) => {
530
+ const handler = handlers[name];
531
+ if (!handler) {
532
+ throw new Error(`Unknown tool: ${name}`);
533
+ }
534
+ return handler(args);
535
+ },
536
+ };
537
+
538
+ const serverInfo: MCPServerInfo = {
539
+ name: 'HubSpot Connector',
540
+ version: '0.1.0',
541
+ protocolVersion: PROTOCOL_VERSION,
542
+ capabilities: {
543
+ tools: { listChanged: false },
544
+ },
545
+ };
546
+
547
+ const server = new StdioMCPServer(toolProvider, serverInfo);
548
+ server.start();
549
+
550
+ function buildEnvelope(result: RequestResult): any {
551
+ return {
552
+ ok: true,
553
+ data: result.data,
554
+ meta: {
555
+ durationMs: result.meta.durationMs,
556
+ vendorRequestId: result.meta.vendorRequestId,
557
+ baseUrl: result.meta.baseUrl,
558
+ },
559
+ nextCursor: result.nextCursor,
560
+ warnings: [],
561
+ };
562
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "rootDir": "src",
6
+ "outDir": "dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "lib": ["ES2020", "DOM"]
11
+ },
12
+ "include": ["src"]
13
+ }
@@ -0,0 +1,49 @@
1
+ # Jira MCP Connector (MVP)
2
+
3
+ This connector exposes Jira Cloud APIs to CoWork OS through MCP tools.
4
+
5
+ ## Requirements
6
+
7
+ Provide credentials via environment variables:
8
+
9
+ - `JIRA_BASE_URL` (required) Example: `https://your-domain.atlassian.net`
10
+
11
+ Authentication options (choose one):
12
+
13
+ **1) OAuth (Bearer token)**
14
+ - `JIRA_ACCESS_TOKEN`
15
+ - Optional refresh: `JIRA_CLIENT_ID`, `JIRA_CLIENT_SECRET`, `JIRA_REFRESH_TOKEN`
16
+
17
+ **2) API token (Basic auth)**
18
+ - `JIRA_EMAIL`
19
+ - `JIRA_API_TOKEN`
20
+
21
+ Optional:
22
+ - `JIRA_API_VERSION` (default: `3`)
23
+ Notes for OAuth:
24
+ - If using Atlassian 3LO OAuth, set `JIRA_BASE_URL` to `https://api.atlassian.com/ex/jira/<cloudId>` (the app can set this automatically when using the OAuth UI).
25
+
26
+ ## Build & Run
27
+
28
+ ```bash
29
+ npm install
30
+ npm run build
31
+ npm start
32
+ ```
33
+
34
+ ## Add to CoWork MCP Settings
35
+
36
+ - **Command**: `node`
37
+ - **Args**: `/absolute/path/to/connectors/jira-mcp/dist/index.js`
38
+ - **Env**: set the variables above
39
+
40
+ ## Tools
41
+
42
+ - `jira.health`
43
+ - `jira.list_projects`
44
+ - `jira.get_issue`
45
+ - `jira.search_issues`
46
+ - `jira.create_issue`
47
+ - `jira.update_issue`
48
+
49
+ See `docs/enterprise-connectors.md` for the contract.