@telos.ready/mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +218 -0
  2. package/build/index.js +431 -0
  3. package/package.json +37 -0
package/README.md ADDED
@@ -0,0 +1,218 @@
1
+ # Telos MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that provides access to Telos project management tools. This server allows AI assistants to interact with Telos tickets, documentation, and project tasks.
4
+
5
+ ## Features
6
+
7
+ This MCP server provides 8 tools for managing Telos projects:
8
+
9
+ ### Core Tools
10
+
11
+ 1. **ask-question** - Ask questions about tickets and get AI-powered answers with context
12
+ 2. **get-ticket-details** - Retrieve full ticket information including description and comments
13
+ 3. **add-ticket-comment** - Add comments to existing tickets
14
+ 4. **get-next-task** - Get the next prioritized task for a specific application
15
+
16
+ ### Development Tools
17
+
18
+ 5. **get-git-branch-for-ticket** - Get the correct git branch name for ticket development
19
+ 6. **mark-ticket-as-blocked** - Mark tickets as blocked with a reason
20
+ 7. **add-documentation** - Create knowledge tickets with documentation content
21
+ 8. **update-action-item** - Update the status of action items in tickets
22
+
23
+ ## Installation
24
+
25
+ ### Using with MCP Clients
26
+
27
+ Add to your MCP client configuration:
28
+
29
+ #### Claude Desktop
30
+
31
+ Update your `~/Library/Application Support/Claude/claude_desktop_config.json`:
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "telos": {
37
+ "command": "npx",
38
+ "args": ["-y", "@modelcontextprotocol/server-telos"],
39
+ "env": {
40
+ "TELOS_PERSONAL_ACCESS_TOKEN": "<YOUR_PAT_TOKEN>"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ #### Cursor MCP
48
+
49
+ Update your `~/.cursor/mcp.json`:
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "telos": {
55
+ "command": "npx",
56
+ "args": ["-y", "@modelcontextprotocol/server-telos"],
57
+ "env": {
58
+ "TELOS_PERSONAL_ACCESS_TOKEN": "<YOUR_PAT_TOKEN>"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ### Local Development
66
+
67
+ ```bash
68
+ # Clone the repository
69
+ git clone <repository-url>
70
+ cd telos-mcp
71
+
72
+ # Install dependencies
73
+ npm install
74
+
75
+ # Build the project
76
+ npm run build
77
+
78
+ # Run locally
79
+ TELOS_PERSONAL_ACCESS_TOKEN=pat_your_token_here node build/index.js
80
+ ```
81
+
82
+ ## Configuration
83
+
84
+ ### Required Environment Variables
85
+
86
+ - `TELOS_PERSONAL_ACCESS_TOKEN` - Your Telos Personal Access Token (starts with `pat_`)
87
+
88
+ ### Optional Environment Variables
89
+
90
+ - `TELOS_BASE_URL` - Base URL for Telos instance (defaults to `https://go.telosready.com`)
91
+
92
+ ## Getting Your Personal Access Token
93
+
94
+ 1. Log into your Telos instance
95
+ 2. Go to Account → Personal Access Tokens
96
+ 3. Create a new token with a descriptive name
97
+ 4. Copy the token (it starts with `pat_`)
98
+ 5. Use it in your MCP configuration
99
+
100
+ ## Tool Usage Examples
101
+
102
+ ### Add a Comment to a Ticket
103
+
104
+ ```json
105
+ {
106
+ "name": "add-ticket-comment",
107
+ "arguments": {
108
+ "ticketReference": "TEL037",
109
+ "content": "I've reviewed the requirements and have some questions about the implementation approach.",
110
+ "internal": false
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Get Ticket Details
116
+
117
+ ```json
118
+ {
119
+ "name": "get-ticket-details",
120
+ "arguments": {
121
+ "ticketReference": "TEL037"
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Mark Ticket as Blocked
127
+
128
+ ```json
129
+ {
130
+ "name": "mark-ticket-as-blocked",
131
+ "arguments": {
132
+ "ticketReference": "TEL037",
133
+ "reason": "Waiting for external API documentation"
134
+ }
135
+ }
136
+ ```
137
+
138
+ ### Get Next Task
139
+
140
+ ```json
141
+ {
142
+ "name": "get-next-task",
143
+ "arguments": {
144
+ "applicationSlug": "myapp"
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### Update Action Item Status
150
+
151
+ ```json
152
+ {
153
+ "name": "update-action-item",
154
+ "arguments": {
155
+ "ticketReference": "TEL037",
156
+ "actionPoint": "Review database schema",
157
+ "status": "COMPLETED"
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## Error Handling
163
+
164
+ The server will return appropriate error messages for:
165
+
166
+ - Authentication failures (invalid PAT token)
167
+ - Missing required parameters
168
+ - Ticket not found
169
+ - Network connectivity issues
170
+ - Telos API errors
171
+
172
+ ## Development
173
+
174
+ ### Building
175
+
176
+ ```bash
177
+ npm run build
178
+ ```
179
+
180
+ ### Publishing
181
+
182
+ ```bash
183
+ npm publish --access public
184
+ ```
185
+
186
+ ## Advanced Features
187
+
188
+ For full access to all Telos MCP features, you can also connect directly to your Telos instance:
189
+
190
+ ```json
191
+ {
192
+ "mcpServers": {
193
+ "telos-direct": {
194
+ "type": "sse",
195
+ "url": "https://go.telosready.com/mcp?apiKey=pat_your_token_here"
196
+ }
197
+ }
198
+ }
199
+ ```
200
+
201
+ This provides access to advanced features like:
202
+
203
+ - AI-powered question answering
204
+ - Real-time ticket updates
205
+ - Advanced action item management
206
+
207
+ ## Support
208
+
209
+ If you encounter issues:
210
+
211
+ 1. Check that your PAT token is valid and not expired
212
+ 2. Verify you have access to the applications/tickets you're trying to access
213
+ 3. Ensure your Telos instance URL is correct
214
+ 4. Check the MCP client logs for detailed error messages
215
+
216
+ ## License
217
+
218
+ ISC
package/build/index.js ADDED
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import axios from "axios";
6
+ // Configuration
7
+ const DEFAULT_TELOS_BASE_URL = "https://go.telosready.com";
8
+ // Create server instance
9
+ const server = new McpServer({
10
+ name: "telos-mcp",
11
+ version: "1.0.0",
12
+ });
13
+ // HTTP Client for Telos API
14
+ class TelosClient {
15
+ client;
16
+ config;
17
+ constructor(config) {
18
+ this.config = config;
19
+ this.client = axios.create({
20
+ baseURL: config.baseUrl,
21
+ timeout: 30000,
22
+ headers: {
23
+ "User-Agent": "telos-mcp/1.0.0",
24
+ "Content-Type": "application/json",
25
+ },
26
+ });
27
+ }
28
+ async testConnection() {
29
+ try {
30
+ if (!this.config.apiKey) {
31
+ console.error("Connection test failed: No API key provided");
32
+ return false;
33
+ }
34
+ const response = await this.client.get("/account/test-pat", {
35
+ headers: {
36
+ Authorization: `Bearer ${this.config.apiKey}`,
37
+ },
38
+ timeout: 5000, // 5 second timeout for connection test
39
+ });
40
+ return response.status === 200;
41
+ }
42
+ catch (error) {
43
+ // Don't log errors here - let the caller handle them
44
+ return false;
45
+ }
46
+ }
47
+ async post(endpoint, data) {
48
+ try {
49
+ if (!this.config.apiKey) {
50
+ throw new Error("No API key provided. Please set TELOS_PERSONAL_ACCESS_TOKEN environment variable.");
51
+ }
52
+ const response = await this.client.post(endpoint, data, {
53
+ headers: {
54
+ Authorization: `Bearer ${this.config.apiKey}`,
55
+ },
56
+ });
57
+ return response.data;
58
+ }
59
+ catch (error) {
60
+ if (axios.isAxiosError(error)) {
61
+ const status = error.response?.status;
62
+ const message = error.response?.data?.error ||
63
+ error.response?.data?.message ||
64
+ error.message;
65
+ if (status === 401) {
66
+ throw new Error(`Authentication failed. Please check your PAT token.`);
67
+ }
68
+ else if (status === 404) {
69
+ throw new Error(`Resource not found.`);
70
+ }
71
+ else {
72
+ throw new Error(`API Error (${status}): ${message}`);
73
+ }
74
+ }
75
+ throw error;
76
+ }
77
+ }
78
+ }
79
+ // Initialize Telos client
80
+ const apiKey = process.env.TELOS_PERSONAL_ACCESS_TOKEN || "";
81
+ const baseUrl = process.env.TELOS_BASE_URL || DEFAULT_TELOS_BASE_URL;
82
+ // Don't exit immediately - let the server start and handle the error per request
83
+ if (!apiKey) {
84
+ console.error("⚠️ WARNING: TELOS_PERSONAL_ACCESS_TOKEN environment variable is not set");
85
+ console.error("Get your PAT from: Account → Personal Access Tokens in your Telos instance");
86
+ console.error("Server will start but API calls will fail until token is provided");
87
+ }
88
+ const telosClient = new TelosClient({
89
+ baseUrl,
90
+ apiKey,
91
+ });
92
+ // Tool 1: Ask Question
93
+ server.tool("ask-question", "Asks a question about a ticket and gets an answer with supporting context. This tool uses the agent service to provide comprehensive answers with relevant documentation. Provide both a ticket reference and a specific question.", {
94
+ ticketReference: z
95
+ .string()
96
+ .describe('The ticket reference for the ticket you want to ask a question about, which is an uppercase alphabetical code followed by a number, such as "XXX037".'),
97
+ question: z
98
+ .string()
99
+ .describe("The question you want to ask about the ticket."),
100
+ }, async ({ ticketReference, question }) => {
101
+ try {
102
+ const result = await telosClient.post("/mcp/ask-question", {
103
+ ticketReference,
104
+ question,
105
+ });
106
+ if (result.success) {
107
+ return {
108
+ content: [{ type: "text", text: result.result }],
109
+ };
110
+ }
111
+ else {
112
+ return {
113
+ content: [{ type: "text", text: `Error: ${result}` }],
114
+ };
115
+ }
116
+ }
117
+ catch (error) {
118
+ return {
119
+ content: [
120
+ {
121
+ type: "text",
122
+ text: `Error asking question about ticket ${ticketReference}: ${error}`,
123
+ },
124
+ ],
125
+ };
126
+ }
127
+ });
128
+ // Tool 2: Add Ticket Comment
129
+ server.tool("add-ticket-comment", "Adds a comment to an existing ticket. This tool allows adding a comment to a specific ticket with proper attribution.", {
130
+ ticketReference: z
131
+ .string()
132
+ .describe('The ticket reference for the ticket you want to add a comment to, which is an uppercase alphabetical code followed by a number, such as "XXX037".'),
133
+ content: z
134
+ .string()
135
+ .describe("The content of the comment to add to the ticket. This can be any text content including markdown."),
136
+ internal: z
137
+ .boolean()
138
+ .optional()
139
+ .default(false)
140
+ .describe("Whether this is an internal comment (not visible to clients)."),
141
+ }, async ({ ticketReference, content, internal }) => {
142
+ try {
143
+ const result = await telosClient.post("/mcp/add-ticket-comment", {
144
+ ticketReference,
145
+ content,
146
+ internal,
147
+ });
148
+ if (result.success) {
149
+ return {
150
+ content: [{ type: "text", text: result.result }],
151
+ };
152
+ }
153
+ else {
154
+ return {
155
+ content: [{ type: "text", text: `Error: ${result}` }],
156
+ };
157
+ }
158
+ }
159
+ catch (error) {
160
+ return {
161
+ content: [
162
+ {
163
+ type: "text",
164
+ text: `Error adding comment to ticket ${ticketReference}: ${error}`,
165
+ },
166
+ ],
167
+ };
168
+ }
169
+ });
170
+ // Tool 3: Add Documentation (Create Knowledge Ticket)
171
+ server.tool("add-documentation", "Creates a new knowledge ticket with documentation content. Only use this tool if a comment cannot be added directly to a ticket. This tool creates a knowledge ticket that can be used to store and organize documentation, procedures, guides, or any other knowledge-based content for future reference.", {
172
+ content: z
173
+ .string()
174
+ .describe("The documentation content to add. This should be well-formatted markdown content that provides useful information or knowledge."),
175
+ title: z.string().optional().describe("Title for the knowledge ticket."),
176
+ applicationSlug: z
177
+ .string()
178
+ .describe("The slug of the application that the documentation relates to."),
179
+ }, async ({ content, title, applicationSlug }) => {
180
+ try {
181
+ const result = await telosClient.post("/mcp/add-documentation", {
182
+ content,
183
+ title,
184
+ applicationSlug,
185
+ });
186
+ if (result.success) {
187
+ return {
188
+ content: [{ type: "text", text: result.result }],
189
+ };
190
+ }
191
+ else {
192
+ return {
193
+ content: [{ type: "text", text: `Error: ${result.error}` }],
194
+ };
195
+ }
196
+ }
197
+ catch (error) {
198
+ return {
199
+ content: [
200
+ {
201
+ type: "text",
202
+ text: `Error creating documentation: ${error.message}`,
203
+ },
204
+ ],
205
+ };
206
+ }
207
+ });
208
+ // Tool 4: Get Ticket Details
209
+ server.tool("get-ticket-details", "Gets the details of a ticket. The ticket reference must be provided. This tool will provide the initial description of the ticket and any comments that users have made on the ticket.", {
210
+ ticketReference: z
211
+ .string()
212
+ .describe('The ticket reference for the requested ticket, which is an uppercase alphabetical code followed by a number, such as "XXX037".'),
213
+ }, async ({ ticketReference }) => {
214
+ try {
215
+ const result = await telosClient.post("/mcp/get-ticket-details", {
216
+ ticketReference,
217
+ });
218
+ if (result.success) {
219
+ return {
220
+ content: [{ type: "text", text: result.result }],
221
+ };
222
+ }
223
+ else {
224
+ return {
225
+ content: [{ type: "text", text: `Error: ${result}` }],
226
+ };
227
+ }
228
+ }
229
+ catch (error) {
230
+ return {
231
+ content: [
232
+ {
233
+ type: "text",
234
+ text: `Error getting ticket details for ${ticketReference}: ${error}`,
235
+ },
236
+ ],
237
+ };
238
+ }
239
+ });
240
+ // Tool 5: Get Next Task
241
+ server.tool("get-next-task", "Gets the next task for a member and provides a ticket reference, next step and instructions for completing the task.", {
242
+ applicationSlug: z
243
+ .string()
244
+ .describe("Application slug must be provided to specify the application for the next task."),
245
+ }, async ({ applicationSlug }) => {
246
+ try {
247
+ const result = await telosClient.post("/mcp/get-next-task", {
248
+ applicationSlug,
249
+ });
250
+ if (result.success) {
251
+ return {
252
+ content: [{ type: "text", text: result.result }],
253
+ };
254
+ }
255
+ else {
256
+ return {
257
+ content: [{ type: "text", text: `Error: ${result.error}` }],
258
+ };
259
+ }
260
+ }
261
+ catch (error) {
262
+ return {
263
+ content: [
264
+ {
265
+ type: "text",
266
+ text: `Error getting next task for application ${applicationSlug}: ${error.message}`,
267
+ },
268
+ ],
269
+ };
270
+ }
271
+ });
272
+ // Tool 6: Get Git Branch for Ticket
273
+ server.tool("get-git-branch-for-ticket", "Gets the git branch to be used for development work on this ticket. All commits for this ticket must use this branch. Git branches must stay ahead of main.", {
274
+ ticketReference: z
275
+ .string()
276
+ .describe('The ticket reference, which is an uppercase alphabetical code followed by a number, such as "TEL037".'),
277
+ }, async ({ ticketReference }) => {
278
+ try {
279
+ const result = await telosClient.post("/mcp/get-git-branch-for-ticket", {
280
+ ticketReference,
281
+ });
282
+ if (result.success) {
283
+ return {
284
+ content: [{ type: "text", text: result.result }],
285
+ };
286
+ }
287
+ else {
288
+ return {
289
+ content: [{ type: "text", text: `Error: ${result.error}` }],
290
+ };
291
+ }
292
+ }
293
+ catch (error) {
294
+ return {
295
+ content: [
296
+ {
297
+ type: "text",
298
+ text: `Error getting git branch for ticket ${ticketReference}: ${error}`,
299
+ },
300
+ ],
301
+ };
302
+ }
303
+ });
304
+ // Tool 7: Mark Ticket as Blocked
305
+ server.tool("mark-ticket-as-blocked", "Marks a ticket as blocked with a specified reason. This tool will load the ticket, add a comment with the blocking reason, and set the ticket status to blocked.", {
306
+ ticketReference: z
307
+ .string()
308
+ .describe('The ticket reference for the ticket to mark as blocked, which is an uppercase alphabetical code followed by a number, such as "TEL037".'),
309
+ reason: z
310
+ .string()
311
+ .describe("The reason why the ticket is being blocked. This will be added as a comment to the ticket."),
312
+ }, async ({ ticketReference, reason }) => {
313
+ try {
314
+ const result = await telosClient.post("/mcp/mark-ticket-as-blocked", {
315
+ ticketReference,
316
+ reason,
317
+ });
318
+ if (result.success) {
319
+ return {
320
+ content: [{ type: "text", text: result.result }],
321
+ };
322
+ }
323
+ else {
324
+ return {
325
+ content: [{ type: "text", text: `Error: ${result.error}` }],
326
+ };
327
+ }
328
+ }
329
+ catch (error) {
330
+ return {
331
+ content: [
332
+ {
333
+ type: "text",
334
+ text: `Error blocking ticket ${ticketReference}: ${error.message}`,
335
+ },
336
+ ],
337
+ };
338
+ }
339
+ });
340
+ // Tool 8: Update Action Item
341
+ server.tool("update-action-item", "Updates the status of an action item in a ticket. This tool finds a specific action item in the ticket or its comments and updates its status to INCOMPLETE, INPROGRESS, or COMPLETED.", {
342
+ ticketReference: z
343
+ .string()
344
+ .describe('The ticket reference for the ticket containing the action item, which is an uppercase alphabetical code followed by a number, such as "TEL037".'),
345
+ actionPoint: z
346
+ .string()
347
+ .describe("The text of the action item to update. This will be matched against existing action items in the ticket."),
348
+ status: z
349
+ .enum(["INCOMPLETE", "INPROGRESS", "COMPLETED"])
350
+ .describe("The new status for the action item. Must be one of: INCOMPLETE, INPROGRESS, COMPLETED"),
351
+ }, async ({ ticketReference, actionPoint, status }) => {
352
+ try {
353
+ const result = await telosClient.post("/mcp/update-action-item", {
354
+ ticketReference,
355
+ actionPoint,
356
+ status,
357
+ });
358
+ if (result.success) {
359
+ return {
360
+ content: [{ type: "text", text: result.result }],
361
+ };
362
+ }
363
+ else {
364
+ return {
365
+ content: [{ type: "text", text: `Error: ${result.error}` }],
366
+ };
367
+ }
368
+ }
369
+ catch (error) {
370
+ return {
371
+ content: [
372
+ {
373
+ type: "text",
374
+ text: `Error updating action item for ticket ${ticketReference}: ${error.message}`,
375
+ },
376
+ ],
377
+ };
378
+ }
379
+ });
380
+ async function main() {
381
+ try {
382
+ // Initialize transport first
383
+ const transport = new StdioServerTransport();
384
+ // Log startup information to stderr (like weather example)
385
+ console.error("Telos MCP Server starting...");
386
+ console.error(`Connecting to Telos at: ${baseUrl}`);
387
+ console.error(`API Key: ${apiKey.substring(0, 8)}...`);
388
+ // Connect to the transport (this should not fail)
389
+ await server.connect(transport);
390
+ // Log success message
391
+ console.error("✅ Telos MCP Server running on stdio");
392
+ console.error("Available tools: ask-question, add-ticket-comment, add-documentation, get-ticket-details, get-next-task, get-git-branch-for-ticket, mark-ticket-as-blocked, update-action-item");
393
+ // Test connection in background without blocking or exiting
394
+ // This is non-critical and should not cause the server to exit
395
+ setImmediate(async () => {
396
+ try {
397
+ const connected = await Promise.race([
398
+ telosClient.testConnection(),
399
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Connection test timeout")), 5000)),
400
+ ]);
401
+ if (connected) {
402
+ console.error("✅ Telos API connection successful");
403
+ }
404
+ else {
405
+ console.error("⚠️ Telos API connection failed - tools may not work properly");
406
+ }
407
+ }
408
+ catch (error) {
409
+ console.error("⚠️ Telos API connection test error:", error.message);
410
+ console.error("Server will continue running - connection will be tested per request");
411
+ }
412
+ });
413
+ }
414
+ catch (error) {
415
+ console.error("Fatal error during server initialization:", error);
416
+ process.exit(1);
417
+ }
418
+ }
419
+ // Handle process termination gracefully
420
+ process.on("SIGTERM", () => {
421
+ console.error("Received SIGTERM, shutting down gracefully");
422
+ process.exit(0);
423
+ });
424
+ process.on("SIGINT", () => {
425
+ console.error("Received SIGINT, shutting down gracefully");
426
+ process.exit(0);
427
+ });
428
+ main().catch((error) => {
429
+ console.error("Fatal error in main():", error);
430
+ process.exit(1);
431
+ });
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@telos.ready/mcp",
3
+ "version": "1.0.0",
4
+ "description": "Telos MCP server for managing tickets and project tasks",
5
+ "main": "build/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "tsc && chmod 755 build/index.js",
9
+ "test": "echo \"Error: no test specified\" && exit 1",
10
+ "prepublishOnly": "npm run build"
11
+ },
12
+ "bin": {
13
+ "server-telos": "build/index.js"
14
+ },
15
+ "files": [
16
+ "build",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "mcp",
21
+ "telos",
22
+ "tickets",
23
+ "project-management",
24
+ "model-context-protocol"
25
+ ],
26
+ "author": "",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.13.3",
30
+ "axios": "^1.7.7",
31
+ "zod": "^3.25.69"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.0.10",
35
+ "typescript": "^5.8.3"
36
+ }
37
+ }