@silverbackbase/root 0.2.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.
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # @silverbackbase/root
2
+
3
+ Business knowledge primitive for AI agents. Gives agents a durable, structured understanding of a business — offers, personas, objections, differentiators, competitors, geo zones, pricing, FAQs.
4
+
5
+ ## How it works
6
+
7
+ Give Root a client URL. In 5 minutes, all your agents have full business context — no manual briefing.
8
+
9
+ ```
10
+ URL → Firecrawl (site scraping) → LLM extraction → DataForSEO (local competitors) → structured knowledge
11
+ ```
12
+
13
+ ## MCP Tools
14
+
15
+ | Tool | Type | Description |
16
+ |------|------|-------------|
17
+ | `root_get_context` | Read | Full business knowledge — call before any content/SEO/prospection task |
18
+ | `root_get_category` | Read | Specific category (personas, objections, etc.) |
19
+ | `root_search` | Read | Text search across all knowledge |
20
+ | `root_onboard_from_url` | Write | Onboard a client from their website URL |
21
+ | `root_create_item` | Write | Add a knowledge item manually |
22
+ | `root_update_item` | Write | Update an existing item |
23
+ | `root_delete_item` | Write | Delete an item |
24
+
25
+ ---
26
+
27
+ ## Self-hosted
28
+
29
+ ### Requirements
30
+
31
+ - Node.js ≥ 22
32
+ - Firecrawl API key (for onboarding)
33
+ - Anthropic API key (for LLM extraction)
34
+ - DataForSEO credentials (for competitor analysis, optional)
35
+
36
+ No database setup required — Root uses SQLite by default.
37
+
38
+ ### Install
39
+
40
+ Add to your Claude Code MCP config (`.mcp.json` in your project, or `~/.claude/claude_desktop_config.json`):
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "root": {
46
+ "command": "npx",
47
+ "args": ["-y", "--package=@silverbackbase/root", "root-mcp"],
48
+ "env": {
49
+ "ROOT_ACCOUNT_ID": "my-agency",
50
+ "ANTHROPIC_API_KEY": "sk-ant-...",
51
+ "FIRECRAWL_API_KEY": "fc-...",
52
+ "DATAFORSEO_LOGIN": "...",
53
+ "DATAFORSEO_PASSWORD": "..."
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ Root creates a `root.db` SQLite file automatically on first run. No migration, no setup.
61
+
62
+ ---
63
+
64
+ ## Cloud deployment
65
+
66
+ For multi-user or hosted setups, set `DATABASE_URL` to switch to PostgreSQL automatically:
67
+
68
+ ```env
69
+ DATABASE_URL=postgresql://user:password@host:5432/root
70
+ ROOT_ACCOUNT_ID=my-agency
71
+ ANTHROPIC_API_KEY=sk-ant-...
72
+ FIRECRAWL_API_KEY=fc-...
73
+ ```
74
+
75
+ Root detects `DATABASE_URL` at startup and creates the table if it doesn't exist.
76
+
77
+ ---
78
+
79
+ ## Environment variables
80
+
81
+ | Variable | Required | Description |
82
+ |----------|----------|-------------|
83
+ | `ROOT_ACCOUNT_ID` | No | Account identifier (default: `"default"`) |
84
+ | `DB_PATH` | No | SQLite file path (default: `./root.db`). Ignored when `DATABASE_URL` is set. |
85
+ | `DATABASE_URL` | No | PostgreSQL connection string. If set, Root uses Postgres instead of SQLite. |
86
+ | `ANTHROPIC_API_KEY` | For onboarding | LLM extraction from website content |
87
+ | `FIRECRAWL_API_KEY` | For onboarding | Website scraping |
88
+ | `DATAFORSEO_LOGIN` | For onboarding | Competitor analysis via SERP |
89
+ | `DATAFORSEO_PASSWORD` | For onboarding | Competitor analysis via SERP |
90
+
91
+ ---
92
+
93
+ ## Knowledge categories
94
+
95
+ | Category | Contains |
96
+ |----------|---------|
97
+ | `offer` | Services, products, prices |
98
+ | `persona` | Target customer profiles |
99
+ | `objection` | Common sales objections and responses |
100
+ | `differentiator` | Competitive advantages |
101
+ | `competitor` | Competitor names, positioning, weaknesses |
102
+ | `geo` | Geographic zones served |
103
+ | `pricing` | Pricing structure and packages |
104
+ | `faq` | Frequently asked questions |
105
+ | `profile` | General business profile (name, sector, tone) |
106
+
107
+ ---
108
+
109
+ ## Requirements
110
+
111
+ - Node.js 22+
112
+ - Claude Code or any MCP-compatible AI agent
113
+
114
+ ## License
115
+
116
+ MIT — [SilverBackBase](https://www.silverbackbase.com)
@@ -0,0 +1,19 @@
1
+ import type { KnowledgeCategory } from "./types.js";
2
+ export interface KnowledgeRow {
3
+ id: string;
4
+ accountId: string;
5
+ category: KnowledgeCategory;
6
+ data: Record<string, unknown>;
7
+ createdAt: Date;
8
+ updatedAt: Date;
9
+ }
10
+ export interface KnowledgeDB {
11
+ getAll(accountId: string): Promise<KnowledgeRow[]>;
12
+ getByCategory(accountId: string, category: string): Promise<KnowledgeRow[]>;
13
+ insert(id: string, accountId: string, category: string, data: Record<string, unknown>): Promise<void>;
14
+ update(id: string, accountId: string, data: Record<string, unknown>): Promise<KnowledgeRow | undefined>;
15
+ remove(id: string, accountId: string): Promise<void>;
16
+ search(accountId: string, query: string): Promise<KnowledgeRow[]>;
17
+ }
18
+ export declare const db: KnowledgeDB;
19
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/core/db.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACnD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC5E,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IACxG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CACnE;AAqJD,eAAO,MAAM,EAAE,EAAE,WAES,CAAC"}
@@ -0,0 +1,142 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import postgres from "postgres";
3
+ const CREATE_TABLE_SQL = `
4
+ CREATE TABLE IF NOT EXISTS knowledge_items (
5
+ id TEXT PRIMARY KEY,
6
+ account_id TEXT NOT NULL,
7
+ category TEXT NOT NULL,
8
+ data TEXT NOT NULL,
9
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
10
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
11
+ );
12
+ CREATE INDEX IF NOT EXISTS idx_ki_account ON knowledge_items(account_id);
13
+ CREATE INDEX IF NOT EXISTS idx_ki_category ON knowledge_items(account_id, category);
14
+ `;
15
+ const CREATE_TABLE_PG = `
16
+ CREATE TABLE IF NOT EXISTS knowledge_items (
17
+ id TEXT PRIMARY KEY,
18
+ account_id TEXT NOT NULL,
19
+ category TEXT NOT NULL,
20
+ data JSONB NOT NULL,
21
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
22
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
23
+ );
24
+ CREATE INDEX IF NOT EXISTS idx_ki_account ON knowledge_items(account_id);
25
+ CREATE INDEX IF NOT EXISTS idx_ki_category ON knowledge_items(account_id, category);
26
+ `;
27
+ function parseSqliteRow(row) {
28
+ return {
29
+ id: row.id,
30
+ accountId: row.account_id,
31
+ category: row.category,
32
+ data: JSON.parse(row.data),
33
+ createdAt: new Date(row.created_at),
34
+ updatedAt: new Date(row.updated_at),
35
+ };
36
+ }
37
+ function createSQLiteDB(dbPath) {
38
+ const sqlite = new DatabaseSync(dbPath);
39
+ sqlite.exec("PRAGMA journal_mode = WAL;");
40
+ sqlite.exec("PRAGMA foreign_keys = ON;");
41
+ sqlite.exec(CREATE_TABLE_SQL);
42
+ return {
43
+ async getAll(accountId) {
44
+ const stmt = sqlite.prepare("SELECT * FROM knowledge_items WHERE account_id = ?");
45
+ return stmt.all(accountId).map(parseSqliteRow);
46
+ },
47
+ async getByCategory(accountId, category) {
48
+ const stmt = sqlite.prepare("SELECT * FROM knowledge_items WHERE account_id = ? AND category = ?");
49
+ return stmt.all(accountId, category).map(parseSqliteRow);
50
+ },
51
+ async insert(id, accountId, category, data) {
52
+ const now = new Date().toISOString();
53
+ const stmt = sqlite.prepare("INSERT INTO knowledge_items (id, account_id, category, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)");
54
+ stmt.run(id, accountId, category, JSON.stringify(data), now, now);
55
+ },
56
+ async update(id, accountId, data) {
57
+ const getStmt = sqlite.prepare("SELECT * FROM knowledge_items WHERE id = ? AND account_id = ?");
58
+ const existing = getStmt.get(id, accountId);
59
+ if (!existing)
60
+ return undefined;
61
+ const merged = { ...JSON.parse(existing.data), ...data };
62
+ const now = new Date().toISOString();
63
+ const stmt = sqlite.prepare("UPDATE knowledge_items SET data = ?, updated_at = ? WHERE id = ? AND account_id = ?");
64
+ stmt.run(JSON.stringify(merged), now, id, accountId);
65
+ return parseSqliteRow({ ...existing, data: JSON.stringify(merged), updated_at: now });
66
+ },
67
+ async remove(id, accountId) {
68
+ const stmt = sqlite.prepare("DELETE FROM knowledge_items WHERE id = ? AND account_id = ?");
69
+ stmt.run(id, accountId);
70
+ },
71
+ async search(accountId, query) {
72
+ const stmt = sqlite.prepare("SELECT * FROM knowledge_items WHERE account_id = ? AND data LIKE ?");
73
+ return stmt.all(accountId, `%${query}%`).map(parseSqliteRow);
74
+ },
75
+ };
76
+ }
77
+ function createPostgresDB(url) {
78
+ const sql = postgres(url);
79
+ let initialized = false;
80
+ async function ensureInit() {
81
+ if (initialized)
82
+ return;
83
+ await sql.unsafe(CREATE_TABLE_PG);
84
+ initialized = true;
85
+ }
86
+ function parseRow(row) {
87
+ return {
88
+ id: row.id,
89
+ accountId: row.account_id,
90
+ category: row.category,
91
+ data: (typeof row.data === "string" ? JSON.parse(row.data) : row.data),
92
+ createdAt: new Date(row.created_at),
93
+ updatedAt: new Date(row.updated_at),
94
+ };
95
+ }
96
+ return {
97
+ async getAll(accountId) {
98
+ await ensureInit();
99
+ const rows = await sql `SELECT * FROM knowledge_items WHERE account_id = ${accountId}`;
100
+ return rows.map((r) => parseRow(r));
101
+ },
102
+ async getByCategory(accountId, category) {
103
+ await ensureInit();
104
+ const rows = await sql `SELECT * FROM knowledge_items WHERE account_id = ${accountId} AND category = ${category}`;
105
+ return rows.map((r) => parseRow(r));
106
+ },
107
+ async insert(id, accountId, category, data) {
108
+ await ensureInit();
109
+ const now = new Date();
110
+ await sql `
111
+ INSERT INTO knowledge_items (id, account_id, category, data, created_at, updated_at)
112
+ VALUES (${id}, ${accountId}, ${category}, ${JSON.stringify(data)}, ${now}, ${now})
113
+ `;
114
+ },
115
+ async update(id, accountId, data) {
116
+ await ensureInit();
117
+ const [existing] = await sql `SELECT * FROM knowledge_items WHERE id = ${id} AND account_id = ${accountId}`;
118
+ if (!existing)
119
+ return undefined;
120
+ const existingRow = parseRow(existing);
121
+ const merged = { ...existingRow.data, ...data };
122
+ const now = new Date();
123
+ await sql `UPDATE knowledge_items SET data = ${JSON.stringify(merged)}, updated_at = ${now} WHERE id = ${id} AND account_id = ${accountId}`;
124
+ return { ...existingRow, data: merged, updatedAt: now };
125
+ },
126
+ async remove(id, accountId) {
127
+ await ensureInit();
128
+ await sql `DELETE FROM knowledge_items WHERE id = ${id} AND account_id = ${accountId}`;
129
+ },
130
+ async search(accountId, query) {
131
+ await ensureInit();
132
+ const rows = await sql `SELECT * FROM knowledge_items WHERE account_id = ${accountId} AND data::text ILIKE ${"%" + query + "%"}`;
133
+ return rows.map((r) => parseRow(r));
134
+ },
135
+ };
136
+ }
137
+ const databaseUrl = process.env.DATABASE_URL;
138
+ const dbPath = process.env.DB_PATH ?? "./root.db";
139
+ export const db = databaseUrl
140
+ ? createPostgresDB(databaseUrl)
141
+ : createSQLiteDB(dbPath);
142
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/core/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { getContext, getCategory, createItem, updateItem, deleteItem, searchKnowledge } from "./knowledge.js";
2
+ export { onboardFromUrl } from "./onboard.js";
3
+ export type { KnowledgeCategory, KnowledgeItem, BusinessContext, OnboardResult } from "./types.js";
4
+ export { CATEGORIES } from "./types.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { getContext, getCategory, createItem, updateItem, deleteItem, searchKnowledge } from "./knowledge.js";
2
+ export { onboardFromUrl } from "./onboard.js";
3
+ export { CATEGORIES } from "./types.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { KnowledgeCategory, BusinessContext } from "./types.js";
2
+ export declare function getContext(accountId: string): Promise<BusinessContext>;
3
+ export declare function getCategory(accountId: string, category: KnowledgeCategory): Promise<{
4
+ id: string;
5
+ }[]>;
6
+ export declare function createItem(accountId: string, category: KnowledgeCategory, data: Record<string, unknown>): Promise<{
7
+ id: string;
8
+ category: "offer" | "persona" | "objection" | "differentiator" | "competitor" | "geo" | "pricing" | "faq" | "profile";
9
+ }>;
10
+ export declare function updateItem(accountId: string, id: string, data: Record<string, unknown>): Promise<{
11
+ id: string;
12
+ category: "offer" | "persona" | "objection" | "differentiator" | "competitor" | "geo" | "pricing" | "faq" | "profile";
13
+ }>;
14
+ export declare function deleteItem(accountId: string, id: string): Promise<{
15
+ success: boolean;
16
+ id: string;
17
+ }>;
18
+ export declare function searchKnowledge(accountId: string, query: string): Promise<{
19
+ id: string;
20
+ category: "offer" | "persona" | "objection" | "differentiator" | "competitor" | "geo" | "pricing" | "faq" | "profile";
21
+ }[]>;
22
+ //# sourceMappingURL=knowledge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge.d.ts","sourceRoot":"","sources":["../../src/core/knowledge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAsC5E;AAED,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB;;KAG/E;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;GAK9B;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;GAK9B;AAED,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;;GAG7D;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;KAGrE"}
@@ -0,0 +1,84 @@
1
+ import { nanoid } from "nanoid";
2
+ import { db } from "./db.js";
3
+ export async function getContext(accountId) {
4
+ const items = await db.getAll(accountId);
5
+ const result = {
6
+ profile: null,
7
+ offers: [],
8
+ personas: [],
9
+ objections: [],
10
+ differentiators: [],
11
+ competitors: [],
12
+ geo: [],
13
+ pricing: [],
14
+ faqs: [],
15
+ gaps: [],
16
+ };
17
+ for (const item of items) {
18
+ const entry = { id: item.id, ...item.data };
19
+ switch (item.category) {
20
+ case "profile":
21
+ result.profile = entry;
22
+ break;
23
+ case "offer":
24
+ result.offers.push(entry);
25
+ break;
26
+ case "persona":
27
+ result.personas.push(entry);
28
+ break;
29
+ case "objection":
30
+ result.objections.push(entry);
31
+ break;
32
+ case "differentiator":
33
+ result.differentiators.push(entry);
34
+ break;
35
+ case "competitor":
36
+ result.competitors.push(entry);
37
+ break;
38
+ case "geo":
39
+ result.geo.push(entry);
40
+ break;
41
+ case "pricing":
42
+ result.pricing.push(entry);
43
+ break;
44
+ case "faq":
45
+ result.faqs.push(entry);
46
+ break;
47
+ }
48
+ }
49
+ if (!result.profile)
50
+ result.gaps.push("profile — business name, sector, and positioning not set");
51
+ if (result.offers.length === 0)
52
+ result.gaps.push("offers — no offers defined");
53
+ if (result.personas.length === 0)
54
+ result.gaps.push("personas — no personas defined");
55
+ if (result.differentiators.length === 0)
56
+ result.gaps.push("differentiators — no differentiators defined");
57
+ if (result.competitors.length === 0)
58
+ result.gaps.push("competitors — no competitors found");
59
+ return result;
60
+ }
61
+ export async function getCategory(accountId, category) {
62
+ const items = await db.getByCategory(accountId, category);
63
+ return items.map((item) => ({ id: item.id, ...item.data }));
64
+ }
65
+ export async function createItem(accountId, category, data) {
66
+ const id = nanoid();
67
+ await db.insert(id, accountId, category, data);
68
+ return { id, category, ...data };
69
+ }
70
+ export async function updateItem(accountId, id, data) {
71
+ const item = await db.update(id, accountId, data);
72
+ if (!item)
73
+ throw new Error(`Item ${id} not found`);
74
+ return { id: item.id, category: item.category, ...item.data };
75
+ }
76
+ export async function deleteItem(accountId, id) {
77
+ await db.remove(id, accountId);
78
+ return { success: true, id };
79
+ }
80
+ export async function searchKnowledge(accountId, query) {
81
+ const items = await db.search(accountId, query);
82
+ return items.map((item) => ({ id: item.id, category: item.category, ...item.data }));
83
+ }
84
+ //# sourceMappingURL=knowledge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge.js","sourceRoot":"","sources":["../../src/core/knowledge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { OnboardResult } from "./types.js";
2
+ export declare function onboardFromUrl(accountId: string, url: string): Promise<OnboardResult>;
3
+ //# sourceMappingURL=onboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/core/onboard.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAqB,aAAa,EAAsB,MAAM,YAAY,CAAC;AAwIvF,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,aAAa,CAAC,CAwCxB"}
@@ -0,0 +1,146 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ import FirecrawlApp from "@mendable/firecrawl-js";
3
+ import { createItem, getContext } from "./knowledge.js";
4
+ const FOLLOWUP_MAP = {
5
+ profile: "Quel est le nom de l'entreprise, son secteur d'activité et son positionnement principal ?",
6
+ personas: "Qui sont tes clients idéaux ? Décris 2-3 profils types avec leurs problèmes et objectifs.",
7
+ objections: "Quelles sont les 3 objections que tu entends le plus souvent lors des ventes ?",
8
+ differentiators: "Qu'est-ce qui te différencie vraiment de tes concurrents ? Ce que tu fais qu'eux ne font pas.",
9
+ offers: "Quelles sont tes offres principales et leurs tarifs ?",
10
+ geo: "Dans quelles zones géographiques travailles-tu principalement ?",
11
+ };
12
+ async function scrapeWebsite(url) {
13
+ const apiKey = process.env.FIRECRAWL_API_KEY;
14
+ if (!apiKey)
15
+ throw new Error("FIRECRAWL_API_KEY is required for onboarding");
16
+ const firecrawl = new FirecrawlApp({ apiKey });
17
+ const result = await firecrawl.crawlUrl(url, {
18
+ limit: 20,
19
+ scrapeOptions: { formats: ["markdown"] },
20
+ });
21
+ if (!result.success)
22
+ throw new Error("Firecrawl failed to crawl the website");
23
+ return result.data
24
+ .map((page) => `# ${page.metadata?.title ?? page.url ?? ""}\n\n${page.markdown ?? ""}`)
25
+ .join("\n\n---\n\n")
26
+ .slice(0, 60000);
27
+ }
28
+ async function extractWithLLM(content) {
29
+ const apiKey = process.env.ANTHROPIC_API_KEY;
30
+ if (!apiKey)
31
+ throw new Error("ANTHROPIC_API_KEY is required for onboarding");
32
+ const anthropic = new Anthropic({ apiKey });
33
+ const response = await anthropic.messages.create({
34
+ model: "claude-sonnet-4-6",
35
+ max_tokens: 4096,
36
+ messages: [
37
+ {
38
+ role: "user",
39
+ content: `Analyse ce contenu de site web et extrais la connaissance métier structurée. Retourne UNIQUEMENT un JSON valide avec ces clés exactes :
40
+
41
+ {
42
+ "profile": { "name": "...", "sector": "...", "positioning": "...", "tagline": "...", "description": "..." },
43
+ "offers": [{ "name": "...", "description": "...", "price": "...", "target_persona": "...", "key_benefits": [] }],
44
+ "personas": [{ "name": "...", "description": "...", "pain_points": [], "goals": [], "triggers": [] }],
45
+ "objections": [{ "objection": "...", "response": "..." }],
46
+ "differentiators": [{ "claim": "...", "proof": "..." }],
47
+ "geo": [{ "zone": "...", "cities": [], "characteristics": "..." }],
48
+ "pricing": [{ "package_name": "...", "price": "...", "included": [], "excluded": [] }],
49
+ "faqs": [{ "question": "...", "answer": "..." }],
50
+ "gaps": ["liste des catégories avec données insuffisantes ou absentes"]
51
+ }
52
+
53
+ Si une catégorie n'a pas de données, retourne un tableau vide (ou null pour profile).
54
+
55
+ Contenu du site :
56
+ ${content}`,
57
+ },
58
+ ],
59
+ });
60
+ const text = response.content[0].type === "text" ? response.content[0].text : "";
61
+ const match = text.match(/\{[\s\S]*\}/);
62
+ if (!match)
63
+ throw new Error("LLM did not return valid JSON");
64
+ return JSON.parse(match[0]);
65
+ }
66
+ async function findCompetitors(geoZones, sector) {
67
+ const login = process.env.DATAFORSEO_LOGIN;
68
+ const password = process.env.DATAFORSEO_PASSWORD;
69
+ if (!login || !password || geoZones.length === 0 || !sector)
70
+ return [];
71
+ const zone = geoZones[0];
72
+ const keyword = `${sector} ${zone}`.trim();
73
+ const credentials = Buffer.from(`${login}:${password}`).toString("base64");
74
+ try {
75
+ const response = await fetch("https://api.dataforseo.com/v3/serp/google/organic/live/advanced", {
76
+ method: "POST",
77
+ headers: {
78
+ Authorization: `Basic ${credentials}`,
79
+ "Content-Type": "application/json",
80
+ },
81
+ body: JSON.stringify([
82
+ {
83
+ keyword,
84
+ location_name: "France",
85
+ language_code: "fr",
86
+ device: "desktop",
87
+ os: "windows",
88
+ depth: 10,
89
+ },
90
+ ]),
91
+ });
92
+ if (!response.ok)
93
+ return [];
94
+ const data = (await response.json());
95
+ return (data.tasks?.[0]?.result?.[0]?.items ?? [])
96
+ .filter((item) => item.type === "organic" && item.domain)
97
+ .slice(0, 5)
98
+ .map((item) => ({
99
+ name: item.title ?? item.domain,
100
+ domain: item.domain,
101
+ positioning: item.description ?? "",
102
+ source: "dataforseo_serp",
103
+ zone,
104
+ keyword,
105
+ }));
106
+ }
107
+ catch {
108
+ return [];
109
+ }
110
+ }
111
+ export async function onboardFromUrl(accountId, url) {
112
+ const content = await scrapeWebsite(url);
113
+ const extracted = await extractWithLLM(content);
114
+ const savedCounts = {};
115
+ const save = async (category, items) => {
116
+ for (const item of items)
117
+ await createItem(accountId, category, item);
118
+ if (items.length > 0)
119
+ savedCounts[category] = items.length;
120
+ };
121
+ if (extracted.profile) {
122
+ await createItem(accountId, "profile", extracted.profile);
123
+ savedCounts.profile = 1;
124
+ }
125
+ await save("offer", extracted.offers);
126
+ await save("persona", extracted.personas);
127
+ await save("objection", extracted.objections);
128
+ await save("differentiator", extracted.differentiators);
129
+ await save("geo", extracted.geo);
130
+ await save("pricing", extracted.pricing);
131
+ await save("faq", extracted.faqs);
132
+ const geoZones = extracted.geo.map((g) => String(g.zone ?? "")).filter(Boolean);
133
+ const sector = String(extracted.profile?.sector ?? "");
134
+ const competitors = await findCompetitors(geoZones, sector);
135
+ await save("competitor", competitors);
136
+ const gaps = extracted.gaps ?? [];
137
+ const followupQuestions = gaps
138
+ .map((gap) => {
139
+ const key = Object.keys(FOLLOWUP_MAP).find((k) => gap.toLowerCase().includes(k));
140
+ return key ? FOLLOWUP_MAP[key] : `Information manquante : ${gap}`;
141
+ })
142
+ .filter(Boolean);
143
+ const context = await getContext(accountId);
144
+ return { context, gaps, followup_questions: followupQuestions, saved_counts: savedCounts };
145
+ }
146
+ //# sourceMappingURL=onboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../src/core/onboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ export declare const knowledgeItems: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "knowledge_items";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "id";
7
+ tableName: "knowledge_items";
8
+ dataType: "string";
9
+ columnType: "PgText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ accountId: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "account_id";
24
+ tableName: "knowledge_items";
25
+ dataType: "string";
26
+ columnType: "PgText";
27
+ data: string;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: false;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: [string, ...string[]];
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ category: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "category";
41
+ tableName: "knowledge_items";
42
+ dataType: "string";
43
+ columnType: "PgText";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
47
+ hasDefault: false;
48
+ isPrimaryKey: false;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: [string, ...string[]];
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ data: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "data";
58
+ tableName: "knowledge_items";
59
+ dataType: "json";
60
+ columnType: "PgJsonb";
61
+ data: Record<string, unknown>;
62
+ driverParam: unknown;
63
+ notNull: true;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: undefined;
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {
73
+ $type: Record<string, unknown>;
74
+ }>;
75
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
76
+ name: "created_at";
77
+ tableName: "knowledge_items";
78
+ dataType: "date";
79
+ columnType: "PgTimestamp";
80
+ data: Date;
81
+ driverParam: string;
82
+ notNull: true;
83
+ hasDefault: true;
84
+ isPrimaryKey: false;
85
+ isAutoincrement: false;
86
+ hasRuntimeDefault: false;
87
+ enumValues: undefined;
88
+ baseColumn: never;
89
+ identity: undefined;
90
+ generated: undefined;
91
+ }, {}, {}>;
92
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
93
+ name: "updated_at";
94
+ tableName: "knowledge_items";
95
+ dataType: "date";
96
+ columnType: "PgTimestamp";
97
+ data: Date;
98
+ driverParam: string;
99
+ notNull: true;
100
+ hasDefault: true;
101
+ isPrimaryKey: false;
102
+ isAutoincrement: false;
103
+ hasRuntimeDefault: false;
104
+ enumValues: undefined;
105
+ baseColumn: never;
106
+ identity: undefined;
107
+ generated: undefined;
108
+ }, {}, {}>;
109
+ };
110
+ dialect: "pg";
111
+ }>;
112
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOzB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { pgTable, text, jsonb, timestamp } from "drizzle-orm/pg-core";
2
+ export const knowledgeItems = pgTable("knowledge_items", {
3
+ id: text("id").primaryKey(),
4
+ accountId: text("account_id").notNull(),
5
+ category: text("category").notNull(),
6
+ data: jsonb("data").$type().notNull(),
7
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
8
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
9
+ });
10
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,iBAAiB,EAAE;IACvD,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACvC,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACpC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAA2B,CAAC,OAAO,EAAE;IAC9D,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;IACjF,SAAS,EAAE,SAAS,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE;CAClF,CAAC,CAAC"}
@@ -0,0 +1,40 @@
1
+ export declare const CATEGORIES: readonly ["offer", "persona", "objection", "differentiator", "competitor", "geo", "pricing", "faq", "profile"];
2
+ export type KnowledgeCategory = (typeof CATEGORIES)[number];
3
+ export interface KnowledgeItem {
4
+ id: string;
5
+ accountId: string;
6
+ category: KnowledgeCategory;
7
+ data: Record<string, unknown>;
8
+ createdAt: Date;
9
+ updatedAt: Date;
10
+ }
11
+ export interface BusinessContext {
12
+ profile: Record<string, unknown> | null;
13
+ offers: Record<string, unknown>[];
14
+ personas: Record<string, unknown>[];
15
+ objections: Record<string, unknown>[];
16
+ differentiators: Record<string, unknown>[];
17
+ competitors: Record<string, unknown>[];
18
+ geo: Record<string, unknown>[];
19
+ pricing: Record<string, unknown>[];
20
+ faqs: Record<string, unknown>[];
21
+ gaps: string[];
22
+ }
23
+ export interface OnboardResult {
24
+ context: BusinessContext;
25
+ gaps: string[];
26
+ followup_questions: string[];
27
+ saved_counts: Record<string, number>;
28
+ }
29
+ export interface ExtractedKnowledge {
30
+ profile?: Record<string, unknown>;
31
+ offers: Record<string, unknown>[];
32
+ personas: Record<string, unknown>[];
33
+ objections: Record<string, unknown>[];
34
+ differentiators: Record<string, unknown>[];
35
+ geo: Record<string, unknown>[];
36
+ pricing: Record<string, unknown>[];
37
+ faqs: Record<string, unknown>[];
38
+ gaps: string[];
39
+ }
40
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,gHAUb,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB"}
@@ -0,0 +1,12 @@
1
+ export const CATEGORIES = [
2
+ "offer",
3
+ "persona",
4
+ "objection",
5
+ "differentiator",
6
+ "competitor",
7
+ "geo",
8
+ "pricing",
9
+ "faq",
10
+ "profile",
11
+ ];
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO;IACP,SAAS;IACT,WAAW;IACX,gBAAgB;IAChB,YAAY;IACZ,KAAK;IACL,SAAS;IACT,KAAK;IACL,SAAS;CACD,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { getContext, getCategory, createItem, updateItem, deleteItem, searchKnowledge, } from "../core/knowledge.js";
6
+ import { onboardFromUrl } from "../core/onboard.js";
7
+ const ACCOUNT_ID = process.env.ROOT_ACCOUNT_ID ?? "default";
8
+ const server = new Server({ name: "root", version: "0.1.0" }, { capabilities: { tools: {} } });
9
+ const tools = [
10
+ {
11
+ name: "root_get_context",
12
+ description: "Returns complete business knowledge for this account — offers, personas, objections, differentiators, competitors, geo zones, pricing, and FAQs. ALWAYS call this tool before generating any content, SEO article, email, or prospection material.",
13
+ inputSchema: { type: "object", properties: {}, required: [] },
14
+ },
15
+ {
16
+ name: "root_get_category",
17
+ description: "Returns a specific category of business knowledge. Use when you need only one type of data (e.g. just personas for a prospection email, just objections for a sales script).",
18
+ inputSchema: {
19
+ type: "object",
20
+ properties: {
21
+ category: {
22
+ type: "string",
23
+ enum: [
24
+ "offer",
25
+ "persona",
26
+ "objection",
27
+ "differentiator",
28
+ "competitor",
29
+ "geo",
30
+ "pricing",
31
+ "faq",
32
+ "profile",
33
+ ],
34
+ description: "The knowledge category to retrieve",
35
+ },
36
+ },
37
+ required: ["category"],
38
+ },
39
+ },
40
+ {
41
+ name: "root_search",
42
+ description: "Search across all business knowledge with a text query. Use to find a specific objection, competitor detail, or FAQ answer.",
43
+ inputSchema: {
44
+ type: "object",
45
+ properties: {
46
+ query: { type: "string", description: "Search query" },
47
+ },
48
+ required: ["query"],
49
+ },
50
+ },
51
+ {
52
+ name: "root_onboard_from_url",
53
+ description: "Onboard a client by scraping their website URL. Automatically extracts offers, personas, differentiators, geo zones, pricing, FAQs, and analyzes local competitors. Returns full context + gaps + followup questions for missing data.",
54
+ inputSchema: {
55
+ type: "object",
56
+ properties: {
57
+ url: {
58
+ type: "string",
59
+ description: "The client website URL (e.g. https://client.fr)",
60
+ },
61
+ },
62
+ required: ["url"],
63
+ },
64
+ },
65
+ {
66
+ name: "root_create_item",
67
+ description: "Add a new knowledge item to a category. Use to add an offer, persona, competitor, or any data not extracted automatically.",
68
+ inputSchema: {
69
+ type: "object",
70
+ properties: {
71
+ category: {
72
+ type: "string",
73
+ enum: [
74
+ "offer",
75
+ "persona",
76
+ "objection",
77
+ "differentiator",
78
+ "competitor",
79
+ "geo",
80
+ "pricing",
81
+ "faq",
82
+ "profile",
83
+ ],
84
+ },
85
+ data: {
86
+ type: "object",
87
+ description: "The item data as a JSON object",
88
+ additionalProperties: true,
89
+ },
90
+ },
91
+ required: ["category", "data"],
92
+ },
93
+ },
94
+ {
95
+ name: "root_update_item",
96
+ description: "Update an existing knowledge item by ID.",
97
+ inputSchema: {
98
+ type: "object",
99
+ properties: {
100
+ id: { type: "string", description: "The item ID to update" },
101
+ data: {
102
+ type: "object",
103
+ description: "Fields to update (merged with existing data)",
104
+ additionalProperties: true,
105
+ },
106
+ },
107
+ required: ["id", "data"],
108
+ },
109
+ },
110
+ {
111
+ name: "root_delete_item",
112
+ description: "Delete a knowledge item by ID.",
113
+ inputSchema: {
114
+ type: "object",
115
+ properties: {
116
+ id: { type: "string", description: "The item ID to delete" },
117
+ },
118
+ required: ["id"],
119
+ },
120
+ },
121
+ ];
122
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
123
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
124
+ const { name, arguments: args } = request.params;
125
+ try {
126
+ switch (name) {
127
+ case "root_get_context": {
128
+ const context = await getContext(ACCOUNT_ID);
129
+ return { content: [{ type: "text", text: JSON.stringify(context) }] };
130
+ }
131
+ case "root_get_category": {
132
+ const { category } = args;
133
+ const items = await getCategory(ACCOUNT_ID, category);
134
+ return {
135
+ content: [
136
+ { type: "text", text: JSON.stringify({ category, count: items.length, items }) },
137
+ ],
138
+ };
139
+ }
140
+ case "root_search": {
141
+ const { query } = args;
142
+ const results = await searchKnowledge(ACCOUNT_ID, query);
143
+ return {
144
+ content: [
145
+ { type: "text", text: JSON.stringify({ query, count: results.length, results }) },
146
+ ],
147
+ };
148
+ }
149
+ case "root_onboard_from_url": {
150
+ const { url } = args;
151
+ const result = await onboardFromUrl(ACCOUNT_ID, url);
152
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
153
+ }
154
+ case "root_create_item": {
155
+ const { category, data } = args;
156
+ const item = await createItem(ACCOUNT_ID, category, data);
157
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, item }) }] };
158
+ }
159
+ case "root_update_item": {
160
+ const { id, data } = args;
161
+ const item = await updateItem(ACCOUNT_ID, id, data);
162
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, item }) }] };
163
+ }
164
+ case "root_delete_item": {
165
+ const { id } = args;
166
+ const result = await deleteItem(ACCOUNT_ID, id);
167
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
168
+ }
169
+ default:
170
+ throw new Error(`Unknown tool: ${name}`);
171
+ }
172
+ }
173
+ catch (err) {
174
+ return {
175
+ content: [{ type: "text", text: JSON.stringify({ error: String(err) }) }],
176
+ isError: true,
177
+ };
178
+ }
179
+ });
180
+ async function main() {
181
+ const transport = new StdioServerTransport();
182
+ await server.connect(transport);
183
+ }
184
+ main().catch(console.error);
185
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@silverbackbase/root",
3
+ "version": "0.2.0",
4
+ "description": "Business knowledge primitive for AI agents",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "root-mcp": "./dist/mcp/server.js"
9
+ },
10
+ "main": "./dist/core/index.js",
11
+ "types": "./dist/core/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/core/index.js",
15
+ "types": "./dist/core/index.d.ts"
16
+ }
17
+ },
18
+ "files": ["dist"],
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.json && node scripts/fix-shebangs.mjs",
21
+ "typecheck": "tsc -p tsconfig.json --noEmit"
22
+ },
23
+ "dependencies": {
24
+ "@anthropic-ai/sdk": "^0.40.0",
25
+ "@mendable/firecrawl-js": "^1.0.0",
26
+ "@modelcontextprotocol/sdk": "^1.29.0",
27
+ "nanoid": "^5.0.0",
28
+ "postgres": "^3.4.0",
29
+ "zod": "^3.23.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "typescript": "^5.4.0"
34
+ },
35
+ "engines": {
36
+ "node": ">=22"
37
+ },
38
+ "packageManager": "pnpm@9.0.0"
39
+ }