@envseal/mcp-server 0.1.1 → 0.1.3
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/cli-args.d.ts +2 -2
- package/dist/cli-args.js +1 -1
- package/dist/confirm.d.ts +9 -8
- package/dist/confirm.js +12 -59
- package/dist/test-prompter.js +16 -8
- package/dist/tools/describe.js +0 -1
- package/package.json +5 -5
package/dist/cli-args.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Argument handling for the `envseal-mcp` binary, kept out of bin.ts so it can
|
|
3
3
|
* be tested without importing a module whose side effect is starting a server.
|
|
4
4
|
*/
|
|
5
|
-
export declare const VERSION = "0.1.
|
|
6
|
-
export declare const USAGE = "envseal-mcp 0.1.
|
|
5
|
+
export declare const VERSION = "0.1.3";
|
|
6
|
+
export declare const USAGE = "envseal-mcp 0.1.3\n\nServes the SEP/1 tools to an MCP host over stdio JSON-RPC.\n\nUsage: envseal-mcp [--project <dir>]\n\nOptions:\n --project <dir> Project root to broker secrets for. Default: search\n upward from the current directory for env.schema.jsonc,\n .git or package.json. If none is found, this server\n refuses to start rather than create .envseal/ in a\n directory that is not a project.\n --help, -h Show this help and exit.\n --version, -v Print the version and exit.\n\nTransport: stdio only. --http and --port were removed; they were parsed and\nthen ignored, and no HTTP or SSE transport was ever implemented. For an HTTP\nsurface use @envseal/http-server, which speaks REST + OpenAPI on loopback --\nthat is a different protocol, not MCP over HTTP.\n";
|
|
7
7
|
export type ParsedArgv = {
|
|
8
8
|
kind: 'help';
|
|
9
9
|
} | {
|
package/dist/cli-args.js
CHANGED
|
@@ -5,7 +5,7 @@ import { findProjectRoot } from '@envseal/core';
|
|
|
5
5
|
* Argument handling for the `envseal-mcp` binary, kept out of bin.ts so it can
|
|
6
6
|
* be tested without importing a module whose side effect is starting a server.
|
|
7
7
|
*/
|
|
8
|
-
export const VERSION = '0.1.
|
|
8
|
+
export const VERSION = '0.1.3';
|
|
9
9
|
export const USAGE = `envseal-mcp ${VERSION}
|
|
10
10
|
|
|
11
11
|
Serves the SEP/1 tools to an MCP host over stdio JSON-RPC.
|
package/dist/confirm.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { escapeForDisplay as coreEscapeForDisplay, displayArg as coreDisplayArg, type BrokerOptions, type TargetInfo } from '@envseal/core';
|
|
2
2
|
import type { ManifestEntry, VerifyResult } from '@envseal/protocol';
|
|
3
3
|
import type { Prompter } from '@envseal/prompters';
|
|
4
4
|
/**
|
|
@@ -18,13 +18,11 @@ import type { Prompter } from '@envseal/prompters';
|
|
|
18
18
|
* between a prompt-injected model and arbitrary code holding live
|
|
19
19
|
* credentials. In CI these operations are simply unavailable, and say so.
|
|
20
20
|
*
|
|
21
|
-
* DUPLICATION: this file
|
|
22
|
-
* packages/sdk/src/confirm.ts
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* packages test the behaviour independently, so drift shows up as a red test
|
|
27
|
-
* rather than as a binding that quietly stops asking.
|
|
21
|
+
* DUPLICATION: this file remains a hand-maintained twin of
|
|
22
|
+
* packages/sdk/src/confirm.ts for the ask/outcome mapping, but the dialog
|
|
23
|
+
* BODY (escaping, truncation caps, fingerprints, warnings) is shared via
|
|
24
|
+
* useConfirmationBody from @envseal/core so the CLI, SDK and MCP surfaces
|
|
25
|
+
* can never drift on what the user is shown.
|
|
28
26
|
*/
|
|
29
27
|
/** Value-entry key name carrying the `env_use` confirmation. */
|
|
30
28
|
export declare const CONFIRM_KEY_USE = "APPROVE";
|
|
@@ -40,10 +38,13 @@ export interface ConfirmSurface {
|
|
|
40
38
|
prompter: () => Promise<Prompter>;
|
|
41
39
|
timeoutMs?: number;
|
|
42
40
|
}
|
|
41
|
+
export declare const escapeForDisplay: typeof coreEscapeForDisplay;
|
|
42
|
+
export declare const displayArg: typeof coreDisplayArg;
|
|
43
43
|
export declare function useConfirmationBody(info: {
|
|
44
44
|
command: string[];
|
|
45
45
|
keys: string[];
|
|
46
46
|
networkEgress: boolean;
|
|
47
|
+
target?: TargetInfo;
|
|
47
48
|
}, projectRoot: string): string;
|
|
48
49
|
export declare function probeConfirmationBody(entry: ManifestEntry): string | null;
|
|
49
50
|
/**
|
package/dist/confirm.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { useConfirmationBody as sharedUseConfirmationBody, escapeForDisplay as coreEscapeForDisplay, displayArg as coreDisplayArg, } from '@envseal/core';
|
|
2
3
|
import { SepError, zero } from '@envseal/protocol';
|
|
3
4
|
import { makeDisplayNonce } from '@envseal/prompters';
|
|
4
5
|
/**
|
|
@@ -18,21 +19,17 @@ import { makeDisplayNonce } from '@envseal/prompters';
|
|
|
18
19
|
* between a prompt-injected model and arbitrary code holding live
|
|
19
20
|
* credentials. In CI these operations are simply unavailable, and say so.
|
|
20
21
|
*
|
|
21
|
-
* DUPLICATION: this file
|
|
22
|
-
* packages/sdk/src/confirm.ts
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* packages test the behaviour independently, so drift shows up as a red test
|
|
27
|
-
* rather than as a binding that quietly stops asking.
|
|
22
|
+
* DUPLICATION: this file remains a hand-maintained twin of
|
|
23
|
+
* packages/sdk/src/confirm.ts for the ask/outcome mapping, but the dialog
|
|
24
|
+
* BODY (escaping, truncation caps, fingerprints, warnings) is shared via
|
|
25
|
+
* useConfirmationBody from @envseal/core so the CLI, SDK and MCP surfaces
|
|
26
|
+
* can never drift on what the user is shown.
|
|
28
27
|
*/
|
|
29
28
|
/** Value-entry key name carrying the `env_use` confirmation. */
|
|
30
29
|
export const CONFIRM_KEY_USE = 'APPROVE';
|
|
31
30
|
/** Value-entry key name carrying the `env_verify` probe-consent question. */
|
|
32
31
|
export const CONFIRM_KEY_PROBE = 'APPROVE_PROBE';
|
|
33
32
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
34
|
-
/** Per-argument display cap; longer arguments are shown truncated, and said to be. */
|
|
35
|
-
const MAX_ARG_CHARS = 300;
|
|
36
33
|
/** Whole-dialog cap. Past this we refuse rather than ask about something unreadable. */
|
|
37
34
|
const MAX_BODY_CHARS = 16 * 1024;
|
|
38
35
|
const INSTRUCTION = 'Type yes to approve, or submit an empty box to deny.';
|
|
@@ -41,57 +38,13 @@ const INSTRUCTION = 'Type yes to approve, or submit an empty box to deny.';
|
|
|
41
38
|
* `env_use` in a loop and stack up dialogs until one gets clicked through.
|
|
42
39
|
*/
|
|
43
40
|
let confirmationOpen = false;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*/
|
|
50
|
-
function escapeForDisplay(value) {
|
|
51
|
-
let out = '';
|
|
52
|
-
for (const ch of value) {
|
|
53
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
54
|
-
if (code < 0x20 || (code >= 0x7f && code <= 0x9f)) {
|
|
55
|
-
out += `<0x${code.toString(16).padStart(2, '0')}>`;
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
out += ch;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return out;
|
|
62
|
-
}
|
|
63
|
-
function displayArg(arg) {
|
|
64
|
-
const escaped = escapeForDisplay(arg);
|
|
65
|
-
if (escaped.length <= MAX_ARG_CHARS) {
|
|
66
|
-
return escaped;
|
|
67
|
-
}
|
|
68
|
-
const hidden = escaped.length - MAX_ARG_CHARS;
|
|
69
|
-
return `${escaped.slice(0, MAX_ARG_CHARS)}[... ${hidden} more characters, not shown]`;
|
|
70
|
-
}
|
|
41
|
+
// Re-exported so existing importers of the twins keep working; the
|
|
42
|
+
// implementations live in @envseal/core/display.ts and are shared across
|
|
43
|
+
// every binding (CLI, SDK and MCP alike).
|
|
44
|
+
export const escapeForDisplay = coreEscapeForDisplay;
|
|
45
|
+
export const displayArg = coreDisplayArg;
|
|
71
46
|
export function useConfirmationBody(info, projectRoot) {
|
|
72
|
-
|
|
73
|
-
'EnvSeal is about to run a program with these secrets in its environment.',
|
|
74
|
-
'',
|
|
75
|
-
` project: ${escapeForDisplay(projectRoot)}`,
|
|
76
|
-
` keys: ${info.keys.length > 0 ? info.keys.map(escapeForDisplay).join(', ') : '(none)'}`,
|
|
77
|
-
'',
|
|
78
|
-
' command, one argument per line, exactly as it will be run (no shell):',
|
|
79
|
-
];
|
|
80
|
-
info.command.forEach((arg, index) => {
|
|
81
|
-
lines.push(` [${index}] ${displayArg(arg)}`);
|
|
82
|
-
});
|
|
83
|
-
lines.push('');
|
|
84
|
-
if (info.networkEgress) {
|
|
85
|
-
lines.push(' WARNING: this command can reach the network, so it could send these', ' values somewhere. Only continue if you trust it.');
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
// Honest about what the check is worth: NETWORK_TOOLS plus a URL scan is a
|
|
89
|
-
// heuristic, and claiming more would be the kind of overstatement this
|
|
90
|
-
// project has already had to walk back once.
|
|
91
|
-
lines.push(' No network tool or URL was recognised in this command. That is a', ' heuristic, not a guarantee: any program can open a socket.');
|
|
92
|
-
}
|
|
93
|
-
lines.push('', `${INSTRUCTION} Nothing runs unless you approve.`);
|
|
94
|
-
return lines.join('\n');
|
|
47
|
+
return sharedUseConfirmationBody(info, projectRoot);
|
|
95
48
|
}
|
|
96
49
|
export function probeConfirmationBody(entry) {
|
|
97
50
|
const probe = entry.verify;
|
package/dist/test-prompter.js
CHANGED
|
@@ -24,14 +24,22 @@ export function createStubPrompter(value) {
|
|
|
24
24
|
return {
|
|
25
25
|
id: 'ide',
|
|
26
26
|
available: async () => true,
|
|
27
|
-
prompt: async (req) =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
prompt: async (req) => {
|
|
28
|
+
// Announce the stub on stderr, mirroring the CLI's probe-approval
|
|
29
|
+
// notice: any answer that did not come from a human must be visible in
|
|
30
|
+
// the transcript. A model that discovers the two-variable gate cannot
|
|
31
|
+
// use it as a silent approval oracle.
|
|
32
|
+
process.stderr.write(`ENVSEAL_TEST_MODE: prompter is a STUB — key(s) [${req.keys.map((k) => k.key).join(', ')}] ` +
|
|
33
|
+
`answered from ENVSEAL_TEST_PROMPTER_VALUE with no UI.\n`);
|
|
34
|
+
return {
|
|
35
|
+
ticket: req.ticket,
|
|
36
|
+
results: req.keys.map((k) => ({
|
|
37
|
+
key: k.key,
|
|
38
|
+
outcome: 'entered',
|
|
39
|
+
value: secretFromUtf8(value),
|
|
40
|
+
})),
|
|
41
|
+
};
|
|
42
|
+
},
|
|
35
43
|
cancel: async () => {
|
|
36
44
|
/* nothing to tear down */
|
|
37
45
|
},
|
package/dist/tools/describe.js
CHANGED
|
@@ -11,7 +11,6 @@ export const description = 'Returns only redacted status: whether each key is pr
|
|
|
11
11
|
export const inputSchema = INPUT_SCHEMAS.env_describe;
|
|
12
12
|
export async function handler(args, broker) {
|
|
13
13
|
try {
|
|
14
|
-
const input = INPUT_SCHEMAS.env_describe.parse(args ?? {});
|
|
15
14
|
const result = await broker.describe();
|
|
16
15
|
return respond(result);
|
|
17
16
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envseal/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
27
27
|
"zod": "^3.24.1",
|
|
28
|
-
"@envseal/protocol": "0.1.
|
|
29
|
-
"@envseal/core": "0.1.
|
|
30
|
-
"@envseal/prompters": "0.1.
|
|
31
|
-
"@envseal/registry": "0.1.
|
|
28
|
+
"@envseal/protocol": "0.1.3",
|
|
29
|
+
"@envseal/core": "0.1.3",
|
|
30
|
+
"@envseal/prompters": "0.1.3",
|
|
31
|
+
"@envseal/registry": "0.1.3"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"tsx": "^4.19.2",
|