@sage-protocol/cli 0.3.9 → 0.4.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.
@@ -7,7 +7,7 @@
7
7
  "RPC_URL": "https://sepolia.base.org",
8
8
  "LIBRARY_REGISTRY_ADDRESS": "0x032F2E93549640a319163Ba600fc50bA1AFBE50F",
9
9
  "SUBDAO_FACTORY_ADDRESS": "0x6bb6A83149F84Df0584aC863e5fE3416AFb214aC",
10
- "SUBGRAPH_URL": "https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.0/gn",
10
+ "SUBGRAPH_URL": "https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.1/gn",
11
11
  "PINATA_JWT": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySW5mb3JtYXRpb24iOnsiaWQiOiI4ODE2MWM1Ni05NTYzLTQyNTUtYTU4Ni0xMzBiMTUyMWIxNmIiLCJlbWFpbCI6InRlcnJlbmV0d2VsbHM0N0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicGluX3BvbGljeSI6eyJyZWdpb25zIjpbeyJkZXNpcmVkUmVwbGljYXRpb25Db3VudCI6MSwiaWQiOiJGUkExIn0seyJkZXNpcmVkUmVwbGljYXRpb25Db3VudCI6MSwiaWQiOiJOWUMxIn1dLCJ2ZXJzaW9uIjoxfSwibWZhX2VuYWJsZWQiOmZhbHNlLCJzdGF0dXMiOiJBQ1RJVkUifSwiYXV0aGVudGljYXRpb25UeXBlIjoic2NvcGVkS2V5Iiwic2NvcGVkS2V5S2V5IjoiMDRjZWY4NzE5OTg1ODE1M2U5Y2IiLCJzY29wZWRLZXlTZWNyZXQiOiJmNDFjMDVhZTNjN2EzMTliOTAxZjhiNGI3YjMxMDljMThmNjgyZmRkZGY3OGU1ZmZkZjQ4MTM4OTQ0OWUyOGVjIiwiZXhwIjoxNzk1NTMzNTA4fQ.tQKFU7aZ3EKb7RkB99QTaWVduaaKaKjEa1nk43aeSlo"
12
12
  }
13
13
  }
@@ -2,11 +2,13 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const axios = require('axios');
4
4
  const { getAddress, id, parseUnits, formatUnits, Contract, MaxUint256 } = require('ethers');
5
+ const { decryptAesGcm, fromB64 } = require('../utils/aes');
5
6
  const sdk = require('@sage-protocol/sdk');
6
7
  const { formatJson } = require('../utils/format');
7
8
  const { getProvider, getWallet } = require('../utils/provider');
8
9
  const { loadAbi } = require('../utils/abi-loader');
9
10
  const { buildAccessControlConditions, receiptIdToHex } = require('../utils/personal-helpers');
11
+ const { buildNodeAuthSig } = require('../utils/lit');
10
12
  const ConfigManager = require('../config');
11
13
 
12
14
  if (ConfigManager && typeof ConfigManager.loadEnv === 'function') {
@@ -114,7 +116,7 @@ async function createAction(opts) {
114
116
  try { return contract.interface.parseLog(l); } catch (_) { return null; }
115
117
  }).find(Boolean);
116
118
  const out = {
117
- txHash: receipt.transactionHash,
119
+ txHash: receipt.hash,
118
120
  registry: evt && evt.args ? evt.args.registry : undefined,
119
121
  policy,
120
122
  };
@@ -140,7 +142,7 @@ async function setPriceAction(key, price, opts) {
140
142
  key,
141
143
  price,
142
144
  priceWei: priceWei.toString(),
143
- txHash: receipt.transactionHash,
145
+ txHash: receipt.hash,
144
146
  };
