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,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,22 @@
1
+ # Linear MCP Connector (MVP)
2
+
3
+ This connector exposes Linear GraphQL APIs to CoWork OS through MCP tools.
4
+
5
+ ## Requirements
6
+
7
+ - `LINEAR_API_KEY` (required)
8
+
9
+ ## Build & Run
10
+
11
+ ```bash
12
+ npm install
13
+ npm run build
14
+ npm start
15
+ ```
16
+
17
+ ## Tools
18
+
19
+ - `linear.health`
20
+ - `linear.list_projects`
21
+ - `linear.search_issues`
22
+ - `linear.get_issue`
@@ -0,0 +1,402 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const readline = __importStar(require("readline"));
37
+ const PROTOCOL_VERSION = '2024-11-05';
38
+ const MCP_METHODS = {
39
+ INITIALIZE: 'initialize',
40
+ INITIALIZED: 'notifications/initialized',
41
+ SHUTDOWN: 'shutdown',
42
+ TOOLS_LIST: 'tools/list',
43
+ TOOLS_CALL: 'tools/call',
44
+ };
45
+ const MCP_ERROR_CODES = {
46
+ PARSE_ERROR: -32700,
47
+ INVALID_REQUEST: -32600,
48
+ METHOD_NOT_FOUND: -32601,
49
+ INVALID_PARAMS: -32602,
50
+ INTERNAL_ERROR: -32603,
51
+ SERVER_NOT_INITIALIZED: -32002,
52
+ };
53
+ class LinearClient {
54
+ constructor(config) {
55
+ this.config = config;
56
+ }
57
+ async health() {
58
+ const query = `query Viewer { viewer { id name email } }`;
59
+ return this.requestGraphQL(query, undefined, 'viewer');
60
+ }
61
+ async listProjects(limit, cursor) {
62
+ const query = `query Projects($first: Int, $after: String) {
63
+ projects(first: $first, after: $after) {
64
+ nodes { id name state { name } }
65
+ pageInfo { hasNextPage endCursor }
66
+ }
67
+ }`;
68
+ return this.requestGraphQL(query, { first: limit, after: cursor }, 'projects');
69
+ }
70
+ async searchIssues(queryText, limit, cursor, projectId, teamId) {
71
+ const filter = {
72
+ title: { contains: queryText },
73
+ };
74
+ if (projectId) {
75
+ filter.project = { id: { eq: projectId } };
76
+ }
77
+ if (teamId) {
78
+ filter.team = { id: { eq: teamId } };
79
+ }
80
+ const query = `query Issues($first: Int, $after: String, $filter: IssueFilter) {
81
+ issues(first: $first, after: $after, filter: $filter) {
82
+ nodes {
83
+ id
84
+ title
85
+ identifier
86
+ url
87
+ state { id name }
88
+ project { id name }
89
+ team { id name }
90
+ }
91
+ pageInfo { hasNextPage endCursor }
92
+ }
93
+ }`;
94
+ return this.requestGraphQL(query, { first: limit, after: cursor, filter }, 'issues');
95
+ }
96
+ async getIssue(issueId) {
97
+ const query = `query Issue($id: String!) {
98
+ issue(id: $id) {
99
+ id
100
+ title
101
+ identifier
102
+ url
103
+ description
104
+ state { id name }
105
+ project { id name }
106
+ team { id name }
107
+ assignee { id name email }
108
+ }
109
+ }`;
110
+ return this.requestGraphQL(query, { id: issueId }, 'issue');
111
+ }
112
+ getBaseUrl() {
113
+ return this.config.baseUrl.replace(/\/$/, '');
114
+ }
115
+ getAuthHeader() {
116
+ if (!this.config.apiKey) {
117
+ throw new Error('LINEAR_API_KEY is required');
118
+ }
119
+ return `Bearer ${this.config.apiKey}`;
120
+ }
121
+ async requestGraphQL(query, variables, rootField) {
122
+ const start = Date.now();
123
+ const url = this.getBaseUrl();
124
+ const res = await fetch(url, {
125
+ method: 'POST',
126
+ headers: {
127
+ Authorization: this.getAuthHeader(),
128
+ 'Content-Type': 'application/json',
129
+ 'User-Agent': 'CoWork-Linear-Connector/0.1.0',
130
+ },
131
+ body: JSON.stringify({ query, variables }),
132
+ });
133
+ const durationMs = Date.now() - start;
134
+ const vendorRequestId = res.headers.get('x-request-id') || undefined;
135
+ if (!res.ok) {
136
+ const message = await res.text();
137
+ throw new Error(message || `Linear API error (${res.status})`);
138
+ }
139
+ const payload = (await res.json());
140
+ if (payload.errors && payload.errors.length > 0) {
141
+ const message = payload.errors.map((err) => err.message || 'GraphQL error').join('; ');
142
+ throw new Error(message);
143
+ }
144
+ const extracted = rootField ? payload.data?.[rootField] : payload.data;
145
+ const pageInfo = extracted?.pageInfo;
146
+ const nextCursor = pageInfo?.hasNextPage ? pageInfo.endCursor : undefined;
147
+ return {
148
+ data: extracted,
149
+ meta: {
150
+ durationMs,
151
+ vendorRequestId,
152
+ baseUrl: this.config.baseUrl,
153
+ },
154
+ nextCursor,
155
+ };
156
+ }
157
+ }
158
+ class StdioMCPServer {
159
+ constructor(toolProvider, serverInfo) {
160
+ this.toolProvider = toolProvider;
161
+ this.serverInfo = serverInfo;
162
+ this.initialized = false;
163
+ this.rl = null;
164
+ }
165
+ start() {
166
+ this.rl = readline.createInterface({
167
+ input: process.stdin,
168
+ output: process.stdout,
169
+ terminal: false,
170
+ });
171
+ this.rl.on('line', (line) => this.handleLine(line));
172
+ this.rl.on('close', () => this.stop());
173
+ process.on('SIGINT', () => this.stop());
174
+ process.on('SIGTERM', () => this.stop());
175
+ }
176
+ stop() {
177
+ if (this.rl) {
178
+ this.rl.close();
179
+ this.rl = null;
180
+ }
181
+ process.exit(0);
182
+ }
183
+ handleLine(line) {
184
+ const trimmed = line.trim();
185
+ if (!trimmed)
186
+ return;
187
+ try {
188
+ const message = JSON.parse(trimmed);
189
+ this.handleMessage(message);
190
+ }
191
+ catch {
192
+ this.sendError(0, MCP_ERROR_CODES.PARSE_ERROR, 'Parse error');
193
+ }
194
+ }
195
+ async handleMessage(message) {
196
+ if ('id' in message && message.id !== null) {
197
+ await this.handleRequest(message);
198
+ return;
199
+ }
200
+ if ('method' in message) {
201
+ await this.handleNotification(message);
202
+ }
203
+ }
204
+ async handleRequest(request) {
205
+ const { id, method, params } = request;
206
+ try {
207
+ let result;
208
+ switch (method) {
209
+ case MCP_METHODS.INITIALIZE:
210
+ result = this.handleInitialize(params);
211
+ break;
212
+ case MCP_METHODS.TOOLS_LIST:
213
+ this.requireInitialized();
214
+ result = this.handleToolsList();
215
+ break;
216
+ case MCP_METHODS.TOOLS_CALL:
217
+ this.requireInitialized();
218
+ result = await this.handleToolsCall(params);
219
+ break;
220
+ case MCP_METHODS.SHUTDOWN:
221
+ result = this.handleShutdown();
222
+ break;
223
+ default:
224
+ throw this.createError(MCP_ERROR_CODES.METHOD_NOT_FOUND, `Method not found: ${method}`);
225
+ }
226
+ this.sendResult(id, result);
227
+ }
228
+ catch (error) {
229
+ if (error.code !== undefined) {
230
+ this.sendError(id, error.code, error.message, error.data);
231
+ }
232
+ else {
233
+ this.sendError(id, MCP_ERROR_CODES.INTERNAL_ERROR, error?.message || 'Internal error');
234
+ }
235
+ }
236
+ }
237
+ async handleNotification(notification) {
238
+ const { method } = notification;
239
+ if (method === MCP_METHODS.INITIALIZED) {
240
+ this.initialized = true;
241
+ }
242
+ }
243
+ handleInitialize(_params) {
244
+ if (this.initialized) {
245
+ throw this.createError(MCP_ERROR_CODES.INVALID_REQUEST, 'Already initialized');
246
+ }
247
+ return {
248
+ protocolVersion: PROTOCOL_VERSION,
249
+ capabilities: this.serverInfo.capabilities,
250
+ serverInfo: this.serverInfo,
251
+ };
252
+ }
253
+ handleToolsList() {
254
+ return { tools: this.toolProvider.getTools() };
255
+ }
256
+ async handleToolsCall(params) {
257
+ const { name, arguments: args } = params || {};
258
+ if (!name) {
259
+ throw this.createError(MCP_ERROR_CODES.INVALID_PARAMS, 'Tool name is required');
260
+ }
261
+ try {
262
+ const result = await this.toolProvider.executeTool(name, args || {});
263
+ if (typeof result === 'string') {
264
+ return { content: [{ type: 'text', text: result }] };
265
+ }
266
+ if (result && typeof result === 'object') {
267
+ if (result.content && Array.isArray(result.content)) {
268
+ return result;
269
+ }
270
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
271
+ }
272
+ return { content: [{ type: 'text', text: String(result) }] };
273
+ }
274
+ catch (error) {
275
+ return {
276
+ content: [{ type: 'text', text: `Error: ${error?.message || 'Tool failed'}` }],
277
+ isError: true,
278
+ };
279
+ }
280
+ }
281
+ handleShutdown() {
282
+ setImmediate(() => this.stop());
283
+ return {};
284
+ }
285
+ sendResult(id, result) {
286
+ const response = { jsonrpc: '2.0', id, result };
287
+ this.sendMessage(response);
288
+ }
289
+ sendError(id, code, message, data) {
290
+ const response = {
291
+ jsonrpc: '2.0',
292
+ id,
293
+ error: { code, message, data },
294
+ };
295
+ this.sendMessage(response);
296
+ }
297
+ sendMessage(message) {
298
+ process.stdout.write(JSON.stringify(message) + '\n');
299
+ }
300
+ requireInitialized() {
301
+ if (!this.initialized) {
302
+ throw this.createError(MCP_ERROR_CODES.SERVER_NOT_INITIALIZED, 'Server not initialized');
303
+ }
304
+ }
305
+ createError(code, message, data) {
306
+ return { code, message, data };
307
+ }
308
+ }
309
+ // ==================== Tool Definitions ====================
310
+ const CONNECTOR_PREFIX = 'linear';
311
+ const DEFAULT_BASE_URL = 'https://api.linear.app/graphql';
312
+ const tools = [
313
+ {
314
+ name: `${CONNECTOR_PREFIX}.health`,
315
+ description: 'Check connector health and authentication status',
316
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
317
+ },
318
+ {
319
+ name: `${CONNECTOR_PREFIX}.list_projects`,
320
+ description: 'List Linear projects',
321
+ inputSchema: {
322
+ type: 'object',
323
+ properties: {
324
+ limit: { type: 'number', description: 'Max projects to return' },
325
+ cursor: { type: 'string', description: 'Pagination cursor' },
326
+ },
327
+ additionalProperties: false,
328
+ },
329
+ },
330
+ {
331
+ name: `${CONNECTOR_PREFIX}.search_issues`,
332
+ description: 'Search issues by title',
333
+ inputSchema: {
334
+ type: 'object',
335
+ properties: {
336
+ query: { type: 'string', description: 'Search text for issue titles' },
337
+ projectId: { type: 'string', description: 'Filter by project id' },
338
+ teamId: { type: 'string', description: 'Filter by team id' },
339
+ limit: { type: 'number', description: 'Max issues to return' },
340
+ cursor: { type: 'string', description: 'Pagination cursor' },
341
+ },
342
+ required: ['query'],
343
+ additionalProperties: false,
344
+ },
345
+ },
346
+ {
347
+ name: `${CONNECTOR_PREFIX}.get_issue`,
348
+ description: 'Fetch an issue by id',
349
+ inputSchema: {
350
+ type: 'object',
351
+ properties: {
352
+ id: { type: 'string', description: 'Issue id' },
353
+ },
354
+ required: ['id'],
355
+ additionalProperties: false,
356
+ },
357
+ },
358
+ ];
359
+ const config = {
360
+ baseUrl: process.env.LINEAR_BASE_URL || DEFAULT_BASE_URL,
361
+ apiKey: process.env.LINEAR_API_KEY,
362
+ };
363
+ const client = new LinearClient(config);
364
+ const handlers = {
365
+ [`${CONNECTOR_PREFIX}.health`]: async () => buildEnvelope(await client.health()),
366
+ [`${CONNECTOR_PREFIX}.list_projects`]: async (args) => buildEnvelope(await client.listProjects(args.limit, args.cursor)),
367
+ [`${CONNECTOR_PREFIX}.search_issues`]: async (args) => buildEnvelope(await client.searchIssues(args.query, args.limit, args.cursor, args.projectId, args.teamId)),
368
+ [`${CONNECTOR_PREFIX}.get_issue`]: async (args) => buildEnvelope(await client.getIssue(args.id)),
369
+ };
370
+ const toolProvider = {
371
+ getTools: () => tools,
372
+ executeTool: async (name, args) => {
373
+ const handler = handlers[name];
374
+ if (!handler) {
375
+ throw new Error(`Unknown tool: ${name}`);
376
+ }
377
+ return handler(args);
378
+ },
379
+ };
380
+ const serverInfo = {
381
+ name: 'Linear Connector',
382
+ version: '0.1.0',
383
+ protocolVersion: PROTOCOL_VERSION,
384
+ capabilities: {
385
+ tools: { listChanged: false },
386
+ },
387
+ };
388
+ const server = new StdioMCPServer(toolProvider, serverInfo);
389
+ server.start();
390
+ function buildEnvelope(result) {
391
+ return {
392
+ ok: true,
393
+ data: result.data,
394
+ meta: {
395
+ durationMs: result.meta.durationMs,
396
+ vendorRequestId: result.meta.vendorRequestId,
397
+ baseUrl: result.meta.baseUrl,
398
+ },
399
+ nextCursor: result.nextCursor,
400
+ warnings: [],
401
+ };
402
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "cowork-linear-mcp",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc -p tsconfig.json",
9
+ "start": "node dist/index.js"
10
+ },
11
+ "devDependencies": {
12
+ "@types/node": "^20.11.30",
13
+ "typescript": "^5.7.3"
14
+ }
15
+ }