Skip to main content
Glama
joplin-api-client.ts4.01 kB
import axios, { AxiosResponse } from "axios" interface JoplinAPIClientConfig { host?: string port?: number token: string } interface JoplinAPIResponse<T = any> { items: T[] has_more: boolean } interface RequestOptions { query?: Record<string, any> [key: string]: any } class JoplinAPIClient { private readonly baseURL: string private readonly token: string constructor({ host = "127.0.0.1", port = 41184, token }: JoplinAPIClientConfig) { this.baseURL = `http://${host}:${port}` this.token = token } async serviceAvailable(): Promise<boolean> { try { const response: AxiosResponse<string> = await axios.get(`${this.baseURL}/ping`) return response.status === 200 && response.data === "JoplinClipperServer" } catch (error: unknown) { process.stderr.write(`Error checking Joplin service availability: ${error}\n`) return false } } async getAllItems<T = any>(path: string, options: RequestOptions = {}): Promise<T[]> { let page = 1 const items: T[] = [] try { while (true) { const response = await this.get<JoplinAPIResponse<T>>( path, this.mergeRequestOptions(options, { query: { page } }), ) // Validate response format if (!response || typeof response !== "object" || !Array.isArray(response.items)) { throw new Error(`Unexpected response format from Joplin API for path: ${path}`) } items.push(...response.items) page += 1 if (!response.has_more) break } return items } catch (error: unknown) { process.stderr.write(`Error in getAllItems for path ${path}: ${error}\n`) throw error } } async get<T = any>(path: string, options: RequestOptions = {}): Promise<T> { try { const { data }: AxiosResponse<T> = await axios.get(`${this.baseURL}${path}`, { params: this.requestOptions(options).query, }) return data } catch (error: unknown) { process.stderr.write(`Error in GET request for path ${path}: ${error}\n`) throw error } } async post<T = any>(path: string, body: any, options: RequestOptions = {}): Promise<T> { try { const { data }: AxiosResponse<T> = await axios.post(`${this.baseURL}${path}`, body, { params: this.requestOptions(options).query, }) return data } catch (error: unknown) { process.stderr.write(`Error in POST request for path ${path}: ${error}\n`) throw error } } async delete<T = any>(path: string, options: RequestOptions = {}): Promise<T> { try { const { data }: AxiosResponse<T> = await axios.delete(`${this.baseURL}${path}`, { params: this.requestOptions(options).query, }) return data } catch (error: unknown) { process.stderr.write(`Error in DELETE request for path ${path}: ${error}\n`) throw error } } async put<T = any>(path: string, body: any, options: RequestOptions = {}): Promise<T> { try { const { data }: AxiosResponse<T> = await axios.put(`${this.baseURL}${path}`, body, { params: this.requestOptions(options).query, }) return data } catch (error: unknown) { process.stderr.write(`Error in PUT request for path ${path}: ${error}\n`) throw error } } private requestOptions(options: RequestOptions = {}): RequestOptions { return this.mergeRequestOptions( { query: { token: this.token }, }, options, ) } private mergeRequestOptions(options1: RequestOptions, options2: RequestOptions): RequestOptions { return { query: { ...(options1.query || {}), ...(options2.query || {}), }, ...this.except(options1, "query"), ...this.except(options2, "query"), } } private except(obj: Record<string, any>, key: string): Record<string, any> { const result = { ...obj } delete result[key] return result } } export default JoplinAPIClient

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/jordanburke/joplin-mcp-server'

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