@zeph-to/mcp-server
v1.14.0
Published
Zeph MCP server — AI agent notifications, prompts, and input via MCP protocol
Maintainers
Readme
@zeph-to/mcp-server
Zeph MCP server for AI agents. Send notifications, copy to clipboard, request confirmations, and collect text input from users across their devices — all via the Model Context Protocol.
Setup
The easiest way to set up for all agents at once:
npx @zeph-to/cli installThis saves credentials to ~/.zeph/config.json and configures your agents automatically. The MCP server reads from this file — no env vars needed.
Claude Code (manual)
Add to ~/.claude/settings.json:
{
"mcpServers": {
"zeph": {
"command": "npx",
"args": ["-y", "@zeph-to/mcp-server"],
"env": {
"ZEPH_API_KEY": "ak_...",
"ZEPH_HOOK_ID": "hook_...",
"ZEPH_DEVICE_ID": "dev_..."
}
}
}
}Cursor / Other MCP Clients
{
"command": "npx",
"args": ["-y", "@zeph-to/mcp-server"],
"env": {
"ZEPH_API_KEY": "ak_..."
}
}Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| ZEPH_API_KEY | Yes* | API key from Settings > API Keys |
| ZEPH_HOOK_ID | No | Hook ID (optional — only needed for interactive tools like zeph_ask/zeph_prompt/zeph_input) |
| ZEPH_DEVICE_ID | No | Target device ID (optional — only needed for interactive tools like zeph_ask/zeph_prompt/zeph_input). Omit to send to all devices |
| ZEPH_BASE_URL | No | API base URL (default: https://api.zeph.to/v1) |
| ZEPH_DISABLE_SESSION_CACHE | No | Set to 1/true to skip writing the session-id handoff file under ~/.cache/zeph/. Useful for read-only filesystems, ephemeral CI runners, or sandboxed envs that audit filesystem writes. The plugin's stop hook still works without it (transcript-path UUID extraction is the primary path; the cache is a fallback for older Claude Code versions). |
* 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.
Tools
zeph_notify
Send a one-way push notification. Supports optional URL (auto-switches to link type).
title: "Build complete"
body: "All 42 tests passed"
url: "https://github.com/org/repo/actions/runs/123" (optional)
priority: "low" | "normal" | "high" | "urgent"
targetDeviceId: "dev_..." (optional, overrides ZEPH_DEVICE_ID)zeph_clipboard
Copy text to the user's device clipboard.
text: "npm install @zeph-to/mcp-server"
targetDeviceId: "dev_..." (optional)zeph_list
List recent push notifications.
limit: 5 (1-20, default: 5)
type: "note" (optional filter: note, link, file, clipboard, hook)Returns: { pushes: [...], total: 5, hasMore: true }
zeph_dismiss
Mark a specific push as read.
pushId: "push_01HX..."zeph_dismiss_all
Clear all notifications at once. No parameters.
Returns: { dismissed: 12, badge: 0 }
zeph_broadcast
Send a notification to all subscribers of a channel.
channelId: "ch_..."
title: "Deploy complete"
body: "v2.1.0 is live"
url: "https://..." (optional)
priority: "normal"zeph_file
Send a text file to the user's device.
fileName: "report.json"
content: "{\"status\": \"ok\"}"
title: "Build Report" (optional, defaults to fileName)
targetDeviceId: "dev_..." (optional)Returns: { pushId: "...", fileKey: "...", fileSize: 42 }
zeph_prompt
Ask the user to choose from 2-4 options. Blocks until response or timeout.
Requires ZEPH_HOOK_ID.
title: "Deploy to production?"
body: "3 migrations pending"
actions: [{ id: "yes", label: "Deploy", style: "primary" },
{ id: "no", label: "Cancel", style: "danger" }]
timeout: 120 (seconds, default: 120, max: 300)
fallback: "no" (auto-select on timeout, optional)Returns: { actionId: "yes", timedOut: false }
zeph_ask
Ask the user a question with optional quick-reply buttons and a text input field. Combines prompt (buttons) and input (text) in a single notification. Blocks until response or timeout.
Requires ZEPH_HOOK_ID.
title: "What should we do?"
body: "3 tests failed in auth module" (optional)
actions: [{ id: "fix", label: "Fix now", style: "primary" },
{ id: "skip", label: "Skip", style: "secondary" }] (optional, 1-4)
placeholder: "Or type a custom response..." (optional)
inputType: "text" | "multiline" (default: text)
timeout: 120 (seconds, default: 120, max: 600)
fallback: "skip" (auto-select on timeout, optional)Returns: { actionId: "fix", timedOut: false } or { value: "custom text", timedOut: false }
zeph_input
Request free-form text input from the user. Blocks until response or timeout.
Requires ZEPH_HOOK_ID.
title: "Commit message"
body: "Summarize the changes"
placeholder: "feat: ..."
inputType: "text" | "password" | "multiline"
timeout: 120 (seconds, default: 120, max: 600)Returns: { value: "feat: add clipboard sync", timedOut: false }
Client timeouts
zeph_ask, zeph_prompt, and zeph_input block until the user responds, up to their timeout (max 600s). That whole time the MCP request stays open. To keep the client from giving up early, the server emits a notifications/progress every 5s while waiting. Clients must either set a per-request timeout above the tool's timeout, or reset their timeout on progress notifications. Claude Code does the latter by default.
Resources
zeph://devices
Lists connected devices with online status. Use to check which devices will receive notifications.
zeph://channels
Lists channels the user owns or subscribes to. Use to find channelId for zeph_broadcast.
Usage Guide
When to use each tool
| Situation | Tool | Example |
|-----------|------|---------|
| Long task finished | zeph_notify | Build complete, test results, deploy done |
| Need user decision | zeph_prompt | Choose deploy target, confirm destructive action |
| Need free-form input | zeph_input | Commit message, env var value, description |
| Share code/logs | zeph_file | Error logs, test reports, generated config |
| Share snippet | zeph_clipboard | API key, URL, shell command |
Recommended patterns
Task completion notification:
zeph_notify(
title: "Build complete: web app",
body: "All 42 tests passed. Bundle size: 1.2MB (-3%)"
)Decision gate in CI/deploy flow:
zeph_prompt(
title: "Deploy to production?",
body: "3 migrations pending. Last deploy: 2h ago.",
actions: [
{ id: "deploy", label: "Deploy", style: "primary" },
{ id: "staging", label: "Staging only", style: "secondary" },
{ id: "cancel", label: "Cancel", style: "danger" }
],
fallback: "cancel"
)Collecting user input remotely:
zeph_input(
title: "Commit message",
body: "Changed: hooks.ts, input.ts, prompt.ts",
placeholder: "feat: ..."
)Error alert with link:
zeph_notify(
title: "CI failed: lint errors",
body: "2 errors in src/auth.ts",
url: "https://github.com/org/repo/actions/runs/456",
priority: "high"
)When NOT to use
- Short responses the user can see immediately in the terminal
- Read-only operations (file search, code analysis)
- Every single tool call — only notify on meaningful milestones
Multi-session workflow
When running multiple AI agent sessions in parallel, use zeph_notify to signal completion so the user knows which session finished without checking each terminal.
API Key Permissions
The API key needs the following scopes:
push:read— forzeph_listpush:write— forzeph_notify,zeph_clipboard,zeph_dismiss,zeph_dismiss_all,zeph_filehook:write— forzeph_promptandzeph_inputchannel:read— forzeph://channelsresource
Create an API key with the MCP preset in Settings > API Keys for the correct permissions.
Encryption
Push bodies are encrypted with AES-256-GCM. The wrapping key is derived via ECDH P-256 and synced across your own devices on first server startup so every device can read the same push. Toggle encryption in the Zeph app (Settings → Encryption); when disabled, the server sends plaintext. No configuration needed.
Threat model honesty: keys are persisted on the Zeph backend to enable cross-device sync, so this is device-shared encryption — not true end-to-end. It protects push contents from passive network observers and from a leaked database snapshot taken without the key store, but it does not protect against the Zeph backend itself (it has the keys it serves to your devices). A true E2E mode (per-device keypairs, server stores only public keys, no key escrow) is on the roadmap.
License
Apache-2.0
