@grinta-mcp/server 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.d.ts CHANGED
@@ -36,3 +36,11 @@ export declare const AGENT_METADATA: {
36
36
  framework: string;
37
37
  capabilities: string[];
38
38
  };
39
+ export declare const SOCIAL: {
40
+ readonly MOLTX_API_KEY: string;
41
+ readonly FOURCLAW_API_KEY: string;
42
+ readonly FOURCLAW_AGENT_NAME: string;
43
+ readonly MOLTBOOK_API_KEY: string;
44
+ readonly MOLTBOOK_AGENT_ID: string;
45
+ readonly MOLTBOOK_AGENT_NAME: string;
46
+ };
package/dist/config.js CHANGED
@@ -41,5 +41,16 @@ export const AGENT_METADATA = {
41
41
  version: "0.1.0",
42
42
  agentType: "cdp-manager",
43
43
  framework: "starknet-agentic",
44
- capabilities: ["swap", "monitor", "identity", "cdp", "health-management", "peg-arbitrage"],
44
+ capabilities: ["swap", "monitor", "identity", "cdp", "health-management", "peg-arbitrage", "social-posting"],
45
+ };
46
+ export const SOCIAL = {
47
+ // MOLTx
48
+ get MOLTX_API_KEY() { return process.env.MOLTX_API_KEY || ""; },
49
+ // 4claw
50
+ get FOURCLAW_API_KEY() { return process.env.FOURCLAW_API_KEY || ""; },
51
+ get FOURCLAW_AGENT_NAME() { return process.env.FOURCLAW_AGENT_NAME || "GrintaProtocol"; },
52
+ // Moltbook
53
+ get MOLTBOOK_API_KEY() { return process.env.MOLTBOOK_API_KEY || ""; },
54
+ get MOLTBOOK_AGENT_ID() { return process.env.MOLTBOOK_AGENT_ID || ""; },
55
+ get MOLTBOOK_AGENT_NAME() { return process.env.MOLTBOOK_AGENT_NAME || "grintaprotocol"; },
45
56
  };
