@lioneltay/worktree-mcp 0.0.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/.claude-plugin/plugin.json +10 -0
- package/.mcp.json +8 -0
- package/README.md +70 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +149 -0
- package/package.json +34 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "worktree-mcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Manage git worktrees from Claude Code",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "lioneltay"
|
|
7
|
+
},
|
|
8
|
+
"repository": "https://github.com/lioneltay/agent-forge/tree/main/packages/claude-plugins/worktree-mcp",
|
|
9
|
+
"license": "MIT"
|
|
10
|
+
}
|
package/.mcp.json
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# @lioneltay/worktree-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for managing git worktrees so AI agents can create isolated workspaces for parallel development.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Add the MCP server to Claude Code:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
claude mcp add worktree npx -y @lioneltay/worktree-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or add it manually to your `.mcp.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"worktree": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@lioneltay/worktree-mcp"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Claude Code Plugin
|
|
27
|
+
|
|
28
|
+
You can also install as a plugin:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# 1. Add the marketplace
|
|
32
|
+
claude plugin marketplace add lioneltay/agent-forge
|
|
33
|
+
|
|
34
|
+
# 2. Install the plugin
|
|
35
|
+
claude plugin install worktree-mcp@agent-forge
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
### `create_worktree`
|
|
41
|
+
|
|
42
|
+
Create a new git worktree.
|
|
43
|
+
|
|
44
|
+
**Input:**
|
|
45
|
+
- `branch` (string, required) — Branch name to checkout or create
|
|
46
|
+
- `newBranch` (boolean, optional) — Create a new branch
|
|
47
|
+
- `from` (string, optional) — Base branch/commit for new branch
|
|
48
|
+
- `name` (string, optional) — Custom folder name
|
|
49
|
+
|
|
50
|
+
**Returns:** The created worktree name and path.
|
|
51
|
+
|
|
52
|
+
### `list_worktrees`
|
|
53
|
+
|
|
54
|
+
List all managed worktrees with status.
|
|
55
|
+
|
|
56
|
+
**Returns:** JSON array of worktree objects with name, path, branch, status, ahead/behind counts, and change counts.
|
|
57
|
+
|
|
58
|
+
### `remove_worktree`
|
|
59
|
+
|
|
60
|
+
Remove a git worktree.
|
|
61
|
+
|
|
62
|
+
**Input:**
|
|
63
|
+
- `name` (string, required) — Worktree name to remove
|
|
64
|
+
- `force` (boolean, optional) — Force removal with uncommitted changes
|
|
65
|
+
|
|
66
|
+
**Returns:** Confirmation message.
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
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 { create, list, remove, status } from "@lioneltay/worktree-manager";
|
|
6
|
+
const server = new McpServer({
|
|
7
|
+
name: "worktree-mcp",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
});
|
|
10
|
+
server.registerTool("create_worktree", {
|
|
11
|
+
description: "Create a new git worktree. Worktrees allow parallel work on multiple branches without stashing or switching.",
|
|
12
|
+
inputSchema: {
|
|
13
|
+
branch: z.string().describe("Branch name to checkout or create"),
|
|
14
|
+
from: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Base branch/commit to create a new branch from (only used when branch doesn't exist yet)"),
|
|
18
|
+
},
|
|
19
|
+
}, async ({ branch, from }) => {
|
|
20
|
+
try {
|
|
21
|
+
let result;
|
|
22
|
+
let created = false;
|
|
23
|
+
try {
|
|
24
|
+
result = await create(branch, { from });
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
if (e instanceof Error && e.message.includes("not found")) {
|
|
28
|
+
result = await create(branch, { newBranch: true, from });
|
|
29
|
+
created = true;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: created
|
|
40
|
+
? `Created new branch '${branch}' and worktree '${result.name}' at ${result.path}`
|
|
41
|
+
: `Checked out existing branch '${branch}' into worktree '${result.name}' at ${result.path}`,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: `Failed to create worktree: ${error instanceof Error ? error.message : String(error)}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
server.registerTool("list_worktrees", {
|
|
59
|
+
description: "List all managed git worktrees with their status, branch, and change counts.",
|
|
60
|
+
inputSchema: {},
|
|
61
|
+
}, async () => {
|
|
62
|
+
try {
|
|
63
|
+
const worktrees = await list();
|
|
64
|
+
if (worktrees.length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: "text", text: "No worktrees found." }],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: JSON.stringify(worktrees, null, 2),
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
return {
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
type: "text",
|
|
83
|
+
text: `Failed to list worktrees: ${error instanceof Error ? error.message : String(error)}`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
isError: true,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* Resolve a worktree name or branch name to the actual worktree name.
|
|
92
|
+
* Tries the input as-is first, then normalizes it (strip origin/, replace /\ with --).
|
|
93
|
+
*/
|
|
94
|
+
async function resolveWorktreeName(input) {
|
|
95
|
+
// Try exact match first
|
|
96
|
+
const exact = await status(input);
|
|
97
|
+
if (exact)
|
|
98
|
+
return input;
|
|
99
|
+
// Normalize: strip origin/ prefix, replace /\ with --
|
|
100
|
+
const normalized = input.replace(/^origin\//, "").replace(/[/\\]/g, "--");
|
|
101
|
+
if (normalized !== input) {
|
|
102
|
+
const found = await status(normalized);
|
|
103
|
+
if (found)
|
|
104
|
+
return normalized;
|
|
105
|
+
}
|
|
106
|
+
return input; // Let remove() throw its own "not found" error
|
|
107
|
+
}
|
|
108
|
+
server.registerTool("remove_worktree", {
|
|
109
|
+
description: "Remove a git worktree. Accepts the worktree name or branch name. Fails if the worktree has uncommitted changes unless force is true.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
name: z.string().describe("Worktree name or branch name (e.g. 'feature--foo' or 'feature/foo')"),
|
|
112
|
+
force: z
|
|
113
|
+
.boolean()
|
|
114
|
+
.optional()
|
|
115
|
+
.describe("Force removal even if there are uncommitted changes"),
|
|
116
|
+
},
|
|
117
|
+
}, async ({ name, force }) => {
|
|
118
|
+
try {
|
|
119
|
+
const resolved = await resolveWorktreeName(name);
|
|
120
|
+
await remove(resolved, { force });
|
|
121
|
+
return {
|
|
122
|
+
content: [
|
|
123
|
+
{
|
|
124
|
+
type: "text",
|
|
125
|
+
text: `Removed worktree '${resolved}'.`,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: `Failed to remove worktree: ${error instanceof Error ? error.message : String(error)}`,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
isError: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
async function main() {
|
|
143
|
+
const transport = new StdioServerTransport();
|
|
144
|
+
await server.connect(transport);
|
|
145
|
+
}
|
|
146
|
+
main().catch((error) => {
|
|
147
|
+
console.error("Fatal error:", error);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lioneltay/worktree-mcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MCP server for managing git worktrees",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/lioneltay/agent-forge",
|
|
8
|
+
"directory": "packages/claude-plugins/worktree-mcp"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"bin": {
|
|
13
|
+
"worktree-mcp": "dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
".claude-plugin",
|
|
18
|
+
".mcp.json",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"dev": "tsc --watch"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
27
|
+
"@lioneltay/worktree-manager": "workspace:*",
|
|
28
|
+
"zod": "^3.24.2"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.13.1",
|
|
32
|
+
"typescript": "^5.7.3"
|
|
33
|
+
}
|
|
34
|
+
}
|