@hebbianvault/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
@@ -90,6 +90,7 @@ Your token decides what the adapter can see and do — a personal-workspace toke
90
90
  | `hebbian_read_node` | Read a single node by UUID (content, metadata, connected edges) |
91
91
  | `hebbian_search` | Find nodes in your workspace matching a query |
92
92
  | `hebbian_ask` | Ask a question and get an answer backed by source quotes |
93
+ | `hebbian_context` | Describe a task and a token budget, get back a salience-ranked context pack that fits |
93
94
  | `hebbian_capture` | Write a note into your workspace |
94
95
  | `hebbian_traverse` | Explore nodes connected to a starting node |
95
96
  | `hebbian_provenance` | See where a node's knowledge came from |
@@ -98,6 +99,38 @@ Your token decides what the adapter can see and do — a personal-workspace toke
98
99
 
99
100
  Results only ever include what your token is allowed to see.
100
101
 
102
+ ## Onboard an agent you already use
103
+
104
+ Every agent you already run carries its own context: a Claude Code memory directory, CLAUDE.md files, a folder of markdown notes. The `absorb` command brings that accumulated context into Hebbian, so an agent you have used for months arrives with what it already knows instead of a blank slate.
105
+
106
+ Absorbed files land as seeds in your review lane (they do not write straight into the workspace), attributed to the agent principal you name. You review and promote them like any other proposed knowledge.
107
+
108
+ First, register the agent and issue it a token from your Hebbian integrations page (AI Tools tab), and note its agent id. Then point `absorb` at the store:
109
+
110
+ ```bash
111
+ # A Claude Code memory directory (MEMORY.md index + CLAUDE.md files + notes)
112
+ HEBBIAN_API_TOKEN=your_agent_token \
113
+ npx -y @hebbianvault/mcp absorb claude-code ~/.claude/projects/my-project --agent <agent_id>
114
+
115
+ # Any directory of markdown files
116
+ HEBBIAN_API_TOKEN=your_agent_token \
117
+ npx -y @hebbianvault/mcp absorb markdown ./docs --agent <agent_id>
118
+
119
+ # Walk and report what would be uploaded, without uploading
120
+ npx -y @hebbianvault/mcp absorb claude-code ~/.claude/projects/my-project --dry-run
121
+ ```
122
+
123
+ Each `*.md` file becomes one seed. The title is the file's first heading (or its name), and the source identifier is its path relative to the directory you pointed at. Re-running `absorb` on the same directory is safe: a file already absorbed for that agent is reported as a duplicate, not uploaded twice.
124
+
125
+ ### What never leaves your machine
126
+
127
+ `absorb` excludes secrets before anything is sent:
128
+
129
+ - Files whose names look like credentials are skipped entirely: `.env*`, anything with `credentials`, `secret`, or `token` in the name, plus `.pem`, `.key`, and SSH key files.
130
+ - Token-shaped strings inside the files it does read are redacted before upload: `sk-...`, `ghp_...`, `hbn_...`, Slack and AWS keys, JWTs, long hex and base64 blobs, and Bearer header values.
131
+
132
+ Every run prints a summary of how many files were skipped and how many strings were redacted, so you can see exactly what was filtered.
133
+
101
134
  ## Security
102
135
 
103
136
  - Tokens are bearer credentials — treat like passwords.
@@ -107,7 +140,7 @@ Results only ever include what your token is allowed to see.
107
140
 
108
141
  ## Python
109
142
 
110
- A Python sibling package is available as `hebbianvault-mcp` on PyPI. Same 8 tools, same configuration.
143
+ A Python sibling package is available as `hebbianvault-mcp` on PyPI. Same 9 tools, same configuration.
111
144
 
112
145
  ## Development
113
146
 