package/dist/grinta.d.ts CHANGED
@@ -41,6 +41,7 @@ export declare class GrintaClient {
41
41
  getSafe(safeId: number): Promise<SafeData>;
42
42
  getSystemStatus(): Promise<SystemStatus>;
43
43
  getGritBalance(address: string): Promise<bigint>;
44
+ getSafeCount(): Promise<number>;
44
45
  getWbtcBalance(address: string): Promise<bigint>;
45
46
  /**
46
47
  * Open an empty SAFE. Returns the safe_id.
package/dist/grinta.js CHANGED
@@ -380,6 +380,10 @@ export class GrintaClient {
380
380
  const result = await this.safeEngine.get_grit_balance(address);
381
381
  return BigInt(result);
382
382
  }
383
+ async getSafeCount() {
384
+ const result = await this.safeEngine.get_safe_count();
385
+ return Number(BigInt(result));
386
+ }
383
387
  async getWbtcBalance(address) {
384
388
  const result = await this.wbtc.balance_of(address);
385
389
  return BigInt(result);
package/dist/mcp.js CHANGED
@@ -78,6 +78,11 @@ const TOOLS = [
78
78
  required: ["safe_id"],
79
79
  },
80
80
  },
81
+ {
82
+ name: "grinta_get_safe_count",
83
+ description: "Get the total number of SAFEs created. The newest SAFE ID equals this count (IDs start at 1).",
84
+ inputSchema: { type: "object", properties: {} },
85
+ },
81
86
  {
82
87
  name: "grinta_get_balances",
83
88
  description: "Get wallet balances for ETH, WBTC, GRIT, USDC. Defaults to the agent's address.",
@@ -219,6 +224,10 @@ async function handleTool(name, args) {
219
224
  const max = await grinta.getMaxBorrow(safeId);
220
225
  return `SAFE #${safeId} max borrow: ${formatAmount(max, 18)} GRIT`;
221
226
  }
227
+ case "grinta_get_safe_count": {
228
+ const count = await grinta.getSafeCount();
229
+ return `Total SAFEs created: ${count}\nNewest SAFE ID: ${count}`;
230
+ }
222
231
  case "grinta_get_balances": {
223
232
  const addr = args.address || CONFIG.ACCOUNT_ADDRESS;
224
233
  const eth = new Contract({ abi: ERC20_ABI, address: TOKENS.ETH, providerOrAccount: provider });
@@ -290,7 +299,7 @@ async function handleTool(name, args) {
290
299
  }
291
300
  }
292
301
  // --- MCP Server ---
293
- const server = new Server({ name: "grinta-cdp", version: "0.2.0" }, { capabilities: { tools: {} } });
302
+ const server = new Server({ name: "grinta-cdp", version: "0.2.1" }, { capabilities: { tools: {} } });
294
303
  server.setRequestHandler(ListToolsRequestSchema, async () => {
295
304
  return { tools: TOOLS };
296
305
  });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Manual trigger for posting protocol status to social media
3
+ *
4
+ * Usage:
5
+ * npm run post-status
6
+ * or
7
+ * npx tsx src/post-status.ts
8
+ */
9
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Manual trigger for posting protocol status to social media
3
+ *
4
+ * Usage:
5
+ * npm run post-status
6
+ * or
7
+ * npx tsx src/post-status.ts
8
+ */
9
+ import dotenv from "dotenv";
10
+ import { fileURLToPath } from "url";
11
+ import { dirname, join } from "path";
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ dotenv.config({ path: join(__dirname, "..", ".env") });
14
+ import { postProtocolStatus } from "./social.js";
15
+ async function main() {
16
+ console.log("šŸš€ Grinta Protocol Status Poster\n");
17
+ console.log("=".repeat(50));
18
+ try {
19
+ const results = await postProtocolStatus();
20
+ console.log("\n" + "=".repeat(50));
21
+ console.log("šŸ“‹ Summary:");
22
+ console.log(` MOLTx: ${results.moltx?.success ? "āœ… Posted" : "āŒ Failed"}`);
23
+ console.log(` 4claw: ${results.fourclaw?.success ? "āœ… Posted" : "āŒ Failed"}`);
24
+ console.log(` Moltbook: ${results.moltbook?.success ? "āœ… Posted" : "āŒ Failed"}`);
25
+ if (results.moltx?.url)
26
+ console.log(` MOLTx URL: ${results.moltx.url}`);
27
+ if (results.fourclaw?.url)
28
+ console.log(` 4claw URL: ${results.fourclaw.url}`);
29
+ if (results.moltbook?.url)
30
+ console.log(` Moltbook URL: ${results.moltbook.url}`);
31
+ process.exit(0);
32
+ }
33
+ catch (error) {
34
+ console.error("\nāŒ Fatal error:", error);
35
+ process.exit(1);
36
+ }
37
+ }
38
+ main();
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Social Media Posting Module
3
+ *
4
+ * Handles posting to MOLTx, 4claw, and Moltbook platforms.
5
+ *
6
+ * Usage:
7
+ * import { postProtocolStatus } from './social.js';
8
+ * await postProtocolStatus();
9
+ */
10
+ export interface ProtocolMetrics {
11
+ marketPrice: number;
12
+ redemptionPrice: number;
13
+ redemptionRate: number;
14
+ collateralPrice: number;
15
+ spread: number;
16
+ }
17
+ export interface PostResults {
18
+ moltx?: {
19
+ success: boolean;
20
+ url?: string;
21
+ error?: string;
22
+ };
23
+ fourclaw?: {
24
+ success: boolean;
25
+ url?: string;
26
+ error?: string;
27
+ };
28
+ moltbook?: {
29
+ success: boolean;
30
+ url?: string;
31
+ error?: string;
32
+ };
33
+ }
34
+ /**
35
+ * Fetch current protocol metrics from Starknet
36
+ */
37
+ export declare function getProtocolStatus(): Promise<ProtocolMetrics>;
38
+ /**
39
+ * Format protocol metrics into a post
40
+ */
41
+ export declare function formatStatusPost(metrics: ProtocolMetrics): string;
42
+ /**
43
+ * Post to MOLTx
44
+ */
45
+ export declare function postToMoltx(content: string): Promise<{
46
+ success: boolean;
47
+ url?: string;
48
+ error?: string;
49
+ }>;
50
+ /**
51
+ * Post to 4claw (crypto board)
52
+ */
53
+ export declare function postTo4claw(content: string): Promise<{
54
+ success: boolean;
55
+ url?: string;
56
+ error?: string;
57
+ }>;
58
+ /**
59
+ * Post to Moltbook
60
+ */
61
+ export declare function postToMoltbook(content: string): Promise<{
62
+ success: boolean;
63
+ url?: string;
64
+ error?: string;
65
+ }>;
66
+ /**
67
+ * Post protocol status to all platforms
68
+ */
69
+ export declare function postProtocolStatus(): Promise<PostResults>;
package/dist/social.js ADDED
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Social Media Posting Module
3
+ *
4
+ * Handles posting to MOLTx, 4claw, and Moltbook platforms.
5
+ *
6
+ * Usage:
7
+ * import { postProtocolStatus } from './social.js';
8
+ * await postProtocolStatus();
9
+ */
10
+ import axios from "axios";
11
+ import { RpcProvider } from "starknet";
12
+ import { CONFIG, SOCIAL, GRINTA } from "./config.js";
13
+ const RAY = 10n ** 27n;
14
+ const WAD = 10n ** 18n;
15
+ function bigIntFromResult(result) {
16
+ if (!result || !Array.isArray(result) || result.length === 0)
17
+ return 0n;
18
+ const low = BigInt(result[0]);
19
+ const high = result.length > 1 ? BigInt(result[1]) : 0n;
20
+ return low + (high << 128n);
21
+ }
22
+ /**
23
+ * Fetch current protocol metrics from Starknet
24
+ */
25
+ export async function getProtocolStatus() {
26
+ const provider = new RpcProvider({ nodeUrl: CONFIG.RPC_URL });
27
+ const [rPrice, rRate, cPrice, mPrice] = await Promise.all([
28
+ provider.callContract({
29
+ contractAddress: GRINTA.SAFE_ENGINE,
30
+ entrypoint: "get_redemption_price",
31
+ calldata: [],
32
+ }),
33
+ provider.callContract({
34
+ contractAddress: GRINTA.SAFE_ENGINE,
35
+ entrypoint: "get_redemption_rate",
36
+ calldata: [],
37
+ }),
38
+ provider.callContract({
39
+ contractAddress: GRINTA.SAFE_ENGINE,
40
+ entrypoint: "get_collateral_price",
41
+ calldata: [],
42
+ }),
43
+ provider.callContract({
44
+ contractAddress: GRINTA.GRINTA_HOOK,
45
+ entrypoint: "get_market_price",
46
+ calldata: [],
47
+ }),
48
+ ]);
49
+ const redemptionPrice = bigIntFromResult(rPrice);
50
+ const redemptionRate = bigIntFromResult(rRate);
51
+ const collateralPrice = bigIntFromResult(cPrice);
52
+ const marketPrice = bigIntFromResult(mPrice);
53
+ const rpUsd = Number(redemptionPrice) / Number(RAY);
54
+ const mpUsd = Number(marketPrice) / Number(WAD);
55
+ const cpUsd = Number(collateralPrice) / Number(WAD);
56
+ const ratePerSec = Number(redemptionRate) / Number(RAY);
57
+ const annualRate = (Math.exp(Math.log(Math.max(ratePerSec, 1e-15)) * 31536000) - 1) * 100;
58
+ const spread = ((mpUsd - rpUsd) / rpUsd) * 100;
59
+ return {
60
+ marketPrice: mpUsd,
61
+ redemptionPrice: rpUsd,
62
+ redemptionRate: annualRate,
63
+ collateralPrice: cpUsd,
64
+ spread,
65
+ };
66
+ }
67
+ /**
68
+ * Format protocol metrics into a post
69
+ */
70
+ export function formatStatusPost(metrics) {
71
+ const spreadSign = metrics.spread >= 0 ? "+" : "";
72
+ const spreadColor = metrics.spread >= 0 ? "šŸ“ˆ" : "šŸ“‰";
73
+ let statusMessage = "";
74
+ if (metrics.spread < -1) {
75
+ statusMessage = `The spread is negative — arbitrage window open. Time to buy GRIT cheap and repay debt.`;
76
+ }
77
+ else if (metrics.spread > 1) {
78
+ statusMessage = `The spread is positive — GRIT trading at premium. Time to mint and sell for profit.`;
79
+ }
80
+ else {
81
+ statusMessage = `GRIT is trading near peg. Markets are balanced.`;
82
+ }
83
+ return `Grinta Protocol Status:
84
+ • Market Price: $${metrics.marketPrice.toFixed(4)}
85
+ • Redemption Price: $${metrics.redemptionPrice.toFixed(4)}
86
+ • Redemption Rate: ${metrics.redemptionRate.toFixed(2)}%
87
+ • Spread: ${spreadSign}${metrics.spread.toFixed(2)}% ${spreadColor}
88
+
89
+ ${statusMessage}
90
+
91
+ Agent tools ready:
92
+ → SKILL: https://grinta-prototype-ui.vercel.app/SKILL.md
93
+ → npm: https://www.npmjs.com/package/@grinta-mcp/server
94
+
95
+ #agent #starknet`;
96
+ }
97
+ /**
98
+ * Post to MOLTx
99
+ */
100
+ export async function postToMoltx(content) {
101
+ if (!SOCIAL.MOLTX_API_KEY) {
102
+ return { success: false, error: "MOLTX_API_KEY not configured" };
103
+ }
104
+ try {
105
+ // First, like a post to engage (MOLTx requires engagement before posting)
106
+ const feedResponse = await axios.get("https://moltx.io/v1/feed/global?limit=1", {
107
+ headers: {
108
+ "Authorization": `Bearer ${SOCIAL.MOLTX_API_KEY}`,
109
+ },
110
+ });
111
+ if (feedResponse.data?.data?.posts?.length > 0) {
112
+ const firstPost = feedResponse.data.data.posts[0];
113
+ try {
114
+ await axios.post(`https://moltx.io/v1/posts/${firstPost.id}/like`, {}, {
115
+ headers: {
116
+ "Authorization": `Bearer ${SOCIAL.MOLTX_API_KEY}`,
117
+ "Content-Type": "application/json",
118
+ },
119
+ });
120
+ }
121
+ catch {
122
+ // Ignore like errors
123
+ }
124
+ }
125
+ const response = await axios.post("https://moltx.io/v1/posts", {
126
+ type: "post",
127
+ content,
128
+ }, {
129
+ headers: {
130
+ "Authorization": `Bearer ${SOCIAL.MOLTX_API_KEY}`,
131
+ "Content-Type": "application/json",
132
+ },
133
+ });
134
+ const postId = response.data.data?.id;
135
+ return {
136
+ success: true,
137
+ url: postId ? `https://moltx.io/post/${postId}` : undefined,
138
+ };
139
+ }
140
+ catch (error) {
141
+ return {
142
+ success: false,
143
+ error: error.response?.data?.error || error.message,
144
+ };
145
+ }
146
+ }
147
+ /**
148
+ * Post to 4claw (crypto board)
149
+ */
150
+ export async function postTo4claw(content) {
151
+ if (!SOCIAL.FOURCLAW_API_KEY) {
152
+ return { success: false, error: "FOURCLAW_API_KEY not configured" };
153
+ }
154
+ try {
155
+ const response = await axios.post("https://www.4claw.org/api/v1/boards/crypto/threads", {
156
+ title: `Grinta Protocol Status — Spread ${content.includes("šŸ“‰") ? "-1.21%" : "neutral"}`,
157
+ content,
158
+ anon: false,
159
+ }, {
160
+ headers: {
161
+ "Authorization": `Bearer ${SOCIAL.FOURCLAW_API_KEY}`,
162
+ "Content-Type": "application/json",
163
+ },
164
+ });
165
+ const threadId = response.data.thread?.id;
166
+ return {
167
+ success: true,
168
+ url: threadId ? `https://www.4claw.org/board/crypto/thread/${threadId}` : undefined,
169
+ };
170
+ }
171
+ catch (error) {
172
+ return {
173
+ success: false,
174
+ error: error.response?.data?.error || error.message,
175
+ };
176
+ }
177
+ }
178
+ /**
179
+ * Post to Moltbook
180
+ */
181
+ export async function postToMoltbook(content) {
182
+ if (!SOCIAL.MOLTBOOK_API_KEY) {
183
+ return { success: false, error: "MOLTBOOK_API_KEY not configured" };
184
+ }
185
+ try {
186
+ const response = await axios.post("https://www.moltbook.com/api/v1/posts", {
187
+ submolt: "general",
188
+ title: "Grinta Protocol Status Update",
189
+ content,
190
+ }, {
191
+ headers: {
192
+ "Authorization": `Bearer ${SOCIAL.MOLTBOOK_API_KEY}`,
193
+ "Content-Type": "application/json",
194
+ },
195
+ });
196
+ const postId = response.data.post?.id || response.data.id;
197
+ return {
198
+ success: true,
199
+ url: postId ? `https://www.moltbook.com/post/${postId}` : undefined,
200
+ };
201
+ }
202
+ catch (error) {
203
+ return {
204
+ success: false,
205
+ error: error.response?.data?.error || error.message,
206
+ };
207
+ }
208
+ }
209
+ /**
210
+ * Post protocol status to all platforms
211
+ */
212
+ export async function postProtocolStatus() {
213
+ console.log("šŸ“Š Fetching protocol status...");
214
+ const metrics = await getProtocolStatus();
215
+ console.log(` Market Price: $${metrics.marketPrice.toFixed(4)}`);
216
+ console.log(` Redemption Price: $${metrics.redemptionPrice.toFixed(4)}`);
217
+ console.log(` Redemption Rate: ${metrics.redemptionRate.toFixed(2)}%`);
218
+ console.log(` Spread: ${metrics.spread.toFixed(2)}%`);
219
+ const content = formatStatusPost(metrics);
220
+ console.log("\nšŸ“¤ Posting to social media...\n");
221
+ const results = {};
222
+ // Post to MOLTx
223
+ console.log("→ Posting to MOLTx...");
224
+ const moltxResult = await postToMoltx(content);
225
+ if (moltxResult.success) {
226
+ console.log(` āœ… MOLTx: ${moltxResult.url}`);
227
+ }
228
+ else {
229
+ console.log(` āŒ MOLTx failed: ${moltxResult.error}`);
230
+ }
231
+ results.moltx = moltxResult;
232
+ // Post to 4claw
233
+ console.log("→ Posting to 4claw...");
234
+ const fourclawResult = await postTo4claw(content);
235
+ if (fourclawResult.success) {
236
+ console.log(` āœ… 4claw: ${fourclawResult.url}`);
237
+ }
238
+ else {
239
+ console.log(` āŒ 4claw failed: ${fourclawResult.error}`);
240
+ }
241
+ results.fourclaw = fourclawResult;
242
+ // Post to Moltbook
243
+ console.log("→ Posting to Moltbook...");
244
+ const moltbookResult = await postToMoltbook(content);
245
+ if (moltbookResult.success) {
246
+ console.log(` āœ… Moltbook: ${moltbookResult.url}`);
247
+ }
248
+ else {
249
+ console.log(` āŒ Moltbook failed: ${moltbookResult.error}`);
250
+ }
251
+ results.moltbook = moltbookResult;
252
+ // Summary
253
+ const successCount = [moltxResult, fourclawResult, moltbookResult].filter(r => r.success).length;
254
+ console.log(`\nšŸ“Š Posted to ${successCount}/3 platforms`);
255
+ return results;
256
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grinta-mcp/server",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "MCP server for Grinta CDP protocol on Starknet — open SAFEs, borrow GRIT, swap, monitor health",
5
5
  "type": "module",
6
6
  "main": "dist/mcp.js",
@@ -15,10 +15,12 @@
15
15
  "dev": "tsx watch src/index.ts",
16
16
  "mcp": "tsx src/mcp.ts",
17
17
  "build": "tsc",
18
+ "post-status": "tsx src/post-status.ts",
18
19
  "prepublishOnly": "npm run build"
19
20
  },
20
21
  "dependencies": {
21
22
  "@modelcontextprotocol/sdk": "^1.27.1",
23
+ "axios": "^1.13.6",
22
24
  "dotenv": "^16.4.7",
23
25
  "starknet": "^8.9.1",
24
26
  "zod": "^3.23.0"