import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { BaseTool } from '../baseTool.js';
const RefreshTokenSchema = z.object({}); // No inputs β uses environment variables
const ToolDefinition = {
name: 'hubspot-refresh-token',
description: `
π Refreshes the HubSpot OAuth access token using the refresh token from environment variables.
π― Purpose:
- Use only when HubSpot API requests fail due to expired tokens.
- Automatically refreshes and sets the active access token in process.env.PRIVATE_APP_ACCESS_TOKEN.
- Not required for long-lived Private App tokens.
π‘οΈ Guardrails:
- Only use if using OAuth (i.e. REFRESH_TOKEN is present in environment).
- Do not invoke more than once per session unless a 401 Unauthorized response is received from HubSpot API.
`,
inputSchema: zodToJsonSchema(RefreshTokenSchema),
annotations: {
title: 'Refresh HubSpot OAuth Token',
readOnlyHint: false,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
};
export class RefreshTokenTool extends BaseTool {
constructor() {
super(RefreshTokenSchema, ToolDefinition);
}
async process() {
const { CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN } = process.env;
if (!CLIENT_ID || !CLIENT_SECRET || !REFRESH_TOKEN) {
return {
content: [
{
type: 'text',
text: 'β Missing CLIENT_ID, CLIENT_SECRET, or REFRESH_TOKEN in environment.',
},
],
isError: true,
};
}
try {
const res = await fetch('https://api.hubapi.com/oauth/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
refresh_token: REFRESH_TOKEN,
}),
});
const json = await res.json();
if (!res.ok) {
throw new Error(json.message || 'Token refresh failed');
}
process.env.PRIVATE_APP_ACCESS_TOKEN = json.access_token;
return {
content: [
{
type: 'text',
text: JSON.stringify({
access_token: json.access_token,
expires_in: json.expires_in,
refresh_token: json.refresh_token,
}, null, 2),
},
],
};
} catch (err) {
return {
content: [
{
type: 'text',
text: `β Token refresh failed: ${err instanceof Error ? err.message : String(err)}`,
},
],
isError: true,
};
}
}
}