sigmap MCP over HTTP
README.md
# sigmap MCP over HTTP (Podman)
An **extremely lightweight** Podman/OCI image that hosts the
[`sigmap`](https://github.com/manojmallick/sigmap) MCP server as a **remote
Streamable HTTP** endpoint.
## Why a bridge is needed
`sigmap`'s MCP server speaks **stdio only** (`sigmap --mcp`) — its own docs note
"the MCP server uses stdio, not a network port." To make it reachable over the
network we wrap it with [`supergateway`](https://github.com/supercorp-ai/supergateway),
which spawns the stdio server and re-exposes it over MCP **Streamable HTTP**:
```
MCP client ──HTTP──▶ supergateway ──stdio──▶ sigmap --mcp
```
Everything runs on a single Node.js runtime (Alpine base), so the image stays
small and dependency-free beyond these two npm packages.
## Files
| File | Purpose |
| ---------------- | -------------------------------------------------------------- |
| `Containerfile` | Multi-stage Alpine build; installs `sigmap` + `supergateway`. |
| `package.json` | Pins `sigmap@8.9.1` and `supergateway@3.4.3`. |
| `entrypoint.sh` | Runs in `/workspace`, builds the initial index, launches supergateway. |
## Build
Built as a single squashed layer (`docker` format is required for the baked-in
`HEALTHCHECK` to be honored):
```bash
podman build --format docker --squash-all -t sigmap-mcp:latest .
```
## Run
Bind-mount the repository you want sigmap to index at `/workspace` (read-write —
sigmap stores its index/context files there):
```bash
podman run -d --name sigmap-mcp \
--network=host \
--userns=keep-id \
-v /path/to/your/repo:/workspace \
--restart unless-stopped \
sigmap-mcp:latest
```
The MCP endpoint is then at `http://localhost:8765/mcp`, with a liveness probe at
`http://localhost:8765/healthz` (returns `ok`). On startup the container builds an
initial index of the mounted repo so tools return real data immediately (disable
with `-e AUTO_INDEX=false`).
It also starts without a mount — tools just report "no context":
```bash
podman run --rm --network=host --name sigmap-mcp sigmap-mcp:latest
```
> Port **8765** is the default because some Cursor team/enterprise MCP policies
> allowlist that local port. Override with `-e PORT=...` if you need a different one.
### File access & permissions (rootless podman)
`--userns=keep-id` maps your host user into the container so bind-mounted files
are readable **and writable** by sigmap. On this host (uid/gid `1000`, matching the
image's `node` user) files map cleanly and generated index files come back owned by
you. If your host uid isn't `1000`, force the mapping onto the `node` user with
`--userns=keep-id:uid=1000,gid=1000`. Rootful podman doesn't need `keep-id`.
### Networking notes
- **Rootless without a network helper:** if `podman run -p ...` fails with
`could not find slirp4netns` (and `pasta` isn't installed either), run with the
host network instead — the server binds directly to the host's port:
```bash
podman run --rm --network=host --name sigmap-mcp sigmap-mcp:latest
```
- **Behind an HTTP proxy:** if `http_proxy`/`https_proxy` are set on the host,
local `curl` tests may be hijacked by the proxy. Bypass it with `--noproxy '*'`
(see the test below). The container's own healthcheck already avoids the proxy.
## Configuration (env vars)
| Variable | Default | Description |
| ----------------- | -------------- | --------------------------------------------- |
| `PORT` | `8765` | HTTP listen port. |
| `MCP_PATH` | `/mcp` | Streamable HTTP endpoint path. |
| `LOG_LEVEL` | `info` | `debug` \| `info` \| `none`. |
| `STATEFUL` | `false` | Stateless HTTP (recommended — sigmap holds no state). `true` uses per-session child processes + session IDs. |
| `SESSION_TIMEOUT` | `60000` | Stateful session idle timeout (ms); only used when `STATEFUL=true`. |
| `WORKSPACE` | `/workspace` | Directory sigmap runs in (mount your repo here). |
| `AUTO_INDEX` | `true` | Build a sigmap index on startup if the mount is non-empty. |
| `STDIO_CMD` | `sigmap --mcp` | The stdio command supergateway wraps. |
Example:
```bash
podman run -d --network=host -e LOG_LEVEL=debug -e AUTO_INDEX=false \
-v "$PWD":/workspace --userns=keep-id --name sigmap-mcp sigmap-mcp:latest
```
## Quick test
Initialize an MCP session (add `--noproxy '*'` if you are behind an HTTP proxy):
```bash
curl -sS --noproxy '*' http://127.0.0.1:8765/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
```
Expected response (SSE frame) identifies the server and its capabilities:
```
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","serverInfo":{"name":"sigmap","version":"8.9.1",...},"capabilities":{"tools":{}}}}
```
The response also carries an `mcp-session-id` header; reuse it (plus a
`notifications/initialized` POST) to call `tools/list`, which returns all 19
sigmap tools (`read_context`, `search_signatures`, `get_lines`, …).
## Use from Cursor
Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project), then enable
it under Settings → Tools & MCP:
```json
{
"mcpServers": {
"sigmap": {
"url": "http://localhost:8765/mcp"
}
}
}
```
Verify from the CLI with `cursor-agent mcp list` (expect `sigmap: ready`).
## How indexing works
sigmap runs inside the mounted `/workspace` and reads its generated context files
from disk on every MCP call. The container builds an initial index on startup; to
refresh it after changing files, either restart the container or re-run generation
inside it:
```bash
podman exec -w /workspace sigmap-mcp sigmap
```
Generated artifacts (`.github/copilot-instructions.md`, `.context/`, `PROJECT_MAP.md`)
are written into your mounted repo and, thanks to `--userns=keep-id`, are owned by
your host user.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues