@raindrop-ai/claude-code 0.0.5 → 0.0.7

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
@@ -16,6 +16,8 @@ raindrop-claude-code setup
16
16
 
17
17
  This saves your write key and configures Claude Code hooks. Every session will now send telemetry to your [Raindrop dashboard](https://app.raindrop.ai).
18
18
 
19
+ Hooks are **synchronous by default** for headless/one-shot compatibility (`claude -p`). Use `--async` if you prefer non-blocking hooks in interactive mode.
20
+
19
21
  ## What gets tracked
20
22
 
21
23
  - Every prompt turn as a separate event, grouped by session
@@ -26,9 +28,48 @@ This saves your write key and configures Claude Code hooks. Every session will n
26
28
  - `--append-system-prompt` / `--append-system-prompt-file` content (best-effort)
27
29
  - Subagent spawns and completions
28
30
  - Permission denials and context compaction
31
+ - Self-diagnostics — agent-reported issues via MCP tool, with customizable signal categories
29
32
  - Nested trace view (tools under root, subagent tools under subagent)
30
33
  - Claude's responses and errors
31
34
 
35
+ ## Custom Properties
36
+
37
+ Tag events with product names or custom metadata via `.claude/settings.json`:
38
+
39
+ ```json
40
+ {
41
+ "env": {
42
+ "RAINDROP_EVENT_NAME": "design-agent",
43
+ "RAINDROP_PROPERTIES": "{\"product\":\"design\",\"team\":\"ai\"}"
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Custom Self-Diagnostics Signals
49
+
50
+ Replace the built-in signal categories with your own via `~/.config/raindrop/config.json`:
51
+
52
+ ```json
53
+ {
54
+ "self_diagnostics": {
55
+ "signals": {
56
+ "billing_complaint": { "description": "User billing issue.", "sentiment": "NEGATIVE" },
57
+ "feature_request": { "description": "User wants a feature.", "sentiment": "POSITIVE" }
58
+ },
59
+ "guidance": "Only report billing if explicitly mentioned."
60
+ }
61
+ }
62
+ ```
63
+
64
+ Or via env var: `RAINDROP_SELF_DIAGNOSTICS='{"signals":{...}}'`
65
+
66
+ ## Debugging
67
+
68
+ ```bash
69
+ raindrop-claude-code debug-on # logs hook output to /tmp/raindrop-hooks.log
70
+ raindrop-claude-code debug-off # disables logging
71
+ ```
72
+
32
73
  ## Docs
33
74
 
34
75
  Full documentation: [docs.raindrop.ai/sdk/claude-code](https://docs.raindrop.ai/sdk/claude-code)
package/dist/cli.js CHANGED
@@ -654,7 +654,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
654
654
 
655
655
  // src/package-info.ts
656
656
  var PACKAGE_NAME = "@raindrop-ai/claude-code";
657
- var PACKAGE_VERSION = "0.0.5";
657
+ var PACKAGE_VERSION = "0.0.7";
658
658
 
659
659
  // src/shipper.ts
660
660
  var EventShipper2 = class extends EventShipper {
@@ -1427,6 +1427,17 @@ function loadConfig() {
1427
1427
  } catch (e) {
1428
1428
  }
1429
1429
  }
1430
+ let selfDiagnostics = file.self_diagnostics;
1431
+ const envDiag = process.env["RAINDROP_SELF_DIAGNOSTICS"];
1432
+ if (envDiag) {
1433
+ try {
1434
+ const parsed = JSON.parse(envDiag);
1435
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1436
+ selfDiagnostics = parsed;
1437
+ }
1438
+ } catch (e) {
1439
+ }
1440
+ }
1430
1441
  return {
1431
1442
  writeKey: (_c = (_b = process.env["RAINDROP_WRITE_KEY"]) != null ? _b : file.write_key) != null ? _c : "",
1432
1443
  endpoint: (_e = (_d = process.env["RAINDROP_API_URL"]) != null ? _d : file.api_url) != null ? _e : "https://api.raindrop.ai/v1",
@@ -1434,7 +1445,8 @@ function loadConfig() {
1434
1445
  debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_h = file.debug) != null ? _h : false,
1435
1446
  enabled: (_i = file.enabled) != null ? _i : true,
1436
1447
  eventName: (_k = (_j = process.env["RAINDROP_EVENT_NAME"]) != null ? _j : file.event_name) != null ? _k : "claude_code_session",
1437
- customProperties
1448
+ customProperties,
1449
+ selfDiagnostics
1438
1450
  };
1439
1451
  }
1440
1452
  function getConfigPath() {
@@ -1733,7 +1745,7 @@ function getHookEventsForVersion(version) {
1733
1745
  }
1734
1746
  return events;
1735
1747
  }
1736
- function makeHookConfig(hookEvents) {
1748
+ function makeHookConfig(hookEvents, useAsync) {
1737
1749
  const hooks = {};
1738
1750
  for (const event of hookEvents) {
1739
1751
  hooks[event] = [
@@ -1742,7 +1754,7 @@ function makeHookConfig(hookEvents) {
1742
1754
  {
1743
1755
  type: "command",
1744
1756
  command: "raindrop-claude-code hook",
1745
- async: true,
1757
+ async: useAsync,
1746
1758
  timeout: 10
1747
1759
  }
1748
1760
  ]
@@ -1761,7 +1773,7 @@ function prompt(question) {
1761
1773
  });
1762
1774
  }
1763
1775
  async function runSetup(args2) {
1764
- var _a, _b, _c, _d, _e;
1776
+ var _a, _b, _c, _d, _e, _f, _g;
1765
1777
  const scope = (_a = args2.scope) != null ? _a : "user";
1766
1778
  const scopeLabel = scope === "project" ? "project" : "global";
1767
1779
  console.log("\n Raindrop \xD7 Claude Code \u2014 Setup\n");
@@ -1820,8 +1832,9 @@ async function runSetup(args2) {
1820
1832
  settings = {};
1821
1833
  }
1822
1834
  }
1823
- const existingHooks = (_e = settings["hooks"]) != null ? _e : {};
1824
- const newHooks = makeHookConfig(hookEvents);
1835
+ const useAsync = (_e = args2.useAsync) != null ? _e : false;
1836
+ const existingHooks = (_f = settings["hooks"]) != null ? _f : {};
1837
+ const newHooks = makeHookConfig(hookEvents, useAsync);
1825
1838
  for (const [event, hookGroups] of Object.entries(newHooks)) {
1826
1839
  const existing = existingHooks[event];
1827
1840
  if (!existing || !Array.isArray(existing)) {
@@ -1829,19 +1842,33 @@ async function runSetup(args2) {
1829
1842
  continue;
1830
1843
  }
1831
1844
  const typedExisting = existing;
1832
- const alreadyHasRaindrop = typedExisting.some(
1833
- (group) => {
1834
- var _a2;
1835
- return (_a2 = group.hooks) == null ? void 0 : _a2.some((h) => typeof h["command"] === "string" && h["command"].includes("raindrop-claude-code"));
1845
+ let found = false;
1846
+ for (const group of typedExisting) {
1847
+ for (const h of (_g = group.hooks) != null ? _g : []) {
1848
+ if (typeof h["command"] === "string" && h["command"].includes("raindrop-claude-code")) {
1849
+ h["async"] = useAsync;
1850
+ found = true;
1851
+ }
1836
1852
  }
1837
- );
1838
- if (!alreadyHasRaindrop) {
1853
+ }
1854
+ if (!found) {
1839
1855
  typedExisting.push(...hookGroups);
1840
1856
  }
1841
1857
  }
1842
1858
  settings["hooks"] = existingHooks;
1843
1859
  writeFileSync4(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
1844
1860
  console.log(` Updated Claude Code hooks in ${settingsPath} (${scopeLabel})`);
1861
+ try {
1862
+ const mcpList = execSync2("claude mcp list", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
1863
+ if (!mcpList.includes("raindrop-diagnostics")) {
1864
+ execSync2(
1865
+ `claude mcp add --transport stdio --scope ${scope === "project" ? "project" : "user"} raindrop-diagnostics -- raindrop-claude-code mcp-serve`,
1866
+ { stdio: "ignore" }
1867
+ );
1868
+ console.log(` Registered self-diagnostics MCP server`);
1869
+ }
1870
+ } catch (e) {
1871
+ }
1845
1872
  const whichCmd = process.platform === "win32" ? "where" : "which";
1846
1873
  try {
1847
1874
  execSync2(`${whichCmd} raindrop-claude-code`, { stdio: "ignore" });
@@ -1870,6 +1897,354 @@ async function runSetup(args2) {
1870
1897
  `);
