carpool-mcp 0.1.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.
@@ -0,0 +1,49 @@
1
+ // apps/mcp/src/consent.ts
2
+ var CONSENT_MODES = ["ask", "off"];
3
+ var CONSENT_MODE_ENV = "CARPOOL_PUBLISH_MODE";
4
+ var NON_PROMPTING_MODES = /* @__PURE__ */ new Set(["bypassPermissions", "acceptEdits", "dontAsk", "auto"]);
5
+ var ASK_REASON = "Publishing puts this research on sale. It cannot be recalled once bought \u2014 delisting stops new sales but copies already paid for stay with their buyers. Review the question, sources and stripped items before approving.";
6
+ function decideConsent(input, cfg = {}) {
7
+ if (input.agent_id) {
8
+ return {
9
+ decision: "deny",
10
+ reason: "carpool_publish is not available to subagents. Publishing is irreversible and needs a human in an interactive session; a subagent cannot be one."
11
+ };
12
+ }
13
+ if (input.permission_mode && NON_PROMPTING_MODES.has(input.permission_mode)) {
14
+ return {
15
+ decision: "deny",
16
+ reason: `carpool_publish is not available in permission mode "${input.permission_mode}", which does not prompt. Publishing cannot be auto-approved.`
17
+ };
18
+ }
19
+ const raw = cfg.mode?.trim();
20
+ if (raw === "off") {
21
+ return {
22
+ decision: "deny",
23
+ reason: `Publishing is turned off here (${CONSENT_MODE_ENV}=off).`
24
+ };
25
+ }
26
+ if (raw !== void 0 && raw !== "" && raw !== "ask") {
27
+ return {
28
+ decision: "deny",
29
+ reason: `${CONSENT_MODE_ENV} is set to "${raw}", which is not one of ${CONSENT_MODES.join(" | ")}. Refusing rather than guessing at an irreversible action.`
30
+ };
31
+ }
32
+ return { decision: "ask", reason: ASK_REASON };
33
+ }
34
+ function hookOutput(d) {
35
+ return {
36
+ hookSpecificOutput: {
37
+ hookEventName: "PreToolUse",
38
+ permissionDecision: d.decision,
39
+ permissionDecisionReason: d.reason
40
+ }
41
+ };
42
+ }
43
+ export {
44
+ ASK_REASON,
45
+ CONSENT_MODES,
46
+ CONSENT_MODE_ENV,
47
+ decideConsent,
48
+ hookOutput
49
+ };
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PreToolUse hook for carpool_publish.
4
+ *
5
+ * This exists because an MCP server cannot tell that it is running inside a
6
+ * subagent. Only the client knows, so only the client can enforce it.
7
+ *
8
+ * **It decides nothing itself.** Every rule lives in `src/consent.ts` and is
9
+ * imported from the compiled `dist/consent.js` below. That is the whole point of
10
+ * this file's current shape: it used to carry a hand-written subset of those
11
+ * rules — different reason strings, a different mode vocabulary, no awareness of
12
+ * the `auto` path — while `decideConsent`'s five tests covered the copy nobody
13
+ * executed. The tested decision could `allow`; this one always asked. Two
14
+ * implementations of one security decision, and the tests were pointed at the
15
+ * wrong one.
16
+ *
17
+ * What the decision can actually see, and therefore what it decides on:
18
+ * - `agent_id` — present for a subagent. Deniable, and denied.
19
+ * - `permission_mode` — a non-prompting mode must never auto-approve a
20
+ * publish, which is irreversible.
21
+ * - `CARPOOL_PUBLISH_MODE` — `ask` (default) or `off`. Nothing else.
22
+ *
23
+ * What it cannot see: whether the session is `-p`, headless, or backgrounded.
24
+ * There is no documented field for that. So the README says what this does —
25
+ * denies subagents, non-prompting modes and `off`, otherwise asks — and does not
26
+ * claim "blocked outside interactive sessions", which would be a lie.
27
+ *
28
+ * There is no `allow` outcome. Auto-listing always asks first.
29
+ *
30
+ * Install:
31
+ * "hooks": { "PreToolUse": [{ "matcher": "mcp__carpool__carpool_publish",
32
+ * "hooks": [{ "type": "command", "command": "node <path>/pre-publish.mjs" }] }] }
33
+ * Run `pnpm --filter @carpool/mcp build` first — this loads dist/consent.js.
34
+ */
35
+ import { readFileSync } from "node:fs";
36
+
37
+ /** Emit and exit. Every exit from this file goes through here. */
38
+ function emit(decision, reason) {
39
+ process.stdout.write(
40
+ JSON.stringify({
41
+ hookSpecificOutput: {
42
+ hookEventName: "PreToolUse",
43
+ permissionDecision: decision,
44
+ permissionDecisionReason: reason,
45
+ },
46
+ }),
47
+ );
48
+ process.exit(0);
49
+ }
50
+
51
+ let input;
52
+ try {
53
+ input = JSON.parse(readFileSync(0, "utf8"));
54
+ } catch {
55
+ // Unparseable input is not a reason to let an irreversible action through.
56
+ emit("deny", "carpool publish hook could not read its input; refusing rather than guessing.");
57
+ }
58
+
59
+ // Fail closed on a missing build rather than falling back to a second copy of
60
+ // the rules — a fallback is how the two implementations diverged in the first
61
+ // place.
62
+ let decideConsent, hookOutput;
63
+ try {
64
+ ({ decideConsent, hookOutput } = await import(new URL("../dist/consent.js", import.meta.url).href));
65
+ } catch (e) {
66
+ emit(
67
+ "deny",
68
+ "carpool publish hook could not load its decision module (dist/consent.js): " +
69
+ `${e.message}. Run \`pnpm --filter @carpool/mcp build\`. Refusing rather than guessing.`,
70
+ );
71
+ }
72
+
73
+ const out = hookOutput(decideConsent(input, { mode: process.env.CARPOOL_PUBLISH_MODE }));
74
+ process.stdout.write(JSON.stringify(out));
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "carpool-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Carpool: search, buy and sell prior agent research over x402 on Hedera.",
5
+ "type": "module",
6
+ "bin": {
7
+ "carpool-mcp": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "hooks",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=20.19"
17
+ },
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/RomarioKavin1/carpool.git"
22
+ },
23
+ "homepage": "https://carpool-dashboard-plum.vercel.app",
24
+ "bugs": {
25
+ "url": "https://github.com/RomarioKavin1/carpool/issues"
26
+ },
27
+ "keywords": [
28
+ "mcp",
29
+ "claude",
30
+ "x402",
31
+ "hedera",
32
+ "research",
33
+ "agents"
34
+ ],
35
+ "dependencies": {
36
+ "@hiero-ledger/sdk": "2.88.0",
37
+ "@modelcontextprotocol/sdk": "1.30.0",
38
+ "@x402/fetch": "2.25.0",
39
+ "@x402/hedera": "2.25.0",
40
+ "zod": "3.25.76"
41
+ }
42
+ }