get-portfolio-chart
Retrieve portfolio performance chart data for cryptocurrency investments using a share token and specified time range (24h, 1w, 1m, 3m, 6m, 1y, all).
Instructions
Get portfolio performance chart data.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| shareToken | No | Portfolio share token. You can get your share token from the portfolio you want to retrive data from by clicking Share button on CoinStats web app portfolio tracker section - top right. | |
| type | Yes | One of 24h, 1w, 1m, 3m, 6m, 1y, all |
Input Schema (JSON Schema)
{
"properties": {
"shareToken": {
"description": "Portfolio share token. You can get your share token from the portfolio you want to retrive data from by clicking Share button on CoinStats web app portfolio tracker section - top right.",
"type": "string"
},
"type": {
"description": "One of 24h, 1w, 1m, 3m, 6m, 1y, all",
"type": "string"
}
},
"required": [
"type"
],
"type": "object"
}
Implementation Reference
- src/tools/toolConfigs.ts:369-383 (schema)Defines the schema, name, description, endpoint, and parameters (shareToken optional, type required) for the get-portfolio-chart tool.{ name: 'get-portfolio-chart', description: 'Get portfolio performance chart data.', endpoint: '/portfolio/chart', method: 'GET', parameters: { shareToken: z .string() .optional() .describe( 'Portfolio share token. You can get your share token from the portfolio you want to retrive data from by clicking Share button on CoinStats web app portfolio tracker section - top right.' ), type: z.string().describe('One of 24h, 1w, 1m, 3m, 6m, 1y, all'), }, },
- src/tools/toolFactory.ts:19-79 (registration)Registers the get-portfolio-chart tool (among others) with the MCP server using the config details, providing a generic handler that dispatches to universalApiHandler for API calls.export function registerTools(server: McpServer, toolConfigs: ToolConfig<any>[]) { toolConfigs.forEach((config) => { server.tool(config.name, config.description, config.parameters, async (params: Record<string, any>) => { // Handle local operations if (config.isLocal) { // Handle specific local tools if (config.name === 'save-share-token') { await saveToCache('shareToken', params.shareToken); return { content: [ { type: 'text', text: 'Share token saved successfully', }, ], }; } if (config.name === 'get-share-token') { const shareToken = await getFromCache('shareToken'); return { content: [ { type: 'text', text: shareToken ? shareToken : 'No share token found in cache', isError: !shareToken, }, ], }; } // Future local tools can be added here // Default response for unhandled local tools return { content: [ { type: 'text', text: 'Operation completed', }, ], }; } // Handle API operations const basePath = config.basePath || COINSTATS_API_BASE; const method = config.method || 'GET'; // Methods that typically have a request body const bodyMethods = ['POST', 'PUT', 'PATCH', 'DELETE']; // For GET/DELETE requests, all params go in the URL // For POST/PUT/PATCH, send params as the body if (bodyMethods.includes(method.toUpperCase())) { return universalApiHandler(basePath, config.endpoint, method, {}, params); } else { return universalApiHandler(basePath, config.endpoint, method, params); } }); }); }
- src/index.ts:18-18 (registration)Calls registerTools with the MCP server and allToolConfigs array, which includes the get-portfolio-chart configuration, thereby registering the tool.registerTools(server, allToolConfigs);
- src/services/request.ts:35-97 (handler)Core handler function invoked by tool registrations for API tools like get-portfolio-chart. Performs URL construction, fetch to CoinStats API, and returns formatted response.export async function universalApiHandler<T>( basePath: string, endpoint: string, method: string = 'GET', params: Record<string, any> = {}, body?: any ): Promise<{ content: Array<{ type: 'text'; text: string; isError?: boolean }>; }> { try { // Handle path parameters - replace {paramName} in endpoint with actual values let processedEndpoint = endpoint; let processedParams = { ...params }; // Find all path parameters in the endpoint (e.g., {coinId}, {id}, {type}) const pathParamMatches = endpoint.match(/\{([^}]+)\}/g); if (pathParamMatches) { for (const match of pathParamMatches) { const paramName = match.slice(1, -1); // Remove { and } if (processedParams[paramName] !== undefined) { // Replace the placeholder with the actual value processedEndpoint = processedEndpoint.replace(match, processedParams[paramName]); // Remove the parameter from query params since it's now part of the path delete processedParams[paramName]; } else { throw new Error(`Required path parameter '${paramName}' is missing`); } } } // MCP clients might not support '~' in parameter names, so we replace '-' with '~' specifically for the /coins endpoint before making the request. if (endpoint === '/coins') { processedParams = Object.entries(processedParams).reduce((acc, [key, value]) => { acc[key.replace(/-/g, '~')] = value; return acc; }, {} as Record<string, any>); } const url = `${basePath}${processedEndpoint}`; const data = await makeRequestCsApi<T>(url, method, processedParams, body); if (!data) { return { content: [{ type: 'text', text: 'Something went wrong', isError: true }], }; } return { content: [ { type: 'text', text: JSON.stringify(data), }, ], }; } catch (error) { return { content: [{ type: 'text', text: `Error: ${error}`, isError: true }], }; } }
- src/services/request.ts:4-32 (helper)Low-level HTTP request maker used by universalApiHandler to call the CoinStats API endpoints.export async function makeRequestCsApi<T>(url: string, method: string = 'GET', params: Record<string, any> = {}, body?: any): Promise<T | null> { const headers = { 'X-API-KEY': COINSTATS_API_KEY, 'Content-Type': 'application/json', }; try { // Build request options const options: RequestInit = { method, headers }; // Add body for non-GET requests if provided if (method !== 'GET' && body) { options.body = JSON.stringify(body); } // Add query params for all requests const queryParams = new URLSearchParams(params); const queryString = queryParams.toString(); const urlWithParams = queryString ? `${url}?${queryString}` : url; const response = await fetch(urlWithParams, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return (await response.json()) as T; } catch (error) { return null; } }