145
147
  if (opts && opts.json) {
146
148
  console.log(JSON.stringify(result, null, 2));
@@ -172,7 +174,7 @@ async function sellAction(key, price, opts = {}) {
172
174
  const tx = await facet.createPersonalRegistry(policy);
173
175
  const rcpt = await tx.wait();
174
176
  if (!opts.json) {
175
- console.log(`📚 Personal registry created (policy=${policy}) -> tx ${rcpt.transactionHash}`);
177
+ console.log(`📚 Personal registry created (policy=${policy}) -> tx ${rcpt.hash}`);
176
178
  }
177
179
  }
178
180
 
@@ -244,10 +246,12 @@ async function sellAction(key, price, opts = {}) {
244
246
  throw new Error('Lit encryption helpers not available. Ensure @lit-protocol/encryption is installed.');
245
247
  }
246
248
 
247
- const litNetwork = opts.litNetwork || process.env.LIT_NETWORK || 'serrano';
248
- const client = new LitNodeClient({ litNetwork });
249
+ const litNetwork = opts.litNetwork || process.env.LIT_NETWORK || 'datil-test';
250
+ const client = new LitNodeClient({ litNetwork, debug: false });
249
251
  await client.connect();
250
252
 
253
+ const litChain = await inferLitChain(provider, opts.chain);
254
+
251
255
  let sessionSigs = null;
252
256
  if (opts.session) {
253
257
  sessionSigs = loadJsonFile(opts.session, '--session');
@@ -261,13 +265,14 @@ async function sellAction(key, price, opts = {}) {
261
265
  authSig = loadJsonFile(opts.authSig, '--auth-sig');
262
266
  } else if (process.env.LIT_AUTH_SIG) {
263
267
  authSig = loadJsonFile(process.env.LIT_AUTH_SIG, 'LIT_AUTH_SIG');
264
- }
265
- if (!authSig) {
266
- throw new Error('Provide Lit session signatures (--session) or authSig (--auth-sig) for encryption.');
268
+ } else {
269
+ // Auto-generate authSig from connected wallet
270
+ if (!opts.json) {
271
+ console.log('🔐 Generating Lit auth signature from wallet...');
272
+ }
273
+ authSig = await buildNodeAuthSig(wallet, litChain);
267
274
  }
268
275
  }
269
-
270
- const litChain = await inferLitChain(provider, opts.chain);
271
276
  const accessConditions = buildAccessControlConditions(receiptAddress, receiptId, litChain);
272
277
 
273
278
  const encResp = await encryptUint8Array({
@@ -328,12 +333,12 @@ async function sellAction(key, price, opts = {}) {
328
333
  const marketplace = new Contract(marketplaceAddress, marketplaceAbi, wallet);
329
334
  const priceTx = await marketplace.setPrice(keyHash, priceWei);
330
335
  const priceReceipt = await priceTx.wait();
331
- result.txHash = priceReceipt.transactionHash;
336
+ result.txHash = priceReceipt.hash;
332
337
 
333
338
  if (opts.json) {
334
339
  console.log(JSON.stringify(result, null, 2));
335
340
  } else {
336
- console.log(`✅ Listed ${key} for ${price} SXXX (${priceReceipt.transactionHash})`);
341
+ console.log(`✅ Listed ${key} for ${price} SXXX (tx: ${priceReceipt.hash})`);
337
342
  if (result.encryptedCid) {
338
343
  console.log(`🔒 Encrypted CID: ${result.encryptedCid}`);
339
344
  console.log(`🗃️ Manifest CID: ${result.manifestCid}`);
@@ -430,7 +435,48 @@ async function listAction(opts = {}) {
430
435
  let personalResourceMap = new Map();
431
436
 
432
437
  if (!explicitKeys.length && subgraphUrl) {
433
- const document = `
438
+ // First try to get all listings (PriceSet events)
439
+ const listingsQuery = `
440
+ query($creator: Bytes!, $first: Int!) {
441
+ personalListings(where: { creator: $creator, listed: true }, first: $first, orderBy: updatedAt, orderDirection: desc) {
442
+ id
443
+ creator
444
+ key
445
+ price
446
+ listed
447
+ createdAt
448
+ updatedAt
449
+ }
450
+ }
451
+ `;
452
+ try {
453
+ const listingsData = await subgraphQuery(subgraphUrl, listingsQuery, {
454
+ creator: creatorAddress.toLowerCase(),
455
+ first: limit,
456
+ });
457
+
458
+ for (const listing of listingsData?.personalListings || []) {
459
+ const keyHash = String(listing.key);
460
+ const idKey = `hash:${keyHash}`;
461
+ if (!entries.has(idKey)) {
462
+ entries.set(idKey, {
463
+ type: 'hash',
464
+ keyHash,
465
+ priceFromListing: listing.price ? BigInt(String(listing.price)) : null,
466
+ listedAt: listing.createdAt ? Number(listing.createdAt) : null,
467
+ updatedAt: listing.updatedAt ? Number(listing.updatedAt) : null,
468
+ });
469
+ }
470
+ }
471
+ } catch (listingError) {
472
+ // PersonalListing entity may not exist yet, fall back to purchases
473
+ if (!listingError.message?.includes('Cannot query field')) {
474
+ console.warn(`⚠️ Listings query: ${listingError.message}`);
475
+ }
476
+ }
477
+
478
+ // Also fetch purchases for additional metadata
479
+ const purchasesQuery = `
434
480
  query($creator: Bytes!, $first: Int!) {
435
481
  licensePurchases(where: { creator: $creator }, first: $first, orderBy: blockTimestamp, orderDirection: desc) {
436
482
  key
@@ -444,7 +490,7 @@ async function listAction(opts = {}) {
444
490
  }
445
491
  `;
446
492
  try {
447
- const data = await subgraphQuery(subgraphUrl, document, {
493
+ const data = await subgraphQuery(subgraphUrl, purchasesQuery, {
448
494
  creator: creatorAddress.toLowerCase(),
449
495
  first: limit,
450
496
  });
@@ -452,7 +498,15 @@ async function listAction(opts = {}) {
452
498
  for (const purchase of data?.licensePurchases || []) {
453
499
  const keyHash = String(purchase.key);
454
500
  const idKey = `hash:${keyHash}`;
455
- if (!entries.has(idKey)) {
501
+ const existing = entries.get(idKey);
502
+ if (existing) {
503
+ // Enrich existing listing with purchase data
504
+ existing.receiptId = purchase.receiptId ? BigInt(String(purchase.receiptId)) : existing.receiptId;
505
+ existing.lastBuyer = purchase.buyer ? getAddress(purchase.buyer) : existing.lastBuyer;
506
+ existing.lastPurchaseAt = purchase.blockTimestamp ? Number(purchase.blockTimestamp) : existing.lastPurchaseAt;
507
+ existing.encryptedCid = purchase.encryptedCid || existing.encryptedCid;
508
+ existing.metadata = purchase.metadata || existing.metadata;
509
+ } else {
456
510
  entries.set(idKey, {
457
511
  type: 'hash',
458
512
  keyHash,
@@ -501,7 +555,10 @@ async function listAction(opts = {}) {
501
555
  }
502
556
 
503
557
  if (!entries.size) {
504
- console.log('ℹ️ No listings found. Provide --keys or ensure listings have been purchased/indexed.');
558
+ console.log('ℹ️ No listings found.');
559
+ console.log(' Use --key <name> to check a specific listing by key.');
560
+ console.log(' Example: sage personal list --mine --key "my-prompt"');
561
+ console.log(' Note: Listings appear in subgraph after first purchase.');
505
562
  return [];
506
563
  }
507
564
 
@@ -678,6 +735,10 @@ async function myLicensesAction(opts = {}) {
678
735
  console.log(JSON.stringify(results, null, 2));
679
736
  } else if (results.length === 0) {
680
737
  console.log('ℹ️ No active personal licenses found for this holder.');
738
+ console.log('');
739
+ console.log(' If you recently purchased a license, the subgraph may still be syncing.');
740
+ console.log(' To check a specific license directly on-chain, use:');
741
+ console.log(' sage personal premium access <creator> <key>');
681
742
  } else {
682
743
  results.forEach((item) => {
683
744
  console.log(`🧾 ${item.receiptIdHex} — balance ${item.balance}`);
@@ -751,7 +812,7 @@ async function buyAction(creator, key, expectedPriceArg, opts = {}) {
751
812
  const approveTx = await sxxx.approve(marketplaceAddress, approveAmount);
752
813
  const approveReceipt = await approveTx.wait();
753
814
  if (!opts.json) {
754
- console.log(`✅ SXXX approval tx ${approveReceipt.transactionHash}`);
815
+ console.log(`✅ SXXX approval tx ${approveReceipt.hash}`);
755
816
  }
756
817
  }
757
818
 
@@ -778,7 +839,7 @@ async function buyAction(creator, key, expectedPriceArg, opts = {}) {
778
839
  : sdk.personal.computeReceiptId(creatorAddress, key);
779
840
 
780
841
  const result = {
781
- txHash: receipt.transactionHash,
842
+ txHash: receipt.hash,
782
843
  creator: creatorAddress,
783
844
  buyer: buyerAddress,
784
845
  key,
@@ -841,6 +902,32 @@ async function accessAction(creator, key, opts) {
841
902
 
842
903
  let encryptedCid = resource?.encryptedCid || null;
843
904
  let metadata = resource?.metadata || null;
905
+ let manifestObj = null;
906
+
907
+ // If --manifest provided, fetch manifest from IPFS and extract encryptedCid
908
+ if (opts.manifest) {
909
+ const gateway = resolveGateway(opts.gateway);
910
+ try {
911
+ if (!opts.json) {
912
+ console.log(`📥 Fetching manifest from IPFS: ${opts.manifest}`);
913
+ }
914
+ const manifestResp = await axios.get(`${gateway}${opts.manifest}`, { timeout: 15000 });
915
+ const manifest = manifestResp.data;
916
+ manifestObj = manifest;
917
+ if (manifest?.encryptedCid) {
918
+ encryptedCid = manifest.encryptedCid;
919
+ if (!opts.json) {
920
+ console.log(`✅ Found encryptedCid in manifest: ${encryptedCid}`);
921
+ }
922
+ }
923
+ if (manifest?.metadata && !metadata) {
924
+ metadata = manifest.metadata;
925
+ }
926
+ } catch (err) {
927
+ console.warn(`⚠️ Failed to fetch manifest: ${err?.message || err}`);
928
+ }
929
+ }
930
+
844
931
  if (!encryptedCid && opts.encryptedCid) {
845
932
  encryptedCid = String(opts.encryptedCid);
846
933
  }
@@ -856,9 +943,12 @@ async function accessAction(creator, key, opts) {
856
943
  if (encryptedPayload && looksLikeCid(encryptedPayload)) {
857
944
  try {
858
945
  const resp = await axios.get(`${gateway}${encryptedPayload}`, { timeout: 10000 });
946
+ if (resp.status !== 200) {
947
+ throw new Error(`Status ${resp.status}`);
948
+ }
859
949
  encryptedPayload = resp.data;
860
950
  } catch (error) {
861
- console.warn(`⚠️ Failed to fetch encrypted payload from IPFS via ${gateway}:`, error?.message || error);
951
+ throw new Error(`Failed to fetch encrypted payload from IPFS via ${gateway}: ${error.message}`);
862
952
  }
863
953
  }
864
954
 
@@ -879,10 +969,12 @@ async function accessAction(creator, key, opts) {
879
969
 
880
970
  if (!opts.skipDecrypt && payloadObject) {
881
971
  const { LitNodeClient } = requireLit();
882
- const litNetwork = opts.litNetwork || process.env.LIT_NETWORK || 'serrano';
883
- const client = new LitNodeClient({ litNetwork });
972
+ const litNetwork = opts.litNetwork || process.env.LIT_NETWORK || 'datil-test';
973
+ const client = new LitNodeClient({ litNetwork, debug: false });
884
974
  await client.connect();
885
975
 
976
+ const litChain = await inferLitChain(provider, opts.chain);
977
+
886
978
  let sessionSigs = null;
887
979
  if (opts.session) {
888
980
  sessionSigs = loadJsonFile(opts.session, '--session');
@@ -891,22 +983,80 @@ async function accessAction(creator, key, opts) {
891
983
  }
892
984
 
893
985
  let authSig = null;
894
- if (opts.authSig) {
895
- authSig = loadJsonFile(opts.authSig, '--auth-sig');
896
- } else if (process.env.LIT_AUTH_SIG) {
897
- authSig = loadJsonFile(process.env.LIT_AUTH_SIG, 'LIT_AUTH_SIG');
986
+ if (!sessionSigs) {
987
+ if (opts.authSig) {
988
+ authSig = loadJsonFile(opts.authSig, '--auth-sig');
989
+ } else if (process.env.LIT_AUTH_SIG) {
990
+ authSig = loadJsonFile(process.env.LIT_AUTH_SIG, 'LIT_AUTH_SIG');
991
+ } else {
992
+ // Auto-generate authSig from connected wallet
993
+ if (!opts.json) {
994
+ console.log('🔐 Generating Lit auth signature from wallet...');
995
+ }
996
+ authSig = await buildNodeAuthSig(wallet, litChain);
997
+ }
898
998
  }
899
999
 
900
- const litChain = await inferLitChain(provider, opts.chain);
901
- decrypted = await sdk.personal.decryptWithLit({
902
- encryptedCid: payloadObject,
903
- receiptId,
904
- chain: litChain,
905
- receiptAddress,
906
- litClient: client,
907
- sessionSigs: sessionSigs || undefined,
908
- authSig: authSig || undefined,
909
- });
1000
+ // Check for Hybrid Encryption (Sage Personal Manifest)
1001
+ if (payloadObject.type === 'sage-personal-encrypted' && payloadObject.enc === 'aes-256-gcm') {
1002
+ if (!manifestObj || !manifestObj.lit) {
1003
+ throw new Error('Hybrid encryption detected but no Manifest provided (use --manifest <cid>).');
1004
+ }
1005
+ if (!opts.json) console.log('🔐 Decrypting hybrid content (Lit + AES-GCM)...');
1006
+
1007
+ const litParams = manifestObj.lit;
1008
+ const condition = litParams.evmContractConditions?.[0] || {
1009
+ conditionType: 'evmContract',
1010
+ contractAddress: receiptAddress,
1011
+ functionName: 'balanceOf',
1012
+ functionParams: [':userAddress', receiptId.toString()],
1013
+ functionAbi: {
1014
+ inputs: [{ internalType: 'address', name: 'account', type: 'address' }, { internalType: 'uint256', name: 'id', type: 'uint256' }],
1015
+ name: 'balanceOf',
1016
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
1017
+ stateMutability: 'view',
1018
+ type: 'function',
1019
+ },
1020
+ chain: litChain,
1021
+ returnValueTest: { comparator: '>', value: '0' },
1022
+ };
1023
+
1024
+ const request = {
1025
+ unifiedAccessControlConditions: [condition],
1026
+ chain: litChain,
1027
+ ciphertext: litParams.encryptedSymmetricKey,
1028
+ dataToEncryptHash: litParams.dataToEncryptHash,
1029
+ authSig,
1030
+ sessionSigs,
1031
+ };
1032
+
1033
+ const response = await client.decrypt(request);
1034
+ if (!response || !response.decryptedData) {
1035
+ throw new Error('[personal] Lit decryption of symmetric key failed');
1036
+ }
1037
+
1038
+ // response.decryptedData is Uint8Array
1039
+ const symmetricKey = response.decryptedData;
1040
+
1041
+ decrypted = decryptAesGcm(
1042
+ fromB64(payloadObject.ciphertext),
1043
+ Buffer.from(symmetricKey),
1044
+ fromB64(payloadObject.iv),
1045
+ fromB64(payloadObject.tag)
1046
+ );
1047
+
1048
+ } else {
1049
+ // Legacy/Direct Lit Decryption
1050
+ decrypted = await sdk.personal.decryptWithLit({
1051
+ encryptedCid: payloadObject,
1052
+ receiptId,
1053
+ chain: litChain,
1054
+ receiptAddress,
1055
+ litClient: client,
1056
+ sessionSigs: sessionSigs || undefined,
1057
+ authSig: authSig || undefined,
1058
+ });
1059
+ }
910
1060
 
911
1061
  if (opts.out) {
912
1062
  outputPath = path.resolve(opts.out);
@@ -1047,6 +1197,7 @@ function register(program) {
1047
1197
  .option('--out <file>', 'Write decrypted output to file')
1048
1198
  .option('--skip-decrypt', 'Skip decryption and only report license status')
1049
1199
  .option('--encrypted-cid <cid>', 'Override encrypted CID')
1200
+ .option('--manifest <cid>', 'Manifest CID to fetch encrypted content info from IPFS')
1050
1201
  .option('--json', 'Emit machine-readable JSON report', false)
1051
1202
  .action((creator, key, opts) => accessAction(creator, key, opts).catch((err) => { console.error(err.message); process.exit(1); }));
1052
1203
 
@@ -18,10 +18,10 @@ Create or update your `.env` file with the required configuration:
18
18
 
19
19
  ```bash
20
20
  # Subgraph (preferred) + Blockchain
21
- SUBGRAPH_URL=https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.0/gn
22
- RPC_URL=https://sepolia.base.org
23
- LIBRARY_REGISTRY_ADDRESS=0x032F2E93549640a319163Ba600fc50bA1AFBE50F
24
- SUBDAO_FACTORY_ADDRESS=0x6bb6A83149F84Df0584aC863e5fE3416AFb214aC
21
+ SUBGRAPH_URL=https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.2/gn
22
+ RPC_URL=https://base-sepolia.publicnode.com
23
+ LIBRARY_REGISTRY_ADDRESS=0x419Cd79d58db6A5aE1900dd7F0c1b8d153FfaD06
24
+ SUBDAO_FACTORY_ADDRESS=0xED573E7e4c00fE325f846B961Df3352807eA3807
25
25
 
26
26
  # Optional: Custom IPFS Gateway
27
27
  IPFS_GATEWAY=https://ipfs.io/ipfs
@@ -65,10 +65,10 @@ You should see a JSON response listing the available tools.
65
65
  "command": "node",
66
66
  "args": ["/absolute/path/to/sage-protocol/cli/mcp-server-stdio.js"],
67
67
  "env": {
68
- "SUBGRAPH_URL": "https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.0/gn",
69
- "RPC_URL": "https://sepolia.base.org",
70
- "LIBRARY_REGISTRY_ADDRESS": "0x032F2E93549640a319163Ba600fc50bA1AFBE50F",
71
- "SUBDAO_FACTORY_ADDRESS": "0x6bb6A83149F84Df0584aC863e5fE3416AFb214aC"
68
+ "SUBGRAPH_URL": "https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.2/gn",
69
+ "RPC_URL": "https://base-sepolia.publicnode.com",
70
+ "LIBRARY_REGISTRY_ADDRESS": "0x419Cd79d58db6A5aE1900dd7F0c1b8d153FfaD06",
71
+ "SUBDAO_FACTORY_ADDRESS": "0xED573E7e4c00fE325f846B961Df3352807eA3807"
72
72
  }
73
73
  }
74
74
  }
@@ -97,36 +97,37 @@ Can you search for on-chain prompts related to "real estate"?
97
97
  or
98
98
 
99
99
  ```
100
- Please list all available SubDAOs in the Sage Protocol.
100
+ Please list all available DAOs in the Sage Protocol.
101
101
  ```
102
102
 
103
103
  ## Available MCP Tools
104
104
 
105
- Your Sage Protocol MCP Server provides these tools:
106
-
107
- ### 1. `search_onchain_prompts`
108
- - **Purpose**: Search prompts from LibraryRegistry and SubDAO registries
109
- - **Parameters**:
110
- - `query` (optional): Search terms
111
- - `subdao` (optional): Filter by SubDAO address
112
- - `tags` (optional): Filter by tags array
113
- - `limit` (optional): Max results (default: 10)
114
- - `includeContent` (optional): Fetch full content (default: false)
115
-
116
- ### 2. `get_prompt_content`
117
- - **Purpose**: Fetch full prompt content from IPFS
118
- - **Parameters**:
119
- - `cid` (required): IPFS CID of the prompt
120
-
121
- ### 3. `list_subdaos`
122
- - **Purpose**: List all SubDAOs in the protocol
123
- - **Parameters**:
124
- - `limit` (optional): Max results (default: 20)
125
-
126
- ### 4. `get_library_manifests`
127
- - **Purpose**: Get executed library manifests from LibraryRegistry
128
- - **Parameters**:
129
- - `limit` (optional): Max results (default: 10)
105
+ Your Sage Protocol MCP Server provides these tools. For the full list, see `docs/development/mcp-config.md`.
106
+
107
+ ### Quick Workflow (recommended)
108
+ - `quick_create_prompt` - Create a new prompt (handles library creation automatically)
109
+ - `quick_iterate_prompt` - Update an existing prompt with automatic backup
110
+ - `quick_test_prompt` / `test_prompt` - Test a prompt with variable substitution
111
+ - `improve_prompt` - Analyze a prompt and suggest improvements
112
+ - `rename_prompt` - Rename a prompt's key/display name
113
+ - `help` - Get help on Sage MCP workflows
114
+
115
+ ### Discovery & Search
116
+ - `search_prompts` - Unified search across local and on-chain sources
117
+ - `search_onchain_prompts` - Search directly from on-chain registries
118
+ - `trending_prompts` - List trending prompts
119
+ - `list_prompts` - List prompts from local libraries
120
+ - `get_prompt` - Retrieve a specific prompt by key
121
+ - `get_prompt_content` - Fetch full content from IPFS by CID
122
+ - `list_libraries` - Page through libraries (local or on-chain)
123
+ - `list_subdaos` - List all available DAOs
124
+ - `get_library_manifests` - Get executed library manifests
125
+
126
+ ### Publishing & Governance
127
+ - `suggest_subdaos_for_library` - Recommend DAOs for publishing
128
+ - `generate_publishing_commands` - Generate CLI commands to publish
129
+ - `publish_manifest_flow` - Validate Push → Proposal payload (no signing)
130
+ - `list_proposals` - List active proposals for a DAO
130
131
 
131
132
  ## Troubleshooting
132
133
 
@@ -44,7 +44,7 @@ function buildAccessControlConditions(receiptAddress, receiptId, chain) {
44
44
  type: 'function',
45
45
  },
46
46
  chain: litChain,
47
- returnValueTest: { comparator: '>', value: '0' },
47
+ returnValueTest: { key: '', comparator: '>', value: '0' },
48
48
  },
49
49
  ];
50
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-protocol/cli",
3
- "version": "0.3.9",
3
+ "version": "0.4.0",
4
4
  "description": "Sage Protocol CLI for managing AI prompt libraries",
5
5
  "bin": {
6
6
  "sage": "./bin/sage.js"