Skip to main content
Glama

QuickFile MCP Server

by marcusquinn
client.ts9.35 kB
/** * QuickFile Client Tools * Client/customer management operations */ import type { Tool } from "@modelcontextprotocol/sdk/types.js"; import { getApiClient } from "../api/client.js"; import type { Client, ClientContact } from "../types/quickfile.js"; import { handleToolError, successResult, cleanParams, buildAddressFromArgs, buildEntityData, buildEntityUpdateData, searchSchemaProperties, entitySchemaProperties, type ToolResult, } from "./utils.js"; // ============================================================================= // Tool Definitions // ============================================================================= export const clientTools: Tool[] = [ { name: "quickfile_client_search", description: "Search for clients by company name, contact name, email, or postcode", inputSchema: { type: "object", properties: { ...searchSchemaProperties, orderBy: { type: "string", enum: ["CompanyName", "DateCreated", "ClientID"], description: "Field to order results by", }, }, required: [], }, }, { name: "quickfile_client_get", description: "Get detailed information about a specific client by ID", inputSchema: { type: "object", properties: { clientId: { type: "number", description: "The client ID" }, }, required: ["clientId"], }, }, { name: "quickfile_client_create", description: "Create a new client record", inputSchema: { type: "object", properties: entitySchemaProperties, required: [], }, }, { name: "quickfile_client_update", description: "Update an existing client record", inputSchema: { type: "object", properties: { clientId: { type: "number", description: "The client ID to update" }, ...entitySchemaProperties, }, required: ["clientId"], }, }, { name: "quickfile_client_delete", description: "Delete a client record (use with caution)", inputSchema: { type: "object", properties: { clientId: { type: "number", description: "The client ID to delete" }, }, required: ["clientId"], }, }, { name: "quickfile_client_insert_contacts", description: "Add a new contact to an existing client", inputSchema: { type: "object", properties: { clientId: { type: "number", description: "The client ID" }, firstName: { type: "string", description: "Contact first name" }, lastName: { type: "string", description: "Contact last name" }, email: { type: "string", description: "Contact email" }, telephone: { type: "string", description: "Contact telephone" }, mobile: { type: "string", description: "Contact mobile" }, isPrimary: { type: "boolean", description: "Set as primary contact", default: false, }, }, required: ["clientId", "firstName", "lastName"], }, }, { name: "quickfile_client_login_url", description: "Get a passwordless login URL for a client to view their invoices", inputSchema: { type: "object", properties: { clientId: { type: "number", description: "The client ID" }, }, required: ["clientId"], }, }, ]; // ============================================================================= // Tool Handlers // ============================================================================= interface ClientSearchResponse { RecordsetCount: number; ReturnCount: number; Record: Array<{ ClientID: number; ClientCreatedDate: string; CompanyName: string; Status: string; PrimaryContact?: { FirstName?: string; Surname?: string; Telephone?: string; Email?: string; }; AccountBalance?: string; }>; } interface ClientGetResponse { ClientDetails: Client; } interface ClientCreateResponse { ClientID: number; } interface ClientLoginResponse { LoginURL: string; } interface ContactInsertResponse { ContactID: number; } // ============================================================================= // Helper Functions // ============================================================================= function buildSearchParams( args: Record<string, unknown>, ): Record<string, unknown> { const searchParams: Record<string, unknown> = { ReturnCount: (args.returnCount as number) ?? 25, Offset: (args.offset as number) ?? 0, OrderResultsBy: (args.orderBy as string) ?? "CompanyName", OrderDirection: (args.orderDirection as string) ?? "ASC", }; if (args.companyName) { searchParams.CompanyName = args.companyName; } if (args.firstName) { searchParams.FirstName = args.firstName; } if (args.lastName) { searchParams.Surname = args.lastName; } if (args.email) { searchParams.Email = args.email; } if (args.telephone) { searchParams.Telephone = args.telephone; } return searchParams; } // ============================================================================= // Tool Handler // ============================================================================= export async function handleClientTool( toolName: string, args: Record<string, unknown>, ): Promise<ToolResult> { const apiClient = getApiClient(); try { switch (toolName) { case "quickfile_client_search": { const searchParams = buildSearchParams(args); const response = await apiClient.request< { SearchParameters: typeof searchParams }, ClientSearchResponse >("Client_Search", { SearchParameters: searchParams }); const clients = response.Record || []; return successResult({ totalRecords: response.RecordsetCount, returnedCount: response.ReturnCount, clients: clients, }); } case "quickfile_client_get": { const response = await apiClient.request< { ClientID: number }, ClientGetResponse >("Client_Get", { ClientID: args.clientId as number }); return successResult(response.ClientDetails); } case "quickfile_client_create": { const address = buildAddressFromArgs(args); const clientData = buildEntityData(args, address); const cleanData = cleanParams(clientData); const response = await apiClient.request< { ClientData: typeof cleanData }, ClientCreateResponse >("Client_Create", { ClientData: cleanData }); return successResult({ success: true, clientId: response.ClientID, message: `Client created successfully with ID ${response.ClientID}`, }); } case "quickfile_client_update": { const clientId = args.clientId as number; const address = buildAddressFromArgs(args); const entityData = buildEntityUpdateData(args, address); const updateData = { ClientID: clientId, ...entityData }; const cleanData = cleanParams(updateData); await apiClient.request< { ClientData: typeof cleanData }, Record<string, never> >("Client_Update", { ClientData: cleanData }); return successResult({ success: true, clientId, message: `Client #${clientId} updated successfully`, }); } case "quickfile_client_delete": { const clientId = args.clientId as number; await apiClient.request<{ ClientID: number }, Record<string, never>>( "Client_Delete", { ClientID: clientId }, ); return successResult({ success: true, clientId, message: `Client #${clientId} deleted successfully`, }); } case "quickfile_client_insert_contacts": { const contact: ClientContact = { FirstName: args.firstName as string, LastName: args.lastName as string, Email: args.email as string | undefined, Telephone: args.telephone as string | undefined, Mobile: args.mobile as string | undefined, IsPrimary: (args.isPrimary as boolean) ?? false, }; const response = await apiClient.request< { ClientID: number; Contact: ClientContact }, ContactInsertResponse >("Client_InsertContacts", { ClientID: args.clientId as number, Contact: contact, }); return successResult({ success: true, contactId: response.ContactID, message: `Contact added to client #${args.clientId}`, }); } case "quickfile_client_login_url": { const response = await apiClient.request< { ClientID: number }, ClientLoginResponse >("Client_LogIn", { ClientID: args.clientId as number }); return successResult({ clientId: args.clientId, loginUrl: response.LoginURL, message: "Passwordless login URL generated (valid for limited time)", }); } default: return { content: [{ type: "text", text: `Unknown client tool: ${toolName}` }], isError: true, }; } } catch (error) { return handleToolError(error); } }

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/marcusquinn/quickfile-mcp'

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