@jashwanth0712/synapse-mcp 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1002 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Synapse MCP
|
|
2
|
+
|
|
3
|
+
**AI Knowledge Oracle on Stellar**
|
|
4
|
+
|
|
5
|
+
A self-contained MCP server for AI agent knowledge sharing with real Stellar payments. Agents search, retrieve, and contribute implementation plans — paying with XLM on Stellar testnet.
|
|
6
|
+
|
|
7
|
+
## Vision
|
|
8
|
+
|
|
9
|
+
Synapse is a decentralized knowledge marketplace for AI agents. V1 ships with embedded SQLite and full-text search. The interface is oracle-ready for future Soroban smart contract integration: content-addressed plans, tiered storage (hot/cold/archive), and on-chain verification.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### As an MCP Server (Claude Code, Cursor, etc.)
|
|
14
|
+
|
|
15
|
+
Add to your `.mcp.json`:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"mcpServers": {
|
|
20
|
+
"synapse-mcp": {
|
|
21
|
+
"command": "npx",
|
|
22
|
+
"args": ["synapse-mcp"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or from source:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"synapse-mcp": {
|
|
34
|
+
"command": "node",
|
|
35
|
+
"args": ["packages/synapse-mcp/dist/index.js"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### CLI Commands
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
synapse-mcp # Start MCP server (stdio)
|
|
45
|
+
synapse-mcp dashboard # Show wallet, contributions, usage
|
|
46
|
+
synapse-mcp wallet # Print wallet address and balance
|
|
47
|
+
synapse-mcp fund # Fund wallet via Friendbot (testnet)
|
|
48
|
+
synapse-mcp stats # Show usage and contribution stats
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## MCP Tools
|
|
52
|
+
|
|
53
|
+
### `synapse_search` (0.2 XLM)
|
|
54
|
+
|
|
55
|
+
Search the knowledge base for implementation plans using full-text search with BM25 scoring.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
synapse_search({ query: "kubernetes deployment", tags: ["k8s"] })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `synapse_recall` (1 XLM)
|
|
62
|
+
|
|
63
|
+
Retrieve the full content of a plan by ID. Returns complete markdown with implementation details.
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
synapse_recall({ id: "abc-123-def" })
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `synapse_learn` (Free)
|
|
70
|
+
|
|
71
|
+
Upload a new plan. Contributors earn 70% of future retrieval fees.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
synapse_learn({
|
|
75
|
+
title: "Auth with NextAuth",
|
|
76
|
+
content: "# Setup\n...",
|
|
77
|
+
tags: ["auth", "nextjs"],
|
|
78
|
+
domain: "web",
|
|
79
|
+
language: "typescript",
|
|
80
|
+
framework: "nextjs"
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Architecture
|
|
85
|
+
|
|
86
|
+
- **Storage**: Embedded SQLite with FTS5 full-text search, WAL mode
|
|
87
|
+
- **Payments**: Real Stellar transactions on testnet via Horizon API
|
|
88
|
+
- **Content Addressing**: SHA-256 hashes prevent duplicates
|
|
89
|
+
- **Revenue Split**: 70% to contributors, 30% to platform (tracked)
|
|
90
|
+
- **Oracle Interface**: Full Soroban contract interface defined, V2 implementation
|
|
91
|
+
|
|
92
|
+
## Data Locations (XDG-Compliant)
|
|
93
|
+
|
|
94
|
+
| Path | Purpose |
|
|
95
|
+
|------|---------|
|
|
96
|
+
| `~/.config/synapse-mcp/wallet.json` | Stellar keypair |
|
|
97
|
+
| `~/.local/share/synapse-mcp/kb.db` | SQLite knowledge base |
|
|
98
|
+
| `~/.local/share/synapse-mcp/history.json` | Usage history |
|
|
99
|
+
|
|
100
|
+
## Environment Variables
|
|
101
|
+
|
|
102
|
+
| Variable | Description |
|
|
103
|
+
|----------|-------------|
|
|
104
|
+
| `STELLAR_SECRET_KEY` | Use existing Stellar key |
|
|
105
|
+
| `SYNAPSE_CONFIG_DIR` | Custom config directory |
|
|
106
|
+
| `SYNAPSE_DATA_DIR` | Custom data directory |
|
|
107
|
+
| `SYNAPSE_PLATFORM_ADDRESS` | Override platform address |
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pnpm install
|
|
113
|
+
pnpm build
|
|
114
|
+
pnpm test
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Network
|
|
118
|
+
|
|
119
|
+
V1 runs on **Stellar testnet** only. Wallets are auto-created and funded via Friendbot.
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,1002 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { Keypair, Horizon, TransactionBuilder, Networks, Operation, Asset } from '@stellar/stellar-sdk';
|
|
5
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
6
|
+
import Database from 'better-sqlite3';
|
|
7
|
+
import { randomUUID, createHash } from 'crypto';
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
|
|
13
|
+
var __defProp = Object.defineProperty;
|
|
14
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
+
var __esm = (fn, res) => function __init() {
|
|
16
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
17
|
+
};
|
|
18
|
+
var __export = (target, all) => {
|
|
19
|
+
for (var name in all)
|
|
20
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
21
|
+
};
|
|
22
|
+
var xdgConfig, xdgData, CONFIG_DIR, DATA_DIR, DB_PATH, WALLET_PATH, HISTORY_PATH, PLATFORM_ADDRESS, SEARCH_COST_XLM, RECALL_COST_XLM;
|
|
23
|
+
var init_config = __esm({
|
|
24
|
+
"src/config.ts"() {
|
|
25
|
+
xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
26
|
+
xdgData = process.env.XDG_DATA_HOME || join(homedir(), ".local", "share");
|
|
27
|
+
CONFIG_DIR = process.env.SYNAPSE_CONFIG_DIR || join(xdgConfig, "synapse-mcp");
|
|
28
|
+
DATA_DIR = process.env.SYNAPSE_DATA_DIR || join(xdgData, "synapse-mcp");
|
|
29
|
+
DB_PATH = join(DATA_DIR, "kb.db");
|
|
30
|
+
WALLET_PATH = join(CONFIG_DIR, "wallet.json");
|
|
31
|
+
HISTORY_PATH = join(DATA_DIR, "history.json");
|
|
32
|
+
PLATFORM_ADDRESS = process.env.SYNAPSE_PLATFORM_ADDRESS || "GC63PSERYMUUUJKYSSFQ7FKRAU5UPIP3XUC6X7DLMZUB7SSCPW5BSIRT";
|
|
33
|
+
SEARCH_COST_XLM = "0.2";
|
|
34
|
+
RECALL_COST_XLM = "1";
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
function loadOrCreateWallet() {
|
|
38
|
+
const envSecret = process.env.STELLAR_SECRET_KEY;
|
|
39
|
+
if (envSecret) {
|
|
40
|
+
return Keypair.fromSecret(envSecret);
|
|
41
|
+
}
|
|
42
|
+
if (existsSync(WALLET_PATH)) {
|
|
43
|
+
const data = JSON.parse(
|
|
44
|
+
readFileSync(WALLET_PATH, "utf-8")
|
|
45
|
+
);
|
|
46
|
+
return Keypair.fromSecret(data.secretKey);
|
|
47
|
+
}
|
|
48
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
49
|
+
const keypair = Keypair.random();
|
|
50
|
+
const walletData = {
|
|
51
|
+
publicKey: keypair.publicKey(),
|
|
52
|
+
secretKey: keypair.secret(),
|
|
53
|
+
network: "stellar-testnet",
|
|
54
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
55
|
+
};
|
|
56
|
+
writeFileSync(WALLET_PATH, JSON.stringify(walletData, null, 2), {
|
|
57
|
+
mode: 384
|
|
58
|
+
});
|
|
59
|
+
return keypair;
|
|
60
|
+
}
|
|
61
|
+
async function fundWithFriendbot(publicKey) {
|
|
62
|
+
const url = `https://friendbot.stellar.org?addr=${encodeURIComponent(publicKey)}`;
|
|
63
|
+
const res = await fetch(url);
|
|
64
|
+
return res.ok;
|
|
65
|
+
}
|
|
66
|
+
async function getBalance(publicKey) {
|
|
67
|
+
const url = `https://horizon-testnet.stellar.org/accounts/${publicKey}`;
|
|
68
|
+
const res = await fetch(url);
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
if (res.status === 404) return "0 (account not funded)";
|
|
71
|
+
return "unknown";
|
|
72
|
+
}
|
|
73
|
+
const data = await res.json();
|
|
74
|
+
const native = data.balances.find((b) => b.asset_type === "native");
|
|
75
|
+
return native ? `${native.balance} XLM` : "0 XLM";
|
|
76
|
+
}
|
|
77
|
+
function getWalletInfo() {
|
|
78
|
+
const envSecret = process.env.STELLAR_SECRET_KEY;
|
|
79
|
+
if (envSecret) {
|
|
80
|
+
const kp = Keypair.fromSecret(envSecret);
|
|
81
|
+
return { publicKey: kp.publicKey(), secretPresent: true, source: "env" };
|
|
82
|
+
}
|
|
83
|
+
if (existsSync(WALLET_PATH)) {
|
|
84
|
+
mkdirSync(dirname(WALLET_PATH), { recursive: true });
|
|
85
|
+
const data = JSON.parse(
|
|
86
|
+
readFileSync(WALLET_PATH, "utf-8")
|
|
87
|
+
);
|
|
88
|
+
return {
|
|
89
|
+
publicKey: data.publicKey,
|
|
90
|
+
secretPresent: true,
|
|
91
|
+
source: "file"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return { publicKey: "", secretPresent: false, source: "none" };
|
|
95
|
+
}
|
|
96
|
+
var init_manager = __esm({
|
|
97
|
+
"src/wallet/manager.ts"() {
|
|
98
|
+
init_config();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
function initDatabase(dbPath) {
|
|
102
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
103
|
+
const db = new Database(dbPath);
|
|
104
|
+
db.pragma("journal_mode = WAL");
|
|
105
|
+
db.pragma("foreign_keys = ON");
|
|
106
|
+
db.exec(`
|
|
107
|
+
CREATE TABLE IF NOT EXISTS plans (
|
|
108
|
+
id TEXT PRIMARY KEY,
|
|
109
|
+
title TEXT NOT NULL,
|
|
110
|
+
description TEXT NOT NULL,
|
|
111
|
+
content TEXT NOT NULL,
|
|
112
|
+
content_hash TEXT NOT NULL UNIQUE,
|
|
113
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
114
|
+
domain TEXT,
|
|
115
|
+
language TEXT,
|
|
116
|
+
framework TEXT,
|
|
117
|
+
contributor_address TEXT NOT NULL,
|
|
118
|
+
quality_score REAL NOT NULL DEFAULT 0,
|
|
119
|
+
purchase_count INTEGER NOT NULL DEFAULT 0,
|
|
120
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS plans_fts USING fts5(
|
|
124
|
+
title,
|
|
125
|
+
description,
|
|
126
|
+
tags,
|
|
127
|
+
content,
|
|
128
|
+
content=plans,
|
|
129
|
+
content_rowid=rowid
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
CREATE TRIGGER IF NOT EXISTS plans_ai AFTER INSERT ON plans BEGIN
|
|
133
|
+
INSERT INTO plans_fts(rowid, title, description, tags, content)
|
|
134
|
+
VALUES (new.rowid, new.title, new.description, new.tags, new.content);
|
|
135
|
+
END;
|
|
136
|
+
|
|
137
|
+
CREATE TRIGGER IF NOT EXISTS plans_ad AFTER DELETE ON plans BEGIN
|
|
138
|
+
INSERT INTO plans_fts(plans_fts, rowid, title, description, tags, content)
|
|
139
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.tags, old.content);
|
|
140
|
+
END;
|
|
141
|
+
|
|
142
|
+
CREATE TRIGGER IF NOT EXISTS plans_au AFTER UPDATE ON plans BEGIN
|
|
143
|
+
INSERT INTO plans_fts(plans_fts, rowid, title, description, tags, content)
|
|
144
|
+
VALUES ('delete', old.rowid, old.title, old.description, old.tags, old.content);
|
|
145
|
+
INSERT INTO plans_fts(rowid, title, description, tags, content)
|
|
146
|
+
VALUES (new.rowid, new.title, new.description, new.tags, new.content);
|
|
147
|
+
END;
|
|
148
|
+
|
|
149
|
+
CREATE TABLE IF NOT EXISTS purchases (
|
|
150
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
151
|
+
plan_id TEXT NOT NULL REFERENCES plans(id),
|
|
152
|
+
buyer_address TEXT NOT NULL,
|
|
153
|
+
amount_stroops INTEGER NOT NULL,
|
|
154
|
+
contributor_share_stroops INTEGER NOT NULL,
|
|
155
|
+
operator_share_stroops INTEGER NOT NULL,
|
|
156
|
+
transaction_hash TEXT,
|
|
157
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
CREATE INDEX IF NOT EXISTS idx_purchases_plan_id ON purchases(plan_id);
|
|
161
|
+
CREATE INDEX IF NOT EXISTS idx_purchases_buyer ON purchases(buyer_address);
|
|
162
|
+
CREATE INDEX IF NOT EXISTS idx_plans_contributor ON plans(contributor_address);
|
|
163
|
+
CREATE INDEX IF NOT EXISTS idx_plans_content_hash ON plans(content_hash);
|
|
164
|
+
`);
|
|
165
|
+
return db;
|
|
166
|
+
}
|
|
167
|
+
var init_schema = __esm({
|
|
168
|
+
"src/storage/db/schema.ts"() {
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
var CONTRIBUTOR_SHARE, PlanStore;
|
|
172
|
+
var init_plans = __esm({
|
|
173
|
+
"src/storage/db/plans.ts"() {
|
|
174
|
+
CONTRIBUTOR_SHARE = 0.7;
|
|
175
|
+
PlanStore = class {
|
|
176
|
+
constructor(db) {
|
|
177
|
+
this.db = db;
|
|
178
|
+
}
|
|
179
|
+
insert(plan) {
|
|
180
|
+
const id = randomUUID();
|
|
181
|
+
const content_hash = createHash("sha256").update(plan.content).digest("hex");
|
|
182
|
+
const tagsJson = JSON.stringify(plan.tags);
|
|
183
|
+
const stmt = this.db.prepare(`
|
|
184
|
+
INSERT INTO plans (id, title, description, content, content_hash, tags, domain, language, framework, contributor_address)
|
|
185
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
186
|
+
`);
|
|
187
|
+
stmt.run(
|
|
188
|
+
id,
|
|
189
|
+
plan.title,
|
|
190
|
+
plan.description,
|
|
191
|
+
plan.content,
|
|
192
|
+
content_hash,
|
|
193
|
+
tagsJson,
|
|
194
|
+
plan.domain || null,
|
|
195
|
+
plan.language || null,
|
|
196
|
+
plan.framework || null,
|
|
197
|
+
plan.contributor_address
|
|
198
|
+
);
|
|
199
|
+
return this.getById(id);
|
|
200
|
+
}
|
|
201
|
+
getById(id) {
|
|
202
|
+
const stmt = this.db.prepare("SELECT * FROM plans WHERE id = ?");
|
|
203
|
+
return stmt.get(id) || null;
|
|
204
|
+
}
|
|
205
|
+
getMeta(id) {
|
|
206
|
+
const plan = this.db.prepare(
|
|
207
|
+
"SELECT id, title, description, tags, domain, language, framework, quality_score, purchase_count, created_at FROM plans WHERE id = ?"
|
|
208
|
+
).get(id);
|
|
209
|
+
if (!plan) return null;
|
|
210
|
+
return {
|
|
211
|
+
...plan,
|
|
212
|
+
tags: JSON.parse(plan.tags)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
search(query, tags, limit = 20) {
|
|
216
|
+
let sql;
|
|
217
|
+
const params = [];
|
|
218
|
+
if (query && query.trim()) {
|
|
219
|
+
sql = `
|
|
220
|
+
SELECT p.id, p.title, p.description, p.tags, p.domain, p.quality_score, p.purchase_count,
|
|
221
|
+
bm25(plans_fts) as rank
|
|
222
|
+
FROM plans_fts fts
|
|
223
|
+
JOIN plans p ON p.rowid = fts.rowid
|
|
224
|
+
WHERE plans_fts MATCH ?
|
|
225
|
+
`;
|
|
226
|
+
params.push(query);
|
|
227
|
+
} else {
|
|
228
|
+
sql = `
|
|
229
|
+
SELECT p.id, p.title, p.description, p.tags, p.domain, p.quality_score, p.purchase_count,
|
|
230
|
+
0 as rank
|
|
231
|
+
FROM plans p
|
|
232
|
+
WHERE 1=1
|
|
233
|
+
`;
|
|
234
|
+
}
|
|
235
|
+
if (tags && tags.length > 0) {
|
|
236
|
+
for (const tag of tags) {
|
|
237
|
+
sql += " AND p.tags LIKE ?";
|
|
238
|
+
params.push(`%${tag}%`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
sql += query && query.trim() ? " ORDER BY rank LIMIT ?" : " ORDER BY p.purchase_count DESC, p.created_at DESC LIMIT ?";
|
|
242
|
+
params.push(limit);
|
|
243
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
244
|
+
return rows.map((row) => ({
|
|
245
|
+
id: row.id,
|
|
246
|
+
title: row.title,
|
|
247
|
+
description: row.description,
|
|
248
|
+
tags: JSON.parse(row.tags),
|
|
249
|
+
domain: row.domain,
|
|
250
|
+
quality_score: row.quality_score,
|
|
251
|
+
purchase_count: row.purchase_count,
|
|
252
|
+
rank: row.rank
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
recordPurchase(planId, buyerAddress, amountStroops, transactionHash) {
|
|
256
|
+
const contributorShare = Math.floor(amountStroops * CONTRIBUTOR_SHARE);
|
|
257
|
+
const operatorShare = amountStroops - contributorShare;
|
|
258
|
+
this.db.prepare(
|
|
259
|
+
`
|
|
260
|
+
INSERT INTO purchases (plan_id, buyer_address, amount_stroops, contributor_share_stroops, operator_share_stroops, transaction_hash)
|
|
261
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
262
|
+
`
|
|
263
|
+
).run(
|
|
264
|
+
planId,
|
|
265
|
+
buyerAddress,
|
|
266
|
+
amountStroops,
|
|
267
|
+
contributorShare,
|
|
268
|
+
operatorShare,
|
|
269
|
+
transactionHash
|
|
270
|
+
);
|
|
271
|
+
this.db.prepare(
|
|
272
|
+
"UPDATE plans SET purchase_count = purchase_count + 1 WHERE id = ?"
|
|
273
|
+
).run(planId);
|
|
274
|
+
return {
|
|
275
|
+
plan_id: planId,
|
|
276
|
+
buyer_address: buyerAddress,
|
|
277
|
+
amount_stroops: amountStroops,
|
|
278
|
+
contributor_share_stroops: contributorShare,
|
|
279
|
+
operator_share_stroops: operatorShare,
|
|
280
|
+
transaction_hash: transactionHash,
|
|
281
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
getContributorStats(address) {
|
|
285
|
+
const stats = this.db.prepare(
|
|
286
|
+
`
|
|
287
|
+
SELECT
|
|
288
|
+
? as contributor_address,
|
|
289
|
+
(SELECT COUNT(*) FROM plans WHERE contributor_address = ?) as plans_count,
|
|
290
|
+
COALESCE((SELECT SUM(contributor_share_stroops) FROM purchases p JOIN plans pl ON p.plan_id = pl.id WHERE pl.contributor_address = ?), 0) as total_earned_stroops,
|
|
291
|
+
COALESCE((SELECT COUNT(*) FROM purchases p JOIN plans pl ON p.plan_id = pl.id WHERE pl.contributor_address = ?), 0) as total_purchases
|
|
292
|
+
`
|
|
293
|
+
).get(address, address, address, address);
|
|
294
|
+
return stats;
|
|
295
|
+
}
|
|
296
|
+
getKBStats() {
|
|
297
|
+
const totalPlans = this.db.prepare("SELECT COUNT(*) as count FROM plans").get().count;
|
|
298
|
+
const totalPurchases = this.db.prepare("SELECT COUNT(*) as count FROM purchases").get().count;
|
|
299
|
+
const totalContributors = this.db.prepare(
|
|
300
|
+
"SELECT COUNT(DISTINCT contributor_address) as count FROM plans"
|
|
301
|
+
).get().count;
|
|
302
|
+
const plans = this.db.prepare("SELECT tags FROM plans").all();
|
|
303
|
+
const tagCounts = {};
|
|
304
|
+
for (const row of plans) {
|
|
305
|
+
const tags = JSON.parse(row.tags);
|
|
306
|
+
for (const tag of tags) {
|
|
307
|
+
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const topTags = Object.entries(tagCounts).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([tag, count]) => ({ tag, count }));
|
|
311
|
+
return {
|
|
312
|
+
total_plans: totalPlans,
|
|
313
|
+
total_purchases: totalPurchases,
|
|
314
|
+
total_contributors: totalContributors,
|
|
315
|
+
top_tags: topTags
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
contentHashExists(content) {
|
|
319
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
320
|
+
const row = this.db.prepare("SELECT 1 FROM plans WHERE content_hash = ?").get(hash);
|
|
321
|
+
return !!row;
|
|
322
|
+
}
|
|
323
|
+
getContentHash(planId) {
|
|
324
|
+
const plan = this.db.prepare("SELECT content_hash FROM plans WHERE id = ?").get(planId);
|
|
325
|
+
return plan?.content_hash || null;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// src/storage/local-provider.ts
|
|
332
|
+
var SOROBAN_ERROR, LocalStorageProvider;
|
|
333
|
+
var init_local_provider = __esm({
|
|
334
|
+
"src/storage/local-provider.ts"() {
|
|
335
|
+
init_schema();
|
|
336
|
+
init_plans();
|
|
337
|
+
SOROBAN_ERROR = "Soroban oracle not available in local mode. Coming in v2.";
|
|
338
|
+
LocalStorageProvider = class {
|
|
339
|
+
planStore;
|
|
340
|
+
db;
|
|
341
|
+
constructor(dbPath) {
|
|
342
|
+
this.db = initDatabase(dbPath);
|
|
343
|
+
this.planStore = new PlanStore(this.db);
|
|
344
|
+
}
|
|
345
|
+
// === Core CRUD ===
|
|
346
|
+
async store(plan, _options) {
|
|
347
|
+
const description = plan.description || plan.content.slice(0, 200).replace(/\n/g, " ").trim();
|
|
348
|
+
return this.planStore.insert({
|
|
349
|
+
title: plan.title,
|
|
350
|
+
description,
|
|
351
|
+
content: plan.content,
|
|
352
|
+
tags: plan.tags,
|
|
353
|
+
domain: plan.domain,
|
|
354
|
+
language: plan.language,
|
|
355
|
+
framework: plan.framework,
|
|
356
|
+
contributor_address: plan.contributor_address
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
async getById(id) {
|
|
360
|
+
return this.planStore.getById(id);
|
|
361
|
+
}
|
|
362
|
+
async getMeta(id) {
|
|
363
|
+
return this.planStore.getMeta(id);
|
|
364
|
+
}
|
|
365
|
+
async search(options) {
|
|
366
|
+
return this.planStore.search(options.query, options.tags, options.limit);
|
|
367
|
+
}
|
|
368
|
+
async contentExists(content) {
|
|
369
|
+
return this.planStore.contentHashExists(content);
|
|
370
|
+
}
|
|
371
|
+
// === Payment / Purchase Tracking ===
|
|
372
|
+
async recordPurchase(planId, buyerAddress, amountStroops, txHash) {
|
|
373
|
+
return this.planStore.recordPurchase(
|
|
374
|
+
planId,
|
|
375
|
+
buyerAddress,
|
|
376
|
+
amountStroops,
|
|
377
|
+
txHash
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
async getContributorStats(address) {
|
|
381
|
+
return this.planStore.getContributorStats(address);
|
|
382
|
+
}
|
|
383
|
+
async getKBStats() {
|
|
384
|
+
return this.planStore.getKBStats();
|
|
385
|
+
}
|
|
386
|
+
// === On-Chain Oracle (Soroban) - V2 ===
|
|
387
|
+
async publishToChain(_planId) {
|
|
388
|
+
throw new Error(SOROBAN_ERROR);
|
|
389
|
+
}
|
|
390
|
+
async verifyIntegrity(_planId) {
|
|
391
|
+
throw new Error(SOROBAN_ERROR);
|
|
392
|
+
}
|
|
393
|
+
async syncFromChain(_contractId) {
|
|
394
|
+
throw new Error(SOROBAN_ERROR);
|
|
395
|
+
}
|
|
396
|
+
async getOnChainMeta(_planId) {
|
|
397
|
+
throw new Error(SOROBAN_ERROR);
|
|
398
|
+
}
|
|
399
|
+
// === Content Addressing ===
|
|
400
|
+
async getContentHash(planId) {
|
|
401
|
+
const hash = this.planStore.getContentHash(planId);
|
|
402
|
+
if (!hash) throw new Error(`Plan not found: ${planId}`);
|
|
403
|
+
return hash;
|
|
404
|
+
}
|
|
405
|
+
// === Lifecycle ===
|
|
406
|
+
async close() {
|
|
407
|
+
this.db.close();
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
function appendHistory(entry) {
|
|
413
|
+
mkdirSync(dirname(HISTORY_PATH), { recursive: true });
|
|
414
|
+
let history = [];
|
|
415
|
+
if (existsSync(HISTORY_PATH)) {
|
|
416
|
+
try {
|
|
417
|
+
history = JSON.parse(
|
|
418
|
+
readFileSync(HISTORY_PATH, "utf-8")
|
|
419
|
+
);
|
|
420
|
+
} catch {
|
|
421
|
+
history = [];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
history.push({ ...entry, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
425
|
+
if (history.length > MAX_ENTRIES) {
|
|
426
|
+
history = history.slice(-MAX_ENTRIES);
|
|
427
|
+
}
|
|
428
|
+
writeFileSync(HISTORY_PATH, JSON.stringify(history, null, 2));
|
|
429
|
+
}
|
|
430
|
+
function getHistory() {
|
|
431
|
+
if (!existsSync(HISTORY_PATH)) return [];
|
|
432
|
+
try {
|
|
433
|
+
return JSON.parse(
|
|
434
|
+
readFileSync(HISTORY_PATH, "utf-8")
|
|
435
|
+
);
|
|
436
|
+
} catch {
|
|
437
|
+
return [];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
var MAX_ENTRIES;
|
|
441
|
+
var init_tracker = __esm({
|
|
442
|
+
"src/history/tracker.ts"() {
|
|
443
|
+
init_config();
|
|
444
|
+
MAX_ENTRIES = 1e3;
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
async function submitPayment(fromKeypair, toAddress, amountXLM) {
|
|
448
|
+
const server = new Horizon.Server(HORIZON_URL);
|
|
449
|
+
try {
|
|
450
|
+
const account = await server.loadAccount(fromKeypair.publicKey());
|
|
451
|
+
const transaction = new TransactionBuilder(account, {
|
|
452
|
+
fee: "100",
|
|
453
|
+
networkPassphrase: Networks.TESTNET
|
|
454
|
+
}).addOperation(
|
|
455
|
+
Operation.payment({
|
|
456
|
+
destination: toAddress,
|
|
457
|
+
asset: Asset.native(),
|
|
458
|
+
amount: amountXLM
|
|
459
|
+
})
|
|
460
|
+
).setTimeout(30).build();
|
|
461
|
+
transaction.sign(fromKeypair);
|
|
462
|
+
const result = await server.submitTransaction(transaction);
|
|
463
|
+
const hash = typeof result === "object" && result !== null && "hash" in result ? result.hash : null;
|
|
464
|
+
return { success: true, txHash: hash };
|
|
465
|
+
} catch (err) {
|
|
466
|
+
const message = err instanceof Error ? err.message : "Unknown payment error";
|
|
467
|
+
return { success: false, txHash: null, error: message };
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
var HORIZON_URL;
|
|
471
|
+
var init_stellar = __esm({
|
|
472
|
+
"src/payments/stellar.ts"() {
|
|
473
|
+
HORIZON_URL = "https://horizon-testnet.stellar.org";
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// src/mcp/server.ts
|
|
478
|
+
var server_exports = {};
|
|
479
|
+
__export(server_exports, {
|
|
480
|
+
startMcpServer: () => startMcpServer
|
|
481
|
+
});
|
|
482
|
+
async function startMcpServer() {
|
|
483
|
+
const keypair = loadOrCreateWallet();
|
|
484
|
+
const publicKey = keypair.publicKey();
|
|
485
|
+
const storage = new LocalStorageProvider(DB_PATH);
|
|
486
|
+
try {
|
|
487
|
+
const bal = await getBalance(publicKey);
|
|
488
|
+
if (bal.includes("not funded")) {
|
|
489
|
+
await fundWithFriendbot(publicKey);
|
|
490
|
+
}
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
const server = new McpServer({
|
|
494
|
+
name: "synapse-mcp",
|
|
495
|
+
version: "0.1.0"
|
|
496
|
+
});
|
|
497
|
+
server.tool(
|
|
498
|
+
"synapse_search",
|
|
499
|
+
"Search the Synapse knowledge base for implementation plans, patterns, and solutions contributed by AI agents and developers. Returns ranked results using full-text search with BM25 scoring. Each search costs 0.2 XLM paid via Stellar testnet. Example: synapse_search({ query: 'kubernetes deployment', tags: ['k8s'] })",
|
|
500
|
+
{
|
|
501
|
+
query: z.string().describe(
|
|
502
|
+
"Search query (e.g., 'kubernetes deployment', 'auth flow')"
|
|
503
|
+
),
|
|
504
|
+
tags: z.array(z.string()).optional().describe("Filter by tags (e.g., ['k8s', 'auth'])")
|
|
505
|
+
},
|
|
506
|
+
async ({ query, tags }) => {
|
|
507
|
+
try {
|
|
508
|
+
const payment = await submitPayment(
|
|
509
|
+
keypair,
|
|
510
|
+
PLATFORM_ADDRESS,
|
|
511
|
+
SEARCH_COST_XLM
|
|
512
|
+
);
|
|
513
|
+
if (!payment.success) {
|
|
514
|
+
return {
|
|
515
|
+
content: [
|
|
516
|
+
{
|
|
517
|
+
type: "text",
|
|
518
|
+
text: `Payment failed: ${payment.error}
|
|
519
|
+
Ensure your wallet is funded. Run: synapse-mcp fund`
|
|
520
|
+
}
|
|
521
|
+
],
|
|
522
|
+
isError: true
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
const results = await storage.search({ query, tags });
|
|
526
|
+
appendHistory({
|
|
527
|
+
action: "search",
|
|
528
|
+
query,
|
|
529
|
+
txHash: payment.txHash || void 0,
|
|
530
|
+
costXlm: 0.2
|
|
531
|
+
});
|
|
532
|
+
const balance = await getBalance(publicKey).catch(() => "unknown");
|
|
533
|
+
const text = results.length === 0 ? `No plans found for "${query}".` : results.map(
|
|
534
|
+
(r, i) => `${i + 1}. **${r.title}** (id: ${r.id})
|
|
535
|
+
${r.description}
|
|
536
|
+
Tags: ${r.tags.join(", ")} | Score: ${r.quality_score} | Purchases: ${r.purchase_count}`
|
|
537
|
+
).join("\n\n");
|
|
538
|
+
return {
|
|
539
|
+
content: [
|
|
540
|
+
{ type: "text", text },
|
|
541
|
+
{
|
|
542
|
+
type: "text",
|
|
543
|
+
text: `
|
|
544
|
+
Cost: ${SEARCH_COST_XLM} XLM | Tx: ${payment.txHash || "n/a"}
|
|
545
|
+
Wallet: ${publicKey} | Balance: ${balance}`
|
|
546
|
+
}
|
|
547
|
+
]
|
|
548
|
+
};
|
|
549
|
+
} catch (err) {
|
|
550
|
+
return {
|
|
551
|
+
content: [
|
|
552
|
+
{
|
|
553
|
+
type: "text",
|
|
554
|
+
text: `Search error: ${err.message}`
|
|
555
|
+
}
|
|
556
|
+
],
|
|
557
|
+
isError: true
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
server.tool(
|
|
563
|
+
"synapse_recall",
|
|
564
|
+
"Retrieve the full content of a specific plan from the Synapse knowledge base. Use the plan ID from synapse_search results. Returns complete markdown content including implementation details, code examples, and architectural decisions. Each retrieval costs 1 XLM. Example: synapse_recall({ id: 'abc-123-def' })",
|
|
565
|
+
{
|
|
566
|
+
id: z.string().describe("Plan ID (UUID from search results)")
|
|
567
|
+
},
|
|
568
|
+
async ({ id }) => {
|
|
569
|
+
try {
|
|
570
|
+
const plan = await storage.getById(id);
|
|
571
|
+
if (!plan) {
|
|
572
|
+
return {
|
|
573
|
+
content: [
|
|
574
|
+
{ type: "text", text: `Plan not found: ${id}` }
|
|
575
|
+
],
|
|
576
|
+
isError: true
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
const payment = await submitPayment(
|
|
580
|
+
keypair,
|
|
581
|
+
PLATFORM_ADDRESS,
|
|
582
|
+
RECALL_COST_XLM
|
|
583
|
+
);
|
|
584
|
+
if (!payment.success) {
|
|
585
|
+
return {
|
|
586
|
+
content: [
|
|
587
|
+
{
|
|
588
|
+
type: "text",
|
|
589
|
+
text: `Payment failed: ${payment.error}
|
|
590
|
+
Ensure your wallet is funded. Run: synapse-mcp fund`
|
|
591
|
+
}
|
|
592
|
+
],
|
|
593
|
+
isError: true
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
await storage.recordPurchase(
|
|
597
|
+
id,
|
|
598
|
+
publicKey,
|
|
599
|
+
1e7,
|
|
600
|
+
// 1 XLM in stroops
|
|
601
|
+
payment.txHash
|
|
602
|
+
);
|
|
603
|
+
appendHistory({
|
|
604
|
+
action: "recall",
|
|
605
|
+
planId: id,
|
|
606
|
+
txHash: payment.txHash || void 0,
|
|
607
|
+
costXlm: 1
|
|
608
|
+
});
|
|
609
|
+
const balance = await getBalance(publicKey).catch(() => "unknown");
|
|
610
|
+
const tags = JSON.parse(plan.tags);
|
|
611
|
+
const text = [
|
|
612
|
+
`# ${plan.title}`,
|
|
613
|
+
"",
|
|
614
|
+
`**Domain**: ${plan.domain || "general"} | **Tags**: ${tags.join(", ")}`,
|
|
615
|
+
`**Contributor**: ${plan.contributor_address}`,
|
|
616
|
+
`**Content Hash**: ${plan.content_hash}`,
|
|
617
|
+
"",
|
|
618
|
+
plan.content
|
|
619
|
+
].join("\n");
|
|
620
|
+
return {
|
|
621
|
+
content: [
|
|
622
|
+
{ type: "text", text },
|
|
623
|
+
{
|
|
624
|
+
type: "text",
|
|
625
|
+
text: `
|
|
626
|
+
Cost: ${RECALL_COST_XLM} XLM | Tx: ${payment.txHash || "n/a"}
|
|
627
|
+
Wallet: ${publicKey} | Balance: ${balance}`
|
|
628
|
+
}
|
|
629
|
+
]
|
|
630
|
+
};
|
|
631
|
+
} catch (err) {
|
|
632
|
+
return {
|
|
633
|
+
content: [
|
|
634
|
+
{
|
|
635
|
+
type: "text",
|
|
636
|
+
text: `Recall error: ${err.message}`
|
|
637
|
+
}
|
|
638
|
+
],
|
|
639
|
+
isError: true
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
server.tool(
|
|
645
|
+
"synapse_learn",
|
|
646
|
+
"Upload a new implementation plan to the Synapse knowledge base. Share your learnings, patterns, and solutions so other AI agents can benefit. Plans are content-addressed (SHA-256) to prevent duplicates. Contributors earn 70% of future retrieval fees when other agents access their plans. Example: synapse_learn({ title: 'Auth with NextAuth', content: '# Setup...', tags: ['auth', 'nextjs'] })",
|
|
647
|
+
{
|
|
648
|
+
title: z.string().min(3).max(200).describe("Plan title"),
|
|
649
|
+
content: z.string().min(10).describe("Full plan content (markdown)"),
|
|
650
|
+
tags: z.array(z.string()).describe(
|
|
651
|
+
"Tags for discoverability (e.g., ['auth', 'nextjs'])"
|
|
652
|
+
),
|
|
653
|
+
domain: z.string().optional().describe("Domain (e.g., 'web', 'devops', 'ml')"),
|
|
654
|
+
language: z.string().optional().describe("Programming language"),
|
|
655
|
+
framework: z.string().optional().describe("Framework used")
|
|
656
|
+
},
|
|
657
|
+
async ({ title, content, tags, domain, language, framework }) => {
|
|
658
|
+
try {
|
|
659
|
+
const exists = await storage.contentExists(content);
|
|
660
|
+
if (exists) {
|
|
661
|
+
return {
|
|
662
|
+
content: [
|
|
663
|
+
{
|
|
664
|
+
type: "text",
|
|
665
|
+
text: "Duplicate content - a plan with identical content already exists."
|
|
666
|
+
}
|
|
667
|
+
],
|
|
668
|
+
isError: true
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const plan = await storage.store({
|
|
672
|
+
title,
|
|
673
|
+
content,
|
|
674
|
+
tags,
|
|
675
|
+
domain,
|
|
676
|
+
language,
|
|
677
|
+
framework,
|
|
678
|
+
contributor_address: publicKey
|
|
679
|
+
});
|
|
680
|
+
appendHistory({ action: "learn", planId: plan.id });
|
|
681
|
+
return {
|
|
682
|
+
content: [
|
|
683
|
+
{
|
|
684
|
+
type: "text",
|
|
685
|
+
text: [
|
|
686
|
+
"Plan stored successfully!",
|
|
687
|
+
"",
|
|
688
|
+
`ID: ${plan.id}`,
|
|
689
|
+
`Title: ${plan.title}`,
|
|
690
|
+
`Content Hash: ${plan.content_hash}`,
|
|
691
|
+
`Contributor: ${publicKey}`,
|
|
692
|
+
"",
|
|
693
|
+
"Revenue: You earn 70% of future retrieval fees when other agents access this plan."
|
|
694
|
+
].join("\n")
|
|
695
|
+
}
|
|
696
|
+
]
|
|
697
|
+
};
|
|
698
|
+
} catch (err) {
|
|
699
|
+
return {
|
|
700
|
+
content: [
|
|
701
|
+
{
|
|
702
|
+
type: "text",
|
|
703
|
+
text: `Learn error: ${err.message}`
|
|
704
|
+
}
|
|
705
|
+
],
|
|
706
|
+
isError: true
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
const transport = new StdioServerTransport();
|
|
712
|
+
await server.connect(transport);
|
|
713
|
+
}
|
|
714
|
+
var init_server = __esm({
|
|
715
|
+
"src/mcp/server.ts"() {
|
|
716
|
+
init_manager();
|
|
717
|
+
init_local_provider();
|
|
718
|
+
init_tracker();
|
|
719
|
+
init_stellar();
|
|
720
|
+
init_config();
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
// src/cli/dashboard.ts
|
|
725
|
+
var dashboard_exports = {};
|
|
726
|
+
__export(dashboard_exports, {
|
|
727
|
+
dashboardCommand: () => dashboardCommand
|
|
728
|
+
});
|
|
729
|
+
async function dashboardCommand() {
|
|
730
|
+
const info = getWalletInfo();
|
|
731
|
+
const history = getHistory();
|
|
732
|
+
console.log(
|
|
733
|
+
chalk.bold.cyan(`
|
|
734
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
735
|
+
\u2551 SYNAPSE MCP Dashboard \u2551
|
|
736
|
+
\u2551 AI Knowledge Oracle on Stellar \u2551
|
|
737
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
738
|
+
`)
|
|
739
|
+
);
|
|
740
|
+
console.log(chalk.bold(" WALLET"));
|
|
741
|
+
if (info.secretPresent) {
|
|
742
|
+
console.log(` Address: ${chalk.cyan(info.publicKey)}`);
|
|
743
|
+
console.log(
|
|
744
|
+
` Source: ${info.source === "env" ? "env var" : "wallet.json"}`
|
|
745
|
+
);
|
|
746
|
+
try {
|
|
747
|
+
const balance = await getBalance(info.publicKey);
|
|
748
|
+
console.log(` Balance: ${chalk.green(balance)}`);
|
|
749
|
+
} catch {
|
|
750
|
+
console.log(` Balance: ${chalk.red("unavailable")}`);
|
|
751
|
+
}
|
|
752
|
+
} else {
|
|
753
|
+
console.log(
|
|
754
|
+
chalk.yellow(
|
|
755
|
+
" No wallet configured. Run MCP server to auto-generate."
|
|
756
|
+
)
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
console.log("");
|
|
760
|
+
console.log(chalk.bold(" USAGE"));
|
|
761
|
+
const searches = history.filter((h) => h.action === "search").length;
|
|
762
|
+
const recalls = history.filter((h) => h.action === "recall").length;
|
|
763
|
+
const learns = history.filter((h) => h.action === "learn").length;
|
|
764
|
+
console.log(` Searches: ${searches}`);
|
|
765
|
+
console.log(` Recalls: ${recalls}`);
|
|
766
|
+
console.log(` Learns: ${learns}`);
|
|
767
|
+
console.log(
|
|
768
|
+
` Spent: ~${(searches * 0.2 + recalls * 1).toFixed(1)} XLM`
|
|
769
|
+
);
|
|
770
|
+
try {
|
|
771
|
+
const storage = new LocalStorageProvider(DB_PATH);
|
|
772
|
+
const stats = await storage.getKBStats();
|
|
773
|
+
console.log("");
|
|
774
|
+
console.log(chalk.bold(" KNOWLEDGE BASE"));
|
|
775
|
+
console.log(` Plans: ${stats.total_plans}`);
|
|
776
|
+
console.log(` Purchases: ${stats.total_purchases}`);
|
|
777
|
+
console.log(` Contributors: ${stats.total_contributors}`);
|
|
778
|
+
if (stats.top_tags.length > 0) {
|
|
779
|
+
console.log(
|
|
780
|
+
` Top Tags: ${stats.top_tags.map((t) => t.tag).join(", ")}`
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
if (info.secretPresent) {
|
|
784
|
+
const cStats = await storage.getContributorStats(info.publicKey);
|
|
785
|
+
if (cStats.plans_count > 0) {
|
|
786
|
+
console.log("");
|
|
787
|
+
console.log(chalk.bold(" CONTRIBUTIONS"));
|
|
788
|
+
console.log(` Plans: ${cStats.plans_count}`);
|
|
789
|
+
console.log(` Purchases: ${cStats.total_purchases}`);
|
|
790
|
+
console.log(
|
|
791
|
+
` Earned: ${chalk.green((cStats.total_earned_stroops / 1e7).toFixed(2) + " XLM")}`
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
await storage.close();
|
|
796
|
+
} catch {
|
|
797
|
+
}
|
|
798
|
+
if (history.length > 0) {
|
|
799
|
+
console.log("");
|
|
800
|
+
console.log(chalk.bold(" RECENT ACTIVITY"));
|
|
801
|
+
const recent = history.slice(-5).reverse();
|
|
802
|
+
for (const entry of recent) {
|
|
803
|
+
const time = new Date(entry.timestamp).toLocaleString();
|
|
804
|
+
const desc = entry.action === "search" ? `search: "${entry.query}"` : entry.action === "recall" ? `recall: ${entry.planId}` : `learn: ${entry.planId}`;
|
|
805
|
+
console.log(` ${chalk.dim(time)} ${desc}`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
console.log("");
|
|
809
|
+
}
|
|
810
|
+
var init_dashboard = __esm({
|
|
811
|
+
"src/cli/dashboard.ts"() {
|
|
812
|
+
init_manager();
|
|
813
|
+
init_tracker();
|
|
814
|
+
init_local_provider();
|
|
815
|
+
init_config();
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
// src/cli/wallet.ts
|
|
820
|
+
var wallet_exports = {};
|
|
821
|
+
__export(wallet_exports, {
|
|
822
|
+
walletCommand: () => walletCommand
|
|
823
|
+
});
|
|
824
|
+
async function walletCommand() {
|
|
825
|
+
const info = getWalletInfo();
|
|
826
|
+
if (!info.secretPresent) {
|
|
827
|
+
console.log(
|
|
828
|
+
chalk.yellow(
|
|
829
|
+
"No wallet found. Run the MCP server once to auto-generate one."
|
|
830
|
+
)
|
|
831
|
+
);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
console.log(chalk.bold("Synapse Wallet"));
|
|
835
|
+
console.log(` Address: ${chalk.cyan(info.publicKey)}`);
|
|
836
|
+
console.log(
|
|
837
|
+
` Source: ${info.source === "env" ? "STELLAR_SECRET_KEY env var" : WALLET_PATH}`
|
|
838
|
+
);
|
|
839
|
+
console.log(` Network: stellar-testnet`);
|
|
840
|
+
try {
|
|
841
|
+
const balance = await getBalance(info.publicKey);
|
|
842
|
+
console.log(` Balance: ${chalk.green(balance)}`);
|
|
843
|
+
} catch {
|
|
844
|
+
console.log(` Balance: ${chalk.red("unable to fetch")}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
var init_wallet = __esm({
|
|
848
|
+
"src/cli/wallet.ts"() {
|
|
849
|
+
init_manager();
|
|
850
|
+
init_config();
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// src/cli/fund.ts
|
|
855
|
+
var fund_exports = {};
|
|
856
|
+
__export(fund_exports, {
|
|
857
|
+
fundCommand: () => fundCommand
|
|
858
|
+
});
|
|
859
|
+
async function fundCommand() {
|
|
860
|
+
const keypair = loadOrCreateWallet();
|
|
861
|
+
const publicKey = keypair.publicKey();
|
|
862
|
+
console.log(chalk.bold("Funding wallet via Friendbot (testnet)..."));
|
|
863
|
+
console.log(` Address: ${chalk.cyan(publicKey)}`);
|
|
864
|
+
const success = await fundWithFriendbot(publicKey);
|
|
865
|
+
if (success) {
|
|
866
|
+
const balance = await getBalance(publicKey);
|
|
867
|
+
console.log(chalk.green(` Funded! Balance: ${balance}`));
|
|
868
|
+
} else {
|
|
869
|
+
console.log(
|
|
870
|
+
chalk.yellow(
|
|
871
|
+
" Friendbot returned an error (wallet may already be funded)."
|
|
872
|
+
)
|
|
873
|
+
);
|
|
874
|
+
try {
|
|
875
|
+
const balance = await getBalance(publicKey);
|
|
876
|
+
console.log(` Current balance: ${balance}`);
|
|
877
|
+
} catch {
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
var init_fund = __esm({
|
|
882
|
+
"src/cli/fund.ts"() {
|
|
883
|
+
init_manager();
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
// src/cli/stats.ts
|
|
888
|
+
var stats_exports = {};
|
|
889
|
+
__export(stats_exports, {
|
|
890
|
+
statsCommand: () => statsCommand
|
|
891
|
+
});
|
|
892
|
+
async function statsCommand() {
|
|
893
|
+
const history = getHistory();
|
|
894
|
+
const info = getWalletInfo();
|
|
895
|
+
console.log(chalk.bold("Synapse Usage Statistics"));
|
|
896
|
+
console.log(
|
|
897
|
+
` Searches: ${history.filter((h) => h.action === "search").length}`
|
|
898
|
+
);
|
|
899
|
+
console.log(
|
|
900
|
+
` Recalls: ${history.filter((h) => h.action === "recall").length}`
|
|
901
|
+
);
|
|
902
|
+
console.log(
|
|
903
|
+
` Learns: ${history.filter((h) => h.action === "learn").length}`
|
|
904
|
+
);
|
|
905
|
+
console.log(` Total: ${history.length}`);
|
|
906
|
+
try {
|
|
907
|
+
const storage = new LocalStorageProvider(DB_PATH);
|
|
908
|
+
const kbStats = await storage.getKBStats();
|
|
909
|
+
console.log("");
|
|
910
|
+
console.log(chalk.bold("Knowledge Base"));
|
|
911
|
+
console.log(` Total plans: ${kbStats.total_plans}`);
|
|
912
|
+
console.log(` Total purchases: ${kbStats.total_purchases}`);
|
|
913
|
+
console.log(` Total contributors: ${kbStats.total_contributors}`);
|
|
914
|
+
if (info.secretPresent) {
|
|
915
|
+
const cStats = await storage.getContributorStats(info.publicKey);
|
|
916
|
+
console.log("");
|
|
917
|
+
console.log(chalk.bold("Your Contributions"));
|
|
918
|
+
console.log(` Plans uploaded: ${cStats.plans_count}`);
|
|
919
|
+
console.log(` Total purchases: ${cStats.total_purchases}`);
|
|
920
|
+
console.log(
|
|
921
|
+
` Earnings: ${(cStats.total_earned_stroops / 1e7).toFixed(2)} XLM`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
await storage.close();
|
|
925
|
+
} catch {
|
|
926
|
+
console.log(
|
|
927
|
+
chalk.dim("\n No local database found yet. Use synapse_learn to add plans.")
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
var init_stats = __esm({
|
|
932
|
+
"src/cli/stats.ts"() {
|
|
933
|
+
init_tracker();
|
|
934
|
+
init_manager();
|
|
935
|
+
init_local_provider();
|
|
936
|
+
init_config();
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
// src/index.ts
|
|
941
|
+
var command = process.argv[2];
|
|
942
|
+
if (!command) {
|
|
943
|
+
const { startMcpServer: startMcpServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
944
|
+
await startMcpServer2();
|
|
945
|
+
} else {
|
|
946
|
+
switch (command) {
|
|
947
|
+
case "dashboard": {
|
|
948
|
+
const { dashboardCommand: dashboardCommand2 } = await Promise.resolve().then(() => (init_dashboard(), dashboard_exports));
|
|
949
|
+
await dashboardCommand2();
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
case "wallet": {
|
|
953
|
+
const { walletCommand: walletCommand2 } = await Promise.resolve().then(() => (init_wallet(), wallet_exports));
|
|
954
|
+
await walletCommand2();
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
case "fund": {
|
|
958
|
+
const { fundCommand: fundCommand2 } = await Promise.resolve().then(() => (init_fund(), fund_exports));
|
|
959
|
+
await fundCommand2();
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
case "stats": {
|
|
963
|
+
const { statsCommand: statsCommand2 } = await Promise.resolve().then(() => (init_stats(), stats_exports));
|
|
964
|
+
await statsCommand2();
|
|
965
|
+
break;
|
|
966
|
+
}
|
|
967
|
+
default:
|
|
968
|
+
console.error(`Unknown command: ${command}`);
|
|
969
|
+
printUsage();
|
|
970
|
+
process.exit(1);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function printUsage() {
|
|
974
|
+
console.log(`
|
|
975
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
976
|
+
\u2551 SYNAPSE MCP \u2551
|
|
977
|
+
\u2551 AI Knowledge Oracle on Stellar \u2551
|
|
978
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
979
|
+
|
|
980
|
+
Usage: synapse-mcp [command]
|
|
981
|
+
|
|
982
|
+
Commands:
|
|
983
|
+
(none) Start MCP server (stdio mode)
|
|
984
|
+
dashboard Show wallet, contributions, and usage
|
|
985
|
+
wallet Print wallet address and balance
|
|
986
|
+
fund Fund wallet via Friendbot (testnet)
|
|
987
|
+
stats Show usage and contribution stats
|
|
988
|
+
|
|
989
|
+
MCP Tools:
|
|
990
|
+
synapse_search Search plans (0.2 XLM)
|
|
991
|
+
synapse_recall Retrieve full plan (1 XLM)
|
|
992
|
+
synapse_learn Upload a plan (free)
|
|
993
|
+
|
|
994
|
+
Environment:
|
|
995
|
+
STELLAR_SECRET_KEY Use existing Stellar key
|
|
996
|
+
SYNAPSE_CONFIG_DIR Custom config directory
|
|
997
|
+
SYNAPSE_DATA_DIR Custom data directory
|
|
998
|
+
SYNAPSE_PLATFORM_ADDRESS Override platform address
|
|
999
|
+
`);
|
|
1000
|
+
}
|
|
1001
|
+
//# sourceMappingURL=index.js.map
|
|
1002
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/wallet/manager.ts","../src/storage/db/schema.ts","../src/storage/db/plans.ts","../src/storage/local-provider.ts","../src/history/tracker.ts","../src/payments/stellar.ts","../src/mcp/server.ts","../src/cli/dashboard.ts","../src/cli/wallet.ts","../src/cli/fund.ts","../src/cli/stats.ts","../src/index.ts"],"names":["mkdirSync","dirname","existsSync","readFileSync","writeFileSync","chalk","startMcpServer","dashboardCommand","walletCommand","fundCommand","statsCommand"],"mappings":"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","file":"index.js","sourcesContent":["import { join } from \"path\";\nimport { homedir } from \"os\";\n\nconst xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), \".config\");\nconst xdgData = process.env.XDG_DATA_HOME || join(homedir(), \".local\", \"share\");\n\nexport const CONFIG_DIR =\n process.env.SYNAPSE_CONFIG_DIR || join(xdgConfig, \"synapse-mcp\");\n\nexport const DATA_DIR =\n process.env.SYNAPSE_DATA_DIR || join(xdgData, \"synapse-mcp\");\n\nexport const DB_PATH = join(DATA_DIR, \"kb.db\");\nexport const WALLET_PATH = join(CONFIG_DIR, \"wallet.json\");\nexport const HISTORY_PATH = join(DATA_DIR, \"history.json\");\n\nexport const NETWORK = \"stellar-testnet\";\n\n// Hardcoded Synapse platform address (testnet)\nexport const PLATFORM_ADDRESS =\n process.env.SYNAPSE_PLATFORM_ADDRESS ||\n \"GC63PSERYMUUUJKYSSFQ7FKRAU5UPIP3XUC6X7DLMZUB7SSCPW5BSIRT\";\n\nexport const SEARCH_COST_XLM = \"0.2\";\nexport const RECALL_COST_XLM = \"1\";\n","import { Keypair } from \"@stellar/stellar-sdk\";\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\";\nimport { dirname } from \"path\";\nimport { WALLET_PATH, CONFIG_DIR } from \"../config.js\";\n\ninterface WalletFile {\n publicKey: string;\n secretKey: string;\n network: string;\n createdAt: string;\n}\n\nexport function loadOrCreateWallet(): Keypair {\n const envSecret = process.env.STELLAR_SECRET_KEY;\n if (envSecret) {\n return Keypair.fromSecret(envSecret);\n }\n\n if (existsSync(WALLET_PATH)) {\n const data = JSON.parse(\n readFileSync(WALLET_PATH, \"utf-8\"),\n ) as WalletFile;\n return Keypair.fromSecret(data.secretKey);\n }\n\n mkdirSync(CONFIG_DIR, { recursive: true });\n const keypair = Keypair.random();\n const walletData: WalletFile = {\n publicKey: keypair.publicKey(),\n secretKey: keypair.secret(),\n network: \"stellar-testnet\",\n createdAt: new Date().toISOString(),\n };\n writeFileSync(WALLET_PATH, JSON.stringify(walletData, null, 2), {\n mode: 0o600,\n });\n\n return keypair;\n}\n\nexport async function fundWithFriendbot(\n publicKey: string,\n): Promise<boolean> {\n const url = `https://friendbot.stellar.org?addr=${encodeURIComponent(publicKey)}`;\n const res = await fetch(url);\n return res.ok;\n}\n\nexport async function getBalance(publicKey: string): Promise<string> {\n const url = `https://horizon-testnet.stellar.org/accounts/${publicKey}`;\n const res = await fetch(url);\n if (!res.ok) {\n if (res.status === 404) return \"0 (account not funded)\";\n return \"unknown\";\n }\n const data = (await res.json()) as {\n balances: Array<{ asset_type: string; balance: string }>;\n };\n const native = data.balances.find((b) => b.asset_type === \"native\");\n return native ? `${native.balance} XLM` : \"0 XLM\";\n}\n\nexport function getWalletInfo(): {\n publicKey: string;\n secretPresent: boolean;\n source: string;\n} {\n const envSecret = process.env.STELLAR_SECRET_KEY;\n if (envSecret) {\n const kp = Keypair.fromSecret(envSecret);\n return { publicKey: kp.publicKey(), secretPresent: true, source: \"env\" };\n }\n\n if (existsSync(WALLET_PATH)) {\n mkdirSync(dirname(WALLET_PATH), { recursive: true });\n const data = JSON.parse(\n readFileSync(WALLET_PATH, \"utf-8\"),\n ) as WalletFile;\n return {\n publicKey: data.publicKey,\n secretPresent: true,\n source: \"file\",\n };\n }\n\n return { publicKey: \"\", secretPresent: false, source: \"none\" };\n}\n","import Database from \"better-sqlite3\";\nimport { mkdirSync } from \"fs\";\nimport { dirname } from \"path\";\n\nexport function initDatabase(dbPath: string): Database.Database {\n mkdirSync(dirname(dbPath), { recursive: true });\n\n const db = new Database(dbPath);\n\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n\n db.exec(`\n CREATE TABLE IF NOT EXISTS plans (\n id TEXT PRIMARY KEY,\n title TEXT NOT NULL,\n description TEXT NOT NULL,\n content TEXT NOT NULL,\n content_hash TEXT NOT NULL UNIQUE,\n tags TEXT NOT NULL DEFAULT '[]',\n domain TEXT,\n language TEXT,\n framework TEXT,\n contributor_address TEXT NOT NULL,\n quality_score REAL NOT NULL DEFAULT 0,\n purchase_count INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE VIRTUAL TABLE IF NOT EXISTS plans_fts USING fts5(\n title,\n description,\n tags,\n content,\n content=plans,\n content_rowid=rowid\n );\n\n CREATE TRIGGER IF NOT EXISTS plans_ai AFTER INSERT ON plans BEGIN\n INSERT INTO plans_fts(rowid, title, description, tags, content)\n VALUES (new.rowid, new.title, new.description, new.tags, new.content);\n END;\n\n CREATE TRIGGER IF NOT EXISTS plans_ad AFTER DELETE ON plans BEGIN\n INSERT INTO plans_fts(plans_fts, rowid, title, description, tags, content)\n VALUES ('delete', old.rowid, old.title, old.description, old.tags, old.content);\n END;\n\n CREATE TRIGGER IF NOT EXISTS plans_au AFTER UPDATE ON plans BEGIN\n INSERT INTO plans_fts(plans_fts, rowid, title, description, tags, content)\n VALUES ('delete', old.rowid, old.title, old.description, old.tags, old.content);\n INSERT INTO plans_fts(rowid, title, description, tags, content)\n VALUES (new.rowid, new.title, new.description, new.tags, new.content);\n END;\n\n CREATE TABLE IF NOT EXISTS purchases (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n plan_id TEXT NOT NULL REFERENCES plans(id),\n buyer_address TEXT NOT NULL,\n amount_stroops INTEGER NOT NULL,\n contributor_share_stroops INTEGER NOT NULL,\n operator_share_stroops INTEGER NOT NULL,\n transaction_hash TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE INDEX IF NOT EXISTS idx_purchases_plan_id ON purchases(plan_id);\n CREATE INDEX IF NOT EXISTS idx_purchases_buyer ON purchases(buyer_address);\n CREATE INDEX IF NOT EXISTS idx_plans_contributor ON plans(contributor_address);\n CREATE INDEX IF NOT EXISTS idx_plans_content_hash ON plans(content_hash);\n `);\n\n return db;\n}\n","import type Database from \"better-sqlite3\";\nimport { createHash, randomUUID } from \"crypto\";\nimport type {\n Plan,\n PlanMeta,\n PlanSearchResult,\n Purchase,\n ContributorStats,\n KBStats,\n} from \"../../types.js\";\n\nconst CONTRIBUTOR_SHARE = 0.7;\n\nexport class PlanStore {\n constructor(private db: Database.Database) {}\n\n insert(plan: {\n title: string;\n description: string;\n content: string;\n tags: string[];\n domain?: string;\n language?: string;\n framework?: string;\n contributor_address: string;\n }): Plan {\n const id = randomUUID();\n const content_hash = createHash(\"sha256\")\n .update(plan.content)\n .digest(\"hex\");\n const tagsJson = JSON.stringify(plan.tags);\n\n const stmt = this.db.prepare(`\n INSERT INTO plans (id, title, description, content, content_hash, tags, domain, language, framework, contributor_address)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n stmt.run(\n id,\n plan.title,\n plan.description,\n plan.content,\n content_hash,\n tagsJson,\n plan.domain || null,\n plan.language || null,\n plan.framework || null,\n plan.contributor_address,\n );\n\n return this.getById(id)!;\n }\n\n getById(id: string): Plan | null {\n const stmt = this.db.prepare(\"SELECT * FROM plans WHERE id = ?\");\n return (stmt.get(id) as Plan) || null;\n }\n\n getMeta(id: string): PlanMeta | null {\n const plan = this.db\n .prepare(\n \"SELECT id, title, description, tags, domain, language, framework, quality_score, purchase_count, created_at FROM plans WHERE id = ?\",\n )\n .get(id) as Plan | undefined;\n\n if (!plan) return null;\n\n return {\n ...plan,\n tags: JSON.parse(plan.tags) as string[],\n };\n }\n\n search(query: string, tags?: string[], limit = 20): PlanSearchResult[] {\n let sql: string;\n const params: unknown[] = [];\n\n if (query && query.trim()) {\n sql = `\n SELECT p.id, p.title, p.description, p.tags, p.domain, p.quality_score, p.purchase_count,\n bm25(plans_fts) as rank\n FROM plans_fts fts\n JOIN plans p ON p.rowid = fts.rowid\n WHERE plans_fts MATCH ?\n `;\n params.push(query);\n } else {\n sql = `\n SELECT p.id, p.title, p.description, p.tags, p.domain, p.quality_score, p.purchase_count,\n 0 as rank\n FROM plans p\n WHERE 1=1\n `;\n }\n\n if (tags && tags.length > 0) {\n for (const tag of tags) {\n sql += \" AND p.tags LIKE ?\";\n params.push(`%${tag}%`);\n }\n }\n\n sql +=\n query && query.trim()\n ? \" ORDER BY rank LIMIT ?\"\n : \" ORDER BY p.purchase_count DESC, p.created_at DESC LIMIT ?\";\n params.push(limit);\n\n const rows = this.db.prepare(sql).all(...params) as Array<\n Plan & { rank: number }\n >;\n\n return rows.map((row) => ({\n id: row.id,\n title: row.title,\n description: row.description,\n tags: JSON.parse(row.tags) as string[],\n domain: row.domain,\n quality_score: row.quality_score,\n purchase_count: row.purchase_count,\n rank: row.rank,\n }));\n }\n\n recordPurchase(\n planId: string,\n buyerAddress: string,\n amountStroops: number,\n transactionHash: string | null,\n ): Purchase {\n const contributorShare = Math.floor(amountStroops * CONTRIBUTOR_SHARE);\n const operatorShare = amountStroops - contributorShare;\n\n this.db\n .prepare(\n `\n INSERT INTO purchases (plan_id, buyer_address, amount_stroops, contributor_share_stroops, operator_share_stroops, transaction_hash)\n VALUES (?, ?, ?, ?, ?, ?)\n `,\n )\n .run(\n planId,\n buyerAddress,\n amountStroops,\n contributorShare,\n operatorShare,\n transactionHash,\n );\n\n this.db\n .prepare(\n \"UPDATE plans SET purchase_count = purchase_count + 1 WHERE id = ?\",\n )\n .run(planId);\n\n return {\n plan_id: planId,\n buyer_address: buyerAddress,\n amount_stroops: amountStroops,\n contributor_share_stroops: contributorShare,\n operator_share_stroops: operatorShare,\n transaction_hash: transactionHash,\n created_at: new Date().toISOString(),\n };\n }\n\n getContributorStats(address: string): ContributorStats {\n const stats = this.db\n .prepare(\n `\n SELECT\n ? as contributor_address,\n (SELECT COUNT(*) FROM plans WHERE contributor_address = ?) as plans_count,\n COALESCE((SELECT SUM(contributor_share_stroops) FROM purchases p JOIN plans pl ON p.plan_id = pl.id WHERE pl.contributor_address = ?), 0) as total_earned_stroops,\n COALESCE((SELECT COUNT(*) FROM purchases p JOIN plans pl ON p.plan_id = pl.id WHERE pl.contributor_address = ?), 0) as total_purchases\n `,\n )\n .get(address, address, address, address) as ContributorStats;\n\n return stats;\n }\n\n getKBStats(): KBStats {\n const totalPlans = (\n this.db\n .prepare(\"SELECT COUNT(*) as count FROM plans\")\n .get() as { count: number }\n ).count;\n const totalPurchases = (\n this.db\n .prepare(\"SELECT COUNT(*) as count FROM purchases\")\n .get() as { count: number }\n ).count;\n const totalContributors = (\n this.db\n .prepare(\n \"SELECT COUNT(DISTINCT contributor_address) as count FROM plans\",\n )\n .get() as { count: number }\n ).count;\n\n const plans = this.db\n .prepare(\"SELECT tags FROM plans\")\n .all() as Array<{ tags: string }>;\n const tagCounts: Record<string, number> = {};\n for (const row of plans) {\n const tags = JSON.parse(row.tags) as string[];\n for (const tag of tags) {\n tagCounts[tag] = (tagCounts[tag] || 0) + 1;\n }\n }\n\n const topTags = Object.entries(tagCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([tag, count]) => ({ tag, count }));\n\n return {\n total_plans: totalPlans,\n total_purchases: totalPurchases,\n total_contributors: totalContributors,\n top_tags: topTags,\n };\n }\n\n contentHashExists(content: string): boolean {\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\n const row = this.db\n .prepare(\"SELECT 1 FROM plans WHERE content_hash = ?\")\n .get(hash);\n return !!row;\n }\n\n getContentHash(planId: string): string | null {\n const plan = this.db\n .prepare(\"SELECT content_hash FROM plans WHERE id = ?\")\n .get(planId) as { content_hash: string } | undefined;\n return plan?.content_hash || null;\n }\n}\n","import type {\n Plan,\n PlanMeta,\n PlanSearchResult,\n Purchase,\n ContributorStats,\n KBStats,\n StorePlanInput,\n} from \"../types.js\";\nimport type { StorageProvider, SearchOptions, StoreOptions } from \"./provider.js\";\nimport { initDatabase } from \"./db/schema.js\";\nimport { PlanStore } from \"./db/plans.js\";\n\nconst SOROBAN_ERROR =\n \"Soroban oracle not available in local mode. Coming in v2.\";\n\nexport class LocalStorageProvider implements StorageProvider {\n private planStore: PlanStore;\n private db: ReturnType<typeof initDatabase>;\n\n constructor(dbPath: string) {\n this.db = initDatabase(dbPath);\n this.planStore = new PlanStore(this.db);\n }\n\n // === Core CRUD ===\n\n async store(plan: StorePlanInput, _options?: StoreOptions): Promise<Plan> {\n const description =\n plan.description ||\n plan.content.slice(0, 200).replace(/\\n/g, \" \").trim();\n\n return this.planStore.insert({\n title: plan.title,\n description,\n content: plan.content,\n tags: plan.tags,\n domain: plan.domain,\n language: plan.language,\n framework: plan.framework,\n contributor_address: plan.contributor_address,\n });\n }\n\n async getById(id: string): Promise<Plan | null> {\n return this.planStore.getById(id);\n }\n\n async getMeta(id: string): Promise<PlanMeta | null> {\n return this.planStore.getMeta(id);\n }\n\n async search(options: SearchOptions): Promise<PlanSearchResult[]> {\n return this.planStore.search(options.query, options.tags, options.limit);\n }\n\n async contentExists(content: string): Promise<boolean> {\n return this.planStore.contentHashExists(content);\n }\n\n // === Payment / Purchase Tracking ===\n\n async recordPurchase(\n planId: string,\n buyerAddress: string,\n amountStroops: number,\n txHash: string | null,\n ): Promise<Purchase> {\n return this.planStore.recordPurchase(\n planId,\n buyerAddress,\n amountStroops,\n txHash,\n );\n }\n\n async getContributorStats(address: string): Promise<ContributorStats> {\n return this.planStore.getContributorStats(address);\n }\n\n async getKBStats(): Promise<KBStats> {\n return this.planStore.getKBStats();\n }\n\n // === On-Chain Oracle (Soroban) - V2 ===\n\n async publishToChain(\n _planId: string,\n ): Promise<{ txHash: string; contractId: string }> {\n throw new Error(SOROBAN_ERROR);\n }\n\n async verifyIntegrity(\n _planId: string,\n ): Promise<{ verified: boolean; onChainHash: string; localHash: string }> {\n throw new Error(SOROBAN_ERROR);\n }\n\n async syncFromChain(_contractId: string): Promise<Plan[]> {\n throw new Error(SOROBAN_ERROR);\n }\n\n async getOnChainMeta(_planId: string): Promise<PlanMeta | null> {\n throw new Error(SOROBAN_ERROR);\n }\n\n // === Content Addressing ===\n\n async getContentHash(planId: string): Promise<string> {\n const hash = this.planStore.getContentHash(planId);\n if (!hash) throw new Error(`Plan not found: ${planId}`);\n return hash;\n }\n\n // === Lifecycle ===\n\n async close(): Promise<void> {\n this.db.close();\n }\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\";\nimport { dirname } from \"path\";\nimport { HISTORY_PATH } from \"../config.js\";\n\ninterface HistoryEntry {\n action: \"search\" | \"recall\" | \"learn\";\n query?: string;\n planId?: string;\n txHash?: string;\n costXlm?: number;\n timestamp: string;\n}\n\nconst MAX_ENTRIES = 1000;\n\nexport function appendHistory(\n entry: Omit<HistoryEntry, \"timestamp\">,\n): void {\n mkdirSync(dirname(HISTORY_PATH), { recursive: true });\n\n let history: HistoryEntry[] = [];\n if (existsSync(HISTORY_PATH)) {\n try {\n history = JSON.parse(\n readFileSync(HISTORY_PATH, \"utf-8\"),\n ) as HistoryEntry[];\n } catch {\n history = [];\n }\n }\n\n history.push({ ...entry, timestamp: new Date().toISOString() });\n\n if (history.length > MAX_ENTRIES) {\n history = history.slice(-MAX_ENTRIES);\n }\n\n writeFileSync(HISTORY_PATH, JSON.stringify(history, null, 2));\n}\n\nexport function getHistory(): HistoryEntry[] {\n if (!existsSync(HISTORY_PATH)) return [];\n try {\n return JSON.parse(\n readFileSync(HISTORY_PATH, \"utf-8\"),\n ) as HistoryEntry[];\n } catch {\n return [];\n }\n}\n","import {\n Keypair,\n TransactionBuilder,\n Networks,\n Operation,\n Asset,\n Horizon,\n} from \"@stellar/stellar-sdk\";\n\nconst HORIZON_URL = \"https://horizon-testnet.stellar.org\";\n\nexport interface PaymentResult {\n success: boolean;\n txHash: string | null;\n error?: string;\n}\n\nexport async function submitPayment(\n fromKeypair: Keypair,\n toAddress: string,\n amountXLM: string,\n): Promise<PaymentResult> {\n const server = new Horizon.Server(HORIZON_URL);\n\n try {\n const account = await server.loadAccount(fromKeypair.publicKey());\n\n const transaction = new TransactionBuilder(account, {\n fee: \"100\",\n networkPassphrase: Networks.TESTNET,\n })\n .addOperation(\n Operation.payment({\n destination: toAddress,\n asset: Asset.native(),\n amount: amountXLM,\n }),\n )\n .setTimeout(30)\n .build();\n\n transaction.sign(fromKeypair);\n\n const result = await server.submitTransaction(transaction);\n const hash =\n typeof result === \"object\" && result !== null && \"hash\" in result\n ? (result as { hash: string }).hash\n : null;\n\n return { success: true, txHash: hash };\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Unknown payment error\";\n return { success: false, txHash: null, error: message };\n }\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { loadOrCreateWallet, getBalance, fundWithFriendbot } from \"../wallet/manager.js\";\nimport { LocalStorageProvider } from \"../storage/local-provider.js\";\nimport { appendHistory } from \"../history/tracker.js\";\nimport { submitPayment } from \"../payments/stellar.js\";\nimport {\n DB_PATH,\n PLATFORM_ADDRESS,\n SEARCH_COST_XLM,\n RECALL_COST_XLM,\n} from \"../config.js\";\n\nexport async function startMcpServer(): Promise<void> {\n const keypair = loadOrCreateWallet();\n const publicKey = keypair.publicKey();\n const storage = new LocalStorageProvider(DB_PATH);\n\n // Auto-fund on testnet if new wallet\n try {\n const bal = await getBalance(publicKey);\n if (bal.includes(\"not funded\")) {\n await fundWithFriendbot(publicKey);\n }\n } catch {\n // Non-fatal\n }\n\n const server = new McpServer({\n name: \"synapse-mcp\",\n version: \"0.1.0\",\n });\n\n // Tool: synapse_search (0.2 XLM)\n server.tool(\n \"synapse_search\",\n \"Search the Synapse knowledge base for implementation plans, patterns, and solutions contributed by AI agents and developers. Returns ranked results using full-text search with BM25 scoring. Each search costs 0.2 XLM paid via Stellar testnet. Example: synapse_search({ query: 'kubernetes deployment', tags: ['k8s'] })\",\n {\n query: z\n .string()\n .describe(\n \"Search query (e.g., 'kubernetes deployment', 'auth flow')\",\n ),\n tags: z\n .array(z.string())\n .optional()\n .describe(\"Filter by tags (e.g., ['k8s', 'auth'])\"),\n },\n async ({ query, tags }) => {\n try {\n // Submit payment\n const payment = await submitPayment(\n keypair,\n PLATFORM_ADDRESS,\n SEARCH_COST_XLM,\n );\n\n if (!payment.success) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Payment failed: ${payment.error}\\nEnsure your wallet is funded. Run: synapse-mcp fund`,\n },\n ],\n isError: true,\n };\n }\n\n const results = await storage.search({ query, tags });\n appendHistory({\n action: \"search\",\n query,\n txHash: payment.txHash || undefined,\n costXlm: 0.2,\n });\n\n const balance = await getBalance(publicKey).catch(() => \"unknown\");\n\n const text =\n results.length === 0\n ? `No plans found for \"${query}\".`\n : results\n .map(\n (r, i) =>\n `${i + 1}. **${r.title}** (id: ${r.id})\\n ${r.description}\\n Tags: ${r.tags.join(\", \")} | Score: ${r.quality_score} | Purchases: ${r.purchase_count}`,\n )\n .join(\"\\n\\n\");\n\n return {\n content: [\n { type: \"text\" as const, text },\n {\n type: \"text\" as const,\n text: `\\nCost: ${SEARCH_COST_XLM} XLM | Tx: ${payment.txHash || \"n/a\"}\\nWallet: ${publicKey} | Balance: ${balance}`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Search error: ${(err as Error).message}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n\n // Tool: synapse_recall (1 XLM)\n server.tool(\n \"synapse_recall\",\n \"Retrieve the full content of a specific plan from the Synapse knowledge base. Use the plan ID from synapse_search results. Returns complete markdown content including implementation details, code examples, and architectural decisions. Each retrieval costs 1 XLM. Example: synapse_recall({ id: 'abc-123-def' })\",\n {\n id: z\n .string()\n .describe(\"Plan ID (UUID from search results)\"),\n },\n async ({ id }) => {\n try {\n const plan = await storage.getById(id);\n if (!plan) {\n return {\n content: [\n { type: \"text\" as const, text: `Plan not found: ${id}` },\n ],\n isError: true,\n };\n }\n\n // Submit payment\n const payment = await submitPayment(\n keypair,\n PLATFORM_ADDRESS,\n RECALL_COST_XLM,\n );\n\n if (!payment.success) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Payment failed: ${payment.error}\\nEnsure your wallet is funded. Run: synapse-mcp fund`,\n },\n ],\n isError: true,\n };\n }\n\n // Record purchase\n await storage.recordPurchase(\n id,\n publicKey,\n 10_000_000, // 1 XLM in stroops\n payment.txHash,\n );\n\n appendHistory({\n action: \"recall\",\n planId: id,\n txHash: payment.txHash || undefined,\n costXlm: 1,\n });\n\n const balance = await getBalance(publicKey).catch(() => \"unknown\");\n const tags = JSON.parse(plan.tags) as string[];\n\n const text = [\n `# ${plan.title}`,\n \"\",\n `**Domain**: ${plan.domain || \"general\"} | **Tags**: ${tags.join(\", \")}`,\n `**Contributor**: ${plan.contributor_address}`,\n `**Content Hash**: ${plan.content_hash}`,\n \"\",\n plan.content,\n ].join(\"\\n\");\n\n return {\n content: [\n { type: \"text\" as const, text },\n {\n type: \"text\" as const,\n text: `\\nCost: ${RECALL_COST_XLM} XLM | Tx: ${payment.txHash || \"n/a\"}\\nWallet: ${publicKey} | Balance: ${balance}`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Recall error: ${(err as Error).message}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n\n // Tool: synapse_learn (free)\n server.tool(\n \"synapse_learn\",\n \"Upload a new implementation plan to the Synapse knowledge base. Share your learnings, patterns, and solutions so other AI agents can benefit. Plans are content-addressed (SHA-256) to prevent duplicates. Contributors earn 70% of future retrieval fees when other agents access their plans. Example: synapse_learn({ title: 'Auth with NextAuth', content: '# Setup...', tags: ['auth', 'nextjs'] })\",\n {\n title: z\n .string()\n .min(3)\n .max(200)\n .describe(\"Plan title\"),\n content: z\n .string()\n .min(10)\n .describe(\"Full plan content (markdown)\"),\n tags: z\n .array(z.string())\n .describe(\n \"Tags for discoverability (e.g., ['auth', 'nextjs'])\",\n ),\n domain: z\n .string()\n .optional()\n .describe(\"Domain (e.g., 'web', 'devops', 'ml')\"),\n language: z\n .string()\n .optional()\n .describe(\"Programming language\"),\n framework: z\n .string()\n .optional()\n .describe(\"Framework used\"),\n },\n async ({ title, content, tags, domain, language, framework }) => {\n try {\n // Check for duplicate content\n const exists = await storage.contentExists(content);\n if (exists) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Duplicate content - a plan with identical content already exists.\",\n },\n ],\n isError: true,\n };\n }\n\n const plan = await storage.store({\n title,\n content,\n tags,\n domain,\n language,\n framework,\n contributor_address: publicKey,\n });\n\n appendHistory({ action: \"learn\", planId: plan.id });\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: [\n \"Plan stored successfully!\",\n \"\",\n `ID: ${plan.id}`,\n `Title: ${plan.title}`,\n `Content Hash: ${plan.content_hash}`,\n `Contributor: ${publicKey}`,\n \"\",\n \"Revenue: You earn 70% of future retrieval fees when other agents access this plan.\",\n ].join(\"\\n\"),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Learn error: ${(err as Error).message}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","import chalk from \"chalk\";\nimport { getWalletInfo, getBalance } from \"../wallet/manager.js\";\nimport { getHistory } from \"../history/tracker.js\";\nimport { LocalStorageProvider } from \"../storage/local-provider.js\";\nimport { DB_PATH } from \"../config.js\";\n\nexport async function dashboardCommand(): Promise<void> {\n const info = getWalletInfo();\n const history = getHistory();\n\n console.log(\n chalk.bold.cyan(`\n ╔═══════════════════════════════════════╗\n ║ SYNAPSE MCP Dashboard ║\n ║ AI Knowledge Oracle on Stellar ║\n ╚═══════════════════════════════════════╝\n`),\n );\n\n // Wallet section\n console.log(chalk.bold(\" WALLET\"));\n if (info.secretPresent) {\n console.log(` Address: ${chalk.cyan(info.publicKey)}`);\n console.log(\n ` Source: ${info.source === \"env\" ? \"env var\" : \"wallet.json\"}`,\n );\n try {\n const balance = await getBalance(info.publicKey);\n console.log(` Balance: ${chalk.green(balance)}`);\n } catch {\n console.log(` Balance: ${chalk.red(\"unavailable\")}`);\n }\n } else {\n console.log(\n chalk.yellow(\n \" No wallet configured. Run MCP server to auto-generate.\",\n ),\n );\n }\n\n // Usage section\n console.log(\"\");\n console.log(chalk.bold(\" USAGE\"));\n const searches = history.filter((h) => h.action === \"search\").length;\n const recalls = history.filter((h) => h.action === \"recall\").length;\n const learns = history.filter((h) => h.action === \"learn\").length;\n console.log(` Searches: ${searches}`);\n console.log(` Recalls: ${recalls}`);\n console.log(` Learns: ${learns}`);\n console.log(\n ` Spent: ~${(searches * 0.2 + recalls * 1).toFixed(1)} XLM`,\n );\n\n // KB Stats\n try {\n const storage = new LocalStorageProvider(DB_PATH);\n const stats = await storage.getKBStats();\n console.log(\"\");\n console.log(chalk.bold(\" KNOWLEDGE BASE\"));\n console.log(` Plans: ${stats.total_plans}`);\n console.log(` Purchases: ${stats.total_purchases}`);\n console.log(` Contributors: ${stats.total_contributors}`);\n if (stats.top_tags.length > 0) {\n console.log(\n ` Top Tags: ${stats.top_tags.map((t) => t.tag).join(\", \")}`,\n );\n }\n\n // Contributor stats\n if (info.secretPresent) {\n const cStats = await storage.getContributorStats(info.publicKey);\n if (cStats.plans_count > 0) {\n console.log(\"\");\n console.log(chalk.bold(\" CONTRIBUTIONS\"));\n console.log(` Plans: ${cStats.plans_count}`);\n console.log(` Purchases: ${cStats.total_purchases}`);\n console.log(\n ` Earned: ${chalk.green((cStats.total_earned_stroops / 10_000_000).toFixed(2) + \" XLM\")}`,\n );\n }\n }\n\n await storage.close();\n } catch {\n // DB may not exist yet\n }\n\n // Recent activity\n if (history.length > 0) {\n console.log(\"\");\n console.log(chalk.bold(\" RECENT ACTIVITY\"));\n const recent = history.slice(-5).reverse();\n for (const entry of recent) {\n const time = new Date(entry.timestamp).toLocaleString();\n const desc =\n entry.action === \"search\"\n ? `search: \"${entry.query}\"`\n : entry.action === \"recall\"\n ? `recall: ${entry.planId}`\n : `learn: ${entry.planId}`;\n console.log(` ${chalk.dim(time)} ${desc}`);\n }\n }\n\n console.log(\"\");\n}\n","import chalk from \"chalk\";\nimport { getWalletInfo, getBalance } from \"../wallet/manager.js\";\nimport { WALLET_PATH } from \"../config.js\";\n\nexport async function walletCommand(): Promise<void> {\n const info = getWalletInfo();\n\n if (!info.secretPresent) {\n console.log(\n chalk.yellow(\n \"No wallet found. Run the MCP server once to auto-generate one.\",\n ),\n );\n return;\n }\n\n console.log(chalk.bold(\"Synapse Wallet\"));\n console.log(` Address: ${chalk.cyan(info.publicKey)}`);\n console.log(\n ` Source: ${info.source === \"env\" ? \"STELLAR_SECRET_KEY env var\" : WALLET_PATH}`,\n );\n console.log(` Network: stellar-testnet`);\n\n try {\n const balance = await getBalance(info.publicKey);\n console.log(` Balance: ${chalk.green(balance)}`);\n } catch {\n console.log(` Balance: ${chalk.red(\"unable to fetch\")}`);\n }\n}\n","import chalk from \"chalk\";\nimport {\n loadOrCreateWallet,\n fundWithFriendbot,\n getBalance,\n} from \"../wallet/manager.js\";\n\nexport async function fundCommand(): Promise<void> {\n const keypair = loadOrCreateWallet();\n const publicKey = keypair.publicKey();\n\n console.log(chalk.bold(\"Funding wallet via Friendbot (testnet)...\"));\n console.log(` Address: ${chalk.cyan(publicKey)}`);\n\n const success = await fundWithFriendbot(publicKey);\n\n if (success) {\n const balance = await getBalance(publicKey);\n console.log(chalk.green(` Funded! Balance: ${balance}`));\n } else {\n console.log(\n chalk.yellow(\n \" Friendbot returned an error (wallet may already be funded).\",\n ),\n );\n try {\n const balance = await getBalance(publicKey);\n console.log(` Current balance: ${balance}`);\n } catch {\n // ignore\n }\n }\n}\n","import chalk from \"chalk\";\nimport { getHistory } from \"../history/tracker.js\";\nimport { getWalletInfo } from \"../wallet/manager.js\";\nimport { LocalStorageProvider } from \"../storage/local-provider.js\";\nimport { DB_PATH } from \"../config.js\";\n\nexport async function statsCommand(): Promise<void> {\n const history = getHistory();\n const info = getWalletInfo();\n\n console.log(chalk.bold(\"Synapse Usage Statistics\"));\n console.log(\n ` Searches: ${history.filter((h) => h.action === \"search\").length}`,\n );\n console.log(\n ` Recalls: ${history.filter((h) => h.action === \"recall\").length}`,\n );\n console.log(\n ` Learns: ${history.filter((h) => h.action === \"learn\").length}`,\n );\n console.log(` Total: ${history.length}`);\n\n try {\n const storage = new LocalStorageProvider(DB_PATH);\n const kbStats = await storage.getKBStats();\n\n console.log(\"\");\n console.log(chalk.bold(\"Knowledge Base\"));\n console.log(` Total plans: ${kbStats.total_plans}`);\n console.log(` Total purchases: ${kbStats.total_purchases}`);\n console.log(` Total contributors: ${kbStats.total_contributors}`);\n\n if (info.secretPresent) {\n const cStats = await storage.getContributorStats(info.publicKey);\n console.log(\"\");\n console.log(chalk.bold(\"Your Contributions\"));\n console.log(` Plans uploaded: ${cStats.plans_count}`);\n console.log(` Total purchases: ${cStats.total_purchases}`);\n console.log(\n ` Earnings: ${(cStats.total_earned_stroops / 10_000_000).toFixed(2)} XLM`,\n );\n }\n\n await storage.close();\n } catch {\n console.log(\n chalk.dim(\"\\n No local database found yet. Use synapse_learn to add plans.\"),\n );\n }\n}\n","const command = process.argv[2];\n\nif (!command) {\n // Default: start MCP server (stdio mode)\n const { startMcpServer } = await import(\"./mcp/server.js\");\n await startMcpServer();\n} else {\n switch (command) {\n case \"dashboard\": {\n const { dashboardCommand } = await import(\"./cli/dashboard.js\");\n await dashboardCommand();\n break;\n }\n case \"wallet\": {\n const { walletCommand } = await import(\"./cli/wallet.js\");\n await walletCommand();\n break;\n }\n case \"fund\": {\n const { fundCommand } = await import(\"./cli/fund.js\");\n await fundCommand();\n break;\n }\n case \"stats\": {\n const { statsCommand } = await import(\"./cli/stats.js\");\n await statsCommand();\n break;\n }\n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n}\n\nfunction printUsage(): void {\n console.log(`\n ╔═══════════════════════════════════════╗\n ║ SYNAPSE MCP ║\n ║ AI Knowledge Oracle on Stellar ║\n ╚═══════════════════════════════════════╝\n\n Usage: synapse-mcp [command]\n\n Commands:\n (none) Start MCP server (stdio mode)\n dashboard Show wallet, contributions, and usage\n wallet Print wallet address and balance\n fund Fund wallet via Friendbot (testnet)\n stats Show usage and contribution stats\n\n MCP Tools:\n synapse_search Search plans (0.2 XLM)\n synapse_recall Retrieve full plan (1 XLM)\n synapse_learn Upload a plan (free)\n\n Environment:\n STELLAR_SECRET_KEY Use existing Stellar key\n SYNAPSE_CONFIG_DIR Custom config directory\n SYNAPSE_DATA_DIR Custom data directory\n SYNAPSE_PLATFORM_ADDRESS Override platform address\n`);\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jashwanth0712/synapse-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "AI agent knowledge oracle with Stellar payments. Self-contained MCP server with embedded SQLite, full-text search, and on-chain verification (coming soon).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"synapse-mcp": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./storage": {
|
|
17
|
+
"types": "./dist/storage/provider.d.ts",
|
|
18
|
+
"import": "./dist/storage/provider.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"keywords": [
|
|
27
|
+
"mcp",
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"ai-agents",
|
|
30
|
+
"knowledge-base",
|
|
31
|
+
"stellar",
|
|
32
|
+
"soroban",
|
|
33
|
+
"oracle"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"clean": "rm -rf dist",
|
|
45
|
+
"typecheck": "tsc --noEmit"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
49
|
+
"@stellar/stellar-sdk": "^13.1.0",
|
|
50
|
+
"better-sqlite3": "^11.0.0",
|
|
51
|
+
"chalk": "^5.3.0",
|
|
52
|
+
"zod": "^3.22.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/better-sqlite3": "^7.6.8",
|
|
56
|
+
"tsup": "^8.0.0",
|
|
57
|
+
"typescript": "^5.3.0",
|
|
58
|
+
"vitest": "^1.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|