Skip to main content
Glama
mikolajwojcicki

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.