Skip to content

Instantly share code, notes, and snippets.

@renezander030
Created April 28, 2026 07:22
Show Gist options
  • Select an option

  • Save renezander030/83ad49aeffa5f8749325a2b19617823f to your computer and use it in GitHub Desktop.

Select an option

Save renezander030/83ad49aeffa5f8749325a2b19617823f to your computer and use it in GitHub Desktop.
Context7 v2 — extending Upstash's MCP docs-server pattern to enterprise GraphQL APIs, lessons from building leanix-mcp-integration

Context7 v2 — enterprise-GraphQL MCP server pattern

Updated 2026-04-28 — what changes when your MCP server backs a private GraphQL API instead of a public docs index.

Upstash/context7 is the reference MCP server for fetching up-to-date library docs. 53k stars, clean pattern: resolve a library ID, query docs, return MCP-formatted text. Works because the data is public, the schema is stable, and auth is a single API key.

That pattern breaks the moment you point an MCP server at an enterprise GraphQL API — per-tenant auth, schemas that drift, 50-type datamodels, write-side mutations. I learned that while building leanix-mcp-integration, which bridges LeanIX's enterprise-architecture GraphQL platform to Claude.

Five extensions to Context7's pattern that the enterprise case forces:

1. Multi-tenant auth, not a single API key

Context7 takes one CONTEXT7_API_KEY header. Enterprise APIs are per-workspace: you need subdomain + token per tenant, and the MCP server has to route each call to the right workspace.

// .env per-instance
LEANIX_SUBDOMAIN=acme-corp
LEANIX_TOKEN=<workspace-scoped-token>

Run one MCP server process per workspace, or make the workspace part of every tool call. No middle ground.

2. Live schema introspection, not a pre-built index

Context7 indexes docs ahead of time. Enterprise GraphQL schemas drift — new fact-sheet types, custom fields, deprecated enums. You can't pre-index.

Approach: resolve field names at query-build time using a cached-but-refreshable schema introspection. Cache for an hour, invalidate on mutation.

3. Zod-schema every tool parameter

Context7 validates inputs with JSON Schema. Enterprise tools need to reject bad input at the MCP boundary — one bad mutation corrupts business data. Use Zod:

import { z } from "zod";

const createFactSheetSchema = z.object({
  type: z.enum(FACT_SHEET_TYPES),
  name: z.string().min(1).max(200),
  state: z.enum(["DRAFT", "APPROVED"]).optional(),
});

Validate at the tool handler. Don't let malformed input reach the GraphQL layer.

4. Mutations need an approval envelope, not just a return value

Context7 is read-only. The moment your MCP server can write, the MCP tool's return value is not enough — you need a human-in-the-loop approval before the mutation executes. This is the same discipline as CLAUDE.md runtime rule 7 (HITL as a first-class step type).

Wrap every write tool to return a preview first, and require a follow-up confirm-mutation call with the preview ID before the GraphQL mutation fires.

5. Silent error responses, detailed server logs

Context7 returns the error from the docs service. Enterprise MCP servers must not leak schema details, auth errors, or stack traces back to the model — the model may pass them to the user, and that's a data leak.

async function withErrorHandling(fn) {
  try { return await fn(); }
  catch (e) {
    console.error(e);                               // internal
    return { content: [{ type: "text", text: "Operation failed." }] };  // surface
  }
}

Same principle as CLAUDE.md runtime rule 10 (log silently).

When to use this pattern

  • Internal GraphQL APIs (LeanIX, Shopify Admin, Contentful, internal platforms)
  • Multi-tenant SaaS where auth scope matters
  • Any MCP server that can mutate, not just read

When Context7's lighter pattern is enough

  • Public docs / OpenAPI / fixed-schema APIs
  • Single-tenant personal use
  • Read-only tools

Weekly gists on MCP, Claude Code, and automation: follow @renezander030. The reference implementation for this pattern is in leanix-mcp-integration (Node.js, MIT).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment