This guide walks through deploying Azure API Management (APIM) as a gateway in front ofDocumentation Index
Fetch the complete documentation index at: https://context7.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
mcp.context7.com, with per-user authentication backed by your Microsoft Entra tenant. Developers sign in once with their corporate Entra account in their MCP client (for example VS Code with GitHub Copilot), and every request to Context7 is attributed to that individual through the oid claim on their Entra access token.
The gateway uses an On-Behalf-Of (OBO) flow: clients acquire a token for the APIM gateway, APIM exchanges it via Entra for a token targeting your MCP API, and the resulting token is forwarded to mcp.context7.com. Context7 validates the token against your tenant configuration and resolves the user. APIM remains the network and audit boundary; MFA and Conditional Access continue to be enforced by Entra at sign-in.
Architecture
Before you start
You will need:- An Azure subscription in the same tenant as your Entra users.
- A Microsoft Entra admin who can register applications and grant tenant-wide consent.
- A Context7 enterprise teamspace. The dashboard surfaces the Entra ID configuration card only for enterprise and enterprise-trial teamspaces.
- The Azure CLI installed locally (
brew install azure-clion macOS) and authenticated withaz login.
Part 1: Provision API Management
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
Part 2: Register Entra applications
Two Entra app registrations are required: one representing the MCP API (the protected resource), and one representing the APIM gateway (the OBO intermediary).Step 1: Register the MCP API app
This app represents Context7’s MCP server in your tenant. Issued tokens will carry its Application (client) ID as theaud claim.
-
Microsoft Entra admin center → App registrations → + New registration.
- Name:
Context7 MCP API - Supported account types: Single tenant only (formerly labelled “Accounts in this organizational directory only”)
- Redirect URI: leave blank
- Register
- Name:
- Copy the Application (client) ID from the Overview page. You will provide it to Context7 in Part 4.
-
Expose an API → Add next to “Application ID URI” → accept the default
api://<client-id>→ Save. -
+ Add a scope:
- Scope name:
mcp.access - Who can consent: Admins and users
- Admin consent display name:
Access Context7 MCP server - State: Enabled
- Add scope
- Scope name:
-
Manifest → set
requestedAccessTokenVersion: 2under theapiobject. Save.
Step 2: Register the APIM Gateway app
This app represents the gateway as an Entra resource. Clients request tokens for it; APIM exchanges them for MCP API tokens.-
App registrations → + New registration.
- Name:
Context7 APIM Gateway - Supported account types: Single tenant only (formerly labelled “Accounts in this organizational directory only”)
- Register
- Name:
- Copy the Application (client) ID and Directory (tenant) ID from the Overview page.
- Certificates & secrets → + New client secret → set an expiry → Add. Copy the Value column immediately; it is only shown once.
- Expose an API → Add next to “Application ID URI” → accept the default → Save.
-
+ Add a scope:
- Scope name:
Mcp.Gateway.Access - Who can consent: Admins and users
- State: Enabled
- Add scope
- Scope name:
-
Manifest → set
requestedAccessTokenVersion: 2. Save.
Step 3: Wire the permission chain
The APIM Gateway app must be allowed to call the MCP API on a user’s behalf, and the MCP client tools must be allowed to call the Gateway. In the Context7 APIM Gateway app:- API permissions → + Add a permission → My APIs →
Context7 MCP API→ Delegated permissions → checkmcp.access→ Add permissions. - Click Grant admin consent for
<your tenant>at the top of the permissions table. - Expose an API → Authorized client applications → + Add a client application. For each MCP client tool you want to allow, add its client ID and tick the
Mcp.Gateway.Accessscope:- VS Code with GitHub Copilot:
aebc6443-996d-45c2-90f0-388ff96faa56(Microsoft’s well-known VS Code client ID). - Other tools: check the tool’s documentation for its published OAuth client ID.
- VS Code with GitHub Copilot:
- Expose an API → Authorized client applications → + Add a client application. Add the Context7 APIM Gateway app’s client ID and tick the
mcp.accessscope. This skips the end-user consent prompt during the OBO exchange.
Part 3: Configure API Management
Step 1: Store credentials as named values
Step 2: Create the API and the MCP operation
Step 3: Attach the OBO policy
Save the following aspolicy.xml:
policy.xml
Two XML quirks the policy works around:
- Named values do not substitute inside policy expressions.
{{...}}is substituted by APIM only in plain element/attribute text. References inside a@{...}or@(...)expression are treated as literal C# strings. Pull each named value into acontext.Variablesslot with an attribute-form<set-variable name="..." value="{{...}}" />block, then reference the variable inside the expression. - Attribute values cannot contain raw
",<,>, or&. Use",<,>,&in attribute-form expressions. Element content (like<set-body>) only needs to escape<,>, and&; double quotes are fine. CDATA sections also avoid escaping but disable named-value substitution, so prefer plain element content with escapes.
jq wraps the policy XML in the JSON envelope the management API expects, written to policy-body.json:
The cache duration (
3000 seconds) is slightly below Entra’s default access token lifetime of one hour. Adjust if your Conditional Access policies issue shorter-lived tokens. The cache key includes the oid claim so each user’s OBO token is isolated.Part 4: Onboard your tenant in Context7
Context7 validates inbound tokens in two stages: first the token’s signature, audience, issuer, and scope are checked against your teamspace’s tenant configuration; then the token’soid claim is resolved against a list of pre-provisioned users for the teamspace. Both checks are mandatory — there is no auto-provisioning, so any developer who has not been added to the teamspace’s user list will be rejected with 401 even when their token is otherwise valid.
An owner or admin of the teamspace configures both from the dashboard.
Step 1: Configure the tenant
- Sign in to context7.com/dashboard and select the teamspace that will own the integration.
-
Overview tab → Microsoft Entra ID card → Configure.
The card is visible only for enterprise teamspaces and active enterprise trials.
-
Fill in:
- Tenant ID — your Entra directory ID
- Audience — the MCP API app Application (client) ID (the second token’s audience, not the gateway app)
- Required scope —
mcp.access
- Click Test connection. Context7 fetches your tenant’s OpenID Connect discovery document from Microsoft and confirms the issuer matches.
- Save.
Step 2: Pre-provision your developers
After saving the tenant configuration, scroll down to the Microsoft Entra ID users card on the same Overview tab. This is where you list every Entra user who should be able to authenticate to the teamspace through APIM.- Click Add user.
- Enter the user’s:
- Email — the address they sign in to Entra with.
- Object ID (oid) — their Entra object ID. Find it in the Entra admin center under Users →
<name>→ Overview → Object ID, or have the user runaz ad signed-in-user show --query id -o tsvafteraz login.
- Click Add user to save.
developer member of the teamspace, and inserts a (tenant, oid, teamspace) mapping. The same email can be added to multiple teamspaces — the Clerk user is reused and a separate mapping row is created per teamspace.
For large rollouts (tens or hundreds of developers), CSV bulk-import and Entra SCIM provisioning are on the roadmap. Reach out to context7@upstash.com if your scale needs either ahead of general availability.
aud matches the audience you provided, signed by your tenant’s Entra issuer, with the required scope present, and whose oid matches one of the users you added in Step 2.
Part 5: Expose OAuth discovery for MCP clients
For MCP clients (VS Code with GitHub Copilot, Claude Code, Cursor) to authenticate themselves without a manually-pasted Bearer token, APIM needs to advertise an OAuth 2.1 discovery surface and bridge/authorize and /token to Entra. The MCP server returns 401 with a WWW-Authenticate header pointing at a PRM document; clients follow the chain to obtain a token.
Step 1: Create the OAuth proxy API at the host root
A second API in APIM at the root path hosts the well-known endpoints and the OAuth bridge:Step 2: Attach the discovery + redirect policies
Save the three policy files:prm-policy.xml
as-metadata-policy.xml
authorize-policy.xml
/token operation needs no custom policy. APIM forwards POST /token to the service URL (.../oauth2/v2.0/token) with the form-encoded body and JSON response intact.
We use a 302 redirect on
/authorize rather than a transparent proxy. Microsoft’s sign-in page contains relative URLs and cookies tied to login.microsoftonline.com; proxying the HTML breaks form submissions. The redirect lands the user’s browser on Microsoft’s domain directly, where the sign-in flow works normally.Step 3: Add WWW-Authenticate to the MCP API on 401
Update the<on-error> block in the context7-mcp policy from Part 3 so unauthorised requests carry the discovery hint:
az rest command from Part 3.
Step 4: Verify the discovery surface
- PRM and AS metadata return the JSON documents you just defined.
/authorizereturns302 Foundwith aLocationheader pointing at Entra’s authorize endpoint.- An unauthorised POST to
/context7/mcpreturns401withWWW-Authenticate: Bearer resource_metadata="https://<your-apim-host>/.well-known/oauth-protected-resource".
Part 6: Connect an MCP client
Configure your MCP client to talk to the APIM endpoint. Example for VS Code with GitHub Copilot:~/Library/Application Support/Code/User/mcp.json on macOS (similar paths on Windows/Linux, or .vscode/mcp.json per workspace):
- VS Code calls
/context7/mcpwith no token. APIM returns401with theWWW-Authenticateheader. - VS Code follows the PRM, then the AS metadata.
- VS Code attempts Dynamic Client Registration. Because Entra ID does not implement RFC 7591, the request fails and VS Code shows a dialog: “Dynamic Client Registration not supported. Do you want to proceed by manually providing a client registration?”
- Click Copy URIs & Proceed, then paste the APIM Gateway app’s Application (client) ID when prompted. (This is the same app whose client secret APIM uses for the OBO call.)
- A browser window opens. The URL bar should switch to
login.microsoftonline.com/...after APIM’s 302 redirect. Sign in with your Entra account and accept theMcp.Gateway.Accessconsent. - The browser redirects to a localhost loopback URI that VS Code is listening on. VS Code captures the auth code.
- VS Code exchanges the code at APIM’s
/tokenendpoint, which forwards the request to Entra. Entra returns an access token whose audience is the Gateway app. - VS Code calls
/context7/mcpwith the new token. APIM validates it, performs the OBO exchange to mint an MCP-API-audience token, and forwards to Context7. - The MCP server validates the OBO token against your tenant configuration and serves the request as the signed-in user.
The manual client ID step is required because Entra ID doesn’t implement RFC 7591 Dynamic Client Registration. You can document the Gateway app’s client ID in your internal onboarding guide and rotate it on the normal app-registration lifecycle. Microsoft’s first-party VS Code client ID (
aebc6443-996d-45c2-90f0-388ff96faa56) can also be used by pre-authorising it on the Gateway scope, but the Gateway app’s own client ID is the most consistent path because PKCE removes the need for a client secret on the user-facing side.End-to-end token claims
The flow produces two distinct tokens, both issued by your Entra tenant for the same user:| Hop | iss | aud | oid | scp |
|---|---|---|---|---|
| Client → APIM | https://login.microsoftonline.com/<tid>/v2.0 | <apim-gateway-app-id> | user’s Entra object ID | Mcp.Gateway.Access |
| APIM → Context7 (after OBO) | https://login.microsoftonline.com/<tid>/v2.0 | <mcp-api-app-id> | user’s Entra object ID | mcp.access |
oid to a user in your teamspace.
Troubleshooting
401 Unauthorized at APIM
The inbound token failed validate-azure-ad-token. Decode it at jwt.io and check:
issishttps://login.microsoftonline.com/<your-tenant-id>/v2.0. If it ishttps://sts.windows.net/<tid>/, the APIM Gateway app’s manifest needsrequestedAccessTokenVersion: 2.audequals the APIM Gateway app’s client ID, not the MCP API app’s.scpcontainsMcp.Gateway.Access.expis in the future.
OBO exchange returns AADSTS50013 or AADSTS65001
The APIM Gateway app is not authorised to call the MCP API on a user’s behalf. In the APIM Gateway app, verify the API permissions tab lists Context7 MCP API → mcp.access (Delegated) with Granted for <your tenant>. If admin consent has not been granted, run Grant admin consent.
OBO exchange returns AADSTS70011: Invalid scope
The scope parameter in the OBO request is malformed. Verify that the mcp-api-app-id named value contains the MCP API app’s client ID (a GUID) and that the policy expression renders api://<guid>/.default.
401 Unauthorized from Context7 with a valid OBO token
Either the token does not match the teamspace’s tenant configuration, or the user is not on the teamspace’s pre-provisioned list.
First open Overview → Microsoft Entra ID in the dashboard and verify:
- Tenant ID matches the OBO token’s
tidclaim. - Audience matches the OBO token’s
audclaim exactly (case-sensitive, GUID form for v2 tokens). - Required scope matches a value in the token’s
scpclaim.
oid is probably not in the teamspace’s provisioned users list. Decode the OBO token at jwt.io and copy the oid claim, then check that exact value appears in Overview → Microsoft Entra ID users. If it’s missing, add the user (see Part 4 Step 2).
Streaming responses get cut off
APIM diagnostics with “Number of payload bytes to log” set above zero truncate MCP’s Server-Sent Events stream. Set Frontend Response payload logging to0 at the service level.
Wrong client ID returned to MCP clients
If a client signs in but receivesAADSTS65005: The resource is disabled or similar, the client’s app ID is not in the APIM Gateway app’s Authorized client applications list for the Mcp.Gateway.Access scope. Add it explicitly.
What this does not cover
- 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. - Dynamic Client Registration. Entra does not implement RFC 7591. Each approved MCP client must be pre-registered (or use a Microsoft first-party client ID) and pre-authorized on the APIM Gateway app’s scope.
- Group-based authorization at Context7. Context7 resolves the
oidclaim to a user record and authorises against teamspace membership. Group-based policies inside Context7 (for example, restricting which Entra group can use which libraries) are not configured by this guide; reach out if your team needs that level of control.