@@ -0,0 +1,25 @@
1
+ /**
2
+ * src/absorb/cli.ts
3
+ *
4
+ * The `absorb` command (ADR-055 §4). Onboard an agent you already use by
5
+ * uploading its existing context store as review-lane seeds.
6
+ *
7
+ * hebbian-mcp absorb claude-code <dir> --agent <agent_id>
8
+ * hebbian-mcp absorb markdown <dir> --agent <agent_id>
9
+ *
10
+ * Reads *.md files locally (Claude Code memory dir: MEMORY.md index + CLAUDE.md
11
+ * files; generic markdown: any *.md), skips credential-named files, redacts
12
+ * token-shaped strings, then batches the items to
13
+ * POST /v1/agents/{agent_id}/absorb with the agent token from the existing
14
+ * config/env convention (config.ts).
15
+ *
16
+ * --dry-run walks and reports without uploading. The auth token + API URL come
17
+ * from HEBBIAN_API_TOKEN / config file exactly like the MCP server.
18
+ */
19
+ /**
20
+ * Run the absorb command. Returns the process exit code (0 ok, non-zero error).
21
+ * Reads the store, prints the skipped/redacted summary, then (unless --dry-run)
22
+ * uploads in batches and prints the per-batch tally.
23
+ */
24
+ export declare function runAbsorb(argv: string[]): Promise<number>;
25
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/absorb/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA6FH;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiE/D"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * src/absorb/cli.ts
3
+ *
4
+ * The `absorb` command (ADR-055 §4). Onboard an agent you already use by
5
+ * uploading its existing context store as review-lane seeds.
6
+ *
7
+ * hebbian-mcp absorb claude-code <dir> --agent <agent_id>
8
+ * hebbian-mcp absorb markdown <dir> --agent <agent_id>
9
+ *
10
+ * Reads *.md files locally (Claude Code memory dir: MEMORY.md index + CLAUDE.md
11
+ * files; generic markdown: any *.md), skips credential-named files, redacts
12
+ * token-shaped strings, then batches the items to
13
+ * POST /v1/agents/{agent_id}/absorb with the agent token from the existing
14
+ * config/env convention (config.ts).
15
+ *
16
+ * --dry-run walks and reports without uploading. The auth token + API URL come
17
+ * from HEBBIAN_API_TOKEN / config file exactly like the MCP server.
18
+ */
19
+ import { HebbianClient } from "../client.js";
20
+ import { loadConfig } from "../config.js";
21
+ import { isSupportedStore, scanDirectory, supportedStores, } from "./importers.js";
22
+ /** Server-side batch cap (ADR-055 §2). Keep in sync with MAX_BATCH_ITEMS. */
23
+ const BATCH_SIZE = 200;
24
+ function parseFlags(argv) {
25
+ const positionals = [];
26
+ let agentId;
27
+ let dryRun = false;
28
+ for (let i = 0; i < argv.length; i += 1) {
29
+ const a = argv[i];
30
+ if (a === "--agent" || a === "--agent-id") {
31
+ agentId = argv[i + 1];
32
+ i += 1;
33
+ }
34
+ else if (a.startsWith("--agent=")) {
35
+ agentId = a.slice("--agent=".length);
36
+ }
37
+ else if (a === "--dry-run") {
38
+ dryRun = true;
39
+ }
40
+ else {
41
+ positionals.push(a);
42
+ }
43
+ }
44
+ return { agentId, dryRun, positionals };
45
+ }
46
+ const USAGE = [
47
+ "Usage: hebbian-mcp absorb <store> <dir> --agent <agent_id> [--dry-run]",
48
+ "",
49
+ ` store one of: ${supportedStores().join(", ")}`,
50
+ " dir path to the context store directory",
51
+ " --agent the agent principal UUID to absorb into (the agent you already use)",
52
+ " --dry-run walk and report; do not upload",
53
+ "",
54
+ "Auth: set HEBBIAN_API_TOKEN to the agent's token (or use the config file).",
55
+ "Reads *.md files, skips credential-named files, redacts token-shaped strings,",
56
+ "then uploads each file as a review-lane seed attributed to the agent.",
57
+ ].join("\n");
58
+ /** Resolve + validate args. Returns null (after printing usage) on a usage error. */
59
+ function resolveArgs(argv) {
60
+ const { agentId, dryRun, positionals } = parseFlags(argv);
61
+ const [store, dir] = positionals;
62
+ if (!store || !dir) {
63
+ process.stderr.write(`${USAGE}\n`);
64
+ return null;
65
+ }
66
+ if (!isSupportedStore(store)) {
67
+ process.stderr.write(`absorb: unsupported store "${store}". Supported: ${supportedStores().join(", ")}\n`);
68
+ return null;
69
+ }
70
+ if (!dryRun && !agentId) {
71
+ process.stderr.write("absorb: --agent <agent_id> is required (or use --dry-run)\n");
72
+ return null;
73
+ }
74
+ return { store, dir, agentId: agentId ?? "", dryRun };
75
+ }
76
+ function chunk(arr, size) {
77
+ const out = [];
78
+ for (let i = 0; i < arr.length; i += size)
79
+ out.push(arr.slice(i, i + size));
80
+ return out;
81
+ }
82
+ /**
83
+ * Run the absorb command. Returns the process exit code (0 ok, non-zero error).
84
+ * Reads the store, prints the skipped/redacted summary, then (unless --dry-run)
85
+ * uploads in batches and prints the per-batch tally.
86
+ */
87
+ export async function runAbsorb(argv) {
88
+ const args = resolveArgs(argv);
89
+ if (args === null)
90
+ return 2;
91
+ const scan = scanDirectory(args.dir, args.store);
92
+ // Honest summary line (ADR-055 §4): how many files, skipped secrets, redactions.
93
+ process.stderr.write(`[absorb] store=${args.store} dir=${args.dir}: ` +
94
+ `${scan.items.length} file(s) to absorb, ` +
95
+ `skipped ${scan.skippedSecretFiles.length} credential-named file(s), ` +
96
+ `redacted ${scan.redactedSecrets} token-shaped string(s) across ${scan.redactedItems} file(s)\n`);
97
+ if (scan.skippedSecretFiles.length > 0) {
98
+ for (const f of scan.skippedSecretFiles) {
99
+ process.stderr.write(`[absorb] skipped (looks like secrets): ${f}\n`);
100
+ }
101
+ }
102
+ if (scan.items.length === 0) {
103
+ process.stderr.write("[absorb] nothing to absorb.\n");
104
+ return 0;
105
+ }
106
+ if (args.dryRun) {
107
+ process.stderr.write(`[absorb] dry-run: would upload ${scan.items.length} item(s) to ` +
108
+ `/v1/agents/${args.agentId || "<agent_id>"}/absorb in ` +
109
+ `${chunk(scan.items, BATCH_SIZE).length} batch(es). No upload performed.\n`);
110
+ return 0;
111
+ }
112
+ const config = loadConfig();
113
+ const client = new HebbianClient(config.apiUrl, config.token, config.tenant);
114
+ let accepted = 0;
115
+ let duplicates = 0;
116
+ let errors = 0;
117
+ const batches = chunk(scan.items, BATCH_SIZE);
118
+ for (let i = 0; i < batches.length; i += 1) {
119
+ const batch = batches[i];
120
+ try {
121
+ const resp = (await client.post(`/v1/agents/${args.agentId}/absorb`, {
122
+ items: batch,
123
+ }));
124
+ accepted += resp.accepted ?? 0;
125
+ duplicates += resp.duplicates ?? 0;
126
+ errors += resp.errors ?? 0;
127
+ process.stderr.write(`[absorb] batch ${i + 1}/${batches.length}: ` +
128
+ `accepted ${resp.accepted ?? 0}, duplicate ${resp.duplicates ?? 0}, error ${resp.errors ?? 0}\n`);
129
+ }
130
+ catch (err) {
131
+ const message = err instanceof Error ? err.message : String(err);
132
+ process.stderr.write(`[absorb] batch ${i + 1}/${batches.length} failed: ${message}\n`);
133
+ return 1;
134
+ }
135
+ }
136
+ process.stderr.write(`[absorb] done: ${accepted} new seed(s), ${duplicates} already absorbed, ${errors} error(s). ` +
137
+ "New seeds await review in the Hebbian review lane.\n");
138
+ return errors > 0 ? 1 : 0;
139
+ }
140
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/absorb/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * src/absorb/importers.ts
3
+ *
4
+ * Local importers for the absorb command (ADR-055 §4). An importer walks a
5
+ * directory, reads the files relevant to one store kind, and turns each into an
6
+ * AbsorbItem. It NEVER posts anything — the CLI batches the items to the API.
7
+ *
8
+ * Two importers ship in this slice:
9
+ * claude-code — a Claude Code memory directory: every *.md file, including
10
+ * the MEMORY.md index and any CLAUDE.md files.
11
+ * markdown — generic catch-all: walk a directory tree for every *.md file.
12
+ *
13
+ * Both reuse the same secret guards (secrets.ts): a credential-named file is
14
+ * skipped, and token-shaped strings are redacted from the content before it is
15
+ * handed back. Each importer reports skipped/redacted counts so the CLI can
16
+ * print an honest summary.
17
+ */
18
+ /** One memory item ready to absorb. Matches the API's AbsorbItem shape. */
19
+ export interface AbsorbItem {
20
+ store_kind: string;
21
+ /** Stable identifier within the store — the file's path relative to the root. */
22
+ source_id: string;
23
+ /** First markdown heading, else the filename. */
24
+ title: string;
25
+ /** Redacted file content. */
26
+ content: string;
27
+ /** Filesystem birthtime (ISO-8601), best-effort. */
28
+ created_at?: string;
29
+ /** Filesystem mtime (ISO-8601). */
30
+ updated_at?: string;
31
+ }
32
+ /** Outcome of walking a directory: items to upload + what was skipped/redacted. */
33
+ export interface ScanResult {
34
+ items: AbsorbItem[];
35
+ skippedSecretFiles: string[];
36
+ redactedItems: number;
37
+ redactedSecrets: number;
38
+ }
39
+ declare const SUPPORTED_STORES: readonly ["claude-code", "markdown"];
40
+ export type StoreKind = (typeof SUPPORTED_STORES)[number];
41
+ export declare function isSupportedStore(kind: string): kind is StoreKind;
42
+ export declare function supportedStores(): readonly string[];
43
+ /**
44
+ * Walk a directory and build absorb items for one store kind.
45
+ *
46
+ * Shared by both importers — they differ only in the store_kind tag and (for
47
+ * documentation) the expectation of what lives in the directory; the file
48
+ * selection (every *.md, including MEMORY.md and CLAUDE.md) is identical, which
49
+ * is exactly what a Claude Code memory directory contains.
50
+ */
51
+ export declare function scanDirectory(root: string, storeKind: string): ScanResult;
52
+ export {};
53
+ //# sourceMappingURL=importers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importers.d.ts","sourceRoot":"","sources":["../../src/absorb/importers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAeD,QAAA,MAAM,gBAAgB,sCAAuC,CAAC;AAC9D,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,SAAS,CAEhE;AAED,wBAAgB,eAAe,IAAI,SAAS,MAAM,EAAE,CAEnD;AA4CD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAwCzE"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * src/absorb/importers.ts
3
+ *
4
+ * Local importers for the absorb command (ADR-055 §4). An importer walks a
5
+ * directory, reads the files relevant to one store kind, and turns each into an
6
+ * AbsorbItem. It NEVER posts anything — the CLI batches the items to the API.
7
+ *
8
+ * Two importers ship in this slice:
9
+ * claude-code — a Claude Code memory directory: every *.md file, including
10
+ * the MEMORY.md index and any CLAUDE.md files.
11
+ * markdown — generic catch-all: walk a directory tree for every *.md file.
12
+ *
13
+ * Both reuse the same secret guards (secrets.ts): a credential-named file is
14
+ * skipped, and token-shaped strings are redacted from the content before it is
15
+ * handed back. Each importer reports skipped/redacted counts so the CLI can
16
+ * print an honest summary.
17
+ */
18
+ import { readdirSync, readFileSync, statSync } from "node:fs";
19
+ import { join, relative, sep } from "node:path";
20
+ import { redactSecrets, shouldSkipFile } from "./secrets.js";
21
+ /** Directories never descended into (noise + secret stores). */
22
+ const SKIP_DIRS = new Set([
23
+ "node_modules",
24
+ ".git",
25
+ ".venv",
26
+ "venv",
27
+ "__pycache__",
28
+ "dist",
29
+ "build",
30
+ ".next",
31
+ ".cache",
32
+ ]);
33
+ const SUPPORTED_STORES = ["claude-code", "markdown"];
34
+ export function isSupportedStore(kind) {
35
+ return SUPPORTED_STORES.includes(kind);
36
+ }
37
+ export function supportedStores() {
38
+ return SUPPORTED_STORES;
39
+ }
40
+ /** Recursively collect every *.md file under root (relative paths). */
41
+ function collectMarkdownFiles(root) {
42
+ const out = [];
43
+ const walk = (dir) => {
44
+ let entries;
45
+ try {
46
+ entries = readdirSync(dir);
47
+ }
48
+ catch {
49
+ return; // unreadable dir — skip quietly
50
+ }
51
+ for (const entry of entries) {
52
+ const abs = join(dir, entry);
53
+ let st;
54
+ try {
55
+ st = statSync(abs);
56
+ }
57
+ catch {
58
+ continue;
59
+ }
60
+ if (st.isDirectory()) {
61
+ if (!SKIP_DIRS.has(entry))
62
+ walk(abs);
63
+ continue;
64
+ }
65
+ if (st.isFile() && entry.toLowerCase().endsWith(".md")) {
66
+ out.push(relative(root, abs));
67
+ }
68
+ }
69
+ };
70
+ walk(root);
71
+ return out.sort();
72
+ }
73
+ /** First markdown heading in the content, else the basename without extension. */
74
+ function deriveTitle(content, relPath) {
75
+ for (const raw of content.split("\n")) {
76
+ const line = raw.trim();
77
+ const m = /^#{1,6}\s+(.+?)\s*#*$/.exec(line);
78
+ if (m && m[1])
79
+ return m[1].trim();
80
+ }
81
+ const base = relPath.split(/[\\/]/).pop() ?? relPath;
82
+ return base.replace(/\.md$/i, "");
83
+ }
84
+ /**
85
+ * Walk a directory and build absorb items for one store kind.
86
+ *
87
+ * Shared by both importers — they differ only in the store_kind tag and (for
88
+ * documentation) the expectation of what lives in the directory; the file
89
+ * selection (every *.md, including MEMORY.md and CLAUDE.md) is identical, which
90
+ * is exactly what a Claude Code memory directory contains.
91
+ */
92
+ export function scanDirectory(root, storeKind) {
93
+ const rel = collectMarkdownFiles(root);
94
+ const items = [];
95
+ const skippedSecretFiles = [];
96
+ let redactedItems = 0;
97
+ let redactedSecrets = 0;
98
+ for (const relPath of rel) {
99
+ const base = relPath.split(/[\\/]/).pop() ?? relPath;
100
+ if (shouldSkipFile(base)) {
101
+ skippedSecretFiles.push(relPath);
102
+ continue;
103
+ }
104
+ const abs = join(root, relPath);
105
+ let raw;
106
+ let st;
107
+ try {
108
+ raw = readFileSync(abs, "utf-8");
109
+ st = statSync(abs);
110
+ }
111
+ catch {
112
+ continue;
113
+ }
114
+ const { content, redactedCount } = redactSecrets(raw);
115
+ if (redactedCount > 0) {
116
+ redactedItems += 1;
117
+ redactedSecrets += redactedCount;
118
+ }
119
+ // Normalise source_id to forward slashes so it is stable across OSes.
120
+ const sourceId = relPath.split(sep).join("/");
121
+ items.push({
122
+ store_kind: storeKind,
123
+ source_id: sourceId,
124
+ title: deriveTitle(content, relPath),
125
+ content,
126
+ created_at: st.birthtime?.toISOString?.(),
127
+ updated_at: st.mtime?.toISOString?.(),
128
+ });
129
+ }
130
+ return { items, skippedSecretFiles, redactedItems, redactedSecrets };
131
+ }
132
+ //# sourceMappingURL=importers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importers.js","sourceRoot":"","sources":["../../src/absorb/importers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * src/absorb/secrets.ts
3
+ *
4
+ * Secret exclusion + redaction for the absorb importers (ADR-055 §4).
5
+ *
6
+ * Two layers, both non-negotiable:
7
+ * 1. File exclusion — never read a file whose name looks like it holds
8
+ * credentials (.env*, *credentials*, *secret*, *token*-named files).
9
+ * 2. Content redaction — even in a file we DO read, replace token-shaped
10
+ * strings (long base64/hex, sk-..., ghp_..., hbn_..., AWS keys, JWTs)
11
+ * with a [REDACTED] marker before the content ever leaves the machine.
12
+ *
13
+ * Nothing here talks to the network. The importer calls shouldSkipFile() per
14
+ * file and redactSecrets() on every body it is about to upload.
15
+ */
16
+ /**
17
+ * True when a file should be skipped entirely because its NAME indicates it
18
+ * holds credentials. Matches on the basename only (path is ignored), case-
19
+ * insensitively.
20
+ */
21
+ export declare function shouldSkipFile(filename: string): boolean;
22
+ /** Result of redacting a string: the cleaned text + how many secrets were hit. */
23
+ export interface RedactionResult {
24
+ content: string;
25
+ redactedCount: number;
26
+ }
27
+ /**
28
+ * Redact token-shaped strings from content. Returns the cleaned content and the
29
+ * number of distinct matches replaced. Idempotent on already-clean text
30
+ * (redactedCount === 0). The Bearer rule keeps the leading "Bearer " word so the
31
+ * surrounding prose still reads sensibly.
32
+ */
33
+ export declare function redactSecrets(content: string): RedactionResult;
34
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/absorb/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAqDH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAa9D"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * src/absorb/secrets.ts
3
+ *
4
+ * Secret exclusion + redaction for the absorb importers (ADR-055 §4).
5
+ *
6
+ * Two layers, both non-negotiable:
7
+ * 1. File exclusion — never read a file whose name looks like it holds
8
+ * credentials (.env*, *credentials*, *secret*, *token*-named files).
9
+ * 2. Content redaction — even in a file we DO read, replace token-shaped
10
+ * strings (long base64/hex, sk-..., ghp_..., hbn_..., AWS keys, JWTs)
11
+ * with a [REDACTED] marker before the content ever leaves the machine.
12
+ *
13
+ * Nothing here talks to the network. The importer calls shouldSkipFile() per
14
+ * file and redactSecrets() on every body it is about to upload.
15
+ */
16
+ /** A file basename matches one of these → skip it entirely. */
17
+ const SKIP_NAME_PATTERNS = [
18
+ /^\.env(\..*)?$/i, // .env, .env.local, .env.production, …
19
+ /credentials/i, // aws credentials, gcloud credentials, *credentials*.json
20
+ /secret/i, // secrets.yaml, my-secret.md, *secret*
21
+ /(^|[._-])tokens?([._-]|$)/i, // token.txt, api-tokens.json, auth_token.md
22
+ /\.pem$/i, // private keys
23
+ /\.key$/i,
24
+ /id_rsa/i, // ssh keys
25
+ /\.p12$/i,
26
+ /\.pfx$/i,
27
+ ];
28
+ /**
29
+ * Token-shaped substrings to redact from content. Ordered specific → generic so
30
+ * a prefixed key (sk-…, ghp_…) is matched by its own rule before the generic
31
+ * long-base64 catch-all. Each match is replaced with [REDACTED].
32
+ */
33
+ const REDACT_PATTERNS = [
34
+ // OpenAI / Anthropic style: sk-… , sk-ant-…
35
+ /\bsk-[A-Za-z0-9_-]{16,}\b/g,
36
+ // GitHub tokens: ghp_, gho_, ghu_, ghs_, ghr_, github_pat_
37
+ /\bgh[opusr]_[A-Za-z0-9]{16,}\b/g,
38
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
39
+ // Hebbian tokens.
40
+ /\bhbn_[A-Za-z0-9_-]{16,}\b/g,
41
+ // Slack tokens: xoxb-, xoxp-, xapp-…
42
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
43
+ // AWS access key id.
44
+ /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,
45
+ // Google API key.
46
+ /\bAIza[A-Za-z0-9_-]{30,}\b/g,
47
+ // JWT (three base64url segments).
48
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,
49
+ // Bearer headers — redact the value, keep the word.
50
+ /\b([Bb]earer\s+)[A-Za-z0-9._-]{20,}\b/g,
51
+ // URL-embedded credentials: scheme://user:password@host (postgres://, redis://,
52
+ // amqp://, mongodb://, …). Redact the password, keep scheme/user/host so the
53
+ // surrounding prose still reads sensibly.
54
+ /\b([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:@\/\s]*:)[^@\s]{3,}(?=@)/g,
55
+ // Supabase secret / personal-access keys.
56
+ /\bsb_secret_[A-Za-z0-9_-]{10,}\b/g,
57
+ /\bsbp_[A-Za-z0-9]{20,}\b/g,
58
+ // Generic long hex (>= 40 chars) — sha-like / hex secrets.
59
+ /\b[0-9a-fA-F]{40,}\b/g,
60
+ // Generic long base64-ish blob (>= 40 chars). Last so prefixed keys win first.
61
+ /\b[A-Za-z0-9+/]{40,}={0,2}\b/g,
62
+ ];
63
+ const REDACTION_MARKER = "[REDACTED]";
64
+ /**
65
+ * True when a file should be skipped entirely because its NAME indicates it
66
+ * holds credentials. Matches on the basename only (path is ignored), case-
67
+ * insensitively.
68
+ */
69
+ export function shouldSkipFile(filename) {
70
+ const base = filename.split(/[\\/]/).pop() ?? filename;
71
+ return SKIP_NAME_PATTERNS.some((re) => re.test(base));
72
+ }
73
+ /**
74
+ * Redact token-shaped strings from content. Returns the cleaned content and the
75
+ * number of distinct matches replaced. Idempotent on already-clean text
76
+ * (redactedCount === 0). The Bearer rule keeps the leading "Bearer " word so the
77
+ * surrounding prose still reads sensibly.
78
+ */
79
+ export function redactSecrets(content) {
80
+ let redactedCount = 0;
81
+ let out = content;
82
+ for (const re of REDACT_PATTERNS) {
83
+ out = out.replace(re, (match, ...groups) => {
84
+ redactedCount += 1;
85
+ // Rules with a capture group ("Bearer ", "scheme://user:") keep the prefix
86
+ // so the surrounding prose still reads sensibly; only the secret is replaced.
87
+ const prefix = typeof groups[0] === "string" ? groups[0] : "";
88
+ return `${prefix}${REDACTION_MARKER}`;
89
+ });
90
+ }
91
+ return { content: out, redactedCount };
92
+ }
93
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/absorb/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,+DAA+D;AAC/D,MAAM,kBAAkB,GAAa;IACnC,iBAAiB,EAAE,uCAAuC;IAC1D,cAAc,EAAE,0DAA0D;IAC1E,SAAS,EAAE,uCAAuC;IAClD,4BAA4B,EAAE,4CAA4C;IAC1E,SAAS,EAAE,eAAe;IAC1B,SAAS;IACT,SAAS,EAAE,WAAW;IACtB,SAAS;IACT,SAAS;CACV,CAAC;AAEF;;;;GAIG;AACH,MAAM,eAAe,GAAa;IAChC,4CAA4C;IAC5C,4BAA4B;IAC5B,2DAA2D;IAC3D,iCAAiC;IACjC,mCAAmC;IACnC,kBAAkB;IAClB,6BAA6B;IAC7B,qCAAqC;IACrC,mCAAmC;IACnC,qBAAqB;IACrB,gCAAgC;IAChC,kBAAkB;IAClB,6BAA6B;IAC7B,kCAAkC;IAClC,iEAAiE;IACjE,oDAAoD;IACpD,wCAAwC;IACxC,gFAAgF;IAChF,6EAA6E;IAC7E,0CAA0C;IAC1C,6DAA6D;IAC7D,0CAA0C;IAC1C,mCAAmC;IACnC,2BAA2B;IAC3B,2DAA2D;IAC3D,uBAAuB;IACvB,+EAA+E;IAC/E,+BAA+B;CAChC,CAAC;AAEF,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAEtC;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;IACvD,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACxD,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,GAAG,GAAG,OAAO,CAAC;IAClB,KAAK,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC;QACjC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,MAAM,EAAE,EAAE;YACzC,aAAa,IAAI,CAAC,CAAC;YACnB,2EAA2E;YAC3E,8EAA8E;YAC9E,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO,GAAG,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;AACzC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -9,10 +9,11 @@
9
9
  * Auth: set HEBBIAN_API_TOKEN (or HEBBIAN_TOKEN) env var, or write a config
