@talkspresso/mcp-server 1.4.2 → 1.5.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/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.4.2');
17
+ console.log('1.5.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.4.2',
45
+ version: '1.5.0',
46
46
  });
47
47
  const apiClient = new TalkspressoClient();
48
48
  registerAppointmentTools(server, apiClient);
@@ -18,6 +18,7 @@ export declare class SetupApiClient {
18
18
  about?: string;
19
19
  bio?: string;
20
20
  categories?: string[];
21
+ profile_handle?: string;
21
22
  }): Promise<any>;
22
23
  createService(data: {
23
24
  title: string;
@@ -26,6 +27,7 @@ export declare class SetupApiClient {
26
27
  duration: number;
27
28
  type: string;
28
29
  }): Promise<any>;
30
+ checkHandleAvailability(handle: string): Promise<boolean>;
29
31
  updateCalendar(data: {
30
32
  timezone?: string;
31
33
  availability?: Record<string, any>;
package/dist/setup-api.js CHANGED
@@ -73,6 +73,19 @@ export class SetupApiClient {
73
73
  throw this.formatError(err);
74
74
  }
75
75
  }
76
+ async checkHandleAvailability(handle) {
77
+ try {
78
+ await this.http.get('/profile/check_handle_availability', {
79
+ params: { profile_handle: handle },
80
+ });
81
+ return true; // 200 = available
82
+ }
83
+ catch (err) {
84
+ if (err.response?.status === 409)
85
+ return false; // taken
86
+ return false; // assume taken on error
87
+ }
88
+ }
76
89
  async updateCalendar(data) {
77
90
  try {
78
91
  const response = await this.http.put('/calendar', data);
package/dist/setup.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { confirm, input, password as passwordPrompt, select } from '@inquirer/prompts';
2
+ import { confirm, input, password as passwordPrompt } from '@inquirer/prompts';
3
3
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
4
4
  import { join } from 'path';
5
5
  import { homedir } from 'os';
@@ -49,40 +49,6 @@ function mcpEntry(apiKey) {
49
49
  env: { TALKSPRESSO_API_KEY: apiKey },
50
50
  };
51
51
  }
52
- const SERVICE_PRESETS = [
53
- { name: '1:1 Video Call (30 min, $100)', value: { title: '1:1 Video Call', description: 'A private 30-minute video session.', price: 100, duration: 30, type: 'video_call' } },
54
- { name: '1:1 Video Call (60 min, $200)', value: { title: '1:1 Video Call', description: 'A private 60-minute video session.', price: 200, duration: 60, type: 'video_call' } },
55
- { name: 'Group Workshop (60 min, $50)', value: { title: 'Group Workshop', description: 'A 60-minute interactive group session.', price: 50, duration: 60, type: 'workshop' } },
56
- { name: 'Custom (I\'ll set it up)', value: 'custom' },
57
- { name: 'Skip for now', value: 'skip' },
58
- ];
59
- const AVAILABILITY_PRESETS = [
60
- { name: 'Weekdays 9 AM - 5 PM', value: 'weekdays-9-5' },
61
- { name: 'Weekdays 10 AM - 6 PM', value: 'weekdays-10-6' },
62
- { name: 'Every day 9 AM - 9 PM', value: 'everyday-9-9' },
63
- { name: 'Skip for now', value: 'skip' },
64
- ];
65
- function buildAvailability(preset) {
66
- const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
67
- const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
68
- const make = (selectedDays, start, end) => {
69
- const result = {};
70
- for (const day of days) {
71
- result[day] = {
72
- is_selected: selectedDays.includes(day),
73
- start_time: start,
74
- end_time: end,
75
- };
76
- }
77
- return result;
78
- };
79
- switch (preset) {
80
- case 'weekdays-9-5': return make(weekdays, '09:00', '17:00');
81
- case 'weekdays-10-6': return make(weekdays, '10:00', '18:00');
82
- case 'everyday-9-9': return make(days, '09:00', '21:00');
83
- default: return null;
84
- }
85
- }
86
52
  export async function runSetup() {
87
53
  process.on('SIGINT', () => {
88
54
  console.log(`\n\n ${chalk.dim('Setup cancelled. Run')} npx @talkspresso/mcp-server --setup ${chalk.dim('anytime.')}\n`);
@@ -92,7 +58,7 @@ export async function runSetup() {
92
58
  // ── Welcome ──────────────────────────────────────────
93
59
  console.log(LOGO);
94
60
  console.log(` ${chalk.white.bold('Manage your business with AI.')}`);
95
- console.log(` ${chalk.dim("Let's get you set up in a few minutes.")}\n`);
61
+ console.log(` ${chalk.dim("Let's connect your account. Takes about 60 seconds.")}\n`);
96
62
  // ── Account ──────────────────────────────────────────
97
63
  let accessToken;
98
64
  let userName;
@@ -170,89 +136,40 @@ export async function runSetup() {
170
136
  console.log(` ${chalk.dim('Create one manually at')} ${chalk.cyan('app.talkspresso.com/settings/api-keys')}\n`);
171
137
  process.exit(1);
172
138
  }
173
- // ── Profile Setup ────────────────────────────────────
174
- console.log(chalk.white.bold(' Set up your profile\n'));
175
- const expertTitle = await input({
176
- message: 'Your professional title (e.g., "Executive Coach", "Fitness Creator"):',
177
- });
178
- const about = await input({
179
- message: 'One line about what you offer:',
180
- });
181
- if (expertTitle || about) {
182
- try {
183
- await api.updateProfile({
184
- expert_title: expertTitle || undefined,
185
- about: about || undefined,
186
- });
187
- console.log(` ${chalk.green('✓')} Profile updated\n`);
188
- }
189
- catch {
190
- console.log(` ${chalk.yellow('!')} Could not update profile. You can update it later.\n`);
191
- }
192
- }
193
- // ── First Service ────────────────────────────────────
194
- console.log(chalk.white.bold(' Create your first offering\n'));
195
- console.log(` ${chalk.dim('This is what people will book with you.')}\n`);
196
- const serviceChoice = await select({
197
- message: 'Pick a starting template:',
198
- choices: SERVICE_PRESETS,
199
- });
200
- if (serviceChoice === 'custom') {
201
- const title = await input({ message: 'Service title:' });
202
- const description = await input({ message: 'Short description:' });
203
- const priceStr = await input({ message: 'Price in dollars (0 for free):' });
204
- const durationStr = await input({ message: 'Duration in minutes:' });
205
- const type = await select({
206
- message: 'Service type:',
207
- choices: [
208
- { name: '1:1 Video Call', value: 'video_call' },
209
- { name: 'Group Session', value: 'group_session' },
210
- { name: 'Workshop', value: 'workshop' },
211
- { name: 'Webinar', value: 'webinar' },
212
- ],
139
+ // ── Handle / Vanity URL ────────────────────────────
140
+ let profileHandle = '';
141
+ const defaultHandle = userName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
142
+ console.log(` ${chalk.dim('Your booking page will be:')} ${chalk.cyan(`app.talkspresso.com/`)}${chalk.cyan.bold('<your-handle>')}\n`);
143
+ let handleAttempts = 0;
144
+ while (handleAttempts < 5) {
145
+ const handle = await input({
146
+ message: 'Choose your handle:',
147
+ default: handleAttempts === 0 ? defaultHandle : undefined,
213
148
  });
214
- try {
215
- await api.createService({
216
- title,
217
- description: description || `A ${durationStr}-minute ${type.replace('_', ' ')} session.`,
218
- price: parseInt(priceStr) || 0,
219
- duration: parseInt(durationStr) || 30,
220
- type,
221
- });
222
- console.log(` ${chalk.green('✓')} Service "${title}" created\n`);
149
+ const cleaned = handle.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
150
+ if (!cleaned || cleaned.length < 3) {
151
+ console.log(` ${chalk.red('✗')} Handle must be at least 3 characters.\n`);
152
+ handleAttempts++;
153
+ continue;
223
154
  }
224
- catch (err) {
225
- console.log(` ${chalk.yellow('!')} Could not create service: ${err.message}. You can create one later.\n`);
155
+ const available = await api.checkHandleAvailability(cleaned);
156
+ if (available) {
157
+ profileHandle = cleaned;
158
+ console.log(` ${chalk.green('✓')} ${chalk.cyan(`app.talkspresso.com/${cleaned}`)} is yours!\n`);
159
+ break;
160
+ }
161
+ else {
162
+ console.log(` ${chalk.red('✗')} "${cleaned}" is taken. Try another.\n`);
163
+ handleAttempts++;
226
164
  }
227
165
  }
228
- else if (serviceChoice !== 'skip') {
166
+ // Save handle to profile
167
+ if (profileHandle) {
229
168
  try {
230
- const preset = serviceChoice;
231
- await api.createService(preset);
232
- console.log(` ${chalk.green('✓')} Service "${preset.title}" created ($${preset.price}, ${preset.duration} min)\n`);
233
- }
234
- catch (err) {
235
- console.log(` ${chalk.yellow('!')} Could not create service: ${err.message}. You can create one later.\n`);
169
+ await api.updateProfile({ profile_handle: profileHandle });
236
170
  }
237
- }
238
- // ── Availability ─────────────────────────────────────
239
- console.log(chalk.white.bold(' Set your availability\n'));
240
- const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
241
- console.log(` ${chalk.dim(`Detected timezone: ${timezone}`)}\n`);
242
- const availPreset = await select({
243
- message: 'When are you available for sessions?',
244
- choices: AVAILABILITY_PRESETS,
245
- });
246
- if (availPreset !== 'skip') {
247
- const availability = buildAvailability(availPreset);
248
- if (availability) {
249
- try {
250
- await api.updateCalendar({ timezone, availability });
251
- console.log(` ${chalk.green('✓')} Availability set\n`);
252
- }
253
- catch {
254
- console.log(` ${chalk.yellow('!')} Could not set availability. You can update it later.\n`);
255
- }
171
+ catch {
172
+ // Non-fatal, handle might already be set
256
173
  }
257
174
  }
258
175
  // ── Configure Claude Desktop ─────────────────────────
@@ -295,16 +212,12 @@ export async function runSetup() {
295
212
  }
296
213
  }
297
214
  // ── Success ──────────────────────────────────────────
298
- console.log(chalk.green.bold('\n You\'re all set!\n'));
215
+ console.log(chalk.green.bold('\n You\'re connected!\n'));
299
216
  const done = [];
300
217
  done.push(`Account: ${userEmail}`);
301
218
  done.push(`API key saved`);
302
- if (expertTitle)
303
- done.push(`Profile: ${expertTitle}`);
304
- if (serviceChoice !== 'skip')
305
- done.push('First service created');
306
- if (availPreset !== 'skip')
307
- done.push('Availability configured');
219
+ if (profileHandle)
220
+ done.push(`Handle: app.talkspresso.com/${profileHandle}`);
308
221
  if (configuredDesktop)
309
222
  done.push('Claude Desktop configured');
310
223
  if (configuredCode)
@@ -312,15 +225,19 @@ export async function runSetup() {
312
225
  for (const item of done) {
313
226
  console.log(` ${chalk.green('✓')} ${item}`);
314
227
  }
315
- console.log(`\n ${chalk.bold('What to do next:')}\n`);
228
+ console.log(`\n ${chalk.bold('Now the fun part:')}\n`);
316
229
  if (configuredDesktop || configuredCode) {
317
- console.log(` 1. ${chalk.white('Restart Claude')} (if it's open)`);
318
- console.log(` 2. ${chalk.white('Try:')} "Show me my Talkspresso schedule"`);
319
- console.log(` 3. ${chalk.white('Try:')} "Connect my Stripe account so I can get paid"`);
320
- console.log(` 4. ${chalk.white('Try:')} "Get my booking link so I can share it"`);
230
+ console.log(` 1. ${chalk.white('Restart Claude')} (quit and reopen)`);
231
+ console.log(` 2. ${chalk.white('Say:')} ${chalk.cyan('"Help me set up my Talkspresso business"')}`);
232
+ console.log();
233
+ console.log(` ${chalk.dim('Brew, your AI business advisor, will:')}`);
234
+ console.log(` ${chalk.dim(' - Ask about what you do')}`);
235
+ console.log(` ${chalk.dim(' - Suggest sessions and pricing for your niche')}`);
236
+ console.log(` ${chalk.dim(' - Create your services and set up your availability')}`);
237
+ console.log(` ${chalk.dim(' - Get your booking page ready to share')}`);
321
238
  }
322
239
  else {
323
- console.log(` 1. ${chalk.white('Run')} ${chalk.cyan('npx @talkspresso/mcp-server --setup')} ${chalk.white('to connect to Claude')}`);
240
+ console.log(` Run ${chalk.cyan('npx @talkspresso/mcp-server --setup')} to connect to Claude.`);
324
241
  }
325
- console.log(`\n ${chalk.dim('Need help? Ask Claude: "What can I do with Talkspresso?"')}\n`);
242
+ console.log(`\n ${chalk.dim('Your entire business, built through conversation.')}\n`);
326
243
  }
@@ -5,11 +5,24 @@ export function registerBrewTools(server, client) {
5
5
  const data = await client.get('/guide/status');
6
6
  return formatMcpResponse(data);
7
7
  });
8
- server.tool('start-brew-conversation', 'Start a new conversation with Brew or resume your existing one. Brew is your AI business mentor.', {}, async () => {
8
+ server.tool('start-brew-conversation', `Start a new conversation with Brew or resume your existing one. Brew is your AI business mentor on Talkspresso.
9
+
10
+ IMPORTANT FOR NEW USERS: If the user just set up their account or says "help me set up my business", start a Brew conversation first. Brew will detect they are new and guide them through:
11
+ 1. Understanding what they do and who they serve
12
+ 2. Suggesting personalized session types and pricing for their niche
13
+ 3. Creating their first services (use confirm-brew-action to execute proposals)
14
+ 4. Setting up availability and profile
15
+ 5. Getting their booking page ready to share
16
+
17
+ Brew knows the user's full context (profile, services, bookings, stage) and gives niche-specific advice.`, {}, async () => {
9
18
  const data = await client.post('/guide/start');
10
19
  return formatMcpResponse(data);
11
20
  });
12
- server.tool('send-brew-message', 'Send a message to Brew and get AI-powered business advice. Brew can help with pricing, services, profile optimization, and growth strategy.', {
21
+ server.tool('send-brew-message', `Send a message to Brew and get AI-powered business advice. Brew can help with pricing, services, profile optimization, and growth strategy.
22
+
23
+ When Brew proposes an action (like creating a service or updating availability), the response will include a write_proposal with actionData and a messageId. To execute the proposed action, call confirm-brew-action with the conversationId, actionType, and messageId.
24
+
25
+ Brew can propose these actions: create_service, update_service, convert_service_type, add_package_session, update_service_capacity, update_availability, update_profile, connect_stripe, create_appointment, cancel_appointment.`, {
13
26
  conversationId: z.string().describe('The Brew conversation ID (get from start-brew-conversation)'),
14
27
  message: z.string().describe('Your message to Brew'),
15
28
  }, async (params) => {
@@ -18,6 +31,21 @@ export function registerBrewTools(server, client) {
18
31
  });
19
32
  return formatMcpResponse(data);
20
33
  });
34
+ server.tool('confirm-brew-action', `Execute a proposed action from Brew. When Brew suggests creating a service, updating availability, or making other changes, it returns a write_proposal with a messageId. Call this tool to confirm and execute that action.
35
+
36
+ Supported action types: create_service, update_service, convert_service_type, add_package_session, update_service_capacity, update_availability, update_profile, connect_stripe, create_appointment, cancel_appointment.
37
+
38
+ Always confirm with the user before executing Brew's proposals.`, {
39
+ conversationId: z.string().describe('The Brew conversation ID'),
40
+ actionType: z.string().describe('The action type from the write_proposal (e.g., "create_service", "update_availability")'),
41
+ messageId: z.string().describe('The message ID from the write_proposal response'),
42
+ }, async (params) => {
43
+ const data = await client.post(`/guide/${params.conversationId}/confirm-action`, {
44
+ actionType: params.actionType,
45
+ messageId: params.messageId,
46
+ });
47
+ return formatMcpResponse(data);
48
+ });
21
49
  server.tool('get-brew-conversation-history', 'Get the message history for a Brew conversation.', {
22
50
  conversationId: z.string().describe('The Brew conversation ID'),
23
51
  }, async (params) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talkspresso/mcp-server",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "description": "Manage your Talkspresso business through Claude Code or any MCP-compatible AI assistant",
5
5
  "bin": {
6
6
  "talkspresso-mcp": "./dist/index.js"