@pschroee/redmine-mcp 0.4.5 → 0.5.1
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/dist/formatters/account.d.ts +2 -0
- package/dist/formatters/account.js +48 -0
- package/dist/formatters/agile.d.ts +6 -0
- package/dist/formatters/agile.js +53 -0
- package/dist/formatters/checklist.d.ts +5 -0
- package/dist/formatters/checklist.js +30 -0
- package/dist/formatters/file.d.ts +5 -0
- package/dist/formatters/file.js +61 -0
- package/dist/formatters/group.d.ts +11 -0
- package/dist/formatters/group.js +53 -0
- package/dist/formatters/index.d.ts +15 -1
- package/dist/formatters/index.js +15 -1
- package/dist/formatters/issue.d.ts +13 -3
- package/dist/formatters/issue.js +43 -10
- package/dist/formatters/journals.d.ts +7 -1
- package/dist/formatters/journals.js +13 -15
- package/dist/formatters/membership.d.ts +5 -0
- package/dist/formatters/membership.js +41 -0
- package/dist/formatters/metadata.d.ts +135 -0
- package/dist/formatters/metadata.js +213 -0
- package/dist/formatters/project.d.ts +11 -0
- package/dist/formatters/project.js +82 -0
- package/dist/formatters/relation.d.ts +5 -0
- package/dist/formatters/relation.js +55 -0
- package/dist/formatters/search.d.ts +5 -0
- package/dist/formatters/search.js +61 -0
- package/dist/formatters/time.d.ts +11 -0
- package/dist/formatters/time.js +47 -0
- package/dist/formatters/user.d.ts +11 -0
- package/dist/formatters/user.js +83 -0
- package/dist/formatters/utils.d.ts +12 -0
- package/dist/formatters/utils.js +31 -0
- package/dist/formatters/version.d.ts +11 -0
- package/dist/formatters/version.js +75 -0
- package/dist/formatters/wiki.d.ts +11 -0
- package/dist/formatters/wiki.js +86 -0
- package/dist/redmine/client.d.ts +4 -0
- package/dist/redmine/client.js +6 -0
- package/dist/server.js +1 -1
- package/dist/tools/account.js +7 -1
- package/dist/tools/admin.js +25 -4
- package/dist/tools/agile.js +19 -3
- package/dist/tools/checklists.js +13 -2
- package/dist/tools/core.js +23 -5
- package/dist/tools/enumerations.js +19 -3
- package/dist/tools/files.js +13 -2
- package/dist/tools/memberships.js +13 -2
- package/dist/tools/metadata.js +37 -6
- package/dist/tools/relations.js +25 -4
- package/dist/tools/roles.js +13 -2
- package/dist/tools/search.js +7 -1
- package/dist/tools/time.js +13 -2
- package/dist/tools/wiki.js +13 -2
- package/package.json +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { formatDate } from "./utils.js";
|
|
2
|
+
const USER_STATUS = {
|
|
3
|
+
1: "Active",
|
|
4
|
+
2: "Registered",
|
|
5
|
+
3: "Locked",
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Format a single user as complete Markdown
|
|
9
|
+
*/
|
|
10
|
+
export function formatUser(response) {
|
|
11
|
+
const user = response.user;
|
|
12
|
+
const lines = [];
|
|
13
|
+
// Header
|
|
14
|
+
lines.push(`# ${user.firstname} ${user.lastname}`);
|
|
15
|
+
lines.push("");
|
|
16
|
+
// Status line
|
|
17
|
+
const statusParts = [];
|
|
18
|
+
statusParts.push(`**Login:** ${user.login}`);
|
|
19
|
+
const status = user.status !== undefined ? USER_STATUS[user.status] || "Unknown" : "Unknown";
|
|
20
|
+
statusParts.push(`**Status:** ${status}`);
|
|
21
|
+
if (user.admin) {
|
|
22
|
+
statusParts.push(`**Role:** Admin`);
|
|
23
|
+
}
|
|
24
|
+
lines.push(statusParts.join(" | "));
|
|
25
|
+
lines.push("");
|
|
26
|
+
// Metadata table
|
|
27
|
+
lines.push("| Field | Value |");
|
|
28
|
+
lines.push("|-------|-------|");
|
|
29
|
+
if (user.mail) {
|
|
30
|
+
lines.push(`| Email | ${user.mail} |`);
|
|
31
|
+
}
|
|
32
|
+
lines.push(`| Created | ${formatDate(user.created_on)} |`);
|
|
33
|
+
if (user.last_login_on) {
|
|
34
|
+
lines.push(`| Last Login | ${formatDate(user.last_login_on)} |`);
|
|
35
|
+
}
|
|
36
|
+
lines.push("");
|
|
37
|
+
// Groups
|
|
38
|
+
if (user.groups && user.groups.length > 0) {
|
|
39
|
+
lines.push("## Groups");
|
|
40
|
+
lines.push("");
|
|
41
|
+
for (const group of user.groups) {
|
|
42
|
+
lines.push(`- ${group.name}`);
|
|
43
|
+
}
|
|
44
|
+
lines.push("");
|
|
45
|
+
}
|
|
46
|
+
// Project Memberships
|
|
47
|
+
if (user.memberships && user.memberships.length > 0) {
|
|
48
|
+
lines.push("## Project Memberships");
|
|
49
|
+
lines.push("");
|
|
50
|
+
for (const membership of user.memberships) {
|
|
51
|
+
const roles = membership.roles.map((r) => r.name).join(", ");
|
|
52
|
+
lines.push(`- **${membership.project.name}:** ${roles}`);
|
|
53
|
+
}
|
|
54
|
+
lines.push("");
|
|
55
|
+
}
|
|
56
|
+
return lines.join("\n").trimEnd();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Format a list of users as Markdown table
|
|
60
|
+
*/
|
|
61
|
+
export function formatUserList(response) {
|
|
62
|
+
const lines = [];
|
|
63
|
+
// Header
|
|
64
|
+
lines.push(`# Users (${response.total_count})`);
|
|
65
|
+
lines.push("");
|
|
66
|
+
// Empty case
|
|
67
|
+
if (response.users.length === 0) {
|
|
68
|
+
lines.push("No users found.");
|
|
69
|
+
return lines.join("\n");
|
|
70
|
+
}
|
|
71
|
+
// Table header
|
|
72
|
+
lines.push("| Name | Login | Email | Status | Role |");
|
|
73
|
+
lines.push("|------|-------|-------|--------|------|");
|
|
74
|
+
// Table rows
|
|
75
|
+
for (const user of response.users) {
|
|
76
|
+
const name = `${user.firstname} ${user.lastname}`;
|
|
77
|
+
const email = user.mail || "";
|
|
78
|
+
const status = user.status !== undefined ? USER_STATUS[user.status] || "Unknown" : "Unknown";
|
|
79
|
+
const role = user.admin ? "Admin" : "User";
|
|
80
|
+
lines.push(`| ${name} | ${user.login} | ${email} | ${status} | ${role} |`);
|
|
81
|
+
}
|
|
82
|
+
return lines.join("\n");
|
|
83
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format an ISO date string to readable local datetime format (YYYY-MM-DD HH:mm)
|
|
3
|
+
*/
|
|
4
|
+
export declare function formatDate(isoDate: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Format an ISO date string to short date format (YYYY-MM-DD)
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatDateShort(isoDate: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Truncate text to a maximum length with ellipsis
|
|
11
|
+
*/
|
|
12
|
+
export declare function truncate(text: string, maxLength: number): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format an ISO date string to readable local datetime format (YYYY-MM-DD HH:mm)
|
|
3
|
+
*/
|
|
4
|
+
export function formatDate(isoDate) {
|
|
5
|
+
const date = new Date(isoDate);
|
|
6
|
+
const year = date.getFullYear();
|
|
7
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
8
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
9
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
10
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
11
|
+
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Format an ISO date string to short date format (YYYY-MM-DD)
|
|
15
|
+
*/
|
|
16
|
+
export function formatDateShort(isoDate) {
|
|
17
|
+
const date = new Date(isoDate);
|
|
18
|
+
const year = date.getFullYear();
|
|
19
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
20
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
21
|
+
return `${year}-${month}-${day}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Truncate text to a maximum length with ellipsis
|
|
25
|
+
*/
|
|
26
|
+
export function truncate(text, maxLength) {
|
|
27
|
+
if (text.length <= maxLength) {
|
|
28
|
+
return text;
|
|
29
|
+
}
|
|
30
|
+
return text.slice(0, maxLength) + "...";
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RedmineVersion, RedmineVersionsResponse } from "../redmine/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Format a single version as complete Markdown
|
|
4
|
+
*/
|
|
5
|
+
export declare function formatVersion(response: {
|
|
6
|
+
version: RedmineVersion;
|
|
7
|
+
}): string;
|
|
8
|
+
/**
|
|
9
|
+
* Format a list of versions as Markdown table
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatVersionList(response: RedmineVersionsResponse): string;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const STATUS_BADGE = {
|
|
2
|
+
open: "Open",
|
|
3
|
+
locked: "Locked",
|
|
4
|
+
closed: "Closed",
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Format a status badge
|
|
8
|
+
*/
|
|
9
|
+
function formatStatusBadge(status) {
|
|
10
|
+
return STATUS_BADGE[status] || status;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Calculate progress percentage from estimated/spent hours
|
|
14
|
+
*/
|
|
15
|
+
function formatProgress(estimated, spent) {
|
|
16
|
+
if (!estimated || estimated === 0) {
|
|
17
|
+
return spent ? `${spent}h spent` : "-";
|
|
18
|
+
}
|
|
19
|
+
const percentage = Math.round((spent || 0) / estimated * 100);
|
|
20
|
+
return `${percentage}%`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Format a single version as complete Markdown
|
|
24
|
+
*/
|
|
25
|
+
export function formatVersion(response) {
|
|
26
|
+
const version = response.version;
|
|
27
|
+
const lines = [];
|
|
28
|
+
// Title with status badge
|
|
29
|
+
lines.push(`# ${version.name} [${formatStatusBadge(version.status)}]`);
|
|
30
|
+
lines.push("");
|
|
31
|
+
// Metadata table
|
|
32
|
+
lines.push("| Field | Value |");
|
|
33
|
+
lines.push("|-------|-------|");
|
|
34
|
+
lines.push(`| Project | ${version.project.name} |`);
|
|
35
|
+
if (version.due_date) {
|
|
36
|
+
lines.push(`| Due Date | ${version.due_date} |`);
|
|
37
|
+
}
|
|
38
|
+
lines.push(`| Status | ${formatStatusBadge(version.status)} |`);
|
|
39
|
+
if (version.estimated_hours !== undefined || version.spent_hours !== undefined) {
|
|
40
|
+
const est = version.estimated_hours !== undefined ? `${version.estimated_hours}h` : "-";
|
|
41
|
+
const spent = version.spent_hours !== undefined ? `${version.spent_hours}h` : "-";
|
|
42
|
+
lines.push(`| Est/Spent Hours | ${est} / ${spent} |`);
|
|
43
|
+
}
|
|
44
|
+
if (version.description) {
|
|
45
|
+
lines.push(`| Description | ${version.description} |`);
|
|
46
|
+
}
|
|
47
|
+
if (version.wiki_page_title) {
|
|
48
|
+
lines.push(`| Wiki Page | ${version.wiki_page_title} |`);
|
|
49
|
+
}
|
|
50
|
+
lines.push(`| Sharing | ${version.sharing} |`);
|
|
51
|
+
lines.push("");
|
|
52
|
+
return lines.join("\n");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Format a list of versions as Markdown table
|
|
56
|
+
*/
|
|
57
|
+
export function formatVersionList(response) {
|
|
58
|
+
const lines = [];
|
|
59
|
+
lines.push(`# Versions (${response.total_count})`);
|
|
60
|
+
lines.push("");
|
|
61
|
+
if (response.versions.length === 0) {
|
|
62
|
+
lines.push("No versions found.");
|
|
63
|
+
return lines.join("\n");
|
|
64
|
+
}
|
|
65
|
+
// Table header
|
|
66
|
+
lines.push("| Name | Status | Due Date | Progress |");
|
|
67
|
+
lines.push("|------|--------|----------|----------|");
|
|
68
|
+
for (const version of response.versions) {
|
|
69
|
+
const status = formatStatusBadge(version.status);
|
|
70
|
+
const dueDate = version.due_date || "-";
|
|
71
|
+
const progress = formatProgress(version.estimated_hours, version.spent_hours);
|
|
72
|
+
lines.push(`| ${version.name} | ${status} | ${dueDate} | ${progress} |`);
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RedmineWikiPage, RedmineWikiPagesResponse } from "../redmine/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Format a single wiki page as Markdown
|
|
4
|
+
*/
|
|
5
|
+
export declare function formatWikiPage(response: {
|
|
6
|
+
wiki_page: RedmineWikiPage;
|
|
7
|
+
}): string;
|
|
8
|
+
/**
|
|
9
|
+
* Format wiki pages list as Markdown
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatWikiPageList(response: RedmineWikiPagesResponse): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { formatDate } from "./utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* Format a single wiki page as Markdown
|
|
4
|
+
*/
|
|
5
|
+
export function formatWikiPage(response) {
|
|
6
|
+
const page = response.wiki_page;
|
|
7
|
+
const lines = [];
|
|
8
|
+
// Title
|
|
9
|
+
lines.push(`# ${page.title}`);
|
|
10
|
+
lines.push("");
|
|
11
|
+
// Metadata line
|
|
12
|
+
const metaParts = [];
|
|
13
|
+
metaParts.push(`**Version:** ${page.version}`);
|
|
14
|
+
metaParts.push(`**Author:** ${page.author.name}`);
|
|
15
|
+
metaParts.push(`**Updated:** ${formatDate(page.updated_on)}`);
|
|
16
|
+
if (page.parent) {
|
|
17
|
+
metaParts.push(`**Parent:** ${page.parent.title}`);
|
|
18
|
+
}
|
|
19
|
+
lines.push(metaParts.join(" | "));
|
|
20
|
+
lines.push("");
|
|
21
|
+
// Separator
|
|
22
|
+
lines.push("---");
|
|
23
|
+
lines.push("");
|
|
24
|
+
// Content
|
|
25
|
+
lines.push(page.text);
|
|
26
|
+
lines.push("");
|
|
27
|
+
// Attachments
|
|
28
|
+
if (page.attachments && page.attachments.length > 0) {
|
|
29
|
+
lines.push("## Attachments");
|
|
30
|
+
lines.push("");
|
|
31
|
+
for (const att of page.attachments) {
|
|
32
|
+
lines.push(`- [${att.filename}](${att.content_url}) (${att.filesize} bytes)`);
|
|
33
|
+
}
|
|
34
|
+
lines.push("");
|
|
35
|
+
}
|
|
36
|
+
return lines.join("\n");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build a tree structure from flat wiki page list
|
|
40
|
+
*/
|
|
41
|
+
function buildWikiTree(pages) {
|
|
42
|
+
const tree = new Map();
|
|
43
|
+
for (const page of pages) {
|
|
44
|
+
const parentTitle = page.parent?.title ?? null;
|
|
45
|
+
if (!tree.has(parentTitle)) {
|
|
46
|
+
tree.set(parentTitle, []);
|
|
47
|
+
}
|
|
48
|
+
tree.get(parentTitle).push(page);
|
|
49
|
+
}
|
|
50
|
+
return tree;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Recursively format wiki pages as tree
|
|
54
|
+
*/
|
|
55
|
+
function formatWikiTree(tree, parentTitle, indent) {
|
|
56
|
+
const lines = [];
|
|
57
|
+
const children = tree.get(parentTitle) || [];
|
|
58
|
+
for (const page of children) {
|
|
59
|
+
const prefix = " ".repeat(indent);
|
|
60
|
+
lines.push(`${prefix}- **${page.title}** (v${page.version})`);
|
|
61
|
+
// Recursively add children
|
|
62
|
+
const childLines = formatWikiTree(tree, page.title, indent + 1);
|
|
63
|
+
lines.push(...childLines);
|
|
64
|
+
}
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Format wiki pages list as Markdown
|
|
69
|
+
*/
|
|
70
|
+
export function formatWikiPageList(response) {
|
|
71
|
+
const pages = response.wiki_pages;
|
|
72
|
+
const lines = [];
|
|
73
|
+
// Header
|
|
74
|
+
lines.push(`# Wiki Pages (${pages.length})`);
|
|
75
|
+
lines.push("");
|
|
76
|
+
// Empty case
|
|
77
|
+
if (pages.length === 0) {
|
|
78
|
+
lines.push("No wiki pages found.");
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
}
|
|
81
|
+
// Build tree and format
|
|
82
|
+
const tree = buildWikiTree(pages);
|
|
83
|
+
const treeLines = formatWikiTree(tree, null, 0);
|
|
84
|
+
lines.push(...treeLines);
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
package/dist/redmine/client.d.ts
CHANGED
|
@@ -3,6 +3,10 @@ export declare class RedmineClient {
|
|
|
3
3
|
private baseUrl;
|
|
4
4
|
private apiKey;
|
|
5
5
|
constructor(baseUrl: string, apiKey: string);
|
|
6
|
+
/**
|
|
7
|
+
* Get the base URL of the Redmine instance
|
|
8
|
+
*/
|
|
9
|
+
getBaseUrl(): string;
|
|
6
10
|
private request;
|
|
7
11
|
listIssues(params?: {
|
|
8
12
|
project_id?: string | number;
|
package/dist/redmine/client.js
CHANGED
|
@@ -7,6 +7,12 @@ export class RedmineClient {
|
|
|
7
7
|
// Remove trailing slash if present
|
|
8
8
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Get the base URL of the Redmine instance
|
|
12
|
+
*/
|
|
13
|
+
getBaseUrl() {
|
|
14
|
+
return this.baseUrl;
|
|
15
|
+
}
|
|
10
16
|
async request(method, path, body) {
|
|
11
17
|
try {
|
|
12
18
|
const response = await fetch(`${this.baseUrl}${path}`, {
|
package/dist/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { registerTools } from "./tools/index.js";
|
|
|
3
3
|
export function createServer(redmineClient, toolGroups) {
|
|
4
4
|
const server = new McpServer({
|
|
5
5
|
name: "redmine-mcp",
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.5.1",
|
|
7
7
|
});
|
|
8
8
|
registerTools(server, redmineClient, toolGroups);
|
|
9
9
|
return server;
|
package/dist/tools/account.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { formatMyAccount } from "../formatters/index.js";
|
|
1
2
|
export function registerAccountTools(server, client) {
|
|
2
3
|
server.registerTool("get_my_account", {
|
|
3
4
|
description: "Get current user account information",
|
|
4
5
|
}, async () => {
|
|
5
6
|
const result = await client.getMyAccount();
|
|
7
|
+
if ("error" in result) {
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
6
12
|
return {
|
|
7
|
-
content: [{ type: "text", text:
|
|
13
|
+
content: [{ type: "text", text: formatMyAccount(result) }],
|
|
8
14
|
};
|
|
9
15
|
});
|
|
10
16
|
}
|
package/dist/tools/admin.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { formatUser, formatUserList, formatGroup, formatGroupList } from "../formatters/index.js";
|
|
2
3
|
export function registerAdminTools(server, client) {
|
|
3
4
|
// === USERS ===
|
|
4
5
|
server.registerTool("list_users", {
|
|
@@ -12,8 +13,13 @@ export function registerAdminTools(server, client) {
|
|
|
12
13
|
},
|
|
13
14
|
}, async (params) => {
|
|
14
15
|
const result = await client.listUsers(params);
|
|
16
|
+
if ("error" in result) {
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
15
21
|
return {
|
|
16
|
-
content: [{ type: "text", text:
|
|
22
|
+
content: [{ type: "text", text: formatUserList(result) }],
|
|
17
23
|
};
|
|
18
24
|
});
|
|
19
25
|
server.registerTool("get_user", {
|
|
@@ -24,8 +30,13 @@ export function registerAdminTools(server, client) {
|
|
|
24
30
|
},
|
|
25
31
|
}, async (params) => {
|
|
26
32
|
const result = await client.getUser(params.user_id, params.include);
|
|
33
|
+
if ("error" in result) {
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
27
38
|
return {
|
|
28
|
-
content: [{ type: "text", text:
|
|
39
|
+
content: [{ type: "text", text: formatUser(result) }],
|
|
29
40
|
};
|
|
30
41
|
});
|
|
31
42
|
server.registerTool("create_user", {
|
|
@@ -84,8 +95,13 @@ export function registerAdminTools(server, client) {
|
|
|
84
95
|
description: "List all groups (admin only)",
|
|
85
96
|
}, async () => {
|
|
86
97
|
const result = await client.listGroups();
|
|
98
|
+
if ("error" in result) {
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
87
103
|
return {
|
|
88
|
-
content: [{ type: "text", text:
|
|
104
|
+
content: [{ type: "text", text: formatGroupList(result) }],
|
|
89
105
|
};
|
|
90
106
|
});
|
|
91
107
|
server.registerTool("get_group", {
|
|
@@ -96,8 +112,13 @@ export function registerAdminTools(server, client) {
|
|
|
96
112
|
},
|
|
97
113
|
}, async (params) => {
|
|
98
114
|
const result = await client.getGroup(params.group_id, params.include);
|
|
115
|
+
if ("error" in result) {
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
99
120
|
return {
|
|
100
|
-
content: [{ type: "text", text:
|
|
121
|
+
content: [{ type: "text", text: formatGroup(result) }],
|
|
101
122
|
};
|
|
102
123
|
});
|
|
103
124
|
server.registerTool("create_group", {
|
package/dist/tools/agile.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { formatSprint, formatSprintList, formatAgileData } from "../formatters/index.js";
|
|
2
3
|
export function registerAgileTools(server, client) {
|
|
3
4
|
// === SPRINTS ===
|
|
4
5
|
server.registerTool("list_agile_sprints", {
|
|
@@ -8,8 +9,13 @@ export function registerAgileTools(server, client) {
|
|
|
8
9
|
},
|
|
9
10
|
}, async (params) => {
|
|
10
11
|
const result = await client.listAgileSprints(params.project_id);
|
|
12
|
+
if ("error" in result) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
11
17
|
return {
|
|
12
|
-
content: [{ type: "text", text:
|
|
18
|
+
content: [{ type: "text", text: formatSprintList(result) }],
|
|
13
19
|
};
|
|
14
20
|
});
|
|
15
21
|
server.registerTool("get_agile_sprint", {
|
|
@@ -20,8 +26,13 @@ export function registerAgileTools(server, client) {
|
|
|
20
26
|
},
|
|
21
27
|
}, async (params) => {
|
|
22
28
|
const result = await client.getAgileSprint(params.project_id, params.sprint_id);
|
|
29
|
+
if ("error" in result) {
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
23
34
|
return {
|
|
24
|
-
content: [{ type: "text", text:
|
|
35
|
+
content: [{ type: "text", text: formatSprint(result) }],
|
|
25
36
|
};
|
|
26
37
|
});
|
|
27
38
|
server.registerTool("create_agile_sprint", {
|
|
@@ -81,8 +92,13 @@ export function registerAgileTools(server, client) {
|
|
|
81
92
|
},
|
|
82
93
|
}, async (params) => {
|
|
83
94
|
const result = await client.getIssueAgileData(params.issue_id);
|
|
95
|
+
if ("error" in result) {
|
|
96
|
+
return {
|
|
97
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
84
100
|
return {
|
|
85
|
-
content: [{ type: "text", text:
|
|
101
|
+
content: [{ type: "text", text: formatAgileData(result) }],
|
|
86
102
|
};
|
|
87
103
|
});
|
|
88
104
|
server.registerTool("update_issue_agile_data", {
|
package/dist/tools/checklists.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { formatChecklist, formatChecklistList } from "../formatters/index.js";
|
|
2
3
|
export function registerChecklistsTools(server, client) {
|
|
3
4
|
server.registerTool("list_checklist_items", {
|
|
4
5
|
description: "List all checklist items for an issue (requires redmine_checklists plugin)",
|
|
@@ -7,8 +8,13 @@ export function registerChecklistsTools(server, client) {
|
|
|
7
8
|
},
|
|
8
9
|
}, async (params) => {
|
|
9
10
|
const result = await client.listChecklists(params.issue_id);
|
|
11
|
+
if ("error" in result) {
|
|
12
|
+
return {
|
|
13
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
10
16
|
return {
|
|
11
|
-
content: [{ type: "text", text:
|
|
17
|
+
content: [{ type: "text", text: formatChecklistList(result) }],
|
|
12
18
|
};
|
|
13
19
|
});
|
|
14
20
|
server.registerTool("get_checklist_item", {
|
|
@@ -18,8 +24,13 @@ export function registerChecklistsTools(server, client) {
|
|
|
18
24
|
},
|
|
19
25
|
}, async (params) => {
|
|
20
26
|
const result = await client.getChecklist(params.checklist_id);
|
|
27
|
+
if ("error" in result) {
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
21
32
|
return {
|
|
22
|
-
content: [{ type: "text", text:
|
|
33
|
+
content: [{ type: "text", text: formatChecklist(result) }],
|
|
23
34
|
};
|
|
24
35
|
});
|
|
25
36
|
server.registerTool("create_checklist_item", {
|
package/dist/tools/core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { formatIssueResponse } from "../formatters/index.js";
|
|
2
|
+
import { formatIssueResponse, formatIssueList, formatProject, formatProjectList } from "../formatters/index.js";
|
|
3
3
|
export function registerCoreTools(server, client) {
|
|
4
4
|
// === ISSUES ===
|
|
5
5
|
server.registerTool("list_issues", {
|
|
@@ -24,8 +24,13 @@ export function registerCoreTools(server, client) {
|
|
|
24
24
|
},
|
|
25
25
|
}, async (params) => {
|
|
26
26
|
const result = await client.listIssues(params);
|
|
27
|
+
if ("error" in result) {
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
27
32
|
return {
|
|
28
|
-
content: [{ type: "text", text:
|
|
33
|
+
content: [{ type: "text", text: formatIssueList(result) }],
|
|
29
34
|
};
|
|
30
35
|
});
|
|
31
36
|
server.registerTool("get_issue", {
|
|
@@ -33,6 +38,7 @@ export function registerCoreTools(server, client) {
|
|
|
33
38
|
inputSchema: {
|
|
34
39
|
issue_id: z.number().describe("The issue ID"),
|
|
35
40
|
include: z.string().optional().describe("Include: attachments, relations, journals, watchers, children, changesets, allowed_statuses"),
|
|
41
|
+
include_description_diffs: z.boolean().optional().default(false).describe("Include full description diffs in history (can be verbose)"),
|
|
36
42
|
},
|
|
37
43
|
}, async (params) => {
|
|
38
44
|
// Fetch issue and enumerations in parallel
|
|
@@ -71,7 +77,9 @@ export function registerCoreTools(server, client) {
|
|
|
71
77
|
}
|
|
72
78
|
// Format response as Markdown
|
|
73
79
|
return {
|
|
74
|
-
content: [{ type: "text", text: formatIssueResponse(result, lookup
|
|
80
|
+
content: [{ type: "text", text: formatIssueResponse(result, lookup, {
|
|
81
|
+
includeDescriptionDiffs: params.include_description_diffs,
|
|
82
|
+
}) }],
|
|
75
83
|
};
|
|
76
84
|
});
|
|
77
85
|
server.registerTool("create_issue", {
|
|
@@ -181,8 +189,13 @@ export function registerCoreTools(server, client) {
|
|
|
181
189
|
},
|
|
182
190
|
}, async (params) => {
|
|
183
191
|
const result = await client.listProjects(params);
|
|
192
|
+
if ("error" in result) {
|
|
193
|
+
return {
|
|
194
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
184
197
|
return {
|
|
185
|
-
content: [{ type: "text", text:
|
|
198
|
+
content: [{ type: "text", text: formatProjectList(result) }],
|
|
186
199
|
};
|
|
187
200
|
});
|
|
188
201
|
server.registerTool("get_project", {
|
|
@@ -193,8 +206,13 @@ export function registerCoreTools(server, client) {
|
|
|
193
206
|
},
|
|
194
207
|
}, async (params) => {
|
|
195
208
|
const result = await client.getProject(params.project_id, params.include);
|
|
209
|
+
if ("error" in result) {
|
|
210
|
+
return {
|
|
211
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
196
214
|
return {
|
|
197
|
-
content: [{ type: "text", text:
|
|
215
|
+
content: [{ type: "text", text: formatProject(result) }],
|
|
198
216
|
};
|
|
199
217
|
});
|
|
200
218
|
server.registerTool("create_project", {
|
|
@@ -1,26 +1,42 @@
|
|
|
1
|
+
import { formatPriorityList, formatActivityList, formatDocumentCategoryList } from "../formatters/index.js";
|
|
1
2
|
export function registerEnumerationsTools(server, client) {
|
|
2
3
|
server.registerTool("list_issue_priorities", {
|
|
3
4
|
description: "List all issue priorities with their IDs (Low, Normal, High, Urgent, Immediate)",
|
|
4
5
|
}, async () => {
|
|
5
6
|
const result = await client.listIssuePriorities();
|
|
7
|
+
if ("error" in result) {
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
6
12
|
return {
|
|
7
|
-
content: [{ type: "text", text:
|
|
13
|
+
content: [{ type: "text", text: formatPriorityList(result) }],
|
|
8
14
|
};
|
|
9
15
|
});
|
|
10
16
|
server.registerTool("list_time_entry_activities", {
|
|
11
17
|
description: "List all time entry activities with their IDs (Design, Development, etc.)",
|
|
12
18
|
}, async () => {
|
|
13
19
|
const result = await client.listTimeEntryActivities();
|
|
20
|
+
if ("error" in result) {
|
|
21
|
+
return {
|
|
22
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
14
25
|
return {
|
|
15
|
-
content: [{ type: "text", text:
|
|
26
|
+
content: [{ type: "text", text: formatActivityList(result) }],
|
|
16
27
|
};
|
|
17
28
|
});
|
|
18
29
|
server.registerTool("list_document_categories", {
|
|
19
30
|
description: "List all document categories with their IDs",
|
|
20
31
|
}, async () => {
|
|
21
32
|
const result = await client.listDocumentCategories();
|
|
33
|
+
if ("error" in result) {
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
22
38
|
return {
|
|
23
|
-
content: [{ type: "text", text:
|
|
39
|
+
content: [{ type: "text", text: formatDocumentCategoryList(result) }],
|
|
24
40
|
};
|
|
25
41
|
});
|
|
26
42
|
}
|
package/dist/tools/files.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { readFile } from "fs/promises";
|
|
3
|
+
import { formatAttachment, formatFileList } from "../formatters/index.js";
|
|
3
4
|
export function registerFilesTools(server, client) {
|
|
4
5
|
// === ATTACHMENTS ===
|
|
5
6
|
server.registerTool("get_attachment", {
|
|
@@ -9,8 +10,13 @@ export function registerFilesTools(server, client) {
|
|
|
9
10
|
},
|
|
10
11
|
}, async (params) => {
|
|
11
12
|
const result = await client.getAttachment(params.attachment_id);
|
|
13
|
+
if ("error" in result) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
12
18
|
return {
|
|
13
|
-
content: [{ type: "text", text:
|
|
19
|
+
content: [{ type: "text", text: formatAttachment(result) }],
|
|
14
20
|
};
|
|
15
21
|
});
|
|
16
22
|
server.registerTool("delete_attachment", {
|
|
@@ -47,8 +53,13 @@ export function registerFilesTools(server, client) {
|
|
|
47
53
|
},
|
|
48
54
|
}, async (params) => {
|
|
49
55
|
const result = await client.listProjectFiles(params.project_id);
|
|
56
|
+
if ("error" in result) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
50
61
|
return {
|
|
51
|
-
content: [{ type: "text", text:
|
|
62
|
+
content: [{ type: "text", text: formatFileList(result) }],
|
|
52
63
|
};
|
|
53
64
|
});
|
|
54
65
|
server.registerTool("upload_project_file", {
|