Jake Shore 5adccfd36e housecall-pro: Complete MCP server with 47 tools, 16 React apps, full API client
- API Client: Housecall Pro API with auth, pagination, error handling
- 47 Tools across 10 categories:
  - jobs-tools (10): list, get, create, update, complete, cancel, line items, schedule, reschedule
  - customers-tools (7): list, get, create, update, delete, search, addresses
  - estimates-tools (8): list, get, create, update, send, approve, decline, convert to job
  - invoices-tools (6): list, get, create, send, mark paid, list payments
  - employees-tools (6): list, get, create, update, schedule, time entries
  - dispatch-tools (3): dispatch board, assign employee, availability
  - tags-tools (5): list, create, delete, add to job/customer
  - notifications-tools (3): list, send, mark read
  - reviews-tools (3): list, get, request review
  - reporting-tools (3): revenue, job completion, employee performance
- 16 React MCP Apps for rich UI:
  - job-dashboard, job-detail, job-grid
  - customer-detail, customer-grid
  - estimate-builder, estimate-grid
  - invoice-dashboard, invoice-detail
  - dispatch-board
  - employee-schedule, employee-performance
  - review-dashboard, revenue-dashboard
  - tag-manager, notification-center
- Complete types, comprehensive README, full package.json
2026-02-12 17:39:57 -05:00

137 lines
3.5 KiB
TypeScript

/**
* Housecall Pro MCP Server
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { HousecallProClient } from './clients/housecall-pro.js';
import { registerAllTools } from './tools/index.js';
export class HousecallProServer {
private server: Server;
private client: HousecallProClient;
private tools: Record<string, any>;
constructor() {
this.server = new Server(
{
name: 'housecall-pro',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
// Initialize client (API key will be set from env var)
const apiKey = process.env.HOUSECALL_PRO_API_KEY;
if (!apiKey) {
throw new Error('HOUSECALL_PRO_API_KEY environment variable is required');
}
this.client = new HousecallProClient({
apiKey,
baseUrl: process.env.HOUSECALL_PRO_BASE_URL,
});
// Register all tools
this.tools = registerAllTools(this.client);
this.setupHandlers();
}
private setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: Object.entries(this.tools).map(([name, tool]) => ({
name,
description: tool.description,
inputSchema: tool.parameters,
})),
};
});
// Handle tool execution
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const tool = this.tools[name];
if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}
try {
const result = await tool.handler(args || {});
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error: any) {
// Handle API errors
if (error.error === 'APIError') {
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: error.error,
message: error.message,
status: error.status,
}, null, 2),
},
],
isError: true,
};
}
// Handle other errors
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: 'ExecutionError',
message: error.message || 'Tool execution failed',
details: error,
}, null, 2),
},
],
isError: true,
};
}
});
// List resources (empty for now, but could include templates, reports, etc.)
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [],
};
});
// Read resource (empty for now)
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
throw new Error(`Resource not found: ${request.params.uri}`);
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Housecall Pro MCP server running on stdio');
}
}