@foundation0/git 1.2.5 → 1.3.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/mcp/README.md CHANGED
@@ -1,250 +1,266 @@
1
- # mcp
2
-
3
- `@foundation0/git/mcp` exposes `@foundation0/git`'s `GitServiceApi` over an MCP server/client boundary.
4
-
5
- ## Purpose
6
-
7
- - **Server**: exposes each leaf `gitServiceApi` method as an MCP tool.
8
- - **Client**: connects to the server with stdio and calls exposed tools.
9
-
10
- Each tool is addressed by the full API path (`repo.issue.create`, `search.issues`, etc.) and accepts:
11
-
12
- - `args`: array of positional string arguments
13
- - `options`: object for request body/query/header customisation
14
-
15
- Tool results are returned as a compact envelope by default:
16
-
17
- ```json
18
- { "ok": true, "data": { "...": "..." }, "meta": { "status": 200 } }
19
- ```
20
-
21
- If the upstream git API returns `{ ok: false }` or an HTTP status `>= 400`, the MCP tool call is marked as an error (`isError: true`) and a typed error envelope is returned:
22
-
23
- ```json
24
- { "ok": false, "error": { "code": "HTTP_NOT_FOUND", "status": 404, "message": "HTTP 404", "retryable": false }, "meta": { "status": 404 } }
25
- ```
26
-
27
- To include the full debug payload (mapping/request/response headers), pass `format: "debug"` (either top-level or in `options`).
28
-
29
- Use the `batch` tool to run multiple calls in one request. It is the preferred mode when you want to issue multiple MCP calls because it executes them in parallel and returns a combined response.
30
-
31
- Batch payload format:
32
-
33
- ```json
34
- {
35
- "calls": [
36
- { "tool": "repo.issue.create", "options": { "data": { "title": "Bug report", "body": "Details" } } },
37
- { "tool": "repo.contents.view", "args": ["README.md"] }
38
- ],
39
- "continueOnError": false
40
- }
41
- ```
42
-
43
- ## Install
44
-
45
- Global install:
46
-
47
- ```bash
48
- pnpm add -g @foundation0/git
49
- ```
50
-
51
- Run from npm without global install:
52
-
53
- ```bash
54
- npx -y -p @foundation0/git f0-git-mcp --help
55
- ```
56
-
57
- Using pnpm:
58
-
59
- ```bash
60
- pnpm dlx @foundation0/git f0-git-mcp --help
61
- ```
62
-
63
- From the monorepo root:
64
-
65
- ```bash
66
- pnpm -C packages/git test
67
- ```
68
-
69
- ## Server
70
-
71
- ```ts
72
- import { createGitMcpServer } from '@foundation0/git/mcp'
73
-
74
- const server = createGitMcpServer({
75
- config: {
76
- platform: 'GITEA',
77
- giteaHost: 'https://gitea.example.com',
78
- giteaToken: process.env.GITEA_TOKEN,
79
- },
80
- defaultOwner: 'example-org',
81
- defaultRepo: 'example-repo',
82
- })
83
-
84
- await server.run()
85
- ```
86
-
87
- To customize tool names:
88
-
89
- ```ts
90
- createGitMcpServer({ toolsPrefix: 'git' })
91
- ```
92
-
93
- This emits tools like `git.repo.issue.create`.
94
-
95
- ## Client
96
-
97
- ```ts
98
- import { GitMcpClient } from '@foundation0/git/mcp'
99
-
100
- const client = new GitMcpClient()
101
- await client.connect('node', ['path/to/mcp-server-entry.mjs'])
102
-
103
- const tools = await client.listTools()
104
- console.log(tools)
105
-
106
- const result = await client.callByPath('repo.issue.create', [
107
- ], {
108
- data: {
109
- title: 'Bug report',
110
- body: 'Details',
111
- },
112
- })
113
-
114
- console.log(result.isError)
115
- console.log(result.text)
116
- ```
117
-
118
- `callByPath` converts positional args and options for convenience. `call` can also be used with the raw tool arguments object:
119
-
120
- ```ts
121
- await client.call('repo.issue.create', {
122
- options: {
123
- data: { title: 'Issue from MCP' },
124
- },
125
- })
126
- ```
127
-
128
- To return a debug payload for a specific call:
129
-
130
- ```ts
131
- await client.call('repo.issue.list', { format: 'debug' })
132
- ```
133
-
134
- ## Using with MCP-compatible agents
135
-
136
- Create a small server entry file for your agent (for example `packages/git/mcp/server-entry.ts`):
137
-
138
- ```ts
139
- import { runGitMcpServer } from '@foundation0/git/mcp'
140
-
141
- await runGitMcpServer({
142
- config: {
143
- platform: 'GITEA',
144
- giteaHost: process.env.GITEA_HOST ?? 'https://gitea.example.com',
145
- giteaToken: process.env.GITEA_TOKEN,
146
- },
147
- defaultOwner: 'example-org',
148
- defaultRepo: 'example-repo',
149
- toolsPrefix: process.env.MCP_TOOLS_PREFIX ?? undefined,
150
- })
151
- ```
152
-
153
- Then point any MCP client at this file with stdio transport.
154
-
155
- You can also invoke the packaged CLI directly:
156
-
157
- ```bash
158
- f0-git-mcp --tools-prefix git
159
- ```
160
-
161
- Claude Desktop:
162
-
163
- ```json
164
- {
165
- "mcpServers": {
166
- "workspace-git": {
167
- "command": "bun",
168
- "args": ["run", "/abs/path/packages/git/mcp/server-entry.ts"],
169
- "env": {
170
- "GITEA_HOST": "https://gitea.example.com",
171
- "GITEA_TOKEN": "YOUR_TOKEN",
172
- "MCP_TOOLS_PREFIX": "git"
173
- }
174
- }
175
- }
176
- }
177
- ```
178
-
179
- Cursor:
180
-
181
- ```json
182
- {
183
- "mcpServers": {
184
- "workspace-git": {
185
- "command": "bun",
186
- "args": ["run", "/abs/path/packages/git/mcp/server-entry.ts"],
187
- "env": {
188
- "GITEA_HOST": "https://gitea.example.com",
189
- "GITEA_TOKEN": "YOUR_TOKEN"
190
- }
191
- }
192
- }
193
- }
194
- ```
195
-
196
- Windsurf / any MCP-compatible client using the same schema:
197
-
198
- ```json
199
- {
200
- "mcpServers": {
201
- "workspace-git": {
202
- "command": "bun",
203
- "args": ["run", "/abs/path/packages/git/mcp/server-entry.ts"],
204
- "env": {
205
- "GITEA_HOST": "https://gitea.example.com",
206
- "GITEA_TOKEN": "YOUR_TOKEN"
207
- }
208
- }
209
- }
210
- }
211
- ```
212
-
213
- Tips:
214
-
215
- - Use a tool-name prefix (for example `git`) to avoid collisions with other MCP servers.
216
- - For Node-only agents, transpile/bundle this package first and point `command` to the compiled JS entrypoint.
217
-
218
- ## Security notes
219
-
220
- - Prefer `https://` for `GITEA_HOST` to avoid leaking `GITEA_TOKEN` over plaintext HTTP.
221
- - The CLI entrypoint in `packages/git/mcp/src/cli.ts` rejects `http://` hosts unless `--allow-insecure-http` is passed for local testing.
222
- - Set `GITEA_TOKEN` in the MCP process environment. The package does not auto-load `.env` files.
223
-
224
- ## API surface
225
-
226
- - `createGitMcpServer(options)` / `runGitMcpServer(options)`
227
- - `GitMcpClient`
228
- - `createGitMcpClient(options)`
229
- - `normalizeToolCallNameForServer(prefix, toolName)`
230
- - Includes label-management tools exposed from the API object:
231
- `repo.label.listManaged`, `repo.label.getByName`, `repo.label.upsert`, and `repo.label.deleteByName`
232
- - Includes Gitea Actions convenience tools exposed from the API object (best-effort helpers):
233
- `repo.actions.tasks.list`, `repo.actions.jobs.logs`, `repo.actions.jobs.logsTail`, `repo.actions.jobs.logsForRunTail`,
234
- and `repo.actions.artifacts.downloadZipUrl`
235
- - Includes a discovery helper:
236
- `help.actionsLogs` (also available under `repo.help.actionsLogs`)
237
-
238
- ## Notes: jobs.logsForRunTail
239
-
240
- `repo.actions.jobs.logsForRunTail` (and the `help.*` variants) expect a `headSha` and a `runNumber`. Prefer the named form to avoid positional confusion:
241
-
242
- ```json
243
- {
244
- "owner": "F0",
245
- "repo": "adl",
246
- "headSha": "6dd4062ec271277e7e83ac6864fec9208559564c",
247
- "runNumber": 11,
248
- "maxLines": 250
249
- }
250
- ```
1
+ # mcp
2
+
3
+ `@foundation0/git/mcp` exposes `@foundation0/git`'s `GitServiceApi` over an MCP server/client boundary.
4
+
5
+ ## Purpose
6
+
7
+ - **Server**: exposes each leaf `gitServiceApi` method as an MCP tool.
8
+ - **Client**: connects to the server with stdio and calls exposed tools.
9
+
10
+ Each tool is addressed by the full API path (`repo.issue.create`, `search.issues`, etc.) and accepts:
11
+
12
+ - `args`: array of positional string arguments
13
+ - `options`: object for request body/query/header customisation
14
+
15
+ Tool results are returned as a compact envelope by default:
16
+
17
+ ```json
18
+ { "ok": true, "data": { "...": "..." }, "meta": { "status": 200 } }
19
+ ```
20
+
21
+ If the upstream git API returns `{ ok: false }` or an HTTP status `>= 400`, the MCP tool call is marked as an error (`isError: true`) and a typed error envelope is returned:
22
+
23
+ ```json
24
+ { "ok": false, "error": { "code": "HTTP_NOT_FOUND", "status": 404, "message": "HTTP 404", "retryable": false }, "meta": { "status": 404 } }
25
+ ```
26
+
27
+ To include the full debug payload (mapping/request/response headers), pass `format: "debug"` (either top-level or in `options`).
28
+
29
+ Use the `batch` tool to run multiple calls in one request. It is the preferred mode when you want to issue multiple MCP calls because it executes them in parallel and returns a combined response.
30
+
31
+ Batch payload format:
32
+
33
+ ```json
34
+ {
35
+ "calls": [
36
+ { "tool": "repo.issue.create", "options": { "data": { "title": "Bug report", "body": "Details" } } },
37
+ { "tool": "repo.contents.view", "args": ["README.md"] }
38
+ ],
39
+ "continueOnError": false
40
+ }
41
+ ```
42
+
43
+ ## Install
44
+
45
+ Global install:
46
+
47
+ ```bash
48
+ pnpm add -g @foundation0/git
49
+ ```
50
+
51
+ Run from npm without global install:
52
+
53
+ ```bash
54
+ npx -y -p @foundation0/git f0-git-mcp --help
55
+ ```
56
+
57
+ Using pnpm:
58
+
59
+ ```bash
60
+ pnpm dlx @foundation0/git f0-git-mcp --help
61
+ ```
62
+
63
+ From the monorepo root:
64
+
65
+ ```bash
66
+ pnpm -C packages/git test
67
+ ```
68
+
69
+ ## Server
70
+
71
+ ```ts
72
+ import { createGitMcpServer } from '@foundation0/git/mcp'
73
+
74
+ const server = createGitMcpServer({
75
+ config: {
76
+ platform: 'GITEA',
77
+ giteaHost: 'https://gitea.example.com',
78
+ giteaToken: process.env.GITEA_TOKEN,
79
+ },
80
+ defaultOwner: 'example-org',
81
+ defaultRepo: 'example-repo',
82
+ })
83
+
84
+ await server.run()
85
+ ```
86
+
87
+ To customize tool names:
88
+
89
+ ```ts
90
+ createGitMcpServer({ toolsPrefix: 'git' })
91
+ ```
92
+
93
+ This emits tools like `git.repo.issue.create`.
94
+
95
+ ## Client
96
+
97
+ ```ts
98
+ import { GitMcpClient } from '@foundation0/git/mcp'
99
+
100
+ const client = new GitMcpClient()
101
+ await client.connect('node', ['path/to/mcp-server-entry.mjs'])
102
+
103
+ const tools = await client.listTools()
104
+ console.log(tools)
105
+
106
+ const result = await client.callByPath('repo.issue.create', [
107
+ ], {
108
+ data: {
109
+ title: 'Bug report',
110
+ body: 'Details',
111
+ },
112
+ })
113
+
114
+ console.log(result.isError)
115
+ console.log(result.text)
116
+ ```
117
+
118
+ `callByPath` converts positional args and options for convenience. `call` can also be used with the raw tool arguments object:
119
+
120
+ ```ts
121
+ await client.call('repo.issue.create', {
122
+ options: {
123
+ data: { title: 'Issue from MCP' },
124
+ },
125
+ })
126
+ ```
127
+
128
+ To return a debug payload for a specific call:
129
+
130
+ ```ts
131
+ await client.call('repo.issue.list', { format: 'debug' })
132
+ ```
133
+
134
+ ## Using with MCP-compatible agents
135
+
136
+ Create a small server entry file for your agent (for example `packages/git/mcp/server-entry.ts`):
137
+
138
+ ```ts
139
+ import { runGitMcpServer } from '@foundation0/git/mcp'
140
+
141
+ await runGitMcpServer({
142
+ config: {
143
+ platform: 'GITEA',
144
+ giteaHost: process.env.GITEA_HOST ?? 'https://gitea.example.com',
145
+ giteaToken: process.env.GITEA_TOKEN,
146
+ },
147
+ defaultOwner: 'example-org',
148
+ defaultRepo: 'example-repo',
149
+ toolsPrefix: process.env.MCP_TOOLS_PREFIX ?? undefined,
150
+ })
151
+ ```
152
+
153
+ Then point any MCP client at this file with stdio transport.
154
+
155
+ You can also invoke the packaged CLI directly:
156
+
157
+ ```bash
158
+ f0-git-mcp --tools-prefix git
159
+ ```
160
+
161
+ Claude Desktop:
162
+
163
+ ```json
164
+ {
165
+ "mcpServers": {
166
+ "workspace-git": {
167
+ "command": "bun",
168
+ "args": ["run", "/abs/path/packages/git/mcp/server-entry.ts"],
169
+ "env": {
170
+ "GITEA_HOST": "https://gitea.example.com",
171
+ "GITEA_TOKEN": "YOUR_TOKEN",
172
+ "MCP_TOOLS_PREFIX": "git"
173
+ }
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ Cursor:
180
+
181
+ ```json
182
+ {
183
+ "mcpServers": {
184
+ "workspace-git": {
185
+ "command": "bun",
186
+ "args": ["run", "/abs/path/packages/git/mcp/server-entry.ts"],
187
+ "env": {
188
+ "GITEA_HOST": "https://gitea.example.com",
189
+ "GITEA_TOKEN": "YOUR_TOKEN"
190
+ }
191
+ }
192
+ }
193
+ }
194
+ ```
195
+
196
+ Windsurf / any MCP-compatible client using the same schema:
197
+
198
+ ```json
199
+ {
200
+ "mcpServers": {
201
+ "workspace-git": {
202
+ "command": "bun",
203
+ "args": ["run", "/abs/path/packages/git/mcp/server-entry.ts"],
204
+ "env": {
205
+ "GITEA_HOST": "https://gitea.example.com",
206
+ "GITEA_TOKEN": "YOUR_TOKEN"
207
+ }
208
+ }
209
+ }
210
+ }
211
+ ```
212
+
213
+ Tips:
214
+
215
+ - Use a tool-name prefix (for example `git`) to avoid collisions with other MCP servers.
216
+ - For Node-only agents, transpile/bundle this package first and point `command` to the compiled JS entrypoint.
217
+
218
+ ## Security notes
219
+
220
+ - Prefer `https://` for `GITEA_HOST` to avoid leaking `GITEA_TOKEN` over plaintext HTTP.
221
+ - The CLI entrypoint in `packages/git/mcp/src/cli.ts` rejects `http://` hosts unless `--allow-insecure-http` is passed for local testing.
222
+ - Set `GITEA_TOKEN` in the MCP process environment. The package does not auto-load `.env` files.
223
+
224
+ ## API surface
225
+
226
+ - `createGitMcpServer(options)` / `runGitMcpServer(options)`
227
+ - `GitMcpClient`
228
+ - `createGitMcpClient(options)`
229
+ - `normalizeToolCallNameForServer(prefix, toolName)`
230
+ - Includes label-management tools exposed from the API object:
231
+ `repo.label.listManaged`, `repo.label.getByName`, `repo.label.upsert`, and `repo.label.deleteByName`
232
+ - Includes Gitea Actions convenience tools exposed from the API object (best-effort helpers):
233
+ `repo.actions.tasks.list`, `repo.actions.jobs.logs`, `repo.actions.jobs.logsTail`, `repo.actions.jobs.logsForRunTail`,
234
+ `repo.actions.diagnoseLatestFailure`, and `repo.actions.artifacts.downloadZipUrl`
235
+ - Includes a discovery helper:
236
+ `help.actionsLogs` (also available under `repo.help.actionsLogs`)
237
+
238
+ ## Notes: jobs.logsForRunTail
239
+
240
+ `repo.actions.jobs.logsForRunTail` (and the `help.*` variants) expect a `headSha` and a `runNumber`. Prefer the named form to avoid positional confusion:
241
+
242
+ ```json
243
+ {
244
+ "owner": "F0",
245
+ "repo": "adl",
246
+ "headSha": "6dd4062ec271277e7e83ac6864fec9208559564c",
247
+ "runNumber": 11,
248
+ "maxLines": 250
249
+ }
250
+ ```
251
+
252
+ ## Notes: actions.diagnoseLatestFailure
253
+
254
+ `repo.actions.diagnoseLatestFailure` is a convenience tool that finds the most recent failing Actions task and returns a log tail.
255
+
256
+ ```json
257
+ { "owner": "F0", "repo": "adl", "workflowName": "TypeScript", "maxLines": 250 }
258
+ ```
259
+
260
+ ## Notes: actions.runs.artifacts
261
+
262
+ `repo.actions.runs.artifacts` accepts a named form to avoid legacy positional ordering:
263
+
264
+ ```json
265
+ { "owner": "F0", "repo": "adl", "runId": 11 }
266
+ ```
package/mcp/cli.mjs CHANGED
@@ -1,37 +1,37 @@
1
- #!/usr/bin/env node
2
- import { spawnSync } from 'node:child_process'
3
- import path from 'node:path'
4
- import { fileURLToPath } from 'node:url'
5
-
6
- const cliPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'src', 'cli.ts')
7
-
8
- try {
9
- const result = spawnSync(
10
- 'bun',
11
- ['run', cliPath, ...process.argv.slice(2)],
12
- {
13
- stdio: 'inherit',
14
- },
15
- )
16
-
17
- if (result.error) {
18
- throw result.error
19
- }
20
-
21
- const code = result.status
22
- if (typeof code === 'number') {
23
- process.exit(code)
24
- }
25
-
26
- process.exit(0)
27
- } catch (error) {
28
- const typedError = error instanceof Object ? error : null
29
- const errno = typedError && 'code' in typedError ? typedError.code : undefined
30
- if (errno === 'ENOENT') {
31
- console.error('Bun is required to run f0-git-mcp. Install it and retry: https://bun.sh/docs/installation')
32
- process.exit(1)
33
- }
34
-
35
- console.error('Failed to launch Git MCP server', error)
36
- process.exit(1)
37
- }
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process'
3
+ import path from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+
6
+ const cliPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'src', 'cli.ts')
7
+
8
+ try {
9
+ const result = spawnSync(
10
+ 'bun',
11
+ ['run', cliPath, ...process.argv.slice(2)],
12
+ {
13
+ stdio: 'inherit',
14
+ },
15
+ )
16
+
17
+ if (result.error) {
18
+ throw result.error
19
+ }
20
+
21
+ const code = result.status
22
+ if (typeof code === 'number') {
23
+ process.exit(code)
24
+ }
25
+
26
+ process.exit(0)
27
+ } catch (error) {
28
+ const typedError = error instanceof Object ? error : null
29
+ const errno = typedError && 'code' in typedError ? typedError.code : undefined
30
+ if (errno === 'ENOENT') {
31
+ console.error('Bun is required to run f0-git-mcp. Install it and retry: https://bun.sh/docs/installation')
32
+ process.exit(1)
33
+ }
34
+
35
+ console.error('Failed to launch Git MCP server', error)
36
+ process.exit(1)
37
+ }