@ebowwa/tooling-mcp 0.1.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 (3) hide show
  1. package/README.md +58 -0
  2. package/index.ts +222 -0
  3. package/package.json +40 -0
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @ebowwa/tooling-mcp
2
+
3
+ HTTP MCP server for codespaces monorepo management.
4
+
5
+ ## Features
6
+
7
+ - **Monorepo Status**: Get status of all repositories in the monorepo
8
+ - **Repository Discovery**: List and discover all repositories
9
+ - **Git Operations**: Get git status for any repository
10
+ - **Sync Operations**: Sync single or all repositories
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @ebowwa/tooling-mcp
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Start the MCP server (runs on port 8912 by default):
21
+
22
+ ```bash
23
+ # Start with default port
24
+ tooling-mcp
25
+
26
+ # Start with custom port
27
+ MCP_PORT=9000 tooling-mcp
28
+
29
+ # Development mode
30
+ bun run dev
31
+ ```
32
+
33
+ ## API
34
+
35
+ ### Health Check
36
+ ```
37
+ GET http://localhost:8912/health
38
+ ```
39
+
40
+ ### MCP Endpoint
41
+ ```
42
+ POST http://localhost:8912/mcp
43
+ ```
44
+
45
+ Available tools:
46
+ - `status` - Get status of all repos in the monorepo
47
+ - `list_repos` - List all discovered repos
48
+ - `get_repo_path` - Get filesystem path to a repo
49
+ - `sync` - Sync all repos or a specific repo
50
+ - `git_status` - Get detailed git status for a repo
51
+
52
+ ## Dependencies
53
+
54
+ - [`@ebowwa/tooling`](https://www.npmjs.com/package/@ebowwa/tooling) - Core library for monorepo management
55
+
56
+ ## License
57
+
58
+ MIT
package/index.ts ADDED
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * @ebowwa/tooling-mcp
4
+ *
5
+ * HTTP MCP server for codespaces monorepo management.
6
+ * Exposes tooling functionality via Model Context Protocol.
7
+ *
8
+ * Provides tools for:
9
+ * - Monorepo status across all repos
10
+ * - Git operations (status, sync)
11
+ * - Repository discovery and path resolution
12
+ *
13
+ * Runs on HTTP port 8912 by default (set MCP_PORT env var to change)
14
+ */
15
+
16
+ import { StateManager } from "@ebowwa/tooling/src/state.js";
17
+ import { SyncManager } from "@ebowwa/tooling/src/sync.js";
18
+ import { getGitStatus } from "@ebowwa/tooling/src/git.js";
19
+ import type { GitStatus } from "@ebowwa/tooling/src/git.js";
20
+
21
+ const state = new StateManager();
22
+ const sync = new SyncManager(state);
23
+
24
+ // ==============
25
+ // MCP Tools
26
+ //=============
27
+
28
+ /**
29
+ * Get status of all repos in the monorepo
30
+ */
31
+ async function statusTool(): Promise<string> {
32
+ const statuses = await sync.getStatus();
33
+ const lines = [
34
+ "📊 Codespaces Monorepo Status",
35
+ "=" .repeat(40),
36
+ "",
37
+ `Phase: ${state.phase}`,
38
+ `Current repo: ${state.currentRepo || "none"}`,
39
+ "",
40
+ "Repos:",
41
+ ];
42
+
43
+ for (const [repo, status] of Object.entries(statuses)) {
44
+ const dirty = status.dirty ? " ⚠️ dirty" : "";
45
+ const behind = status.commitsBehind ? ` ↓${status.commitsBehind}` : "";
46
+ const ahead = status.commitsAhead ? ` ↑${status.commitsAhead}` : "";
47
+
48
+ lines.push(` ${repo}:`);
49
+ lines.push(` branch: ${status.branch}${dirty}${behind}${ahead}`);
50
+ lines.push(` commit: ${status.lastCommit?.slice(0, 8) || "unknown"}`);
51
+ }
52
+
53
+ return lines.join("\n");
54
+ }
55
+
56
+ /**
57
+ * List all discovered repos
58
+ */
59
+ async function listReposTool(): Promise<string> {
60
+ const lines = [
61
+ "📁 Discovered Repositories",
62
+ "=" .repeat(40),
63
+ "",
64
+ ];
65
+
66
+ for (const [name, remote] of Object.entries(state.remotes)) {
67
+ lines.push(` ${name}:`);
68
+ lines.push(` path: ${remote.path}`);
69
+ lines.push(` url: ${remote.url}`);
70
+ lines.push(` main branch: ${remote.mainBranch}`);
71
+ lines.push("");
72
+ }
73
+
74
+ return lines.join("\n");
75
+ }
76
+
77
+ /**
78
+ * Get the filesystem path to a repo
79
+ */
80
+ async function getRepoPathTool(repoName: string): Promise<string> {
81
+ try {
82
+ const path = state.getRepoPath(repoName);
83
+ return `Path to '${repoName}': ${path}`;
84
+ } catch (error) {
85
+ throw new Error(`Unknown repo: ${repoName}. Available repos: ${Object.keys(state.remotes).join(", ")}`);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Sync all repos or a specific repo
91
+ */
92
+ async function syncTool(repo?: string): Promise<string> {
93
+ try {
94
+ let result;
95
+
96
+ if (repo) {
97
+ result = await sync.syncRepo(repo);
98
+ return `Synced ${repo}: ${result.synced ? "✓ success" : "✗ failed"}${result.error ? ` - ${result.error}` : ""}`;
99
+ } else {
100
+ result = await sync.syncAll();
101
+ const lines = ["🔄 Sync Results", ""];
102
+
103
+ for (const [name, r] of Object.entries(result.repos)) {
104
+ lines.push(` ${r.synced ? "✓" : "✗"} ${name}`);
105
+ }
106
+
107
+ if (result.errors.length > 0) {
108
+ lines.push("", "Errors:");
109
+ result.errors.forEach(e => lines.push(` - ${e}`));
110
+ }
111
+
112
+ return lines.join("\n");
113
+ }
114
+ } catch (error) {
115
+ throw new Error(`Sync failed: ${error}`);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Get detailed git status for a repo
121
+ */
122
+ async function gitStatusTool(repoName: string): Promise<string> {
123
+ try {
124
+ const path = state.getRepoPath(repoName);
125
+ const status = await getGitStatus(path);
126
+
127
+ const lines = [
128
+ `📋 Git Status: ${repoName}`,
129
+ "=" .repeat(40),
130
+ "",
131
+ `Branch: ${status.branch}`,
132
+ `Dirty: ${status.dirty ? "Yes" : "No"}`,
133
+ `Commit: ${status.lastCommit || "unknown"}`,
134
+ ];
135
+
136
+ if (status.commitsBehind) {
137
+ lines.push(`Behind: ${status.commitsBehind} commits`);
138
+ }
139
+
140
+ if (status.commitsAhead) {
141
+ lines.push(`Ahead: ${status.commitsAhead} commits`);
142
+ }
143
+
144
+ return lines.join("\n");
145
+ } catch (error) {
146
+ throw new Error(`Failed to get git status for '${repoName}': ${error}`);
147
+ }
148
+ }
149
+
150
+ // ==============
151
+ // MCP Server
152
+ //=============
153
+
154
+ // Simple SSE-based MCP server
155
+ const MCP_PORT = parseInt(process.env.MCP_PORT || "8912");
156
+
157
+ Bun.serve({
158
+ port: MCP_PORT,
159
+ fetch: async (req) => {
160
+ const url = new URL(req.url);
161
+
162
+ // Health check
163
+ if (url.pathname === "/health") {
164
+ return Response.json({ status: "ok", port: MCP_PORT });
165
+ }
166
+
167
+ // MCP endpoint
168
+ if (url.pathname === "/mcp") {
169
+ if (req.method === "POST") {
170
+ const body = await req.json();
171
+ const { tool, args } = body;
172
+
173
+ try {
174
+ let result;
175
+
176
+ switch (tool) {
177
+ case "status":
178
+ result = await statusTool();
179
+ break;
180
+ case "list_repos":
181
+ result = await listReposTool();
182
+ break;
183
+ case "get_repo_path":
184
+ result = await getRepoPathTool(args?.repo);
185
+ break;
186
+ case "sync":
187
+ result = await syncTool(args?.repo);
188
+ break;
189
+ case "git_status":
190
+ result = await gitStatusTool(args?.repo);
191
+ break;
192
+ default:
193
+ return Response.json({ error: `Unknown tool: ${tool}` }, { status: 400 });
194
+ }
195
+
196
+ return Response.json({ result });
197
+ } catch (error) {
198
+ return Response.json({ error: String(error) }, { status: 500 });
199
+ }
200
+ }
201
+
202
+ // GET returns available tools
203
+ return Response.json({
204
+ name: "tooling-mcp",
205
+ version: "0.1.0",
206
+ tools: [
207
+ { name: "status", description: "Get status of all repos in the monorepo" },
208
+ { name: "list_repos", description: "List all discovered repos" },
209
+ { name: "get_repo_path", description: "Get filesystem path to a repo", args: ["repo"] },
210
+ { name: "sync", description: "Sync all repos or a specific repo", args: ["repo?"] },
211
+ { name: "git_status", description: "Get detailed git status for a repo", args: ["repo"] },
212
+ ]
213
+ });
214
+ }
215
+
216
+ return Response.json({ error: "Not found" }, { status: 404 });
217
+ },
218
+ });
219
+
220
+ console.log(`🚀 Tooling MCP Server running on port ${MCP_PORT}`);
221
+ console.log(` Health: http://localhost:${MCP_PORT}/health`);
222
+ console.log(` MCP: http://localhost:${MCP_PORT}/mcp`);
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@ebowwa/tooling-mcp",
3
+ "version": "0.1.0",
4
+ "description": "HTTP MCP server for codespaces monorepo management - status, sync, and git operations",
5
+ "type": "module",
6
+ "main": "./index.ts",
7
+ "bin": {
8
+ "tooling-mcp": "./index.ts"
9
+ },
10
+ "scripts": {
11
+ "start": "bun run index.ts",
12
+ "dev": "MCP_PORT=8912 bun run index.ts"
13
+ },
14
+ "keywords": [
15
+ "mcp",
16
+ "model-context-protocol",
17
+ "monorepo",
18
+ "git",
19
+ "sync",
20
+ "codespaces",
21
+ "tooling"
22
+ ],
23
+ "author": "Ebowwa Labs <labs@ebowwa.com>",
24
+ "license": "MIT",
25
+ "homepage": "https://github.com/ebowwa/codespaces/tree/main/packages/src/tooling-mcp#readme",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/ebowwa/codespaces.git",
29
+ "directory": "packages/src/tooling-mcp"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/ebowwa/codespaces/issues"
33
+ },
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "dependencies": {
38
+ "@ebowwa/tooling": "^0.3.1"
39
+ }
40
+ }