Skip to main content

Documentation Index

Fetch the complete documentation index at: https://context7-ctx7-1655-azure-apim-integration.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Azure API Management (APIM, sometimes marketed as “Azure AI Gateway”) can sit in front of mcp.context7.com and let your organization control who reaches it from your tenant. APIM validates a Microsoft Entra ID token at the gateway, and Context7 supports two ways of resolving that identity at the backend.

Two integration patterns

Shared identityPer-user identity
StatusAvailable todayIn development
Token on deviceEntra-issuedEntra-issued
APIM validatesYes (validate-azure-ad-token)Yes (validate-azure-ad-token)
Forwarded to Context7Single teamspace API keySame Entra JWT
Context7 seesOne shared teamspace identityEach user individually
Per-user usage / auditIn APIM logs onlyIn APIM and in Context7
MFA / conditional accessEnforced by Entra at sign-inEnforced by Entra at sign-in
For enterprise deployments, per-user identity is the recommended target. Start the POC on shared identity to unblock security review and initial trials, then migrate once per-user identity ships. Both patterns use the same APIM topology, so migration is essentially a policy change in APIM (drop the API key injection, keep validate-azure-ad-token) plus a one-time tenant configuration on the Context7 side.

Pattern 1: Shared identity (available today)

Architecture

MCP client ──(Entra JWT)──► APIM ──(Context7 API key)──► mcp.context7.com

                            ├─ validate-azure-ad-token
                            ├─ set-header Authorization
                            └─ rewrite-uri /mcp → /mcp/oauth
  1. The MCP client (Claude Code, Cursor, VS Code, ChatGPT) obtains an access token from Entra ID.
  2. The client calls APIM with Authorization: Bearer <entra-jwt>.
  3. APIM validates the JWT against your tenant, audience, and required scope.
  4. APIM strips the Entra token and injects your Context7 teamspace API key.
  5. The request lands at mcp.context7.com/mcp/oauth as an authenticated Context7 request.

Before you start

You will need:
  • An Azure subscription in the same tenant as your Entra users.
  • An Entra admin who can register applications.
  • A Context7 API key from a teamspace. Generate one at context7.com/dashboard under API Keys. It starts with ctx7sk_.
  • The Azure CLI installed locally (brew install azure-cli on macOS) and authenticated to your subscription (az login).

Provision APIM

APIM Basic v2 provisions in about 5 minutes and supports MCP routing. Consumption tier does not support MCP backends. Create a Bicep file:
apim.bicep
param name string
param location string
param publisherEmail string
param publisherName string

resource apim 'Microsoft.ApiManagement/service@2023-09-01-preview' = {
  name: name
  location: location
  sku: {
    name: 'BasicV2'
    capacity: 1
  }
  properties: {
    publisherEmail: publisherEmail
    publisherName: publisherName
  }
}

output gatewayUrl string = apim.properties.gatewayUrl
Deploy it:
RG=rg-context7-mcp
LOC=westeurope
APIM=apim-context7-$(openssl rand -hex 3)

az group create -n $RG -l $LOC
az deployment group create -g $RG --template-file apim.bicep \
  --parameters name=$APIM location=$LOC \
    publisherEmail=you@example.com publisherName="Your Org"
Capture the gateway URL once it returns:
APIM_HOST=$(az apim show -g $RG -n $APIM --query gatewayUrl -o tsv)
echo $APIM_HOST  # https://<apim-name>.azure-api.net

Register the MCP API in Entra