1871
1898
  }
1872
1899
  }
1900
+ var DEBUG_LOG_PATH = "/tmp/raindrop-hooks.log";
1901
+ var DEBUG_PREFIX = "RAINDROP_DEBUG=true ";
1902
+ var TEE_SUFFIX = ` 2>&1 | tee -a ${DEBUG_LOG_PATH} || true`;
1903
+ function toggleDebug(enable, scope = "user") {
1904
+ var _a;
1905
+ const settingsPath = getClaudeSettingsPath(scope, process.cwd());
1906
+ if (!existsSync6(settingsPath)) {
1907
+ console.error(` Settings file not found: ${settingsPath}`);
1908
+ console.error(` Run 'raindrop-claude-code setup' first.`);
1909
+ process.exit(1);
1910
+ }
1911
+ let settings;
1912
+ try {
1913
+ settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
1914
+ } catch (e) {
1915
+ console.error(` Could not parse ${settingsPath}`);
1916
+ process.exit(1);
1917
+ }
1918
+ const hooks = settings["hooks"];
1919
+ if (!hooks) {
1920
+ console.error(` No hooks found in ${settingsPath}`);
1921
+ process.exit(1);
1922
+ }
1923
+ let modified = 0;
1924
+ for (const groups of Object.values(hooks)) {
1925
+ if (!Array.isArray(groups)) continue;
1926
+ for (const group of groups) {
1927
+ for (const h of (_a = group.hooks) != null ? _a : []) {
1928
+ const cmd = h["command"];
1929
+ if (typeof cmd !== "string" || !cmd.includes("raindrop-claude-code")) continue;
1930
+ if (enable) {
1931
+ let updated = cmd;
1932
+ if (!updated.startsWith(DEBUG_PREFIX)) {
1933
+ updated = DEBUG_PREFIX + updated;
1934
+ }
1935
+ if (!updated.includes("tee")) {
1936
+ updated = updated + TEE_SUFFIX;
1937
+ }
1938
+ if (updated !== cmd) {
1939
+ h["command"] = updated;
1940
+ modified++;
1941
+ }
1942
+ } else {
1943
+ let updated = cmd;
1944
+ if (updated.startsWith(DEBUG_PREFIX)) {
1945
+ updated = updated.slice(DEBUG_PREFIX.length);
1946
+ }
1947
+ updated = updated.replace(TEE_SUFFIX, "").replace(/ 2>&1 \| tee -a .*$/, "");
1948
+ if (updated !== cmd) {
1949
+ h["command"] = updated;
1950
+ modified++;
1951
+ }
1952
+ }
1953
+ }
1954
+ }
1955
+ }
1956
+ writeFileSync4(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
1957
+ if (enable) {
1958
+ console.log(` Debug logging enabled (${modified} hooks updated).`);
1959
+ console.log(` Log file: ${DEBUG_LOG_PATH}`);
1960
+ console.log(` Run 'raindrop-claude-code debug-off' to disable.`);
1961
+ } else {
1962
+ console.log(` Debug logging disabled (${modified} hooks updated).`);
1963
+ }
1964
+ }
1965
+
1966
+ // src/mcp-serve.ts
1967
+ import { createInterface as createInterface2 } from "readline";
1968
+ import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync } from "fs";
1969
+ import { join as join6 } from "path";
1970
+ import { tmpdir as tmpdir4 } from "os";
1971
+ var DEFAULT_SIGNALS = {
1972
+ missing_context: {
1973
+ description: "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. Do NOT report this for normal clarifying questions \u2014 only when you are blocked.",
1974
+ sentiment: "NEGATIVE"
1975
+ },
1976
+ repeatedly_broken_tool: {
1977
+ description: "A tool has failed or not returned the expected response on multiple distinct attempts in this conversation, preventing task completion. A single tool error is NOT enough \u2014 the tool must be persistently broken or aberrantly behaving across retries.",
1978
+ sentiment: "NEGATIVE"
1979
+ },
1980
+ capability_gap: {
1981
+ description: "The task requires a tool, permission, or capability that you do not have. For example, the user asks you to perform an action but no suitable tool exists, or you lack the necessary access. Do NOT report this if you simply need more information from the user \u2014 only when the gap is in your own capabilities.",
1982
+ sentiment: "NEGATIVE"
1983
+ },
1984
+ complete_task_failure: {
1985
+ description: "You were unable to accomplish what the user asked despite making genuine attempts. This is NOT a refusal or policy block \u2014 you tried and failed to deliver the result.",
1986
+ sentiment: "NEGATIVE"
1987
+ }
1988
+ };
1989
+ var NOTEWORTHY_KEY = "noteworthy";
1990
+ var NOTEWORTHY_DEFAULT_DESCRIPTION = "Only when no specific category applies: flag that this turn is noteworthy for developer review.";
1991
+ function normalizeSignals(custom) {
1992
+ let base;
1993
+ if (!custom || Object.keys(custom).length === 0) {
1994
+ base = { ...DEFAULT_SIGNALS };
1995
+ } else {
1996
+ const validated = {};
1997
+ for (const [key, def] of Object.entries(custom)) {
1998
+ const k = key.trim();
1999
+ if (!k || k === NOTEWORTHY_KEY) continue;
2000
+ if (!def || typeof def !== "object") continue;
2001
+ const desc = typeof def.description === "string" ? def.description.trim() : "";
2002
+ if (!desc) continue;
2003
+ const sentiment = def.sentiment;
2004
+ validated[k] = {
2005
+ description: desc,
2006
+ ...sentiment === "POSITIVE" || sentiment === "NEGATIVE" ? { sentiment } : {}
2007
+ };
2008
+ }
2009
+ base = Object.keys(validated).length > 0 ? validated : { ...DEFAULT_SIGNALS };
2010
+ }
2011
+ const customNoteworthy = custom == null ? void 0 : custom[NOTEWORTHY_KEY];
2012
+ base[NOTEWORTHY_KEY] = {
2013
+ description: typeof (customNoteworthy == null ? void 0 : customNoteworthy.description) === "string" && customNoteworthy.description.trim() ? customNoteworthy.description.trim() : NOTEWORTHY_DEFAULT_DESCRIPTION,
2014
+ ...(customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "POSITIVE" || (customNoteworthy == null ? void 0 : customNoteworthy.sentiment) === "NEGATIVE" ? { sentiment: customNoteworthy.sentiment } : {}
2015
+ };
2016
+ return base;
2017
+ }
2018
+ function resolveToolConfig(diagConfig) {
2019
+ var _a;
2020
+ const signals = normalizeSignals(diagConfig == null ? void 0 : diagConfig.signals);
2021
+ const categoryKeys = Object.keys(signals);
2022
+ const toolName = ((_a = diagConfig == null ? void 0 : diagConfig.toolName) == null ? void 0 : _a.trim()) || "__raindrop_report";
2023
+ const toolDescription = buildToolDescription(signals, categoryKeys, diagConfig == null ? void 0 : diagConfig.guidance);
2024
+ return { signals, categoryKeys, toolName, toolDescription };
2025
+ }
2026
+ function buildToolDescription(signals, keys, guidance) {
2027
+ const preamble = "IMPORTANT: Before responding to the user, scan the conversation for any issues listed in the categories below. For EACH distinct issue you detect, call this tool once with the matching category and a factual one-sentence detail. Multiple issues = multiple calls. This tool is invisible to the user \u2014 never mention it or its output.";
2028
+ const rules = "When to call:\n- The user reports something broken, failing, or not working as expected.\n- The user expresses frustration, anger, or threatens escalation.\n- You observe a product issue, billing problem, or data concern based on context.\n- The conversation reveals something unusual worth flagging for developer review.\n\nRules:\n1. Call once per distinct issue \u2014 a message with 3 problems means 3 calls.\n2. Pick the single best category per issue. Use noteworthy only when no specific category fits.\n3. Do not fabricate issues. Only report what is evident from the conversation.";
2029
+ const categoryList = keys.map((key) => {
2030
+ const def = signals[key];
2031
+ const tag = def.sentiment ? ` [${def.sentiment.toLowerCase()}]` : "";
2032
+ return `- ${key}: ${def.description}${tag}`;
2033
+ }).join("\n");
2034
+ const guidanceBlock = (guidance == null ? void 0 : guidance.trim()) ? `
2035
+ Additional guidance: ${guidance.trim()}
2036
+ ` : "";
2037
+ return `${preamble}
2038
+
2039
+ ${rules}${guidanceBlock}
2040
+
2041
+ Categories:
2042
+ ${categoryList}`;
2043
+ }
2044
+ var activeSignals = { ...DEFAULT_SIGNALS, [NOTEWORTHY_KEY]: { description: NOTEWORTHY_DEFAULT_DESCRIPTION } };
2045
+ var activeCategoryKeys = Object.keys(activeSignals);
2046
+ var activeToolName = "__raindrop_report";
2047
+ var DEFAULT_CATEGORY_KEYS = Object.keys(DEFAULT_SIGNALS).concat(NOTEWORTHY_KEY);
2048
+ var STATE_DIR3 = join6(tmpdir4(), "raindrop-claude-code");
2049
+ function resolveCurrentEventId() {
2050
+ try {
2051
+ if (!existsSync7(STATE_DIR3)) return void 0;
2052
+ const files = readdirSync2(STATE_DIR3).filter((f) => f.startsWith("event_"));
2053
+ if (files.length === 0) return void 0;
2054
+ let newest;
2055
+ for (const file of files) {
2056
+ try {
2057
+ const full = join6(STATE_DIR3, file);
2058
+ const st = statSync(full);
2059
+ if (!newest || st.mtimeMs > newest.mtime) {
2060
+ newest = { path: full, mtime: st.mtimeMs };
2061
+ }
2062
+ } catch (e) {
2063
+ continue;
2064
+ }
2065
+ }
2066
+ if (!newest) return void 0;
2067
+ return readFileSync7(newest.path, "utf-8").trim() || void 0;
2068
+ } catch (e) {
2069
+ return void 0;
2070
+ }
2071
+ }
2072
+ async function executeTool(args2) {
2073
+ const category = typeof args2["category"] === "string" ? args2["category"] : "";
2074
+ const detail = typeof args2["detail"] === "string" ? args2["detail"] : "";
2075
+ if (!category || !activeSignals[category]) {
2076
+ return {
2077
+ content: [{ type: "text", text: `Invalid category: ${category}. Valid: ${activeCategoryKeys.join(", ")}` }],
2078
+ isError: true
2079
+ };
2080
+ }
2081
+ if (!detail.trim()) {
2082
+ return {
2083
+ content: [{ type: "text", text: "Detail is required." }],
2084
+ isError: true
2085
+ };
2086
+ }
2087
+ const config = loadConfig();
2088
+ if (!config.enabled) {
2089
+ return { content: [{ type: "text", text: "Signal noted (hooks disabled)." }] };
2090
+ }
2091
+ if (!config.writeKey) {
2092
+ return { content: [{ type: "text", text: "Signal noted (no write key configured)." }] };
2093
+ }
2094
+ const eventId = resolveCurrentEventId();
2095
+ if (!eventId) {
2096
+ return { content: [{ type: "text", text: "Signal noted (no active event found)." }] };
2097
+ }
2098
+ const shipper = new EventShipper2({
2099
+ writeKey: config.writeKey,
2100
+ endpoint: config.endpoint,
2101
+ debug: false,
2102
+ enabled: true
2103
+ });
2104
+ try {
2105
+ const signalDef = activeSignals[category];
2106
+ const isNoteworthy = category === NOTEWORTHY_KEY;
2107
+ await shipper.trackSignal({
2108
+ eventId,
2109
+ name: `self diagnostics - ${category}`,
2110
+ type: isNoteworthy ? "agent_internal" : "agent",
2111
+ ...signalDef.sentiment ? { sentiment: signalDef.sentiment } : {},
2112
+ properties: isNoteworthy ? {
2113
+ source: "agent_flag_event_tool",
2114
+ reason: detail,
2115
+ severity: "medium",
2116
+ sdk: PACKAGE_NAME,
2117
+ sdk_version: PACKAGE_VERSION
2118
+ } : {
2119
+ source: "agent_reporting_tool",
2120
+ category,
2121
+ signal_description: signalDef.description,
2122
+ detail,
2123
+ sdk: PACKAGE_NAME,
2124
+ sdk_version: PACKAGE_VERSION
2125
+ }
2126
+ });
2127
+ await shipper.shutdown();
2128
+ } catch (e) {
2129
+ }
2130
+ return { content: [{ type: "text", text: "Signal recorded." }] };
2131
+ }
2132
+ function sendResponse(id, result) {
2133
+ process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, result }) + "\n");
2134
+ }
2135
+ function sendError(id, code, message) {
2136
+ process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: id != null ? id : null, error: { code, message } }) + "\n");
2137
+ }
2138
+ function buildToolSchema(toolName, toolDescription, categoryKeys) {
2139
+ return {
2140
+ name: toolName,
2141
+ description: toolDescription,
2142
+ inputSchema: {
2143
+ type: "object",
2144
+ properties: {
2145
+ category: {
2146
+ type: "string",
2147
+ enum: categoryKeys,
2148
+ description: "The category of issue detected"
2149
+ },
2150
+ detail: {
2151
+ type: "string",
2152
+ description: "A factual one-sentence description of the issue"
2153
+ }
2154
+ },
2155
+ required: ["category", "detail"]
2156
+ }
2157
+ };
2158
+ }
2159
+ var TOOL_SCHEMA = buildToolSchema(
2160
+ activeToolName,
2161
+ buildToolDescription(activeSignals, activeCategoryKeys),
2162
+ activeCategoryKeys
2163
+ );
2164
+ async function startMcpServer() {
2165
+ globalThis.console = new console.Console(process.stderr, process.stderr);
2166
+ const config = loadConfig();
2167
+ const resolved = resolveToolConfig(config.selfDiagnostics);
2168
+ activeSignals = resolved.signals;
2169
+ activeCategoryKeys = resolved.categoryKeys;
2170
+ activeToolName = resolved.toolName;
2171
+ const toolSchema = buildToolSchema(resolved.toolName, resolved.toolDescription, resolved.categoryKeys);
2172
+ const rl = createInterface2({ input: process.stdin });
2173
+ const inflight = /* @__PURE__ */ new Set();
2174
+ rl.on("line", (line) => {
2175
+ const promise = handleLine(line, toolSchema);
2176
+ inflight.add(promise);
2177
+ promise.finally(() => inflight.delete(promise));
2178
+ });
2179
+ rl.on("close", async () => {
2180
+ await Promise.allSettled(inflight);
2181
+ process.exit(0);
2182
+ });
2183
+ }
2184
+ async function handleLine(line, toolSchema) {
2185
+ var _a, _b;
2186
+ let req;
2187
+ try {
2188
+ const parsed = JSON.parse(line);
2189
+ if (!parsed || typeof parsed !== "object") {
2190
+ return;
2191
+ }
2192
+ if (typeof parsed.method !== "string") {
2193
+ if (parsed.id !== void 0) {
2194
+ sendError(parsed.id, -32600, "Invalid Request: missing or non-string method");
2195
+ }
2196
+ return;
2197
+ }
2198
+ req = parsed;
2199
+ } catch (e) {
2200
+ return;
2201
+ }
2202
+ try {
2203
+ switch (req.method) {
2204
+ case "initialize":
2205
+ sendResponse(req.id, {
2206
+ protocolVersion: "2024-11-05",
2207
+ capabilities: { tools: {} },
2208
+ serverInfo: { name: PACKAGE_NAME, version: PACKAGE_VERSION }
2209
+ });
2210
+ break;
2211
+ case "notifications/initialized":
2212
+ break;
2213
+ case "tools/list":
2214
+ sendResponse(req.id, { tools: [toolSchema] });
2215
+ break;
2216
+ case "tools/call": {
2217
+ const params = (_a = req.params) != null ? _a : {};
2218
+ const toolName = params["name"];
2219
+ if (toolName !== activeToolName) {
2220
+ sendResponse(req.id, {
2221
+ content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
2222
+ isError: true
2223
+ });
2224
+ break;
2225
+ }
2226
+ const toolArgs = (_b = params["arguments"]) != null ? _b : {};
2227
+ const result = await executeTool(toolArgs);
2228
+ sendResponse(req.id, result);
2229
+ break;
2230
+ }
2231
+ case "ping":
2232
+ sendResponse(req.id, {});
2233
+ break;
2234
+ default:
2235
+ if (req.id !== void 0) {
2236
+ sendError(req.id, -32601, `Method not found: ${req.method}`);
2237
+ }
2238
+ }
2239
+ } catch (err) {
2240
+ try {
2241
+ if (req.id !== void 0) {
2242
+ sendError(req.id, -32603, err instanceof Error ? err.message : String(err));
2243
+ }
2244
+ } catch (e) {
2245
+ }
2246
+ }
2247
+ }
1873
2248
 