10
10
  * file at ~/.config/hebbian/mcp-tenant.json with { "token": "hbn_..." }.
11
11
  *
12
- * 8 tools:
12
+ * 9 tools:
13
13
  * hebbian_read_node — fetch a node by UUID
14
14
  * hebbian_search — search the workspace for matching nodes
15
15
  * hebbian_ask — synthesis Q&A returned with source quotes
16
+ * hebbian_context — task-shaped retrieval: a salience-ranked context pack within a token budget
16
17
  * hebbian_capture — write a note into the workspace
17
18
  * hebbian_traverse — walk the graph from a starting node
18
19
  * hebbian_provenance — source trail for a node
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
package/dist/index.js CHANGED
@@ -9,10 +9,11 @@
9
9
  * Auth: set HEBBIAN_API_TOKEN (or HEBBIAN_TOKEN) env var, or write a config
10
10
  * file at ~/.config/hebbian/mcp-tenant.json with { "token": "hbn_..." }.
11
11
  *
12
- * 8 tools:
12
+ * 9 tools:
13
13
  * hebbian_read_node — fetch a node by UUID
14
14
  * hebbian_search — search the workspace for matching nodes
15
15
  * hebbian_ask — synthesis Q&A returned with source quotes
16
+ * hebbian_context — task-shaped retrieval: a salience-ranked context pack within a token budget
16
17
  * hebbian_capture — write a note into the workspace
