@nutrient-sdk/dws-mcp-server
Version:
MCP server for Nutrient DWS Processor API
74 lines (73 loc) • 2.63 kB
JavaScript
import { getToken, invalidateCachedToken } from '../auth/nutrient-oauth.js';
/** Authenticates with static, per-product API keys — each key is pinned to its own tenant. */
export class StaticKeyCredentialProvider {
keys;
constructor(keys) {
this.keys = keys;
}
async token(product) {
const key = this.keys[product];
if (!key) {
// Defensive: callers must gate on `supports` first. Reaching here is a bug at the call site.
throw new Error(`No API key configured for product "${product}"`);
}
return key;
}
invalidate() {
// Static keys don't expire or rotate; nothing to invalidate.
}
canRefresh() {
// A rejected static key stays rejected — a 401 is terminal, not worth retrying.
return false;
}
supports(product) {
return Boolean(this.keys[product]);
}
}
/** Authenticates with a Nutrient OAuth token. `product:all` scope covers both products with one token. */
export class OAuthCredentialProvider {
config;
constructor(config) {
this.config = config;
}
async token() {
return getToken(this.config);
}
async invalidate() {
// Awaited by the client's 401 handler so the cached token is gone before the retry
// re-resolves — otherwise the refetch could race the unlink and read the stale token.
await invalidateCachedToken(this.config);
}
canRefresh() {
return true;
}
supports() {
return true;
}
}
export function buildOAuthConfig(environment) {
return {
authorizeUrl: `${environment.authServerUrl}/oauth/authorize`,
tokenUrl: `${environment.authServerUrl}/oauth/token`,
registrationUrl: `${environment.authServerUrl}/oauth/register`,
clientId: environment.clientId,
scopes: ['mcp:invoke', 'product:all', 'offline_access'],
resource: environment.dwsApiBaseUrl,
};
}
/**
* Selects the credential strategy for an `Environment`.
*
* Any static key present locks the server into static-key mode: falling through to
* OAuth here would silently discard the configured key and open a browser consent
* flow on what may be a headless machine.
*/
export function createCredentialProvider(environment) {
if (environment.nutrientApiKey || environment.nutrientExtractionApiKey) {
return new StaticKeyCredentialProvider({
processor: environment.nutrientApiKey,
extraction: environment.nutrientExtractionApiKey,
});
}
return new OAuthCredentialProvider(buildOAuthConfig(environment));
}