@agfpd/totp-presence-mcp 0.2.0 → 0.2.1

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
@@ -11,7 +11,7 @@ owner-presence gate against prompt injection.
11
11
  ## Modes
12
12
 
13
13
  - **SOFT (default, zero-sudo, one command):** MCP tools
14
- (`totp_verify` / `totp_check_session` / `totp_status`) the agent calls itself,
14
+ (`totp_verify` / `totp_check_session`) the agent calls itself,
15
15
  plus a tiny conditional SessionStart directive. Advisory — cannot DENY a tool
16
16
  call; `tamper_resistant: false`.
17
17
  - **HARD (opt-in, two honest actions — one sudo, one phone pairing):** a
@@ -31,7 +31,10 @@ codex plugin add totp-presence@agfpd
31
31
 
32
32
  The source repo is **private** (`agfpd` org), so installing requires read access
33
33
  to the org (git/gh authenticated) — the same access model as every other agfpd
34
- plugin.
34
+ plugin. The MCP server is published separately on npm as
35
+ [`@agfpd/totp-presence-mcp`](https://www.npmjs.com/package/@agfpd/totp-presence-mcp)
36
+ and `.mcp.json` launches it via `npx`, so **Node ≥18 must be on `PATH`** (npx
37
+ fetches and runs it on first session start).
35
38
 
36
39
  Restart the session afterward — `hooks.json` / `.mcp.json` are not hot-reload.
37
40
  SOFT (advisory MCP) is live immediately. For HARD: `/totp-presence:setup` (one
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agfpd/totp-presence-mcp",
3
- "version": "0.2.0",
4
- "description": "MCP server for the totp-presence identity-gate plugin (Claude Code + Codex CLI). Three tools — totp_verify / totp_check_session / totp_status — wrap the root-owned /etc/totp-presence/verify so an agent can prove the physical owner is present before risky actions. The server runs as the user, never reads the seed; all verification happens under sudo in the audited root verifier.",
3
+ "version": "0.2.1",
4
+ "description": "MCP server for the totp-presence identity-gate plugin (Claude Code + Codex CLI). Two tools — totp_verify / totp_check_session — wrap the root-owned /etc/totp-presence/verify so an agent can prove the physical owner is present before risky actions. The server runs as the user, never reads the seed; all verification happens under sudo in the audited root verifier.",
5
5
  "license": "Apache-2.0",
6
6
  "author": {
7
7
  "name": "agfpd",
package/src/server.mjs CHANGED
@@ -6,7 +6,6 @@
6
6
  * code and inspect presence sessions without a hook:
7
7
  * totp_verify(code, integration) open/renew a presence session
8
8
  * totp_check_session(integration) inspect an integration's session state
9
- * totp_status() core install state + honest capability matrix
10
9
  *
11
10
  * The previous build hand-rolled the JSON-RPC stdio transport on the Python
12
11
  * stdlib. This rewrite delegates the wire protocol (initialize handshake,
@@ -38,7 +37,6 @@ import {
38
37
  ToolError,
39
38
  totpVerify,
40
39
  totpCheckSession,
41
- totpStatus,
42
40
  } from './totp.mjs';
43
41
 
44
42
  const here = dirname(fileURLToPath(import.meta.url));
@@ -95,21 +93,6 @@ export const TOOLS = [
95
93
  additionalProperties: false,
96
94
  },
97
95
  },
98
- {
99
- name: 'totp_status',
100
- description:
101
- 'Report core install state and the honest capability matrix.\n\n' +
102
- 'Returns core_installed, mode (soft|hard), tamper_resistant (false|true), ' +
103
- "and each integration's session state for the invoking user. " +
104
- "mode/tamper_resistant never over-assure: 'soft' means advisory-only " +
105
- "(cannot DENY a tool call); 'hard' requires the root-owned guard to be " +
106
- "installed. Note: 'hard' here means the guard is present — the PreToolUse " +
107
- 'hook must also be registered and the session restarted for enforcement to ' +
108
- 'be live. On Codex, PreToolUse covers Bash + apply_patch + mcp__ only (not ' +
109
- 'every tool — openai/codex#20204), and headless `codex exec` does not fire ' +
110
- 'the hook at all (Phase 0).',
111
- inputSchema: { type: 'object', properties: {}, additionalProperties: false },
112
- },
113
96
  ];
114
97
 
115
98
  /**
@@ -133,8 +116,6 @@ export function createServer({ version, runVerify } = {}) {
133
116
  value = totpVerify(args.code, args.integration, { runVerify });
134
117
  } else if (name === 'totp_check_session') {
135
118
  value = totpCheckSession(args.integration);
136
- } else if (name === 'totp_status') {
137
- value = totpStatus();
138
119
  } else {
139
120
  // Tool-not-found is an exceptional condition, not a tool execution error
140
121
  // → protocol error (-32602), matching the audited Python build.
package/src/totp.mjs CHANGED
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import { spawnSync } from 'node:child_process';
21
- import { statSync, readFileSync, readdirSync } from 'node:fs';
21
+ import { statSync, readFileSync } from 'node:fs';
22
22
  import { userInfo } from 'node:os';
23
23
 
24
24
  // --------------------------------------------------------------------------
@@ -32,7 +32,7 @@ export const RUNTIME_BASE = '/var/run/totp-presence';
32
32
 
33
33
  // Root-owned PreToolUse guard scripts. Their presence is the server's best proxy
34
34
  // for "HARD enforcement is installed" (the hook must also be registered and the
35
- // session restarted see the note in totpStatus).
35
+ // session restarted before HARD actually fires see detectCapability).
36
36
  export const ROOT_GUARDS = [`${INSTALL_DIR}/claude-code-guard.sh`, `${INSTALL_DIR}/guard.sh`];
37
37
 
38
38
  export const DEFAULT_WINDOW_SECONDS = 1500;
@@ -69,14 +69,6 @@ function isFile(p) {
69
69
  }
70
70
  }
71
71
 
72
- function isDir(p) {
73
- try {
74
- return statSync(p).isDirectory();
75
- } catch {
76
- return false;
77
- }
78
- }
79
-
80
72
  /**
81
73
  * Resolve the human user this server should scope sessions to.
82
74
  *
@@ -175,8 +167,7 @@ function requireCore() {
175
167
  if (!coreInstalled()) {
176
168
  throw new ToolError(
177
169
  'totp-presence core is not installed on this host. Run: ' +
178
- '/totp-presence:setup (one guided sudo). Use totp_status to check ' +
179
- 'installation state.',
170
+ '/totp-presence:setup (one guided sudo).',
180
171
  );
181
172
  }
182
173
  }
@@ -195,7 +186,7 @@ function resolveIntegration(name) {
195
186
  if (!isFile(config)) {
196
187
  throw new ToolError(
197
188
  `Integration ${JSON.stringify(name)} is not installed — ${config} does not exist. ` +
198
- 'Use totp_status to see which integrations are available.',
189
+ 'Run /totp-presence:setup to enroll an integration.',
199
190
  );
200
191
  }
201
192
  return { name, user, sessionFile: sessionFileFor(name, user), config };
@@ -287,7 +278,7 @@ export function totpVerify(code, integration, { runVerify = defaultRunVerify } =
287
278
  throw new ToolError(
288
279
  (r.stderr || '').trim() ||
289
280
  (r.stdout || '').trim() ||
290
- `Verify exited with code ${rc}. Use totp_status to check the install.`,
281
+ `Verify exited with code ${rc}. Check the totp-presence install (e.g. core/setup.sh status).`,
291
282
  );
292
283
  }
293
284
 
@@ -331,48 +322,3 @@ export function totpCheckSession(integration) {
331
322
  expires_in_seconds: ts + window - now,
332
323
  };
333
324
  }
334
-
335
- /** Report core install state and the honest capability matrix. */
336
- export function totpStatus() {
337
- const result = {
338
- core_installed: coreInstalled(),
339
- install_dir: INSTALL_DIR,
340
- runtime_base: RUNTIME_BASE,
341
- user: null,
342
- integrations: [],
343
- ...detectCapability(),
344
- };
345
- try {
346
- result.user = invokingUser();
347
- } catch (exc) {
348
- result.user_error = exc instanceof Error ? exc.message : String(exc);
349
- }
350
-
351
- if (!isDir(INSTALL_DIR)) {
352
- return result;
353
- }
354
-
355
- let entries;
356
- try {
357
- entries = readdirSync(INSTALL_DIR).filter(f => f.endsWith('-config')).sort();
358
- } catch {
359
- entries = [];
360
- }
361
- for (const fname of entries) {
362
- const name = fname.slice(0, -'-config'.length);
363
- if (!INTEGRATION_NAME_RE.test(name)) {
364
- continue;
365
- }
366
- try {
367
- result.integrations.push(totpCheckSession(name));
368
- } catch (exc) {
369
- // never let one integration break status
370
- result.integrations.push({
371
- integration: name,
372
- open: false,
373
- error: exc instanceof Error ? exc.message : String(exc),
374
- });
375
- }
376
- }
377
- return result;
378
- }