@zt-playground/mcp-server 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 (90) hide show
  1. package/README.md +190 -0
  2. package/dist/auth.d.ts +6 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +44 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/context-store.d.ts +42 -0
  7. package/dist/context-store.d.ts.map +1 -0
  8. package/dist/context-store.js +119 -0
  9. package/dist/context-store.js.map +1 -0
  10. package/dist/http-client.d.ts +17 -0
  11. package/dist/http-client.d.ts.map +1 -0
  12. package/dist/http-client.js +113 -0
  13. package/dist/http-client.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +59 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/resources/issue.d.ts +3 -0
  19. package/dist/resources/issue.d.ts.map +1 -0
  20. package/dist/resources/issue.js +59 -0
  21. package/dist/resources/issue.js.map +1 -0
  22. package/dist/resources/project.d.ts +3 -0
  23. package/dist/resources/project.d.ts.map +1 -0
  24. package/dist/resources/project.js +51 -0
  25. package/dist/resources/project.js.map +1 -0
  26. package/dist/tools/analytics.d.ts +3 -0
  27. package/dist/tools/analytics.d.ts.map +1 -0
  28. package/dist/tools/analytics.js +53 -0
  29. package/dist/tools/analytics.js.map +1 -0
  30. package/dist/tools/attachments.d.ts +3 -0
  31. package/dist/tools/attachments.d.ts.map +1 -0
  32. package/dist/tools/attachments.js +30 -0
  33. package/dist/tools/attachments.js.map +1 -0
  34. package/dist/tools/auth.d.ts +3 -0
  35. package/dist/tools/auth.d.ts.map +1 -0
  36. package/dist/tools/auth.js +68 -0
  37. package/dist/tools/auth.js.map +1 -0
  38. package/dist/tools/bugs.d.ts +3 -0
  39. package/dist/tools/bugs.d.ts.map +1 -0
  40. package/dist/tools/bugs.js +78 -0
  41. package/dist/tools/bugs.js.map +1 -0
  42. package/dist/tools/comments.d.ts +3 -0
  43. package/dist/tools/comments.d.ts.map +1 -0
  44. package/dist/tools/comments.js +28 -0
  45. package/dist/tools/comments.js.map +1 -0
  46. package/dist/tools/context.d.ts +3 -0
  47. package/dist/tools/context.d.ts.map +1 -0
  48. package/dist/tools/context.js +106 -0
  49. package/dist/tools/context.js.map +1 -0
  50. package/dist/tools/database.d.ts +3 -0
  51. package/dist/tools/database.d.ts.map +1 -0
  52. package/dist/tools/database.js +154 -0
  53. package/dist/tools/database.js.map +1 -0
  54. package/dist/tools/epics.d.ts +3 -0
  55. package/dist/tools/epics.d.ts.map +1 -0
  56. package/dist/tools/epics.js +78 -0
  57. package/dist/tools/epics.js.map +1 -0
  58. package/dist/tools/issues.d.ts +3 -0
  59. package/dist/tools/issues.d.ts.map +1 -0
  60. package/dist/tools/issues.js +137 -0
  61. package/dist/tools/issues.js.map +1 -0
  62. package/dist/tools/labels.d.ts +3 -0
  63. package/dist/tools/labels.d.ts.map +1 -0
  64. package/dist/tools/labels.js +39 -0
  65. package/dist/tools/labels.js.map +1 -0
  66. package/dist/tools/monitor.d.ts +3 -0
  67. package/dist/tools/monitor.d.ts.map +1 -0
  68. package/dist/tools/monitor.js +115 -0
  69. package/dist/tools/monitor.js.map +1 -0
  70. package/dist/tools/projects.d.ts +3 -0
  71. package/dist/tools/projects.d.ts.map +1 -0
  72. package/dist/tools/projects.js +39 -0
  73. package/dist/tools/projects.js.map +1 -0
  74. package/dist/tools/relations.d.ts +3 -0
  75. package/dist/tools/relations.d.ts.map +1 -0
  76. package/dist/tools/relations.js +51 -0
  77. package/dist/tools/relations.js.map +1 -0
  78. package/dist/tools/repository.d.ts +3 -0
  79. package/dist/tools/repository.d.ts.map +1 -0
  80. package/dist/tools/repository.js +176 -0
  81. package/dist/tools/repository.js.map +1 -0
  82. package/dist/tools/ssh.d.ts +3 -0
  83. package/dist/tools/ssh.d.ts.map +1 -0
  84. package/dist/tools/ssh.js +141 -0
  85. package/dist/tools/ssh.js.map +1 -0
  86. package/dist/utils.d.ts +15 -0
  87. package/dist/utils.d.ts.map +1 -0
  88. package/dist/utils.js +31 -0
  89. package/dist/utils.js.map +1 -0
  90. package/package.json +30 -0
