@talkspresso/mcp-server 1.0.0 → 1.2.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 +69 -20
- package/dist/setup-api.d.ts +18 -0
- package/dist/setup-api.js +55 -0
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +199 -0
- package/dist/tools/appointments.js +38 -0
- package/dist/tools/brew.d.ts +3 -0
- package/dist/tools/brew.js +85 -0
- package/dist/tools/calendar.d.ts +3 -0
- package/dist/tools/calendar.js +20 -0
- package/dist/tools/clients.js +16 -0
- package/dist/tools/earnings.js +25 -0
- package/dist/tools/fileLibrary.d.ts +3 -0
- package/dist/tools/fileLibrary.js +58 -0
- package/dist/tools/googleCalendar.d.ts +3 -0
- package/dist/tools/googleCalendar.js +37 -0
- package/dist/tools/live.d.ts +3 -0
- package/dist/tools/live.js +44 -0
- package/dist/tools/messaging.d.ts +3 -0
- package/dist/tools/messaging.js +52 -0
- package/dist/tools/notifications.d.ts +3 -0
- package/dist/tools/notifications.js +25 -0
- package/dist/tools/organization.d.ts +3 -0
- package/dist/tools/organization.js +68 -0
- package/dist/tools/prequalification.d.ts +3 -0
- package/dist/tools/prequalification.js +95 -0
- package/dist/tools/products.d.ts +3 -0
- package/dist/tools/products.js +86 -0
- package/dist/tools/profile.js +26 -0
- package/dist/tools/promoCodes.d.ts +3 -0
- package/dist/tools/promoCodes.js +68 -0
- package/dist/tools/recordings.d.ts +3 -0
- package/dist/tools/recordings.js +76 -0
- package/dist/tools/services.js +32 -0
- package/dist/tools/subscription.d.ts +3 -0
- package/dist/tools/subscription.js +31 -0
- package/dist/tools/testimonials.d.ts +3 -0
- package/dist/tools/testimonials.js +72 -0
- package/package.json +12 -4
package/dist/index.js
CHANGED
|
@@ -1,21 +1,70 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
version
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
await server.
|
|
2
|
+
const args = process.argv.slice(2);
|
|
3
|
+
if (args.includes('--setup')) {
|
|
4
|
+
const { runSetup } = await import('./setup.js');
|
|
5
|
+
await runSetup();
|
|
6
|
+
}
|
|
7
|
+
else if (args.includes('--help')) {
|
|
8
|
+
console.log('Usage: talkspresso-mcp [options]\n');
|
|
9
|
+
console.log('Options:');
|
|
10
|
+
console.log(' --setup Interactive setup wizard');
|
|
11
|
+
console.log(' --help Show this help message');
|
|
12
|
+
console.log(' --version Show version number');
|
|
13
|
+
console.log('\nWith no options, starts the MCP server (stdio transport).');
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
else if (args.includes('--version')) {
|
|
17
|
+
console.log('1.1.0');
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
|
|
22
|
+
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
23
|
+
const { TalkspressoClient } = await import('./client.js');
|
|
24
|
+
const { registerAppointmentTools } = await import('./tools/appointments.js');
|
|
25
|
+
const { registerClientTools } = await import('./tools/clients.js');
|
|
26
|
+
const { registerServiceTools } = await import('./tools/services.js');
|
|
27
|
+
const { registerEarningsTools } = await import('./tools/earnings.js');
|
|
28
|
+
const { registerProfileTools } = await import('./tools/profile.js');
|
|
29
|
+
const { registerCalendarTools } = await import('./tools/calendar.js');
|
|
30
|
+
const { registerNotificationTools } = await import('./tools/notifications.js');
|
|
31
|
+
const { registerTestimonialTools } = await import('./tools/testimonials.js');
|
|
32
|
+
const { registerRecordingTools } = await import('./tools/recordings.js');
|
|
33
|
+
const { registerOrganizationTools } = await import('./tools/organization.js');
|
|
34
|
+
const { registerProductTools } = await import('./tools/products.js');
|
|
35
|
+
const { registerPromoCodeTools } = await import('./tools/promoCodes.js');
|
|
36
|
+
const { registerFileLibraryTools } = await import('./tools/fileLibrary.js');
|
|
37
|
+
const { registerPrequalificationTools } = await import('./tools/prequalification.js');
|
|
38
|
+
const { registerMessagingTools } = await import('./tools/messaging.js');
|
|
39
|
+
const { registerLiveTools } = await import('./tools/live.js');
|
|
40
|
+
const { registerGoogleCalendarTools } = await import('./tools/googleCalendar.js');
|
|
41
|
+
const { registerBrewTools } = await import('./tools/brew.js');
|
|
42
|
+
const { registerSubscriptionTools } = await import('./tools/subscription.js');
|
|
43
|
+
const server = new McpServer({
|
|
44
|
+
name: 'talkspresso',
|
|
45
|
+
version: '1.2.0',
|
|
46
|
+
});
|
|
47
|
+
const apiClient = new TalkspressoClient();
|
|
48
|
+
registerAppointmentTools(server, apiClient);
|
|
49
|
+
registerClientTools(server, apiClient);
|
|
50
|
+
registerServiceTools(server, apiClient);
|
|
51
|
+
registerEarningsTools(server, apiClient);
|
|
52
|
+
registerProfileTools(server, apiClient);
|
|
53
|
+
registerCalendarTools(server, apiClient);
|
|
54
|
+
registerNotificationTools(server, apiClient);
|
|
55
|
+
registerTestimonialTools(server, apiClient);
|
|
56
|
+
registerRecordingTools(server, apiClient);
|
|
57
|
+
registerOrganizationTools(server, apiClient);
|
|
58
|
+
registerProductTools(server, apiClient);
|
|
59
|
+
registerPromoCodeTools(server, apiClient);
|
|
60
|
+
registerFileLibraryTools(server, apiClient);
|
|
61
|
+
registerPrequalificationTools(server, apiClient);
|
|
62
|
+
registerMessagingTools(server, apiClient);
|
|
63
|
+
registerLiveTools(server, apiClient);
|
|
64
|
+
registerGoogleCalendarTools(server, apiClient);
|
|
65
|
+
registerBrewTools(server, apiClient);
|
|
66
|
+
registerSubscriptionTools(server, apiClient);
|
|
67
|
+
const transport = new StdioServerTransport();
|
|
68
|
+
await server.connect(transport);
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class SetupApiClient {
|
|
2
|
+
private http;
|
|
3
|
+
constructor();
|
|
4
|
+
setAccessToken(token: string): void;
|
|
5
|
+
register(data: {
|
|
6
|
+
name: string;
|
|
7
|
+
email: string;
|
|
8
|
+
password: string;
|
|
9
|
+
timezone: string;
|
|
10
|
+
}): Promise<any>;
|
|
11
|
+
login(data: {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
}): Promise<any>;
|
|
15
|
+
createApiKey(name: string): Promise<any>;
|
|
16
|
+
getStatus(err: unknown): number | undefined;
|
|
17
|
+
private formatError;
|
|
18
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class SetupApiClient {
|
|
3
|
+
http;
|
|
4
|
+
constructor() {
|
|
5
|
+
const baseURL = process.env.TALKSPRESSO_API_URL || 'https://api.talkspresso.com';
|
|
6
|
+
this.http = axios.create({
|
|
7
|
+
baseURL,
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
timeout: 30000,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
setAccessToken(token) {
|
|
13
|
+
this.http.defaults.headers['Authorization'] = `Bearer ${token}`;
|
|
14
|
+
}
|
|
15
|
+
async register(data) {
|
|
16
|
+
try {
|
|
17
|
+
const response = await this.http.post('/user/register', data);
|
|
18
|
+
return response.data?.data;
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
throw this.formatError(err);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async login(data) {
|
|
25
|
+
try {
|
|
26
|
+
const response = await this.http.post('/user/login', data);
|
|
27
|
+
return response.data?.data;
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
throw this.formatError(err);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async createApiKey(name) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await this.http.post('/api-keys', { name });
|
|
36
|
+
return response.data?.data;
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
throw this.formatError(err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
getStatus(err) {
|
|
43
|
+
if (err instanceof Error && 'status' in err)
|
|
44
|
+
return err.status;
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
formatError(err) {
|
|
48
|
+
const data = err.response?.data;
|
|
49
|
+
const status = err.response?.status;
|
|
50
|
+
const message = data?.error || err.message || 'API request failed';
|
|
51
|
+
const error = new Error(message);
|
|
52
|
+
error.status = status;
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSetup(): Promise<void>;
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { confirm, input, password as passwordPrompt } from '@inquirer/prompts';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
import { SetupApiClient } from './setup-api.js';
|
|
7
|
+
const LOGO = `
|
|
8
|
+
${chalk.hex('#032B43').bold('╔╦╗')}${chalk.hex('#0A4B6E')('┌─┐┬ ┬┌─┌─┐┌─┐┬─┐┌─┐┌─┐┌─┐┌─┐')}
|
|
9
|
+
${chalk.hex('#032B43').bold(' ║ ')}${chalk.hex('#0A4B6E')('├─┤│ ├┴┐└─┐├─┘├┬┘├┤ └─┐└─┐│ │')}
|
|
10
|
+
${chalk.hex('#032B43').bold(' ╩ ')}${chalk.hex('#0A4B6E')('┴ ┴┴─┘┴ ┴└─┘┴ ┴└─└─┘└─┘└─┘└─┘')}
|
|
11
|
+
`;
|
|
12
|
+
function getClaudeDesktopConfigPath() {
|
|
13
|
+
const home = homedir();
|
|
14
|
+
if (process.platform === 'darwin') {
|
|
15
|
+
return join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
16
|
+
}
|
|
17
|
+
else if (process.platform === 'win32') {
|
|
18
|
+
return join(process.env.APPDATA || join(home, 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
|
|
19
|
+
}
|
|
20
|
+
return join(home, '.config', 'Claude', 'claude_desktop_config.json');
|
|
21
|
+
}
|
|
22
|
+
function getClaudeCodeConfigPath() {
|
|
23
|
+
return join(homedir(), '.claude', 'mcp_servers.json');
|
|
24
|
+
}
|
|
25
|
+
function readJsonSafe(path) {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function writeJsonSafe(path, data) {
|
|
34
|
+
try {
|
|
35
|
+
const dir = join(path, '..');
|
|
36
|
+
if (!existsSync(dir))
|
|
37
|
+
mkdirSync(dir, { recursive: true });
|
|
38
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf-8');
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function mcpEntry(apiKey) {
|
|
46
|
+
return {
|
|
47
|
+
command: 'npx',
|
|
48
|
+
args: ['-y', '@talkspresso/mcp-server'],
|
|
49
|
+
env: { TALKSPRESSO_API_KEY: apiKey },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function runSetup() {
|
|
53
|
+
process.on('SIGINT', () => {
|
|
54
|
+
console.log(`\n\n ${chalk.dim('Setup cancelled. Run')} npx @talkspresso/mcp-server --setup ${chalk.dim('anytime.')}\n`);
|
|
55
|
+
process.exit(0);
|
|
56
|
+
});
|
|
57
|
+
const api = new SetupApiClient();
|
|
58
|
+
// Welcome
|
|
59
|
+
console.log(LOGO);
|
|
60
|
+
console.log(` ${chalk.white.bold('Manage your business with AI.')}`);
|
|
61
|
+
console.log(` ${chalk.dim("Let's get you connected in about 30 seconds.")}\n`);
|
|
62
|
+
// Account
|
|
63
|
+
let accessToken;
|
|
64
|
+
let userName;
|
|
65
|
+
let userEmail;
|
|
66
|
+
const hasAccount = await confirm({ message: 'Do you already have a Talkspresso account?' });
|
|
67
|
+
if (hasAccount) {
|
|
68
|
+
// Login flow
|
|
69
|
+
let attempts = 0;
|
|
70
|
+
while (true) {
|
|
71
|
+
const email = await input({ message: 'Email:' });
|
|
72
|
+
const pw = await passwordPrompt({ message: 'Password:' });
|
|
73
|
+
try {
|
|
74
|
+
const user = await api.login({ email, password: pw });
|
|
75
|
+
accessToken = user.access_token;
|
|
76
|
+
userName = user.name || email.split('@')[0];
|
|
77
|
+
userEmail = email;
|
|
78
|
+
console.log(` ${chalk.green('✓')} Logged in as ${chalk.bold(email)}\n`);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
attempts++;
|
|
83
|
+
if (attempts >= 3) {
|
|
84
|
+
console.log(`\n ${chalk.red('Too many failed attempts.')} Create an account at ${chalk.cyan('app.talkspresso.com')}\n`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
console.log(` ${chalk.red('✗')} Invalid email or password. Try again.\n`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Registration flow
|
|
93
|
+
const name = await input({ message: 'Your name:' });
|
|
94
|
+
const email = await input({ message: 'Email:' });
|
|
95
|
+
const pw = await passwordPrompt({ message: 'Password (min 8 characters):' });
|
|
96
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
97
|
+
try {
|
|
98
|
+
const user = await api.register({ name, email, password: pw, timezone });
|
|
99
|
+
accessToken = user.access_token;
|
|
100
|
+
userName = name;
|
|
101
|
+
userEmail = email;
|
|
102
|
+
console.log(` ${chalk.green('✓')} Account created for ${chalk.bold(email)}`);
|
|
103
|
+
console.log(` ${chalk.dim(`Timezone: ${timezone}`)}\n`);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const status = api.getStatus(err);
|
|
107
|
+
if (status === 409) {
|
|
108
|
+
console.log(`\n ${chalk.yellow('Account already exists.')} Let's log you in instead.\n`);
|
|
109
|
+
const pw2 = await passwordPrompt({ message: 'Password:' });
|
|
110
|
+
try {
|
|
111
|
+
const user = await api.login({ email, password: pw2 });
|
|
112
|
+
accessToken = user.access_token;
|
|
113
|
+
userName = user.name || name;
|
|
114
|
+
userEmail = email;
|
|
115
|
+
console.log(` ${chalk.green('✓')} Logged in as ${chalk.bold(email)}\n`);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
console.log(`\n ${chalk.red('Could not log in.')} Try again at ${chalk.cyan('app.talkspresso.com')}\n`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log(`\n ${chalk.red('Could not create account:')} ${err.message}\n`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Generate API key
|
|
129
|
+
api.setAccessToken(accessToken);
|
|
130
|
+
let apiKey;
|
|
131
|
+
try {
|
|
132
|
+
const keyData = await api.createApiKey('Claude AI Assistant');
|
|
133
|
+
apiKey = keyData.key;
|
|
134
|
+
console.log(` ${chalk.green('✓')} API key created: ${chalk.dim(apiKey.substring(0, 12) + '...')}\n`);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.log(` ${chalk.red('Could not create API key:')} ${err.message}`);
|
|
138
|
+
console.log(` ${chalk.dim('Create one manually at')} ${chalk.cyan('app.talkspresso.com/settings/api-keys')}\n`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
// Configure Claude Desktop
|
|
142
|
+
const desktopPath = getClaudeDesktopConfigPath();
|
|
143
|
+
const desktopDir = join(desktopPath, '..');
|
|
144
|
+
let configuredDesktop = false;
|
|
145
|
+
if (existsSync(desktopDir)) {
|
|
146
|
+
const addDesktop = await confirm({ message: 'Add Talkspresso to Claude Desktop?' });
|
|
147
|
+
if (addDesktop) {
|
|
148
|
+
const existing = readJsonSafe(desktopPath) || {};
|
|
149
|
+
if (!existing.mcpServers)
|
|
150
|
+
existing.mcpServers = {};
|
|
151
|
+
existing.mcpServers.talkspresso = mcpEntry(apiKey);
|
|
152
|
+
configuredDesktop = writeJsonSafe(desktopPath, existing);
|
|
153
|
+
if (configuredDesktop) {
|
|
154
|
+
console.log(` ${chalk.green('✓')} Claude Desktop configured\n`);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
console.log(` ${chalk.red('✗')} Could not write config. Add manually:\n`);
|
|
158
|
+
console.log(chalk.dim(JSON.stringify({ mcpServers: { talkspresso: mcpEntry(apiKey) } }, null, 2)));
|
|
159
|
+
console.log();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Configure Claude Code
|
|
164
|
+
const codePath = getClaudeCodeConfigPath();
|
|
165
|
+
let configuredCode = false;
|
|
166
|
+
const addCode = await confirm({ message: 'Add Talkspresso to Claude Code?' });
|
|
167
|
+
if (addCode) {
|
|
168
|
+
const existing = readJsonSafe(codePath) || {};
|
|
169
|
+
existing.talkspresso = mcpEntry(apiKey);
|
|
170
|
+
configuredCode = writeJsonSafe(codePath, existing);
|
|
171
|
+
if (configuredCode) {
|
|
172
|
+
console.log(` ${chalk.green('✓')} Claude Code configured\n`);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.log(` ${chalk.red('✗')} Could not write config. Add manually:\n`);
|
|
176
|
+
console.log(chalk.dim(JSON.stringify({ talkspresso: mcpEntry(apiKey) }, null, 2)));
|
|
177
|
+
console.log();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Success
|
|
181
|
+
console.log(chalk.green.bold('\n You\'re all set!\n'));
|
|
182
|
+
const done = [];
|
|
183
|
+
done.push(`Account: ${userEmail}`);
|
|
184
|
+
done.push(`API key generated and saved`);
|
|
185
|
+
if (configuredDesktop)
|
|
186
|
+
done.push('Claude Desktop configured');
|
|
187
|
+
if (configuredCode)
|
|
188
|
+
done.push('Claude Code configured');
|
|
189
|
+
for (const item of done) {
|
|
190
|
+
console.log(` ${chalk.green('✓')} ${item}`);
|
|
191
|
+
}
|
|
192
|
+
console.log(`\n ${chalk.bold('Next steps:')}\n`);
|
|
193
|
+
if (configuredDesktop || configuredCode) {
|
|
194
|
+
console.log(` 1. ${chalk.white('Restart Claude')} (if it\'s open)`);
|
|
195
|
+
console.log(` 2. ${chalk.white('Try:')} "Show me my Talkspresso schedule"`);
|
|
196
|
+
}
|
|
197
|
+
console.log(` ${configuredDesktop || configuredCode ? '3' : '1'}. ${chalk.white('Open your dashboard:')} ${chalk.cyan('https://app.talkspresso.com/dashboard')}`);
|
|
198
|
+
console.log(` ${chalk.dim('Brew will help you set up your first session.')}\n`);
|
|
199
|
+
}
|
|
@@ -92,4 +92,42 @@ export function registerAppointmentTools(server, client) {
|
|
|
92
92
|
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
93
93
|
};
|
|
94
94
|
});
|
|
95
|
+
server.tool('check-availability', 'Check available time slots for a specific date and service. Use this before scheduling to see when you are free.', {
|
|
96
|
+
date: z.string().describe('Date to check in YYYY-MM-DD format'),
|
|
97
|
+
service_id: z.string().optional().describe('Service ID to check availability for (determines duration). Use list-services to find this.'),
|
|
98
|
+
duration: z.number().optional().describe('Duration in minutes if no service_id (default 30)'),
|
|
99
|
+
}, async (params) => {
|
|
100
|
+
const data = await client.post('/appointments/slots', {
|
|
101
|
+
date: params.date,
|
|
102
|
+
service_id: params.service_id,
|
|
103
|
+
duration: params.duration || 30,
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
server.tool('send-test-invite', 'Send a test appointment invitation to yourself or a specific email. Great for verifying your booking flow works correctly.', {
|
|
110
|
+
email: z.string().describe('Email address to send the test invite to'),
|
|
111
|
+
name: z.string().optional().describe('Name for the test client (default "Test Client")'),
|
|
112
|
+
service_id: z.string().optional().describe('Service ID to use for the test invite. Use list-services to find this.'),
|
|
113
|
+
scheduled_date: z.string().optional().describe('Date in YYYY-MM-DD format (defaults to tomorrow)'),
|
|
114
|
+
scheduled_time: z.string().optional().describe('Time in HH:mm format, 24-hour (defaults to 10:00)'),
|
|
115
|
+
}, async (params) => {
|
|
116
|
+
// Build a test invite using the standard invite endpoint
|
|
117
|
+
const tomorrow = new Date();
|
|
118
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
119
|
+
const defaultDate = tomorrow.toISOString().split('T')[0];
|
|
120
|
+
const data = await client.post('/appointments/invite', {
|
|
121
|
+
client_name: params.name || 'Test Client',
|
|
122
|
+
client_email: params.email,
|
|
123
|
+
service_id: params.service_id,
|
|
124
|
+
scheduled_date: params.scheduled_date || defaultDate,
|
|
125
|
+
scheduled_time: params.scheduled_time || '10:00',
|
|
126
|
+
is_complimentary: true,
|
|
127
|
+
invitation_message: 'This is a test invitation to verify the booking flow.',
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
131
|
+
};
|
|
132
|
+
});
|
|
95
133
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerBrewTools(server, client) {
|
|
3
|
+
server.tool('get-brew-status', 'Check your Brew AI mentor status including Pro tier, question quota, and active conversation.', {}, async () => {
|
|
4
|
+
const data = await client.get('/guide/status');
|
|
5
|
+
return {
|
|
6
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
7
|
+
};
|
|
8
|
+
});
|
|
9
|
+
server.tool('start-brew-conversation', 'Start a new conversation with Brew or resume your existing one. Brew is your AI business mentor.', {}, async () => {
|
|
10
|
+
const data = await client.post('/guide/start');
|
|
11
|
+
return {
|
|
12
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
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.', {
|
|
16
|
+
conversationId: z.string().describe('The Brew conversation ID (get from start-brew-conversation)'),
|
|
17
|
+
message: z.string().describe('Your message to Brew'),
|
|
18
|
+
}, async (params) => {
|
|
19
|
+
const data = await client.post(`/guide/${params.conversationId}/message`, {
|
|
20
|
+
message: params.message,
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
server.tool('get-brew-conversation-history', 'Get the message history for a Brew conversation.', {
|
|
27
|
+
conversationId: z.string().describe('The Brew conversation ID'),
|
|
28
|
+
}, async (params) => {
|
|
29
|
+
const data = await client.get(`/guide/${params.conversationId}/history`);
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
server.tool('get-brew-insights', 'Get proactive coaching insights and nudges from Brew. Shows opportunities, milestones, and reminders based on your activity.', {}, async () => {
|
|
35
|
+
const data = await client.get('/guide/insights');
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
server.tool('set-brew-goal', 'Set a business goal for Brew to help you work toward.', {
|
|
41
|
+
goal: z.string().describe('Your business goal (e.g. "Get my first 5 bookings" or "Reach $1000/month")'),
|
|
42
|
+
conversationId: z.string().optional().describe('Brew conversation ID to associate the goal with'),
|
|
43
|
+
}, async (params) => {
|
|
44
|
+
const data = await client.post('/guide/goals', params);
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
server.tool('get-brew-preferences', 'Get your Brew notification preferences including intensity, channel, and sleep status.', {}, async () => {
|
|
50
|
+
const data = await client.get('/guide/preferences');
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
server.tool('update-brew-preferences', 'Update Brew notification preferences. Control how often and how Brew reaches out.', {
|
|
56
|
+
intensity: z.enum(['high', 'medium', 'low']).optional().describe('Notification intensity'),
|
|
57
|
+
channelPreference: z.enum(['email', 'in_app', 'both']).optional().describe('How to receive notifications'),
|
|
58
|
+
topicFilters: z.array(z.string()).optional().describe('Topics to filter for'),
|
|
59
|
+
}, async (params) => {
|
|
60
|
+
const data = await client.put('/guide/preferences', params);
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
server.tool('sleep-brew', 'Put Brew to sleep for a specified number of weeks. Pauses all proactive notifications.', {
|
|
66
|
+
weeks: z.number().describe('Number of weeks to sleep (1, 2, or 4)'),
|
|
67
|
+
}, async (params) => {
|
|
68
|
+
const data = await client.post('/guide/preferences/sleep', { weeks: params.weeks });
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
server.tool('wake-brew', 'Wake Brew from sleep mode and resume proactive notifications.', {}, async () => {
|
|
74
|
+
const data = await client.post('/guide/preferences/wake');
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
server.tool('get-dashboard-insight', 'Get a single focused coaching insight for your dashboard. Returns stage-appropriate advice and next action.', {}, async () => {
|
|
80
|
+
const data = await client.get('/guide/dashboard-insight');
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerCalendarTools(server, client) {
|
|
3
|
+
server.tool('get-calendar', 'Get your calendar settings including timezone, availability windows, and booking preferences. Use this to check your current availability schedule.', {}, async () => {
|
|
4
|
+
const data = await client.get('/calendar/me');
|
|
5
|
+
return {
|
|
6
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
7
|
+
};
|
|
8
|
+
});
|
|
9
|
+
server.tool('update-calendar', 'Update your calendar settings like timezone, availability windows, booking interval, and buffer time. Use this when you want to change your availability schedule or timezone.', {
|
|
10
|
+
timezone: z.string().optional().describe('IANA timezone string (e.g., "America/New_York", "Europe/London")'),
|
|
11
|
+
availability: z.record(z.any()).optional().describe('Weekly availability object. Keys are day names (Monday-Sunday), values have is_selected, start_time, end_time.'),
|
|
12
|
+
booking_interval_days: z.number().optional().describe('How far in advance clients can book (in days)'),
|
|
13
|
+
buffer_minutes: z.number().optional().describe('Buffer time between appointments in minutes'),
|
|
14
|
+
}, async (params) => {
|
|
15
|
+
const data = await client.put('/calendar', params);
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
}
|
package/dist/tools/clients.js
CHANGED
|
@@ -18,4 +18,20 @@ export function registerClientTools(server, client) {
|
|
|
18
18
|
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
19
19
|
};
|
|
20
20
|
});
|
|
21
|
+
server.tool('get-session-history', 'Get rich session history with a specific client including session summaries, action items, and topics discussed. Use this to prep for a meeting or remember what you talked about last time.', {
|
|
22
|
+
id: z.string().describe('The client ID'),
|
|
23
|
+
}, async (params) => {
|
|
24
|
+
const data = await client.get(`/client/${params.id}/session-history`);
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
server.tool('get-client-activity', 'Get client activity dashboard including emails sent, testimonials, pre-qualification responses, and engagement stats.', {
|
|
30
|
+
id: z.string().describe('The client ID'),
|
|
31
|
+
}, async (params) => {
|
|
32
|
+
const data = await client.get(`/client/${params.id}/activity`);
|
|
33
|
+
return {
|
|
34
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
35
|
+
};
|
|
36
|
+
});
|
|
21
37
|
}
|
package/dist/tools/earnings.js
CHANGED
|
@@ -10,4 +10,29 @@ export function registerEarningsTools(server, client) {
|
|
|
10
10
|
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
11
11
|
};
|
|
12
12
|
});
|
|
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
|
+
const [appointments, earnings] = await Promise.all([
|
|
15
|
+
client.get('/appointments/me', { status: 'all', limit: 200 }),
|
|
16
|
+
client.get('/transaction/my', { limit: 200 }),
|
|
17
|
+
]);
|
|
18
|
+
const now = new Date();
|
|
19
|
+
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
|
|
20
|
+
const allAppts = appointments.appointments || appointments || [];
|
|
21
|
+
const allTxns = Array.isArray(earnings) ? earnings : earnings.transactions || [];
|
|
22
|
+
const thisMonthTxns = allTxns.filter((t) => t.createdAt >= monthStart || t.created_at >= monthStart);
|
|
23
|
+
const thisMonthRevenue = thisMonthTxns
|
|
24
|
+
.filter((t) => t.transaction_type === 'payment' || t.type === 'payment')
|
|
25
|
+
.reduce((sum, t) => sum + (parseFloat(t.amount) || 0), 0);
|
|
26
|
+
const thisMonthCompleted = allAppts.filter((a) => (a.status === 'completed') && (a.start_time >= monthStart || a.createdAt >= monthStart)).length;
|
|
27
|
+
const upcomingCount = allAppts.filter((a) => a.status === 'active' || a.status === 'scheduled').length;
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: 'text', text: JSON.stringify({
|
|
30
|
+
month: `${now.toLocaleString('default', { month: 'long' })} ${now.getFullYear()}`,
|
|
31
|
+
revenue_this_month: `$${thisMonthRevenue.toFixed(2)}`,
|
|
32
|
+
completed_sessions_this_month: thisMonthCompleted,
|
|
33
|
+
upcoming_sessions: upcomingCount,
|
|
34
|
+
total_transactions_this_month: thisMonthTxns.length,
|
|
35
|
+
}, null, 2) }],
|
|
36
|
+
};
|
|
37
|
+
});
|
|
13
38
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerFileLibraryTools(server, client) {
|
|
3
|
+
server.tool('list-files', 'Get all files in your file library. Files can be attached to products, services, or shared with clients.', {}, async () => {
|
|
4
|
+
const data = await client.get('/file-library');
|
|
5
|
+
return {
|
|
6
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
7
|
+
};
|
|
8
|
+
});
|
|
9
|
+
server.tool('get-file', 'Get detailed information about a specific file in your library.', {
|
|
10
|
+
id: z.string().describe('The file ID'),
|
|
11
|
+
}, async (params) => {
|
|
12
|
+
const data = await client.get(`/file-library/${params.id}`);
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
server.tool('get-file-library-stats', 'Get statistics about your file library including total files, storage used, and file types.', {}, async () => {
|
|
18
|
+
const data = await client.get('/file-library/stats');
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
server.tool('get-file-library-folders', 'Get all folders in your file library for organization.', {}, async () => {
|
|
24
|
+
const data = await client.get('/file-library/folders');
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
server.tool('update-file', 'Update file metadata like name, description, or folder.', {
|
|
30
|
+
id: z.string().describe('The file ID to update'),
|
|
31
|
+
name: z.string().optional().describe('New file name'),
|
|
32
|
+
description: z.string().optional().describe('File description'),
|
|
33
|
+
folder: z.string().optional().describe('Folder to move the file into'),
|
|
34
|
+
}, async (params) => {
|
|
35
|
+
const { id, ...updateData } = params;
|
|
36
|
+
const data = await client.put(`/file-library/${id}`, updateData);
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
server.tool('delete-file', 'Delete a file from your library.', {
|
|
42
|
+
id: z.string().describe('The file ID to delete'),
|
|
43
|
+
}, async (params) => {
|
|
44
|
+
const data = await client.delete(`/file-library/${params.id}`);
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
server.tool('add-file-tags', 'Add tags to a file for easier organization and search.', {
|
|
50
|
+
id: z.string().describe('The file ID'),
|
|
51
|
+
tags: z.array(z.string()).describe('Tags to add to the file'),
|
|
52
|
+
}, async (params) => {
|
|
53
|
+
const data = await client.post(`/file-library/${params.id}/tags`, { tags: params.tags });
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
}
|