Skip to content

Add a private MCP server and REST API for ad network data - #128

Merged
RaviTejaKNTS merged 1 commit into
mainfrom
private-mcp-api
Nov 14, 2026
Merged

RaviTejaKNTS merged 1 commit into
mainfrom
private-mcp-api

Conversation

@RaviTejaKNTS

Copy link
Copy Markdown
Collaborator

What

A private MCP server and REST API over stack127's ad network data, in the existing backend on OVH. The owner will connect it to claude.ai and ChatGPT for research and data stories. It is built so it can later become a public, paid product, but everything public stays off.

  • One core, two front doors: lib/data-api/ad-networks/ answers every question. The MCP route (app/api/mcp/, Streamable HTTP, stateless, @modelcontextprotocol/sdk 1.32.2) and REST routes (app/api/v1/ad-networks/...) are thin wrappers with the same schemas and output.
  • Eight read-only tools: ad_networks_overview, list_ad_networks, get_ad_network, compare_ad_networks, count_sites, find_sites, lookup_site, network_movements.
    • Every answer carries its data time, the definitions it depends on, caveats, and a stack127 URL to cite.
    • No publisher names or internal check fields.
  • Safety: read-only transactions with a statement timeout, allow-listed SQL fragments, row caps and cursors, durable per-principal rate limits, a usage log, and no-store on every response.
  • Auth: the browser connectors use a secret path; header clients and REST use a bearer token. Both are read only from env and compared in constant time.
  • Secret path protection:
    • proxy.ts keeps private paths out of Cache-Tag, canonical Link and x-stack127-pathname.
    • Wrong secrets and unknown API paths get the same generic 404.
    • Every existing public API keeps its exact headers (1,008-comparison baseline test).
  • SQL tool: run_readonly_sql exists but stays locked until it has its own restricted database login (STACK127_DATA_SQL_DATABASE_URL).
  • Flags: all feature flags default off.
  • Migration: creates the reader views and the counter table only; no roles (production logins lack CREATEROLE).
  • Unchanged: Dockerfile and start scripts are identical to main.
  • Docs: stack127_docs/mcp-and-api.md covers the tools, routes, auth, limits, connecting each client, rotation, the off switches, and the Cloudflare rules to add before enabling.

Review

Four security-focused review rounds by a separate reviewer. Findings fixed along the way:

  • a migration that production could not run;
  • the secret path leaking into proxy headers;
  • SQL tool isolation;
  • an array row-budget bypass;
  • text-only MCP output;
  • changed public API headers.

A server wrapper added in round 3 was removed in round 4, because it broke graceful shutdown. The final review found no runtime blocker; its two documentation points are fixed.

Checks

  • npm run quality: TypeScript and 129 tests.
  • npm run test:data-api: 15 tests, including the public header baseline.
  • Routing tests through Next's real router: 1,239 HTTP requests.
  • Development integration: all eight tools.

After merge

  1. Confirm the migration applied and the release SHA is live, with all flags off.
  2. Add the narrow Cloudflare cache bypass for /api/mcp and /api/v1 and the redirect no-store response rule.
  3. Run the synthetic edge probes.
  4. Add the secrets to Infisical, enable MCP and REST, and test every tool live.

🤖 Generated with Claude Code

One core (lib/data-api/ad-networks) answers every question; the MCP
route (Streamable HTTP, stateless, SDK 1.32.2) and versioned REST
routes under /api/v1 are thin wrappers with the same schemas and
output. Eight read-only tools: overview, list, profile, compare,
count, find, lookup, movements. Every answer carries its data time,
definitions, caveats and a citable stack127 URL, and never returns
publisher names or internal check fields.

Requests run in read-only transactions with a statement timeout,
allow-listed SQL fragments, row caps, durable per-principal rate
limits, and no-store headers. The browser connectors use a secret
path; header clients use a bearer token. proxy.ts keeps private
paths out of cache tags, canonical links and pathname headers, and
wrong secrets share the generic 404. A private SQL tool stays locked
until it has its own database login. All feature flags default off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RaviTejaKNTS
RaviTejaKNTS merged commit 4316d03 into main Nov 14, 2026
4 checks passed

This branch was previously deployed

1 inactive deployment
production — fe3ef954 Deployed Nov 14, 2026 by RaviTejaKNTS via verify-production-build #119
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant