@talkspresso/mcp-server 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export declare class TalkspressoClient {
2
2
  private http;
3
+ private _providerId;
3
4
  constructor();
4
5
  get<T = any>(path: string, params?: Record<string, any>): Promise<T>;
5
6
  post<T = any>(path: string, data?: any): Promise<T>;
6
7
  put<T = any>(path: string, data?: any): Promise<T>;
7
8
  delete<T = any>(path: string): Promise<T>;
9
+ getProviderId(): Promise<string>;
8
10
  private formatError;
9
11
  }
package/dist/client.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import axios from 'axios';
2
2
  export class TalkspressoClient {
3
3
  http;
4
+ _providerId = null;
4
5
  constructor() {
5
6
  const apiKey = process.env.TALKSPRESSO_API_KEY;
6
7
  if (!apiKey) {
@@ -52,6 +53,13 @@ export class TalkspressoClient {
52
53
  throw this.formatError(err);
53
54
  }
54
55
  }
56
+ async getProviderId() {
57
+ if (this._providerId)
58
+ return this._providerId;
59
+ const profile = await this.get('/profile');
60
+ this._providerId = profile.id || profile.user_id;
61
+ return this._providerId;
62
+ }
55
63
  formatError(err) {
56
64
  const data = err.response?.data;
57
65
  const status = err.response?.status;
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ else if (args.includes('--help')) {
14
14
  process.exit(0);
15
15
  }
16
16
  else if (args.includes('--version')) {
17
- console.log('1.1.0');
17
+ console.log('1.3.0');
18
18
  process.exit(0);
19
19
  }
20
20
  else {
@@ -42,7 +42,7 @@ else {
42
42
  const { registerSubscriptionTools } = await import('./tools/subscription.js');
43
43
  const server = new McpServer({
44
44
  name: 'talkspresso',
45
- version: '1.2.0',
45
+ version: '1.3.0',
46
46
  });
47
47
  const apiClient = new TalkspressoClient();
48
48
  registerAppointmentTools(server, apiClient);
@@ -1,4 +1,19 @@
1
1
  import { z } from 'zod';
2
+ function trimAppointment(apt) {
3
+ return {
4
+ id: apt.id,
5
+ status: apt.status,
6
+ start_time: apt.start_time,
7
+ end_time: apt.end_time,
8
+ duration: apt.duration,
9
+ client_name: apt.client?.name || apt.client_name,
10
+ client_email: apt.client?.email || apt.client_email,
11
+ service_title: apt.service?.title || apt.service_title,
12
+ service_price: apt.service?.price,
13
+ is_complimentary: apt.is_complimentary,
14
+ invitation_sent: apt.invitation_sent,
15
+ };
16
+ }
2
17
  export function registerAppointmentTools(server, client) {
3
18
  server.tool('list-appointments', 'Get your appointments with optional filters. Returns upcoming appointments by default.', {
4
19
  status: z.enum(['upcoming', 'completed', 'all']).optional().describe('Filter by appointment status (default: upcoming)'),
@@ -6,8 +21,11 @@ export function registerAppointmentTools(server, client) {
6
21
  limit: z.number().optional().describe('Number of results per page'),
7
22
  }, async (params) => {
8
23
  const data = await client.get('/appointments/me', params);
24
+ // Trim response to essential fields to avoid exceeding token limits
25
+ const appointments = Array.isArray(data) ? data : data?.appointments || data?.rows || [];
26
+ const trimmed = appointments.map(trimAppointment);
9
27
  return {
10
- content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
28
+ content: [{ type: 'text', text: JSON.stringify(trimmed, null, 2) }],
11
29
  };
12
30
  });
13
31
  server.tool('get-appointment', 'Get detailed information about a specific appointment', {
@@ -43,7 +61,7 @@ export function registerAppointmentTools(server, client) {
43
61
  content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
44
62
  };
45
63
  });
46
- server.tool('create-appointment', 'Create an appointment and send an invitation to the client. Use list-clients first to find the client by name if needed. Use list-services to find a service_id if linking to a specific service.', {
64
+ server.tool('create-appointment', 'Create an appointment WITHOUT sending the invitation email. Returns appointment details for review. After reviewing, use send-appointment-invite to send the email to the client.', {
47
65
  client_name: z.string().describe('Client full name'),
48
66
  client_email: z.string().optional().describe('Client email address (invitation sent here)'),
49
67
  scheduled_date: z.string().describe('Date in YYYY-MM-DD format'),
@@ -53,7 +71,7 @@ export function registerAppointmentTools(server, client) {
53
71
  custom_title: z.string().optional().describe('Custom session title (overrides service title)'),
54
72
  custom_price: z.number().optional().describe('Price in dollars (required if no service_id and not complimentary)'),
55
73
  is_complimentary: z.boolean().optional().describe('If true, session is free (default: true)'),
56
- invitation_message: z.string().optional().describe('Personal message included in the invitation email'),
74
+ invitation_message: z.string().optional().describe('Personal message to include when the invitation is sent'),
57
75
  }, async (params) => {
58
76
  const data = await client.post('/appointments/invite', {
59
77
  client_name: params.client_name,
@@ -66,6 +84,21 @@ export function registerAppointmentTools(server, client) {
66
84
  custom_price: params.custom_price,
67
85
  is_complimentary: params.is_complimentary ?? true,
68
86
  invitation_message: params.invitation_message,
87
+ skip_email: true,
88
+ });
89
+ return {
90
+ content: [{ type: 'text', text: JSON.stringify({
91
+ ...data,
92
+ _note: 'Appointment created. Email NOT sent yet. Use send-appointment-invite to send the invitation email after reviewing.',
93
+ }, null, 2) }],
94
+ };
95
+ });
96
+ server.tool('send-appointment-invite', 'Send the invitation email for an existing appointment. Use this after create-appointment to send the invite once the provider has reviewed the details.', {
97
+ id: z.string().describe('The appointment ID to send the invitation for'),
98
+ invitation_message: z.string().optional().describe('Personal message to include in the invitation email (overrides the one from create-appointment)'),
99
+ }, async (params) => {
100
+ const data = await client.post(`/appointments/${params.id}/resend-invite`, {
101
+ invitation_message: params.invitation_message,
69
102
  });
70
103
  return {
71
104
  content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
@@ -97,10 +130,12 @@ export function registerAppointmentTools(server, client) {
97
130
  service_id: z.string().optional().describe('Service ID to check availability for (determines duration). Use list-services to find this.'),
98
131
  duration: z.number().optional().describe('Duration in minutes if no service_id (default 30)'),
99
132
  }, async (params) => {
133
+ const providerId = await client.getProviderId();
100
134
  const data = await client.post('/appointments/slots', {
101
135
  date: params.date,
102
136
  service_id: params.service_id,
103
- duration: params.duration || 30,
137
+ interval: params.duration || 30,
138
+ provider_id: providerId,
104
139
  });
105
140
  return {
106
141
  content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
@@ -12,8 +12,8 @@ export function registerEarningsTools(server, client) {
12
12
  });
13
13
  server.tool('get-revenue-summary', 'Get a quick revenue summary: this month earnings, total bookings, new clients, and completed sessions. Use this for a "how is this month going?" check.', {}, async () => {
14
14
  const [appointments, earnings] = await Promise.all([
15
- client.get('/appointments/me', { status: 'all', limit: 200 }),
16
- client.get('/transaction/my', { limit: 200 }),
15
+ client.get('/appointments/me', { status: 'all', limit: 100 }),
16
+ client.get('/transaction/my', { limit: 100 }),
17
17
  ]);
18
18
  const now = new Date();
19
19
  const monthStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talkspresso/mcp-server",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Manage your Talkspresso business through Claude, ChatGPT, or any MCP-compatible AI",
5
5
  "bin": {
6
6
  "talkspresso-mcp": "./dist/index.js"