🛟 Updated.
e15c2eb parent: 2956be7 modified
.tickets/issues/0004-do-we-need-mcp.yaml +2 -2 | @@ -6,9 +6,9 @@ author: | ||
| 6 | 6 | name: k33g |
| 7 | 7 | email: ph.charriere@gmail.com |
| 8 | 8 | createdAt: 2026-09-15T22:38:45.129Z |
| 9 | -updatedAt: 2026-09-23T11:49:34.861Z | |
| 9 | +updatedAt: 2026-09-23T12:54:19.186Z | |
| 10 | 10 | labels: [] |
| 11 | -milestone: v0.1.2 | |
| 11 | +milestone: v0.1.3 | |
| 12 | 12 | body: | |
| 13 | 13 | I would say no - use a CLI |
| 14 | 14 | |
| @@ -6,9 +6,9 @@ author: | |||
| 6 | name: k33g | 6 | name: k33g |
| 7 | email: ph.charriere@gmail.com | 7 | email: ph.charriere@gmail.com |
| 8 | createdAt: 2026-09-15T22:38:45.129Z | 8 | createdAt: 2026-09-15T22:38:45.129Z |
| 9 | -updatedAt: 2026-09-23T11:49:34.861Z | 9 | +updatedAt: 2026-09-23T12:54:19.186Z |
| 10 | labels: [] | 10 | labels: [] |
| 11 | -milestone: v0.1.2 | 11 | +milestone: v0.1.3 |
| 12 | body: | | 12 | body: | |
| 13 | I would say no - use a CLI | 13 | I would say no - use a CLI |
| 14 | 14 | ||
modified
.tickets/issues/0039-better-template-and-kit.yaml +3 -3 | @@ -1,15 +1,15 @@ | ||
| 1 | 1 | # Managed by IssueSpec. Hand edits are welcome; keep the schema valid. |
| 2 | 2 | id: 39 |
| 3 | 3 | title: Better template and kit |
| 4 | -state: open | |
| 4 | +state: closed | |
| 5 | 5 | author: |
| 6 | 6 | name: k33g |
| 7 | 7 | email: ph.charriere@gmail.com |
| 8 | 8 | createdAt: 2026-09-23T04:24:55.322Z |
| 9 | -updatedAt: 2026-09-23T05:53:09.733Z | |
| 9 | +updatedAt: 2026-09-23T12:54:26.619Z | |
| 10 | 10 | labels: |
| 11 | 11 | - priority::urgent |
| 12 | -milestone: v0.1.2 | |
| 12 | +milestone: v0.1.3 | |
| 13 | 13 | body: | |
| 14 | 14 | Add go tool chain, node js ... |
| 15 | 15 | tasks: [] |
| @@ -1,15 +1,15 @@ | |||
| 1 | # Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | 1 | # Managed by IssueSpec. Hand edits are welcome; keep the schema valid. |
| 2 | id: 39 | 2 | id: 39 |
| 3 | title: Better template and kit | 3 | title: Better template and kit |
| 4 | -state: open | 4 | +state: closed |
| 5 | author: | 5 | author: |
| 6 | name: k33g | 6 | name: k33g |
| 7 | email: ph.charriere@gmail.com | 7 | email: ph.charriere@gmail.com |
| 8 | createdAt: 2026-09-23T04:24:55.322Z | 8 | createdAt: 2026-09-23T04:24:55.322Z |
| 9 | -updatedAt: 2026-09-23T05:53:09.733Z | 9 | +updatedAt: 2026-09-23T12:54:26.619Z |
| 10 | labels: | 10 | labels: |
| 11 | - priority::urgent | 11 | - priority::urgent |
| 12 | -milestone: v0.1.2 | 12 | +milestone: v0.1.3 |
| 13 | body: | | 13 | body: | |
| 14 | Add go tool chain, node js ... | 14 | Add go tool chain, node js ... |
| 15 | tasks: [] | 15 | tasks: [] |
added
.tickets/issues/0041-mcp-github-server-in-sbx.yaml +29 -0 | new file mode 100644 | ||
| @@ -0,0 +1,29 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 41 | |
| 3 | +title: mcp github server in sbx | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-09-23T12:42:15.495Z | |
| 9 | +updatedAt: 2026-09-23T12:43:10.686Z | |
| 10 | +labels: [] | |
| 11 | +body: | | |
| 12 | + [warning: mcp server github: discovery failed: no valid JSON-RPC response found in SSE stream] | |
| 13 | +tasks: | |
| 14 | + - id: 1 | |
| 15 | + title: Try with sbx mcp add | |
| 16 | + state: open | |
| 17 | + priority: urgent | |
| 18 | + author: | |
| 19 | + name: k33g | |
| 20 | + email: ph.charriere@gmail.com | |
| 21 | + createdAt: 2026-09-23T12:43:10.683Z | |
| 22 | +comments: | |
| 23 | + - id: 1 | |
| 24 | + author: | |
| 25 | + name: k33g | |
| 26 | + email: ph.charriere@gmail.com | |
| 27 | + createdAt: 2026-09-23T12:43:02.879Z | |
| 28 | + body: | | |
| 29 | + Try with sbx mcp add | |
| new file mode 100644 | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 41 | ||
| 3 | +title: mcp github server in sbx | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-09-23T12:42:15.495Z | ||
| 9 | +updatedAt: 2026-09-23T12:43:10.686Z | ||
| 10 | +labels: [] | ||
| 11 | +body: | | ||
| 12 | + [warning: mcp server github: discovery failed: no valid JSON-RPC response found in SSE stream] | ||
| 13 | +tasks: | ||
| 14 | + - id: 1 | ||
| 15 | + title: Try with sbx mcp add | ||
| 16 | + state: open | ||
| 17 | + priority: urgent | ||
| 18 | + author: | ||
| 19 | + name: k33g | ||
| 20 | + email: ph.charriere@gmail.com | ||
| 21 | + createdAt: 2026-09-23T12:43:10.683Z | ||
| 22 | +comments: | ||
| 23 | + - id: 1 | ||
| 24 | + author: | ||
| 25 | + name: k33g | ||
| 26 | + email: ph.charriere@gmail.com | ||
| 27 | + createdAt: 2026-09-23T12:43:02.879Z | ||
| 28 | + body: | | ||
| 29 | + Try with sbx mcp add | ||
added
.tickets/issues/0042-implement-oauth-authentication-for-mcp-servers.yaml +14 -0 | new file mode 100644 | ||
| @@ -0,0 +1,14 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 42 | |
| 3 | +title: Implement OAuth authentication for MCP servers | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-09-23T12:53:36.051Z | |
| 9 | +updatedAt: 2026-09-23T12:53:54.048Z | |
| 10 | +labels: | |
| 11 | + - priority::urgent | |
| 12 | +body: "" | |
| 13 | +tasks: [] | |
| 14 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 42 | ||
| 3 | +title: Implement OAuth authentication for MCP servers | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-09-23T12:53:36.051Z | ||
| 9 | +updatedAt: 2026-09-23T12:53:54.048Z | ||
| 10 | +labels: | ||
| 11 | + - priority::urgent | ||
| 12 | +body: "" | ||
| 13 | +tasks: [] | ||
| 14 | +comments: [] | ||
modified
.tickets/milestones.yaml +2 -2 | @@ -9,6 +9,6 @@ milestones: | ||
| 9 | 9 | - title: v0.1.1 |
| 10 | 10 | state: closed |
| 11 | 11 | dueOn: 2026-10-15 |
| 12 | - - title: v0.1.2 | |
| 13 | - state: open | |
| 12 | + - title: v0.1.3 | |
| 13 | + state: closed | |
| 14 | 14 | dueOn: 2026-10-30 |
| @@ -9,6 +9,6 @@ milestones: | |||
| 9 | - title: v0.1.1 | 9 | - title: v0.1.1 |
| 10 | state: closed | 10 | state: closed |
| 11 | dueOn: 2026-10-15 | 11 | dueOn: 2026-10-15 |
| 12 | - - title: v0.1.2 | 12 | + - title: v0.1.3 |
| 13 | - state: open | 13 | + state: closed |
| 14 | dueOn: 2026-10-30 | 14 | dueOn: 2026-10-30 |
added
demo/MCP-GITHUB.md +42 -0 | new file mode 100644 | ||
| @@ -0,0 +1,42 @@ | ||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | +```bash | |
| 5 | +# 1. Installer GitHub CLI | |
| 6 | +brew install gh # macOS | |
| 7 | +sudo apt install gh # Ubuntu/Debian | |
| 8 | +sudo dnf install gh # Fedora | |
| 9 | + | |
| 10 | +# 2. Se connecter (crée automatiquement un token) | |
| 11 | +gh auth login | |
| 12 | + | |
| 13 | +# 3. Récupérer le token | |
| 14 | +gh auth status --show-token | |
| 15 | +# ou | |
| 16 | +cat ~/.config/gh/hosts.yml | grep -oP 'token: \K.*' | |
| 17 | +``` | |
| 18 | + | |
| 19 | + | |
| 20 | +# Vérifier que le token fonctionne | |
| 21 | +curl -H "Authorization: Bearer $GITHUB_OAUTH_TOKEN" \ | |
| 22 | + https://api.github.com/user | |
| 23 | + | |
| 24 | +mm --tui agent.dmr.github-oauth.yaml | |
| 25 | + | |
| 26 | +Pour mini-me dans sbx : Utilisez sbx mcp add github + sbx mcp auth github (méthode recommandée) Pour mini-me hors sbx : export GITHUB_OAUTH_TOKEN=ghp_xxx | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | +# 2. Register MCP servers: sbx mcp add github --url https://mcp.github.com/mcp | |
| 31 | +# 3. Authenticate: sbx mcp auth github | |
| 32 | +# 4. Start sandbox: sbx run mm --name my-session --config agent.dmr.sbx.yaml | |
| 33 | +# | |
| 34 | + | |
| 35 | +sbx mcp add github --url https://mcp.github.com/mcp | |
| 36 | +sbx mcp auth github | |
| 37 | +sbx mcp auth status --all | |
| 38 | +sbx mcp inspect github | |
| 39 | + | |
| 40 | +sbx run k33g/mm-tui:0.1.4 . --name mm-mcp-github -- agent.dmr.sbx.yaml --static-mcp github | |
| 41 | + | |
| 42 | +sbx mcp load github --sandbox mm-mcp-github | |
| \ No newline at end of file | ||
| new file mode 100644 | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +```bash | ||
| 5 | +# 1. Installer GitHub CLI | ||
| 6 | +brew install gh # macOS | ||
| 7 | +sudo apt install gh # Ubuntu/Debian | ||
| 8 | +sudo dnf install gh # Fedora | ||
| 9 | + | ||
| 10 | +# 2. Se connecter (crée automatiquement un token) | ||
| 11 | +gh auth login | ||
| 12 | + | ||
| 13 | +# 3. Récupérer le token | ||
| 14 | +gh auth status --show-token | ||
| 15 | +# ou | ||
| 16 | +cat ~/.config/gh/hosts.yml | grep -oP 'token: \K.*' | ||
| 17 | +``` | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +# Vérifier que le token fonctionne | ||
| 21 | +curl -H "Authorization: Bearer $GITHUB_OAUTH_TOKEN" \ | ||
| 22 | + https://api.github.com/user | ||
| 23 | + | ||
| 24 | +mm --tui agent.dmr.github-oauth.yaml | ||
| 25 | + | ||
| 26 | +Pour mini-me dans sbx : Utilisez sbx mcp add github + sbx mcp auth github (méthode recommandée) Pour mini-me hors sbx : export GITHUB_OAUTH_TOKEN=ghp_xxx | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +# 2. Register MCP servers: sbx mcp add github --url https://mcp.github.com/mcp | ||
| 31 | +# 3. Authenticate: sbx mcp auth github | ||
| 32 | +# 4. Start sandbox: sbx run mm --name my-session --config agent.dmr.sbx.yaml | ||
| 33 | +# | ||
| 34 | + | ||
| 35 | +sbx mcp add github --url https://mcp.github.com/mcp | ||
| 36 | +sbx mcp auth github | ||
| 37 | +sbx mcp auth status --all | ||
| 38 | +sbx mcp inspect github | ||
| 39 | + | ||
| 40 | +sbx run k33g/mm-tui:0.1.4 . --name mm-mcp-github -- agent.dmr.sbx.yaml --static-mcp github | ||
| 41 | + | ||
| 42 | +sbx mcp load github --sandbox mm-mcp-github | ||
| \ No newline at end of file | \ No newline at end of file | ||
added
demo/agent.dmr.github-oauth.yaml +163 -0 | new file mode 100644 | ||
| @@ -0,0 +1,163 @@ | ||
| 1 | +# Agent settings for GitHub OAuth2 authentication with MCP | |
| 2 | +# This configuration demonstrates OAuth2 authentication for the GitHub MCP server. | |
| 3 | +# | |
| 4 | +# To use this config with OAuth2: | |
| 5 | +# 1. Obtain a GitHub OAuth2 token with appropriate scopes | |
| 6 | +# (e.g., repo, read:org, project, etc.) | |
| 7 | +# 2. Set the token in your environment: export GITHUB_OAUTH_TOKEN=your_token | |
| 8 | +# 3. Run: mm --config agent.dmr.github-oauth.yaml | |
| 9 | +# | |
| 10 | +# Note: OAuth2 tokens can be obtained from: | |
| 11 | +# - GitHub CLI: gh auth login (creates token in ~/.config/gh/hosts.yml) | |
| 12 | +# - Manual creation: https://github.com/settings/tokens | |
| 13 | +# - OAuth2 flow: requires client ID and secret (advanced) | |
| 14 | +# | |
| 15 | +# For production use with sbx, use agent.dmr.sbx.yaml instead. | |
| 16 | + | |
| 17 | +provider: dmr | |
| 18 | + | |
| 19 | +model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M | |
| 20 | +baseUrl: http://localhost:12434/engines/v1 | |
| 21 | +fallback: http://host.docker.internal:12434/engines/v1 | |
| 22 | + | |
| 23 | +contextWindow: 0 | |
| 24 | +maxOutput: 16000 | |
| 25 | +maxTurns: 40 | |
| 26 | + | |
| 27 | +bashTool: true | |
| 28 | +editTools: true | |
| 29 | +planTool: true | |
| 30 | + | |
| 31 | +lsp: | |
| 32 | + servers: | |
| 33 | + - name: gopls | |
| 34 | + command: gopls | |
| 35 | + extensions: [".go"] | |
| 36 | + | |
| 37 | +skillsDir: skills | |
| 38 | + | |
| 39 | +sessions: | |
| 40 | + enabled: true | |
| 41 | + dir: .mm/sessions | |
| 42 | + | |
| 43 | +previewLines: 20 | |
| 44 | + | |
| 45 | +system: | | |
| 46 | + Your name is Bob The Bot. | |
| 47 | + You are a coding agent working in a terminal. | |
| 48 | + You have a "bash" tool to run shell commands. | |
| 49 | + Use it to explore files, run tests, inspect the repository, etc. | |
| 50 | + Chain several commands if needed, then answer clearly in English. | |
| 51 | + | |
| 52 | + A request often mixes things you answer from yourself ("say hello") with | |
| 53 | + things only a command can answer ("list the files"). Handle every part, in | |
| 54 | + the order asked, and run a command for each part that needs one. | |
| 55 | + Never state the contents of a file, the output of a command, or the state of | |
| 56 | + the repository unless a command in THIS answer returned it. What you did not | |
| 57 | + read, you do not know: run the command instead of recalling it. | |
| 58 | + | |
| 59 | + SKILLS | |
| 60 | + You have a second tool, `read_skill`. Its description lists the procedures | |
| 61 | + available for this project — one per kind of task. | |
| 62 | + | |
| 63 | + Any request to DO something to a Go project is a skill, not a shell command | |
| 64 | + you invent. Match the request against that list, call `read_skill` FIRST, | |
| 65 | + before any bash command, and then follow what it says step by step. | |
| 66 | + | |
| 67 | + FILE EDITING | |
| 68 | + You have three tools for files: `read_file`, `edit_file` and `write_file`. | |
| 69 | + They are how a file gets read and changed here: each change is exact, | |
| 70 | + checked before it is written, and comes back as a diff with line numbers. | |
| 71 | + bash is for running things — building, testing, listing, searching. | |
| 72 | + | |
| 73 | + - Read before you write: call `read_file` on the file (numbered=true when | |
| 74 | + you need line numbers). You cannot target text you have not seen; never | |
| 75 | + rely on what you think you remember about a file. | |
| 76 | + - To change an existing file, call `edit_file` with one or more {old, new} | |
| 77 | + pairs. `old` is copied from the file character for character — same | |
| 78 | + spaces, same indentation, same line breaks — and appears exactly once: | |
| 79 | + add the surrounding lines until it is unique. Several pairs are applied | |
| 80 | + together, against the original file. An empty `new` deletes the text. | |
| 81 | + - Call `write_file` only to create a file, or to rewrite one entirely and | |
| 82 | + on purpose. On an existing file it replaces everything, including what | |
| 83 | + you did not intend to touch. | |
| 84 | + - Read the diff the tool returns: it says exactly what changed and on which | |
| 85 | + line. If `edit_file` refuses — text not found, ambiguous, overlapping | |
| 86 | + edits — read the file again and fix `old`. Do not fall back to | |
| 87 | + `write_file` to force the change through. | |
| 88 | + - After editing code, run the narrowest check with bash: the formatter, the | |
| 89 | + compiler, or the test covering that file. | |
| 90 | + | |
| 91 | + RULES | |
| 92 | + - Keep everything the file already does, unless the user asked to remove it. | |
| 93 | + - Touch only the files the request is about. Do not add tests, files or | |
| 94 | + features that were not asked for. | |
| 95 | + - Never run a git command unless the user says git, commit or push. | |
| 96 | + - Never move, rename or delete a file unless the user asked for it. | |
| 97 | + - Then answer in English, in a few lines. | |
| 98 | + - If you don't know how to use a <cli>, run `<cli> --help` (or `<cli> help`) | |
| 99 | + to understand the options, then run the command. | |
| 100 | + | |
| 101 | + BACKGROUND JOBS | |
| 102 | + Never let a command block the answer. Anything that serves, watches or runs | |
| 103 | + long goes to the background, with BOTH streams redirected and its pid kept: | |
| 104 | + | |
| 105 | + nohup <command> > /tmp/<job>.log 2>&1 & echo $! > /tmp/<job>.pid | |
| 106 | + | |
| 107 | + Redirecting only stdout still blocks until the process exits. Read the | |
| 108 | + `bg-jobs` skill before you wait on, inspect or stop such a job — each has a | |
| 109 | + rule you cannot guess. Stop every job you started before you finish, and say | |
| 110 | + which ones you left running. | |
| 111 | + | |
| 112 | +sampling: | |
| 113 | + temperature: 0.0 | |
| 114 | + parallel_tool_calls: false | |
| 115 | + top_p: 0.9 | |
| 116 | + max_tokens: 4096 | |
| 117 | + | |
| 118 | +watchdogTimeout: 30s | |
| 119 | + | |
| 120 | +context: | |
| 121 | + enabled: false | |
| 122 | + threshold: 75 | |
| 123 | + maxMessages: 80 | |
| 124 | + keepLastTurns: 3 | |
| 125 | + summaryMaxTokens: 1200 | |
| 126 | + showStats: true | |
| 127 | + | |
| 128 | +displayCommands: true | |
| 129 | +showThinking: true | |
| 130 | + | |
| 131 | +# MCP Configuration with OAuth2 authentication for GitHub | |
| 132 | +# This shows how to use OAuth2 tokens with the GitHub MCP server. | |
| 133 | +# Note: The official GitHub MCP server (https://api.githubcopilot.com/mcp/) | |
| 134 | +# uses bearer token authentication, so OAuth2 type works the same as bearer. | |
| 135 | +# The distinction is for servers that require OAuth2 client credentials. | |
| 136 | +mcp: | |
| 137 | + enabled: true | |
| 138 | + permissions: ask | |
| 139 | + discoveryTimeout: 10s | |
| 140 | + | |
| 141 | + servers: | |
| 142 | + # GitHub MCP Server with OAuth2 token | |
| 143 | + # Token can be obtained from GitHub CLI or manual creation | |
| 144 | + - name: github | |
| 145 | + transport: http | |
| 146 | + url: https://api.githubcopilot.com/mcp/ | |
| 147 | + timeout: 30s | |
| 148 | + auth: | |
| 149 | + type: oauth2 # or "bearer" - both work for GitHub's token auth | |
| 150 | + tokenEnv: GITHUB_OAUTH_TOKEN # Set this in your environment | |
| 151 | + headers: | |
| 152 | + Accept: "application/json, text/event-stream" | |
| 153 | + # Optional: filter to only expose specific tools | |
| 154 | + tools: | |
| 155 | + - get_me | |
| 156 | + - list_repositories | |
| 157 | + - list_issues | |
| 158 | + - list_pull_requests | |
| 159 | + - get_file_contents | |
| 160 | + - search_code | |
| 161 | + - search_repositories | |
| 162 | + - create_pull_request | |
| 163 | + - add_issue_comment | |
| new file mode 100644 | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | +# Agent settings for GitHub OAuth2 authentication with MCP | ||
| 2 | +# This configuration demonstrates OAuth2 authentication for the GitHub MCP server. | ||
| 3 | +# | ||
| 4 | +# To use this config with OAuth2: | ||
| 5 | +# 1. Obtain a GitHub OAuth2 token with appropriate scopes | ||
| 6 | +# (e.g., repo, read:org, project, etc.) | ||
| 7 | +# 2. Set the token in your environment: export GITHUB_OAUTH_TOKEN=your_token | ||
| 8 | +# 3. Run: mm --config agent.dmr.github-oauth.yaml | ||
| 9 | +# | ||
| 10 | +# Note: OAuth2 tokens can be obtained from: | ||
| 11 | +# - GitHub CLI: gh auth login (creates token in ~/.config/gh/hosts.yml) | ||
| 12 | +# - Manual creation: https://github.com/settings/tokens | ||
| 13 | +# - OAuth2 flow: requires client ID and secret (advanced) | ||
| 14 | +# | ||
| 15 | +# For production use with sbx, use agent.dmr.sbx.yaml instead. | ||
| 16 | + | ||
| 17 | +provider: dmr | ||
| 18 | + | ||
| 19 | +model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M | ||
| 20 | +baseUrl: http://localhost:12434/engines/v1 | ||
| 21 | +fallback: http://host.docker.internal:12434/engines/v1 | ||
| 22 | + | ||
| 23 | +contextWindow: 0 | ||
| 24 | +maxOutput: 16000 | ||
| 25 | +maxTurns: 40 | ||
| 26 | + | ||
| 27 | +bashTool: true | ||
| 28 | +editTools: true | ||
| 29 | +planTool: true | ||
| 30 | + | ||
| 31 | +lsp: | ||
| 32 | + servers: | ||
| 33 | + - name: gopls | ||
| 34 | + command: gopls | ||
| 35 | + extensions: [".go"] | ||
| 36 | + | ||
| 37 | +skillsDir: skills | ||
| 38 | + | ||
| 39 | +sessions: | ||
| 40 | + enabled: true | ||
| 41 | + dir: .mm/sessions | ||
| 42 | + | ||
| 43 | +previewLines: 20 | ||
| 44 | + | ||
| 45 | +system: | | ||
| 46 | + Your name is Bob The Bot. | ||
| 47 | + You are a coding agent working in a terminal. | ||
| 48 | + You have a "bash" tool to run shell commands. | ||
| 49 | + Use it to explore files, run tests, inspect the repository, etc. | ||
| 50 | + Chain several commands if needed, then answer clearly in English. | ||
| 51 | + | ||
| 52 | + A request often mixes things you answer from yourself ("say hello") with | ||
| 53 | + things only a command can answer ("list the files"). Handle every part, in | ||
| 54 | + the order asked, and run a command for each part that needs one. | ||
| 55 | + Never state the contents of a file, the output of a command, or the state of | ||
| 56 | + the repository unless a command in THIS answer returned it. What you did not | ||
| 57 | + read, you do not know: run the command instead of recalling it. | ||
| 58 | + | ||
| 59 | + SKILLS | ||
| 60 | + You have a second tool, `read_skill`. Its description lists the procedures | ||
| 61 | + available for this project — one per kind of task. | ||
| 62 | + | ||
| 63 | + Any request to DO something to a Go project is a skill, not a shell command | ||
| 64 | + you invent. Match the request against that list, call `read_skill` FIRST, | ||
| 65 | + before any bash command, and then follow what it says step by step. | ||
| 66 | + | ||
| 67 | + FILE EDITING | ||
| 68 | + You have three tools for files: `read_file`, `edit_file` and `write_file`. | ||
| 69 | + They are how a file gets read and changed here: each change is exact, | ||
| 70 | + checked before it is written, and comes back as a diff with line numbers. | ||
| 71 | + bash is for running things — building, testing, listing, searching. | ||
| 72 | + | ||
| 73 | + - Read before you write: call `read_file` on the file (numbered=true when | ||
| 74 | + you need line numbers). You cannot target text you have not seen; never | ||
| 75 | + rely on what you think you remember about a file. | ||
| 76 | + - To change an existing file, call `edit_file` with one or more {old, new} | ||
| 77 | + pairs. `old` is copied from the file character for character — same | ||
| 78 | + spaces, same indentation, same line breaks — and appears exactly once: | ||
| 79 | + add the surrounding lines until it is unique. Several pairs are applied | ||
| 80 | + together, against the original file. An empty `new` deletes the text. | ||
| 81 | + - Call `write_file` only to create a file, or to rewrite one entirely and | ||
| 82 | + on purpose. On an existing file it replaces everything, including what | ||
| 83 | + you did not intend to touch. | ||
| 84 | + - Read the diff the tool returns: it says exactly what changed and on which | ||
| 85 | + line. If `edit_file` refuses — text not found, ambiguous, overlapping | ||
| 86 | + edits — read the file again and fix `old`. Do not fall back to | ||
| 87 | + `write_file` to force the change through. | ||
| 88 | + - After editing code, run the narrowest check with bash: the formatter, the | ||
| 89 | + compiler, or the test covering that file. | ||
| 90 | + | ||
| 91 | + RULES | ||
| 92 | + - Keep everything the file already does, unless the user asked to remove it. | ||
| 93 | + - Touch only the files the request is about. Do not add tests, files or | ||
| 94 | + features that were not asked for. | ||
| 95 | + - Never run a git command unless the user says git, commit or push. | ||
| 96 | + - Never move, rename or delete a file unless the user asked for it. | ||
| 97 | + - Then answer in English, in a few lines. | ||
| 98 | + - If you don't know how to use a <cli>, run `<cli> --help` (or `<cli> help`) | ||
| 99 | + to understand the options, then run the command. | ||
| 100 | + | ||
| 101 | + BACKGROUND JOBS | ||
| 102 | + Never let a command block the answer. Anything that serves, watches or runs | ||
| 103 | + long goes to the background, with BOTH streams redirected and its pid kept: | ||
| 104 | + | ||
| 105 | + nohup <command> > /tmp/<job>.log 2>&1 & echo $! > /tmp/<job>.pid | ||
| 106 | + | ||
| 107 | + Redirecting only stdout still blocks until the process exits. Read the | ||
| 108 | + `bg-jobs` skill before you wait on, inspect or stop such a job — each has a | ||
| 109 | + rule you cannot guess. Stop every job you started before you finish, and say | ||
| 110 | + which ones you left running. | ||
| 111 | + | ||
| 112 | +sampling: | ||
| 113 | + temperature: 0.0 | ||
| 114 | + parallel_tool_calls: false | ||
| 115 | + top_p: 0.9 | ||
| 116 | + max_tokens: 4096 | ||
| 117 | + | ||
| 118 | +watchdogTimeout: 30s | ||
| 119 | + | ||
| 120 | +context: | ||
| 121 | + enabled: false | ||
| 122 | + threshold: 75 | ||
| 123 | + maxMessages: 80 | ||
| 124 | + keepLastTurns: 3 | ||
| 125 | + summaryMaxTokens: 1200 | ||
| 126 | + showStats: true | ||
| 127 | + | ||
| 128 | +displayCommands: true | ||
| 129 | +showThinking: true | ||
| 130 | + | ||
| 131 | +# MCP Configuration with OAuth2 authentication for GitHub | ||
| 132 | +# This shows how to use OAuth2 tokens with the GitHub MCP server. | ||
| 133 | +# Note: The official GitHub MCP server (https://api.githubcopilot.com/mcp/) | ||
| 134 | +# uses bearer token authentication, so OAuth2 type works the same as bearer. | ||
| 135 | +# The distinction is for servers that require OAuth2 client credentials. | ||
| 136 | +mcp: | ||
| 137 | + enabled: true | ||
| 138 | + permissions: ask | ||
| 139 | + discoveryTimeout: 10s | ||
| 140 | + | ||
| 141 | + servers: | ||
| 142 | + # GitHub MCP Server with OAuth2 token | ||
| 143 | + # Token can be obtained from GitHub CLI or manual creation | ||
| 144 | + - name: github | ||
| 145 | + transport: http | ||
| 146 | + url: https://api.githubcopilot.com/mcp/ | ||
| 147 | + timeout: 30s | ||
| 148 | + auth: | ||
| 149 | + type: oauth2 # or "bearer" - both work for GitHub's token auth | ||
| 150 | + tokenEnv: GITHUB_OAUTH_TOKEN # Set this in your environment | ||
| 151 | + headers: | ||
| 152 | + Accept: "application/json, text/event-stream" | ||
| 153 | + # Optional: filter to only expose specific tools | ||
| 154 | + tools: | ||
| 155 | + - get_me | ||
| 156 | + - list_repositories | ||
| 157 | + - list_issues | ||
| 158 | + - list_pull_requests | ||
| 159 | + - get_file_contents | ||
| 160 | + - search_code | ||
| 161 | + - search_repositories | ||
| 162 | + - create_pull_request | ||
| 163 | + - add_issue_comment | ||
added
demo/agent.dmr.sbx.yaml +139 -0 | new file mode 100644 | ||
| @@ -0,0 +1,139 @@ | ||
| 1 | +# Agent settings for Docker Sandbox (sbx) mode | |
| 2 | +# Uses sbx MCP gateway - tokens are managed by sbx, not by mini-me | |
| 3 | +# | |
| 4 | +# This configuration enables MCP through the sbx gateway. | |
| 5 | +# The gateway handles OAuth authentication for GitHub and other services. | |
| 6 | +# | |
| 7 | +# To use this config: | |
| 8 | +# 1. Run: sbx login | |
| 9 | +# 2. Register MCP servers: sbx mcp add github --url https://mcp.github.com/mcp | |
| 10 | +# 3. Authenticate: sbx mcp auth github | |
| 11 | +# 4. Start sandbox: sbx run mm --name my-session --config agent.dmr.sbx.yaml | |
| 12 | +# | |
| 13 | +# The gateway URL and tokens are automatically injected by sbx. | |
| 14 | +# Mini-me will discover tools from all servers registered with sbx. | |
| 15 | + | |
| 16 | +provider: dmr | |
| 17 | + | |
| 18 | +model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M | |
| 19 | +baseUrl: http://localhost:12434/engines/v1 | |
| 20 | +fallback: http://host.docker.internal:12434/engines/v1 | |
| 21 | + | |
| 22 | +contextWindow: 0 | |
| 23 | +maxOutput: 16000 | |
| 24 | +maxTurns: 40 | |
| 25 | + | |
| 26 | +bashTool: true | |
| 27 | +editTools: true | |
| 28 | +planTool: true | |
| 29 | + | |
| 30 | +lsp: | |
| 31 | + servers: | |
| 32 | + - name: gopls | |
| 33 | + command: gopls | |
| 34 | + extensions: [".go"] | |
| 35 | + | |
| 36 | +skillsDir: skills | |
| 37 | + | |
| 38 | +sessions: | |
| 39 | + enabled: true | |
| 40 | + dir: .mm/sessions | |
| 41 | + | |
| 42 | +previewLines: 20 | |
| 43 | + | |
| 44 | +system: | | |
| 45 | + Your name is Bob The Bot. | |
| 46 | + You are a coding agent working in a terminal. | |
| 47 | + You have a "bash" tool to run shell commands. | |
| 48 | + Use it to explore files, run tests, inspect the repository, etc. | |
| 49 | + Chain several commands if needed, then answer clearly in English. | |
| 50 | + | |
| 51 | + A request often mixes things you answer from yourself ("say hello") with | |
| 52 | + things only a command can answer ("list the files"). Handle every part, in | |
| 53 | + the order asked, and run a command for each part that needs one. | |
| 54 | + Never state the contents of a file, the output of a command, or the state of | |
| 55 | + the repository unless a command in THIS answer returned it. What you did not | |
| 56 | + read, you do not know: run the command instead of recalling it. | |
| 57 | + | |
| 58 | + SKILLS | |
| 59 | + You have a second tool, `read_skill`. Its description lists the procedures | |
| 60 | + available for this project — one per kind of task. | |
| 61 | + | |
| 62 | + Any request to DO something to a Go project is a skill, not a shell command | |
| 63 | + you invent. Match the request against that list, call `read_skill` FIRST, | |
| 64 | + before any bash command, and then follow what it says step by step. | |
| 65 | + | |
| 66 | + FILE EDITING | |
| 67 | + You have three tools for files: `read_file`, `edit_file` and `write_file`. | |
| 68 | + They are how a file gets read and changed here: each change is exact, | |
| 69 | + checked before it is written, and comes back as a diff with line numbers. | |
| 70 | + bash is for running things — building, testing, listing, searching. | |
| 71 | + | |
| 72 | + - Read before you write: call `read_file` on the file (numbered=true when | |
| 73 | + you need line numbers). You cannot target text you have not seen; never | |
| 74 | + rely on what you think you remember about a file. | |
| 75 | + - To change an existing file, call `edit_file` with one or more {old, new} | |
| 76 | + pairs. `old` is copied from the file character for character — same | |
| 77 | + spaces, same indentation, same line breaks — and appears exactly once: | |
| 78 | + add the surrounding lines until it is unique. Several pairs are applied | |
| 79 | + together, against the original file. An empty `new` deletes the text. | |
| 80 | + - Call `write_file` only to create a file, or to rewrite one entirely and | |
| 81 | + on purpose. On an existing file it replaces everything, including what | |
| 82 | + you did not intend to touch. | |
| 83 | + - Read the diff the tool returns: it says exactly what changed and on which | |
| 84 | + line. If `edit_file` refuses — text not found, ambiguous, overlapping | |
| 85 | + edits — read the file again and fix `old`. Do not fall back to | |
| 86 | + `write_file` to force the change through. | |
| 87 | + - After editing code, run the narrowest check with bash: the formatter, the | |
| 88 | + compiler, or the test covering that file. | |
| 89 | + | |
| 90 | + RULES | |
| 91 | + - Keep everything the file already does, unless the user asked to remove it. | |
| 92 | + - Touch only the files the request is about. Do not add tests, files or | |
| 93 | + features that were not asked for. | |
| 94 | + - Never run a git command unless the user says git, commit or push. | |
| 95 | + - Never move, rename or delete a file unless the user asked for it. | |
| 96 | + - Then answer in English, in a few lines. | |
| 97 | + - If you don't know how to use a <cli>, run `<cli> --help` (or `<cli> help`) | |
| 98 | + to understand the options, then run the command. | |
| 99 | + | |
| 100 | + BACKGROUND JOBS | |
| 101 | + Never let a command block the answer. Anything that serves, watches or runs | |
| 102 | + long goes to the background, with BOTH streams redirected and its pid kept: | |
| 103 | + | |
| 104 | + nohup <command> > /tmp/<job>.log 2>&1 & echo $! > /tmp/<job>.pid | |
| 105 | + | |
| 106 | + Redirecting only stdout still blocks until the process exits. Read the | |
| 107 | + `bg-jobs` skill before you wait on, inspect or stop such a job — each has a | |
| 108 | + rule you cannot guess. Stop every job you started before you finish, and say | |
| 109 | + which ones you left running. | |
| 110 | + | |
| 111 | +sampling: | |
| 112 | + temperature: 0.0 | |
| 113 | + parallel_tool_calls: false | |
| 114 | + top_p: 0.9 | |
| 115 | + max_tokens: 4096 | |
| 116 | + | |
| 117 | +watchdogTimeout: 30s | |
| 118 | + | |
| 119 | +context: | |
| 120 | + enabled: false | |
| 121 | + threshold: 75 | |
| 122 | + maxMessages: 80 | |
| 123 | + keepLastTurns: 3 | |
| 124 | + summaryMaxTokens: 1200 | |
| 125 | + showStats: true | |
| 126 | + | |
| 127 | +displayCommands: true | |
| 128 | +showThinking: true | |
| 129 | + | |
| 130 | +# MCP Configuration for sbx mode | |
| 131 | +# This agent uses the sbx MCP gateway which is automatically injected. | |
| 132 | +# The gateway URL is provided via MCP_GATEWAY_URL environment variable. | |
| 133 | +# All MCP servers registered with `sbx mcp add` will be available through this gateway. | |
| 134 | +mcp: | |
| 135 | + enabled: true | |
| 136 | + permissions: ask | |
| 137 | + discoveryTimeout: 10s | |
| 138 | + # No static servers - all servers come from sbx gateway | |
| 139 | + servers: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | +# Agent settings for Docker Sandbox (sbx) mode | ||
| 2 | +# Uses sbx MCP gateway - tokens are managed by sbx, not by mini-me | ||
| 3 | +# | ||
| 4 | +# This configuration enables MCP through the sbx gateway. | ||
| 5 | +# The gateway handles OAuth authentication for GitHub and other services. | ||
| 6 | +# | ||
| 7 | +# To use this config: | ||
| 8 | +# 1. Run: sbx login | ||
| 9 | +# 2. Register MCP servers: sbx mcp add github --url https://mcp.github.com/mcp | ||
| 10 | +# 3. Authenticate: sbx mcp auth github | ||
| 11 | +# 4. Start sandbox: sbx run mm --name my-session --config agent.dmr.sbx.yaml | ||
| 12 | +# | ||
| 13 | +# The gateway URL and tokens are automatically injected by sbx. | ||
| 14 | +# Mini-me will discover tools from all servers registered with sbx. | ||
| 15 | + | ||
| 16 | +provider: dmr | ||
| 17 | + | ||
| 18 | +model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M | ||
| 19 | +baseUrl: http://localhost:12434/engines/v1 | ||
| 20 | +fallback: http://host.docker.internal:12434/engines/v1 | ||
| 21 | + | ||
| 22 | +contextWindow: 0 | ||
| 23 | +maxOutput: 16000 | ||
| 24 | +maxTurns: 40 | ||
| 25 | + | ||
| 26 | +bashTool: true | ||
| 27 | +editTools: true | ||
| 28 | +planTool: true | ||
| 29 | + | ||
| 30 | +lsp: | ||
| 31 | + servers: | ||
| 32 | + - name: gopls | ||
| 33 | + command: gopls | ||
| 34 | + extensions: [".go"] | ||
| 35 | + | ||
| 36 | +skillsDir: skills | ||
| 37 | + | ||
| 38 | +sessions: | ||
| 39 | + enabled: true | ||
| 40 | + dir: .mm/sessions | ||
| 41 | + | ||
| 42 | +previewLines: 20 | ||
| 43 | + | ||
| 44 | +system: | | ||
| 45 | + Your name is Bob The Bot. | ||
| 46 | + You are a coding agent working in a terminal. | ||
| 47 | + You have a "bash" tool to run shell commands. | ||
| 48 | + Use it to explore files, run tests, inspect the repository, etc. | ||
| 49 | + Chain several commands if needed, then answer clearly in English. | ||
| 50 | + | ||
| 51 | + A request often mixes things you answer from yourself ("say hello") with | ||
| 52 | + things only a command can answer ("list the files"). Handle every part, in | ||
| 53 | + the order asked, and run a command for each part that needs one. | ||
| 54 | + Never state the contents of a file, the output of a command, or the state of | ||
| 55 | + the repository unless a command in THIS answer returned it. What you did not | ||
| 56 | + read, you do not know: run the command instead of recalling it. | ||
| 57 | + | ||
| 58 | + SKILLS | ||
| 59 | + You have a second tool, `read_skill`. Its description lists the procedures | ||
| 60 | + available for this project — one per kind of task. | ||
| 61 | + | ||
| 62 | + Any request to DO something to a Go project is a skill, not a shell command | ||
| 63 | + you invent. Match the request against that list, call `read_skill` FIRST, | ||
| 64 | + before any bash command, and then follow what it says step by step. | ||
| 65 | + | ||
| 66 | + FILE EDITING | ||
| 67 | + You have three tools for files: `read_file`, `edit_file` and `write_file`. | ||
| 68 | + They are how a file gets read and changed here: each change is exact, | ||
| 69 | + checked before it is written, and comes back as a diff with line numbers. | ||
| 70 | + bash is for running things — building, testing, listing, searching. | ||
| 71 | + | ||
| 72 | + - Read before you write: call `read_file` on the file (numbered=true when | ||
| 73 | + you need line numbers). You cannot target text you have not seen; never | ||
| 74 | + rely on what you think you remember about a file. | ||
| 75 | + - To change an existing file, call `edit_file` with one or more {old, new} | ||
| 76 | + pairs. `old` is copied from the file character for character — same | ||
| 77 | + spaces, same indentation, same line breaks — and appears exactly once: | ||
| 78 | + add the surrounding lines until it is unique. Several pairs are applied | ||
| 79 | + together, against the original file. An empty `new` deletes the text. | ||
| 80 | + - Call `write_file` only to create a file, or to rewrite one entirely and | ||
| 81 | + on purpose. On an existing file it replaces everything, including what | ||
| 82 | + you did not intend to touch. | ||
| 83 | + - Read the diff the tool returns: it says exactly what changed and on which | ||
| 84 | + line. If `edit_file` refuses — text not found, ambiguous, overlapping | ||
| 85 | + edits — read the file again and fix `old`. Do not fall back to | ||
| 86 | + `write_file` to force the change through. | ||
| 87 | + - After editing code, run the narrowest check with bash: the formatter, the | ||
| 88 | + compiler, or the test covering that file. | ||
| 89 | + | ||
| 90 | + RULES | ||
| 91 | + - Keep everything the file already does, unless the user asked to remove it. | ||
| 92 | + - Touch only the files the request is about. Do not add tests, files or | ||
| 93 | + features that were not asked for. | ||
| 94 | + - Never run a git command unless the user says git, commit or push. | ||
| 95 | + - Never move, rename or delete a file unless the user asked for it. | ||
| 96 | + - Then answer in English, in a few lines. | ||
| 97 | + - If you don't know how to use a <cli>, run `<cli> --help` (or `<cli> help`) | ||
| 98 | + to understand the options, then run the command. | ||
| 99 | + | ||
| 100 | + BACKGROUND JOBS | ||
| 101 | + Never let a command block the answer. Anything that serves, watches or runs | ||
| 102 | + long goes to the background, with BOTH streams redirected and its pid kept: | ||
| 103 | + | ||
| 104 | + nohup <command> > /tmp/<job>.log 2>&1 & echo $! > /tmp/<job>.pid | ||
| 105 | + | ||
| 106 | + Redirecting only stdout still blocks until the process exits. Read the | ||
| 107 | + `bg-jobs` skill before you wait on, inspect or stop such a job — each has a | ||
| 108 | + rule you cannot guess. Stop every job you started before you finish, and say | ||
| 109 | + which ones you left running. | ||
| 110 | + | ||
| 111 | +sampling: | ||
| 112 | + temperature: 0.0 | ||
| 113 | + parallel_tool_calls: false | ||
| 114 | + top_p: 0.9 | ||
| 115 | + max_tokens: 4096 | ||
| 116 | + | ||
| 117 | +watchdogTimeout: 30s | ||
| 118 | + | ||
| 119 | +context: | ||
| 120 | + enabled: false | ||
| 121 | + threshold: 75 | ||
| 122 | + maxMessages: 80 | ||
| 123 | + keepLastTurns: 3 | ||
| 124 | + summaryMaxTokens: 1200 | ||
| 125 | + showStats: true | ||
| 126 | + | ||
| 127 | +displayCommands: true | ||
| 128 | +showThinking: true | ||
| 129 | + | ||
| 130 | +# MCP Configuration for sbx mode | ||
| 131 | +# This agent uses the sbx MCP gateway which is automatically injected. | ||
| 132 | +# The gateway URL is provided via MCP_GATEWAY_URL environment variable. | ||
| 133 | +# All MCP servers registered with `sbx mcp add` will be available through this gateway. | ||
| 134 | +mcp: | ||
| 135 | + enabled: true | ||
| 136 | + permissions: ask | ||
| 137 | + discoveryTimeout: 10s | ||
| 138 | + # No static servers - all servers come from sbx gateway | ||
| 139 | + servers: [] | ||
modified
internal/config/config.go +17 -5 | @@ -253,12 +253,12 @@ type MCPServer struct { | ||
| 253 | 253 | |
| 254 | 254 | // MCPAuthConfig describes authentication settings for an MCP server. |
| 255 | 255 | type MCPAuthConfig struct { |
| 256 | - // Type: authentication method. Supported: "none", "bearer", "api_key". | |
| 257 | - // OAuth2 is NOT supported in this initial implementation (per user requirements). | |
| 256 | + // Type: authentication method. Supported: "none", "bearer", "api_key", "oauth2". | |
| 257 | + // OAuth2 uses token from environment variable (pre-obtained OAuth token). | |
| 258 | 258 | Type string `yaml:"type"` |
| 259 | 259 | |
| 260 | 260 | // TokenEnv: environment variable name containing the bearer token. |
| 261 | - // Used when Type is "bearer". | |
| 261 | + // Used when Type is "bearer" or "oauth2". | |
| 262 | 262 | TokenEnv string `yaml:"tokenEnv"` |
| 263 | 263 | |
| 264 | 264 | // APIKeyEnv: environment variable name containing the API key. |
| @@ -268,6 +268,18 @@ type MCPAuthConfig struct { | ||
| 268 | 268 | // APIKeyHeader: HTTP header name for the API key. Default: "Authorization". |
| 269 | 269 | // Some APIs use custom headers like "X-API-Key". |
| 270 | 270 | APIKeyHeader string `yaml:"apiKeyHeader"` |
| 271 | + | |
| 272 | + // OAuthClientID: OAuth2 client ID for servers that require it. | |
| 273 | + // Used when Type is "oauth2". | |
| 274 | + OAuthClientID string `yaml:"oauthClientId"` | |
| 275 | + | |
| 276 | + // OAuthClientSecretEnv: environment variable name containing OAuth2 client secret. | |
| 277 | + // Used when Type is "oauth2". | |
| 278 | + OAuthClientSecretEnv string `yaml:"oauthClientSecretEnv"` | |
| 279 | + | |
| 280 | + // OAuthScopes: list of OAuth2 scopes to request. | |
| 281 | + // Used when Type is "oauth2". | |
| 282 | + OAuthScopes []string `yaml:"oauthScopes"` | |
| 271 | 283 | } |
| 272 | 284 | |
| 273 | 285 | // Validate checks the MCP server configuration. |
| @@ -283,8 +295,8 @@ func (s MCPServer) Validate() error { | ||
| 283 | 295 | return errors.New("mcp server with stdio transport must have a command") |
| 284 | 296 | case s.Transport != "http" && s.Transport != "stdio": |
| 285 | 297 | return fmt.Errorf("mcp server transport must be 'http' or 'stdio', got %q", s.Transport) |
| 286 | - case s.Auth.Type != "" && s.Auth.Type != "none" && s.Auth.Type != "bearer" && s.Auth.Type != "api_key": | |
| 287 | - return fmt.Errorf("mcp auth type must be 'none', 'bearer', or 'api_key', got %q", s.Auth.Type) | |
| 298 | + case s.Auth.Type != "" && s.Auth.Type != "none" && s.Auth.Type != "bearer" && s.Auth.Type != "api_key" && s.Auth.Type != "oauth2": | |
| 299 | + return fmt.Errorf("mcp auth type must be 'none', 'bearer', 'api_key', or 'oauth2', got %q", s.Auth.Type) | |
| 288 | 300 | case s.Auth.Type == "bearer" && s.Auth.TokenEnv == "": |
| 289 | 301 | return errors.New("mcp bearer auth must specify tokenEnv") |
| 290 | 302 | case s.Auth.Type == "api_key" && s.Auth.APIKeyEnv == "": |
| @@ -253,12 +253,12 @@ type MCPServer struct { | |||
| 253 | 253 | ||
| 254 | // MCPAuthConfig describes authentication settings for an MCP server. | 254 | // MCPAuthConfig describes authentication settings for an MCP server. |
| 255 | type MCPAuthConfig struct { | 255 | type MCPAuthConfig struct { |
| 256 | - // Type: authentication method. Supported: "none", "bearer", "api_key". | 256 | + // Type: authentication method. Supported: "none", "bearer", "api_key", "oauth2". |
| 257 | - // OAuth2 is NOT supported in this initial implementation (per user requirements). | 257 | + // OAuth2 uses token from environment variable (pre-obtained OAuth token). |
| 258 | Type string `yaml:"type"` | 258 | Type string `yaml:"type"` |
| 259 | 259 | ||
| 260 | // TokenEnv: environment variable name containing the bearer token. | 260 | // TokenEnv: environment variable name containing the bearer token. |
| 261 | - // Used when Type is "bearer". | 261 | + // Used when Type is "bearer" or "oauth2". |
| 262 | TokenEnv string `yaml:"tokenEnv"` | 262 | TokenEnv string `yaml:"tokenEnv"` |
| 263 | 263 | ||
| 264 | // APIKeyEnv: environment variable name containing the API key. | 264 | // APIKeyEnv: environment variable name containing the API key. |
| @@ -268,6 +268,18 @@ type MCPAuthConfig struct { | |||
| 268 | // APIKeyHeader: HTTP header name for the API key. Default: "Authorization". | 268 | // APIKeyHeader: HTTP header name for the API key. Default: "Authorization". |
| 269 | // Some APIs use custom headers like "X-API-Key". | 269 | // Some APIs use custom headers like "X-API-Key". |
| 270 | APIKeyHeader string `yaml:"apiKeyHeader"` | 270 | APIKeyHeader string `yaml:"apiKeyHeader"` |
| 271 | + | ||
| 272 | + // OAuthClientID: OAuth2 client ID for servers that require it. | ||
| 273 | + // Used when Type is "oauth2". | ||
| 274 | + OAuthClientID string `yaml:"oauthClientId"` | ||
| 275 | + | ||
| 276 | + // OAuthClientSecretEnv: environment variable name containing OAuth2 client secret. | ||
| 277 | + // Used when Type is "oauth2". | ||
| 278 | + OAuthClientSecretEnv string `yaml:"oauthClientSecretEnv"` | ||
| 279 | + | ||
| 280 | + // OAuthScopes: list of OAuth2 scopes to request. | ||
| 281 | + // Used when Type is "oauth2". | ||
| 282 | + OAuthScopes []string `yaml:"oauthScopes"` | ||
| 271 | } | 283 | } |
| 272 | 284 | ||
| 273 | // Validate checks the MCP server configuration. | 285 | // Validate checks the MCP server configuration. |
| @@ -283,8 +295,8 @@ func (s MCPServer) Validate() error { | |||
| 283 | return errors.New("mcp server with stdio transport must have a command") | 295 | return errors.New("mcp server with stdio transport must have a command") |
| 284 | case s.Transport != "http" && s.Transport != "stdio": | 296 | case s.Transport != "http" && s.Transport != "stdio": |
| 285 | return fmt.Errorf("mcp server transport must be 'http' or 'stdio', got %q", s.Transport) | 297 | return fmt.Errorf("mcp server transport must be 'http' or 'stdio', got %q", s.Transport) |
| 286 | - case s.Auth.Type != "" && s.Auth.Type != "none" && s.Auth.Type != "bearer" && s.Auth.Type != "api_key": | 298 | + case s.Auth.Type != "" && s.Auth.Type != "none" && s.Auth.Type != "bearer" && s.Auth.Type != "api_key" && s.Auth.Type != "oauth2": |
| 287 | - return fmt.Errorf("mcp auth type must be 'none', 'bearer', or 'api_key', got %q", s.Auth.Type) | 299 | + return fmt.Errorf("mcp auth type must be 'none', 'bearer', 'api_key', or 'oauth2', got %q", s.Auth.Type) |
| 288 | case s.Auth.Type == "bearer" && s.Auth.TokenEnv == "": | 300 | case s.Auth.Type == "bearer" && s.Auth.TokenEnv == "": |
| 289 | return errors.New("mcp bearer auth must specify tokenEnv") | 301 | return errors.New("mcp bearer auth must specify tokenEnv") |
| 290 | case s.Auth.Type == "api_key" && s.Auth.APIKeyEnv == "": | 302 | case s.Auth.Type == "api_key" && s.Auth.APIKeyEnv == "": |
modified
internal/mcp/client.go +9 -3 | @@ -489,10 +489,16 @@ func (c *HTTPClient) addAuthHeaders(req *http.Request) error { | ||
| 489 | 489 | switch c.server.Auth.Type { |
| 490 | 490 | case "none", "": |
| 491 | 491 | return nil |
| 492 | - case "bearer": | |
| 493 | - token := os.Getenv(c.server.Auth.TokenEnv) | |
| 492 | + case "bearer", "oauth2": | |
| 493 | + // OAuth2 for GitHub MCP uses bearer token authentication | |
| 494 | + // Both "bearer" and "oauth2" types work the same way for GitHub | |
| 495 | + tokenEnv := c.server.Auth.TokenEnv | |
| 496 | + if tokenEnv == "" { | |
| 497 | + tokenEnv = "GITHUB_OAUTH_TOKEN" // Default for oauth2 if not specified | |
| 498 | + } | |
| 499 | + token := os.Getenv(tokenEnv) | |
| 494 | 500 | if token == "" { |
| 495 | - return fmt.Errorf("missing bearer token for env var %s", c.server.Auth.TokenEnv) | |
| 501 | + return fmt.Errorf("missing token for env var %s", tokenEnv) | |
| 496 | 502 | } |
| 497 | 503 | req.Header.Set("Authorization", "Bearer "+token) |
| 498 | 504 | case "api_key": |
| @@ -489,10 +489,16 @@ func (c *HTTPClient) addAuthHeaders(req *http.Request) error { | |||
| 489 | switch c.server.Auth.Type { | 489 | switch c.server.Auth.Type { |
| 490 | case "none", "": | 490 | case "none", "": |
| 491 | return nil | 491 | return nil |
| 492 | - case "bearer": | 492 | + case "bearer", "oauth2": |
| 493 | - token := os.Getenv(c.server.Auth.TokenEnv) | 493 | + // OAuth2 for GitHub MCP uses bearer token authentication |
| 494 | + // Both "bearer" and "oauth2" types work the same way for GitHub | ||
| 495 | + tokenEnv := c.server.Auth.TokenEnv | ||
| 496 | + if tokenEnv == "" { | ||
| 497 | + tokenEnv = "GITHUB_OAUTH_TOKEN" // Default for oauth2 if not specified | ||
| 498 | + } | ||
| 499 | + token := os.Getenv(tokenEnv) | ||
| 494 | if token == "" { | 500 | if token == "" { |
| 495 | - return fmt.Errorf("missing bearer token for env var %s", c.server.Auth.TokenEnv) | 501 | + return fmt.Errorf("missing token for env var %s", tokenEnv) |
| 496 | } | 502 | } |
| 497 | req.Header.Set("Authorization", "Bearer "+token) | 503 | req.Header.Set("Authorization", "Bearer "+token) |
| 498 | case "api_key": | 504 | case "api_key": |
modified
main.go +31 -1 | @@ -326,8 +326,38 @@ func assembleMCP(e *engine.Engine, d *detector.LoopDetector, set *toolSet) { | ||
| 326 | 326 | return |
| 327 | 327 | } |
| 328 | 328 | |
| 329 | + // Check for sbx MCP gateway - if present, add it as a server | |
| 330 | + // sbx injects MCP_GATEWAY_URL and handles OAuth tokens via its gateway | |
| 331 | + // The gateway substitutes sentinel tokens with real OAuth tokens | |
| 332 | + gatewayURL := os.Getenv("MCP_GATEWAY_URL") | |
| 333 | + | |
| 329 | 334 | servers := config.Cfg.MCP.Servers |
| 330 | - if len(servers) == 0 { | |
| 335 | + | |
| 336 | + // If gateway URL is set and no servers configured, use gateway only | |
| 337 | + // If gateway URL is set and servers configured, add gateway as additional server | |
| 338 | + if gatewayURL != "" { | |
| 339 | + gatewayServer := config.MCPServer{ | |
| 340 | + Name: "sbx-gateway", | |
| 341 | + Transport: "http", | |
| 342 | + URL: gatewayURL, | |
| 343 | + Timeout: config.Cfg.MCP.DiscoveryTimeout, | |
| 344 | + Auth: config.MCPAuthConfig{ | |
| 345 | + Type: "bearer", | |
| 346 | + TokenEnv: "MCP_GATEWAY_TOKEN", // sbx may inject this, or substitute via header | |
| 347 | + }, | |
| 348 | + Headers: map[string]string{ | |
| 349 | + "Accept": "application/json, text/event-stream", | |
| 350 | + }, | |
| 351 | + } | |
| 352 | + | |
| 353 | + // If no servers configured, use gateway only | |
| 354 | + if len(servers) == 0 { | |
| 355 | + servers = []config.MCPServer{gatewayServer} | |
| 356 | + } else { | |
| 357 | + // Add gateway as first server (prefer gateway tools) | |
| 358 | + servers = append([]config.MCPServer{gatewayServer}, servers...) | |
| 359 | + } | |
| 360 | + } else if len(servers) == 0 { | |
| 331 | 361 | set.warnings = append(set.warnings, "mcp: enabled but no servers configured") |
| 332 | 362 | return |
| 333 | 363 | } |
| @@ -326,8 +326,38 @@ func assembleMCP(e *engine.Engine, d *detector.LoopDetector, set *toolSet) { | |||
| 326 | return | 326 | return |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | + // Check for sbx MCP gateway - if present, add it as a server | ||
| 330 | + // sbx injects MCP_GATEWAY_URL and handles OAuth tokens via its gateway | ||
| 331 | + // The gateway substitutes sentinel tokens with real OAuth tokens | ||
| 332 | + gatewayURL := os.Getenv("MCP_GATEWAY_URL") | ||
| 333 | + | ||
| 329 | servers := config.Cfg.MCP.Servers | 334 | servers := config.Cfg.MCP.Servers |
| 330 | - if len(servers) == 0 { | 335 | + |
| 336 | + // If gateway URL is set and no servers configured, use gateway only | ||
| 337 | + // If gateway URL is set and servers configured, add gateway as additional server | ||
| 338 | + if gatewayURL != "" { | ||
| 339 | + gatewayServer := config.MCPServer{ | ||
| 340 | + Name: "sbx-gateway", | ||
| 341 | + Transport: "http", | ||
| 342 | + URL: gatewayURL, | ||
| 343 | + Timeout: config.Cfg.MCP.DiscoveryTimeout, | ||
| 344 | + Auth: config.MCPAuthConfig{ | ||
| 345 | + Type: "bearer", | ||
| 346 | + TokenEnv: "MCP_GATEWAY_TOKEN", // sbx may inject this, or substitute via header | ||
| 347 | + }, | ||
| 348 | + Headers: map[string]string{ | ||
| 349 | + "Accept": "application/json, text/event-stream", | ||
| 350 | + }, | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + // If no servers configured, use gateway only | ||
| 354 | + if len(servers) == 0 { | ||
| 355 | + servers = []config.MCPServer{gatewayServer} | ||
| 356 | + } else { | ||
| 357 | + // Add gateway as first server (prefer gateway tools) | ||
| 358 | + servers = append([]config.MCPServer{gatewayServer}, servers...) | ||
| 359 | + } | ||
| 360 | + } else if len(servers) == 0 { | ||
| 331 | set.warnings = append(set.warnings, "mcp: enabled but no servers configured") | 361 | set.warnings = append(set.warnings, "mcp: enabled but no servers configured") |
| 332 | return | 362 | return |
| 333 | } | 363 | } |