@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.
@@ -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
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "worktree": {
4
+ "command": "npx",
5
+ "args": ["-y", "@lioneltay/worktree-mcp"]
6
+ }
7
+ }
8
+ }
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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
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
+ }