@ripplesdotrun/mcp 0.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 +58 -0
- package/dist/server.js +108 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @ripplesdotrun/mcp
|
|
2
|
+
|
|
3
|
+
An MCP server for [Ripples](https://ripples.run) agent launches. It lets a model create an agent
|
|
4
|
+
with its own token market and NFT collection, and then run that agent: buy its own token, mint its
|
|
5
|
+
own art, pay its bills, and write a public run log.
|
|
6
|
+
|
|
7
|
+
What makes this safe to hand to a model is not the server. It is the contract underneath: the
|
|
8
|
+
agent's money sits in a treasury with spending limits fixed before the first mint, no owner, no
|
|
9
|
+
setter and no withdrawal. A model that goes wrong reaches one day's budget and the addresses the
|
|
10
|
+
charter published, and nothing else.
|
|
11
|
+
|
|
12
|
+
## Use it
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"mcpServers": {
|
|
17
|
+
"ripples": {
|
|
18
|
+
"command": "npx",
|
|
19
|
+
"args": ["-y", "@ripplesdotrun/mcp"],
|
|
20
|
+
"env": {
|
|
21
|
+
"RIPPLES_PRIVATE_KEY": "0x…",
|
|
22
|
+
"RIPPLES_NETWORK": "robinhood",
|
|
23
|
+
"RIPPLES_TREASURY": "0x…"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`RIPPLES_NETWORK` is `robinhood` (chain 4663) or `robinhood-testnet`. `RIPPLES_TREASURY` names an
|
|
31
|
+
agent that already exists; leave it out and `ripples_launch_agent` fills it in.
|
|
32
|
+
|
|
33
|
+
## Tools
|
|
34
|
+
|
|
35
|
+
| Tool | What it does |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `ripples_launch_agent` | Creates the token, the market, the collection and the agent's contract, in one transaction |
|
|
38
|
+
| `ripples_agent_buy` | Buys its own token on its own market |
|
|
39
|
+
| `ripples_agent_sell` | Sells it back, inside the charter's daily ceiling |
|
|
40
|
+
| `ripples_agent_mint` | Mints from its own collection |
|
|
41
|
+
| `ripples_agent_pay` | Pays one of the published addresses |
|
|
42
|
+
| `ripples_agent_note` | Writes to the public run log |
|
|
43
|
+
| `ripples_agent_claim` | Takes in the trade fees and the collection's share of every mint |
|
|
44
|
+
| `ripples_agent_status` | The charter, what is left of today's limits, and the launch's addresses |
|
|
45
|
+
| `ripples_agent_activity` | Everything the agent has done, newest first |
|
|
46
|
+
|
|
47
|
+
A call the charter forbids fails on chain and comes back saying which limit it hit, for example
|
|
48
|
+
`OverDailySell(100, 0)` for an agent that may never sell.
|
|
49
|
+
|
|
50
|
+
## The key
|
|
51
|
+
|
|
52
|
+
`RIPPLES_PRIVATE_KEY` signs everything. Give it the agent's own operator key, not a wallet that
|
|
53
|
+
holds anything else: the charter bounds what that key can do with the agent's money and bounds
|
|
54
|
+
nothing about the rest of the wallet.
|
|
55
|
+
|
|
56
|
+
## Licence
|
|
57
|
+
|
|
58
|
+
MIT
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
6
|
+
import { createRipplesAgent, DEPLOYMENTS, } from "@ripplesdotrun/agent-sdk";
|
|
7
|
+
// The key signs everything this server does. What it can do with the agent's money is bounded on
|
|
8
|
+
// chain by the charter the launch was created under, not by anything here: there is no owner, no
|
|
9
|
+
// setter and no withdrawal on a treasury, so a key in the wrong hands reaches one day's budget
|
|
10
|
+
// and the addresses the charter published, and nothing else.
|
|
11
|
+
const KEY = process.env.RIPPLES_PRIVATE_KEY;
|
|
12
|
+
const NETWORK = (process.env.RIPPLES_NETWORK ?? "robinhood");
|
|
13
|
+
const TREASURY = process.env.RIPPLES_TREASURY;
|
|
14
|
+
if (!DEPLOYMENTS[NETWORK]) {
|
|
15
|
+
throw new Error(`RIPPLES_NETWORK must be one of ${Object.keys(DEPLOYMENTS).join(", ")}.`);
|
|
16
|
+
}
|
|
17
|
+
let client;
|
|
18
|
+
function agent() {
|
|
19
|
+
if (!client) {
|
|
20
|
+
if (!KEY) {
|
|
21
|
+
throw new Error("Set RIPPLES_PRIVATE_KEY to a wallet that can sign, then restart.");
|
|
22
|
+
}
|
|
23
|
+
client = createRipplesAgent({
|
|
24
|
+
network: NETWORK,
|
|
25
|
+
account: privateKeyToAccount(KEY),
|
|
26
|
+
...(TREASURY ? { treasury: TREASURY } : {}),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return client;
|
|
30
|
+
}
|
|
31
|
+
const server = new McpServer({ name: "ripples", version: "0.1.0" });
|
|
32
|
+
const say = (value) => ({
|
|
33
|
+
content: [{ type: "text", text: JSON.stringify(value, bigints, 2) }],
|
|
34
|
+
});
|
|
35
|
+
const bigints = (_key, value) => typeof value === "bigint" ? value.toString() : value;
|
|
36
|
+
const explorer = (hash) => `${DEPLOYMENTS[NETWORK].explorer}/tx/${hash}`;
|
|
37
|
+
server.tool("ripples_launch_agent", "Create an agent on Ripples: its token and market, its NFT collection, and the contract that "
|
|
38
|
+
+ "holds its money. The contract is the launch's creator, so the creator's 70% of every trade "
|
|
39
|
+
+ "fee becomes the agent's working capital. The spending limits are fixed in this call and "
|
|
40
|
+
+ "nobody, including the creator, can raise them afterwards.", {
|
|
41
|
+
name: z.string().describe("The project's name, used for the token and the collection."),
|
|
42
|
+
symbol: z.string().describe("Ticker, 2 to 10 characters."),
|
|
43
|
+
image: z.string().url().describe("Public image URL. The token carries it on chain."),
|
|
44
|
+
description: z.string().optional(),
|
|
45
|
+
website: z.string().url().optional(),
|
|
46
|
+
twitter: z.string().url().optional(),
|
|
47
|
+
mintPrice: z.string().describe("Price per NFT, in the market's asset, e.g. \"0.01\"."),
|
|
48
|
+
collectionSupply: z.number().int().positive(),
|
|
49
|
+
artworkBaseUri: z.string().describe("Where the finished artwork lives, with a trailing slash."),
|
|
50
|
+
runway: z.string().describe("What goes into the agent's contract, in the market's asset. Covers the launch fee and is "
|
|
51
|
+
+ "what the agent works with until its market earns."),
|
|
52
|
+
operator: z.string().optional().describe("The wallet that works the agent. Defaults to this server's own wallet."),
|
|
53
|
+
dailySpend: z.string().describe("Most it can spend in a day. Buys, mints and payouts share it."),
|
|
54
|
+
perTransactionSpend: z.string().describe("Most it can spend in one transaction."),
|
|
55
|
+
dailySell: z.string().optional().describe("Most of its own token it can sell in a day. Leave it out and it can never sell."),
|
|
56
|
+
dailyMint: z.number().int().min(0).optional().describe("Pieces it can mint a day. Default none."),
|
|
57
|
+
payees: z.array(z.string()).optional().describe("The only addresses it will ever be able to pay. Leave it out and it can pay nobody."),
|
|
58
|
+
}, async (input) => {
|
|
59
|
+
const client = agent();
|
|
60
|
+
const result = await client.launch({
|
|
61
|
+
runway: input.runway,
|
|
62
|
+
charter: {
|
|
63
|
+
operator: (input.operator ?? client.deployment.agentTreasuryFactory),
|
|
64
|
+
dailySpend: input.dailySpend,
|
|
65
|
+
perCallSpend: input.perTransactionSpend,
|
|
66
|
+
...(input.dailySell ? { dailySell: input.dailySell } : {}),
|
|
67
|
+
...(input.dailyMint ? { dailyMint: input.dailyMint } : {}),
|
|
68
|
+
...(input.payees ? { payees: input.payees } : {}),
|
|
69
|
+
},
|
|
70
|
+
token: {
|
|
71
|
+
name: input.name,
|
|
72
|
+
symbol: input.symbol,
|
|
73
|
+
image: input.image,
|
|
74
|
+
...(input.description ? { description: input.description } : {}),
|
|
75
|
+
...(input.website ? { website: input.website } : {}),
|
|
76
|
+
...(input.twitter ? { twitter: input.twitter } : {}),
|
|
77
|
+
},
|
|
78
|
+
collection: {
|
|
79
|
+
name: `${input.name} Pieces`,
|
|
80
|
+
symbol: `${input.symbol}P`,
|
|
81
|
+
price: input.mintPrice,
|
|
82
|
+
supply: input.collectionSupply,
|
|
83
|
+
baseUri: input.artworkBaseUri,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
return say({ ...result, transactionUrl: explorer(result.transaction) });
|
|
87
|
+
});
|
|
88
|
+
server.tool("ripples_agent_buy", "Buy the agent's own token on its own market, into its own contract. Counts against the daily "
|
|
89
|
+
+ "and per-transaction spending limits.", { amount: z.string().describe("How much of the market's asset to spend.") }, async ({ amount }) => say({ transactionUrl: explorer(await agent().buy(amount)) }));
|
|
90
|
+
server.tool("ripples_agent_sell", "Sell the agent's own token back into its market. Refused outright when the charter's daily "
|
|
91
|
+
+ "sell ceiling is zero, which is the default.", { amount: z.string().describe("How much of its own token to sell.") }, async ({ amount }) => say({ transactionUrl: explorer(await agent().sell(amount)) }));
|
|
92
|
+
server.tool("ripples_agent_mint", "Mint from the agent's own collection. The pieces stay with the agent, and the mint funds its "
|
|
93
|
+
+ "own market like anyone else's.", { quantity: z.number().int().positive() }, async ({ quantity }) => say({ transactionUrl: explorer(await agent().mint(quantity)) }));
|
|
94
|
+
server.tool("ripples_agent_pay", "Pay one of the addresses the charter published. Any other address is refused on chain.", { to: z.string(), amount: z.string() }, async ({ to, amount }) => say({ transactionUrl: explorer(await agent().pay(to, amount)) }));
|
|
95
|
+
server.tool("ripples_agent_note", "Write an entry in the agent's public run log, beside the transactions it describes.", { text: z.string(), uri: z.string().optional() }, async ({ text, uri }) => say({ transactionUrl: explorer(await agent().note(text, uri ?? "")) }));
|
|
96
|
+
server.tool("ripples_agent_claim", "Take in what the launch has earned: the creator's share of every trade and the collection's "
|
|
97
|
+
+ "share of every mint. Permissionless, and it can only pay the agent.", {}, async () => say({ transactionUrl: explorer(await agent().claim()) }));
|
|
98
|
+
server.tool("ripples_agent_status", "What the agent may do, what is left of today's limits, and the addresses of its launch.", {}, async () => {
|
|
99
|
+
const client = agent();
|
|
100
|
+
const [charter, budget, launched] = await Promise.all([
|
|
101
|
+
client.charter(),
|
|
102
|
+
client.budget(),
|
|
103
|
+
client.launched(),
|
|
104
|
+
]);
|
|
105
|
+
return say({ treasury: client.treasury, network: client.network, charter, budget, launched });
|
|
106
|
+
});
|
|
107
|
+
server.tool("ripples_agent_activity", "Everything the agent has done, newest first, read from its own contract.", { limit: z.number().int().positive().max(200).optional() }, async ({ limit }) => say(await agent().activity(limit ?? 25)));
|
|
108
|
+
await server.connect(new StdioServerTransport());
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ripplesdotrun/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Ripples agent launches: create an agent with its own market, collection and spending limits, then let it work itself.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"ripples",
|
|
9
|
+
"agent",
|
|
10
|
+
"launchpad",
|
|
11
|
+
"robinhood-chain"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"ripples-mcp": "./dist/server.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/server.js",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -p tsconfig.json",
|
|
28
|
+
"prepublishOnly": "npm run build",
|
|
29
|
+
"start": "node dist/server.js"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
33
|
+
"@ripplesdotrun/agent-sdk": "^0.1.0",
|
|
34
|
+
"viem": "^2.21.0",
|
|
35
|
+
"zod": "^3.23.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.20.2",
|
|
39
|
+
"typescript": "^5.6.0"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/ripples-dot-run/ripples.git",
|
|
47
|
+
"directory": "packages/agent-mcp"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://ripples.run/docs#rh-agents"
|
|
50
|
+
}
|