ProGear MCP Servers
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ProGear MCP ServersWhat's the stock status of the Nike Elite basketball?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ProGear MCP Servers
One MCP gateway for the ProGear basketball-equipment demo, hosting four domains — Inventory, Customer, Sales, and Pricing — each speaking the real MCP protocol (Streamable HTTP transport) and secured by your own Okta org (its own Custom Authorization Server + scope set per domain).
This is a deliberately smaller sibling of ProGearSalesAI: no Auth0 FGA, no LangGraph orchestrator, no frontend. The gateway itself just validates whatever bearer token it's given — it doesn't care how the caller got it. packages/local-tester (local-only, not deployed) demonstrates one way a caller might: human PKCE login + an agent doing the ID-JAG exchange to mint that token, mirroring the original app's Cross-App Access flow.
Deployment shape
One process, one Render service, one build/start command. packages/gateway mounts all 4 domains at different paths behind a single Express app:
Mount | Scopes | Tools |
|
|
|
|
|
|
|
|
|
|
|
|
Each mount validates against its own Okta Custom Authorization Server (different issuer/audience per domain) even though they all run in the same process — a token issued for the inventory auth server can't be used against /customer/mcp, and within a mount, each tool call checks its required scope against the granted scopes in the caller's token (a token missing inventory:write can call check_stock but not update_inventory_quantity).
packages/mcp-inventory, mcp-customer, mcp-sales, mcp-pricing also still work as standalone servers (their own server.ts + /mcp + /health, single-domain env vars) if you ever want to split them back into separate deployments — the gateway just imports each one's tool-registration logic (./tools export) and mounts it under its own auth config instead of calling .listen() itself.
Related MCP server: Auth0 MCP Server Demo — CRM
Data
Seeded from a ported snapshot of the ProGearSalesAI demo dataset: 90 inventory SKUs, 90 pricing entries, 34 customers, tier/volume discount tables (packages/shared/src/data/initial_data.json). Sales orders/quotes are in-memory only. All state resets on process restart — this is a tools server, not a system of record.
Project structure
packages/
shared/ # ported data + store, JWKS auth + scope enforcement, HTTP/MCP transport helper
mcp-inventory/ mcp-customer/ mcp-sales/ mcp-pricing/ # tool definitions + standalone entrypoint each
gateway/ # the actual deployment: mounts all 4 at /inventory, /customer, /sales, /pricing
local-tester/ # local-only: PKCE login + agent ID-JAG exchange, calls the deployed gateway (see its own README)Local development
npm install
npm run build # builds shared + all 4 domains + gateway, in dependency order
npm run dev:gateway # tsx watch, all 4 mounts on one port (default 3000)Without the relevant Okta env vars set for a mount, that mount returns 500 on every /mcp request unless you set ALLOW_INSECURE=true, which skips token validation and grants every scope on every mount — local dev only, never set this in a deployed environment.
Okta setup
Set OKTA_DOMAIN once, plus OKTA_<DOMAIN>_AUTH_SERVER_ID + OKTA_<DOMAIN>_AUDIENCE per domain — these are the exact same env var names already used in the ProGearSalesAI backend (OKTA_CUSTOMER_AUTH_SERVER_ID, OKTA_INVENTORY_AUDIENCE, etc.), so existing values can be copied over as-is:
OKTA_DOMAIN=https://your-org.okta.com
OKTA_INVENTORY_AUTH_SERVER_ID=... OKTA_INVENTORY_AUDIENCE=api://progear-inventory
OKTA_CUSTOMER_AUTH_SERVER_ID=... OKTA_CUSTOMER_AUDIENCE=api://progear-customer
OKTA_SALES_AUTH_SERVER_ID=... OKTA_SALES_AUDIENCE=api://progear-sales
OKTA_PRICING_AUTH_SERVER_ID=... OKTA_PRICING_AUDIENCE=api://progear-pricingSee .env.example for the full list, including which vars from a ProGearSalesAI-style .env don't apply here (Anthropic key, CORS, the AI Agent's own private key/client ID — this gateway validates incoming tokens, it doesn't self-issue any). Tokens are validated by signature + issuer + audience against each domain's own Okta JWKS endpoint (jose's createRemoteJWKSet) — no shared secret needed on this side.
Deploying to Render
Single service, either via the dashboard or the included Blueprint.
Manual (New → Web Service):
Field | Value |
Language | Node |
Root Directory | (blank — npm workspaces monorepo, build runs from repo root) |
Build Command |
|
Start Command |
|
Health Check Path |
|
Blueprint: render.yaml at the repo root defines the same single progear-mcp-gateway service — New → Blueprint, point at this repo, then fill in the 9 Okta env vars it prompts for (marked sync: false).
Connecting an agent
Each mount exposes MCP over Streamable HTTP at POST/GET/DELETE <mount>/mcp (stateless — no session persistence across requests) plus its own GET <mount>/health; there's also a top-level GET /health listing all mounts.
Get an access token from Okta for the relevant Custom Authorization Server + scopes (e.g. client-credentials grant for a service/agent identity), then:
Claude Code CLI:
claude mcp add --transport http progear-inventory \
https://<your-render-url>/inventory/mcp \
--header "Authorization: Bearer <token>"Repeat per domain (/customer/mcp, /sales/mcp, /pricing/mcp) with a token scoped to that domain's audience.
Any other MCP client / agent SDK: point it at the mount's /mcp URL with an Authorization: Bearer <token> header on every request.
OAuth discovery (no static token)
Clients that implement the MCP authorization spec can find Okta on their own instead of being handed a token. Each mount publishes RFC 9728 protected-resource metadata at the root of the gateway, path-scoped to the endpoint it describes:
GET /.well-known/oauth-protected-resource/inventory/mcp
GET /.well-known/oauth-protected-resource/customer/mcp
GET /.well-known/oauth-protected-resource/sales/mcp
GET /.well-known/oauth-protected-resource/pricing/mcp{
"resource": "https://<your-render-url>/inventory/mcp",
"authorization_servers": ["https://your-org.okta.com/oauth2/<inventory-auth-server-id>"],
"scopes_supported": ["inventory:read", "inventory:write", "inventory:alert"],
"bearer_methods_supported": ["header"],
"resource_name": "ProGear Inventory MCP"
}A 401 from /mcp now also carries the pointer, so a client that calls the endpoint cold learns where to authenticate:
WWW-Authenticate: Bearer resource_metadata="https://<your-render-url>/.well-known/oauth-protected-resource/inventory/mcp"(When a token was presented but failed validation, the challenge additionally carries error="invalid_token" and error_description.)
The URLs in these documents are derived from the incoming request (X-Forwarded-Proto + Host, with trust proxy on — correct on Render). Set PUBLIC_BASE_URL=https://<your-render-url> only if something in front of the gateway rewrites the Host header.
To let a client complete the flow, register an OIDC public client (Authorization Code + PKCE) in your Okta org, add the client's redirect URI (Claude.ai uses https://claude.ai/api/mcp/auth_callback), and grant the domain's scopes in that Custom Authorization Server's access policy.
Two things to note about these documents: there is no unscoped /.well-known/oauth-protected-resource — it would be ambiguous across four resources, so it 404s on purpose — and the gateway serves no /.well-known/oauth-authorization-server. That second document comes from Okta at {issuer}/.well-known/oauth-authorization-server, which is what the client follows next. Both endpoints exist only on the gateway; the standalone per-domain dev servers (packages/mcp-*) don't have them.
Connecting Claude Code with a pre-registered client ID
claude mcp add takes a client_id, plus a fixed callback port — which you need, because Okta requires exact redirect URIs and won't accept a random port:
claude mcp add --transport http \
--client-id 0oaXXXXXXXXXXXXXXXX \
--callback-port 33418 \
progear-inventory https://progear-mcp-servers.onrender.com/inventory/mcpThen in Claude Code: /mcp → select progear-inventory → Authenticate. That fetches the protected-resource document above, redirects you to the inventory Custom AS, and stores the token.
Repeat per domain (one Okta app can serve all four):
claude mcp add --transport http --client-id 0oaXXXXXXXXXXXXXXXX --callback-port 33418 \
progear-customer https://progear-mcp-servers.onrender.com/customer/mcp
claude mcp add --transport http --client-id 0oaXXXXXXXXXXXXXXXX --callback-port 33418 \
progear-sales https://progear-mcp-servers.onrender.com/sales/mcp
claude mcp add --transport http --client-id 0oaXXXXXXXXXXXXXXXX --callback-port 33418 \
progear-pricing https://progear-mcp-servers.onrender.com/pricing/mcpOkta side, before that works:
An OIDC app of type Native or SPA (public client): grant types Authorization Code + Refresh Token, PKCE required, no client secret.
Sign-in redirect URI
http://localhost:33418/callback. If auth fails with aredirect_urimismatch, read the actualredirect_uriout of the authorize URL in your browser's address bar and register that instead.In each of the four Custom Authorization Servers, an access-policy rule that allows this client and grants that domain's scopes.
Okta ignores the RFC 8707 resource parameter Claude sends and stamps aud from the Custom AS's own audience setting — which is exactly why the per-domain AS split works here without extra wiring.
If you'd rather skip OAuth entirely for a quick test, the static-token path still works:
claude mcp add --transport http progear-inventory \
https://progear-mcp-servers.onrender.com/inventory/mcp \
--header "Authorization: Bearer <token from the demo app>"This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Unified MCP server for 70+ eCommerce platforms: products, orders, customers, and more.
Search multi-merchant supply, checkout, and track orders via MCP.
MCP server exposing klokin time-tracking operations (employees, time entries, stores) to AI clients.
Authenticated, user-scoped MCP connectors for 30+ business systems.
Related MCP Servers
AlicenseNot gradedqualityBmaintenanceExposes ERP data (production orders, inventory, BOM, customers) to any MCP client through a backend-neutral interface, with a mock backend for testing.MIT- FlicenseNot gradedqualityCmaintenanceA secure MCP server for CRM operations (contacts and deals) with Auth0 OIDC authentication, role-based access control (sales-rep read-only vs sales-manager full access), and on-behalf-of token exchange.
- FlicenseNot gradedqualityBmaintenanceMCP gateway demo for basketball equipment, exposing inventory, customer, sales, and pricing tools with Okta-scoped auth.
- FlicenseNot gradedqualityBmaintenanceMCP server that enables AI platforms to search products, customers, and warehouses, and prepare and submit sales orders to a fixed ERP endpoint with per-session bearer authentication.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/oktaforai-okta/progear-mcp-servers'
If you have feedback or need assistance with the MCP directory API, please join our Discord server