Custom MCP Clients

Demandbase Custom MCP Client Quickstart

This quickstart shows how to build a custom client for the Demandbase Model Context Protocol (MCP) server using OAuth Dynamic Client Registration (DCR), Authorization Code + PKCE, and MCP Streamable HTTP.

The steps are language agnostic. You can implement them with any HTTP client, JSON parser, browser launcher, hosted HTTPS redirect handler, and secure token store.

What You Need

  • Demandbase MCP enabled for your account.
  • A user who can sign in to Demandbase.
  • A hosted HTTPS redirect URI for your application.
  • Demandbase Support has allowlisted that exact redirect URI for your app ID.
  • A secure place to store OAuth registration data and tokens.

Endpoints

Start from these public Demandbase MCP endpoints:

MCP server:
https://gateway.demandbase.com/mcp/servers/db-mcp

Protected resource metadata:
https://gateway.demandbase.com/.well-known/oauth-protected-resource

Do not hardcode the authorization or token endpoints. Discover them from metadata.

1. Choose a Redirect URI

Use an HTTPS redirect URI hosted by your application:

https://your-app.example.com/oauth/demandbase/callback

The redirect URI must match exactly. Scheme, host, path, and trailing slash all matter. For example, these are different redirect URIs:

https://your-app.example.com/oauth/demandbase/callback
https://your-app.example.com/oauth/demandbase/callback/

2. Discover OAuth Metadata

Fetch protected resource metadata:

GET https://gateway.demandbase.com/.well-known/oauth-protected-resource
Accept: application/json

Read:

  • resource
  • authorization_servers
  • scopes_supported

Then fetch authorization server metadata from the advertised authorization server. For Demandbase, the current metadata discovery URL is:

GET https://gateway.demandbase.com/.well-known/oauth-authorization-server
Accept: application/json

Read:

  • issuer
  • authorization_endpoint
  • token_endpoint
  • registration_endpoint
  • token_endpoint_auth_methods_supported
  • scopes_supported

Store client registrations and tokens by issuer.

3. Register Your App and Get the App ID

Register dynamically with the discovered registration_endpoint. The registration response returns your OAuth client_id, which is the app ID Demandbase Support needs for redirect URI allowlisting.

Example request:

POST {registration_endpoint}
Accept: application/json
Content-Type: application/json

{
  "client_name": "Your MCP Client",
  "application_type": "web",
  "redirect_uris": ["https://your-app.example.com/oauth/demandbase/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "openid profile email offline_access"
}

Example response:

{
  "client_id": "0oaexampleclientid",
  "redirect_uris": ["https://your-app.example.com/oauth/demandbase/callback"],
  "token_endpoint_auth_method": "none",
  "scope": "openid profile email offline_access"
}

Store the registration response securely, especially:

  • client_id
  • redirect_uris
  • token_endpoint_auth_method
  • scope
  • client_secret, if one is issued

The returned client_id is your app ID. Copy this value; Demandbase Support needs it to allowlist your redirect URI.

4. Ask Demandbase Support to Allowlist the Redirect URI

Before you test sign-in, send Demandbase Support both:

  • your app ID, also called the OAuth client_id
  • the exact hosted HTTPS redirect URI

Ask Support to allowlist that redirect URI for that app ID.

This is required. The OAuth sign-in flow will fail until Demandbase has allowlisted the redirect URI for the app ID.

5. Start PKCE Login

Generate a PKCE pair:

code_verifier = high_entropy_random_string
code_challenge = base64url(sha256(code_verifier))
code_challenge_method = S256

Generate and store a random state value with the code_verifier.

Build the authorization URL:

{authorization_endpoint}
  ?response_type=code
  &client_id={client_id}
  &redirect_uri={url_encoded_redirect_uri}
  &scope=openid%20profile%20email%20offline_access
  &state={state}
  &code_challenge={code_challenge}
  &code_challenge_method=S256
  &resource={url_encoded_resource}

Open this URL in the user's browser.

6. Handle the Callback

After sign-in, Demandbase redirects the browser to your hosted redirect URI:

{redirect_uri}?code={authorization_code}&state={state}

Your client must:

  • verify state
  • reject OAuth error callbacks
  • extract code
  • exchange the code immediately
  • discard the temporary state and code_verifier

7. Exchange the Code for Tokens

Send a form-encoded request to the discovered token_endpoint:

POST {token_endpoint}
Accept: application/json
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code={authorization_code}&
redirect_uri={exact_redirect_uri}&
client_id={client_id}&
code_verifier={code_verifier}

Store:

  • access_token
  • refresh_token, if returned
  • expires_at
  • scope
  • issuer
  • client_id

If the access token expires, refresh it:

POST {token_endpoint}
Accept: application/json
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token={refresh_token}&
client_id={client_id}

Refresh tokens can rotate. Replace the old refresh token if the response includes a new one.

8. Send MCP Requests

Send JSON-RPC requests to:

https://gateway.demandbase.com/mcp/servers/db-mcp

Use these headers:

Authorization: Bearer {access_token}
Content-Type: application/json
Accept: application/json, text/event-stream

If the server returns an MCP session ID header during initialization, include it on later requests.

Initialize:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": {
      "name": "your-mcp-client",
      "version": "1.0.0"
    }
  }
}

List tools:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {}
}

Call a tool:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "company_global_directory",
    "arguments": {
      "company_name": "Demandbase"
    }
  }
}

Use tools/list as the source of truth for tool names, descriptions, and argument schemas.

9. Parse Responses

The MCP server can respond with either:

  • application/json: a single JSON-RPC response
  • text/event-stream: Server-Sent Events containing JSON-RPC messages

For SSE responses:

  • collect data: lines until a blank line
  • parse the collected event data as JSON
  • continue until you receive the final JSON-RPC response

Always check for a JSON-RPC error object, even when the HTTP status is 200.

Troubleshooting

ProblemWhat to Check
Redirect URI error during sign-inConfirm the app ID/client ID and exact hosted HTTPS callback URI were provided to Demandbase Support and allowlisted together.
DCR returns 401 or 403The registration endpoint may require additional authorization. Contact Demandbase.
Token exchange returns invalid_grantThe code may be expired, already used, or paired with a different redirect URI or PKCE verifier. Restart login.
MCP request returns 401Refresh the access token or sign in again.
MCP request returns 403Confirm the user and account are enabled for Demandbase MCP.
Tool call fails validationRe-read tools/list and send arguments that match the tool's inputSchema.

Production Checklist

  • Discover OAuth endpoints from metadata.
  • Use Authorization Code + PKCE.
  • Validate state on every callback.
  • Store tokens securely.
  • Refresh tokens before access tokens expire.
  • Keep tokens and authorization codes out of logs.
  • Validate tool arguments against inputSchema.
  • Parse both JSON and SSE responses.
  • Use the Demandbase gateway MCP endpoint; do not call downstream services directly.

References