@@ -0,0 +1,39 @@
1
+ import { z } from 'zod';
2
+ import { api } from '../http-client.js';
3
+ import { jsonResult, errorResult, resolveProjectId } from '../utils.js';
4
+ export function registerProjectTools(server) {
5
+ server.tool('list_projects', 'Lists all available projects with issue summary', {}, async () => {
6
+ try {
7
+ const projects = await api.get('/project');
8
+ const results = projects.map((p) => ({
9
+ id: p.id,
10
+ name: p.name,
11
+ description: p.description,
12
+ platforms: p.platforms?.map((pl) => pl.name) || [],
13
+ issueCount: p._count?.tasks ?? 0,
14
+ }));
15
+ return jsonResult(results);
16
+ }
17
+ catch (err) {
18
+ return errorResult(`Failed to list projects: ${err.message}`);
19
+ }
20
+ });
21
+ server.tool('get_platforms', 'Lists configured platforms for a project', {
22
+ projectId: z.string().describe('Project ID or name'),
23
+ }, async ({ projectId: pid }) => {
24
+ try {
25
+ const projectId = await resolveProjectId(pid);
26
+ const project = await api.get(`/project/${projectId}`);
27
+ const platforms = project.platforms || [];
28
+ return jsonResult(platforms.map((p) => ({
29
+ name: p.name,
30
+ repository: p.repository,
31
+ description: p.description,
32
+ })));
33
+ }
34
+ catch (err) {
35
+ return errorResult(`Failed to get platforms: ${err.message}`);
36
+ }
37
+ });
38
+ }
39
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,MAAM,CAAC,IAAI,CACT,eAAe,EACf,iDAAiD,EACjD,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,GAAG,CAAQ,UAAU,CAAC,CAAC;YAElD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBACxC,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE;gBACvD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC;aACjC,CAAC,CAAC,CAAC;YAEJ,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,WAAW,CAAC,4BAA4B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,eAAe,EACf,0CAA0C,EAC1C;QACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;KACrD,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,GAAG,CAAM,YAAY,SAAS,EAAE,CAAC,CAAC;YAE5D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;YAC1C,OAAO,UAAU,CACf,SAAS,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBACzB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,WAAW,EAAE,CAAC,CAAC,WAAW;aAC3B,CAAC,CAAC,CACJ,CAAC;QACJ,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,WAAW,CAAC,4BAA4B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerRelationTools(server: McpServer): void;
3
+ //# sourceMappingURL=relations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../../src/tools/relations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,QA0DtD"}
@@ -0,0 +1,51 @@
1
+ import { z } from 'zod';
2
+ import { api } from '../http-client.js';
3
+ import { jsonResult, errorResult } from '../utils.js';
4
+ export function registerRelationTools(server) {
5
+ server.tool('link_issues', 'Creates a relation between two issues (BLOCKS, RELATES_TO, DUPLICATES)', {
6
+ sourceId: z.string().describe('Source issue ID'),
7
+ targetId: z.string().describe('Target issue ID'),
8
+ type: z
9
+ .enum(['BLOCKS', 'RELATES_TO', 'DUPLICATES'])
10
+ .describe('Relation type'),
11
+ }, async ({ sourceId, targetId, type }) => {
12
+ if (sourceId === targetId) {
13
+ return errorResult('Cannot create a relation to the same issue');
14
+ }
15
+ try {
16
+ const relation = await api.post(`/tasks/${sourceId}/relations`, {
17
+ targetId,
18
+ type,
19
+ });
20
+ return jsonResult({
21
+ id: relation.id,
22
+ type: relation.type,
23
+ source: relation.source,
24
+ target: relation.target,
25
+ createdAt: relation.createdAt,
26
+ });
27
+ }
28
+ catch (err) {
29
+ const message = err.message.includes('already exists')
30
+ ? 'This relation already exists'
31
+ : err.message;
32
+ return errorResult(message);
33
+ }
34
+ });
35
+ server.tool('unlink_issues', 'Removes a relation between issues', {
36
+ issueId: z.string().describe('Issue ID that has the relation'),
37
+ relationId: z.string().describe('Relation ID to remove'),
38
+ }, async ({ issueId, relationId }) => {
39
+ try {
40
+ await api.delete(`/tasks/${issueId}/relations/${relationId}`);
41
+ return jsonResult({
42
+ deleted: true,
43
+ relationId,
44
+ });
45
+ }
46
+ catch (err) {
47
+ return errorResult('Relation not found or already deleted');
48
+ }
49
+ });
50
+ }
51
+ //# sourceMappingURL=relations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations.js","sourceRoot":"","sources":["../../src/tools/relations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,IAAI,CACT,aAAa,EACb,wEAAwE,EACxE;QACE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAChD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAChD,IAAI,EAAE,CAAC;aACJ,IAAI,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;aAC5C,QAAQ,CAAC,eAAe,CAAC;KAC7B,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QACrC,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,WAAW,CAAC,4CAA4C,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,CAAM,UAAU,QAAQ,YAAY,EAAE;gBACnE,QAAQ;gBACR,IAAI;aACL,CAAC,CAAC;YAEH,OAAO,UAAU,CAAC;gBAChB,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,SAAS,EAAE,QAAQ,CAAC,SAAS;aAC9B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;gBACpD,CAAC,CAAC,8BAA8B;gBAChC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;YAChB,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,eAAe,EACf,mCAAmC,EACnC;QACE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QAC9D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;KACzD,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE;QAChC,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,MAAM,CAAC,UAAU,OAAO,cAAc,UAAU,EAAE,CAAC,CAAC;YAE9D,OAAO,UAAU,CAAC;gBAChB,OAAO,EAAE,IAAI;gBACb,UAAU;aACX,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,WAAW,CAAC,uCAAuC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerRepositoryTools(server: McpServer): void;
3
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/tools/repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,QAgNxD"}
@@ -0,0 +1,176 @@
1
+ import { z } from 'zod';
2
+ import { execSync } from 'node:child_process';
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ import { jsonResult, errorResult } from '../utils.js';
6
+ import { resolveRepo } from '../context-store.js';
7
+ function exec(cmd, cwd, timeout = 30_000) {
8
+ return execSync(cmd, { cwd, encoding: 'utf-8', timeout, maxBuffer: 10 * 1024 * 1024 }).trim();
9
+ }
10
+ export function registerRepositoryTools(server) {
11
+ server.tool('repo_list_files', 'Lists files in a repository using git ls-files with optional glob pattern', {
12
+ repoName: z.string().optional().describe('Repo name from context (auto-selected if only one)'),
13
+ pattern: z.string().default('*').describe('Glob pattern to filter files (e.g. "*.ts", "src/**/*.ts")'),
14
+ }, async ({ repoName, pattern }) => {
15
+ try {
16
+ const repo = resolveRepo(repoName);
17
+ const output = exec(`git ls-files "${pattern}"`, repo.path);
18
+ const files = output ? output.split('\n') : [];
19
+ return jsonResult({ repo: repo.name, pattern, count: files.length, files });
20
+ }
21
+ catch (err) {
22
+ return errorResult(err.message);
23
+ }
24
+ });
25
+ server.tool('repo_read_file', 'Reads a file from a repository with optional line range', {
26
+ repoName: z.string().optional().describe('Repo name from context'),
27
+ filePath: z.string().describe('Relative path to file within the repo'),
28
+ startLine: z.number().optional().describe('Start line (1-based)'),
29
+ endLine: z.number().optional().describe('End line (1-based, inclusive)'),
30
+ }, async ({ repoName, filePath, startLine, endLine }) => {
31
+ try {
32
+ const repo = resolveRepo(repoName);
33
+ const fullPath = resolve(repo.path, filePath);
34
+ if (!fullPath.startsWith(resolve(repo.path))) {
35
+ return errorResult('Path traversal not allowed.');
36
+ }
37
+ if (!existsSync(fullPath)) {
38
+ return errorResult(`File not found: ${filePath}`);
39
+ }
40
+ const content = readFileSync(fullPath, 'utf-8');
41
+ const lines = content.split('\n');
42
+ if (startLine || endLine) {
43
+ const start = (startLine ?? 1) - 1;
44
+ const end = endLine ?? lines.length;
45
+ const slice = lines.slice(start, end);
46
+ return jsonResult({
47
+ repo: repo.name,
48
+ file: filePath,
49
+ startLine: start + 1,
50
+ endLine: Math.min(end, lines.length),
51
+ totalLines: lines.length,
52
+ content: slice.join('\n'),
53
+ });
54
+ }
55
+ return jsonResult({
56
+ repo: repo.name,
57
+ file: filePath,
58
+ totalLines: lines.length,
59
+ content,
60
+ });
61
+ }
62
+ catch (err) {
63
+ return errorResult(err.message);
64
+ }
65
+ });
66
+ server.tool('repo_search', 'Searches for a pattern in repository files using grep', {
67
+ repoName: z.string().optional().describe('Repo name from context'),
68
+ pattern: z.string().describe('Search pattern (regex)'),
69
+ glob: z.string().optional().describe('File glob filter (e.g. "*.ts")'),
70
+ maxResults: z.number().default(50).describe('Maximum number of matches'),
71
+ }, async ({ repoName, pattern, glob, maxResults }) => {
72
+ try {
73
+ const repo = resolveRepo(repoName);
74
+ const includeArg = glob ? `--include="${glob}"` : '';
75
+ const cmd = `grep -rn ${includeArg} -E "${pattern.replace(/"/g, '\\"')}" . | head -n ${maxResults}`;
76
+ const output = exec(cmd, repo.path, 15_000);
77
+ const matches = output ? output.split('\n').filter(Boolean) : [];
78
+ return jsonResult({ repo: repo.name, pattern, count: matches.length, matches });
79
+ }
80
+ catch (err) {
81
+ if (err.status === 1)
82
+ return jsonResult({ repo: resolveRepo(repoName).name, pattern, count: 0, matches: [] });
83
+ return errorResult(err.message);
84
+ }
85
+ });
86
+ server.tool('repo_git_log', 'Shows recent git commit history', {
87
+ repoName: z.string().optional().describe('Repo name from context'),
88
+ count: z.number().default(20).describe('Number of commits to show'),
89
+ branch: z.string().optional().describe('Branch name (defaults to current)'),
90
+ }, async ({ repoName, count, branch }) => {
91
+ try {
92
+ const repo = resolveRepo(repoName);
93
+ const branchArg = branch ?? '';
94
+ const output = exec(`git log --oneline --format="%h %an %ar %s" -n ${count} ${branchArg}`, repo.path);
95
+ const commits = output ? output.split('\n').filter(Boolean) : [];
96
+ return jsonResult({ repo: repo.name, count: commits.length, commits });
97
+ }
98
+ catch (err) {
99
+ return errorResult(err.message);
100
+ }
101
+ });
102
+ server.tool('repo_git_status', 'Shows git status (modified, staged, untracked files)', {
103
+ repoName: z.string().optional().describe('Repo name from context'),
104
+ }, async ({ repoName }) => {
105
+ try {
106
+ const repo = resolveRepo(repoName);
107
+ const output = exec('git status --porcelain', repo.path);
108
+ const branch = exec('git branch --show-current', repo.path);
109
+ const files = output
110
+ ? output.split('\n').filter(Boolean).map((line) => ({
111
+ status: line.substring(0, 2).trim(),
112
+ file: line.substring(3),
113
+ }))
114
+ : [];
115
+ return jsonResult({ repo: repo.name, branch, files });
116
+ }
117
+ catch (err) {
118
+ return errorResult(err.message);
119
+ }
120
+ });
121
+ server.tool('repo_git_diff', 'Shows git diff (staged or unstaged changes, or between refs)', {
122
+ repoName: z.string().optional().describe('Repo name from context'),
123
+ ref: z.string().optional().describe('Git ref to diff against (e.g. "HEAD~1", "main", "staged")'),
124
+ }, async ({ repoName, ref }) => {
125
+ try {
126
+ const repo = resolveRepo(repoName);
127
+ let cmd = 'git diff';
128
+ if (ref === 'staged')
129
+ cmd = 'git diff --cached';
130
+ else if (ref)
131
+ cmd = `git diff ${ref}`;
132
+ const output = exec(cmd, repo.path, 15_000);
133
+ return jsonResult({ repo: repo.name, ref: ref ?? 'working tree', diff: output || '(no changes)' });
134
+ }
135
+ catch (err) {
136
+ return errorResult(err.message);
137
+ }
138
+ });
139
+ server.tool('repo_git_branches', 'Lists all branches (local and remote)', {
140
+ repoName: z.string().optional().describe('Repo name from context'),
141
+ }, async ({ repoName }) => {
142
+ try {
143
+ const repo = resolveRepo(repoName);
144
+ const current = exec('git branch --show-current', repo.path);
145
+ const output = exec('git branch -a --format="%(refname:short)"', repo.path);
146
+ const branches = output ? output.split('\n').filter(Boolean) : [];
147
+ return jsonResult({ repo: repo.name, current, branches });
148
+ }
149
+ catch (err) {
150
+ return errorResult(err.message);
151
+ }
152
+ });
153
+ server.tool('repo_exec', 'Executes a shell command in the repository directory (timeout: 30s)', {
154
+ repoName: z.string().optional().describe('Repo name from context'),
155
+ command: z.string().describe('Command to execute'),
156
+ }, async ({ repoName, command }) => {
157
+ try {
158
+ const repo = resolveRepo(repoName);
159
+ const output = exec(command, repo.path, 30_000);
160
+ return jsonResult({ repo: repo.name, command, output });
161
+ }
162
+ catch (err) {
163
+ if (err.stdout || err.stderr) {
164
+ return jsonResult({
165
+ repo: resolveRepo(repoName).name,
166
+ command,
167
+ exitCode: err.status ?? 1,
168
+ stdout: err.stdout?.toString() ?? '',
169
+ stderr: err.stderr?.toString() ?? '',
170
+ });
171
+ }
172
+ return errorResult(err.message);
173
+ }
174
+ });
175
+ }
176
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/tools/repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerSshTools(server: McpServer): void;
3
+ //# sourceMappingURL=ssh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../src/tools/ssh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmEpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,QA8FjD"}
@@ -0,0 +1,141 @@
1
+ import { z } from 'zod';
2
+ import { Client } from 'ssh2';
3
+ import { readFileSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ import { jsonResult, errorResult } from '../utils.js';
7
+ import { resolveSsh } from '../context-store.js';
8
+ function expandHome(p) {
9
+ if (p.startsWith('~/'))
10
+ return resolve(homedir(), p.slice(2));
11
+ return p;
12
+ }
13
+ function sshExec(config, command, timeout = 30_000) {
14
+ return new Promise((resolve, reject) => {
15
+ const conn = new Client();
16
+ const timer = setTimeout(() => {
17
+ conn.end();
18
+ reject(new Error(`SSH command timed out after ${timeout}ms`));
19
+ }, timeout);
20
+ conn.on('ready', () => {
21
+ conn.exec(command, (err, stream) => {
22
+ if (err) {
23
+ clearTimeout(timer);
24
+ conn.end();
25
+ reject(err);
26
+ return;
27
+ }
28
+ let stdout = '';
29
+ let stderr = '';
30
+ stream.on('data', (data) => { stdout += data.toString(); });
31
+ stream.stderr.on('data', (data) => { stderr += data.toString(); });
32
+ stream.on('close', (code) => {
33
+ clearTimeout(timer);
34
+ conn.end();
35
+ resolve({ stdout: stdout.trim(), stderr: stderr.trim(), code: code ?? 0 });
36
+ });
37
+ });
38
+ });
39
+ conn.on('error', (err) => {
40
+ clearTimeout(timer);
41
+ reject(err);
42
+ });
43
+ const connectConfig = {
44
+ host: config.host,
45
+ port: config.port,
46
+ username: config.user,
47
+ readyTimeout: 10_000,
48
+ };
49
+ if (config.keyPath) {
50
+ connectConfig.privateKey = readFileSync(expandHome(config.keyPath));
51
+ }
52
+ else if (config.password) {
53
+ connectConfig.password = config.password;
54
+ }
55
+ conn.connect(connectConfig);
56
+ });
57
+ }
58
+ export function registerSshTools(server) {
59
+ server.tool('ssh_exec', 'Executes a command on a remote server via SSH', {
60
+ serverName: z.string().optional().describe('SSH server name from context (auto-selected if only one)'),
61
+ command: z.string().describe('Command to execute remotely'),
62
+ timeout: z.number().default(30000).describe('Timeout in milliseconds'),
63
+ }, async ({ serverName, command, timeout }) => {
64
+ try {
65
+ const ssh = resolveSsh(serverName);
66
+ const result = await sshExec(ssh, command, timeout);
67
+ return jsonResult({
68
+ server: ssh.name,
69
+ host: ssh.host,
70
+ command,
71
+ exitCode: result.code,
72
+ stdout: result.stdout,
73
+ stderr: result.stderr || undefined,
74
+ });
75
+ }
76
+ catch (err) {
77
+ return errorResult(`SSH error: ${err.message}`);
78
+ }
79
+ });
80
+ server.tool('ssh_logs', 'Reads log files from a remote server (tail + optional grep filter)', {
81
+ serverName: z.string().optional().describe('SSH server name from context'),
82
+ logPath: z.string().describe('Path to log file on the server'),
83
+ lines: z.number().default(100).describe('Number of lines to tail'),
84
+ filter: z.string().optional().describe('Grep filter pattern'),
85
+ }, async ({ serverName, logPath, lines, filter }) => {
86
+ try {
87
+ const ssh = resolveSsh(serverName);
88
+ let cmd = `tail -n ${lines} ${logPath}`;
89
+ if (filter)
90
+ cmd += ` | grep -E "${filter.replace(/"/g, '\\"')}"`;
91
+ const result = await sshExec(ssh, cmd, 15_000);
92
+ const logLines = result.stdout ? result.stdout.split('\n') : [];
93
+ return jsonResult({
94
+ server: ssh.name,
95
+ logPath,
96
+ filter: filter ?? null,
97
+ lineCount: logLines.length,
98
+ lines: logLines,
99
+ });
100
+ }
101
+ catch (err) {
102
+ return errorResult(`SSH logs error: ${err.message}`);
103
+ }
104
+ });
105
+ server.tool('ssh_status', 'Gets server health status (uptime, memory, disk, top processes)', {
106
+ serverName: z.string().optional().describe('SSH server name from context'),
107
+ }, async ({ serverName }) => {
108
+ try {
109
+ const ssh = resolveSsh(serverName);
110
+ const cmd = [
111
+ 'echo "---UPTIME---" && uptime',
112
+ 'echo "---MEMORY---" && free -h',
113
+ 'echo "---DISK---" && df -h',
114
+ 'echo "---PROCESSES---" && ps aux --sort=-%mem | head -16',
115
+ ].join(' && ');
116
+ const result = await sshExec(ssh, cmd, 15_000);
117
+ const sections = result.stdout.split('---');
118
+ const parsed = {};
119
+ for (let i = 0; i < sections.length; i++) {
120
+ const section = sections[i].trim();
121
+ if (section === 'UPTIME' && sections[i + 1])
122
+ parsed.uptime = sections[i + 1].replace('---', '').trim();
123
+ if (section === 'MEMORY' && sections[i + 1])
124
+ parsed.memory = sections[i + 1].replace('---', '').trim();
125
+ if (section === 'DISK' && sections[i + 1])
126
+ parsed.disk = sections[i + 1].replace('---', '').trim();
127
+ if (section === 'PROCESSES' && sections[i + 1])
128
+ parsed.processes = sections[i + 1].replace('---', '').trim();
129
+ }
130
+ return jsonResult({
131
+ server: ssh.name,
132
+ host: ssh.host,
133
+ ...parsed,
134
+ });
135
+ }
136
+ catch (err) {
137
+ return errorResult(`SSH status error: ${err.message}`);
138
+ }
139
+ });
140
+ }
141
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/tools/ssh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export declare function jsonResult(data: unknown): {
2
+ content: {
3
+ type: "text";
4
+ text: string;
5
+ }[];
6
+ };
7
+ export declare function errorResult(msg: string): {
8
+ content: {
9
+ type: "text";
10
+ text: string;
11
+ }[];
12
+ isError: boolean;
13
+ };
14
+ export declare function resolveProjectId(projectIdOrName: string): Promise<string>;
15
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO;;;;;EAIvC;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM;;;;;;EAKtC;AAeD,wBAAsB,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO/E"}
package/dist/utils.js ADDED
@@ -0,0 +1,31 @@
1
+ import { api } from './http-client.js';
2
+ export function jsonResult(data) {
3
+ return {
4
+ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
5
+ };
6
+ }
7
+ export function errorResult(msg) {
8
+ return {
9
+ content: [{ type: 'text', text: msg }],
10
+ isError: true,
11
+ };
12
+ }
13
+ // Cache projects for 1 minute
14
+ let projectCache = null;
15
+ const CACHE_TTL = 60_000;
16
+ async function getProjects() {
17
+ if (projectCache && Date.now() - projectCache.timestamp < CACHE_TTL) {
18
+ return projectCache.data;
19
+ }
20
+ const projects = await api.get('/project');
21
+ projectCache = { data: projects, timestamp: Date.now() };
22
+ return projects;
23
+ }
24
+ export async function resolveProjectId(projectIdOrName) {
25
+ const projects = await getProjects();
26
+ const project = projects.find((p) => p.id === projectIdOrName || p.name === projectIdOrName);
27
+ if (!project)
28
+ throw new Error(`Project not found: ${projectIdOrName}`);
29
+ return project.id;
30
+ }
31
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAEvC,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAC/C,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED,8BAA8B;AAC9B,IAAI,YAAY,GAA8C,IAAI,CAAC;AACnE,MAAM,SAAS,GAAG,MAAM,CAAC;AAEzB,KAAK,UAAU,WAAW;IACxB,IAAI,YAAY,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC;QACpE,OAAO,YAAY,CAAC,IAAI,CAAC;IAC3B,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,GAAG,CAAQ,UAAU,CAAC,CAAC;IAClD,YAAY,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACzD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,eAAuB;IAC5D,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAC3B,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,IAAI,CAAC,CAAC,IAAI,KAAK,eAAe,CACnE,CAAC;IACF,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,eAAe,EAAE,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC,EAAE,CAAC;AACpB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@zt-playground/mcp-server",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "zt-playground-mcp": "dist/index.js"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsx src/index.ts",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "dependencies": {
18
+ "@modelcontextprotocol/sdk": "^1.12.1",
19
+ "pg": "^8.13.0",
20
+ "ssh2": "^1.16.0",
21
+ "zod": "^3.24.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.0.0",
25
+ "@types/pg": "^8.11.0",
26
+ "@types/ssh2": "^1.15.0",
27
+ "tsx": "^4.19.0",
28
+ "typescript": "^5.7.0"
29
+ }
30
+ }