| 1 | # siGit Code wave three: stdio MCP, web search, and the repo tabs |
| 2 | |
| 3 | Status: draft v1 (2026-07-05). Owner: product/eng. Audience: internal (private repo). |
| 4 | Scope: the third feature wave for **siGit Code**, following |
| 5 | [sigit-code-parity-roadmap.md](sigit-code-parity-roadmap.md) (wave one, shipped |
| 6 | in v1.3.2) and [sigit-code-copilot-cli-parity.md](sigit-code-copilot-cli-parity.md) |
| 7 | (wave two, in review). One ecosystem unlock, one core capability with a product |
| 8 | hook, one differentiator. The three are independent and run in parallel; spec 3 |
| 9 | stacks on the unmerged TUI tabs branch and rebases once that merges. |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## 1. Spec: stdio MCP transport |
| 14 | |
| 15 | **Branch:** `feature/mcp-stdio-transport` (sigit) · **Touches:** `src/mcp.rs` |
| 16 | |
| 17 | ### Problem |
| 18 | |
| 19 | Nearly every published MCP server (filesystem, Playwright, Postgres, GitHub's |
| 20 | own) is stdio-first. siGit Code speaks only Streamable HTTP, so users cannot |
| 21 | plug in most of the MCP ecosystem. Claude Code and Copilot CLI both support |
| 22 | stdio. |
| 23 | |
| 24 | ### Design |
| 25 | |
| 26 | - `mcp.toml` server entries gain the stdio shape: `command` (required), |
| 27 | `args = [...]`, `[server.env]`, all mutually exclusive with `url`. Existing |
| 28 | HTTP entries keep working unchanged. |
| 29 | - A stdio server is spawned at discovery (`mcp::init`): child process with |
| 30 | piped stdin/stdout, stderr inherited to the log. JSON-RPC frames are |
| 31 | newline-delimited per the MCP stdio transport spec. The same |
| 32 | `initialize`/`tools/list` handshake and `tools/call` forwarding as HTTP; |
| 33 | the transport is an enum behind the existing server cache. |
| 34 | - Lifecycle: the child lives for the sigit process; a dead child fails calls |
| 35 | with a clear in-band error and is not restarted mid-session (restart on |
| 36 | `/reload`). Discovery timeout applies to the handshake like HTTP. All |
| 37 | children are killed on exit. |
| 38 | - Same namespacing (`mcp__<server>__<tool>`), same output caps, same `/mcp` |
| 39 | listing (show the command instead of the URL). |
| 40 | - Cross-platform: plain std/tokio process handling, no cfg(unix). |
| 41 | |
| 42 | ### Acceptance |
| 43 | |
| 44 | - A `[[server]]` entry with `command = "npx"`, `args = ["-y", "@modelcontextprotocol/server-everything"]` |
| 45 | (or a local stub) discovers tools and serves calls end to end. |
| 46 | - Unit/integration tests use a tiny scripted stdio MCP server (a shell or |
| 47 | Rust stub speaking the handshake) rather than a real npm package; CI stays |
| 48 | hermetic. A dead-child call returns the in-band error. |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | ## 2. Spec: web search through sigit.si |
| 53 | |
| 54 | **Branch:** `feature/mcp-web-search` (sigit-si) · **Touches:** the MCP server, a new search proxy service |
| 55 | |
| 56 | ### Problem |
| 57 | |
| 58 | The agent can fetch a page (`read_website`) but cannot find one. Both |
| 59 | competitors ship search. Routing it through sigit.si instead of baking a |
| 60 | provider key into the client makes search a signed-in cloud feature, keeps one |
| 61 | place to swap providers, and adds a concrete reason to `sigit login`. |
| 62 | |
| 63 | ### Design |
| 64 | |
| 65 | - New MCP tool on the official server: `web_search {query, count?}` returning |
| 66 | a compact JSON list of `{title, url, snippet}` (count default 5, max 10). |
| 67 | - Behind it, a `WebSearchService` with a provider adapter. First provider: |
| 68 | Brave Search API (`SEARCH_PROVIDER=brave`, `BRAVE_SEARCH_API_KEY`), chosen |
| 69 | for its simple REST shape; the adapter boundary keeps Bing/SearXNG swappable. |
| 70 | Unconfigured provider returns an in-band ToolError telling the operator what |
| 71 | to set, never a 500. |
| 72 | - Rate limit per user (reuse the existing rate-limit pattern if one exists; |
| 73 | otherwise a simple per-user counter, e.g. 60 searches/hour) so a runaway |
| 74 | agent cannot burn the provider quota. |
| 75 | - Auth and result envelope follow the existing MCP tools exactly. Update |
| 76 | `.agents/specs/mcp-server.md`. |
| 77 | - Client side: nothing structural (MCP plumbing exists). Read-only |
| 78 | classification of official-server tools ships with spec 3. |
| 79 | |
| 80 | ### Acceptance |
| 81 | |
| 82 | - Request specs: happy path (provider stubbed with WebMock or the repo's |
| 83 | HTTP-stubbing convention), unconfigured provider, rate limit, param |
| 84 | validation. No live provider calls in CI. |
| 85 | |
| 86 | --- |
| 87 | |
| 88 | ## 3. Spec: Issues and Pull requests in the terminal |
| 89 | |
| 90 | **Branch:** `feature/tui-repo-tabs` (sigit, stacked on `feature/tui-tabs`) · |
| 91 | **Touches:** `src/chat.rs`, `src/permissions.rs`, `src/main.rs` (prompt) |
| 92 | |
| 93 | ### Problem |
| 94 | |
| 95 | Copilot CLI shows Issues and Pull requests tabs when run inside a GitHub repo. |
| 96 | sigit.si is our own git host, the MCP repo workflow tools exist server-side |
| 97 | (sigit-si #3), and siGit Code should be the best terminal client sigit.si has. |
| 98 | |
| 99 | ### Design |
| 100 | |
| 101 | - When the session cwd's `origin` remote points at the sigit.si host (parse |
| 102 | `git remote get-url origin`, compare against the `SIGIT_API_URL` host, |
| 103 | default sigit.si), the TUI adds a **Repo** tab after History: two sections, |
| 104 | Issues and Pull requests, fetched through the official MCP server |
| 105 | (`mcp__sigit__list_issues` / `mcp__sigit__list_pull_requests`, repo derived |
| 106 | from the remote's `owner/name`). Up/Down selects, Enter opens the detail |
| 107 | (`get_issue` / `get_pull_request`) in a scrollable view, `r` refreshes, |
| 108 | section toggle on `i`/`p` (or Left/Right). |
| 109 | - Fetches run as spawned tasks feeding the render loop (the Cloud tab |
| 110 | pattern); the tab degrades gracefully when signed out, MCP is off, or the |
| 111 | remote is not sigit.si (tab hidden entirely in that last case). |
| 112 | - Permission classification: the official server's read-only tools |
| 113 | (`list_*`, `get_*`, `search_code`, `get_file_contents`, `web_search`) are |
| 114 | classified read-only in `permissions.rs` so browsing never prompts; |
| 115 | everything else `mcp__sigit__*` stays mutating. Match on the official |
| 116 | server's namespace prefix plus tool-name prefix, not a hardcoded full list, |
| 117 | so new server-side read tools inherit it. |
| 118 | - Prompt guidance in `main.rs`: issue and PR workflows on sigit.si repos go |
| 119 | through the `mcp__sigit__*` tools; reference them by exact name. |
| 120 | - Stacking: branch from `feature/tui-tabs`. After sigit #25 merges, rebase |
| 121 | onto `development` before opening the PR so it shows only its own commits |
| 122 | (the repo's stacking rule). |
| 123 | |
| 124 | ### Acceptance |
| 125 | |
| 126 | - In a repo cloned from sigit.si while signed in, the Repo tab lists real |
| 127 | issues and PRs and opens details; signed out it shows the sign-in hint. |
| 128 | - Unit tests for the remote-URL parsing (ssh and https forms, non-sigit hosts) |
| 129 | and the read-only classification; TUI flows verified under tmux against a |
| 130 | scripted MCP server. |
| 131 | |
| 132 | --- |
| 133 | |
| 134 | ## 4. After this wave |
| 135 | |
| 136 | Hooks, a `/context` usage display, memory recall over `remember`, then the two |
| 137 | big strategic items: sandboxing (Cloud Agent trust) and checkpointing/rewind. |