@zeph-to/mcp-server 1.14.1 → 1.15.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 CHANGED
@@ -5,19 +5,27 @@
5
5
  [![node](https://img.shields.io/node/v/@zeph-to/mcp-server.svg)](https://nodejs.org)
6
6
  [![license](https://img.shields.io/npm/l/@zeph-to/mcp-server.svg)](./LICENSE)
7
7
 
8
- Zeph MCP server — the agent side of the round trip. Your agent finishes a build or hits a decision, calls `zeph_ask`, and the question lands on the user's phone as buttons + a text field; the reply comes back into the same tool call and the agent keeps going. Also: one-way notifications, clipboard, files, and channel broadcasts — all via the [Model Context Protocol](https://modelcontextprotocol.io).
8
+ **Your agent calls `zeph_ask`; the question lands on your phone as buttons + a text field; your reply comes back into the same tool call and the agent keeps going.**
9
9
 
10
- Works with Claude Code, Cursor, Windsurf, Gemini CLI, and any MCP client. Part of the Zeph toolchain: [`@zeph-to/cli`](https://github.com/zeph-to/cli) (installer, push CLI, tmux remote control) · [`zeph-to/plugin`](https://github.com/zeph-to/plugin) (Claude Code plugin bundling this server) · the [Zeph app](https://zeph.to) on your phone.
10
+ Zeph's MCP server is the agent side of that round trip plus one-way notifications, clipboard, files, and channel broadcasts, all over the [Model Context Protocol](https://modelcontextprotocol.io). Works with Claude Code, Cursor, Windsurf, Gemini CLI, and any MCP client.
11
+
12
+ <p align="center">
13
+ <img src="https://zeph.to/readme/demo.gif" alt="Agent calls zeph_ask; the question hits the phone; you tap Deploy; the tool call returns" width="560"><br>
14
+ <sub><em>Your agent calls <code>zeph_ask</code> → the question hits your phone → you tap <b>Deploy</b> → the call returns and the agent ships.</em></sub>
15
+ </p>
16
+
17
+ Part of the Zeph toolchain: [`@zeph-to/cli`](https://github.com/zeph-to/cli) (installer, push CLI, tmux remote control) · [`zeph-to/plugin`](https://github.com/zeph-to/plugin) (Claude Code plugin bundling this server) · the [Zeph app](https://zeph.to) on your phone.
11
18
 
12
19
  ## Setup
13
20
 
14
21
  The easiest way to set up for all agents at once:
15
22
 
16
23
  ```bash
17
- npx @zeph-to/cli install
24
+ npm install -g @zeph-to/cli
25
+ zeph install
18
26
  ```
19
27
 
20
- This saves credentials to `~/.zeph/config.json` and configures your agents automatically. The MCP server reads from this file — no env vars needed.
28
+ This saves credentials to `~/.zeph/config.json` and configures your agents automatically. The MCP server reads from this file — no env vars needed. Install globally so `zeph cc` (phone-driven sessions) works and hooks skip an npx cold-start; `npx @zeph-to/cli install` is a notifications-only alternative.
21
29
 
22
30
  ### Claude Code (manual)
23
31
 
@@ -28,26 +36,26 @@ Add to `~/.claude/settings.json`:
28
36
  "mcpServers": {
29
37
  "zeph": {
30
38
  "command": "npx",
31
- "args": ["-y", "@zeph-to/mcp-server"],
32
- "env": {
33
- "ZEPH_API_KEY": "ak_...",
34
- "ZEPH_HOOK_ID": "hook_...",
35
- "ZEPH_DEVICE_ID": "dev_..."
36
- }
39
+ "args": ["-y", "@zeph-to/mcp-server"]
37
40
  }
38
41
  }
39
42
  }
40
43
  ```
41
44
 
45
+ No `env` block needed: credentials come from `~/.zeph/config.json` (written
46
+ by `zeph install`). Add env vars only to override the file —
47
+ e.g. a second account:
48
+
49
+ ```json
50
+ "env": { "ZEPH_API_KEY": "ak_other_account" }
51
+ ```
52
+
42
53
  ### Cursor / Other MCP Clients
43
54
 
44
55
  ```json
45
56
  {
46
57
  "command": "npx",
47
- "args": ["-y", "@zeph-to/mcp-server"],
48
- "env": {
49
- "ZEPH_API_KEY": "ak_..."
50
- }
58
+ "args": ["-y", "@zeph-to/mcp-server"]
51
59
  }
52
60
  ```
53
61
 
@@ -64,7 +72,7 @@ Add to `~/.claude/settings.json`:
64
72
  | `ZEPH_SESSION_ID` | No | Override the session id attached to pushes (grouping in the app). Auto-detected from the newest Claude Code transcript when unset |
65
73
  | `ZEPH_DISABLE_ENCRYPTION` | No | Set to `1`/`true` to force E2E-style push encryption off, even when the account has keys. Useful while cleaning up legacy key state |
66
74
 
67
- \* If env vars are not set, the server reads from `~/.zeph/config.json` (created by `npx @zeph-to/cli install`). Unresolved `${...}` interpolations are also treated as unset.
75
+ \* If env vars are not set, the server reads from `~/.zeph/config.json` (created by `zeph install`). Unresolved `${...}` interpolations are also treated as unset.
68
76
 
69
77
  ## Tools
70
78
 
@@ -141,6 +149,16 @@ targetDeviceId: "dev_..." (optional)
141
149
 
142
150
  Returns: `{ pushId: "...", fileKey: "...", fileSize: 42 }`
143
151
 
152
+ ### zeph_session_rename
153
+
154
+ Set a custom display name for the **current** agent session, shown in the Zeph app's **Streams › Agents** list. Lets an agent label what it's working on — `"Prod deploy"`, `"Auth refactor"` — so parallel sessions are easy to tell apart on your phone. Renames the session this server runs in (resolved from the listener device id + tmux session name); the name persists until changed.
155
+
156
+ ```
157
+ alias: "Prod deploy watcher" (1-60 chars)
158
+ ```
159
+
160
+ Returns: `{ renamed: true, session: "zeph-myapp", alias: "Prod deploy watcher" }`, or `{ renamed: false, reason: "..." }` when there's no active session to rename (not running inside a `zeph listener` tmux session).
161
+
144
162
  ### zeph_prompt
145
163
 
146
164
  Ask the user to choose from 2-4 options. Blocks until response or timeout.
@@ -164,6 +182,10 @@ Ask the user a question with optional quick-reply buttons and a text input field
164
182
 
165
183
  Requires `ZEPH_HOOK_ID`.
166
184
 
185
+ <p align="center">
186
+ <img src="https://zeph.to/readme/ask-phone.png" alt="zeph_ask rendered on the phone: a question with tappable answer buttons and a text field" width="300">
187
+ </p>
188
+
167
189
  ```
168
190
  title: "What should we do?"
169
191
  body: "3 tests failed in auth module" (optional)
@@ -219,6 +241,7 @@ Lists channels the user owns or subscribes to. Use to find `channelId` for `zeph
219
241
  | Free-form input only | `zeph_input` | Commit message, env var value, description |
220
242
  | Share code/logs | `zeph_file` | Error logs, test reports, generated config |
221
243
  | Share snippet | `zeph_clipboard` | API key, URL, shell command |
244
+ | Label this session | `zeph_session_rename` | Name the run "Prod deploy" so parallel sessions stay distinguishable on the phone |
222
245
 
223
246
  ### Recommended patterns
224
247
 
@@ -293,6 +316,7 @@ The API key needs the following scopes:
293
316
  - `push:read` — for `zeph_list`
294
317
  - `push:write` — for `zeph_notify`, `zeph_clipboard`, `zeph_dismiss`, `zeph_dismiss_all`, `zeph_file`
295
318
  - `hook:write` — for `zeph_ask`, `zeph_prompt`, and `zeph_input`
319
+ - `device:write` — for `zeph_session_rename`
296
320
  - `channel:read` — for `zeph://channels` resource
297
321
 
298
322
  Create an API key with the **MCP** preset in Settings > API Keys for the correct permissions.
@@ -1,5 +1,5 @@
1
1
  import type { McpServerConfig } from './config.js';
2
- import type { PushResponse, HookTriggerResponse, HookEventResponse, DevicesResponse, PushListResponse, DismissResponse, ChannelsResponse, UploadRequestResponse, AttachedFile } from './types.js';
2
+ import type { PushResponse, HookTriggerResponse, HookEventResponse, DevicesResponse, AgentSessionRenameResponse, PushListResponse, DismissResponse, ChannelsResponse, UploadRequestResponse, AttachedFile } from './types.js';
3
3
  export declare class ApiError extends Error {
4
4
  readonly code: string;
5
5
  readonly status: number;
@@ -42,6 +42,9 @@ export declare class ZephApiClient {
42
42
  }): Promise<HookTriggerResponse>;
43
43
  getHookEvent(hookId: string, eventId: string): Promise<HookEventResponse>;
44
44
  listDevices(): Promise<DevicesResponse>;
45
+ /** Set (or clear, when `alias` is empty) the display name for an agent
46
+ * session, keyed by its tmux `name` on `deviceId`. */
47
+ renameAgentSession(deviceId: string, name: string, alias: string): Promise<AgentSessionRenameResponse>;
45
48
  listPushes(params?: {
46
49
  limit?: number;
47
50
  type?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,11 @@ class ZephApiClient {
33
33
  async listDevices() {
34
34
  return this.request('GET', '/devices');
35
35
  }
36
+ /** Set (or clear, when `alias` is empty) the display name for an agent
37
+ * session, keyed by its tmux `name` on `deviceId`. */
38
+ async renameAgentSession(deviceId, name, alias) {
39
+ return this.request('PATCH', `/devices/${encodeURIComponent(deviceId)}/agent-sessions/${encodeURIComponent(name)}`, { alias });
40
+ }
36
41
  async listPushes(params) {
37
42
  const query = new URLSearchParams();
38
43
  if (params?.limit)
package/dist/config.d.ts CHANGED
@@ -24,4 +24,11 @@ export interface McpServerConfig {
24
24
  */
25
25
  export declare const formatPushTitle: (projectName: string, title: string) => string;
26
26
  export declare const loadConfig: () => McpServerConfig;
27
+ /** Listener device id — MUST match cli `computeListenerDeviceId` so `zeph_ask`
28
+ * hooks land under the same agent-session key as the listener's
29
+ * agent.command/state pushes (otherwise they reach the feed but never thread
30
+ * into the agent chat). cli hashes the platform machine id first and falls
31
+ * back to a hostname hash pinned in a sticky file; mirror that order, reading
32
+ * (never writing) the file so both processes resolve the same id. */
33
+ export declare const listenerDeviceId: () => string;
27
34
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;uCAGmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACvB;AAiBD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM,KAAG,MAGpE,CAAC;AAuGF,eAAO,MAAM,UAAU,QAAO,eAyB7B,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;uCAGmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACvB;AAiBD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM,KAAG,MAGpE,CAAC;AAuGF,eAAO,MAAM,UAAU,QAAO,eAyB7B,CAAC;AA4BF;;;;;sEAKsE;AACtE,eAAO,MAAM,gBAAgB,QAAO,MAQnC,CAAC"}
package/dist/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadConfig = exports.formatPushTitle = void 0;
3
+ exports.listenerDeviceId = exports.loadConfig = exports.formatPushTitle = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const os_1 = require("os");
6
6
  const crypto_1 = require("crypto");
@@ -140,16 +140,57 @@ const loadConfig = () => {
140
140
  hookId: resolvedEnv('ZEPH_HOOK_ID') ?? fileConfig.hookId,
141
141
  deviceId: resolvedEnv('ZEPH_DEVICE_ID') ?? fileConfig.deviceId,
142
142
  sessionId,
143
- agentDeviceId: listenerDeviceId(),
143
+ agentDeviceId: (0, exports.listenerDeviceId)(),
144
144
  agentSessionName: detectTmuxSessionName(),
145
145
  projectName: projectNameFromDir(projectDir),
146
146
  };
147
147
  };
148
148
  exports.loadConfig = loadConfig;
149
- /** Per-host listener device id MUST match cli `computeListenerDeviceId`
150
- * (`dev_listener_<sha8(hostname)>`) so hooks land under the same session key
151
- * as the listener's agent.command/state pushes. */
152
- const listenerDeviceId = () => `dev_listener_${(0, crypto_1.createHash)('sha256').update((0, os_1.hostname)()).digest('hex').slice(0, 8)}`;
149
+ const LISTENER_ID_FILE = (0, path_1.join)((0, os_1.homedir)(), '.zeph', 'listener-device-id');
150
+ const hashListenerId = (seed) => `dev_listener_${(0, crypto_1.createHash)('sha256').update(seed).digest('hex').slice(0, 8)}`;
151
+ /** Platform machine id (macOS IOPlatformUUID / Linux machine-id), or null.
152
+ * Mirrors cli `readMachineId` so the hash below matches the listener's id. */
153
+ const readMachineId = () => {
154
+ try {
155
+ if (process.platform === 'darwin') {
156
+ const out = (0, child_process_1.execFileSync)('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], { encoding: 'utf-8' });
157
+ const m = out.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
158
+ if (m)
159
+ return m[1];
160
+ }
161
+ if (process.platform === 'linux') {
162
+ for (const p of ['/etc/machine-id', '/var/lib/dbus/machine-id']) {
163
+ try {
164
+ const v = (0, fs_1.readFileSync)(p, 'utf-8').trim();
165
+ if (v)
166
+ return v;
167
+ }
168
+ catch { /* try next path */ }
169
+ }
170
+ }
171
+ }
172
+ catch { /* no machine id readable — fall through */ }
173
+ return null;
174
+ };
175
+ /** Listener device id — MUST match cli `computeListenerDeviceId` so `zeph_ask`
176
+ * hooks land under the same agent-session key as the listener's
177
+ * agent.command/state pushes (otherwise they reach the feed but never thread
178
+ * into the agent chat). cli hashes the platform machine id first and falls
179
+ * back to a hostname hash pinned in a sticky file; mirror that order, reading
180
+ * (never writing) the file so both processes resolve the same id. */
181
+ const listenerDeviceId = () => {
182
+ const machineId = readMachineId();
183
+ if (machineId)
184
+ return hashListenerId(machineId);
185
+ try {
186
+ const saved = (0, fs_1.readFileSync)(LISTENER_ID_FILE, 'utf-8').trim();
187
+ if (/^dev_listener_[0-9a-f]{8}$/.test(saved))
188
+ return saved;
189
+ }
190
+ catch { /* no sticky file — fall back to hostname */ }
191
+ return hashListenerId((0, os_1.hostname)());
192
+ };
193
+ exports.listenerDeviceId = listenerDeviceId;
153
194
  /** The tmux session name the agent runs in (`zeph-<project>`) — stable half of
154
195
  * the session key. Undefined outside tmux; the hook then stays feed-only. */
155
196
  const detectTmuxSessionName = () => {
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ const dismiss_js_1 = require("./tools/dismiss.js");
18
18
  const broadcast_js_1 = require("./tools/broadcast.js");
19
19
  const file_js_1 = require("./tools/file.js");
20
20
  const ask_js_1 = require("./tools/ask.js");
21
+ const rename_js_1 = require("./tools/rename.js");
21
22
  const devices_js_1 = require("./resources/devices.js");
22
23
  const channels_js_1 = require("./resources/channels.js");
23
24
  const getVersion = () => {
@@ -52,6 +53,7 @@ const createServer = (config) => {
52
53
  '- zeph_prompt: Ask user to choose from options (requires ZEPH_HOOK_ID)',
53
54
  '- zeph_input: Request text input from user (requires ZEPH_HOOK_ID)',
54
55
  '- zeph_ask: Ask user with buttons + text input combined (requires ZEPH_HOOK_ID). Prefer this over zeph_prompt/zeph_input when you need both options and free-text.',
56
+ '- zeph_session_rename: Name this agent session so the user can identify it in the app (Streams › Agents).',
55
57
  '',
56
58
  'Resources:',
57
59
  '- zeph://devices: Check which devices are online',
@@ -68,6 +70,7 @@ const createServer = (config) => {
68
70
  (0, prompt_js_1.registerPromptTool)(server, client, config, waiter);
69
71
  (0, input_js_1.registerInputTool)(server, client, config, waiter);
70
72
  (0, ask_js_1.registerAskTool)(server, client, config, waiter);
73
+ (0, rename_js_1.registerRenameTool)(server, client, config);
71
74
  (0, devices_js_1.registerDevicesResource)(server, client);
72
75
  (0, channels_js_1.registerChannelsResource)(server, client);
73
76
  return server;
@@ -0,0 +1,5 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ZephApiClient } from '../api-client.js';
3
+ import type { McpServerConfig } from '../config.js';
4
+ export declare const registerRenameTool: (server: McpServer, client: ZephApiClient, config: McpServerConfig) => void;
5
+ //# sourceMappingURL=rename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename.d.ts","sourceRoot":"","sources":["../../src/tools/rename.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAKpD,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SAyCnG,CAAC"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerRenameTool = void 0;
4
+ const zod_1 = require("zod");
5
+ const error_format_js_1 = require("../error-format.js");
6
+ // Mirrors the server cap (zeph apps/server/src/functions/devices.ts).
7
+ const MAX_ALIAS_LENGTH = 60;
8
+ const registerRenameTool = (server, client, config) => {
9
+ server.registerTool('zeph_session_rename', {
10
+ description: "Set a custom display name for THIS agent session in the user's Zeph app (the Streams › Agents list). " +
11
+ 'Label what this session is working on — e.g. "Prod deploy" or "Auth refactor" — so the user can tell ' +
12
+ 'parallel sessions apart on their phone. Renames the current session; the name persists until changed.',
13
+ annotations: {
14
+ readOnlyHint: false,
15
+ destructiveHint: false,
16
+ openWorldHint: true,
17
+ },
18
+ inputSchema: {
19
+ alias: zod_1.z
20
+ .string()
21
+ .trim()
22
+ .min(1)
23
+ .max(MAX_ALIAS_LENGTH)
24
+ .describe(`Display name for this session (max ${MAX_ALIAS_LENGTH} characters).`),
25
+ },
26
+ }, async ({ alias }) => {
27
+ try {
28
+ const { agentDeviceId, agentSessionName } = config;
29
+ // The session key is resolved once at startup from the listener device
30
+ // id + tmux session name. Absent it, there is nothing to rename.
31
+ if (!agentDeviceId || !agentSessionName) {
32
+ return (0, error_format_js_1.textResult)({
33
+ renamed: false,
34
+ reason: 'No active agent session detected — this tool renames the tmux session it runs in, which requires `zeph listener` and a tmux session.',
35
+ });
36
+ }
37
+ await client.renameAgentSession(agentDeviceId, agentSessionName, alias);
38
+ return (0, error_format_js_1.textResult)({ renamed: true, session: agentSessionName, alias });
39
+ }
40
+ catch (err) {
41
+ return (0, error_format_js_1.formatToolError)(err);
42
+ }
43
+ });
44
+ };
45
+ exports.registerRenameTool = registerRenameTool;
package/dist/types.d.ts CHANGED
@@ -39,6 +39,9 @@ export interface DeviceRecord {
39
39
  export interface DevicesResponse {
40
40
  data: DeviceRecord[];
41
41
  }
42
+ export interface AgentSessionRenameResponse {
43
+ data: DeviceRecord;
44
+ }
42
45
  export interface PushRecord {
43
46
  pushId: string;
44
47
  type: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeph-to/mcp-server",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "description": "Zeph MCP server — AI agent notifications, prompts, and input via MCP protocol",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",