Skip to main content
Glama
rajeshkumar-okta

ProGear MCP Servers

ProGear MCP Servers

ProGear 농구 용품 데모를 위한 하나의 MCP 게이트웨이로, Inventory, Customer, Sales, Pricing — 네 도메인을 호스팅합니다. 각 도메인은 실제 MCP 프로토콜(Streamable HTTP 전송)로 통신하며, **자신의 Okta 조직(org)**으로 보호됩니다(도메인별로 자체 Customтайм Authorization Server + scope 집합 보유).

이 프로젝트는 ProGearSalesAI를 의도적으로 더 작게 만든 형제 프로젝트입니다. Auth0 FGA, LangGraph 오케스트레이터, 프런트엔드가 없습니다. 게이트웨이 자체는 전달받은 bearer 토큰을 검증하는 것만 할 뿐, 호출자가 그 토큰을 어떻게 얻었는지는 신경 쓰지 않습니다. packages/local-tester(로컬 전용, 배포 안 됨)는 호출자가 토큰을 얻을 수 있는 한 가지 방법을 보여줍니다. 인간의 PKCE 로그인(위젯 Agent )을 수행한 다음 에이전트가 ID-JAG 교환(exchange)을 수행하여 그 토큰을 발급받는 방식으로, 원본 앱의 Cross-App Access 플로우를 그대로 반영합니다.

배포 형태

프로세스 하나, Render 서비스 하나, 빌드/시작 명령 하나. packages/gateway는 단일 Express 앱 뒤에서 4개 도메인을 서로 다른 경로에 마운트합니다:

Mount

Scopes

Tools

/inventory/mcp

inventory:read, inventory:write, inventory:alert

list_products, search_inventory, check_stock, get_low_stock_alerts, get_inventory_summary, update_inventory_quantity

/customer/mcp

customer:read, customer:lookup, customer:history

get_customer, search_customers, get_customers_by_tier, get_top_customers, get_customer_summary

/sales/mcp

sales:read, sales:quote, sales:order

list_orders, get_order, get_pipeline, create_quote, create_order, cancel_order

/pricing/mcp

pricing:read, pricing:margin, pricing:discount

get_price, get_category_pricing, calculate_bulk_price, get_discount_structure

각 마운트는 모두 같은 프로세스 안에서 돌아가지만 각자 자체 Okta Custom Authorization Server로 검증합니다(도메인마다 발급자(issuer)/대상(audience)이 다름). inventory 인증 서버에서 발급된 토큰은 /customer/mcp에 사용할 수 없고, 마운트 내에서 각 도구 호출은 호출자 토큰에 부여된 scope 중에서 필요한 scope를 확인합니다. 따라서 inventory:write가 없는 토큰은 check_stock을 호출할 수 있지만 update_inventory_quantity는 호출할 수 없습니다.

packages/mcp-inventory, mcp-customer, mcp-sales, mcp-pricing도 필요에 따라 별도 배포로 분리할 수 있도록 독립형 서버(자체 server.ts + /mcp + /health, 단일 도메인 환경 변수)로 계속 동작합니다. 게이트웨이는 각각의 도구 등록 로직(./tools 내보내기)은 import해서 .listen()을 직접 호출하는 대신 자체 인증 설정 아래에 마운트할 뿐입니다.

Related MCP server: Meraki MCP Server

데이터

ProGearSalesAI 데모 데이터셋을 포팅한 스냅샷으로 시드(초기화)됩니다: inventory SKU 90개, pricing 항목 90개, 고객 34명, tier/volume 할인 테이블(packages/shared/src/data/initial_data.json). 판매 주문/견적은 메모리에만 존재합니다. 모든 state는 프로세스 재시작 때 초기화됩니다 — 이 서버는 시스템 오브 레코드가 아니라 도구 서버입니다.

프로젝트 구조

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)

로컬 개발

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)

마운트에 필요한 Okta env 변수가 설정되어 있지 않으면 그 마운트는 모든 /mcp 요청에 500을 반환합니다. 단, ALLOW_INSECURE=true를 설정하면 토큰 검증을 건너뛰고 모든 마운트에 모든 scope를 부여합니다 — 개발 전용이며 배포 환경에서는 절대 설정하지 마세요.

Okta 설정 방법

OKTA_DOMAIN은 한 번만 설정하고, OKTA_<DOMAIN>_AUTH_SERVER_ID와 OKTA_<DOMAIN>_AUDIENCE는 도메인마다 설정하세요. 이 이름들은 ProGearSalesAI 백엔드에서 이미 쓰는 정확히 같은 환경 변수 이름입니다(OKTA_CUSTOMER_AUTH_SERVER_ID, OKTA_INVENTORY_AUDIENCE 등). 따라서 있는 값 그대로 가져다 복사하면 됩니다:

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-pricing

