Ingram Cloud

Documentation

Connectors

Connectors

An mcp deployment is a standard MCP server. Connector OAuth makes it connectable from an MCP host — Claude custom connectors and ChatGPT developer-mode connectors today — with no out-of-band token handling: the host gets one URL, discovers the rest, and each person authorizes their own connection.

Connect in Claude

  1. Deploy the agent:

    # Authorization: tenant-admin token (server-side only)
    curl -X POST https://api.cloud.ingram.tech/v1/deployments \
      -H "Authorization: Bearer $IC_TOKEN" \
      -H "IC-Api-Version: 2026-05-01" \
      -H "Content-Type: application/json" \
      -d '{ "target": { "type": "agent", "id": "agt_…" }, "kind": "mcp" }'
    # → 201 { "id": "dep_…", "kind": "mcp", … }
    
  2. In Claude, open Add custom connector (Customize → Connectors → + → Add custom connector; Team and Enterprise plans: Settings → Organization settings → Connectors) and fill it in:

    • Name — anything; the agent's name reads best.
    • Remote MCP server URLhttps://api.cloud.ingram.tech/v1/deployments/dep_…/mcp.
    • Advanced settings (OAuth Client ID / Client Secret) — leave empty. Claude registers itself with the API automatically.
  3. Claude opens the Ingram Cloud consent page. The person pastes their connect token (below) and clicks Authorize. Done — Claude holds its own short-lived credentials from here on and refreshes them itself.

The console has the same walkthrough with the URL pre-filled: Deployments → Connect on any mcp deployment.

Connect in ChatGPT

Same server, same URL. In ChatGPT, Settings → Apps & Connectors → Advanced → Developer mode (once), then Create connector with the deployment's MCP URL. ChatGPT runs the identical OAuth flow through the same consent page.

The connect token

The consent page authenticates the person with a token you already mint — nothing new to build:

  • agent target — the smith token you minted for that person's smith. The smith must run the deployment's agent; the connection is bound to it, so the person talks to their smith — own memory, own threads.
  • smith target — that smith's token, or a tenant-admin token.

The pasted token authorizes once. Afterwards the connection runs on tokens the API mints for it: 1-hour access tokens (ordinary smith tokens scoped to runs:write approvals:write, so the host resolves the smith's tool approvals in-band — the inputResponses retry or tasks/update) and a rotating refresh token that lapses after 90 days unused. So a short-TTL connect token is fine — mint it, show it to the person, let it expire.

The connect-token paste is the one manual step, and you can remove it: register an authorize_url on any of your OAuth providers (PUT /v1/tenant/providers/{provider}) and the API redirects the person to your hosted page instead of rendering its own —

# Authorization: tenant-admin token (server-side only)
curl -X PUT https://api.cloud.ingram.tech/v1/tenant/providers/acme \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{ "authorize_url": "https://app.example.com/connect/authorize" }'

The API stays the authorization server and token issuer; only the consent + identity step moves to you. The flow:

  1. The host opens GET /oauth/authorize as usual. The API validates the request, stashes it as a single-use pending request (10-minute expiry), and 302s the browser to <authorize_url>?request_id=…. The request_id is opaque — the browser never carries an identity assertion.

  2. Your page authenticates the person with your own session, then reads the request server-side to render consent:

    # Authorization: tenant-admin token (server-side only)
    curl https://api.cloud.ingram.tech/v1/oauth/authorize-requests/$REQUEST_ID \
      -H "Authorization: Bearer $IC_TOKEN" \
      -H "IC-Api-Version: 2026-05-01"
    # → { "client_name": "Claude", "target_name": "Concierge",
    #     "deployment_id": "dep_…", "resource": "…/deployments/dep_…/mcp", … }
    
  3. On approve, name the smith the person authorized —

    # Authorization: tenant-admin token (server-side only)
    curl -X POST https://api.cloud.ingram.tech/v1/oauth/authorize-requests/$REQUEST_ID/complete \
      -H "Authorization: Bearer $IC_TOKEN" \
      -H "IC-Api-Version: 2026-05-01" \
      -H "Content-Type: application/json" \
      -d '{ "smith_id": "smt_…" }'
    # → { "redirect_url": "https://claude.ai/…?code=…&state=…" }
    

    — and 302 the browser to redirect_url. …/decline mirrors it with an access_denied redirect. Requests are single-use: a smith_id that doesn't fit the deployment gets 422 smith_mismatch and leaves the request live for a retry; one naming no live smith here gets 404, as it would anywhere else. A completed or declined request reads as 404 too.

The API re-verifies the smith against the deployment (same checks as the connect-token path) before minting the code, so your page can only complete grants for smiths that actually belong to the deployment. Hosts can't tell the difference — the code exchange, tokens, and refresh are unchanged. Providers without an authorize_url keep the built-in consent page.

What the host sees

Exactly the MCP server surface: the ask tool and the smith's working memory as a read-only resource. Isolation is the deployment's: distinct people authorize distinct smiths.

How discovery works

Everything is a published standard; no Ingram-specific client code exists in the loop:

  1. The host POSTs the MCP URL unauthenticated → 401 with a WWW-Authenticate header naming the Protected Resource Metadata (RFC 9728) and the scope to ask for (runs:write).

  2. That metadata names the authorization server; its AS metadata (RFC 8414) lists the endpoints:

    # Unauthenticated (discovery)
    curl https://api.cloud.ingram.tech/.well-known/oauth-authorization-server
    # → { "authorization_endpoint": "…/oauth/authorize",
    #     "token_endpoint": "…/oauth/token",
    #     "registration_endpoint": "…/oauth/register",
    #     "client_id_metadata_document_supported": true,
    #     "authorization_response_iss_parameter_supported": true, … }
    
  3. The host identifies itself. Either way works:

    • Client ID Metadata Document: the host's client_id is an HTTPS URL it serves a JSON registration at (client_id, client_name, redirect_uris). The API fetches and validates it; nothing is stored.
    • Dynamic Client Registration (RFC 7591) at /oauth/register, for hosts that still register this way.
  4. It runs the authorization-code flow with PKCE (S256 only). Every redirect back to the host carries iss (RFC 9207), so the host can check the answer came from the authorization server it discovered.

  5. The tokens it receives verify against /.well-known/jwks.json — the same RS256 key family as every Ingram Cloud token.

Claude's connector directory and the ChatGPT Apps SDK (rich in-chat UI, app-store listing) are distribution steps on top of this same server and are not available yet; a connector needs nothing beyond the URL.