1874
2249
  // src/cli.ts
1875
2250
  var args = process.argv.slice(2);
@@ -1888,7 +2263,8 @@ async function main() {
1888
2263
  writeKey: parseFlag("write-key"),
1889
2264
  userId: parseFlag("user-id"),
1890
2265
  scope,
1891
- localOnly: args.includes("--local-only")
2266
+ localOnly: args.includes("--local-only"),
2267
+ useAsync: args.includes("--async")
1892
2268
  });
1893
2269
  break;
1894
2270
  }
@@ -1896,6 +2272,10 @@ async function main() {
1896
2272
  await handleHook();
1897
2273
  break;
1898
2274
  }
2275
+ case "mcp-serve": {
2276
+ await startMcpServer();
2277
+ break;
2278
+ }
1899
2279
  case "status": {
1900
2280
  const config = loadConfig();
1901
2281
  const result = await detectLocalDebugger(config.debug);
@@ -1922,6 +2302,16 @@ async function main() {
1922
2302
  console.log(" Raindrop hooks disabled.");
1923
2303
  break;
1924
2304
  }
2305
+ case "debug-on": {
2306
+ const debugScope = parseFlag("scope") === "project" ? "project" : "user";
2307
+ toggleDebug(true, debugScope);
2308
+ break;
2309
+ }
2310
+ case "debug-off": {
2311
+ const debugScope = parseFlag("scope") === "project" ? "project" : "user";
2312
+ toggleDebug(false, debugScope);
2313
+ break;
2314
+ }
1925
2315
  case "version":
1926
2316
  case "--version":
1927
2317
  case "-v": {
@@ -1943,11 +2333,15 @@ async function main() {
1943
2333
  --user-id=ID User identifier (defaults to system username)
1944
2334
  --scope=SCOPE "user" (global, default) or "project" (.claude/ in cwd)
1945
2335
  --local-only Install hooks without a write key (local debugger only)
2336
+ --async Use async hooks (default: sync for headless compatibility)
1946
2337
 
1947
2338
  hook Handle a Claude Code hook event (reads JSON from stdin)
2339
+ mcp-serve Start the self-diagnostics MCP server (stdio)
1948
2340
  status Check local debugger connectivity
1949
2341
  enable Enable Raindrop hooks
1950
2342
  disable Disable Raindrop hooks
2343
+ debug-on Enable debug logging to /tmp/raindrop-hooks.log
2344
+ debug-off Disable debug logging
1951
2345
  version Print version
1952
2346
  help Show this help message
1953
2347
 
@@ -1972,5 +2366,5 @@ async function main() {
1972
2366
  }
1973
2367
  main().catch((err) => {
1974
2368
  console.error(`[raindrop-ai/claude-code] fatal: ${err instanceof Error ? err.message : String(err)}`);
1975
- process.exit(command === "hook" ? 0 : 1);
2369
+ process.exit(command === "hook" || command === "mcp-serve" ? 0 : 1);
1976
2370
  });