@thisispamela/mcp 1.1.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/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @thisispamela/mcp
2
+
3
+ MCP (Model Context Protocol) server for the Pamela Voice API. Exposes tools so AI assistants (e.g. Claude Desktop) can create and manage AI-powered phone calls.
4
+
5
+ ## Tools
6
+
7
+ | Tool | Description |
8
+ |------|-------------|
9
+ | `pamela_create_call` | Make an AI-powered phone call (`to`, `task`, optional `voice`, `agentName`, `callerName`, etc.) |
10
+ | `pamela_get_call` | Fetch a call by ID (status, transcript, summary) |
11
+ | `pamela_list_calls` | List recent calls (optional `limit`, `status`, `offset`, `startDate`, `endDate`) |
12
+ | `pamela_cancel_call` | Cancel an in-progress call |
13
+
14
+ ## Setup
15
+
16
+ 1. **Install**
17
+
18
+ ```bash
19
+ npm install -g @thisispamela/mcp
20
+ # or from repo
21
+ cd sdk/mcp && npm install && npm run build
22
+ ```
23
+
24
+ 2. **Configure**
25
+
26
+ Set your Pamela API key (required):
27
+
28
+ ```bash
29
+ export PAMELA_API_KEY=your_api_key
30
+ ```
31
+
32
+ Optional:
33
+
34
+ - `PAMELA_BASE_URL` — API base URL (default: `https://api.thisispamela.com`)
35
+ - `PAMELA_MCP_TRANSPORT` — `stdio` (default) or `http`
36
+ - `PAMELA_MCP_PORT` — HTTP port when using `PAMELA_MCP_TRANSPORT=http` (default: `8033`)
37
+
38
+ 3. **Run**
39
+
40
+ ```bash
41
+ npx pamela-mcp
42
+ # or
43
+ node dist/index.js
44
+ ```
45
+
46
+ HTTP mode:
47
+
48
+ ```bash
49
+ PAMELA_MCP_TRANSPORT=http PAMELA_MCP_PORT=8033 node dist/index.js
50
+ ```
51
+
52
+ ## Claude Desktop
53
+
54
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or the equivalent config path on your system:
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "pamela": {
60
+ "command": "node",
61
+ "args": ["/ABSOLUTE/PATH/TO/sdk/mcp/dist/index.js"],
62
+ "env": {
63
+ "PAMELA_API_KEY": "your_api_key"
64
+ }
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ Use the absolute path to `sdk/mcp/dist/index.js` after building. Restart Claude Desktop after changing the config.
71
+
72
+ ## HTTP Transport
73
+
74
+ When `PAMELA_MCP_TRANSPORT=http`, the server listens on `http://localhost:8033` by default (Streamable HTTP transport).
75
+
76
+ ## Development
77
+
78
+ ```bash
79
+ cd sdk/mcp
80
+ npm install
81
+ npm run build
82
+ ```
83
+
84
+ Then run with `node dist/index.js` and ensure `PAMELA_API_KEY` is set.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7
+ import { createServer } from "http";
8
+
9
+ // src/config.ts
10
+ var DEFAULT_BASE_URL = "https://api.thisispamela.com";
11
+ var DEFAULT_TRANSPORT = "stdio";
12
+ var DEFAULT_PORT = 8033;
13
+ function loadConfig(overrides) {
14
+ const apiKey = overrides?.apiKey ?? process.env.PAMELA_API_KEY ?? "";
15
+ const baseUrl = overrides?.baseUrl ?? process.env.PAMELA_BASE_URL ?? DEFAULT_BASE_URL;
16
+ const transportValue = overrides?.transport ?? process.env.PAMELA_MCP_TRANSPORT ?? DEFAULT_TRANSPORT;
17
+ const transport = transportValue.toLowerCase();
18
+ const portValue = overrides?.port ?? (process.env.PAMELA_MCP_PORT ? Number(process.env.PAMELA_MCP_PORT) : void 0) ?? DEFAULT_PORT;
19
+ const port = Number.isFinite(portValue) ? portValue : DEFAULT_PORT;
20
+ if (!apiKey || apiKey.trim() === "") {
21
+ throw new Error(
22
+ "PAMELA_API_KEY is required. Set it in the environment or pass it when starting the server."
23
+ );
24
+ }
25
+ if (transport !== "stdio" && transport !== "http") {
26
+ throw new Error("PAMELA_MCP_TRANSPORT must be 'stdio' or 'http'.");
27
+ }
28
+ if (!Number.isInteger(port) || port <= 0) {
29
+ throw new Error("PAMELA_MCP_PORT must be a positive integer.");
30
+ }
31
+ return {
32
+ apiKey: apiKey.trim(),
33
+ baseUrl: baseUrl.trim(),
34
+ transport,
35
+ port
36
+ };
37
+ }
38
+
39
+ // src/client.ts
40
+ import { Pamela as PamelaClient } from "@thisispamela/sdk";
41
+ function createClient(config) {
42
+ return new PamelaClient({
43
+ apiKey: config.apiKey,
44
+ baseUrl: config.baseUrl
45
+ });
46
+ }
47
+
48
+ // src/tools/createCall.ts
49
+ import { z } from "zod";
50
+
51
+ // src/errors.ts
52
+ function formatToolError(error) {
53
+ if (error instanceof Error) {
54
+ const name = error.name;
55
+ const message = error.message;
56
+ const details = error.details;
57
+ const statusCode = error.statusCode;
58
+ if (name === "AuthenticationError") {
59
+ return "Pamela API authentication failed. Check PAMELA_API_KEY.";
60
+ }
61
+ if (name === "SubscriptionError") {
62
+ return "Subscription or permission error: " + message;
63
+ }
64
+ if (name === "RateLimitError") {
65
+ return "Rate limit exceeded. Please retry later.";
66
+ }
67
+ if (name === "ValidationError") {
68
+ const parts = ["Invalid request: " + message];
69
+ if (details && typeof details === "object") {
70
+ const fieldErrors = details.field_errors;
71
+ if (Array.isArray(fieldErrors) && fieldErrors.length > 0) {
72
+ parts.push("Details: " + JSON.stringify(fieldErrors));
73
+ }
74
+ }
75
+ return parts.join(" ");
76
+ }
77
+ if (name === "CallError") {
78
+ return "Call error: " + message;
79
+ }
80
+ if (statusCode && statusCode >= 500) {
81
+ return "Pamela API is temporarily unavailable. Please retry.";
82
+ }
83
+ return message || "An error occurred while calling the Pamela API.";
84
+ }
85
+ return "An unexpected error occurred.";
86
+ }
87
+
88
+ // src/tools/createCall.ts
89
+ var createCallInputSchema = {
90
+ to: z.string().min(1).describe("Phone number in E.164 format (e.g. +15551234567)"),
91
+ task: z.string().min(1).describe("What the AI should accomplish on the call"),
92
+ voice: z.enum(["male", "female", "auto"]).optional().describe("Voice selection (male, female, auto)"),
93
+ agentName: z.string().optional().describe("Name for the AI agent (optional)"),
94
+ callerName: z.string().optional().describe("Who the AI is calling on behalf of (optional)"),
95
+ instructions: z.string().optional().describe("Additional instructions for the AI"),
96
+ maxDurationSeconds: z.number().int().positive().optional().describe("Maximum call duration in seconds"),
97
+ metadata: z.record(z.unknown()).optional().describe("Optional metadata")
98
+ };
99
+ function registerCreateCall(server2, client) {
100
+ server2.registerTool(
101
+ "pamela_create_call",
102
+ {
103
+ description: "Make an AI-powered phone call to accomplish a task.",
104
+ inputSchema: createCallInputSchema
105
+ },
106
+ async (args) => {
107
+ try {
108
+ const response = await client.createCall({
109
+ to: args.to,
110
+ task: args.task,
111
+ voice: args.voice,
112
+ agent_name: args.agentName,
113
+ caller_name: args.callerName,
114
+ instructions: args.instructions,
115
+ max_duration_seconds: args.maxDurationSeconds,
116
+ metadata: args.metadata
117
+ });
118
+ const text = JSON.stringify(
119
+ {
120
+ id: response.id,
121
+ status: response.status,
122
+ call_session_id: response.call_session_id,
123
+ created_at: response.created_at
124
+ },
125
+ null,
126
+ 2
127
+ );
128
+ return { content: [{ type: "text", text }] };
129
+ } catch (error) {
130
+ const message = formatToolError(error);
131
+ return { content: [{ type: "text", text: message }], isError: true };
132
+ }
133
+ }
134
+ );
135
+ }
136
+
137
+ // src/tools/getCall.ts
138
+ import { z as z2 } from "zod";
139
+ var getCallInputSchema = {
140
+ callId: z2.string().min(1).describe("The call ID returned from pamela_create_call")
141
+ };
142
+ function registerGetCall(server2, client) {
143
+ server2.registerTool(
144
+ "pamela_get_call",
145
+ {
146
+ description: "Fetch a call by ID. Returns status, duration, transcript (if available), and summary (if available).",
147
+ inputSchema: getCallInputSchema
148
+ },
149
+ async (args) => {
150
+ try {
151
+ const call = await client.getCall(args.callId);
152
+ const text = JSON.stringify(
153
+ {
154
+ id: call.id,
155
+ status: call.status,
156
+ to: call.to,
157
+ from_: call.from_,
158
+ created_at: call.created_at,
159
+ started_at: call.started_at,
160
+ completed_at: call.completed_at,
161
+ duration_seconds: call.duration_seconds,
162
+ max_duration_seconds: call.max_duration_seconds,
163
+ transcript: call.transcript,
164
+ summary: call.summary,
165
+ metadata: call.metadata,
166
+ end_user_id: call.end_user_id
167
+ },
168
+ null,
169
+ 2
170
+ );
171
+ return { content: [{ type: "text", text }] };
172
+ } catch (error) {
173
+ const message = formatToolError(error);
174
+ return { content: [{ type: "text", text: message }], isError: true };
175
+ }
176
+ }
177
+ );
178
+ }
179
+
180
+ // src/tools/listCalls.ts
181
+ import { z as z3 } from "zod";
182
+ var listCallsInputSchema = {
183
+ limit: z3.number().int().min(1).max(100).optional().default(10).describe("Maximum number of calls to return"),
184
+ status: z3.string().optional().describe("Filter by status (e.g. completed, failed)"),
185
+ offset: z3.number().int().min(0).optional().describe("Number of calls to skip"),
186
+ startDate: z3.string().optional().describe("Start date (ISO 8601)"),
187
+ endDate: z3.string().optional().describe("End date (ISO 8601)")
188
+ };
189
+ function registerListCalls(server2, client) {
190
+ server2.registerTool(
191
+ "pamela_list_calls",
192
+ {
193
+ description: "List recent calls for the project. Optionally filter by status or date range.",
194
+ inputSchema: listCallsInputSchema
195
+ },
196
+ async (args) => {
197
+ try {
198
+ const result = await client.listCalls({
199
+ limit: args.limit ?? 10,
200
+ status_filter: args.status,
201
+ offset: args.offset,
202
+ start_date: args.startDate,
203
+ end_date: args.endDate
204
+ });
205
+ const text = JSON.stringify(
206
+ {
207
+ items: result.items,
208
+ total: result.total,
209
+ limit: result.limit,
210
+ offset: result.offset
211
+ },
212
+ null,
213
+ 2
214
+ );
215
+ return { content: [{ type: "text", text }] };
216
+ } catch (error) {
217
+ const message = formatToolError(error);
218
+ return { content: [{ type: "text", text: message }], isError: true };
219
+ }
220
+ }
221
+ );
222
+ }
223
+
224
+ // src/tools/cancelCall.ts
225
+ import { z as z4 } from "zod";
226
+ var cancelCallInputSchema = {
227
+ callId: z4.string().min(1).describe("The call ID to cancel")
228
+ };
229
+ function registerCancelCall(server2, client) {
230
+ server2.registerTool(
231
+ "pamela_cancel_call",
232
+ {
233
+ description: "Cancel an in-progress call.",
234
+ inputSchema: cancelCallInputSchema
235
+ },
236
+ async (args) => {
237
+ try {
238
+ const result = await client.cancelCall(args.callId);
239
+ const text = JSON.stringify(
240
+ {
241
+ success: result.success,
242
+ call_id: result.call_id,
243
+ status: result.status
244
+ },
245
+ null,
246
+ 2
247
+ );
248
+ return { content: [{ type: "text", text }] };
249
+ } catch (error) {
250
+ const message = formatToolError(error);
251
+ return { content: [{ type: "text", text: message }], isError: true };
252
+ }
253
+ }
254
+ );
255
+ }
256
+
257
+ // src/index.ts
258
+ import { createRequire } from "module";
259
+ var require2 = createRequire(import.meta.url);
260
+ var pkg = require2("../package.json");
261
+ var server = new McpServer({
262
+ name: "pamela",
263
+ version: pkg.version
264
+ });
265
+ async function main() {
266
+ const config = loadConfig();
267
+ const client = createClient(config);
268
+ registerCreateCall(server, client);
269
+ registerGetCall(server, client);
270
+ registerListCalls(server, client);
271
+ registerCancelCall(server, client);
272
+ if (config.transport === "http") {
273
+ const transport = new StreamableHTTPServerTransport();
274
+ const httpServer = createServer((req, res) => {
275
+ void transport.handleRequest(req, res);
276
+ });
277
+ await server.connect(transport);
278
+ await new Promise((resolve) => {
279
+ httpServer.listen(config.port, resolve);
280
+ });
281
+ console.error(`Pamela MCP server listening on http://localhost:${config.port}`);
282
+ } else {
283
+ const transport = new StdioServerTransport();
284
+ await server.connect(transport);
285
+ }
286
+ }
287
+ main().catch((error) => {
288
+ console.error("Fatal error:", error);
289
+ process.exit(1);
290
+ });
291
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/tools/createCall.ts","../src/errors.ts","../src/tools/getCall.ts","../src/tools/listCalls.ts","../src/tools/cancelCall.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Pamela MCP server — exposes Voice API tools to MCP clients (e.g. Claude Desktop).\n *\n * Set PAMELA_API_KEY in the environment. Optionally set PAMELA_BASE_URL.\n * Run: node dist/index.js (or use the pamela-mcp bin).\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { createServer } from \"node:http\";\nimport { loadConfig } from \"./config.js\";\nimport { createClient } from \"./client.js\";\nimport { registerCreateCall } from \"./tools/createCall.js\";\nimport { registerGetCall } from \"./tools/getCall.js\";\nimport { registerListCalls } from \"./tools/listCalls.js\";\nimport { registerCancelCall } from \"./tools/cancelCall.js\";\n\nimport { createRequire } from \"node:module\";\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\nconst server = new McpServer({\n name: \"pamela\",\n version: pkg.version,\n});\n\nasync function main(): Promise<void> {\n const config = loadConfig();\n const client = createClient(config);\n\n registerCreateCall(server, client);\n registerGetCall(server, client);\n registerListCalls(server, client);\n registerCancelCall(server, client);\n\n if (config.transport === \"http\") {\n const transport = new StreamableHTTPServerTransport();\n const httpServer = createServer((req, res) => {\n void transport.handleRequest(req, res);\n });\n await server.connect(transport);\n await new Promise<void>((resolve) => {\n httpServer.listen(config.port, resolve);\n });\n console.error(`Pamela MCP server listening on http://localhost:${config.port}`);\n } else {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n }\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});\n","/**\n * MCP server configuration: env vars and defaults.\n */\n\nexport interface McpConfig {\n apiKey: string;\n baseUrl: string;\n transport: \"stdio\" | \"http\";\n port: number;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.thisispamela.com\";\nconst DEFAULT_TRANSPORT: McpConfig[\"transport\"] = \"stdio\";\nconst DEFAULT_PORT = 8033;\n\nexport function loadConfig(overrides?: Partial<McpConfig>): McpConfig {\n const apiKey =\n overrides?.apiKey ??\n process.env.PAMELA_API_KEY ??\n \"\";\n const baseUrl =\n overrides?.baseUrl ??\n process.env.PAMELA_BASE_URL ??\n DEFAULT_BASE_URL;\n const transportValue =\n overrides?.transport ??\n (process.env.PAMELA_MCP_TRANSPORT as McpConfig[\"transport\"] | undefined) ??\n DEFAULT_TRANSPORT;\n const transport = transportValue.toLowerCase() as McpConfig[\"transport\"];\n const portValue =\n overrides?.port ??\n (process.env.PAMELA_MCP_PORT ? Number(process.env.PAMELA_MCP_PORT) : undefined) ??\n DEFAULT_PORT;\n const port = Number.isFinite(portValue) ? portValue : DEFAULT_PORT;\n\n if (!apiKey || apiKey.trim() === \"\") {\n throw new Error(\n \"PAMELA_API_KEY is required. Set it in the environment or pass it when starting the server.\"\n );\n }\n if (transport !== \"stdio\" && transport !== \"http\") {\n throw new Error(\"PAMELA_MCP_TRANSPORT must be 'stdio' or 'http'.\");\n }\n if (!Number.isInteger(port) || port <= 0) {\n throw new Error(\"PAMELA_MCP_PORT must be a positive integer.\");\n }\n\n return {\n apiKey: apiKey.trim(),\n baseUrl: baseUrl.trim(),\n transport,\n port,\n };\n}\n","/**\n * Pamela API client wrapper for MCP tools.\n */\n\nimport { Pamela as PamelaClient } from \"@thisispamela/sdk\";\nimport type { McpConfig } from \"./config.js\";\n\nexport type PamelaApiClient = InstanceType<typeof PamelaClient>;\n\nexport function createClient(config: McpConfig): PamelaApiClient {\n return new PamelaClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n });\n}\n","/**\n * MCP tool: pamela_create_call — make an AI-powered phone call.\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { PamelaApiClient } from \"../client.js\";\nimport { formatToolError } from \"../errors.js\";\n\nexport const createCallInputSchema = {\n to: z.string().min(1).describe(\"Phone number in E.164 format (e.g. +15551234567)\"),\n task: z.string().min(1).describe(\"What the AI should accomplish on the call\"),\n voice: z.enum([\"male\", \"female\", \"auto\"]).optional().describe(\"Voice selection (male, female, auto)\"),\n agentName: z.string().optional().describe(\"Name for the AI agent (optional)\"),\n callerName: z.string().optional().describe(\"Who the AI is calling on behalf of (optional)\"),\n instructions: z.string().optional().describe(\"Additional instructions for the AI\"),\n maxDurationSeconds: z.number().int().positive().optional().describe(\"Maximum call duration in seconds\"),\n metadata: z.record(z.unknown()).optional().describe(\"Optional metadata\"),\n};\n\nexport function registerCreateCall(server: McpServer, client: PamelaApiClient): void {\n server.registerTool(\n \"pamela_create_call\",\n {\n description: \"Make an AI-powered phone call to accomplish a task.\",\n inputSchema: createCallInputSchema,\n },\n async (args: {\n to: string;\n task: string;\n voice?: \"male\" | \"female\" | \"auto\";\n agentName?: string;\n callerName?: string;\n instructions?: string;\n maxDurationSeconds?: number;\n metadata?: Record<string, unknown>;\n }) => {\n try {\n const response = await client.createCall({\n to: args.to,\n task: args.task,\n voice: args.voice,\n agent_name: args.agentName,\n caller_name: args.callerName,\n instructions: args.instructions,\n max_duration_seconds: args.maxDurationSeconds,\n metadata: args.metadata,\n });\n const text = JSON.stringify(\n {\n id: response.id,\n status: response.status,\n call_session_id: response.call_session_id,\n created_at: response.created_at,\n },\n null,\n 2\n );\n return { content: [{ type: \"text\" as const, text }] };\n } catch (error) {\n const message = formatToolError(error);\n return { content: [{ type: \"text\" as const, text: message }], isError: true };\n }\n }\n );\n}\n","/**\n * Map Pamela SDK errors to user-facing MCP tool error messages.\n */\n\nimport type { PamelaError } from \"@thisispamela/sdk\";\n\nexport function formatToolError(error: unknown): string {\n if (error instanceof Error) {\n const name = (error as PamelaError).name;\n const message = error.message;\n const details = (error as PamelaError).details;\n const statusCode = (error as PamelaError).statusCode;\n\n if (name === \"AuthenticationError\") {\n return \"Pamela API authentication failed. Check PAMELA_API_KEY.\";\n }\n if (name === \"SubscriptionError\") {\n return \"Subscription or permission error: \" + message;\n }\n if (name === \"RateLimitError\") {\n return \"Rate limit exceeded. Please retry later.\";\n }\n if (name === \"ValidationError\") {\n const parts = [\"Invalid request: \" + message];\n if (details && typeof details === \"object\") {\n const fieldErrors = (details as Record<string, unknown>).field_errors;\n if (Array.isArray(fieldErrors) && fieldErrors.length > 0) {\n parts.push(\"Details: \" + JSON.stringify(fieldErrors));\n }\n }\n return parts.join(\" \");\n }\n if (name === \"CallError\") {\n return \"Call error: \" + message;\n }\n if (statusCode && statusCode >= 500) {\n return \"Pamela API is temporarily unavailable. Please retry.\";\n }\n return message || \"An error occurred while calling the Pamela API.\";\n }\n return \"An unexpected error occurred.\";\n}\n","/**\n * MCP tool: pamela_get_call — fetch call status, transcript, and summary.\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { PamelaApiClient } from \"../client.js\";\nimport { formatToolError } from \"../errors.js\";\n\nexport const getCallInputSchema = {\n callId: z.string().min(1).describe(\"The call ID returned from pamela_create_call\"),\n};\n\nexport function registerGetCall(server: McpServer, client: PamelaApiClient): void {\n server.registerTool(\n \"pamela_get_call\",\n {\n description: \"Fetch a call by ID. Returns status, duration, transcript (if available), and summary (if available).\",\n inputSchema: getCallInputSchema,\n },\n async (args: { callId: string }) => {\n try {\n const call = await client.getCall(args.callId);\n const text = JSON.stringify(\n {\n id: call.id,\n status: call.status,\n to: call.to,\n from_: call.from_,\n created_at: call.created_at,\n started_at: call.started_at,\n completed_at: call.completed_at,\n duration_seconds: call.duration_seconds,\n max_duration_seconds: call.max_duration_seconds,\n transcript: call.transcript,\n summary: call.summary,\n metadata: call.metadata,\n end_user_id: call.end_user_id,\n },\n null,\n 2\n );\n return { content: [{ type: \"text\" as const, text }] };\n } catch (error) {\n const message = formatToolError(error);\n return { content: [{ type: \"text\" as const, text: message }], isError: true };\n }\n }\n );\n}\n","/**\n * MCP tool: pamela_list_calls — list recent calls.\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { PamelaApiClient } from \"../client.js\";\nimport { formatToolError } from \"../errors.js\";\n\nexport const listCallsInputSchema = {\n limit: z.number().int().min(1).max(100).optional().default(10).describe(\"Maximum number of calls to return\"),\n status: z.string().optional().describe(\"Filter by status (e.g. completed, failed)\"),\n offset: z.number().int().min(0).optional().describe(\"Number of calls to skip\"),\n startDate: z.string().optional().describe(\"Start date (ISO 8601)\"),\n endDate: z.string().optional().describe(\"End date (ISO 8601)\"),\n};\n\nexport function registerListCalls(server: McpServer, client: PamelaApiClient): void {\n server.registerTool(\n \"pamela_list_calls\",\n {\n description: \"List recent calls for the project. Optionally filter by status or date range.\",\n inputSchema: listCallsInputSchema,\n },\n async (args: {\n limit?: number;\n status?: string;\n offset?: number;\n startDate?: string;\n endDate?: string;\n }) => {\n try {\n const result = await client.listCalls({\n limit: args.limit ?? 10,\n status_filter: args.status,\n offset: args.offset,\n start_date: args.startDate,\n end_date: args.endDate,\n });\n const text = JSON.stringify(\n {\n items: result.items,\n total: result.total,\n limit: result.limit,\n offset: result.offset,\n },\n null,\n 2\n );\n return { content: [{ type: \"text\" as const, text }] };\n } catch (error) {\n const message = formatToolError(error);\n return { content: [{ type: \"text\" as const, text: message }], isError: true };\n }\n }\n );\n}\n","/**\n * MCP tool: pamela_cancel_call — cancel an in-progress call.\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { PamelaApiClient } from \"../client.js\";\nimport { formatToolError } from \"../errors.js\";\n\nexport const cancelCallInputSchema = {\n callId: z.string().min(1).describe(\"The call ID to cancel\"),\n};\n\nexport function registerCancelCall(server: McpServer, client: PamelaApiClient): void {\n server.registerTool(\n \"pamela_cancel_call\",\n {\n description: \"Cancel an in-progress call.\",\n inputSchema: cancelCallInputSchema,\n },\n async (args: { callId: string }) => {\n try {\n const result = await client.cancelCall(args.callId);\n const text = JSON.stringify(\n {\n success: result.success,\n call_id: result.call_id,\n status: result.status,\n },\n null,\n 2\n );\n return { content: [{ type: \"text\" as const, text }] };\n } catch (error) {\n const message = formatToolError(error);\n return { content: [{ type: \"text\" as const, text: message }], isError: true };\n }\n }\n );\n}\n"],"mappings":";;;AAQA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,qCAAqC;AAC9C,SAAS,oBAAoB;;;ACA7B,IAAM,mBAAmB;AACzB,IAAM,oBAA4C;AAClD,IAAM,eAAe;AAEd,SAAS,WAAW,WAA2C;AACpE,QAAM,SACJ,WAAW,UACX,QAAQ,IAAI,kBACZ;AACF,QAAM,UACJ,WAAW,WACX,QAAQ,IAAI,mBACZ;AACF,QAAM,iBACJ,WAAW,aACV,QAAQ,IAAI,wBACb;AACF,QAAM,YAAY,eAAe,YAAY;AAC7C,QAAM,YACJ,WAAW,SACV,QAAQ,IAAI,kBAAkB,OAAO,QAAQ,IAAI,eAAe,IAAI,WACrE;AACF,QAAM,OAAO,OAAO,SAAS,SAAS,IAAI,YAAY;AAEtD,MAAI,CAAC,UAAU,OAAO,KAAK,MAAM,IAAI;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,cAAc,WAAW,cAAc,QAAQ;AACjD,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AACA,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,QAAQ,GAAG;AACxC,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO,KAAK;AAAA,IACpB,SAAS,QAAQ,KAAK;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AACF;;;ACjDA,SAAS,UAAU,oBAAoB;AAKhC,SAAS,aAAa,QAAoC;AAC/D,SAAO,IAAI,aAAa;AAAA,IACtB,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,EAClB,CAAC;AACH;;;ACVA,SAAS,SAAS;;;ACEX,SAAS,gBAAgB,OAAwB;AACtD,MAAI,iBAAiB,OAAO;AAC1B,UAAM,OAAQ,MAAsB;AACpC,UAAM,UAAU,MAAM;AACtB,UAAM,UAAW,MAAsB;AACvC,UAAM,aAAc,MAAsB;AAE1C,QAAI,SAAS,uBAAuB;AAClC,aAAO;AAAA,IACT;AACA,QAAI,SAAS,qBAAqB;AAChC,aAAO,uCAAuC;AAAA,IAChD;AACA,QAAI,SAAS,kBAAkB;AAC7B,aAAO;AAAA,IACT;AACA,QAAI,SAAS,mBAAmB;AAC9B,YAAM,QAAQ,CAAC,sBAAsB,OAAO;AAC5C,UAAI,WAAW,OAAO,YAAY,UAAU;AAC1C,cAAM,cAAe,QAAoC;AACzD,YAAI,MAAM,QAAQ,WAAW,KAAK,YAAY,SAAS,GAAG;AACxD,gBAAM,KAAK,cAAc,KAAK,UAAU,WAAW,CAAC;AAAA,QACtD;AAAA,MACF;AACA,aAAO,MAAM,KAAK,GAAG;AAAA,IACvB;AACA,QAAI,SAAS,aAAa;AACxB,aAAO,iBAAiB;AAAA,IAC1B;AACA,QAAI,cAAc,cAAc,KAAK;AACnC,aAAO;AAAA,IACT;AACA,WAAO,WAAW;AAAA,EACpB;AACA,SAAO;AACT;;;ADhCO,IAAM,wBAAwB;AAAA,EACnC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,kDAAkD;AAAA,EACjF,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,2CAA2C;AAAA,EAC5E,OAAO,EAAE,KAAK,CAAC,QAAQ,UAAU,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,sCAAsC;AAAA,EACpG,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EAC5E,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,EAC1F,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oCAAoC;AAAA,EACjF,oBAAoB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EACtG,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,mBAAmB;AACzE;AAEO,SAAS,mBAAmBA,SAAmB,QAA+B;AACnF,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,OAAO,SASD;AACJ,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,WAAW;AAAA,UACvC,IAAI,KAAK;AAAA,UACT,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,UAClB,cAAc,KAAK;AAAA,UACnB,sBAAsB,KAAK;AAAA,UAC3B,UAAU,KAAK;AAAA,QACjB,CAAC;AACD,cAAM,OAAO,KAAK;AAAA,UAChB;AAAA,YACE,IAAI,SAAS;AAAA,YACb,QAAQ,SAAS;AAAA,YACjB,iBAAiB,SAAS;AAAA,YAC1B,YAAY,SAAS;AAAA,UACvB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC,EAAE;AAAA,MACtD,SAAS,OAAO;AACd,cAAM,UAAU,gBAAgB,KAAK;AACrC,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AACF;;;AE7DA,SAAS,KAAAC,UAAS;AAKX,IAAM,qBAAqB;AAAA,EAChC,QAAQC,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,8CAA8C;AACnF;AAEO,SAAS,gBAAgBC,SAAmB,QAA+B;AAChF,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,OAAO,SAA6B;AAClC,UAAI;AACF,cAAM,OAAO,MAAM,OAAO,QAAQ,KAAK,MAAM;AAC7C,cAAM,OAAO,KAAK;AAAA,UAChB;AAAA,YACE,IAAI,KAAK;AAAA,YACT,QAAQ,KAAK;AAAA,YACb,IAAI,KAAK;AAAA,YACT,OAAO,KAAK;AAAA,YACZ,YAAY,KAAK;AAAA,YACjB,YAAY,KAAK;AAAA,YACjB,cAAc,KAAK;AAAA,YACnB,kBAAkB,KAAK;AAAA,YACvB,sBAAsB,KAAK;AAAA,YAC3B,YAAY,KAAK;AAAA,YACjB,SAAS,KAAK;AAAA,YACd,UAAU,KAAK;AAAA,YACf,aAAa,KAAK;AAAA,UACpB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC,EAAE;AAAA,MACtD,SAAS,OAAO;AACd,cAAM,UAAU,gBAAgB,KAAK;AACrC,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AACF;;;AC7CA,SAAS,KAAAC,UAAS;AAKX,IAAM,uBAAuB;AAAA,EAClC,OAAOC,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,SAAS,mCAAmC;AAAA,EAC3G,QAAQA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2CAA2C;AAAA,EAClF,QAAQA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,yBAAyB;AAAA,EAC7E,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uBAAuB;AAAA,EACjE,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qBAAqB;AAC/D;AAEO,SAAS,kBAAkBC,SAAmB,QAA+B;AAClF,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,OAAO,SAMD;AACJ,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,UAAU;AAAA,UACpC,OAAO,KAAK,SAAS;AAAA,UACrB,eAAe,KAAK;AAAA,UACpB,QAAQ,KAAK;AAAA,UACb,YAAY,KAAK;AAAA,UACjB,UAAU,KAAK;AAAA,QACjB,CAAC;AACD,cAAM,OAAO,KAAK;AAAA,UAChB;AAAA,YACE,OAAO,OAAO;AAAA,YACd,OAAO,OAAO;AAAA,YACd,OAAO,OAAO;AAAA,YACd,QAAQ,OAAO;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC,EAAE;AAAA,MACtD,SAAS,OAAO;AACd,cAAM,UAAU,gBAAgB,KAAK;AACrC,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AACF;;;ACpDA,SAAS,KAAAC,UAAS;AAKX,IAAM,wBAAwB;AAAA,EACnC,QAAQC,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,uBAAuB;AAC5D;AAEO,SAAS,mBAAmBC,SAAmB,QAA+B;AACnF,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,OAAO,SAA6B;AAClC,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,WAAW,KAAK,MAAM;AAClD,cAAM,OAAO,KAAK;AAAA,UAChB;AAAA,YACE,SAAS,OAAO;AAAA,YAChB,SAAS,OAAO;AAAA,YAChB,QAAQ,OAAO;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,KAAK,CAAC,EAAE;AAAA,MACtD,SAAS,OAAO;AACd,cAAM,UAAU,gBAAgB,KAAK;AACrC,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AACF;;;APpBA,SAAS,qBAAqB;AAC9B,IAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,MAAMA,SAAQ,iBAAiB;AAErC,IAAM,SAAS,IAAI,UAAU;AAAA,EAC3B,MAAM;AAAA,EACN,SAAS,IAAI;AACf,CAAC;AAED,eAAe,OAAsB;AACnC,QAAM,SAAS,WAAW;AAC1B,QAAM,SAAS,aAAa,MAAM;AAElC,qBAAmB,QAAQ,MAAM;AACjC,kBAAgB,QAAQ,MAAM;AAC9B,oBAAkB,QAAQ,MAAM;AAChC,qBAAmB,QAAQ,MAAM;AAEjC,MAAI,OAAO,cAAc,QAAQ;AAC/B,UAAM,YAAY,IAAI,8BAA8B;AACpD,UAAM,aAAa,aAAa,CAAC,KAAK,QAAQ;AAC5C,WAAK,UAAU,cAAc,KAAK,GAAG;AAAA,IACvC,CAAC;AACD,UAAM,OAAO,QAAQ,SAAS;AAC9B,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,iBAAW,OAAO,OAAO,MAAM,OAAO;AAAA,IACxC,CAAC;AACD,YAAQ,MAAM,mDAAmD,OAAO,IAAI,EAAE;AAAA,EAChF,OAAO;AACL,UAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAM,OAAO,QAAQ,SAAS;AAAA,EAChC;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,gBAAgB,KAAK;AACnC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["server","z","z","server","z","z","server","z","z","server","require"]}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@thisispamela/mcp",
3
+ "version": "1.1.0",
4
+ "description": "Pamela Voice API MCP server — tools for AI assistants",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "bin": {
16
+ "pamela-mcp": "dist/index.js"
17
+ },
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "pamela",
24
+ "voice",
25
+ "api",
26
+ "mcp",
27
+ "model-context-protocol"
28
+ ],
29
+ "author": "Pamela",
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.0.0",
33
+ "@thisispamela/sdk": "^1.1.0",
34
+ "zod": "^3.23.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^20.0.0",
38
+ "tsup": "^8.0.0",
39
+ "typescript": "^5.0.0"
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "README.md"
44
+ ]
45
+ }