@foundation0/git 1.3.0 → 1.3.2

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