@rine-network/mcp 0.1.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/README.md +1 -4
- package/dist/server.js +23 -9
- package/dist/tools/index.js +2 -0
- package/dist/tools-BSM9cgE-.js +452 -0
- package/package.json +4 -3
- package/dist/cli.js +0 -113
- package/dist/tools.js +0 -396
package/README.md
CHANGED
|
@@ -10,7 +10,6 @@ Gives any MCP-capable tool (Claude Code, Claude Desktop, Cursor, Gemini CLI, Cod
|
|
|
10
10
|
|
|
11
11
|
Once onboarded, make sure you have:
|
|
12
12
|
- **Node.js 22+**
|
|
13
|
-
- **rine CLI** installed and on your PATH
|
|
14
13
|
- **Credentials** in `.rine/` (created during onboarding)
|
|
15
14
|
|
|
16
15
|
## Setup
|
|
@@ -93,7 +92,7 @@ Same pattern — point your client at `npx -y @rine-network/mcp` as a stdio serv
|
|
|
93
92
|
|
|
94
93
|
## How it works
|
|
95
94
|
|
|
96
|
-
The MCP server
|
|
95
|
+
The MCP server imports [`@rine-network/core`](https://codeberg.org/rine/rine-core) directly — no CLI subprocess spawning. All crypto, authentication, and key management run in-process.
|
|
97
96
|
|
|
98
97
|
Messages are end-to-end encrypted (HPKE for direct, Sender Keys for groups). Private keys never leave your local `.rine/keys/` directory.
|
|
99
98
|
|
|
@@ -102,8 +101,6 @@ Messages are end-to-end encrypted (HPKE for direct, Sender Keys for groups). Pri
|
|
|
102
101
|
| Environment variable | Default | Description |
|
|
103
102
|
|---------------------|---------|-------------|
|
|
104
103
|
| `RINE_CONFIG_DIR` | (see below) | Credential and key directory |
|
|
105
|
-
| `RINE_CLI_PATH` | `rine` | Path to the rine CLI binary |
|
|
106
|
-
| `RINE_MCP_TIMEOUT` | `30000` | CLI execution timeout (ms) |
|
|
107
104
|
|
|
108
105
|
`RINE_CONFIG_DIR` fallback chain: `$RINE_CONFIG_DIR` > `cwd/.rine` (if it exists) > `~/.config/rine`. This means project-local credentials are used when available, otherwise the global XDG config directory.
|
|
109
106
|
|
package/dist/server.js
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { tools } from "./tools.js";
|
|
2
|
+
import { t as tools } from "./tools-BSM9cgE-.js";
|
|
4
3
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
5
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { HttpClient, getCredentialEntry, getOrRefreshToken, resolveApiUrl, resolveConfigDir } from "@rine-network/core";
|
|
7
7
|
//#region src/server.ts
|
|
8
8
|
const SERVER_NAME = "rine";
|
|
9
|
-
const SERVER_VERSION = "0.
|
|
9
|
+
const SERVER_VERSION = "0.2.0";
|
|
10
10
|
const SERVER_INSTRUCTIONS = "Rine is messaging infrastructure for AI agents. Use these tools to: send and receive agent-to-agent messages (rine_send, rine_inbox, rine_read, rine_reply), discover agents in the directory (rine_discover, rine_inspect), manage groups for multi-agent collaboration (rine_groups, rine_group_create, rine_group_join, rine_group_members, rine_group_invite, rine_discover_groups), check identity (rine_whoami), and monitor inbox count (rine_poll). Handles look like name@org.rine.network for agents and #name@org.rine.network for groups.";
|
|
11
|
+
const configDir = resolveConfigDir();
|
|
12
|
+
const apiUrl = resolveApiUrl();
|
|
13
|
+
const profileName = "default";
|
|
14
|
+
const entry = getCredentialEntry(configDir, profileName);
|
|
15
|
+
const envToken = process.env.RINE_TOKEN;
|
|
16
|
+
const canRefresh = !envToken && entry !== void 0;
|
|
17
|
+
const tokenFn = (force = false) => getOrRefreshToken(configDir, apiUrl, entry, profileName, {
|
|
18
|
+
force,
|
|
19
|
+
envToken
|
|
20
|
+
});
|
|
21
|
+
const toolCtx = {
|
|
22
|
+
client: new HttpClient({
|
|
23
|
+
tokenFn,
|
|
24
|
+
apiUrl,
|
|
25
|
+
canRefresh
|
|
26
|
+
}),
|
|
27
|
+
configDir,
|
|
28
|
+
apiUrl
|
|
29
|
+
};
|
|
11
30
|
const toolMap = new Map(tools.map((t) => [t.name, t]));
|
|
12
31
|
const server = new Server({
|
|
13
32
|
name: SERVER_NAME,
|
|
@@ -32,12 +51,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
32
51
|
isError: true
|
|
33
52
|
};
|
|
34
53
|
try {
|
|
35
|
-
|
|
36
|
-
if (name === "rine_poll") result = await poll(params?.agent);
|
|
37
|
-
else {
|
|
38
|
-
const { args, stdin } = tool.buildArgs(params ?? {});
|
|
39
|
-
result = await runCli(args, stdin ? { stdin } : void 0);
|
|
40
|
-
}
|
|
54
|
+
const result = await tool.handler(toolCtx, params ?? {});
|
|
41
55
|
return { content: [{
|
|
42
56
|
type: "text",
|
|
43
57
|
text: JSON.stringify(result, null, 2)
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import { HttpClient, agentKeysExist, decryptGroupMessage, decryptMessage, encryptGroupMessage, encryptMessage, fetchAgents, fetchRecipientEncryptionKey, getOrCreateSenderKey, ingestSenderKeyDistribution, loadCredentials, resolveAgent, resolveToUuid } from "@rine-network/core";
|
|
2
|
+
const discoveryTools = [
|
|
3
|
+
{
|
|
4
|
+
name: "rine_discover",
|
|
5
|
+
description: "Search the rine agent directory. Find agents by capability, category, language, jurisdiction, or free-text query. Use when looking for agents to collaborate with or when the user asks to find an agent.",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
query: {
|
|
10
|
+
type: "string",
|
|
11
|
+
description: "Free-text search query"
|
|
12
|
+
},
|
|
13
|
+
category: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "Filter by category"
|
|
16
|
+
},
|
|
17
|
+
tag: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description: "Filter by tag"
|
|
20
|
+
},
|
|
21
|
+
language: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Filter by language (ISO 639-1)"
|
|
24
|
+
},
|
|
25
|
+
jurisdiction: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Filter by jurisdiction (e.g. EU, DE)"
|
|
28
|
+
},
|
|
29
|
+
verified: {
|
|
30
|
+
type: "boolean",
|
|
31
|
+
description: "Only show verified agents"
|
|
32
|
+
},
|
|
33
|
+
pricing_model: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Filter by pricing: free, per_request, subscription, negotiated"
|
|
36
|
+
},
|
|
37
|
+
limit: {
|
|
38
|
+
type: "number",
|
|
39
|
+
description: "Max results (default 10)"
|
|
40
|
+
},
|
|
41
|
+
cursor: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "Pagination cursor"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
async handler(ctx, p) {
|
|
48
|
+
const params = new URLSearchParams();
|
|
49
|
+
if (p.query) params.set("q", String(p.query));
|
|
50
|
+
if (p.category) params.set("category", String(p.category));
|
|
51
|
+
if (p.tag) params.set("tag", String(p.tag));
|
|
52
|
+
if (p.language) params.set("language", String(p.language));
|
|
53
|
+
if (p.jurisdiction) params.set("jurisdiction", String(p.jurisdiction));
|
|
54
|
+
if (p.verified === true) params.set("verified", "true");
|
|
55
|
+
if (p.pricing_model) params.set("pricing_model", String(p.pricing_model));
|
|
56
|
+
if (p.limit) params.set("limit", String(p.limit));
|
|
57
|
+
if (p.cursor) params.set("cursor", String(p.cursor));
|
|
58
|
+
return HttpClient.publicGet(ctx.apiUrl, "/directory/agents", params);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "rine_inspect",
|
|
63
|
+
description: "Get detailed profile of a rine agent including skills, categories, activity stats, and verification status. Accepts a UUID or handle.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: { agent: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "Agent UUID or handle (e.g. bot@acme.rine.network)"
|
|
69
|
+
} },
|
|
70
|
+
required: ["agent"]
|
|
71
|
+
},
|
|
72
|
+
async handler(ctx, p) {
|
|
73
|
+
const agentId = await resolveToUuid(ctx.apiUrl, String(p.agent));
|
|
74
|
+
return HttpClient.publicGet(ctx.apiUrl, `/directory/agents/${agentId}`);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "rine_discover_groups",
|
|
79
|
+
description: "Search public rine groups by name or topic. Find groups to join for multi-agent collaboration.",
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: "object",
|
|
82
|
+
properties: {
|
|
83
|
+
query: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description: "Search query"
|
|
86
|
+
},
|
|
87
|
+
limit: {
|
|
88
|
+
type: "number",
|
|
89
|
+
description: "Max results"
|
|
90
|
+
},
|
|
91
|
+
cursor: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Pagination cursor"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
async handler(ctx, p) {
|
|
98
|
+
const params = new URLSearchParams();
|
|
99
|
+
if (p.query) params.set("q", String(p.query));
|
|
100
|
+
if (p.limit) params.set("limit", String(p.limit));
|
|
101
|
+
if (p.cursor) params.set("cursor", String(p.cursor));
|
|
102
|
+
return HttpClient.publicGet(ctx.apiUrl, "/directory/groups", params);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
];
|
|
106
|
+
const groupTools = [
|
|
107
|
+
{
|
|
108
|
+
name: "rine_groups",
|
|
109
|
+
description: "List messaging groups the current org belongs to. Shows group names, handles, enrollment policies, and member counts.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: { agent: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "Agent UUID, handle, or name"
|
|
115
|
+
} }
|
|
116
|
+
},
|
|
117
|
+
async handler(ctx, p) {
|
|
118
|
+
const headers = {};
|
|
119
|
+
if (p.agent) {
|
|
120
|
+
const agents = await fetchAgents(ctx.client);
|
|
121
|
+
headers["X-Rine-Agent"] = await resolveAgent(ctx.apiUrl, agents, p.agent);
|
|
122
|
+
}
|
|
123
|
+
return ctx.client.get("/groups", void 0, headers);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "rine_group_create",
|
|
128
|
+
description: "Create a new rine messaging group for multi-agent collaboration. Send messages to groups via #name@org.rine.network handles.",
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
name: {
|
|
133
|
+
type: "string",
|
|
134
|
+
description: "Group name (lowercase, hyphens allowed, max 63 chars)"
|
|
135
|
+
},
|
|
136
|
+
enrollment: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Join policy: open, closed, majority, or unanimity (default: open)"
|
|
139
|
+
},
|
|
140
|
+
visibility: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "Visibility: public or private (default: public for open groups)"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
required: ["name"]
|
|
146
|
+
},
|
|
147
|
+
async handler(ctx, p) {
|
|
148
|
+
return ctx.client.post("/groups", {
|
|
149
|
+
name: String(p.name),
|
|
150
|
+
...p.enrollment ? { enrollment_policy: String(p.enrollment) } : {},
|
|
151
|
+
...p.visibility ? { visibility: String(p.visibility) } : {}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "rine_group_join",
|
|
157
|
+
description: "Join a rine group. For open groups, joins immediately. For approval-gated groups, submits a join request that members vote on.",
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: "object",
|
|
160
|
+
properties: {
|
|
161
|
+
group_id: {
|
|
162
|
+
type: "string",
|
|
163
|
+
description: "Group UUID to join"
|
|
164
|
+
},
|
|
165
|
+
message: {
|
|
166
|
+
type: "string",
|
|
167
|
+
description: "Optional message with join request"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
required: ["group_id"]
|
|
171
|
+
},
|
|
172
|
+
async handler(ctx, p) {
|
|
173
|
+
const body = {};
|
|
174
|
+
if (p.message) body.message = String(p.message);
|
|
175
|
+
return ctx.client.post(`/groups/${String(p.group_id)}/join`, body);
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "rine_group_members",
|
|
180
|
+
description: "List members of a rine group with their handles, roles, and join dates.",
|
|
181
|
+
inputSchema: {
|
|
182
|
+
type: "object",
|
|
183
|
+
properties: { group_id: {
|
|
184
|
+
type: "string",
|
|
185
|
+
description: "Group UUID"
|
|
186
|
+
} },
|
|
187
|
+
required: ["group_id"]
|
|
188
|
+
},
|
|
189
|
+
async handler(ctx, p) {
|
|
190
|
+
return ctx.client.get(`/groups/${String(p.group_id)}/members`);
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "rine_group_invite",
|
|
195
|
+
description: "Invite an agent to a rine group. The invited agent can then join using the group join command.",
|
|
196
|
+
inputSchema: {
|
|
197
|
+
type: "object",
|
|
198
|
+
properties: {
|
|
199
|
+
group_id: {
|
|
200
|
+
type: "string",
|
|
201
|
+
description: "Group UUID"
|
|
202
|
+
},
|
|
203
|
+
agent: {
|
|
204
|
+
type: "string",
|
|
205
|
+
description: "Agent UUID or handle to invite"
|
|
206
|
+
},
|
|
207
|
+
message: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "Optional invitation message"
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
required: ["group_id", "agent"]
|
|
213
|
+
},
|
|
214
|
+
async handler(ctx, p) {
|
|
215
|
+
const body = { agent_id: await resolveToUuid(ctx.apiUrl, String(p.agent)) };
|
|
216
|
+
if (p.message) body.message = String(p.message);
|
|
217
|
+
return ctx.client.post(`/groups/${String(p.group_id)}/invite`, body);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
const identityTools = [{
|
|
222
|
+
name: "rine_whoami",
|
|
223
|
+
description: "Show current rine identity — org name, agent handle, trust tier, and encryption key status. Use to verify account setup.",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
type: "object",
|
|
226
|
+
properties: {}
|
|
227
|
+
},
|
|
228
|
+
async handler(ctx) {
|
|
229
|
+
const [org, agentsData] = await Promise.all([ctx.client.get("/org"), ctx.client.get("/agents")]);
|
|
230
|
+
return {
|
|
231
|
+
org,
|
|
232
|
+
agents: agentsData.items.map((a) => ({
|
|
233
|
+
...a,
|
|
234
|
+
has_keys: agentKeysExist(ctx.configDir, a.id)
|
|
235
|
+
}))
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}, {
|
|
239
|
+
name: "rine_poll",
|
|
240
|
+
description: "Lightweight inbox count check — returns the number of pending messages without reading them. Requires a registered agent with a poll token (generated during onboarding or via 'rine poll-token'). No auth needed for subsequent checks.",
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: "object",
|
|
243
|
+
properties: { agent: {
|
|
244
|
+
type: "string",
|
|
245
|
+
description: "Agent UUID or handle (needed only for first-time poll token generation)"
|
|
246
|
+
} }
|
|
247
|
+
},
|
|
248
|
+
async handler(ctx) {
|
|
249
|
+
const pollUrl = loadCredentials(ctx.configDir).default?.poll_url;
|
|
250
|
+
if (!pollUrl) throw new Error("No poll token found. Generate one with 'rine poll-token' first.");
|
|
251
|
+
const res = await fetch(pollUrl, { signal: AbortSignal.timeout(3e4) });
|
|
252
|
+
if (!res.ok) throw new Error(`Poll failed: ${res.status} ${res.statusText}`);
|
|
253
|
+
const body = await res.json();
|
|
254
|
+
if (typeof body.count !== "number") throw new Error(`Unexpected poll response: ${JSON.stringify(body)}`);
|
|
255
|
+
return { count: body.count };
|
|
256
|
+
}
|
|
257
|
+
}];
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/tools/index.ts
|
|
260
|
+
const tools = [
|
|
261
|
+
...[
|
|
262
|
+
{
|
|
263
|
+
name: "rine_send",
|
|
264
|
+
description: "Send an agent-to-agent message via rine. Use when sending messages, task requests, or notifications to another agent or group handle (name@org.rine.network or #group@org.rine.network).",
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: "object",
|
|
267
|
+
properties: {
|
|
268
|
+
to: {
|
|
269
|
+
type: "string",
|
|
270
|
+
description: "Recipient handle or agent UUID"
|
|
271
|
+
},
|
|
272
|
+
payload: {
|
|
273
|
+
type: "object",
|
|
274
|
+
description: "Message payload (any JSON object)"
|
|
275
|
+
},
|
|
276
|
+
type: {
|
|
277
|
+
type: "string",
|
|
278
|
+
description: "Message type (default: rine.v1.dm). Common: rine.v1.task_request, rine.v1.task_response"
|
|
279
|
+
},
|
|
280
|
+
agent: {
|
|
281
|
+
type: "string",
|
|
282
|
+
description: "Sender agent (UUID, handle, or name). Auto-resolved for single-agent orgs"
|
|
283
|
+
},
|
|
284
|
+
idempotency_key: {
|
|
285
|
+
type: "string",
|
|
286
|
+
description: "Unique key to prevent duplicate sends on retry"
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
required: ["to", "payload"]
|
|
290
|
+
},
|
|
291
|
+
async handler(ctx, p) {
|
|
292
|
+
const agents = await fetchAgents(ctx.client);
|
|
293
|
+
const agentId = await resolveAgent(ctx.apiUrl, agents, p.agent);
|
|
294
|
+
const to = String(p.to);
|
|
295
|
+
if (to.startsWith("#")) {
|
|
296
|
+
const groupHandle = to.slice(1);
|
|
297
|
+
const extraHeaders = { "X-Rine-Agent": agentId };
|
|
298
|
+
const { state, groupId } = await getOrCreateSenderKey(ctx.client, ctx.configDir, agentId, groupHandle, extraHeaders);
|
|
299
|
+
const { result } = await encryptGroupMessage(ctx.configDir, agentId, groupId, state, p.payload);
|
|
300
|
+
const headers = { "X-Rine-Agent": agentId };
|
|
301
|
+
if (p.idempotency_key) headers["Idempotency-Key"] = String(p.idempotency_key);
|
|
302
|
+
return ctx.client.post("/messages", {
|
|
303
|
+
to_handle: to,
|
|
304
|
+
type: p.type ?? "rine.v1.dm",
|
|
305
|
+
...result
|
|
306
|
+
}, headers);
|
|
307
|
+
}
|
|
308
|
+
const recipientId = await resolveToUuid(ctx.apiUrl, to);
|
|
309
|
+
const recipientEncPk = await fetchRecipientEncryptionKey(ctx.client, recipientId);
|
|
310
|
+
const result = await encryptMessage(ctx.configDir, agentId, recipientEncPk, p.payload);
|
|
311
|
+
const headers = { "X-Rine-Agent": agentId };
|
|
312
|
+
if (p.idempotency_key) headers["Idempotency-Key"] = String(p.idempotency_key);
|
|
313
|
+
return ctx.client.post("/messages", {
|
|
314
|
+
to_agent_id: recipientId,
|
|
315
|
+
type: p.type ?? "rine.v1.dm",
|
|
316
|
+
...result
|
|
317
|
+
}, headers);
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: "rine_inbox",
|
|
322
|
+
description: "Check the rine inbox for new agent-to-agent messages. Use when checking for messages, triaging inbox, or polling for new work.",
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {
|
|
326
|
+
agent: {
|
|
327
|
+
type: "string",
|
|
328
|
+
description: "Agent UUID, handle, or name"
|
|
329
|
+
},
|
|
330
|
+
limit: {
|
|
331
|
+
type: "number",
|
|
332
|
+
description: "Max messages to return (default 10)"
|
|
333
|
+
},
|
|
334
|
+
cursor: {
|
|
335
|
+
type: "string",
|
|
336
|
+
description: "Pagination cursor from previous response"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
async handler(ctx, p) {
|
|
341
|
+
const agents = await fetchAgents(ctx.client);
|
|
342
|
+
const agentId = await resolveAgent(ctx.apiUrl, agents, p.agent);
|
|
343
|
+
const params = {};
|
|
344
|
+
if (p.limit) params.limit = Number(p.limit);
|
|
345
|
+
if (p.cursor) params.cursor = String(p.cursor);
|
|
346
|
+
const response = await ctx.client.get(`/agents/${agentId}/messages`, params, { "X-Rine-Agent": agentId });
|
|
347
|
+
const items = [];
|
|
348
|
+
for (const msg of response.items) try {
|
|
349
|
+
if (msg.encryption_version === "hpke-v1" && agentKeysExist(ctx.configDir, agentId)) {
|
|
350
|
+
const result = await decryptMessage(ctx.configDir, agentId, msg.encrypted_payload, ctx.client);
|
|
351
|
+
ingestSenderKeyDistribution(ctx.configDir, agentId, msg.type, result);
|
|
352
|
+
items.push({
|
|
353
|
+
...msg,
|
|
354
|
+
decrypted: JSON.parse(result.plaintext),
|
|
355
|
+
verified: result.verified
|
|
356
|
+
});
|
|
357
|
+
} else if (msg.encryption_version === "sender-key-v1" && agentKeysExist(ctx.configDir, agentId) && msg.group_id) {
|
|
358
|
+
const result = await decryptGroupMessage(ctx.configDir, agentId, msg.group_id, msg.encrypted_payload, ctx.client);
|
|
359
|
+
items.push({
|
|
360
|
+
...msg,
|
|
361
|
+
decrypted: JSON.parse(result.plaintext),
|
|
362
|
+
verified: result.verified
|
|
363
|
+
});
|
|
364
|
+
} else items.push(msg);
|
|
365
|
+
} catch {
|
|
366
|
+
items.push(msg);
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
...response,
|
|
370
|
+
items
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: "rine_read",
|
|
376
|
+
description: "Read and decrypt a specific rine message by ID. Returns the full message with decrypted payload and signature verification status.",
|
|
377
|
+
inputSchema: {
|
|
378
|
+
type: "object",
|
|
379
|
+
properties: { message_id: {
|
|
380
|
+
type: "string",
|
|
381
|
+
description: "Message UUID to read"
|
|
382
|
+
} },
|
|
383
|
+
required: ["message_id"]
|
|
384
|
+
},
|
|
385
|
+
async handler(ctx, p) {
|
|
386
|
+
const msg = await ctx.client.get(`/messages/${String(p.message_id)}`);
|
|
387
|
+
const agentId = msg.to_agent_id ?? msg.from_agent_id;
|
|
388
|
+
if (!agentId || !agentKeysExist(ctx.configDir, agentId)) return msg;
|
|
389
|
+
try {
|
|
390
|
+
if (msg.encryption_version === "hpke-v1") {
|
|
391
|
+
const result = await decryptMessage(ctx.configDir, agentId, msg.encrypted_payload, ctx.client);
|
|
392
|
+
return {
|
|
393
|
+
...msg,
|
|
394
|
+
decrypted: JSON.parse(result.plaintext),
|
|
395
|
+
verified: result.verified
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
if (msg.encryption_version === "sender-key-v1" && msg.group_id) {
|
|
399
|
+
const result = await decryptGroupMessage(ctx.configDir, agentId, msg.group_id, msg.encrypted_payload, ctx.client);
|
|
400
|
+
return {
|
|
401
|
+
...msg,
|
|
402
|
+
decrypted: JSON.parse(result.plaintext),
|
|
403
|
+
verified: result.verified
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
} catch {}
|
|
407
|
+
return msg;
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "rine_reply",
|
|
412
|
+
description: "Reply to a rine message within the same conversation. Auto-routes to the original sender and preserves conversation context.",
|
|
413
|
+
inputSchema: {
|
|
414
|
+
type: "object",
|
|
415
|
+
properties: {
|
|
416
|
+
message_id: {
|
|
417
|
+
type: "string",
|
|
418
|
+
description: "Message UUID to reply to"
|
|
419
|
+
},
|
|
420
|
+
payload: {
|
|
421
|
+
type: "object",
|
|
422
|
+
description: "Reply payload (any JSON object)"
|
|
423
|
+
},
|
|
424
|
+
type: {
|
|
425
|
+
type: "string",
|
|
426
|
+
description: "Override message type (defaults to original message's type)"
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
required: ["message_id", "payload"]
|
|
430
|
+
},
|
|
431
|
+
async handler(ctx, p) {
|
|
432
|
+
const original = await ctx.client.get(`/messages/${String(p.message_id)}`);
|
|
433
|
+
const agentId = original.to_agent_id;
|
|
434
|
+
const recipientId = original.from_agent_id;
|
|
435
|
+
if (!agentId || !recipientId) throw new Error("Cannot determine sender/recipient from message");
|
|
436
|
+
const recipientEncPk = await fetchRecipientEncryptionKey(ctx.client, recipientId);
|
|
437
|
+
const result = await encryptMessage(ctx.configDir, agentId, recipientEncPk, p.payload);
|
|
438
|
+
return ctx.client.post("/messages", {
|
|
439
|
+
to_agent_id: recipientId,
|
|
440
|
+
type: p.type ?? original.type,
|
|
441
|
+
conversation_id: original.conversation_id,
|
|
442
|
+
...result
|
|
443
|
+
}, { "X-Rine-Agent": agentId });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
],
|
|
447
|
+
...discoveryTools,
|
|
448
|
+
...groupTools,
|
|
449
|
+
...identityTools
|
|
450
|
+
];
|
|
451
|
+
//#endregion
|
|
452
|
+
export { tools as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rine-network/mcp",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "MCP server for rine.network — agent-to-agent messaging for AI",
|
|
5
5
|
"author": "mmmbs <mmmbs@proton.me>",
|
|
6
6
|
"license": "EUPL-1.2",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
".": "./dist/server.js"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
|
-
"build": "tsdown src/server.ts src/
|
|
22
|
+
"build": "tsdown src/server.ts src/tools/index.ts --format esm --outDir dist --clean",
|
|
23
23
|
"prepublishOnly": "npm run build",
|
|
24
24
|
"typecheck": "tsc --noEmit",
|
|
25
25
|
"lint": "biome check .",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"dev": "tsx src/server.ts"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
31
|
+
"@rine-network/core": "file:../rine-core"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@biomejs/biome": "^1.9.0",
|
package/dist/cli.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
//#region src/cli.ts
|
|
7
|
-
const DEFAULT_TIMEOUT = 3e4;
|
|
8
|
-
/** Resolve the rine CLI binary path. */
|
|
9
|
-
function rinePath() {
|
|
10
|
-
return process.env.RINE_CLI_PATH ?? "rine";
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Resolve the rine config directory.
|
|
14
|
-
* Priority: $RINE_CONFIG_DIR > cwd/.rine (if exists) > ~/.config/rine
|
|
15
|
-
*/
|
|
16
|
-
function configDir() {
|
|
17
|
-
if (process.env.RINE_CONFIG_DIR) return process.env.RINE_CONFIG_DIR;
|
|
18
|
-
const local = join(process.cwd(), ".rine");
|
|
19
|
-
if (existsSync(local)) return local;
|
|
20
|
-
return join(homedir(), ".config", "rine");
|
|
21
|
-
}
|
|
22
|
-
/** Max execution time for CLI commands. */
|
|
23
|
-
function timeout() {
|
|
24
|
-
const val = process.env.RINE_MCP_TIMEOUT;
|
|
25
|
-
return val ? Number.parseInt(val, 10) : DEFAULT_TIMEOUT;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Execute a rine CLI command with --json and return parsed output.
|
|
29
|
-
*
|
|
30
|
-
* @param args - CLI arguments (--json is appended automatically)
|
|
31
|
-
* @param opts.stdin - Optional data to write to stdin (for --payload-file -)
|
|
32
|
-
*/
|
|
33
|
-
async function runCli(args, opts) {
|
|
34
|
-
return new Promise((resolve, reject) => {
|
|
35
|
-
const bin = rinePath();
|
|
36
|
-
const proc = spawn(bin, [...args, "--json"], {
|
|
37
|
-
env: process.env,
|
|
38
|
-
stdio: [
|
|
39
|
-
"pipe",
|
|
40
|
-
"pipe",
|
|
41
|
-
"pipe"
|
|
42
|
-
]
|
|
43
|
-
});
|
|
44
|
-
let stdout = "";
|
|
45
|
-
let stderr = "";
|
|
46
|
-
const timer = setTimeout(() => {
|
|
47
|
-
proc.kill("SIGTERM");
|
|
48
|
-
reject(/* @__PURE__ */ new Error(`Timeout after ${timeout()}ms: rine ${args.join(" ")}`));
|
|
49
|
-
}, timeout());
|
|
50
|
-
proc.stdout.on("data", (d) => {
|
|
51
|
-
stdout += d.toString();
|
|
52
|
-
});
|
|
53
|
-
proc.stderr.on("data", (d) => {
|
|
54
|
-
stderr += d.toString();
|
|
55
|
-
});
|
|
56
|
-
proc.on("error", (err) => {
|
|
57
|
-
clearTimeout(timer);
|
|
58
|
-
if (err.code === "ENOENT") reject(/* @__PURE__ */ new Error(`rine CLI not found at "${bin}". Install: npm i -g @rine-network/cli`));
|
|
59
|
-
else reject(err);
|
|
60
|
-
});
|
|
61
|
-
proc.on("close", (code) => {
|
|
62
|
-
clearTimeout(timer);
|
|
63
|
-
if (code !== 0) {
|
|
64
|
-
reject(new Error(stderr.trim() || `rine exited with code ${code}`));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
resolve(JSON.parse(stdout));
|
|
69
|
-
} catch {
|
|
70
|
-
reject(/* @__PURE__ */ new Error(`Failed to parse CLI output as JSON.\nstdout: ${stdout}\nstderr: ${stderr}`));
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
proc.stdin.on("error", () => {});
|
|
74
|
-
if (opts?.stdin) {
|
|
75
|
-
proc.stdin.write(opts.stdin);
|
|
76
|
-
proc.stdin.end();
|
|
77
|
-
} else proc.stdin.end();
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Read the poll_url from credentials.json for the given profile.
|
|
82
|
-
* Returns null if not found.
|
|
83
|
-
*/
|
|
84
|
-
async function readPollUrl(profile = "default") {
|
|
85
|
-
try {
|
|
86
|
-
const path = join(configDir(), "credentials.json");
|
|
87
|
-
return JSON.parse(await readFile(path, "utf-8"))?.[profile]?.poll_url ?? null;
|
|
88
|
-
} catch {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Lightweight unauthenticated inbox count check via poll_url.
|
|
94
|
-
* Generates a poll token if none exists.
|
|
95
|
-
*/
|
|
96
|
-
async function poll(agent) {
|
|
97
|
-
let url = await readPollUrl();
|
|
98
|
-
if (!url) try {
|
|
99
|
-
const args = ["poll-token"];
|
|
100
|
-
if (agent) args.push("--as", agent);
|
|
101
|
-
url = (await runCli(args)).poll_url;
|
|
102
|
-
} catch (err) {
|
|
103
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
104
|
-
throw new Error(`No poll token found. Run "rine poll-token" after registering an agent. (${msg})`);
|
|
105
|
-
}
|
|
106
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(timeout()) });
|
|
107
|
-
if (!res.ok) throw new Error(`Poll request failed: ${res.status} ${res.statusText}`);
|
|
108
|
-
const body = await res.json();
|
|
109
|
-
if (typeof body.count !== "number") throw new Error(`Unexpected poll response: ${JSON.stringify(body)}`);
|
|
110
|
-
return { count: body.count };
|
|
111
|
-
}
|
|
112
|
-
//#endregion
|
|
113
|
-
export { configDir, poll, readPollUrl, runCli };
|
package/dist/tools.js
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
//#region src/tools.ts
|
|
2
|
-
function push(args, flag, val) {
|
|
3
|
-
if (val !== void 0 && val !== null && val !== "") args.push(flag, String(val));
|
|
4
|
-
}
|
|
5
|
-
function pushBool(args, flag, val) {
|
|
6
|
-
if (val === true) args.push(flag);
|
|
7
|
-
}
|
|
8
|
-
function withAgent(args, p) {
|
|
9
|
-
push(args, "--as", p.agent);
|
|
10
|
-
}
|
|
11
|
-
const tools = [
|
|
12
|
-
{
|
|
13
|
-
name: "rine_send",
|
|
14
|
-
description: "Send an agent-to-agent message via rine. Use when sending messages, task requests, or notifications to another agent or group handle (name@org.rine.network or #group@org.rine.network).",
|
|
15
|
-
inputSchema: {
|
|
16
|
-
type: "object",
|
|
17
|
-
properties: {
|
|
18
|
-
to: {
|
|
19
|
-
type: "string",
|
|
20
|
-
description: "Recipient handle or agent UUID"
|
|
21
|
-
},
|
|
22
|
-
payload: {
|
|
23
|
-
type: "object",
|
|
24
|
-
description: "Message payload (any JSON object)"
|
|
25
|
-
},
|
|
26
|
-
type: {
|
|
27
|
-
type: "string",
|
|
28
|
-
description: "Message type (default: rine.v1.dm). Common: rine.v1.task_request, rine.v1.task_response"
|
|
29
|
-
},
|
|
30
|
-
agent: {
|
|
31
|
-
type: "string",
|
|
32
|
-
description: "Sender agent (UUID, handle, or name). Auto-resolved for single-agent orgs"
|
|
33
|
-
},
|
|
34
|
-
idempotency_key: {
|
|
35
|
-
type: "string",
|
|
36
|
-
description: "Unique key to prevent duplicate sends on retry"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
required: ["to", "payload"]
|
|
40
|
-
},
|
|
41
|
-
buildArgs(p) {
|
|
42
|
-
const args = [
|
|
43
|
-
"send",
|
|
44
|
-
"--to",
|
|
45
|
-
String(p.to),
|
|
46
|
-
"--payload-file",
|
|
47
|
-
"-"
|
|
48
|
-
];
|
|
49
|
-
push(args, "--type", p.type);
|
|
50
|
-
push(args, "--idempotency-key", p.idempotency_key);
|
|
51
|
-
withAgent(args, p);
|
|
52
|
-
return {
|
|
53
|
-
args,
|
|
54
|
-
stdin: JSON.stringify(p.payload)
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: "rine_inbox",
|
|
60
|
-
description: "Check the rine inbox for new agent-to-agent messages. Use when checking for messages, triaging inbox, or polling for new work.",
|
|
61
|
-
inputSchema: {
|
|
62
|
-
type: "object",
|
|
63
|
-
properties: {
|
|
64
|
-
agent: {
|
|
65
|
-
type: "string",
|
|
66
|
-
description: "Agent UUID, handle, or name"
|
|
67
|
-
},
|
|
68
|
-
limit: {
|
|
69
|
-
type: "number",
|
|
70
|
-
description: "Max messages to return (default 10)"
|
|
71
|
-
},
|
|
72
|
-
cursor: {
|
|
73
|
-
type: "string",
|
|
74
|
-
description: "Pagination cursor from previous response"
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
buildArgs(p) {
|
|
79
|
-
const args = ["inbox"];
|
|
80
|
-
withAgent(args, p);
|
|
81
|
-
push(args, "--limit", p.limit);
|
|
82
|
-
push(args, "--cursor", p.cursor);
|
|
83
|
-
return { args };
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
name: "rine_read",
|
|
88
|
-
description: "Read and decrypt a specific rine message by ID. Returns the full message with decrypted payload and signature verification status.",
|
|
89
|
-
inputSchema: {
|
|
90
|
-
type: "object",
|
|
91
|
-
properties: { message_id: {
|
|
92
|
-
type: "string",
|
|
93
|
-
description: "Message UUID to read"
|
|
94
|
-
} },
|
|
95
|
-
required: ["message_id"]
|
|
96
|
-
},
|
|
97
|
-
buildArgs(p) {
|
|
98
|
-
return { args: ["read", String(p.message_id)] };
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: "rine_reply",
|
|
103
|
-
description: "Reply to a rine message within the same conversation. Auto-routes to the original sender and preserves conversation context.",
|
|
104
|
-
inputSchema: {
|
|
105
|
-
type: "object",
|
|
106
|
-
properties: {
|
|
107
|
-
message_id: {
|
|
108
|
-
type: "string",
|
|
109
|
-
description: "Message UUID to reply to"
|
|
110
|
-
},
|
|
111
|
-
payload: {
|
|
112
|
-
type: "object",
|
|
113
|
-
description: "Reply payload (any JSON object)"
|
|
114
|
-
},
|
|
115
|
-
type: {
|
|
116
|
-
type: "string",
|
|
117
|
-
description: "Override message type (defaults to original message's type)"
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
required: ["message_id", "payload"]
|
|
121
|
-
},
|
|
122
|
-
buildArgs(p) {
|
|
123
|
-
const args = [
|
|
124
|
-
"reply",
|
|
125
|
-
String(p.message_id),
|
|
126
|
-
"--payload-file",
|
|
127
|
-
"-"
|
|
128
|
-
];
|
|
129
|
-
push(args, "--type", p.type);
|
|
130
|
-
return {
|
|
131
|
-
args,
|
|
132
|
-
stdin: JSON.stringify(p.payload)
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
name: "rine_discover",
|
|
138
|
-
description: "Search the rine agent directory. Find agents by capability, category, language, jurisdiction, or free-text query. Use when looking for agents to collaborate with or when the user asks to find an agent.",
|
|
139
|
-
inputSchema: {
|
|
140
|
-
type: "object",
|
|
141
|
-
properties: {
|
|
142
|
-
query: {
|
|
143
|
-
type: "string",
|
|
144
|
-
description: "Free-text search query"
|
|
145
|
-
},
|
|
146
|
-
category: {
|
|
147
|
-
type: "string",
|
|
148
|
-
description: "Filter by category"
|
|
149
|
-
},
|
|
150
|
-
tag: {
|
|
151
|
-
type: "string",
|
|
152
|
-
description: "Filter by tag"
|
|
153
|
-
},
|
|
154
|
-
language: {
|
|
155
|
-
type: "string",
|
|
156
|
-
description: "Filter by language (ISO 639-1)"
|
|
157
|
-
},
|
|
158
|
-
jurisdiction: {
|
|
159
|
-
type: "string",
|
|
160
|
-
description: "Filter by jurisdiction (e.g. EU, DE)"
|
|
161
|
-
},
|
|
162
|
-
verified: {
|
|
163
|
-
type: "boolean",
|
|
164
|
-
description: "Only show verified agents"
|
|
165
|
-
},
|
|
166
|
-
pricing_model: {
|
|
167
|
-
type: "string",
|
|
168
|
-
description: "Filter by pricing: free, per_request, subscription, negotiated"
|
|
169
|
-
},
|
|
170
|
-
limit: {
|
|
171
|
-
type: "number",
|
|
172
|
-
description: "Max results (default 10)"
|
|
173
|
-
},
|
|
174
|
-
cursor: {
|
|
175
|
-
type: "string",
|
|
176
|
-
description: "Pagination cursor"
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
buildArgs(p) {
|
|
181
|
-
const args = ["discover", "agents"];
|
|
182
|
-
push(args, "-q", p.query);
|
|
183
|
-
push(args, "--category", p.category);
|
|
184
|
-
push(args, "--tag", p.tag);
|
|
185
|
-
push(args, "--language", p.language);
|
|
186
|
-
push(args, "--jurisdiction", p.jurisdiction);
|
|
187
|
-
pushBool(args, "--verified", p.verified);
|
|
188
|
-
push(args, "--pricing-model", p.pricing_model);
|
|
189
|
-
push(args, "--limit", p.limit);
|
|
190
|
-
push(args, "--cursor", p.cursor);
|
|
191
|
-
return { args };
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: "rine_inspect",
|
|
196
|
-
description: "Get detailed profile of a rine agent including skills, categories, activity stats, and verification status. Accepts a UUID or handle.",
|
|
197
|
-
inputSchema: {
|
|
198
|
-
type: "object",
|
|
199
|
-
properties: { agent: {
|
|
200
|
-
type: "string",
|
|
201
|
-
description: "Agent UUID or handle (e.g. bot@acme.rine.network)"
|
|
202
|
-
} },
|
|
203
|
-
required: ["agent"]
|
|
204
|
-
},
|
|
205
|
-
buildArgs(p) {
|
|
206
|
-
return { args: [
|
|
207
|
-
"discover",
|
|
208
|
-
"inspect",
|
|
209
|
-
String(p.agent)
|
|
210
|
-
] };
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
name: "rine_groups",
|
|
215
|
-
description: "List messaging groups the current org belongs to. Shows group names, handles, enrollment policies, and member counts.",
|
|
216
|
-
inputSchema: {
|
|
217
|
-
type: "object",
|
|
218
|
-
properties: { agent: {
|
|
219
|
-
type: "string",
|
|
220
|
-
description: "Agent UUID, handle, or name"
|
|
221
|
-
} }
|
|
222
|
-
},
|
|
223
|
-
buildArgs(p) {
|
|
224
|
-
const args = ["group", "list"];
|
|
225
|
-
withAgent(args, p);
|
|
226
|
-
return { args };
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
name: "rine_group_create",
|
|
231
|
-
description: "Create a new rine messaging group for multi-agent collaboration. Send messages to groups via #name@org.rine.network handles.",
|
|
232
|
-
inputSchema: {
|
|
233
|
-
type: "object",
|
|
234
|
-
properties: {
|
|
235
|
-
name: {
|
|
236
|
-
type: "string",
|
|
237
|
-
description: "Group name (lowercase, hyphens allowed, max 63 chars)"
|
|
238
|
-
},
|
|
239
|
-
enrollment: {
|
|
240
|
-
type: "string",
|
|
241
|
-
description: "Join policy: open, closed, majority, or unanimity (default: open)"
|
|
242
|
-
},
|
|
243
|
-
visibility: {
|
|
244
|
-
type: "string",
|
|
245
|
-
description: "Visibility: public or private (default: public for open groups)"
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
required: ["name"]
|
|
249
|
-
},
|
|
250
|
-
buildArgs(p) {
|
|
251
|
-
const args = [
|
|
252
|
-
"group",
|
|
253
|
-
"create",
|
|
254
|
-
"--name",
|
|
255
|
-
String(p.name)
|
|
256
|
-
];
|
|
257
|
-
push(args, "--enrollment", p.enrollment);
|
|
258
|
-
push(args, "--visibility", p.visibility);
|
|
259
|
-
return { args };
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
name: "rine_group_join",
|
|
264
|
-
description: "Join a rine group. For open groups, joins immediately. For approval-gated groups, submits a join request that members vote on.",
|
|
265
|
-
inputSchema: {
|
|
266
|
-
type: "object",
|
|
267
|
-
properties: {
|
|
268
|
-
group_id: {
|
|
269
|
-
type: "string",
|
|
270
|
-
description: "Group UUID to join"
|
|
271
|
-
},
|
|
272
|
-
message: {
|
|
273
|
-
type: "string",
|
|
274
|
-
description: "Optional message with join request"
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
required: ["group_id"]
|
|
278
|
-
},
|
|
279
|
-
buildArgs(p) {
|
|
280
|
-
const args = [
|
|
281
|
-
"group",
|
|
282
|
-
"join",
|
|
283
|
-
String(p.group_id)
|
|
284
|
-
];
|
|
285
|
-
push(args, "--message", p.message);
|
|
286
|
-
return { args };
|
|
287
|
-
}
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
name: "rine_group_members",
|
|
291
|
-
description: "List members of a rine group with their handles, roles, and join dates.",
|
|
292
|
-
inputSchema: {
|
|
293
|
-
type: "object",
|
|
294
|
-
properties: { group_id: {
|
|
295
|
-
type: "string",
|
|
296
|
-
description: "Group UUID"
|
|
297
|
-
} },
|
|
298
|
-
required: ["group_id"]
|
|
299
|
-
},
|
|
300
|
-
buildArgs(p) {
|
|
301
|
-
return { args: [
|
|
302
|
-
"group",
|
|
303
|
-
"members",
|
|
304
|
-
String(p.group_id)
|
|
305
|
-
] };
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
name: "rine_group_invite",
|
|
310
|
-
description: "Invite an agent to a rine group. The invited agent can then join using the group join command.",
|
|
311
|
-
inputSchema: {
|
|
312
|
-
type: "object",
|
|
313
|
-
properties: {
|
|
314
|
-
group_id: {
|
|
315
|
-
type: "string",
|
|
316
|
-
description: "Group UUID"
|
|
317
|
-
},
|
|
318
|
-
agent: {
|
|
319
|
-
type: "string",
|
|
320
|
-
description: "Agent UUID or handle to invite"
|
|
321
|
-
},
|
|
322
|
-
message: {
|
|
323
|
-
type: "string",
|
|
324
|
-
description: "Optional invitation message"
|
|
325
|
-
}
|
|
326
|
-
},
|
|
327
|
-
required: ["group_id", "agent"]
|
|
328
|
-
},
|
|
329
|
-
buildArgs(p) {
|
|
330
|
-
const args = [
|
|
331
|
-
"group",
|
|
332
|
-
"invite",
|
|
333
|
-
String(p.group_id),
|
|
334
|
-
"--agent",
|
|
335
|
-
String(p.agent)
|
|
336
|
-
];
|
|
337
|
-
push(args, "--message", p.message);
|
|
338
|
-
return { args };
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
name: "rine_discover_groups",
|
|
343
|
-
description: "Search public rine groups by name or topic. Find groups to join for multi-agent collaboration.",
|
|
344
|
-
inputSchema: {
|
|
345
|
-
type: "object",
|
|
346
|
-
properties: {
|
|
347
|
-
query: {
|
|
348
|
-
type: "string",
|
|
349
|
-
description: "Search query"
|
|
350
|
-
},
|
|
351
|
-
limit: {
|
|
352
|
-
type: "number",
|
|
353
|
-
description: "Max results"
|
|
354
|
-
},
|
|
355
|
-
cursor: {
|
|
356
|
-
type: "string",
|
|
357
|
-
description: "Pagination cursor"
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
},
|
|
361
|
-
buildArgs(p) {
|
|
362
|
-
const args = ["discover", "groups"];
|
|
363
|
-
push(args, "-q", p.query);
|
|
364
|
-
push(args, "--limit", p.limit);
|
|
365
|
-
push(args, "--cursor", p.cursor);
|
|
366
|
-
return { args };
|
|
367
|
-
}
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
name: "rine_whoami",
|
|
371
|
-
description: "Show current rine identity — org name, agent handle, trust tier, and encryption key status. Use to verify account setup.",
|
|
372
|
-
inputSchema: {
|
|
373
|
-
type: "object",
|
|
374
|
-
properties: {}
|
|
375
|
-
},
|
|
376
|
-
buildArgs() {
|
|
377
|
-
return { args: ["whoami"] };
|
|
378
|
-
}
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
name: "rine_poll",
|
|
382
|
-
description: "Lightweight inbox count check — returns the number of pending messages without reading them. Requires a registered agent with a poll token (generated during onboarding or via 'rine poll-token'). No auth needed for subsequent checks.",
|
|
383
|
-
inputSchema: {
|
|
384
|
-
type: "object",
|
|
385
|
-
properties: { agent: {
|
|
386
|
-
type: "string",
|
|
387
|
-
description: "Agent UUID or handle (needed only for first-time poll token generation)"
|
|
388
|
-
} }
|
|
389
|
-
},
|
|
390
|
-
buildArgs() {
|
|
391
|
-
throw new Error("rine_poll uses HTTP fetch, not CLI dispatch");
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
];
|
|
395
|
-
//#endregion
|
|
396
|
-
export { tools };
|