Tana MCP Server
/**
* Tana API Client
* A client for interacting with the Tana Input API
*/
import axios from 'axios';
import {
TanaAPIRequest,
TanaAPIResponse,
TanaConfig,
TanaCreateNodesRequest,
TanaNode,
TanaNodeResponse,
TanaSetNameRequest
} from '../types/tana-api';
export class TanaClient {
private readonly apiToken: string;
private readonly endpoint: string;
constructor(config: TanaConfig) {
this.apiToken = config.apiToken;
this.endpoint = config.endpoint || 'https://europe-west1-tagr-prod.cloudfunctions.net/addToNodeV2';
}
/**
* Create nodes in Tana
* @param targetNodeId Optional target node ID to add nodes under
* @param nodes Array of nodes to create
* @returns The created nodes
*/
async createNodes(targetNodeId: string | undefined, nodes: TanaNode[]): Promise<TanaNodeResponse[]> {
if (nodes.length > 100) {
throw new Error('Maximum of 100 nodes can be created in a single request');
}
const request: TanaCreateNodesRequest = {
targetNodeId,
nodes
};
const response = await this.makeRequest(request);
return response.children || [];
}
/**
* Create a single node in Tana
* @param targetNodeId Optional target node ID to add the node under
* @param node Node to create
* @returns The created node
*/
async createNode(targetNodeId: string | undefined, node: TanaNode): Promise<TanaNodeResponse> {
const nodes = await this.createNodes(targetNodeId, [node]);
if (nodes.length === 0) {
throw new Error('Failed to create node');
}
return nodes[0];
}
/**
* Set the name of a node in Tana
* @param targetNodeId ID of the node to rename
* @param newName New name for the node
* @returns The updated node
*/
async setNodeName(targetNodeId: string, newName: string): Promise<TanaNodeResponse> {
const request: TanaSetNameRequest = {
targetNodeId,
setName: newName
};
const response = await this.makeRequest(request);
// Handle the different response format for setName
if (response.children && response.children.length > 0) {
return response.children[0];
}
// If the response doesn't match expected format, make a best guess
return { nodeId: targetNodeId };
}
/**
* Make a request to the Tana API
* @param request The request to send
* @returns The API response
*/
private async makeRequest(request: TanaAPIRequest): Promise<TanaAPIResponse> {
try {
const response = await axios.post<TanaAPIResponse>(this.endpoint, request, {
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
if (axios.isAxiosError(error) && error.response) {
throw new Error(`Tana API error: ${error.response.status} ${error.response.statusText}`);
}
throw error;
}
}
}