Skip to main content
Glama
signalsumo

SignalSumo MCP Server

by signalsumo
README.md
# @signalsumo/mcp

Model Context Protocol server for [SignalSumo](https://signalsumo.com). Lets Claude, Cursor, and any other MCP-compatible client read your SEO data, run technical audits, research keywords, and check backlink profiles through natural-language tool calls.

Every tool wraps a real endpoint on the SignalSumo REST API (`/api/v1/*`). Auth, plan gating, quotas and billing all happen server-side — the MCP layer is a thin, well-typed shim.

## What it exposes today

Eleven read-only tools. Every one reads data your SignalSumo account already
holds — this server computes nothing of its own, so the "Produced by" column is
the product that generates each dataset.

### Rankings

| Tool | Wraps | Purpose | Produced by |
|---|---|---|---|
| `list_tracked_keywords` | `GET /rank/keywords` | Every keyword you track, with country, device and current position | [Rank Tracker](https://signalsumo.com/rank-tracker) |
| `get_rank_history` | `GET /rank/history` | Daily position history for one keyword, plus the URL that ranked | [Rank Tracker](https://signalsumo.com/rank-tracker) |

### Research

| Tool | Wraps | Purpose | Produced by |
|---|---|---|---|
| `research_keyword` | `POST /keyword-research` | Start keyword research (async — returns `job_id`) | [Keyword Research Tool](https://signalsumo.com/keyword-research-tool) |
| `get_backlinks` | `GET /backlinks` | Backlink profile for any domain (paginated) | [Backlink Checker](https://signalsumo.com/backlink-checker) |

### Audits

| Tool | Wraps | Purpose | Produced by |
|---|---|---|---|
| `run_site_audit` | `POST /site-audit` | Start a technical SEO audit (async — returns `job_id`) | [Website Audit Tool](https://signalsumo.com/technical-seo-audit-tool) |
| `get_job_status` | `GET /jobs/:id` | Poll any async job until `done` or `failed` | — |

### AI visibility

| Tool | Wraps | Purpose | Produced by |
|---|---|---|---|
| `list_ai_visibility_projects` | `GET /ai-visibility/projects` | Brands you track across AI answer engines | [AI Visibility Checker](https://signalsumo.com/ai-visibility-checker) |
| `get_ai_share_of_voice` | `GET /ai-visibility/share-of-voice` | How often each engine names you versus competitors | [AI Visibility Checker](https://signalsumo.com/ai-visibility-checker) |

### Search Console

| Tool | Wraps | Purpose | Produced by |
|---|---|---|---|
| `list_gsc_properties` | `GET /gsc/properties` | Connected Search Console properties | [GSC Insights](https://signalsumo.com/google-search-console-insights) |
| `get_gsc_queries` | `GET /gsc/queries` | Queries, clicks, impressions and position from GSC | [GSC Insights](https://signalsumo.com/google-search-console-insights) |

### Account

| Tool | Wraps | Purpose | Produced by |
|---|---|---|---|
| `get_api_usage` | `GET /usage` | Current-month API usage, plan, quota reset date | [Plans & pricing](https://signalsumo.com/pricing) |

Reading is free. `run_site_audit` and `research_keyword` start work that consumes
plan credits; everything else reads data you have already paid for.

More tools follow the same pattern — one file per tool in `src/tools/`,
registered in `src/index.ts`. Full REST reference:
[signalsumo.com/api-docs](https://signalsumo.com/api-docs). Prefer no install?
The [hosted connector](https://signalsumo.com/mcp-server) speaks the same tools
over OAuth.

## Quick start

### 1. Get an API key

Sign in to SignalSumo → **API Keys** → create a key. Copy it once — it won't be shown again.

### 2. Install

```bash
npm install -g @signalsumo/mcp
```

Or run without installing via `npx`:

```bash
npx -y @signalsumo/mcp
```

### 3. Wire it into your MCP client

**Claude Desktop** — edit `claude_desktop_config.json`:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "signalsumo": {
      "command": "npx",
      "args": ["-y", "@signalsumo/mcp"],
      "env": {
        "SIGNALSUMO_API_KEY": "sk_live_your_key_here"
      }
    }
  }
}
```

Restart Claude Desktop. You should see the SignalSumo tools available in the tool picker.

**Claude Code** — add to `~/.claude/mcp_servers.json` (same shape as above).

**Cursor** — Settings → MCP → Add a new server with `command: npx`, `args: ["-y", "@signalsumo/mcp"]`, and set `SIGNALSUMO_API_KEY` in the env.

**ChatGPT** — this package will not help you, and that is not a limitation of the
package. ChatGPT connects to MCP servers as remote HTTPS connectors rather than
spawning a local process, so there is nothing for `npx` to do. Point it at the
hosted connector instead:

```
https://signalsumo.com/mcp
```

It exposes the same tools, authenticates with OAuth rather than an API key, and
needs no install. Setup steps are at
[signalsumo.com/mcp-server](https://signalsumo.com/mcp-server).

The same applies to any client that takes a URL rather than a command — the
split is stdio versus HTTP, not one vendor versus another.

### 4. Try it

Ask Claude:

> "What SEO tools do I have available through SignalSumo? Check my API usage first."

Claude will call `get_api_usage` and describe what it can do with the other tools.

## Local development

```bash
git clone https://github.com/signalsumo/mcp
cd mcp
npm install
cp .env.example .env  # add your key
npm run build
SIGNALSUMO_API_KEY=sk_live_... node dist/index.js
```

Point Claude Desktop at your local build — replace the path with wherever you
cloned the repo:

```json
{
  "mcpServers": {
    "signalsumo-dev": {
      "command": "node",
      "args": ["/path/to/signalsumo-mcp/dist/index.js"],
      "env": {
        "SIGNALSUMO_API_KEY": "sk_live_..."
      }
    }
  }
}
```

## Hosted / multi-tenant mode (HTTP + SSE)

The package ships a second entry point for self-hosting the MCP server as a shared HTTP endpoint. This is what remote MCP clients (claude.ai's remote MCP registry, hosted Cursor, browser-based inspectors) connect to.

**Transport:** Streamable HTTP per the MCP 2025-06-18 spec — POST for client → server calls, GET for the SSE stream, DELETE to end a session. Session isolation is per-connection; each session gets its own `Server` + `SignalSumoClient` so keys and state never leak between users.

**Auth:** every request must carry `Authorization: Bearer <signalsumo_api_key>`. The key is resolved at session-init and used for every subsequent call in that session — the process itself holds no keys.

### Run the HTTP server

```bash
npm run start:http
# or as an installed bin:
signalsumo-mcp-http
```

Env vars:
- `MCP_PORT` — port to listen on (default `3000`)
- `MCP_HOST` — bind address (default `0.0.0.0`)
- `SIGNALSUMO_API_BASE` — API base URL (default `https://signalsumo.com/api/v1`)

### Endpoints

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/healthz` | Liveness probe. Returns `{ok, transport, sessions}`. No auth. |
| `POST` | `/mcp` | Every client → server MCP call. First call in a session must be `initialize` — server responds with an `Mcp-Session-Id` header that subsequent calls must echo. |
| `GET` | `/mcp` | SSE stream for server → client notifications and streamed tool results. Requires `Mcp-Session-Id`. |
| `DELETE` | `/mcp` | Cleanly terminate a session. Requires `Mcp-Session-Id`. |

### Reverse proxy

Put it behind nginx/Caddy on a subdomain (e.g. `mcp.signalsumo.com`), terminate TLS there, and forward `/mcp` to the Node process. **SSE requires HTTP/1.1 with buffering disabled** — nginx snippet:

```nginx
location /mcp {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Authorization $http_authorization;
    proxy_buffering off;              # critical for SSE
    proxy_cache off;
    proxy_read_timeout 24h;
    chunked_transfer_encoding off;
}
```

### Point a client at the hosted server

For MCP clients that accept a URL + Bearer token (e.g. custom scripts, MCP
Inspector, ChatGPT, remote-server support in Claude clients), SignalSumo runs a
hosted endpoint — nothing to deploy:

```
URL:     https://signalsumo.com/mcp
Header:  Authorization: Bearer sk_live_...
```

That endpoint also speaks OAuth 2.1, which is what the Claude and ChatGPT
connector flows use instead of a raw key — see the section below and
[signalsumo.com/mcp-server](https://signalsumo.com/mcp-server).

If you have self-hosted this package on your own subdomain, substitute your own
host and `/mcp` path in the URL above.

### OAuth 2.1 (for the claude.ai/mcp remote registry)

OAuth is handled by the SignalSumo authorization server at `https://signalsumo.com` — the MCP HTTP endpoint here is just the resource server. MCP clients that speak OAuth 2.1 (Claude Desktop's remote MCP support, claude.ai/mcp) discover everything automatically:

1. Client hits `/mcp` without a token → server replies **401** with
   `WWW-Authenticate: Bearer error="unauthorized", resource_metadata="https://signalsumo.com/.well-known/oauth-protected-resource"`
2. Client fetches the resource metadata → learns the authorization server is `https://signalsumo.com`
3. Client fetches `https://signalsumo.com/.well-known/oauth-authorization-server` → learns the endpoints
4. Client POSTs to `/oauth/register` → gets a `client_id` (Dynamic Client Registration, RFC 7591)
5. Client opens `/oauth/authorize?...` in a browser tab → user logs into SignalSumo and clicks "Authorize"
6. Client POSTs to `/oauth/token` with the auth code + PKCE verifier → gets an access token
7. Client uses the access token as `Authorization: Bearer <token>` on `/mcp`

The access token is validated by SignalSumo's `ApiAuth` — the same class that validates raw API keys — so the MCP server itself doesn't need to know about OAuth. Access tokens live 1 hour; refresh tokens are rotated on every use per OAuth 2.1.

## Architecture

```
src/
├── index.ts              # stdio entry (single-user, Claude Desktop / Cursor)
├── server-http.ts        # HTTP + SSE entry (multi-tenant, self-hosted)
├── build-server.ts       # shared: builds an MCP Server with all tools registered
├── client.ts             # Axios wrapper around SignalSumo /api/v1
└── tools/
    ├── types.ts          # Shared ToolDefinition interface
    ├── usage.ts          # get_api_usage
    ├── backlinks.ts      # get_backlinks
    ├── site_audit.ts     # run_site_audit (async)
    ├── keyword_research.ts # research_keyword (async)
    ├── job_status.ts     # get_job_status
    ├── rank_keywords.ts  # list_tracked_keywords
    ├── rank_history.ts   # get_rank_history
    ├── gsc_properties.ts # list_gsc_properties
    ├── gsc_queries.ts    # get_gsc_queries
    ├── ai_visibility_projects.ts # list_ai_visibility_projects
    └── ai_share_of_voice.ts      # get_ai_share_of_voice
```

**Both transports register the same tools** — the only difference is where the API key comes from (env var for stdio, per-request header for HTTP).

**Adding a new tool** — copy an existing file in `src/tools/`, wire the Zod input schema, call `client.get()` / `client.post()`, then register it in the `tools` array in `src/index.ts`. Rebuild, restart your MCP client, done.

## Boundaries

The MCP inherits your API key's trust level. It can do anything the key can do — no more, no less. Endpoints intentionally **not** exposed as tools even though they exist on the REST API:

- Billing / plan changes / credit purchases
- User account or password reset
- Team management
- Admin-only endpoints

## Roadmap

- [x] Read-only rank tracker tools (`list_tracked_keywords`, `get_rank_history`)
- [x] Read-only AI visibility tools (`list_ai_visibility_projects`, `get_ai_share_of_voice`)
- [x] Read-only GSC tools (`list_gsc_properties`, `get_gsc_queries`)
- [x] HTTP + SSE transport (in addition to stdio)
- [x] OAuth 2.1 flow for the claude.ai/mcp remote registry
- [ ] Write-capable rank tracker tools (`add_keyword_to_tracker`, `trigger_rank_scan`)
- [ ] Local SEO tools (grid rank, review AI, citation status)
- [ ] Report generation (`generate_executive_report`)

## License

MIT