@alfe.ai/mcp-tools 0.2.6 → 0.2.8

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.cjs CHANGED
@@ -37,20 +37,6 @@ const OAUTH_PROVIDERS = [
37
37
  "x",
38
38
  "xero"
39
39
  ];
40
- function oauthStartUrl(ctx, provider, scopes, shop) {
41
- if (!ctx.agentId || ctx.agentId.length > 256 || !ctx.tenantId || ctx.tenantId.length > 256) throw new Error("OAuth context is missing a valid agent or tenant identity.");
42
- const url = new URL(ctx.apiUrl);
43
- if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error("OAuth API URL must be an HTTP(S) URL without embedded credentials.");
44
- url.hash = "";
45
- url.search = "";
46
- url.pathname = `${url.pathname.replace(/\/+$/u, "")}/connect/oauth/start`;
47
- url.searchParams.set("provider", provider);
48
- url.searchParams.set("tenantId", ctx.tenantId);
49
- url.searchParams.set("scope", `agent:${ctx.agentId}`);
50
- if (scopes) url.searchParams.set("scopes", scopes);
51
- if (shop) url.searchParams.set("shop", shop);
52
- return url.toString();
53
- }
54
40
  /**
55
41
  * Thin-slice subset of the `services/mcp` `integrations` domain — only
56
42
  * the two subtools whose underlying calls already hit public
@@ -61,9 +47,8 @@ function oauthStartUrl(ctx, provider, scopes, shop) {
61
47
  *
62
48
  * - `integrations_browse_registry` — public `GET /integrations/registry`
63
49
  * via `AgentApiClient.getRegistry()`.
64
- * - `integrations_start_oauth` — generates the public
65
- * universal `GET /connect/oauth/start` URL for the user to open in a browser.
66
- * Pure URL builder; no server-side call. Completion is confirmed out of
50
+ * - `integrations_start_oauth` — asks the agent-authenticated API to mint a
51
+ * one-use OAuth launch URL for the user to open in a browser. Completion is confirmed out of
67
52
  * band (the dashboard, or the hosted MCP server's
68
53
  * `integrations_check_oauth_status`) — the local server intentionally has
69
54
  * no status-check tool yet.
@@ -86,18 +71,18 @@ const registerIntegrationsTools = (server, ctx) => {
86
71
  scopes: zod.z.string().trim().min(1).max(1024).optional().describe("Comma-separated scope groups (e.g. 'gmail,drive' for Google)"),
87
72
  shop: zod.z.string().trim().min(1).max(512).optional().describe("Shopify store handle or myshopify.com domain (required for Shopify only)")
88
73
  }
89
- }, ({ provider, scopes, shop }) => {
74
+ }, async ({ provider, scopes, shop }) => {
90
75
  try {
91
- if (provider === "shopify" && !shop) return Promise.resolve(err("Shopify OAuth requires the shop handle or myshopify.com domain."));
92
- if (provider !== "shopify" && shop) return Promise.resolve(err("The shop argument is only valid for Shopify OAuth."));
93
- const url = oauthStartUrl(ctx, provider, scopes, shop);
94
- return Promise.resolve(ok({
95
- url,
76
+ if (provider === "shopify" && !shop) return err("Shopify OAuth requires the shop handle or myshopify.com domain.");
77
+ if (provider !== "shopify" && shop) return err("The shop argument is only valid for Shopify OAuth.");
78
+ const result = await ctx.client.getOAuthUrl(provider, scopes?.split(",").map((value) => value.trim()).filter(Boolean), shop ? { shop } : void 0);
79
+ return ok({
80
+ url: result.url,
96
81
  provider,
97
- message: `Open this URL to connect ${provider}: ${url}`
98
- }));
82
+ message: `Open this URL to connect ${provider}: ${result.url}`
83
+ });
99
84
  } catch (error) {
100
- return Promise.resolve(err(error instanceof Error ? error.message : "Failed to generate OAuth URL"));
85
+ return err(error instanceof Error ? error.message : "Failed to generate OAuth URL");
101
86
  }
102
87
  });
103
88
  };
package/dist/index.d.cts CHANGED
@@ -81,9 +81,8 @@ declare function err(message: string): ToolResult;
81
81
  *
82
82
  * - `integrations_browse_registry` — public `GET /integrations/registry`
83
83
  * via `AgentApiClient.getRegistry()`.
84
- * - `integrations_start_oauth` — generates the public
85
- * universal `GET /connect/oauth/start` URL for the user to open in a browser.
86
- * Pure URL builder; no server-side call. Completion is confirmed out of
84
+ * - `integrations_start_oauth` — asks the agent-authenticated API to mint a
85
+ * one-use OAuth launch URL for the user to open in a browser. Completion is confirmed out of
87
86
  * band (the dashboard, or the hosted MCP server's
88
87
  * `integrations_check_oauth_status`) — the local server intentionally has
89
88
  * no status-check tool yet.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/integrations.ts","../src/memory.ts","../src/voice.ts","../src/messaging.ts"],"mappings":";;;;;;;AAcA;;;;;AAkCA;;;;AAAkE,UAlCjD,WAAA,CAkCiD;EAYjD,MAAA,EA7CP,cA6CiB;EAMX,MAAE,EAAA,MAAA;EAMF,OAAG,EAAA,MAAA;;;;ACXnB;;;;AC/BA;;;;ACAA;;;;ACjBA;;;gBJuBgB;;;;;;;;;;;KAYJ,eAAA,YAA2B,gBAAgB;;;;;;;;;;;UAYtC,UAAA;;;;;;;;iBAMD,EAAA,iBAAmB;iBAMnB,GAAA,mBAAsB;;;;;;;AA1DtC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;;cCXa,2BAA2B;;;;;;;;AD/CxC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;;;;ACXA;cC/Ba,qBAAqB;;;;;;;AFhBlC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;cG1Ca,oBAAoB;;;;;;;AHhBjC;;;;;AAkCA;;AAAuC,cInC1B,sBJmC0B,EInCF,eJmCE"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/integrations.ts","../src/memory.ts","../src/voice.ts","../src/messaging.ts"],"mappings":";;;;;;;AAcA;;;;;AAkCA;;;;AAAkE,UAlCjD,WAAA,CAkCiD;EAYjD,MAAA,EA7CP,cA6CiB;EAMX,MAAE,EAAA,MAAA;EAMF,OAAG,EAAA,MAAA;;;;ACrCnB;;;;ACLA;;;;ACAA;;;;ACjBA;;;gBJuBgB;;;;;;;;;;;KAYJ,eAAA,YAA2B,gBAAgB;;;;;;;;;;;UAYtC,UAAA;;;;;;;;iBAMD,EAAA,iBAAmB;iBAMnB,GAAA,mBAAsB;;;;;;;AA1DtC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;cCrCa,2BAA2B;;;;;;;;ADrBxC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;;;;ACrCA;cCLa,qBAAqB;;;;;;;AFhBlC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;cG1Ca,oBAAoB;;;;;;;AHhBjC;;;;;AAkCA;;AAAuC,cInC1B,sBJmC0B,EInCF,eJmCE"}
package/dist/index.d.ts CHANGED
@@ -81,9 +81,8 @@ declare function err(message: string): ToolResult;
81
81
  *
82
82
  * - `integrations_browse_registry` — public `GET /integrations/registry`
83
83
  * via `AgentApiClient.getRegistry()`.
84
- * - `integrations_start_oauth` — generates the public
85
- * universal `GET /connect/oauth/start` URL for the user to open in a browser.
86
- * Pure URL builder; no server-side call. Completion is confirmed out of
84
+ * - `integrations_start_oauth` — asks the agent-authenticated API to mint a
85
+ * one-use OAuth launch URL for the user to open in a browser. Completion is confirmed out of
87
86
  * band (the dashboard, or the hosted MCP server's
88
87
  * `integrations_check_oauth_status`) — the local server intentionally has
89
88
  * no status-check tool yet.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/integrations.ts","../src/memory.ts","../src/voice.ts","../src/messaging.ts"],"mappings":";;;;;;;AAcA;;;;;AAkCA;;;;AAAkE,UAlCjD,WAAA,CAkCiD;EAYjD,MAAA,EA7CP,cA6CiB;EAMX,MAAE,EAAA,MAAA;EAMF,OAAG,EAAA,MAAA;;;;ACXnB;;;;AC/BA;;;;ACAA;;;;ACjBA;;;gBJuBgB;;;;;;;;;;;KAYJ,eAAA,YAA2B,gBAAgB;;;;;;;;;;;UAYtC,UAAA;;;;;;;;iBAMD,EAAA,iBAAmB;iBAMnB,GAAA,mBAAsB;;;;;;;AA1DtC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;;cCXa,2BAA2B;;;;;;;;AD/CxC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;;;;ACXA;cC/Ba,qBAAqB;;;;;;;AFhBlC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;cG1Ca,oBAAoB;;;;;;;AHhBjC;;;;;AAkCA;;AAAuC,cInC1B,sBJmC0B,EInCF,eJmCE"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/integrations.ts","../src/memory.ts","../src/voice.ts","../src/messaging.ts"],"mappings":";;;;;;;AAcA;;;;;AAkCA;;;;AAAkE,UAlCjD,WAAA,CAkCiD;EAYjD,MAAA,EA7CP,cA6CiB;EAMX,MAAE,EAAA,MAAA;EAMF,OAAG,EAAA,MAAA;;;;ACrCnB;;;;ACLA;;;;ACAA;;;;ACjBA;;;gBJuBgB;;;;;;;;;;;KAYJ,eAAA,YAA2B,gBAAgB;;;;;;;;;;;UAYtC,UAAA;;;;;;;;iBAMD,EAAA,iBAAmB;iBAMnB,GAAA,mBAAsB;;;;;;;AA1DtC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;cCrCa,2BAA2B;;;;;;;;ADrBxC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;;;;ACrCA;cCLa,qBAAqB;;;;;;;AFhBlC;;;;;AAkCA;;;;;AAYA;AAMA;AAMA;cG1Ca,oBAAoB;;;;;;;AHhBjC;;;;;AAkCA;;AAAuC,cInC1B,sBJmC0B,EInCF,eJmCE"}
package/dist/index.js CHANGED
@@ -36,20 +36,6 @@ const OAUTH_PROVIDERS = [
36
36
  "x",
37
37
  "xero"
38
38
  ];
39
- function oauthStartUrl(ctx, provider, scopes, shop) {
40
- if (!ctx.agentId || ctx.agentId.length > 256 || !ctx.tenantId || ctx.tenantId.length > 256) throw new Error("OAuth context is missing a valid agent or tenant identity.");
41
- const url = new URL(ctx.apiUrl);
42
- if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error("OAuth API URL must be an HTTP(S) URL without embedded credentials.");
43
- url.hash = "";
44
- url.search = "";
45
- url.pathname = `${url.pathname.replace(/\/+$/u, "")}/connect/oauth/start`;
46
- url.searchParams.set("provider", provider);
47
- url.searchParams.set("tenantId", ctx.tenantId);
48
- url.searchParams.set("scope", `agent:${ctx.agentId}`);
49
- if (scopes) url.searchParams.set("scopes", scopes);
50
- if (shop) url.searchParams.set("shop", shop);
51
- return url.toString();
52
- }
53
39
  /**
54
40
  * Thin-slice subset of the `services/mcp` `integrations` domain — only
55
41
  * the two subtools whose underlying calls already hit public
@@ -60,9 +46,8 @@ function oauthStartUrl(ctx, provider, scopes, shop) {
60
46
  *
61
47
  * - `integrations_browse_registry` — public `GET /integrations/registry`
62
48
  * via `AgentApiClient.getRegistry()`.
63
- * - `integrations_start_oauth` — generates the public
64
- * universal `GET /connect/oauth/start` URL for the user to open in a browser.
65
- * Pure URL builder; no server-side call. Completion is confirmed out of
49
+ * - `integrations_start_oauth` — asks the agent-authenticated API to mint a
50
+ * one-use OAuth launch URL for the user to open in a browser. Completion is confirmed out of
66
51
  * band (the dashboard, or the hosted MCP server's
67
52
  * `integrations_check_oauth_status`) — the local server intentionally has
68
53
  * no status-check tool yet.
@@ -85,18 +70,18 @@ const registerIntegrationsTools = (server, ctx) => {
85
70
  scopes: z.string().trim().min(1).max(1024).optional().describe("Comma-separated scope groups (e.g. 'gmail,drive' for Google)"),
86
71
  shop: z.string().trim().min(1).max(512).optional().describe("Shopify store handle or myshopify.com domain (required for Shopify only)")
87
72
  }
88
- }, ({ provider, scopes, shop }) => {
73
+ }, async ({ provider, scopes, shop }) => {
89
74
  try {
90
- if (provider === "shopify" && !shop) return Promise.resolve(err("Shopify OAuth requires the shop handle or myshopify.com domain."));
91
- if (provider !== "shopify" && shop) return Promise.resolve(err("The shop argument is only valid for Shopify OAuth."));
92
- const url = oauthStartUrl(ctx, provider, scopes, shop);
93
- return Promise.resolve(ok({
94
- url,
75
+ if (provider === "shopify" && !shop) return err("Shopify OAuth requires the shop handle or myshopify.com domain.");
76
+ if (provider !== "shopify" && shop) return err("The shop argument is only valid for Shopify OAuth.");
77
+ const result = await ctx.client.getOAuthUrl(provider, scopes?.split(",").map((value) => value.trim()).filter(Boolean), shop ? { shop } : void 0);
78
+ return ok({
79
+ url: result.url,
95
80
  provider,
96
- message: `Open this URL to connect ${provider}: ${url}`
97
- }));
81
+ message: `Open this URL to connect ${provider}: ${result.url}`
82
+ });
98
83
  } catch (error) {
99
- return Promise.resolve(err(error instanceof Error ? error.message : "Failed to generate OAuth URL"));
84
+ return err(error instanceof Error ? error.message : "Failed to generate OAuth URL");
100
85
  }
101
86
  });
102
87
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/types.ts","../src/integrations.ts","../src/workspace-files.ts","../src/memory.ts","../src/audio.ts","../src/voice.ts","../src/messaging.ts"],"sourcesContent":["import type { AgentApiClient } from '@alfe.ai/agent-api-client';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\n/**\n * Context every tool registration function receives. The same context is\n * used by the CLI-bundled local server (`@alfe.ai/mcp-server`) and the\n * eventual lift target for `services/mcp` once each domain's public\n * `/agent/...` endpoints land.\n *\n * `agentId` / `tenantId` are derived from `client.whoami()` at the local\n * server's startup. They're carried in the context so tools that need\n * them (e.g. OAuth-URL builders) don't have to re-issue a whoami call\n * per invocation.\n */\nexport interface ToolContext {\n client: AgentApiClient;\n apiUrl: string;\n agentId: string;\n tenantId: string;\n /**\n * Base URL for the voice service's agent-authed one-shot endpoints\n * (`/voice/tts`, `/voice/stt`). Currently equals `apiUrl` (the endpoints\n * are co-located on the shared api gateway) but carried separately so the\n * voice tools have a stable seam if voice ever moves hosts — mirrors\n * `ResolvedConfig.voiceServiceUrl` in `@alfe.ai/config`. Only consumed by\n * `registerVoiceTools`; when omitted, the voice tools fall back to `apiUrl`.\n */\n voiceApiUrl?: string;\n /**\n * A dedicated `AgentApiClient` whose `apiUrl` is the voice service base\n * (`voiceApiUrl`). `AgentApiClient.tts`/`stt` hit `/voice/tts` and\n * `/voice/stt` RELATIVE to the client's own `apiUrl`, so the voice tools\n * need a client keyed on the voice URL rather than the main `client`. The\n * server (which holds the api key) builds this; `registerVoiceTools` falls\n * back to `client` if it's absent (safe today since voice is co-located).\n */\n voiceClient?: AgentApiClient;\n}\n\n/**\n * Tool registration function shape. Each domain module exports one of\n * these; the server calls them with the live `McpServer` and a\n * `ToolContext`. Implementations call `server.registerTool(name, …)` for\n * each subtool they own.\n *\n * Tool names follow the `<domain>_<verb>` convention (no `alfe_` prefix —\n * the bundler namespaces the surface as `mcp__alfe-platform__<name>`).\n */\nexport type RegisterToolsFn = (server: McpServer, ctx: ToolContext) => void;\n\n/**\n * Standard error-result envelope mirroring the existing `services/mcp`\n * shape so the lifted tool surface stays drop-in compatible for any\n * downstream MCP client.\n *\n * The index signature satisfies the MCP SDK's `CallToolResult` shape —\n * the SDK declares its result type with `[x: string]: unknown` so\n * callers can add transport-level metadata. We don't use any, but the\n * shape needs to be assignable for the registration callback to typecheck.\n */\nexport interface ToolResult {\n content: { type: 'text'; text: string }[];\n isError?: boolean;\n [key: string]: unknown;\n}\n\nexport function ok(data: unknown): ToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n}\n\nexport function err(message: string): ToolResult {\n return {\n content: [{ type: 'text', text: message }],\n isError: true,\n };\n}\n","import { z } from 'zod';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\n\nconst OAUTH_PROVIDERS = [\n 'atlassian',\n 'ctrader',\n 'discord',\n 'github',\n 'google',\n 'microsoft',\n 'myob',\n 'notion',\n 'salesforce',\n 'shopify',\n 'slack',\n 'x',\n 'xero',\n] as const;\n\nfunction oauthStartUrl(\n ctx: ToolContext,\n provider: (typeof OAUTH_PROVIDERS)[number],\n scopes?: string,\n shop?: string,\n): string {\n if (!ctx.agentId || ctx.agentId.length > 256 || !ctx.tenantId || ctx.tenantId.length > 256) {\n throw new Error('OAuth context is missing a valid agent or tenant identity.');\n }\n\n const url = new URL(ctx.apiUrl);\n if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) {\n throw new Error('OAuth API URL must be an HTTP(S) URL without embedded credentials.');\n }\n url.hash = '';\n url.search = '';\n url.pathname = `${url.pathname.replace(/\\/+$/u, '')}/connect/oauth/start`;\n url.searchParams.set('provider', provider);\n url.searchParams.set('tenantId', ctx.tenantId);\n url.searchParams.set('scope', `agent:${ctx.agentId}`);\n if (scopes) url.searchParams.set('scopes', scopes);\n if (shop) url.searchParams.set('shop', shop);\n return url.toString();\n}\n\n/**\n * Thin-slice subset of the `services/mcp` `integrations` domain — only\n * the two subtools whose underlying calls already hit public\n * `/agent/...` (or unauthenticated public) endpoints today. The rest of\n * the domain stays in `services/mcp` until the per-domain endpoint\n * promotion workstream lifts it (see\n * `project_mcp_tool_lift_workstream.md`).\n *\n * - `integrations_browse_registry` — public `GET /integrations/registry`\n * via `AgentApiClient.getRegistry()`.\n * - `integrations_start_oauth` — generates the public\n * universal `GET /connect/oauth/start` URL for the user to open in a browser.\n * Pure URL builder; no server-side call. Completion is confirmed out of\n * band (the dashboard, or the hosted MCP server's\n * `integrations_check_oauth_status`) — the local server intentionally has\n * no status-check tool yet.\n */\nexport const registerIntegrationsTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n server.registerTool(\n 'integrations_browse_registry',\n {\n description:\n 'Browse available integrations in the registry. Returns the catalogue of all published integrations with their manifests.',\n inputSchema: {},\n },\n async () => {\n try {\n const data = await ctx.client.getRegistry();\n return ok(data);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'Failed to browse integrations');\n }\n },\n );\n\n server.registerTool(\n 'integrations_start_oauth',\n {\n description:\n 'Start an OAuth connection flow for an integration. Returns a URL the user must open in their browser to authorize the connection. After the user authorizes, the connection appears in the Alfe dashboard — this local server does not expose a status-check tool, so confirm completion there.',\n inputSchema: {\n provider: z\n .enum(OAUTH_PROVIDERS)\n .describe('The OAuth provider to connect'),\n scopes: z\n .string()\n .trim()\n .min(1)\n .max(1024)\n .optional()\n .describe(\"Comma-separated scope groups (e.g. 'gmail,drive' for Google)\"),\n shop: z\n .string()\n .trim()\n .min(1)\n .max(512)\n .optional()\n .describe('Shopify store handle or myshopify.com domain (required for Shopify only)'),\n },\n },\n ({ provider, scopes, shop }: {\n provider: (typeof OAUTH_PROVIDERS)[number];\n scopes?: string;\n shop?: string;\n }) => {\n try {\n if (provider === 'shopify' && !shop) {\n return Promise.resolve(\n err('Shopify OAuth requires the shop handle or myshopify.com domain.'),\n );\n }\n if (provider !== 'shopify' && shop) {\n return Promise.resolve(err('The shop argument is only valid for Shopify OAuth.'));\n }\n const url = oauthStartUrl(ctx, provider, scopes, shop);\n return Promise.resolve(\n ok({\n url,\n provider,\n message: `Open this URL to connect ${provider}: ${url}`,\n }),\n );\n } catch (error: unknown) {\n return Promise.resolve(\n err(error instanceof Error ? error.message : 'Failed to generate OAuth URL'),\n );\n }\n },\n );\n};\n","import { constants } from 'node:fs';\nimport { lstat, open, realpath, unlink } from 'node:fs/promises';\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nconst MAX_TOOL_PATH_LENGTH = 1024;\n\nfunction isWithin(root: string, candidate: string): boolean {\n const rel = relative(root, candidate);\n return rel === '' || (!isAbsolute(rel) && rel !== '..' && !rel.startsWith(`..${sep}`));\n}\n\nfunction assertRelativeToolPath(input: string, label: string): void {\n if (input.length === 0) throw new Error(`${label} must not be empty.`);\n if (input.length > MAX_TOOL_PATH_LENGTH) {\n throw new Error(`${label} is too long (maximum ${String(MAX_TOOL_PATH_LENGTH)} characters).`);\n }\n if (input.includes('\\0')) throw new Error(`${label} contains a null byte.`);\n if (isAbsolute(input) || /^[A-Za-z]:[\\\\/]/u.test(input) || input.startsWith('\\\\\\\\')) {\n throw new Error(`${label} must be relative to the workspace.`);\n }\n if (input.split(/[\\\\/]/u).includes('..')) {\n throw new Error(`${label} must not contain parent-directory traversal.`);\n }\n}\n\nasync function workspaceRoot(): Promise<string> {\n return realpath(process.cwd());\n}\n\n/**\n * Read a regular file that resolves inside the current workspace.\n *\n * The lexical check rejects obvious traversal, while `realpath` closes the\n * symlink escape that a simple `resolve(...).startsWith(...)` check leaves\n * open. The descriptor is opened with `O_NOFOLLOW`, then size-checked both\n * before and after the read so a growing file cannot bypass the byte cap.\n */\nexport async function readWorkspaceFile(\n input: string,\n maxBytes: number,\n): Promise<{ absolutePath: string; data: Buffer }> {\n assertRelativeToolPath(input, 'Path');\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) {\n throw new Error('Maximum file size must be a positive safe integer.');\n }\n\n const root = await workspaceRoot();\n const lexicalPath = resolve(root, input);\n if (!isWithin(root, lexicalPath)) throw new Error('Path escapes the workspace.');\n\n const absolutePath = await realpath(lexicalPath);\n if (!isWithin(root, absolutePath)) throw new Error('Path resolves outside the workspace.');\n\n const handle = await open(absolutePath, constants.O_RDONLY | constants.O_NOFOLLOW);\n try {\n const before = await handle.stat();\n if (!before.isFile()) throw new Error('Path must refer to a regular file.');\n if (before.size > maxBytes) {\n throw new Error(`File is too large (maximum ${String(maxBytes)} bytes).`);\n }\n\n const data = await handle.readFile();\n const after = await handle.stat();\n if (data.length > maxBytes || after.size > maxBytes) {\n throw new Error(`File is too large (maximum ${String(maxBytes)} bytes).`);\n }\n return { absolutePath, data };\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Write a new file beneath the current workspace without following a parent\n * symlink outside it and without replacing an existing file.\n */\nexport async function resolveWorkspaceOutputPath(input: string): Promise<string> {\n assertRelativeToolPath(input, 'Output path');\n\n const root = await workspaceRoot();\n const lexicalPath = resolve(root, input);\n if (!isWithin(root, lexicalPath)) throw new Error('Output path escapes the workspace.');\n\n const parent = await realpath(dirname(lexicalPath));\n if (!isWithin(root, parent)) throw new Error('Output path resolves outside the workspace.');\n const absolutePath = join(parent, basename(lexicalPath));\n\n try {\n await lstat(absolutePath);\n throw new Error(`Refusing to overwrite existing workspace file: ${input}`);\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n return absolutePath;\n}\n\nexport async function writeWorkspaceFileExclusive(input: string, data: Buffer): Promise<string> {\n const absolutePath = await resolveWorkspaceOutputPath(input);\n\n let handle;\n try {\n handle = await open(absolutePath, 'wx', 0o600);\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new Error(`Refusing to overwrite existing workspace file: ${input}`);\n }\n throw error;\n }\n\n let completed = false;\n try {\n await handle.writeFile(data);\n await handle.sync();\n completed = true;\n return absolutePath;\n } finally {\n await handle.close();\n if (!completed) {\n await unlink(absolutePath).catch(() => undefined);\n }\n }\n}\n","import { z } from 'zod';\nimport { TextDecoder } from 'node:util';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\nimport { readWorkspaceFile } from './workspace-files.js';\n\nconst MEMORY_TAGS = ['fact', 'decision', 'preference', 'event', 'discovery'] as const;\ntype MemoryTag = (typeof MEMORY_TAGS)[number];\nconst MAX_MEMORY_TEXT_CHARACTERS = 100_000;\nconst MAX_MEMORY_FILE_BYTES = MAX_MEMORY_TEXT_CHARACTERS * 4;\n\n/**\n * Memory tools — a runtime-agnostic mirror of the OpenClaw memory-cloud\n * plugin (`@alfe.ai/openclaw-memory-cloud`). All 7 tools (memory_recall,\n * memory_store, memory_learn, memory_forget, memory_graph, memory_stats,\n * memory_navigate) keep the plugin's names, descriptions, and argument\n * schemas so an agent gets the same memory behaviour whether it runs under\n * OpenClaw (via the plugin) or a Claude Code session (via this MCP server's\n * `claude-code` profile). Each subtool is a thin wrapper over the\n * corresponding `AgentApiClient` memory method against public\n * `/agent/memory/...` endpoints.\n *\n * One deliberate divergence: the plugin registers `memory_recall` with a\n * `memory_search` alias, but the MCP `registerTool` API is single-name, so\n * the alias is dropped here (agents should call `memory_recall`).\n *\n * NOTE for OpenClaw agents: these tools are NOT registered in the default\n * server profile — OpenClaw agents already get `memory_*` from their\n * plugin, and registering them here would double the surface. They only\n * register under the `claude-code` profile.\n */\nexport const registerMemoryTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n server.registerTool(\n 'memory_recall',\n {\n description:\n 'Search conversation memory and knowledge graph. Returns structured facts and relevant conversation excerpts.',\n inputSchema: {\n query: z.string().trim().min(1).max(4000).describe('What to search for'),\n limit: z.coerce.number().int().min(1).max(50).optional().describe('Maximum results (default 10)'),\n topic: z.string().trim().min(1).max(256).optional().describe('Filter by topic'),\n tag: z\n .enum(MEMORY_TAGS)\n .optional()\n .describe('Filter by tag (fact/decision/preference/event/discovery)'),\n },\n },\n async (args: { query: string; limit?: number; topic?: string; tag?: MemoryTag }) => {\n try {\n const results = await ctx.client.memorySearch(args.query, {\n limit: args.limit,\n topic: args.topic,\n tag: args.tag,\n });\n return ok(results);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_recall failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_store',\n {\n description: 'Explicitly save a piece of information to long-term memory.',\n inputSchema: {\n text: z.string().trim().min(1).max(5000).describe('The information to remember'),\n topic: z.string().trim().min(1).max(128).optional().describe('Topic category (e.g., person name, project)'),\n tag: z\n .enum(MEMORY_TAGS)\n .optional()\n .describe('Memory type: fact, decision, preference, event, discovery'),\n importance: z.coerce.number().min(0).max(1).optional().describe('Importance 0-1 (default 0.7)'),\n },\n },\n async (args: { text: string; topic?: string; tag?: MemoryTag; importance?: number }) => {\n try {\n const result = await ctx.client.memoryStore(args.text, {\n topic: args.topic,\n tag: args.tag,\n importance: args.importance,\n });\n return ok({ memoryId: result.memoryId, message: `Stored memory: ${result.memoryId}` });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_store failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_learn',\n {\n description:\n 'Save arbitrary content (a paragraph, a doc, a fact list) into long-term memory. The system auto-classifies and extracts entities. Use this when the user asks you to learn, remember, or study something — or when you read a document worth retaining. Pass the source text directly; do not paraphrase first.',\n inputSchema: {\n content: z\n .string()\n .min(1)\n .max(MAX_MEMORY_TEXT_CHARACTERS)\n .optional()\n .describe('Inline text to ingest. Provide either content OR path, not both.'),\n path: z\n .string()\n .min(1)\n .max(1024)\n .optional()\n .describe(\n 'Workspace-relative file path to read and ingest. Provide either content OR path, not both.',\n ),\n source: z\n .string()\n .min(1)\n .max(512)\n .optional()\n .describe('Optional label describing where the content came from (e.g. doc title, url).'),\n },\n },\n async (args: { content?: string; path?: string; source?: string }) => {\n const content = typeof args.content === 'string' ? args.content : undefined;\n const filePath = typeof args.path === 'string' ? args.path : undefined;\n const source = typeof args.source === 'string' ? args.source : undefined;\n\n if (content === undefined && filePath === undefined) {\n return err('Error: provide either `content` or `path`.');\n }\n if (content !== undefined && filePath !== undefined) {\n return err('Error: provide either `content` or `path`, not both.');\n }\n if (source !== undefined && (source.length < 1 || source.length > 512)) {\n return err('Error: source must contain 1–512 characters.');\n }\n\n let text: string;\n let resolvedSource = source;\n let sourceType: 'file' | 'inline' = 'inline';\n\n if (filePath !== undefined) {\n try {\n const { data } = await readWorkspaceFile(filePath, MAX_MEMORY_FILE_BYTES);\n text = new TextDecoder('utf-8', { fatal: true }).decode(data);\n } catch (readErr: unknown) {\n return err(\n `Error reading \"${filePath}\": ${readErr instanceof Error ? readErr.message : String(readErr)}`,\n );\n }\n resolvedSource = source ?? filePath;\n sourceType = 'file';\n } else if (content !== undefined) {\n text = content;\n } else {\n // Unreachable — the both-or-neither cases returned above — but keeps\n // `text` definitely-assigned without a non-null assertion.\n return err('Error: provide either `content` or `path`.');\n }\n\n if (text.trim().length === 0 || text.length > MAX_MEMORY_TEXT_CHARACTERS) {\n return err(\n `Error: memory content must contain 1–${String(MAX_MEMORY_TEXT_CHARACTERS)} characters.`,\n );\n }\n\n try {\n const result = await ctx.client.memoryLearn({ text, source: resolvedSource, sourceType });\n const label = result.source ?? 'content';\n return ok({\n memoriesStored: result.memoriesStored,\n triplesStored: result.triplesStored,\n chunks: result.chunks,\n source: result.source,\n message: `Stored ${String(result.memoriesStored)} memories (${String(result.triplesStored)} facts, ${String(result.chunks)} chunks) from ${label}.`,\n });\n } catch (error: unknown) {\n return err(\n `Error: failed to store memory — ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n },\n );\n\n server.registerTool(\n 'memory_forget',\n {\n description: 'Search for and delete memories matching a query.',\n inputSchema: {\n query: z.string().trim().min(1).max(4000).describe('Search for memories to delete'),\n },\n },\n async (args: { query: string }) => {\n try {\n const results = await ctx.client.memorySearch(args.query, { limit: 5 });\n if (results.memories.length === 0) return ok({ deleted: 0, message: 'No matching memories found.' });\n\n const deleted: string[] = [];\n for (const mem of results.memories) {\n try {\n await ctx.client.memoryDelete(mem.id);\n deleted.push(mem.id);\n } catch {\n // best-effort — skip failures, mirror plugin behaviour\n }\n }\n return ok({\n deleted: deleted.length,\n matched: results.memories.length,\n message: `Deleted ${String(deleted.length)} of ${String(results.memories.length)} matching memories.`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_forget failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_graph',\n {\n description:\n 'Look up what you know about a specific entity from the knowledge graph.',\n inputSchema: {\n entity: z\n .string()\n .trim()\n .min(1)\n .max(512)\n .describe('The entity to look up (person, concept, system)'),\n },\n },\n async ({ entity }) => {\n try {\n const result = await ctx.client.memoryLookupEntity(entity);\n if (result.triples.length === 0) {\n return ok({\n subject: entity,\n triples: [],\n message: `No knowledge found about \"${entity}\".`,\n });\n }\n const facts = result.triples.map(\n (t) =>\n `- ${result.subject} ${t.predicate} ${t.object} (since ${t.validFrom.slice(0, 10)})`,\n );\n return ok({\n subject: result.subject,\n triples: result.triples,\n message: `Known facts about ${result.subject}:\\n${facts.join('\\n')}`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_graph failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_stats',\n {\n description: 'Get memory storage statistics.',\n inputSchema: {},\n },\n async () => {\n try {\n const s = await ctx.client.memoryStats();\n return ok({\n vectorCount: s.vectorCount,\n tripleCount: s.tripleCount,\n storageEstimateBytes: s.storageEstimateBytes,\n message: `Memories: ${String(s.vectorCount)}, Knowledge facts: ${String(s.tripleCount)}, Storage: ${String(Math.round(s.storageEstimateBytes / 1024))} KB`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_stats failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_navigate',\n {\n description:\n 'Browse your memory palace structure — list topics, subtopics, and memory counts.',\n inputSchema: {},\n },\n async () => {\n try {\n const nav = await ctx.client.memoryNavigate();\n return ok(nav);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_navigate failed');\n }\n },\n );\n};\n","/**\n * Minimal WAV (RIFF/PCM) helpers — no dependencies, no re-encoding.\n *\n * Ported from `@alfe.ai/openclaw-voice`'s `audio.ts` so the voice MCP tools\n * hand the agent a playable file (WAV) rather than headerless PCM, and strip\n * the header before sending audio to the linear16 STT endpoint. Kept\n * standalone (rather than importing the plugin) so mcp-tools stays\n * runtime-agnostic with no OpenClaw plugin dep.\n */\n\nexport interface PcmFraming {\n sampleRate: number;\n channels: number;\n bitDepth: number;\n}\n\nexport interface ParsedWav extends PcmFraming {\n /** Headerless PCM sample bytes (the `data` chunk payload). */\n pcm: Buffer;\n}\n\nconst MAX_WAV_PCM_BYTES = 0xffff_ffff - 36;\nconst SUPPORTED_BIT_DEPTHS = new Set([8, 16, 24, 32]);\n\nexport function validatePcmFraming(framing: PcmFraming, pcmLength: number): void {\n const { sampleRate, channels, bitDepth } = framing;\n if (!Number.isInteger(sampleRate) || sampleRate < 8_000 || sampleRate > 384_000) {\n throw new Error('WAV sample rate must be an integer between 8000 and 384000 Hz.');\n }\n if (!Number.isInteger(channels) || channels < 1 || channels > 32) {\n throw new Error('WAV channel count must be an integer between 1 and 32.');\n }\n if (!SUPPORTED_BIT_DEPTHS.has(bitDepth)) {\n throw new Error('WAV bit depth must be one of 8, 16, 24, or 32.');\n }\n const blockAlign = channels * (bitDepth / 8);\n if (pcmLength % blockAlign !== 0) {\n throw new Error('PCM byte length must contain a whole number of sample frames.');\n }\n if (pcmLength > MAX_WAV_PCM_BYTES) {\n throw new Error('PCM payload is too large for a RIFF/WAV container.');\n }\n if (sampleRate * blockAlign > 0xffff_ffff) {\n throw new Error('WAV byte rate exceeds the RIFF field limit.');\n }\n}\n\n/** Prepend a canonical 44-byte PCM WAV header to raw PCM samples. */\nexport function pcmToWav(pcm: Buffer, framing: PcmFraming): Buffer {\n validatePcmFraming(framing, pcm.length);\n const { sampleRate, channels, bitDepth } = framing;\n const bytesPerSample = bitDepth / 8;\n const blockAlign = channels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n\n const header = Buffer.alloc(44);\n header.write('RIFF', 0, 'ascii');\n header.writeUInt32LE(36 + pcm.length, 4);\n header.write('WAVE', 8, 'ascii');\n header.write('fmt ', 12, 'ascii');\n header.writeUInt32LE(16, 16); // fmt chunk size\n header.writeUInt16LE(1, 20); // audioFormat = PCM\n header.writeUInt16LE(channels, 22);\n header.writeUInt32LE(sampleRate, 24);\n header.writeUInt32LE(byteRate, 28);\n header.writeUInt16LE(blockAlign, 32);\n header.writeUInt16LE(bitDepth, 34);\n header.write('data', 36, 'ascii');\n header.writeUInt32LE(pcm.length, 40);\n\n return Buffer.concat([header, pcm]);\n}\n\n/**\n * Parse a WAV buffer into its PCM payload + framing. Returns `null` only when\n * the buffer is not RIFF data (caller may then treat it as raw PCM). A RIFF\n * buffer that claims to be WAV but is truncated, unsupported, or internally\n * inconsistent throws instead of silently uploading container bytes as PCM.\n */\nexport function parseWav(buf: Buffer): ParsedWav | null {\n if (buf.length < 4 || buf.toString('ascii', 0, 4) !== 'RIFF') return null;\n if (buf.length < 12) throw new Error('Malformed WAV: truncated RIFF header.');\n if (buf.toString('ascii', 8, 12) !== 'WAVE') {\n throw new Error('Unsupported RIFF container: expected WAVE.');\n }\n\n const declaredEnd = buf.readUInt32LE(4) + 8;\n if (declaredEnd !== buf.length) {\n throw new Error('Malformed WAV: RIFF size does not match the file length.');\n }\n\n let sampleRate = 0;\n let channels = 0;\n let bitDepth = 0;\n let pcm: Buffer | null = null;\n let blockAlign = 0;\n let byteRate = 0;\n let sawFmt = false;\n let sawData = false;\n\n let offset = 12;\n while (offset < declaredEnd) {\n if (offset + 8 > declaredEnd) throw new Error('Malformed WAV: truncated chunk header.');\n const chunkId = buf.toString('ascii', offset, offset + 4);\n const chunkSize = buf.readUInt32LE(offset + 4);\n const bodyStart = offset + 8;\n const bodyEnd = bodyStart + chunkSize;\n const nextOffset = bodyEnd + (chunkSize % 2);\n if (bodyEnd > declaredEnd || nextOffset > declaredEnd) {\n throw new Error(`Malformed WAV: truncated ${chunkId} chunk.`);\n }\n\n if (chunkId === 'fmt ') {\n if (sawFmt) throw new Error('Malformed WAV: duplicate fmt chunk.');\n if (chunkSize < 16) throw new Error('Malformed WAV: fmt chunk is too short.');\n const audioFormat = buf.readUInt16LE(bodyStart);\n if (audioFormat !== 1) throw new Error('Unsupported WAV encoding: only integer PCM is accepted.');\n channels = buf.readUInt16LE(bodyStart + 2);\n sampleRate = buf.readUInt32LE(bodyStart + 4);\n byteRate = buf.readUInt32LE(bodyStart + 8);\n blockAlign = buf.readUInt16LE(bodyStart + 12);\n bitDepth = buf.readUInt16LE(bodyStart + 14);\n sawFmt = true;\n } else if (chunkId === 'data') {\n if (sawData) throw new Error('Malformed WAV: duplicate data chunk.');\n pcm = buf.subarray(bodyStart, bodyEnd);\n sawData = true;\n }\n\n offset = nextOffset;\n }\n\n if (!sawFmt || !sawData || pcm === null) {\n throw new Error('Malformed WAV: both fmt and data chunks are required.');\n }\n validatePcmFraming({ sampleRate, channels, bitDepth }, pcm.length);\n const expectedBlockAlign = channels * (bitDepth / 8);\n if (blockAlign !== expectedBlockAlign || byteRate !== sampleRate * expectedBlockAlign) {\n throw new Error('Malformed WAV: byte rate or block alignment is inconsistent with its framing.');\n }\n return { pcm, sampleRate, channels, bitDepth };\n}\n","import { z } from 'zod';\nimport type { VoiceTtsModel } from '@alfe.ai/agent-api-client';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\nimport { pcmToWav, parseWav, validatePcmFraming } from './audio.js';\nimport {\n readWorkspaceFile,\n resolveWorkspaceOutputPath,\n writeWorkspaceFileExclusive,\n} from './workspace-files.js';\n\nconst MAX_AUDIO_FILE_BYTES = 10 * 1024 * 1024;\nconst MAX_TTS_RESPONSE_BYTES = 32 * 1024 * 1024;\nconst VOICE_ID_PATTERN = /^[A-Za-z0-9_-]{1,200}$/u;\n\n/**\n * Voice tools — a runtime-agnostic mirror of the one-shot TTS/STT tools in\n * the OpenClaw voice plugin (`@alfe.ai/openclaw-voice`). The channel-only\n * tools (hangup/transfer/dtmf) are intentionally omitted — they require a\n * live channel service (Discord/Twilio) and are meaningless in a standalone\n * MCP session.\n *\n * Both tools read/write files on disk (the MCP transport can't cleanly carry\n * raw audio bytes as JSON), converting between the voice service's headerless\n * PCM and a playable WAV via `./audio.js`.\n *\n * `tts`/`stt` on `AgentApiClient` hit `/voice/tts` and `/voice/stt` relative\n * to the client's `apiUrl`, so we prefer `ctx.voiceClient` (keyed on the\n * voice service URL) and fall back to `ctx.client` when it's absent (safe\n * today — voice is co-located on the shared api gateway).\n */\nexport const registerVoiceTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n const voiceClient = ctx.voiceClient ?? ctx.client;\n\n server.registerTool(\n 'voice_tts',\n {\n description:\n 'Convert text to speech using the Alfe voice service (ElevenLabs). Writes a playable audio file to the workspace and returns its path. Billed per character to your tenant credit pool.',\n inputSchema: {\n text: z.string().trim().min(1).max(5000).describe('Text to synthesize (1–5000 characters).'),\n outputPath: z\n .string()\n .min(1)\n .max(1024)\n .optional()\n .describe(\n 'New workspace-relative audio file to create. Existing files are never overwritten. Defaults to alfe-tts-<timestamp>.wav.',\n ),\n format: z\n .enum(['wav', 'pcm'])\n .optional()\n .describe(\n \"Output container. 'wav' (default) is a playable file; 'pcm' is headerless 24kHz/mono/16-bit raw PCM.\",\n ),\n voiceId: z\n .string()\n .regex(VOICE_ID_PATTERN)\n .optional()\n .describe('ElevenLabs voice ID. Platform default when omitted.'),\n model: z\n .enum(['eleven_turbo_v2_5', 'eleven_multilingual_v2'])\n .optional()\n .describe('TTS model. eleven_turbo_v2_5 (lower latency) when omitted.'),\n },\n },\n async (args: {\n text: string;\n outputPath?: string;\n format?: 'wav' | 'pcm';\n voiceId?: string;\n model?: VoiceTtsModel;\n }) => {\n try {\n const format = args.format ?? 'wav';\n const text = args.text.trim();\n if (text.length < 1 || text.length > 5000) {\n return err('voice_tts text must contain 1–5000 characters.');\n }\n if (args.voiceId !== undefined && !VOICE_ID_PATTERN.test(args.voiceId)) {\n return err('voice_tts voiceId contains unsupported characters.');\n }\n const outputPath = args.outputPath ?? `alfe-tts-${String(Date.now())}.${format}`;\n await resolveWorkspaceOutputPath(outputPath);\n\n const result = await voiceClient.tts({\n text,\n voiceId: args.voiceId,\n model: args.model,\n });\n if (result.audio.length === 0 || result.audio.length > MAX_TTS_RESPONSE_BYTES) {\n return err(`voice_tts returned an invalid audio payload size (${String(result.audio.length)} bytes).`);\n }\n validatePcmFraming(\n {\n sampleRate: result.sampleRate,\n channels: result.channels,\n bitDepth: result.bitDepth,\n },\n result.audio.length,\n );\n\n const bytes =\n format === 'wav'\n ? pcmToWav(result.audio, {\n sampleRate: result.sampleRate,\n channels: result.channels,\n bitDepth: result.bitDepth,\n })\n : result.audio;\n\n const absolutePath = await writeWorkspaceFileExclusive(outputPath, bytes);\n\n return ok({\n path: outputPath,\n absolutePath,\n format,\n sampleRate: result.sampleRate,\n channels: result.channels,\n bitDepth: result.bitDepth,\n bytes: bytes.length,\n characters: text.length,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'voice_tts failed');\n }\n },\n );\n\n server.registerTool(\n 'voice_stt',\n {\n description:\n 'Transcribe an audio file to text using the Alfe voice service (Deepgram). Accepts a WAV file or headerless mono 16-bit PCM. Billed per audio-second to your tenant credit pool.',\n inputSchema: {\n path: z\n .string()\n .min(1)\n .max(1024)\n .describe(\n 'Workspace-relative path to a WAV or raw linear16 mono PCM file (maximum 10 MiB).',\n ),\n sampleRate: z\n .coerce.number()\n .int()\n .min(8000)\n .max(48000)\n .optional()\n .describe(\n 'Sample rate in Hz (8000–48000). Only used for headerless PCM input; ignored for WAV (its header wins). Defaults to 24000.',\n ),\n },\n },\n async (args: { path: string; sampleRate?: number }) => {\n try {\n const { data: raw } = await readWorkspaceFile(args.path, MAX_AUDIO_FILE_BYTES);\n\n const wav = parseWav(raw);\n let audio: Buffer;\n let sampleRate: number;\n if (wav) {\n if (wav.bitDepth !== 16 || wav.channels !== 1) {\n return err(\n `voice_stt needs 16-bit mono audio; got ${String(wav.bitDepth)}-bit / ${String(wav.channels)}-channel WAV. Re-export as 16-bit mono.`,\n );\n }\n audio = wav.pcm;\n sampleRate = wav.sampleRate;\n } else {\n audio = raw;\n sampleRate = args.sampleRate ?? 24000;\n }\n\n if (!Number.isInteger(sampleRate) || sampleRate < 8000 || sampleRate > 48000) {\n return err('voice_stt sampleRate must be an integer between 8000 and 48000 Hz.');\n }\n if (audio.length === 0 || audio.length % 2 !== 0) {\n return err('voice_stt needs non-empty 16-bit PCM with an even byte length.');\n }\n\n const result = await voiceClient.stt({ audio, sampleRate });\n return ok(result);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'voice_stt failed');\n }\n },\n );\n};\n","import { z } from 'zod';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\n\n/**\n * Messaging tools — outbound SMS from the agent's own assigned phone number.\n *\n * `send_text_message` wraps `AgentApiClient.sendSms`, which hits the\n * agent-authed `POST /mobile/sms/send` endpoint. The FROM number is resolved\n * server-side from the agent identity on the token (an agent sends from the\n * single active number assigned to it, or gets a 400 if it has none) — the\n * tool only picks the recipient and body. The send is preflighted against\n * and metered to the tenant credit pool server-side.\n */\nexport const registerMessagingTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n server.registerTool(\n 'send_text_message',\n {\n description:\n \"Send an SMS text message to a phone number from your assigned phone number. The recipient is any E.164 number (e.g. +14155550123). You send from the single number assigned to you — you cannot choose the sender. Billed per message segment to your tenant credit pool. Fails if you have no active phone number.\",\n inputSchema: {\n to: z\n .string()\n .regex(/^\\+[1-9]\\d{6,14}$/u)\n .describe('Recipient phone number in E.164 format (e.g. +14155550123).'),\n body: z.string().min(1).max(4400).describe('The text message body to send.'),\n },\n },\n async (args: { to: string; body: string }) => {\n try {\n const result = await ctx.client.sendSms({ to: args.to, body: args.body });\n return ok({\n sent: result.sent,\n sid: result.sid,\n to: args.to,\n message: `Sent SMS to ${args.to} (sid: ${result.sid}).`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'send_text_message failed');\n }\n },\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/types.ts","../src/integrations.ts","../src/workspace-files.ts","../src/memory.ts","../src/audio.ts","../src/voice.ts","../src/messaging.ts"],"sourcesContent":["import type { AgentApiClient } from '@alfe.ai/agent-api-client';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\n/**\n * Context every tool registration function receives. The same context is\n * used by the CLI-bundled local server (`@alfe.ai/mcp-server`) and the\n * eventual lift target for `services/mcp` once each domain's public\n * `/agent/...` endpoints land.\n *\n * `agentId` / `tenantId` are derived from `client.whoami()` at the local\n * server's startup. They're carried in the context so tools that need\n * them (e.g. OAuth-URL builders) don't have to re-issue a whoami call\n * per invocation.\n */\nexport interface ToolContext {\n client: AgentApiClient;\n apiUrl: string;\n agentId: string;\n tenantId: string;\n /**\n * Base URL for the voice service's agent-authed one-shot endpoints\n * (`/voice/tts`, `/voice/stt`). Currently equals `apiUrl` (the endpoints\n * are co-located on the shared api gateway) but carried separately so the\n * voice tools have a stable seam if voice ever moves hosts — mirrors\n * `ResolvedConfig.voiceServiceUrl` in `@alfe.ai/config`. Only consumed by\n * `registerVoiceTools`; when omitted, the voice tools fall back to `apiUrl`.\n */\n voiceApiUrl?: string;\n /**\n * A dedicated `AgentApiClient` whose `apiUrl` is the voice service base\n * (`voiceApiUrl`). `AgentApiClient.tts`/`stt` hit `/voice/tts` and\n * `/voice/stt` RELATIVE to the client's own `apiUrl`, so the voice tools\n * need a client keyed on the voice URL rather than the main `client`. The\n * server (which holds the api key) builds this; `registerVoiceTools` falls\n * back to `client` if it's absent (safe today since voice is co-located).\n */\n voiceClient?: AgentApiClient;\n}\n\n/**\n * Tool registration function shape. Each domain module exports one of\n * these; the server calls them with the live `McpServer` and a\n * `ToolContext`. Implementations call `server.registerTool(name, …)` for\n * each subtool they own.\n *\n * Tool names follow the `<domain>_<verb>` convention (no `alfe_` prefix —\n * the bundler namespaces the surface as `mcp__alfe-platform__<name>`).\n */\nexport type RegisterToolsFn = (server: McpServer, ctx: ToolContext) => void;\n\n/**\n * Standard error-result envelope mirroring the existing `services/mcp`\n * shape so the lifted tool surface stays drop-in compatible for any\n * downstream MCP client.\n *\n * The index signature satisfies the MCP SDK's `CallToolResult` shape —\n * the SDK declares its result type with `[x: string]: unknown` so\n * callers can add transport-level metadata. We don't use any, but the\n * shape needs to be assignable for the registration callback to typecheck.\n */\nexport interface ToolResult {\n content: { type: 'text'; text: string }[];\n isError?: boolean;\n [key: string]: unknown;\n}\n\nexport function ok(data: unknown): ToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n };\n}\n\nexport function err(message: string): ToolResult {\n return {\n content: [{ type: 'text', text: message }],\n isError: true,\n };\n}\n","import { z } from 'zod';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\n\nconst OAUTH_PROVIDERS = [\n 'atlassian',\n 'ctrader',\n 'discord',\n 'github',\n 'google',\n 'microsoft',\n 'myob',\n 'notion',\n 'salesforce',\n 'shopify',\n 'slack',\n 'x',\n 'xero',\n] as const;\n\n/**\n * Thin-slice subset of the `services/mcp` `integrations` domain — only\n * the two subtools whose underlying calls already hit public\n * `/agent/...` (or unauthenticated public) endpoints today. The rest of\n * the domain stays in `services/mcp` until the per-domain endpoint\n * promotion workstream lifts it (see\n * `project_mcp_tool_lift_workstream.md`).\n *\n * - `integrations_browse_registry` — public `GET /integrations/registry`\n * via `AgentApiClient.getRegistry()`.\n * - `integrations_start_oauth` — asks the agent-authenticated API to mint a\n * one-use OAuth launch URL for the user to open in a browser. Completion is confirmed out of\n * band (the dashboard, or the hosted MCP server's\n * `integrations_check_oauth_status`) — the local server intentionally has\n * no status-check tool yet.\n */\nexport const registerIntegrationsTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n server.registerTool(\n 'integrations_browse_registry',\n {\n description:\n 'Browse available integrations in the registry. Returns the catalogue of all published integrations with their manifests.',\n inputSchema: {},\n },\n async () => {\n try {\n const data = await ctx.client.getRegistry();\n return ok(data);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'Failed to browse integrations');\n }\n },\n );\n\n server.registerTool(\n 'integrations_start_oauth',\n {\n description:\n 'Start an OAuth connection flow for an integration. Returns a URL the user must open in their browser to authorize the connection. After the user authorizes, the connection appears in the Alfe dashboard — this local server does not expose a status-check tool, so confirm completion there.',\n inputSchema: {\n provider: z\n .enum(OAUTH_PROVIDERS)\n .describe('The OAuth provider to connect'),\n scopes: z\n .string()\n .trim()\n .min(1)\n .max(1024)\n .optional()\n .describe(\"Comma-separated scope groups (e.g. 'gmail,drive' for Google)\"),\n shop: z\n .string()\n .trim()\n .min(1)\n .max(512)\n .optional()\n .describe('Shopify store handle or myshopify.com domain (required for Shopify only)'),\n },\n },\n async ({ provider, scopes, shop }: {\n provider: (typeof OAUTH_PROVIDERS)[number];\n scopes?: string;\n shop?: string;\n }) => {\n try {\n if (provider === 'shopify' && !shop) {\n return err('Shopify OAuth requires the shop handle or myshopify.com domain.');\n }\n if (provider !== 'shopify' && shop) {\n return err('The shop argument is only valid for Shopify OAuth.');\n }\n const result = await ctx.client.getOAuthUrl(\n provider,\n scopes?.split(',').map((value) => value.trim()).filter(Boolean),\n shop ? { shop } : undefined,\n );\n return ok({\n url: result.url,\n provider,\n message: `Open this URL to connect ${provider}: ${result.url}`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'Failed to generate OAuth URL');\n }\n },\n );\n};\n","import { constants } from 'node:fs';\nimport { lstat, open, realpath, unlink } from 'node:fs/promises';\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nconst MAX_TOOL_PATH_LENGTH = 1024;\n\nfunction isWithin(root: string, candidate: string): boolean {\n const rel = relative(root, candidate);\n return rel === '' || (!isAbsolute(rel) && rel !== '..' && !rel.startsWith(`..${sep}`));\n}\n\nfunction assertRelativeToolPath(input: string, label: string): void {\n if (input.length === 0) throw new Error(`${label} must not be empty.`);\n if (input.length > MAX_TOOL_PATH_LENGTH) {\n throw new Error(`${label} is too long (maximum ${String(MAX_TOOL_PATH_LENGTH)} characters).`);\n }\n if (input.includes('\\0')) throw new Error(`${label} contains a null byte.`);\n if (isAbsolute(input) || /^[A-Za-z]:[\\\\/]/u.test(input) || input.startsWith('\\\\\\\\')) {\n throw new Error(`${label} must be relative to the workspace.`);\n }\n if (input.split(/[\\\\/]/u).includes('..')) {\n throw new Error(`${label} must not contain parent-directory traversal.`);\n }\n}\n\nasync function workspaceRoot(): Promise<string> {\n return realpath(process.cwd());\n}\n\n/**\n * Read a regular file that resolves inside the current workspace.\n *\n * The lexical check rejects obvious traversal, while `realpath` closes the\n * symlink escape that a simple `resolve(...).startsWith(...)` check leaves\n * open. The descriptor is opened with `O_NOFOLLOW`, then size-checked both\n * before and after the read so a growing file cannot bypass the byte cap.\n */\nexport async function readWorkspaceFile(\n input: string,\n maxBytes: number,\n): Promise<{ absolutePath: string; data: Buffer }> {\n assertRelativeToolPath(input, 'Path');\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) {\n throw new Error('Maximum file size must be a positive safe integer.');\n }\n\n const root = await workspaceRoot();\n const lexicalPath = resolve(root, input);\n if (!isWithin(root, lexicalPath)) throw new Error('Path escapes the workspace.');\n\n const absolutePath = await realpath(lexicalPath);\n if (!isWithin(root, absolutePath)) throw new Error('Path resolves outside the workspace.');\n\n const handle = await open(absolutePath, constants.O_RDONLY | constants.O_NOFOLLOW);\n try {\n const before = await handle.stat();\n if (!before.isFile()) throw new Error('Path must refer to a regular file.');\n if (before.size > maxBytes) {\n throw new Error(`File is too large (maximum ${String(maxBytes)} bytes).`);\n }\n\n const data = await handle.readFile();\n const after = await handle.stat();\n if (data.length > maxBytes || after.size > maxBytes) {\n throw new Error(`File is too large (maximum ${String(maxBytes)} bytes).`);\n }\n return { absolutePath, data };\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Write a new file beneath the current workspace without following a parent\n * symlink outside it and without replacing an existing file.\n */\nexport async function resolveWorkspaceOutputPath(input: string): Promise<string> {\n assertRelativeToolPath(input, 'Output path');\n\n const root = await workspaceRoot();\n const lexicalPath = resolve(root, input);\n if (!isWithin(root, lexicalPath)) throw new Error('Output path escapes the workspace.');\n\n const parent = await realpath(dirname(lexicalPath));\n if (!isWithin(root, parent)) throw new Error('Output path resolves outside the workspace.');\n const absolutePath = join(parent, basename(lexicalPath));\n\n try {\n await lstat(absolutePath);\n throw new Error(`Refusing to overwrite existing workspace file: ${input}`);\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n }\n return absolutePath;\n}\n\nexport async function writeWorkspaceFileExclusive(input: string, data: Buffer): Promise<string> {\n const absolutePath = await resolveWorkspaceOutputPath(input);\n\n let handle;\n try {\n handle = await open(absolutePath, 'wx', 0o600);\n } catch (error: unknown) {\n if ((error as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new Error(`Refusing to overwrite existing workspace file: ${input}`);\n }\n throw error;\n }\n\n let completed = false;\n try {\n await handle.writeFile(data);\n await handle.sync();\n completed = true;\n return absolutePath;\n } finally {\n await handle.close();\n if (!completed) {\n await unlink(absolutePath).catch(() => undefined);\n }\n }\n}\n","import { z } from 'zod';\nimport { TextDecoder } from 'node:util';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\nimport { readWorkspaceFile } from './workspace-files.js';\n\nconst MEMORY_TAGS = ['fact', 'decision', 'preference', 'event', 'discovery'] as const;\ntype MemoryTag = (typeof MEMORY_TAGS)[number];\nconst MAX_MEMORY_TEXT_CHARACTERS = 100_000;\nconst MAX_MEMORY_FILE_BYTES = MAX_MEMORY_TEXT_CHARACTERS * 4;\n\n/**\n * Memory tools — a runtime-agnostic mirror of the OpenClaw memory-cloud\n * plugin (`@alfe.ai/openclaw-memory-cloud`). All 7 tools (memory_recall,\n * memory_store, memory_learn, memory_forget, memory_graph, memory_stats,\n * memory_navigate) keep the plugin's names, descriptions, and argument\n * schemas so an agent gets the same memory behaviour whether it runs under\n * OpenClaw (via the plugin) or a Claude Code session (via this MCP server's\n * `claude-code` profile). Each subtool is a thin wrapper over the\n * corresponding `AgentApiClient` memory method against public\n * `/agent/memory/...` endpoints.\n *\n * One deliberate divergence: the plugin registers `memory_recall` with a\n * `memory_search` alias, but the MCP `registerTool` API is single-name, so\n * the alias is dropped here (agents should call `memory_recall`).\n *\n * NOTE for OpenClaw agents: these tools are NOT registered in the default\n * server profile — OpenClaw agents already get `memory_*` from their\n * plugin, and registering them here would double the surface. They only\n * register under the `claude-code` profile.\n */\nexport const registerMemoryTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n server.registerTool(\n 'memory_recall',\n {\n description:\n 'Search conversation memory and knowledge graph. Returns structured facts and relevant conversation excerpts.',\n inputSchema: {\n query: z.string().trim().min(1).max(4000).describe('What to search for'),\n limit: z.coerce.number().int().min(1).max(50).optional().describe('Maximum results (default 10)'),\n topic: z.string().trim().min(1).max(256).optional().describe('Filter by topic'),\n tag: z\n .enum(MEMORY_TAGS)\n .optional()\n .describe('Filter by tag (fact/decision/preference/event/discovery)'),\n },\n },\n async (args: { query: string; limit?: number; topic?: string; tag?: MemoryTag }) => {\n try {\n const results = await ctx.client.memorySearch(args.query, {\n limit: args.limit,\n topic: args.topic,\n tag: args.tag,\n });\n return ok(results);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_recall failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_store',\n {\n description: 'Explicitly save a piece of information to long-term memory.',\n inputSchema: {\n text: z.string().trim().min(1).max(5000).describe('The information to remember'),\n topic: z.string().trim().min(1).max(128).optional().describe('Topic category (e.g., person name, project)'),\n tag: z\n .enum(MEMORY_TAGS)\n .optional()\n .describe('Memory type: fact, decision, preference, event, discovery'),\n importance: z.coerce.number().min(0).max(1).optional().describe('Importance 0-1 (default 0.7)'),\n },\n },\n async (args: { text: string; topic?: string; tag?: MemoryTag; importance?: number }) => {\n try {\n const result = await ctx.client.memoryStore(args.text, {\n topic: args.topic,\n tag: args.tag,\n importance: args.importance,\n });\n return ok({ memoryId: result.memoryId, message: `Stored memory: ${result.memoryId}` });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_store failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_learn',\n {\n description:\n 'Save arbitrary content (a paragraph, a doc, a fact list) into long-term memory. The system auto-classifies and extracts entities. Use this when the user asks you to learn, remember, or study something — or when you read a document worth retaining. Pass the source text directly; do not paraphrase first.',\n inputSchema: {\n content: z\n .string()\n .min(1)\n .max(MAX_MEMORY_TEXT_CHARACTERS)\n .optional()\n .describe('Inline text to ingest. Provide either content OR path, not both.'),\n path: z\n .string()\n .min(1)\n .max(1024)\n .optional()\n .describe(\n 'Workspace-relative file path to read and ingest. Provide either content OR path, not both.',\n ),\n source: z\n .string()\n .min(1)\n .max(512)\n .optional()\n .describe('Optional label describing where the content came from (e.g. doc title, url).'),\n },\n },\n async (args: { content?: string; path?: string; source?: string }) => {\n const content = typeof args.content === 'string' ? args.content : undefined;\n const filePath = typeof args.path === 'string' ? args.path : undefined;\n const source = typeof args.source === 'string' ? args.source : undefined;\n\n if (content === undefined && filePath === undefined) {\n return err('Error: provide either `content` or `path`.');\n }\n if (content !== undefined && filePath !== undefined) {\n return err('Error: provide either `content` or `path`, not both.');\n }\n if (source !== undefined && (source.length < 1 || source.length > 512)) {\n return err('Error: source must contain 1–512 characters.');\n }\n\n let text: string;\n let resolvedSource = source;\n let sourceType: 'file' | 'inline' = 'inline';\n\n if (filePath !== undefined) {\n try {\n const { data } = await readWorkspaceFile(filePath, MAX_MEMORY_FILE_BYTES);\n text = new TextDecoder('utf-8', { fatal: true }).decode(data);\n } catch (readErr: unknown) {\n return err(\n `Error reading \"${filePath}\": ${readErr instanceof Error ? readErr.message : String(readErr)}`,\n );\n }\n resolvedSource = source ?? filePath;\n sourceType = 'file';\n } else if (content !== undefined) {\n text = content;\n } else {\n // Unreachable — the both-or-neither cases returned above — but keeps\n // `text` definitely-assigned without a non-null assertion.\n return err('Error: provide either `content` or `path`.');\n }\n\n if (text.trim().length === 0 || text.length > MAX_MEMORY_TEXT_CHARACTERS) {\n return err(\n `Error: memory content must contain 1–${String(MAX_MEMORY_TEXT_CHARACTERS)} characters.`,\n );\n }\n\n try {\n const result = await ctx.client.memoryLearn({ text, source: resolvedSource, sourceType });\n const label = result.source ?? 'content';\n return ok({\n memoriesStored: result.memoriesStored,\n triplesStored: result.triplesStored,\n chunks: result.chunks,\n source: result.source,\n message: `Stored ${String(result.memoriesStored)} memories (${String(result.triplesStored)} facts, ${String(result.chunks)} chunks) from ${label}.`,\n });\n } catch (error: unknown) {\n return err(\n `Error: failed to store memory — ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n },\n );\n\n server.registerTool(\n 'memory_forget',\n {\n description: 'Search for and delete memories matching a query.',\n inputSchema: {\n query: z.string().trim().min(1).max(4000).describe('Search for memories to delete'),\n },\n },\n async (args: { query: string }) => {\n try {\n const results = await ctx.client.memorySearch(args.query, { limit: 5 });\n if (results.memories.length === 0) return ok({ deleted: 0, message: 'No matching memories found.' });\n\n const deleted: string[] = [];\n for (const mem of results.memories) {\n try {\n await ctx.client.memoryDelete(mem.id);\n deleted.push(mem.id);\n } catch {\n // best-effort — skip failures, mirror plugin behaviour\n }\n }\n return ok({\n deleted: deleted.length,\n matched: results.memories.length,\n message: `Deleted ${String(deleted.length)} of ${String(results.memories.length)} matching memories.`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_forget failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_graph',\n {\n description:\n 'Look up what you know about a specific entity from the knowledge graph.',\n inputSchema: {\n entity: z\n .string()\n .trim()\n .min(1)\n .max(512)\n .describe('The entity to look up (person, concept, system)'),\n },\n },\n async ({ entity }) => {\n try {\n const result = await ctx.client.memoryLookupEntity(entity);\n if (result.triples.length === 0) {\n return ok({\n subject: entity,\n triples: [],\n message: `No knowledge found about \"${entity}\".`,\n });\n }\n const facts = result.triples.map(\n (t) =>\n `- ${result.subject} ${t.predicate} ${t.object} (since ${t.validFrom.slice(0, 10)})`,\n );\n return ok({\n subject: result.subject,\n triples: result.triples,\n message: `Known facts about ${result.subject}:\\n${facts.join('\\n')}`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_graph failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_stats',\n {\n description: 'Get memory storage statistics.',\n inputSchema: {},\n },\n async () => {\n try {\n const s = await ctx.client.memoryStats();\n return ok({\n vectorCount: s.vectorCount,\n tripleCount: s.tripleCount,\n storageEstimateBytes: s.storageEstimateBytes,\n message: `Memories: ${String(s.vectorCount)}, Knowledge facts: ${String(s.tripleCount)}, Storage: ${String(Math.round(s.storageEstimateBytes / 1024))} KB`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_stats failed');\n }\n },\n );\n\n server.registerTool(\n 'memory_navigate',\n {\n description:\n 'Browse your memory palace structure — list topics, subtopics, and memory counts.',\n inputSchema: {},\n },\n async () => {\n try {\n const nav = await ctx.client.memoryNavigate();\n return ok(nav);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'memory_navigate failed');\n }\n },\n );\n};\n","/**\n * Minimal WAV (RIFF/PCM) helpers — no dependencies, no re-encoding.\n *\n * Ported from `@alfe.ai/openclaw-voice`'s `audio.ts` so the voice MCP tools\n * hand the agent a playable file (WAV) rather than headerless PCM, and strip\n * the header before sending audio to the linear16 STT endpoint. Kept\n * standalone (rather than importing the plugin) so mcp-tools stays\n * runtime-agnostic with no OpenClaw plugin dep.\n */\n\nexport interface PcmFraming {\n sampleRate: number;\n channels: number;\n bitDepth: number;\n}\n\nexport interface ParsedWav extends PcmFraming {\n /** Headerless PCM sample bytes (the `data` chunk payload). */\n pcm: Buffer;\n}\n\nconst MAX_WAV_PCM_BYTES = 0xffff_ffff - 36;\nconst SUPPORTED_BIT_DEPTHS = new Set([8, 16, 24, 32]);\n\nexport function validatePcmFraming(framing: PcmFraming, pcmLength: number): void {\n const { sampleRate, channels, bitDepth } = framing;\n if (!Number.isInteger(sampleRate) || sampleRate < 8_000 || sampleRate > 384_000) {\n throw new Error('WAV sample rate must be an integer between 8000 and 384000 Hz.');\n }\n if (!Number.isInteger(channels) || channels < 1 || channels > 32) {\n throw new Error('WAV channel count must be an integer between 1 and 32.');\n }\n if (!SUPPORTED_BIT_DEPTHS.has(bitDepth)) {\n throw new Error('WAV bit depth must be one of 8, 16, 24, or 32.');\n }\n const blockAlign = channels * (bitDepth / 8);\n if (pcmLength % blockAlign !== 0) {\n throw new Error('PCM byte length must contain a whole number of sample frames.');\n }\n if (pcmLength > MAX_WAV_PCM_BYTES) {\n throw new Error('PCM payload is too large for a RIFF/WAV container.');\n }\n if (sampleRate * blockAlign > 0xffff_ffff) {\n throw new Error('WAV byte rate exceeds the RIFF field limit.');\n }\n}\n\n/** Prepend a canonical 44-byte PCM WAV header to raw PCM samples. */\nexport function pcmToWav(pcm: Buffer, framing: PcmFraming): Buffer {\n validatePcmFraming(framing, pcm.length);\n const { sampleRate, channels, bitDepth } = framing;\n const bytesPerSample = bitDepth / 8;\n const blockAlign = channels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n\n const header = Buffer.alloc(44);\n header.write('RIFF', 0, 'ascii');\n header.writeUInt32LE(36 + pcm.length, 4);\n header.write('WAVE', 8, 'ascii');\n header.write('fmt ', 12, 'ascii');\n header.writeUInt32LE(16, 16); // fmt chunk size\n header.writeUInt16LE(1, 20); // audioFormat = PCM\n header.writeUInt16LE(channels, 22);\n header.writeUInt32LE(sampleRate, 24);\n header.writeUInt32LE(byteRate, 28);\n header.writeUInt16LE(blockAlign, 32);\n header.writeUInt16LE(bitDepth, 34);\n header.write('data', 36, 'ascii');\n header.writeUInt32LE(pcm.length, 40);\n\n return Buffer.concat([header, pcm]);\n}\n\n/**\n * Parse a WAV buffer into its PCM payload + framing. Returns `null` only when\n * the buffer is not RIFF data (caller may then treat it as raw PCM). A RIFF\n * buffer that claims to be WAV but is truncated, unsupported, or internally\n * inconsistent throws instead of silently uploading container bytes as PCM.\n */\nexport function parseWav(buf: Buffer): ParsedWav | null {\n if (buf.length < 4 || buf.toString('ascii', 0, 4) !== 'RIFF') return null;\n if (buf.length < 12) throw new Error('Malformed WAV: truncated RIFF header.');\n if (buf.toString('ascii', 8, 12) !== 'WAVE') {\n throw new Error('Unsupported RIFF container: expected WAVE.');\n }\n\n const declaredEnd = buf.readUInt32LE(4) + 8;\n if (declaredEnd !== buf.length) {\n throw new Error('Malformed WAV: RIFF size does not match the file length.');\n }\n\n let sampleRate = 0;\n let channels = 0;\n let bitDepth = 0;\n let pcm: Buffer | null = null;\n let blockAlign = 0;\n let byteRate = 0;\n let sawFmt = false;\n let sawData = false;\n\n let offset = 12;\n while (offset < declaredEnd) {\n if (offset + 8 > declaredEnd) throw new Error('Malformed WAV: truncated chunk header.');\n const chunkId = buf.toString('ascii', offset, offset + 4);\n const chunkSize = buf.readUInt32LE(offset + 4);\n const bodyStart = offset + 8;\n const bodyEnd = bodyStart + chunkSize;\n const nextOffset = bodyEnd + (chunkSize % 2);\n if (bodyEnd > declaredEnd || nextOffset > declaredEnd) {\n throw new Error(`Malformed WAV: truncated ${chunkId} chunk.`);\n }\n\n if (chunkId === 'fmt ') {\n if (sawFmt) throw new Error('Malformed WAV: duplicate fmt chunk.');\n if (chunkSize < 16) throw new Error('Malformed WAV: fmt chunk is too short.');\n const audioFormat = buf.readUInt16LE(bodyStart);\n if (audioFormat !== 1) throw new Error('Unsupported WAV encoding: only integer PCM is accepted.');\n channels = buf.readUInt16LE(bodyStart + 2);\n sampleRate = buf.readUInt32LE(bodyStart + 4);\n byteRate = buf.readUInt32LE(bodyStart + 8);\n blockAlign = buf.readUInt16LE(bodyStart + 12);\n bitDepth = buf.readUInt16LE(bodyStart + 14);\n sawFmt = true;\n } else if (chunkId === 'data') {\n if (sawData) throw new Error('Malformed WAV: duplicate data chunk.');\n pcm = buf.subarray(bodyStart, bodyEnd);\n sawData = true;\n }\n\n offset = nextOffset;\n }\n\n if (!sawFmt || !sawData || pcm === null) {\n throw new Error('Malformed WAV: both fmt and data chunks are required.');\n }\n validatePcmFraming({ sampleRate, channels, bitDepth }, pcm.length);\n const expectedBlockAlign = channels * (bitDepth / 8);\n if (blockAlign !== expectedBlockAlign || byteRate !== sampleRate * expectedBlockAlign) {\n throw new Error('Malformed WAV: byte rate or block alignment is inconsistent with its framing.');\n }\n return { pcm, sampleRate, channels, bitDepth };\n}\n","import { z } from 'zod';\nimport type { VoiceTtsModel } from '@alfe.ai/agent-api-client';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\nimport { pcmToWav, parseWav, validatePcmFraming } from './audio.js';\nimport {\n readWorkspaceFile,\n resolveWorkspaceOutputPath,\n writeWorkspaceFileExclusive,\n} from './workspace-files.js';\n\nconst MAX_AUDIO_FILE_BYTES = 10 * 1024 * 1024;\nconst MAX_TTS_RESPONSE_BYTES = 32 * 1024 * 1024;\nconst VOICE_ID_PATTERN = /^[A-Za-z0-9_-]{1,200}$/u;\n\n/**\n * Voice tools — a runtime-agnostic mirror of the one-shot TTS/STT tools in\n * the OpenClaw voice plugin (`@alfe.ai/openclaw-voice`). The channel-only\n * tools (hangup/transfer/dtmf) are intentionally omitted — they require a\n * live channel service (Discord/Twilio) and are meaningless in a standalone\n * MCP session.\n *\n * Both tools read/write files on disk (the MCP transport can't cleanly carry\n * raw audio bytes as JSON), converting between the voice service's headerless\n * PCM and a playable WAV via `./audio.js`.\n *\n * `tts`/`stt` on `AgentApiClient` hit `/voice/tts` and `/voice/stt` relative\n * to the client's `apiUrl`, so we prefer `ctx.voiceClient` (keyed on the\n * voice service URL) and fall back to `ctx.client` when it's absent (safe\n * today — voice is co-located on the shared api gateway).\n */\nexport const registerVoiceTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n const voiceClient = ctx.voiceClient ?? ctx.client;\n\n server.registerTool(\n 'voice_tts',\n {\n description:\n 'Convert text to speech using the Alfe voice service (ElevenLabs). Writes a playable audio file to the workspace and returns its path. Billed per character to your tenant credit pool.',\n inputSchema: {\n text: z.string().trim().min(1).max(5000).describe('Text to synthesize (1–5000 characters).'),\n outputPath: z\n .string()\n .min(1)\n .max(1024)\n .optional()\n .describe(\n 'New workspace-relative audio file to create. Existing files are never overwritten. Defaults to alfe-tts-<timestamp>.wav.',\n ),\n format: z\n .enum(['wav', 'pcm'])\n .optional()\n .describe(\n \"Output container. 'wav' (default) is a playable file; 'pcm' is headerless 24kHz/mono/16-bit raw PCM.\",\n ),\n voiceId: z\n .string()\n .regex(VOICE_ID_PATTERN)\n .optional()\n .describe('ElevenLabs voice ID. Platform default when omitted.'),\n model: z\n .enum(['eleven_turbo_v2_5', 'eleven_multilingual_v2'])\n .optional()\n .describe('TTS model. eleven_turbo_v2_5 (lower latency) when omitted.'),\n },\n },\n async (args: {\n text: string;\n outputPath?: string;\n format?: 'wav' | 'pcm';\n voiceId?: string;\n model?: VoiceTtsModel;\n }) => {\n try {\n const format = args.format ?? 'wav';\n const text = args.text.trim();\n if (text.length < 1 || text.length > 5000) {\n return err('voice_tts text must contain 1–5000 characters.');\n }\n if (args.voiceId !== undefined && !VOICE_ID_PATTERN.test(args.voiceId)) {\n return err('voice_tts voiceId contains unsupported characters.');\n }\n const outputPath = args.outputPath ?? `alfe-tts-${String(Date.now())}.${format}`;\n await resolveWorkspaceOutputPath(outputPath);\n\n const result = await voiceClient.tts({\n text,\n voiceId: args.voiceId,\n model: args.model,\n });\n if (result.audio.length === 0 || result.audio.length > MAX_TTS_RESPONSE_BYTES) {\n return err(`voice_tts returned an invalid audio payload size (${String(result.audio.length)} bytes).`);\n }\n validatePcmFraming(\n {\n sampleRate: result.sampleRate,\n channels: result.channels,\n bitDepth: result.bitDepth,\n },\n result.audio.length,\n );\n\n const bytes =\n format === 'wav'\n ? pcmToWav(result.audio, {\n sampleRate: result.sampleRate,\n channels: result.channels,\n bitDepth: result.bitDepth,\n })\n : result.audio;\n\n const absolutePath = await writeWorkspaceFileExclusive(outputPath, bytes);\n\n return ok({\n path: outputPath,\n absolutePath,\n format,\n sampleRate: result.sampleRate,\n channels: result.channels,\n bitDepth: result.bitDepth,\n bytes: bytes.length,\n characters: text.length,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'voice_tts failed');\n }\n },\n );\n\n server.registerTool(\n 'voice_stt',\n {\n description:\n 'Transcribe an audio file to text using the Alfe voice service (Deepgram). Accepts a WAV file or headerless mono 16-bit PCM. Billed per audio-second to your tenant credit pool.',\n inputSchema: {\n path: z\n .string()\n .min(1)\n .max(1024)\n .describe(\n 'Workspace-relative path to a WAV or raw linear16 mono PCM file (maximum 10 MiB).',\n ),\n sampleRate: z\n .coerce.number()\n .int()\n .min(8000)\n .max(48000)\n .optional()\n .describe(\n 'Sample rate in Hz (8000–48000). Only used for headerless PCM input; ignored for WAV (its header wins). Defaults to 24000.',\n ),\n },\n },\n async (args: { path: string; sampleRate?: number }) => {\n try {\n const { data: raw } = await readWorkspaceFile(args.path, MAX_AUDIO_FILE_BYTES);\n\n const wav = parseWav(raw);\n let audio: Buffer;\n let sampleRate: number;\n if (wav) {\n if (wav.bitDepth !== 16 || wav.channels !== 1) {\n return err(\n `voice_stt needs 16-bit mono audio; got ${String(wav.bitDepth)}-bit / ${String(wav.channels)}-channel WAV. Re-export as 16-bit mono.`,\n );\n }\n audio = wav.pcm;\n sampleRate = wav.sampleRate;\n } else {\n audio = raw;\n sampleRate = args.sampleRate ?? 24000;\n }\n\n if (!Number.isInteger(sampleRate) || sampleRate < 8000 || sampleRate > 48000) {\n return err('voice_stt sampleRate must be an integer between 8000 and 48000 Hz.');\n }\n if (audio.length === 0 || audio.length % 2 !== 0) {\n return err('voice_stt needs non-empty 16-bit PCM with an even byte length.');\n }\n\n const result = await voiceClient.stt({ audio, sampleRate });\n return ok(result);\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'voice_stt failed');\n }\n },\n );\n};\n","import { z } from 'zod';\nimport { err, ok, type RegisterToolsFn, type ToolContext } from './types.js';\n\n/**\n * Messaging tools — outbound SMS from the agent's own assigned phone number.\n *\n * `send_text_message` wraps `AgentApiClient.sendSms`, which hits the\n * agent-authed `POST /mobile/sms/send` endpoint. The FROM number is resolved\n * server-side from the agent identity on the token (an agent sends from the\n * single active number assigned to it, or gets a 400 if it has none) — the\n * tool only picks the recipient and body. The send is preflighted against\n * and metered to the tenant credit pool server-side.\n */\nexport const registerMessagingTools: RegisterToolsFn = (server, ctx: ToolContext): void => {\n server.registerTool(\n 'send_text_message',\n {\n description:\n \"Send an SMS text message to a phone number from your assigned phone number. The recipient is any E.164 number (e.g. +14155550123). You send from the single number assigned to you — you cannot choose the sender. Billed per message segment to your tenant credit pool. Fails if you have no active phone number.\",\n inputSchema: {\n to: z\n .string()\n .regex(/^\\+[1-9]\\d{6,14}$/u)\n .describe('Recipient phone number in E.164 format (e.g. +14155550123).'),\n body: z.string().min(1).max(4400).describe('The text message body to send.'),\n },\n },\n async (args: { to: string; body: string }) => {\n try {\n const result = await ctx.client.sendSms({ to: args.to, body: args.body });\n return ok({\n sent: result.sent,\n sid: result.sid,\n to: args.to,\n message: `Sent SMS to ${args.to} (sid: ${result.sid}).`,\n });\n } catch (error: unknown) {\n return err(error instanceof Error ? error.message : 'send_text_message failed');\n }\n },\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/mcp-tools",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Shared MCP tool implementations for Alfe's local CLI-bundled MCP server and (later) the Fly-deployed services/mcp. All tools use AgentApiClient against public /agent/... endpoints — never InternalClient.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "dependencies": {
19
19
  "@modelcontextprotocol/sdk": "^1.29.0",
20
20
  "zod": "^3.25.0",
21
- "@alfe.ai/agent-api-client": "0.15.0"
21
+ "@alfe.ai/agent-api-client": "0.16.1"
22
22
  },
23
23
  "license": "UNLICENSED",
24
24
  "homepage": "https://alfe.ai",