Skip to main content
Glama
VantaInc

Vanta MCP Server

Official
by VantaInc
utils.ts9.64 kB
import { getValidToken, refreshToken } from "../../auth.js"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import { baseApiUrl } from "../../api.js"; import { PAGE_SIZE_DESCRIPTION, PAGE_CURSOR_DESCRIPTION, } from "./descriptions.js"; export async function createAuthHeaders(): Promise<Record<string, string>> { const token = await getValidToken(); return { "Authorization": `Bearer ${token}`, "x-vanta-is-mcp": "true", }; } /** * Makes an authenticated HTTP request using a bearer token from the Vanta MCP auth system. * If the request returns a 401 Unauthorized, it will refresh the token and retry once. */ export async function makeAuthenticatedRequest( url: string, options: RequestInit = {}, ): Promise<Response> { const headers = await createAuthHeaders(); const requestOptions: RequestInit = { ...options, headers: { ...headers, ...options.headers, }, }; // Try the request with the current token let response = await fetch(url, requestOptions); // If we get a 401, refresh the token and try again if (response.status === 401) { try { await refreshToken(); const newHeaders = await createAuthHeaders(); const retryOptions: RequestInit = { ...options, headers: { ...newHeaders, ...options.headers, }, }; response = await fetch(url, retryOptions); } catch (refreshError) { console.error("Failed to refresh token:", refreshError); // Return the original 401 response } } return response; } // ========================================== // RESPONSE PROCESSING UTILITIES // ========================================== /** * Creates an error response with consistent formatting */ export function createErrorResponse(statusText: string): CallToolResult { return { content: [{ type: "text", text: `Error: ${statusText}` }], isError: true, }; } /** * Creates a success response with JSON content */ export async function createSuccessResponse( response: Response, ): Promise<CallToolResult> { try { const jsonData: unknown = await response.json(); return { content: [{ type: "text", text: JSON.stringify(jsonData, null, 2) }], }; } catch (error) { return createErrorResponse( `Failed to parse JSON response: ${error instanceof Error ? error.message : "Unknown error"}`, ); } } /** * Handles API response consistently - either returns success or error */ export async function handleApiResponse( response: Response, ): Promise<CallToolResult> { if (!response.ok) { return createErrorResponse(response.statusText); } return createSuccessResponse(response); } // ========================================== // SCHEMA FACTORY FUNCTIONS // ========================================== /** * Creates a standard pagination schema */ export function createPaginationSchema( customFields: Record<string, z.ZodTypeAny> = {}, ): z.ZodObject<Record<string, z.ZodTypeAny>> { return z.object({ pageSize: z .number() .min(1) .max(100) .describe(PAGE_SIZE_DESCRIPTION) .optional(), pageCursor: z.string().describe(PAGE_CURSOR_DESCRIPTION).optional(), ...customFields, }); } /** * Creates a filter schema with pagination base */ export function createFilterSchema( customFields: Record<string, z.ZodTypeAny> = {}, ): z.ZodObject<Record<string, z.ZodTypeAny>> { return z.object({ pageSize: z .number() .min(1) .max(100) .describe(PAGE_SIZE_DESCRIPTION) .optional(), pageCursor: z.string().describe(PAGE_CURSOR_DESCRIPTION).optional(), ...customFields, }); } /** * Creates a schema with a single required ID parameter plus pagination */ export function createIdWithPaginationSchema(params: { paramName: string; description: string; }): z.ZodObject<Record<string, z.ZodTypeAny>> { return z.object({ [params.paramName]: z.string().describe(params.description), pageSize: z .number() .min(1) .max(100) .describe(PAGE_SIZE_DESCRIPTION) .optional(), pageCursor: z.string().describe(PAGE_CURSOR_DESCRIPTION).optional(), }); } /** * Creates a schema with a single required ID parameter only */ export function createIdSchema(params: { paramName: string; description: string; }): z.ZodObject<Record<string, z.ZodTypeAny>> { return z.object({ [params.paramName]: z.string().describe(params.description), }); } /** * Creates a schema for consolidated tools that can either list resources or get a single resource by ID */ export function createConsolidatedSchema( params: { paramName: string; description: string; resourceName: string; }, additionalFields: Record<string, z.ZodTypeAny> = {}, ): z.ZodObject<Record<string, z.ZodTypeAny>> { const idDescription = `Optional ${params.resourceName} ID. If provided, returns the specific ${params.resourceName}. If omitted, lists all ${params.resourceName}s with optional filtering and pagination.`; return z.object({ [params.paramName]: z.string().describe(idDescription).optional(), ...createPaginationSchema().shape, ...additionalFields, }); } /** * Creates a schema for Trust Center consolidated tools that require a slugId plus optional resource ID */ export function createTrustCenterConsolidatedSchema( params: { paramName: string; description: string; resourceName: string; }, additionalFields: Record<string, z.ZodTypeAny> = {}, ): z.ZodObject<Record<string, z.ZodTypeAny>> { const idDescription = `Optional ${params.resourceName} ID. If provided, returns the specific ${params.resourceName}. If omitted, lists all ${params.resourceName}s with optional filtering and pagination.`; return z.object({ slugId: z .string() .describe( "Trust Center slug ID, e.g. 'company-trust-center' or specific trust center identifier", ), [params.paramName]: z.string().describe(idDescription).optional(), ...createPaginationSchema().shape, ...additionalFields, }); } // ========================================== // URL CONSTRUCTION UTILITIES // ========================================== /** * Builds a URL with query parameters */ export function buildUrl( basePath: string, params: Record<string, string | number | boolean | string[] | undefined> = {}, ): string { const url = new URL(basePath, baseApiUrl()); for (const [key, value] of Object.entries(params)) { if (value !== undefined) { if (Array.isArray(value)) { // Handle arrays by joining with commas url.searchParams.set(key, value.join(",")); } else { url.searchParams.set(key, String(value)); } } } return url.toString(); } // ========================================== // REQUEST HANDLER UTILITIES // ========================================== /** * Makes a simple GET request to the specified endpoint */ export async function makeSimpleGetRequest( endpoint: string, ): Promise<CallToolResult> { const url = buildUrl(endpoint); const response = await makeAuthenticatedRequest(url); return handleApiResponse(response); } /** * Makes a paginated GET request with query parameters */ export async function makePaginatedGetRequest( endpoint: string, params: Record<string, string | number | boolean | string[] | undefined> = {}, ): Promise<CallToolResult> { const url = buildUrl(endpoint, params); const response = await makeAuthenticatedRequest(url); return handleApiResponse(response); } /** * Makes a GET request for a specific resource by ID */ export async function makeGetByIdRequest( endpoint: string, id: string, ): Promise<CallToolResult> { const url = buildUrl(`${endpoint}/${String(id)}`); const response = await makeAuthenticatedRequest(url); return handleApiResponse(response); } /** * Makes a request that can either list resources or get a single resource by ID */ export async function makeConsolidatedRequest( endpoint: string, params: Record<string, string | number | boolean | string[] | undefined>, idParamName: string, ): Promise<CallToolResult> { const id = params[idParamName]; if (id) { // Single resource request return makeGetByIdRequest(endpoint, String(id)); } else { // List request - remove the ID param from the parameters // eslint-disable-next-line @typescript-eslint/no-unused-vars const { [idParamName]: _removedId, ...listParams } = params; return makePaginatedGetRequest(endpoint, listParams); } } /** * Makes a Trust Center request that can either list resources or get a single resource by ID */ export async function makeTrustCenterConsolidatedRequest( baseEndpoint: string, params: Record<string, string | number | boolean | string[] | undefined>, idParamName: string, resourcePath: string, ): Promise<CallToolResult> { const { slugId, [idParamName]: resourceId, ...otherParams } = params; if (resourceId) { // Single resource request: /v1/trust-centers/{slugId}/{resourcePath}/{resourceId} const url = buildUrl( `${baseEndpoint}/${String(slugId)}/${resourcePath}/${String(resourceId)}`, ); const response = await makeAuthenticatedRequest(url); return handleApiResponse(response); } else { // List request: /v1/trust-centers/{slugId}/{resourcePath} const url = buildUrl( `${baseEndpoint}/${String(slugId)}/${resourcePath}`, otherParams, ); const response = await makeAuthenticatedRequest(url); return handleApiResponse(response); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/VantaInc/vanta-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server