@zeph-to/mcp-server 1.10.0 → 1.11.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/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -1
- package/dist/tools/ask.d.ts +1 -1
- package/dist/tools/ask.d.ts.map +1 -1
- package/dist/tools/ask.js +26 -10
- package/dist/tools/file.d.ts +1 -1
- package/dist/tools/file.d.ts.map +1 -1
- package/dist/tools/file.js +2 -1
- package/dist/tools/input.d.ts +1 -1
- package/dist/tools/input.d.ts.map +1 -1
- package/dist/tools/input.js +2 -1
- package/dist/tools/notify.d.ts +1 -1
- package/dist/tools/notify.d.ts.map +1 -1
- package/dist/tools/notify.js +19 -14
- package/dist/tools/prompt.d.ts +1 -1
- package/dist/tools/prompt.d.ts.map +1 -1
- package/dist/tools/prompt.js +2 -1
- package/package.json +5 -2
package/dist/config.d.ts
CHANGED
|
@@ -4,6 +4,15 @@ export interface McpServerConfig {
|
|
|
4
4
|
hookId?: string;
|
|
5
5
|
deviceId?: string;
|
|
6
6
|
sessionId?: string;
|
|
7
|
+
/** Last path segment of the project directory — prefixed onto push titles. */
|
|
8
|
+
projectName: string;
|
|
7
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Prefix a push title with the project name so the device feed stays
|
|
12
|
+
* scannable — "zeph · Build finished" instead of a bare "Build finished".
|
|
13
|
+
* Idempotent: a title already carrying the project segment is returned
|
|
14
|
+
* unchanged (guards against double-prefixing on retries).
|
|
15
|
+
*/
|
|
16
|
+
export declare const formatPushTitle: (projectName: string, title: string) => string;
|
|
8
17
|
export declare const loadConfig: () => McpServerConfig;
|
|
9
18
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -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,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,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,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACvB;AAiBD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM,KAAG,MAGpE,CAAC;AAgGF,eAAO,MAAM,UAAU,QAAO,eAsB7B,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadConfig = void 0;
|
|
3
|
+
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");
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const path_1 = require("path");
|
|
9
9
|
const DEFAULT_BASE_URL = 'https://api.zeph.to/v1';
|
|
10
|
+
const PROJECT_DIR_ENV_KEYS = ['CLAUDE_PROJECT_DIR', 'CURSOR_PROJECT_DIR', 'WINDSURF_PROJECT_DIR'];
|
|
11
|
+
/** The project directory the agent runs in, across supported agents. */
|
|
12
|
+
const detectProjectDir = () => {
|
|
13
|
+
for (const key of PROJECT_DIR_ENV_KEYS) {
|
|
14
|
+
const val = process.env[key];
|
|
15
|
+
if (val)
|
|
16
|
+
return val;
|
|
17
|
+
}
|
|
18
|
+
return process.cwd();
|
|
19
|
+
};
|
|
20
|
+
/** Last path segment of a directory: "/Users/me/code/zeph" -> "zeph". */
|
|
21
|
+
const projectNameFromDir = (dir) => dir.split('/').filter(Boolean).pop() ?? 'project';
|
|
22
|
+
/**
|
|
23
|
+
* Prefix a push title with the project name so the device feed stays
|
|
24
|
+
* scannable — "zeph · Build finished" instead of a bare "Build finished".
|
|
25
|
+
* Idempotent: a title already carrying the project segment is returned
|
|
26
|
+
* unchanged (guards against double-prefixing on retries).
|
|
27
|
+
*/
|
|
28
|
+
const formatPushTitle = (projectName, title) => {
|
|
29
|
+
const prefix = `${projectName} · `;
|
|
30
|
+
return title.startsWith(prefix) ? title : prefix + title;
|
|
31
|
+
};
|
|
32
|
+
exports.formatPushTitle = formatPushTitle;
|
|
10
33
|
const resolvedEnv = (key) => {
|
|
11
34
|
const val = process.env[key];
|
|
12
35
|
return val && !val.startsWith('${') ? val : undefined;
|
|
@@ -111,6 +134,7 @@ const loadConfig = () => {
|
|
|
111
134
|
hookId: resolvedEnv('ZEPH_HOOK_ID') ?? fileConfig.hookId,
|
|
112
135
|
deviceId: resolvedEnv('ZEPH_DEVICE_ID') ?? fileConfig.deviceId,
|
|
113
136
|
sessionId,
|
|
137
|
+
projectName: projectNameFromDir(detectProjectDir()),
|
|
114
138
|
};
|
|
115
139
|
};
|
|
116
140
|
exports.loadConfig = loadConfig;
|
package/dist/tools/ask.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { ZephApiClient } from '../api-client.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type McpServerConfig } from '../config.js';
|
|
4
4
|
export declare const registerAskTool: (server: McpServer, client: ZephApiClient, config: McpServerConfig) => void;
|
|
5
5
|
//# sourceMappingURL=ask.d.ts.map
|
package/dist/tools/ask.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/tools/ask.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/tools/ask.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAuBrE,eAAO,MAAM,eAAe,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SAwHhG,CAAC"}
|
package/dist/tools/ask.js
CHANGED
|
@@ -4,10 +4,22 @@ exports.registerAskTool = void 0;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const error_format_js_1 = require("../error-format.js");
|
|
6
6
|
const poll_js_1 = require("../poll.js");
|
|
7
|
+
const config_js_1 = require("../config.js");
|
|
7
8
|
const crypto_js_1 = require("../crypto.js");
|
|
8
9
|
const mime_js_1 = require("../mime.js");
|
|
9
|
-
|
|
10
|
+
// The device feed shows a short preview of the body. Anything longer than
|
|
11
|
+
// this gets truncated there, so we attach the full text as a file — the
|
|
12
|
+
// user can always open the complete content instead of squinting at a
|
|
13
|
+
// clipped preview.
|
|
10
14
|
const PREVIEW_LENGTH = 200;
|
|
15
|
+
/** Self-contained Markdown for the attached response.md: heading + body + options. */
|
|
16
|
+
const buildAskMarkdown = (title, body, actions) => {
|
|
17
|
+
const parts = [`# ${title}`, '', body];
|
|
18
|
+
if (actions && actions.length > 0) {
|
|
19
|
+
parts.push('', '---', '', `**Options:** ${actions.map((a) => a.label).join(' · ')}`);
|
|
20
|
+
}
|
|
21
|
+
return parts.join('\n');
|
|
22
|
+
};
|
|
11
23
|
const registerAskTool = (server, client, config) => {
|
|
12
24
|
server.registerTool('zeph_ask', {
|
|
13
25
|
description: '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. The user can either tap a button or type a response. Blocks until the user responds or the timeout is reached. Requires ZEPH_HOOK_ID environment variable.',
|
|
@@ -50,21 +62,25 @@ const registerAskTool = (server, client, config) => {
|
|
|
50
62
|
if (!config.hookId)
|
|
51
63
|
return (0, error_format_js_1.hookNotConfiguredError)();
|
|
52
64
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
65
|
+
const pushTitle = (0, config_js_1.formatPushTitle)(config.projectName, title);
|
|
66
|
+
// Attach a file whenever the body would be clipped in the feed preview.
|
|
67
|
+
const exceedsPreview = !!body && body.length > PREVIEW_LENGTH;
|
|
55
68
|
let triggerBody = body;
|
|
56
69
|
let files;
|
|
57
|
-
if (
|
|
70
|
+
if (exceedsPreview && body) {
|
|
58
71
|
const fileName = 'response.md';
|
|
59
72
|
const fileType = (0, mime_js_1.inferMimeType)(fileName);
|
|
60
73
|
const canEncrypt = !!(0, crypto_js_1.getKeyPair)() && !!(0, crypto_js_1.getPublicKey)();
|
|
61
|
-
|
|
74
|
+
// Self-contained Markdown so the file alone tells the whole story.
|
|
75
|
+
const fileMarkdown = buildAskMarkdown(title, body, actions);
|
|
76
|
+
const fileBytes = new TextEncoder().encode(fileMarkdown).byteLength;
|
|
77
|
+
let uploadContent = fileMarkdown;
|
|
62
78
|
let uploadContentType = fileType;
|
|
63
79
|
let fileIv;
|
|
64
80
|
let fileEncryptedKey;
|
|
65
81
|
if (canEncrypt) {
|
|
66
82
|
try {
|
|
67
|
-
const encrypted = await (0, crypto_js_1.encryptFileForSelf)(
|
|
83
|
+
const encrypted = await (0, crypto_js_1.encryptFileForSelf)(fileMarkdown);
|
|
68
84
|
uploadContent = encrypted.ciphertext;
|
|
69
85
|
uploadContentType = 'application/octet-stream';
|
|
70
86
|
fileIv = encrypted.iv;
|
|
@@ -74,13 +90,13 @@ const registerAskTool = (server, client, config) => {
|
|
|
74
90
|
console.error('[Crypto] File encryption failed, sending plaintext:', err);
|
|
75
91
|
}
|
|
76
92
|
}
|
|
77
|
-
const upload = await client.requestUpload({ fileName, fileType: uploadContentType, fileSize: typeof uploadContent === 'string' ?
|
|
93
|
+
const upload = await client.requestUpload({ fileName, fileType: uploadContentType, fileSize: typeof uploadContent === 'string' ? fileBytes : uploadContent.length });
|
|
78
94
|
await client.uploadToS3(upload.data.uploadUrl, uploadContent, uploadContentType);
|
|
79
|
-
triggerBody = body.
|
|
80
|
-
files = [{ fileKey: upload.data.fileKey, fileName, fileSize:
|
|
95
|
+
triggerBody = body.slice(0, PREVIEW_LENGTH) + '...';
|
|
96
|
+
files = [{ fileKey: upload.data.fileKey, fileName, fileSize: fileBytes, fileType, iv: fileIv, encryptedKey: fileEncryptedKey }];
|
|
81
97
|
}
|
|
82
98
|
const trigger = await client.triggerHook(config.hookId, {
|
|
83
|
-
title,
|
|
99
|
+
title: pushTitle,
|
|
84
100
|
body: triggerBody,
|
|
85
101
|
actions,
|
|
86
102
|
timeout,
|
package/dist/tools/file.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { ZephApiClient } from '../api-client.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type McpServerConfig } from '../config.js';
|
|
4
4
|
export declare const registerFileTool: (server: McpServer, client: ZephApiClient, config: McpServerConfig) => void;
|
|
5
5
|
//# sourceMappingURL=file.d.ts.map
|
package/dist/tools/file.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/tools/file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/tools/file.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAKrE,eAAO,MAAM,gBAAgB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SA2EjG,CAAC"}
|
package/dist/tools/file.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerFileTool = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const config_js_1 = require("../config.js");
|
|
5
6
|
const error_format_js_1 = require("../error-format.js");
|
|
6
7
|
const crypto_js_1 = require("../crypto.js");
|
|
7
8
|
const mime_js_1 = require("../mime.js");
|
|
@@ -47,7 +48,7 @@ const registerFileTool = (server, client, config) => {
|
|
|
47
48
|
// Step 3: Upload content to S3
|
|
48
49
|
await client.uploadToS3(upload.data.uploadUrl, uploadContent, fileType);
|
|
49
50
|
// Step 4: Send file push (encrypt push body if possible)
|
|
50
|
-
const pushTitle = title ?? fileName;
|
|
51
|
+
const pushTitle = (0, config_js_1.formatPushTitle)(config.projectName, title ?? fileName);
|
|
51
52
|
let pushPayload = {
|
|
52
53
|
title: pushTitle,
|
|
53
54
|
type: 'file',
|
package/dist/tools/input.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { ZephApiClient } from '../api-client.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type McpServerConfig } from '../config.js';
|
|
4
4
|
export declare const registerInputTool: (server: McpServer, client: ZephApiClient, config: McpServerConfig) => void;
|
|
5
5
|
//# sourceMappingURL=input.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/tools/input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/tools/input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErE,eAAO,MAAM,iBAAiB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SAwDlG,CAAC"}
|
package/dist/tools/input.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.registerInputTool = void 0;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const error_format_js_1 = require("../error-format.js");
|
|
6
6
|
const poll_js_1 = require("../poll.js");
|
|
7
|
+
const config_js_1 = require("../config.js");
|
|
7
8
|
const registerInputTool = (server, client, config) => {
|
|
8
9
|
server.registerTool('zeph_input', {
|
|
9
10
|
description: 'Request text input from the user via push notification. The tool blocks until the user responds or the timeout is reached. Requires ZEPH_HOOK_ID environment variable.',
|
|
@@ -32,7 +33,7 @@ const registerInputTool = (server, client, config) => {
|
|
|
32
33
|
return (0, error_format_js_1.hookNotConfiguredError)();
|
|
33
34
|
try {
|
|
34
35
|
const trigger = await client.triggerHook(config.hookId, {
|
|
35
|
-
title,
|
|
36
|
+
title: (0, config_js_1.formatPushTitle)(config.projectName, title),
|
|
36
37
|
body,
|
|
37
38
|
timeout,
|
|
38
39
|
hookType: 'input',
|
package/dist/tools/notify.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { ZephApiClient } from '../api-client.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type McpServerConfig } from '../config.js';
|
|
4
4
|
export declare const registerNotifyTool: (server: McpServer, client: ZephApiClient, config: McpServerConfig) => void;
|
|
5
5
|
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/tools/notify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/tools/notify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAQrE,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SAiHnG,CAAC"}
|
package/dist/tools/notify.js
CHANGED
|
@@ -3,13 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.registerNotifyTool = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const error_format_js_1 = require("../error-format.js");
|
|
6
|
+
const config_js_1 = require("../config.js");
|
|
6
7
|
const crypto_js_1 = require("../crypto.js");
|
|
7
8
|
const mime_js_1 = require("../mime.js");
|
|
8
|
-
|
|
9
|
+
// The device feed shows a short preview of the body. Anything longer gets
|
|
10
|
+
// truncated there, so we attach the full text as a file for full viewing.
|
|
9
11
|
const PREVIEW_LENGTH = 200;
|
|
10
12
|
const registerNotifyTool = (server, client, config) => {
|
|
11
13
|
server.registerTool('zeph_notify', {
|
|
12
|
-
description: 'Send a one-way push notification to the user\'s devices. Use this to inform the user about task completion, errors, or status updates. Long bodies
|
|
14
|
+
description: 'Send a one-way push notification to the user\'s devices. Use this to inform the user about task completion, errors, or status updates. Long bodies are automatically uploaded as a file for full viewing.',
|
|
13
15
|
annotations: {
|
|
14
16
|
readOnlyHint: false,
|
|
15
17
|
destructiveHint: false,
|
|
@@ -28,21 +30,24 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
28
30
|
}, async ({ title, body, url, priority, targetDeviceId }) => {
|
|
29
31
|
try {
|
|
30
32
|
const deviceId = targetDeviceId ?? config.deviceId;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
+
const pushTitle = (0, config_js_1.formatPushTitle)(config.projectName, title);
|
|
34
|
+
// Attach a file whenever the body would be clipped in the feed preview.
|
|
35
|
+
const isLongBody = !!body && body.length > PREVIEW_LENGTH;
|
|
33
36
|
const canEncrypt = !!(0, crypto_js_1.getKeyPair)() && !!(0, crypto_js_1.getPublicKey)();
|
|
34
37
|
if (isLongBody && body) {
|
|
35
38
|
const fileName = 'response.md';
|
|
36
39
|
const fileType = (0, mime_js_1.inferMimeType)(fileName);
|
|
37
|
-
|
|
40
|
+
// Self-contained Markdown so the file alone carries the full text.
|
|
41
|
+
const fileMarkdown = `# ${title}\n\n${body}`;
|
|
42
|
+
const fileBytes = new TextEncoder().encode(fileMarkdown).byteLength;
|
|
38
43
|
// Encrypt file content if keys available
|
|
39
|
-
let uploadContent =
|
|
44
|
+
let uploadContent = fileMarkdown;
|
|
40
45
|
let uploadContentType = fileType;
|
|
41
46
|
let fileIv;
|
|
42
47
|
let fileEncryptedKey;
|
|
43
48
|
if (canEncrypt) {
|
|
44
49
|
try {
|
|
45
|
-
const encrypted = await (0, crypto_js_1.encryptFileForSelf)(
|
|
50
|
+
const encrypted = await (0, crypto_js_1.encryptFileForSelf)(fileMarkdown);
|
|
46
51
|
uploadContent = encrypted.ciphertext;
|
|
47
52
|
uploadContentType = 'application/octet-stream';
|
|
48
53
|
fileIv = encrypted.iv;
|
|
@@ -52,23 +57,23 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
52
57
|
console.error('[Crypto] File encryption failed, sending plaintext:', err);
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
|
-
const upload = await client.requestUpload({ fileName, fileType: uploadContentType, fileSize: typeof uploadContent === 'string' ?
|
|
60
|
+
const upload = await client.requestUpload({ fileName, fileType: uploadContentType, fileSize: typeof uploadContent === 'string' ? fileBytes : uploadContent.length });
|
|
56
61
|
await client.uploadToS3(upload.data.uploadUrl, uploadContent, uploadContentType);
|
|
57
|
-
const preview = body.
|
|
62
|
+
const preview = body.slice(0, PREVIEW_LENGTH) + '...';
|
|
58
63
|
// Encrypt push body (title/preview/url) if keys available
|
|
59
64
|
let pushPayload = {
|
|
60
|
-
title,
|
|
65
|
+
title: pushTitle,
|
|
61
66
|
body: preview,
|
|
62
67
|
url,
|
|
63
68
|
type: 'file',
|
|
64
69
|
priority,
|
|
65
|
-
files: [{ fileKey: upload.data.fileKey, fileName, fileSize, fileType, iv: fileIv, encryptedKey: fileEncryptedKey }],
|
|
70
|
+
files: [{ fileKey: upload.data.fileKey, fileName, fileSize: fileBytes, fileType, iv: fileIv, encryptedKey: fileEncryptedKey }],
|
|
66
71
|
targetDeviceId: deviceId,
|
|
67
72
|
sessionId: config.sessionId,
|
|
68
73
|
};
|
|
69
74
|
if (canEncrypt) {
|
|
70
75
|
try {
|
|
71
|
-
const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title, body: preview, url });
|
|
76
|
+
const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title: pushTitle, body: preview, url });
|
|
72
77
|
pushPayload = { ...pushPayload, title: undefined, body: enc.body, isEncrypted: enc.isEncrypted, encryptedKey: enc.encryptedKey, senderPublicKey: enc.senderPublicKey };
|
|
73
78
|
}
|
|
74
79
|
catch (err) {
|
|
@@ -80,7 +85,7 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
80
85
|
}
|
|
81
86
|
// Short body — encrypt push only
|
|
82
87
|
let pushPayload = {
|
|
83
|
-
title,
|
|
88
|
+
title: pushTitle,
|
|
84
89
|
body,
|
|
85
90
|
url,
|
|
86
91
|
type: 'hook',
|
|
@@ -90,7 +95,7 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
90
95
|
};
|
|
91
96
|
if (canEncrypt) {
|
|
92
97
|
try {
|
|
93
|
-
const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title, body, url });
|
|
98
|
+
const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title: pushTitle, body, url });
|
|
94
99
|
pushPayload = { ...pushPayload, title: undefined, body: enc.body, isEncrypted: enc.isEncrypted, encryptedKey: enc.encryptedKey, senderPublicKey: enc.senderPublicKey };
|
|
95
100
|
}
|
|
96
101
|
catch (err) {
|
package/dist/tools/prompt.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { ZephApiClient } from '../api-client.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type McpServerConfig } from '../config.js';
|
|
4
4
|
export declare const registerPromptTool: (server: McpServer, client: ZephApiClient, config: McpServerConfig) => void;
|
|
5
5
|
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/tools/prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/tools/prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAErE,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SAuEnG,CAAC"}
|
package/dist/tools/prompt.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.registerPromptTool = void 0;
|
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const error_format_js_1 = require("../error-format.js");
|
|
6
6
|
const poll_js_1 = require("../poll.js");
|
|
7
|
+
const config_js_1 = require("../config.js");
|
|
7
8
|
const registerPromptTool = (server, client, config) => {
|
|
8
9
|
server.registerTool('zeph_prompt', {
|
|
9
10
|
description: 'Ask the user to choose from predefined options via push notification. The tool blocks until the user responds or the timeout is reached. Requires ZEPH_HOOK_ID environment variable.',
|
|
@@ -41,7 +42,7 @@ const registerPromptTool = (server, client, config) => {
|
|
|
41
42
|
return (0, error_format_js_1.hookNotConfiguredError)();
|
|
42
43
|
try {
|
|
43
44
|
const trigger = await client.triggerHook(config.hookId, {
|
|
44
|
-
title,
|
|
45
|
+
title: (0, config_js_1.formatPushTitle)(config.projectName, title),
|
|
45
46
|
body,
|
|
46
47
|
actions,
|
|
47
48
|
timeout,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeph-to/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.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",
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest",
|
|
19
21
|
"prepublishOnly": "npm run build"
|
|
20
22
|
},
|
|
21
23
|
"dependencies": {
|
|
@@ -23,8 +25,9 @@
|
|
|
23
25
|
"zod": "^3.25.1"
|
|
24
26
|
},
|
|
25
27
|
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.0.0",
|
|
26
29
|
"typescript": "^5.8.0",
|
|
27
|
-
"
|
|
30
|
+
"vitest": "^2.1.9"
|
|
28
31
|
},
|
|
29
32
|
"release": {
|
|
30
33
|
"branches": [
|