17
18
  * hebbian_traverse — walk the graph from a starting node
18
19
  * hebbian_provenance — source trail for a node
@@ -28,7 +29,8 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
28
29
  import { loadConfig } from "./config.js";
29
30
  import { HebbianClient } from "./client.js";
30
31
  import { printBanner } from "./banner.js";
31
- import { HEBBIAN_READ_NODE, handleReadNode, HEBBIAN_SEARCH, handleSearch, HEBBIAN_ASK, handleAsk, HEBBIAN_CAPTURE, handleCapture, HEBBIAN_TRAVERSE, handleTraverse, HEBBIAN_PROVENANCE, handleProvenance, HEBBIAN_SALIENCE, handleSalience, HEBBIAN_RECENT_ACTIVITY, handleRecentActivity, } from "./tools/index.js";
32
+ import { runAbsorb } from "./absorb/cli.js";
33
+ import { HEBBIAN_READ_NODE, handleReadNode, HEBBIAN_SEARCH, handleSearch, HEBBIAN_ASK, handleAsk, HEBBIAN_CONTEXT, handleContext, HEBBIAN_CAPTURE, handleCapture, HEBBIAN_TRAVERSE, handleTraverse, HEBBIAN_PROVENANCE, handleProvenance, HEBBIAN_SALIENCE, handleSalience, HEBBIAN_RECENT_ACTIVITY, handleRecentActivity, } from "./tools/index.js";
32
34
  // ── Constants ──────────────────────────────────────────────────────────────────