This Entra app represents the protected MCP resource. Its scope is what Entra users must request when they ask for an access token.
  1. Microsoft Entra admin centerApp registrations+ New registration.
    • Name: Context7 MCP
    • Supported account types: Accounts in this organizational directory only
    • Redirect URI: leave blank
    • Register
  2. Note the Application (client) ID and Directory (tenant) ID from the Overview page.
  3. Expose an APIAdd next to “Application ID URI” → accept the default api://<client-id>Save.
  4. + Add a scope:
    • Scope name: mcp.access
    • Who can consent: Admins and users
    • Admin consent display name: Access Context7 MCP server
    • Admin consent description: anything descriptive
    • State: Enabled
    • Add scope
  5. Manifest → find "requestedAccessTokenVersion" under api → set to 2. Save.
    This forces v2 tokens (iss: https://login.microsoftonline.com/<tid>/v2.0). v1 tokens use a different issuer and will fail JWT validation in APIM if you do not set this.
  6. API permissions → for each MCP client tool you want to allow (Claude Code, Cursor, VS Code, ChatGPT), register its client app separately and grant it delegated permission on mcp.access. Pre-authorize these clients in Expose an APIAuthorized client applications to skip end-user consent.

Configure APIM

Store the Context7 API key

az apim nv create -g $RG --service-name $APIM \
  --named-value-id context7-api-key \
  --display-name context7-api-key \
  --value "ctx7sk_PASTE_YOUR_KEY" \
  --secret true
For production, store the key in Azure Key Vault and reference it from APIM with a Key Vault-backed named value. The example above uses inline storage for brevity.

Create the API and operation

az apim api create -g $RG --service-name $APIM \
  --api-id context7-mcp \
  --display-name "Context7 MCP" \
  --path context7 \
  --service-url https://mcp.context7.com \
  --protocols https \
  --subscription-required false

az apim api operation create -g $RG --service-name $APIM \
  --api-id context7-mcp \
  --operation-id post-mcp \
  --display-name "MCP" \
  --method POST \
  --url-template "/mcp"

Attach the policy

Save this as policy.xml, replacing <your-tenant-id> and <your-mcp-api-app-id> with the values from the Entra registration step:
policy.xml
<policies>
  <inbound>
    <base />
    <validate-azure-ad-token tenant-id="<your-tenant-id>"
        header-name="Authorization"
        failed-validation-httpcode="401"
        failed-validation-error-message="Unauthorized.">
      <audiences>
        <audience><your-mcp-api-app-id></audience>
      </audiences>
      <required-claims>
        <claim name="scp" match="any">
          <value>mcp.access</value>
        </claim>
      </required-claims>
    </validate-azure-ad-token>
    <set-header name="Authorization" exists-action="override">
      <value>@("Bearer " + "{{context7-api-key}}")</value>
    </set-header>
    <rewrite-uri template="/mcp/oauth" />
  </inbound>
  <backend><base /></backend>
  <outbound><base /></outbound>
  <on-error><base /></on-error>
</policies>
Apply via the management REST API (some CLI versions do not expose az apim api policy create):
SUB=$(az account show --query id -o tsv)

python3 -c "import json; print(json.dumps({'properties': {'value': open('policy.xml').read(), 'format': 'xml'}}))" > policy-body.json

az rest --method put \
  --uri "https://management.azure.com/subscriptions/$SUB/resourceGroups/$RG/providers/Microsoft.ApiManagement/service/$APIM/apis/context7-mcp/policies/policy?api-version=2023-09-01-preview" \
  --body @policy-body.json

Test end-to-end

Obtain an Entra access token. The fastest way is device code flow:
TENANT_ID=<your-tenant-id>
CLIENT_ID=<your-mcp-api-app-id>

curl -s -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/devicecode" \
  -d "client_id=$CLIENT_ID" \
  -d "scope=api://$CLIENT_ID/mcp.access offline_access"
Open the verification_uri, enter the user_code, sign in. Then exchange the device_code:
curl -s -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
  -d "client_id=$CLIENT_ID" \
  -d "device_code=<paste-device-code>"
Copy access_token and call APIM:
ENTRA_TOKEN="<paste-access-token>"

curl -i -X POST "$APIM_HOST/context7/mcp" \
  -H "Authorization: Bearer $ENTRA_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"resolve-library-id","arguments":{"query":"routing","libraryName":"Next.js"}}}'
You should see a 200 with library results streamed back as an SSE event. Verify the gateway rejects unauthorized requests:
# No token → 401 at APIM, request never reaches Context7
curl -i -X POST "$APIM_HOST/context7/mcp" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

# Tampered token → 401
curl -i -X POST "$APIM_HOST/context7/mcp" \
  -H "Authorization: Bearer ${ENTRA_TOKEN}XYZ" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

Connecting an MCP client

For a quick test from Claude Code with a static token:
claude mcp add context7-apim https://<your-apim-host>/context7/mcp \
  --transport http \
  --header "Authorization: Bearer $ENTRA_TOKEN"
For human users without manual token handling, MCP clients can do OAuth 2.1 discovery against your APIM. This requires APIM to advertise a Protected Resource Metadata (RFC 9728) endpoint and either pre-register each MCP client tool in Entra or deploy a Dynamic Client Registration shim. Reach out for the OAuth discovery walkthrough if you need it.

Pattern 2: Per-user identity via native Entra validation (in development)

Architecture

MCP client ──(Entra JWT)──► APIM ──(same Entra JWT)──► mcp.context7.com
                            │                          │
                            validate-azure-ad-token    validates iss/aud against
                            (defence-in-depth)         your tenant config,
                                                       resolves oid → C7 user

One-time tenant onboarding

You provide Context7 with:
  • Your Entra tenant ID
  • Your MCP API app ID (audience)
  • The required scope (typically mcp.access)
We configure these against your Context7 teamspace. Once set up, our MCP server validates inbound Entra tokens for your tenant directly, and resolves the token’s oid claim to a per-user Context7 record (auto-provisioned on first sign-in).

Flow

  1. A developer’s MCP client (e.g. VS Code with GitHub Copilot) calls APIM.
  2. The client performs OAuth against your Entra tenant for an access token scoped to your MCP API.
  3. The client retries the request against APIM with the Entra token in the Authorization header.
  4. APIM validates the token locally (validate-azure-ad-token) as defence-in-depth.
  5. APIM forwards the request to mcp.context7.com with the Entra token unchanged.
  6. The Context7 MCP server validates the token against your configured tenant ID and audience.
  7. The oid claim is resolved to a Context7 user record in your teamspace, auto-provisioned on first sign-in.
  8. The request is served attributed to that user.
End state: every developer has their own Context7 identity tied to their Entra account. The token on the device remains Entra-issued throughout. APIM stays as the network and audit boundary. MFA and conditional access continue to be enforced by Entra at sign-in.

Migration from Pattern 1

If you start on Pattern 1 and migrate to Pattern 2 later, the move is:
  1. Share your tenant ID, audience, and scope with us. We onboard your tenant configuration.
  2. Update the APIM policy: keep validate-azure-ad-token, drop the set-header swap and the rewrite-uri.
  3. APIM now forwards the Entra JWT unchanged to Context7.
The Entra app registration, the Bicep, and all client-side wiring stay the same. The Context7 named value (context7-api-key) can be deleted after the cutover.

Status

Pattern 2 is currently in development. Contact context7@upstash.com or your Context7 account contact for the latest timeline and to schedule tenant onboarding.

Troubleshooting

401 Unauthorized with valid-looking token

Decode the token at jwt.io and verify:
  • iss is https://login.microsoftonline.com/<your-tenant-id>/v2.0 (v2 form). If it is https://sts.windows.net/<tid>/, your manifest’s requestedAccessTokenVersion is missing or set to null.
  • aud matches the value in your <audiences> policy block. v2 tokens use the GUID, not api://....
  • scp contains mcp.access.
  • exp is in the future.

Invalid API key. Please check your API key. API keys should start with 'ctx7sk' prefix.

This applies to Pattern 1 only. APIM forwarded successfully but the Context7 backend rejected the key. The context7-api-key named value is missing, set to a placeholder, or the key has been revoked. Generate a fresh key at context7.com/dashboard and update the named value:
az apim nv update -g $RG --service-name $APIM \
  --named-value-id context7-api-key \
  --set value="ctx7sk_NEW_KEY"

AADSTS650057: Invalid resource

The MCP client’s app registration does not list your MCP API as an allowed resource. In your Entra MCP API app → Expose an APIAuthorized client applications, add the client’s app ID and check the mcp.access scope.

Streaming responses get cut off

APIM diagnostics with “Number of payload bytes to log” > 0 break MCP’s SSE streams. Set it to 0 for Frontend Response at the service level.

What’s not covered

  • Self-hosted MCP server. This guide proxies the hosted mcp.context7.com. For air-gapped or compliance scenarios where MCP traffic cannot leave your network, contact context7@upstash.com about the self-hosted MCP package.
  • Dynamic Client Registration. Entra does not implement RFC 7591. MCP clients that depend on it (some versions of Claude.ai, ChatGPT) need to be pre-registered in Entra and pre-authorized on the MCP API app. A DCR shim deployed inside APIM is possible but not officially supported by Microsoft; reach out if you need this.