@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.
- package/README.md +218 -0
- package/build/index.js +431 -0
- 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
|
+
}
|