33
35
  const SERVER_NAME = "@hebbianvault/mcp";
34
36
  const SERVER_VERSION = "0.2.0";
@@ -37,6 +39,7 @@ const TOOLS = [
37
39
  HEBBIAN_READ_NODE,
38
40
  HEBBIAN_SEARCH,
39
41
  HEBBIAN_ASK,
42
+ HEBBIAN_CONTEXT,
40
43
  HEBBIAN_CAPTURE,
41
44
  HEBBIAN_TRAVERSE,
42
45
  HEBBIAN_PROVENANCE,
@@ -73,6 +76,9 @@ async function main() {
73
76
  case "hebbian_ask":
74
77
  result = await handleAsk(client, args);
75
78
  break;
79
+ case "hebbian_context":
80
+ result = await handleContext(client, args);
81
+ break;
76
82
  case "hebbian_capture":
77
83
  result = await handleCapture(client, args);
78
84
  break;
@@ -113,8 +119,23 @@ async function main() {
113
119
  process.stderr.write(`[hebbian-mcp] ${SERVER_NAME}@${SERVER_VERSION} started. ` +
114
120
  `API: ${config.apiUrl}\n`);
115
121
  }
116
- main().catch((err) => {
117
- process.stderr.write(`[hebbian-mcp] Fatal startup error: ${err instanceof Error ? err.message : String(err)}\n`);
118
- process.exit(1);
119
- });
122
+ // ── Entry dispatch ───────────────────────────────────────────────────────────
123
+ // With NO subcommand, boot the MCP stdio server (unchanged default). The first
124
+ // argv is the subcommand: `hebbian-mcp absorb <store> <dir> --agent <id>` runs
125
+ // the one-shot context-absorption importer (ADR-055) instead of the server.
126
+ const subcommand = process.argv[2];
127
+ if (subcommand === "absorb") {
128
+ runAbsorb(process.argv.slice(3))
129
+ .then((code) => process.exit(code))
130
+ .catch((err) => {
131
+ process.stderr.write(`[hebbian-mcp] absorb error: ${err instanceof Error ? err.message : String(err)}\n`);
132
+ process.exit(1);
133
+ });
134
+ }
135
+ else {
136
+ main().catch((err) => {
137
+ process.stderr.write(`[hebbian-mcp] Fatal startup error: ${err instanceof Error ? err.message : String(err)}\n`);
138
+ process.exit(1);
139
+ });
140
+ }
120
141
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * src/tools/context.ts
3
+ *
4
+ * Tool: hebbian_context
5
+ * Task-shaped retrieval: describe a task and a token budget, get back the most
6
+ * relevant context from the workspace, ranked by salience and trimmed to fit.
7
+ * Maps to: POST /v1/context (request body: { task, budget_tokens, filters? })
8
+ *
9
+ * This is the agent-native read. Instead of browsing nodes or running raw
10
+ * search, ask for the context a task needs. The API ranks the workspace by
11
+ * salience and returns a pack of items (each with the node, an excerpt, a
12
+ * score, and a short reason it was included) within your token budget. What
13
+ * the pack can draw on is enforced server-side by your token's scope.
14
+ */
15
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
16
+ import type { HebbianClient } from "../client.js";
17
+ export declare const HEBBIAN_CONTEXT: Tool;
18
+ interface ContextArgs {
19
+ task: string;
20
+ budget_tokens?: number;
21
+ scope?: "synthesis" | "company" | "employee" | "bridge";
22
+ }
23
+ export declare function handleContext(client: HebbianClient, args: ContextArgs): Promise<string>;
24
+ export {};
25
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAOlD,eAAO,MAAM,eAAe,EAAE,IA0C7B,CAAC;AAEF,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;CACzD;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,MAAM,CAAC,CA6BjB"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * src/tools/context.ts
3
+ *
4
+ * Tool: hebbian_context
5
+ * Task-shaped retrieval: describe a task and a token budget, get back the most
6
+ * relevant context from the workspace, ranked by salience and trimmed to fit.
7
+ * Maps to: POST /v1/context (request body: { task, budget_tokens, filters? })
8
+ *
9
+ * This is the agent-native read. Instead of browsing nodes or running raw
10
+ * search, ask for the context a task needs. The API ranks the workspace by
11
+ * salience and returns a pack of items (each with the node, an excerpt, a
12
+ * score, and a short reason it was included) within your token budget. What
13
+ * the pack can draw on is enforced server-side by your token's scope.
14
+ */
15
+ import { HebbianApiError } from "../client.js";
16
+ const DEFAULT_BUDGET_TOKENS = 2000;
17
+ const MIN_BUDGET_TOKENS = 50;
18
+ const MAX_BUDGET_TOKENS = 32000;
19
+ export const HEBBIAN_CONTEXT = {
20
+ name: "hebbian_context",
21
+ description: "Get the most relevant context for a task from your Hebbian workspace, " +
22
+ "ranked by salience and trimmed to a token budget. Give a plain-language " +
23
+ "task description and a budget; get back a context pack. Each item carries " +
24
+ "its source node, an excerpt, a salience score, and a short reason it was " +
25
+ "included. Use this instead of search when you want context shaped for a " +
26
+ "task rather than a raw list of nodes. Results only ever include what your " +
27
+ "token is allowed to see, enforced server-side.",
28
+ inputSchema: {
29
+ type: "object",
30
+ properties: {
31
+ task: {
32
+ type: "string",
33
+ description: "Plain-language description of the task the context is for. " +
34
+ "The more specific the task, the better the pack is targeted.",
35
+ },
36
+ budget_tokens: {
37
+ type: "number",
38
+ description: `Token budget for the returned pack. Default: ${DEFAULT_BUDGET_TOKENS}. ` +
39
+ `Min: ${MIN_BUDGET_TOKENS}. Max: ${MAX_BUDGET_TOKENS}. The pack is ` +
40
+ "trimmed in salience order to fit; the response reports how much was " +
41
+ "used and whether anything was left out.",
42
+ minimum: MIN_BUDGET_TOKENS,
43
+ maximum: MAX_BUDGET_TOKENS,
44
+ },
45
+ scope: {
46
+ type: "string",
47
+ enum: ["synthesis", "company", "employee", "bridge"],
48
+ description: "Which part of the workspace to draw from. 'synthesis' (default) " +
49
+ "blends company and your own notes; 'company' is the company brain " +
50
+ "only; 'employee' is your own notes only; 'bridge' is the " +
51
+ "cross-pollinated view. Your token's scope still bounds what is reachable.",
52
+ },
53
+ },
54
+ required: ["task"],
55
+ additionalProperties: false,
56
+ },
57
+ };
58
+ export async function handleContext(client, args) {
59
+ const { task, budget_tokens, scope } = args;
60
+ if (!task || typeof task !== "string" || task.trim().length === 0) {
61
+ throw new Error("task is required and must be a non-empty string");
62
+ }
63
+ const budget = Math.min(Math.max(MIN_BUDGET_TOKENS, budget_tokens ?? DEFAULT_BUDGET_TOKENS), MAX_BUDGET_TOKENS);
64
+ const body = {
65
+ task: task.trim(),
66
+ budget_tokens: budget,
67
+ };
68
+ if (scope) {
69
+ body.filters = { scope };
70
+ }
71
+ try {
72
+ const result = await client.post("/v1/context", body);
73
+ return JSON.stringify(result, null, 2);
74
+ }
75
+ catch (err) {
76
+ if (err instanceof HebbianApiError) {
77
+ throw new Error(err.toToolError());
78
+ }
79
+ throw err;
80
+ }
81
+ }
82
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,qBAAqB,GAAG,IAAI,CAAC;AACnC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC,MAAM,CAAC,MAAM,eAAe,GAAS;IACnC,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,wEAAwE;QACxE,0EAA0E;QAC1E,4EAA4E;QAC5E,2EAA2E;QAC3E,0EAA0E;QAC1E,4EAA4E;QAC5E,gDAAgD;IAClD,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,6DAA6D;oBAC7D,8DAA8D;aACjE;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,gDAAgD,qBAAqB,IAAI;oBACzE,QAAQ,iBAAiB,UAAU,iBAAiB,gBAAgB;oBACpE,sEAAsE;oBACtE,yCAAyC;gBAC3C,OAAO,EAAE,iBAAiB;gBAC1B,OAAO,EAAE,iBAAiB;aAC3B;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC;gBACpD,WAAW,EACT,kEAAkE;oBAClE,oEAAoE;oBACpE,2DAA2D;oBAC3D,2EAA2E;aAC9E;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;QAClB,oBAAoB,EAAE,KAAK;KAC5B;CACF,CAAC;AAQF,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAqB,EACrB,IAAiB;IAEjB,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAE5C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,aAAa,IAAI,qBAAqB,CAAC,EACnE,iBAAiB,CAClB,CAAC;IAEF,MAAM,IAAI,GAA4B;QACpC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QACjB,aAAa,EAAE,MAAM;KACtB,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * src/tools/index.ts
3
3
  *
4
- * Re-exports all 8 tool definitions for @hebbianvault/mcp.
4
+ * Re-exports all 9 tool definitions for @hebbianvault/mcp.
5
5
  */
6
6
  export { HEBBIAN_READ_NODE, handleReadNode } from "./read_node.js";
7
7
  export { HEBBIAN_SEARCH, handleSearch } from "./search.js";
8
8
  export { HEBBIAN_ASK, handleAsk } from "./ask.js";
9
+ export { HEBBIAN_CONTEXT, handleContext } from "./context.js";
9
10
  export { HEBBIAN_CAPTURE, handleCapture } from "./capture.js";
10
11
  export { HEBBIAN_TRAVERSE, handleTraverse } from "./traverse.js";
11
12
  export { HEBBIAN_PROVENANCE, handleProvenance } from "./provenance.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * src/tools/index.ts
3
3
  *
4
- * Re-exports all 8 tool definitions for @hebbianvault/mcp.
4
+ * Re-exports all 9 tool definitions for @hebbianvault/mcp.
5
5
  */
6
6
  export { HEBBIAN_READ_NODE, handleReadNode } from "./read_node.js";
7
7
  export { HEBBIAN_SEARCH, handleSearch } from "./search.js";
8
8
  export { HEBBIAN_ASK, handleAsk } from "./ask.js";
9
+ export { HEBBIAN_CONTEXT, handleContext } from "./context.js";
9
10
  export { HEBBIAN_CAPTURE, handleCapture } from "./capture.js";
10
11
  export { HEBBIAN_TRAVERSE, handleTraverse } from "./traverse.js";
11
12
  export { HEBBIAN_PROVENANCE, handleProvenance } from "./provenance.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
package/package.json CHANGED
@@ -1,12 +1,20 @@
1
1
  {
2
2
  "name": "@hebbianvault/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Customer-installable MCP server for the Hebbian tenant brain. One package, scope-by-token (Employee or Company). Install in Claude Code, Claude Desktop, Cursor, Cowork, or any MCP-compatible agent.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "hebbian-mcp": "dist/index.js"
9
9
  },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/hebbianvault/hebbian-mcp.git"
13
+ },
14
+ "homepage": "https://github.com/hebbianvault/hebbian-mcp#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/hebbianvault/hebbian-mcp/issues"
17
+ },
10
18
  "exports": {
11
19
  ".": {
12
20
  "types": "./dist/index.d.ts",