@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.
- package/README.md +58 -0
- package/index.ts +222 -0
- 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
|
+
}
|