call-me-cloud-mcp 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.
Files changed (3) hide show
  1. package/README.md +28 -11
  2. package/index.ts +20 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,21 +1,29 @@
1
1
  # call-me-cloud-mcp
2
2
 
3
- MCP (Model Context Protocol) client that enables Claude to make phone calls via [Call-Me Cloud](https://github.com/riverscornelson/call-me-cloud).
3
+ MCP (Model Context Protocol) client that enables Claude to make phone calls and send WhatsApp messages via [Call-Me Cloud](https://github.com/riverscornelson/call-me-cloud).
4
4
 
5
5
  ## What is this?
6
6
 
7
- This package lets Claude Code call you on the phone. It connects to a Call-Me Cloud server (which you deploy) and provides MCP tools for initiating and managing voice calls.
7
+ This package lets Claude Code call you on the phone or message you on WhatsApp. It connects to a Call-Me Cloud server (which you deploy) and provides MCP tools for initiating and managing voice calls and text conversations.
8
8
 
9
9
  ## Installation
10
10
 
11
- Used automatically via npx in Claude Code configurations. Add to your MCP settings:
11
+ ### Via Plugin Marketplace (Recommended)
12
+
13
+ ```
14
+ /plugin install call-me-cloud@claude-plugins-official
15
+ ```
16
+
17
+ ### Via npx (Manual)
18
+
19
+ Add to your MCP settings (`.mcp.json` or Claude Code config):
12
20
 
13
21
  ```json
14
22
  {
15
23
  "mcpServers": {
16
24
  "call-me-cloud": {
17
25
  "command": "npx",
18
- "args": ["-y", "call-me-cloud-mcp"],
26
+ "args": ["-y", "call-me-cloud-mcp@1.3.0"],
19
27
  "env": {
20
28
  "CALLME_CLOUD_URL": "https://your-server.railway.app",
21
29
  "CALLME_API_KEY": "your-api-key"
@@ -64,25 +72,34 @@ End the call with a closing message.
64
72
  end_call({ call_id: "call-1", message: "Sounds good, I'll get started. Talk soon!" })
65
73
  ```
66
74
 
75
+ ### `send_message`
76
+ Send a WhatsApp message and wait for the user's reply.
77
+
78
+ ```
79
+ send_message({ message: "The deploy finished. Want me to run the smoke tests?" })
80
+ ```
81
+
82
+ Returns: `{ messageId, response }` - The message ID and the user's text response.
83
+
67
84
  ## Requirements
68
85
 
69
86
  - Node.js 18+
70
87
  - A deployed [Call-Me Cloud](https://github.com/riverscornelson/call-me-cloud) server
71
- - Twilio or Telnyx account for phone calls
88
+ - Twilio account for phone calls
72
89
  - OpenAI API key (for speech-to-text and text-to-speech)
73
90
 
74
91
  ## How It Works
75
92
 
76
93
  ```
77
- Claude Code --> This MCP Client --> Your Cloud Server --> Phone Call
78
- (stdio) (REST API) (Twilio/Telnyx) (to you)
94
+ Claude Code --> This MCP Client --> Your Cloud Server --> Phone Call / WhatsApp
95
+ (stdio) (REST API) (Twilio) (to you)
79
96
  ```
80
97
 
81
- 1. Claude decides to call you using one of the MCP tools
98
+ 1. Claude decides to call or message you using one of the MCP tools
82
99
  2. This client forwards the request to your cloud server
83
- 3. The server initiates a phone call via Twilio/Telnyx
84
- 4. Audio is processed through OpenAI's real-time API
85
- 5. Your response is transcribed and returned to Claude
100
+ 3. The server initiates a phone call via Twilio or sends a WhatsApp message
101
+ 4. Audio is processed through OpenAI's real-time API (for calls)
102
+ 5. Your response is transcribed/received and returned to Claude
86
103
 
87
104
  ## Server Setup
88
105
 
package/index.ts CHANGED
@@ -14,6 +14,15 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
14
14
  const CLOUD_URL = process.env.CALLME_CLOUD_URL;
15
15
  const API_KEY = process.env.CALLME_API_KEY;
16
16
 
17
+ const TOOL_TIMEOUTS: Record<string, number> = {
18
+ initiate_call: 200_000, // user answers phone + speaks
19
+ continue_call: 200_000, // waiting for spoken/WhatsApp response
20
+ send_message: 200_000, // WhatsApp: server waits up to 3 min
21
+ speak_to_user: 30_000, // fire-and-forget
22
+ end_call: 30_000, // quick hangup
23
+ }
24
+ const DEFAULT_TIMEOUT = 30_000
25
+
17
26
  if (!CLOUD_URL) {
18
27
  console.error('Error: CALLME_CLOUD_URL is required');
19
28
  console.error('Set it to your Railway/Render deployment URL');
@@ -25,7 +34,7 @@ if (!API_KEY) {
25
34
  process.exit(1);
26
35
  }
27
36
 
28
- async function apiCall(path: string, body: unknown): Promise<unknown> {
37
+ async function apiCall(path: string, body: unknown, timeoutMs = DEFAULT_TIMEOUT): Promise<unknown> {
29
38
  const response = await fetch(`${CLOUD_URL}${path}`, {
30
39
  method: 'POST',
31
40
  headers: {
@@ -33,6 +42,7 @@ async function apiCall(path: string, body: unknown): Promise<unknown> {
33
42
  'Authorization': `Bearer ${API_KEY}`,
34
43
  },
35
44
  body: JSON.stringify(body),
45
+ signal: AbortSignal.timeout(timeoutMs),
36
46
  });
37
47
 
38
48
  if (!response.ok) {
@@ -46,7 +56,9 @@ async function apiCall(path: string, body: unknown): Promise<unknown> {
46
56
  async function main() {
47
57
  // Verify cloud server is reachable (uses unauthenticated /health endpoint)
48
58
  try {
49
- const healthResponse = await fetch(`${CLOUD_URL}/health`);
59
+ const healthResponse = await fetch(`${CLOUD_URL}/health`, {
60
+ signal: AbortSignal.timeout(5_000),
61
+ });
50
62
  if (!healthResponse.ok) {
51
63
  throw new Error(`Health check failed: ${healthResponse.status}`);
52
64
  }
@@ -58,7 +70,7 @@ async function main() {
58
70
  }
59
71
 
60
72
  const mcpServer = new Server(
61
- { name: 'call-me-cloud', version: '1.0.1' },
73
+ { name: 'call-me-cloud', version: '1.3.0' },
62
74
  { capabilities: { tools: {} } }
63
75
  );
64
76
 
@@ -139,7 +151,7 @@ async function main() {
139
151
  try {
140
152
  if (request.params.name === 'initiate_call') {
141
153
  const { message } = request.params.arguments as { message: string };
142
- const result = await apiCall('/api/call', { message }) as {
154
+ const result = await apiCall('/api/call', { message }, TOOL_TIMEOUTS.initiate_call) as {
143
155
  callId: string;
144
156
  response: string;
145
157
  contactMode?: 'voice' | 'whatsapp';
@@ -179,7 +191,7 @@ async function main() {
179
191
 
180
192
  if (request.params.name === 'continue_call') {
181
193
  const { call_id, message } = request.params.arguments as { call_id: string; message: string };
182
- const result = await apiCall(`/api/call/${call_id}/continue`, { message }) as {
194
+ const result = await apiCall(`/api/call/${call_id}/continue`, { message }, TOOL_TIMEOUTS.continue_call) as {
183
195
  response: string;
184
196
  contactMode?: 'voice' | 'whatsapp';
185
197
  whatsappSessionWindow?: {
@@ -209,7 +221,7 @@ async function main() {
209
221
 
210
222
  if (request.params.name === 'speak_to_user') {
211
223
  const { call_id, message } = request.params.arguments as { call_id: string; message: string };
212
- await apiCall(`/api/call/${call_id}/speak`, { message });
224
+ await apiCall(`/api/call/${call_id}/speak`, { message }, TOOL_TIMEOUTS.speak_to_user);
213
225
 
214
226
  return {
215
227
  content: [{ type: 'text', text: `Message spoken: "${message}"` }],
@@ -218,7 +230,7 @@ async function main() {
218
230
 
219
231
  if (request.params.name === 'end_call') {
220
232
  const { call_id, message } = request.params.arguments as { call_id: string; message: string };
221
- const result = await apiCall(`/api/call/${call_id}/end`, { message }) as { durationSeconds: number };
233
+ const result = await apiCall(`/api/call/${call_id}/end`, { message }, TOOL_TIMEOUTS.end_call) as { durationSeconds: number };
222
234
 
223
235
  return {
224
236
  content: [{ type: 'text', text: `Call ended. Duration: ${result.durationSeconds}s` }],
@@ -227,7 +239,7 @@ async function main() {
227
239
 
228
240
  if (request.params.name === 'send_message') {
229
241
  const { message } = request.params.arguments as { message: string };
230
- const result = await apiCall('/api/message', { message }) as { messageId: string; response: string };
242
+ const result = await apiCall('/api/message', { message }, TOOL_TIMEOUTS.send_message) as { messageId: string; response: string };
231
243
 
232
244
  return {
233
245
  content: [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "call-me-cloud-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "MCP client for Call-Me Cloud - lets Claude call you via phone",
5
5
  "type": "module",
6
6
  "bin": {