전체 목록은 .env.example 확인하세요. ProGearSalesAI 방식의 .env에는 있지만 여기에는 해당하지 않는 변수도 안내되어 있습니다(Anthropic 키, CORS, AI 에이전트 자체의 개인 키/client ID — 이 게이트웨이는 들어오는 토큰을 검증할 뿐 자체 발급이 없으). 토큰은 각 도메인의 자체 Okta JWKS endpoint(jose의 createRemoteJWKSet)에서 signature + issuer + audience로 검증됩니다. 이쪽에는 shared secret이 필요 없습니다.

Render에 배포

대시보드 또는 포함된 Blueprint 방식, 단일 서비스로 배포합니다.

수동 (New → Web Service):

필드

값

언어

Node

루트 디렉터리

(빈 값 — npm workspaces 모노레포이므로 리포지토리 루트에서 빌드)

빌드 명령

npm install && npm run build

시작 명령

node packages/gateway/dist/server.js

Health Check Path

/health

Blueprint: repo 루트의 render.yaml에는 동일한 단일 progear-mcp-gateway 서비스가 정의되어 있습니다. New → Blueprint에서 이 리포지토리를 가리키고, 요청하는 9개의 Okta env 변수(sync: false 표시)를 입력하면 됩니다.

에이전트 연결

각 마운트는 MCP를 Streamable HTTP로 POST/GET/DELETE <mount>/mcp에 노출하며(stateless, 요청 간 세션 유지 안 함), 별도의 GET <mount>/health도 있습니다. 모든 마운트을 나열하는 최상위 GET /health도 있습니다.

관련 도메인과 scopes에 대한 Okta access token을 받습니다(예: 서비스/에이전트 identity를 위한 client-credentials grant). 그런 다음:

Claude Code CLI:

claude mcp add --transport http progear-inventory \
  https://<your-render-url>/inventory/mcp \
  --header "Authorization: Bearer <token>"

이 과정을 도메인별(/customer/mcp, /sales/mcp, /pricing/mcp)로 반복하며, 해당 도메인의 audience에 대한 scope를 가진 토큰을 사용합니다.

다른 어떤 MCP 클라이언트/agent SDK도: 모든 요청에 Authorization: Bearer <token> 헤더를 붙이고 마운트의 /mcp URL을 가리키면 됩니다.

OAuth 디스커버리(정적 토큰 없음)

MCP authorization spec을 구현하는 클라이언트는 토큰을 받는 대신 Okta를 스스로 찾을 수 있습니다. 각 마운트는 RFC 9728 protected-resource 메타데이터를 게이트웨이의 루트에 게시합니다. 이 metadata는 엔드포인트를 설명하고 그 엔드포인트에 대한 path로 scope가 지정됩니다:

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"
}

이제 /mcp가 반환하는 401에는 그 포인터(pointer)도 포함되어 있어서, 아무 사전 정보 없이 엔드포인트를 호출한 클라이언트도 어디서 인증해야 하는지 알 수 있습니다.

WWW-Authenticate: Bearer resource_metadata="https://<your-render-url>/.well-known/oauth-protected-resource/inventory/mcp"

(토큰을 제출했지만 검증에 실패한 경우에는 인증 challenge에 error="invalid_token"과 error_description도 함께 영향을 미칩니다.)

이 문서에 있는 URL은 들어오는 request(X-Forwarded-Proto + Host, trust proxy 설정 — Render에서 올바름)에서 파생됩니다. 게이트웨이 앞 단계에 Host를 재작성하는 경우에만 PUBLIC_BASE_URL=https://<your-render-url>을 설정합니다.

클라이언트가 플로우를 완료할 수 있게 하려면 Okta org에 OIDC public 클라이언트(Authorization Code + PKCE)를 등록하고, 리다이렉트 URI(예: Claude.ai의 https://claude.ai/api/mcp/auth_callback)를 추가한 다음, 해당 Custom TICK Authorization Server의 access policy에서 해당 도메인의 scope를 grant합니다.

완전한 제로 구성 연결을 위해 아직 부족한 것: 동적 클라이언트 등록(Dynamic Client Registration). Okta의 /oauth2/v1/clients 엔드포인트는 SSWS API 토큰을 요구하므로 익명 등록용으로 표시할 수 없습니다. DCR을 고집하는 클라이언트(VS Code/Copilot)는 앞에 등록 shim이 필요합니다. 사전 등록된 client_id를 수용하는 클라이언트는 위의 디스커버리를 그대로 사용하면 됩니다.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    Aggregates multiple MCP servers behind a single, secure endpoint with unified tool/resource discovery, OAuth authentication, and resilient request routing. Enables users to manage and interact with multiple MCP backends through one centralized interface with load balancing and circuit breakers.
    2
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP clients to query and manage basketball-equipment inventory, customers, sales orders, and pricing through four secured domain-specific endpoints using Okta authentication.
    -