@onsignet/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/index.js +398 -167
- package/package.json +1 -1
- package/dist/daemon-client.d.ts +0 -107
- package/dist/daemon-client.js +0 -203
- package/dist/index.d.ts +0 -7
- package/dist/types.d.ts +0 -53
- package/dist/types.js +0 -4
package/dist/index.js
CHANGED
|
@@ -1,199 +1,432 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Requires the Signet daemon running at http://127.0.0.1:8766 (or SIGNET_DAEMON_URL).
|
|
6
|
-
*/
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// adapters/mcp/src/index.ts
|
|
7
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
7
|
import { z } from "zod";
|
|
10
|
-
|
|
8
|
+
|
|
9
|
+
// adapters/mcp/src/daemon-client.ts
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
var DEFAULT_DAEMON_URL = "http://127.0.0.1:8766";
|
|
14
|
+
var DATA_DIR = process.env.SIGNET_DATA_DIR ?? join(homedir(), ".Signet");
|
|
15
|
+
function getDaemonBaseUrl() {
|
|
16
|
+
return (process.env.SIGNET_DAEMON_URL ?? DEFAULT_DAEMON_URL).replace(/\/$/, "");
|
|
17
|
+
}
|
|
18
|
+
var cachedApiToken;
|
|
19
|
+
function getDaemonApiToken() {
|
|
20
|
+
if (cachedApiToken !== void 0) return cachedApiToken;
|
|
21
|
+
if (process.env.SIGNET_API_TOKEN) {
|
|
22
|
+
cachedApiToken = process.env.SIGNET_API_TOKEN;
|
|
23
|
+
return cachedApiToken;
|
|
24
|
+
}
|
|
25
|
+
const tokenPath = join(DATA_DIR, "api-token");
|
|
26
|
+
try {
|
|
27
|
+
if (existsSync(tokenPath)) {
|
|
28
|
+
cachedApiToken = readFileSync(tokenPath, "utf8").trim() || null;
|
|
29
|
+
return cachedApiToken;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
cachedApiToken = null;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
async function daemonFetch(path, options) {
|
|
37
|
+
const base = getDaemonBaseUrl();
|
|
38
|
+
const url = `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timeout = setTimeout(() => controller.abort(), 15e3);
|
|
41
|
+
try {
|
|
42
|
+
const headers = { "Content-Type": "application/json" };
|
|
43
|
+
const token = getDaemonApiToken();
|
|
44
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
45
|
+
return await fetch(url, {
|
|
46
|
+
...options,
|
|
47
|
+
signal: controller.signal,
|
|
48
|
+
headers: { ...headers, ...options?.headers }
|
|
49
|
+
});
|
|
50
|
+
} catch (e) {
|
|
51
|
+
clearTimeout(timeout);
|
|
52
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
53
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
54
|
+
throw new Error("Signet daemon is not running. Start it with: npx signet-agent start");
|
|
55
|
+
}
|
|
56
|
+
throw e;
|
|
57
|
+
} finally {
|
|
58
|
+
clearTimeout(timeout);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function safeJson(res) {
|
|
62
|
+
try {
|
|
63
|
+
return await res.json();
|
|
64
|
+
} catch {
|
|
65
|
+
return { error: `HTTP ${res.status} (non-JSON response)` };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function status() {
|
|
69
|
+
const res = await daemonFetch("/status");
|
|
70
|
+
const data = await safeJson(res);
|
|
71
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
72
|
+
return data;
|
|
73
|
+
}
|
|
74
|
+
async function discover(params) {
|
|
75
|
+
const searchParams = new URLSearchParams();
|
|
76
|
+
if (params?.query) searchParams.set("q", params.query);
|
|
77
|
+
if (params?.capability) searchParams.set("capability", params.capability);
|
|
78
|
+
if (params?.accepts_payments != null) searchParams.set("accepts_payments", String(params.accepts_payments));
|
|
79
|
+
if (params?.sort) searchParams.set("sort", params.sort);
|
|
80
|
+
if (params?.limit) searchParams.set("limit", String(params.limit));
|
|
81
|
+
if (params?.verification_tier) searchParams.set("verification_tier", params.verification_tier);
|
|
82
|
+
if (params?.online_only != null) searchParams.set("online_only", String(params.online_only));
|
|
83
|
+
if (params?.is_compute_provider != null) searchParams.set("is_compute_provider", String(params.is_compute_provider));
|
|
84
|
+
const qs = searchParams.toString();
|
|
85
|
+
const path = qs ? `/directory/search?${qs}` : "/discover";
|
|
86
|
+
const res = await daemonFetch(path);
|
|
87
|
+
const data = await safeJson(res);
|
|
88
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
89
|
+
return { agents: data.agents ?? (Array.isArray(data) ? data : []) };
|
|
90
|
+
}
|
|
91
|
+
async function send(params) {
|
|
92
|
+
const res = await daemonFetch("/send", {
|
|
93
|
+
method: "POST",
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
to: params.recipientId,
|
|
96
|
+
recipientX25519PublicKey: params.recipientX25519PublicKey,
|
|
97
|
+
payload: {
|
|
98
|
+
type: params.payload?.type ?? "general/1",
|
|
99
|
+
content: params.payload?.content ?? {}
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
const data = await safeJson(res);
|
|
104
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
105
|
+
return { id: data.id, status: data.status ?? "sent" };
|
|
106
|
+
}
|
|
107
|
+
async function getMessages(limit) {
|
|
108
|
+
const query = limit != null ? `?limit=${Number(limit)}` : "";
|
|
109
|
+
const res = await daemonFetch(`/messages${query}`);
|
|
110
|
+
const data = await safeJson(res);
|
|
111
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
112
|
+
return { messages: data.messages ?? [] };
|
|
113
|
+
}
|
|
114
|
+
async function getWebChatIncoming() {
|
|
115
|
+
try {
|
|
116
|
+
const res = await daemonFetch("/web-chat/incoming");
|
|
117
|
+
const data = await safeJson(res);
|
|
118
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}` };
|
|
119
|
+
return { messages: data.messages ?? [] };
|
|
120
|
+
} catch {
|
|
121
|
+
return { messages: [] };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function replyWebChat(sessionId, text) {
|
|
125
|
+
const res = await daemonFetch("/web-chat/reply", {
|
|
126
|
+
method: "POST",
|
|
127
|
+
body: JSON.stringify({ sessionId, text })
|
|
128
|
+
});
|
|
129
|
+
const data = await safeJson(res);
|
|
130
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}` };
|
|
131
|
+
return { ok: data.ok };
|
|
132
|
+
}
|
|
133
|
+
async function getPending() {
|
|
134
|
+
const res = await daemonFetch("/pending");
|
|
135
|
+
const data = await safeJson(res);
|
|
136
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
137
|
+
return { pending: data.pending ?? [] };
|
|
138
|
+
}
|
|
139
|
+
async function approve(messageId) {
|
|
140
|
+
const res = await daemonFetch("/approve", {
|
|
141
|
+
method: "POST",
|
|
142
|
+
body: JSON.stringify({ messageId })
|
|
143
|
+
});
|
|
144
|
+
const data = await safeJson(res);
|
|
145
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
146
|
+
return { ok: data.ok };
|
|
147
|
+
}
|
|
148
|
+
async function deny(messageId, reason) {
|
|
149
|
+
const res = await daemonFetch("/deny", {
|
|
150
|
+
method: "POST",
|
|
151
|
+
body: JSON.stringify({ messageId, reason })
|
|
152
|
+
});
|
|
153
|
+
const data = await safeJson(res);
|
|
154
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
155
|
+
return { ok: data.ok };
|
|
156
|
+
}
|
|
157
|
+
async function getProfile(agentId) {
|
|
158
|
+
const res = await daemonFetch(`/directory/agents/${encodeURIComponent(agentId)}`);
|
|
159
|
+
const data = await safeJson(res);
|
|
160
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
161
|
+
return { profile: data };
|
|
162
|
+
}
|
|
163
|
+
async function updateProfile(fields) {
|
|
164
|
+
const res = await daemonFetch("/directory/profile", {
|
|
165
|
+
method: "PUT",
|
|
166
|
+
body: JSON.stringify(fields)
|
|
167
|
+
});
|
|
168
|
+
const data = await safeJson(res);
|
|
169
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
170
|
+
return { profile: data };
|
|
171
|
+
}
|
|
172
|
+
async function getContacts(query) {
|
|
173
|
+
const qs = query ? `?q=${encodeURIComponent(query)}` : "";
|
|
174
|
+
const res = await daemonFetch(`/contacts${qs}`);
|
|
175
|
+
const data = await safeJson(res);
|
|
176
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
177
|
+
return { contacts: data.contacts ?? (Array.isArray(data) ? data : []) };
|
|
178
|
+
}
|
|
179
|
+
async function addContact(agentId) {
|
|
180
|
+
const res = await daemonFetch("/contacts", {
|
|
181
|
+
method: "POST",
|
|
182
|
+
body: JSON.stringify({ contact_agent_id: agentId })
|
|
183
|
+
});
|
|
184
|
+
const data = await safeJson(res);
|
|
185
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
186
|
+
return { ok: true };
|
|
187
|
+
}
|
|
188
|
+
async function getConversationHistory(withNodeId, limit) {
|
|
189
|
+
const params = new URLSearchParams({ with: withNodeId });
|
|
190
|
+
if (limit) params.set("limit", String(limit));
|
|
191
|
+
const res = await daemonFetch(`/conversation-history?${params.toString()}`);
|
|
192
|
+
const data = await safeJson(res);
|
|
193
|
+
if (!res.ok) return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
194
|
+
return { entries: data.entries ?? [] };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// adapters/mcp/src/index.ts
|
|
11
198
|
function textContent(text) {
|
|
12
|
-
|
|
199
|
+
return { type: "text", text };
|
|
13
200
|
}
|
|
14
201
|
function resultContent(data) {
|
|
15
|
-
|
|
202
|
+
return textContent(JSON.stringify(data, null, 2));
|
|
16
203
|
}
|
|
17
204
|
function errorContent(message, code) {
|
|
18
|
-
|
|
19
|
-
|
|
205
|
+
const obj = code ? { error: message, code } : { error: message };
|
|
206
|
+
return textContent(JSON.stringify(obj));
|
|
20
207
|
}
|
|
21
|
-
|
|
208
|
+
var server = new McpServer(
|
|
209
|
+
{
|
|
22
210
|
name: "signet",
|
|
23
|
-
version: "0.2.0"
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
211
|
+
version: "0.2.0"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
instructions: `Signet is a verified identity, communication, and payment network for AI agents. This MCP server connects you to the Signet network through the local daemon. You can discover other agents, send encrypted messages, request and provide paid services, manage contacts, and coordinate tasks across frameworks (OpenClaw, MCP, Python/LangChain, REST). Every message is signed with your cryptographic identity and encrypted end-to-end. Always respect your owner's spending policies and get approval for payments.`
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
server.tool(
|
|
218
|
+
"signet_status",
|
|
219
|
+
`Check your Signet network connection status and identity. Use this before any network interaction to confirm you're online. Returns your agent ID, relay connection status, public key, and daemon version. Also useful when your owner asks about your network status or when troubleshooting connectivity issues.`,
|
|
220
|
+
{},
|
|
221
|
+
async () => {
|
|
29
222
|
const out = await status();
|
|
30
|
-
if (out.error)
|
|
31
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
223
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
32
224
|
return { content: [resultContent(out)] };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
server.tool(
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
server.tool(
|
|
228
|
+
"signet_discover",
|
|
229
|
+
`Search the Signet agent directory for other agents by capability, name, verification tier, or pricing. Use this to:
|
|
36
230
|
- Find agents that can help with a task (e.g. text classification, research, scheduling)
|
|
37
231
|
- Discover cheap compute providers for model arbitrage (route bulk work to cheaper models)
|
|
38
232
|
- Look up a specific agent or person's agent by name
|
|
39
233
|
- Find verified service providers for high-stakes tasks
|
|
40
|
-
Results include agent profiles with capabilities, pricing, ratings, and online status.`,
|
|
234
|
+
Results include agent profiles with capabilities, pricing, ratings, and online status.`,
|
|
235
|
+
{
|
|
41
236
|
query: z.string().optional().describe("Free-text search (name, description, capability)"),
|
|
42
|
-
capability: z
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.describe("Filter by capability domain: text_classification, summarization, extraction, translation, research, code_review, code_generation, scheduling, coordination, writing, data_analysis, monitoring, customer_service, document_prep, image_analysis, financial_analysis"),
|
|
237
|
+
capability: z.string().optional().describe(
|
|
238
|
+
"Filter by capability domain: text_classification, summarization, extraction, translation, research, code_review, code_generation, scheduling, coordination, writing, data_analysis, monitoring, customer_service, document_prep, image_analysis, financial_analysis"
|
|
239
|
+
),
|
|
46
240
|
accepts_payments: z.boolean().optional().describe("Only agents that accept paid service requests"),
|
|
47
241
|
is_compute_provider: z.boolean().optional().describe("Only agents offering bulk compute capacity"),
|
|
48
|
-
verification_tier: z
|
|
49
|
-
|
|
50
|
-
.optional()
|
|
51
|
-
.describe("Filter by trust tier: free, pro, business"),
|
|
52
|
-
sort: z
|
|
53
|
-
.string()
|
|
54
|
-
.optional()
|
|
55
|
-
.describe("Sort results: rating, price_asc, price_desc, response_time, most_active"),
|
|
242
|
+
verification_tier: z.string().optional().describe("Filter by trust tier: free, pro, business"),
|
|
243
|
+
sort: z.string().optional().describe("Sort results: rating, price_asc, price_desc, response_time, most_active"),
|
|
56
244
|
online_only: z.boolean().optional().describe("Only show currently online agents"),
|
|
57
|
-
limit: z.number().int().min(1).max(50).optional().describe("Max results (default 10, max 50)")
|
|
58
|
-
},
|
|
245
|
+
limit: z.number().int().min(1).max(50).optional().describe("Max results (default 10, max 50)")
|
|
246
|
+
},
|
|
247
|
+
async (args) => {
|
|
59
248
|
const out = await discover(args);
|
|
60
|
-
if (out.error)
|
|
61
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
249
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
62
250
|
return { content: [resultContent({ agents: out.agents })] };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
server.tool(
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
server.tool(
|
|
254
|
+
"signet_send",
|
|
255
|
+
`Send a signed, encrypted message to another agent on the Signet network. Messages are automatically signed with your identity and encrypted end-to-end \u2014 only you and the recipient can read them.
|
|
66
256
|
|
|
67
257
|
Message types:
|
|
68
|
-
- coordination/schedule_request
|
|
69
|
-
- coordination/poll
|
|
70
|
-
- coordination/notify
|
|
71
|
-
- service/request
|
|
72
|
-
- service/offer
|
|
73
|
-
- service/accept
|
|
74
|
-
- service/deliver
|
|
75
|
-
- service/rate
|
|
76
|
-
- inquiry
|
|
258
|
+
- coordination/schedule_request \u2014 Propose a meeting or event
|
|
259
|
+
- coordination/poll \u2014 Ask a group question, collect votes
|
|
260
|
+
- coordination/notify \u2014 Send an update or notification
|
|
261
|
+
- service/request \u2014 Request a paid service (include task, description, items, max_budget)
|
|
262
|
+
- service/offer \u2014 Respond to a service request with terms and price
|
|
263
|
+
- service/accept \u2014 Accept a service offer (triggers payment flow)
|
|
264
|
+
- service/deliver \u2014 Deliver completed work
|
|
265
|
+
- service/rate \u2014 Rate a completed interaction (positive/negative)
|
|
266
|
+
- inquiry \u2014 General question to another agent
|
|
77
267
|
|
|
78
|
-
IMPORTANT: For service/request and service/accept messages, check your owner's spending policy first. Transactions above the auto-approve threshold require human approval.`,
|
|
268
|
+
IMPORTANT: For service/request and service/accept messages, check your owner's spending policy first. Transactions above the auto-approve threshold require human approval.`,
|
|
269
|
+
{
|
|
79
270
|
recipientId: z.string().describe("Recipient agent node ID (e.g. am_...)"),
|
|
80
|
-
recipientX25519PublicKey: z
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.optional()
|
|
86
|
-
.describe("Message type (e.g. service/request, coordination/schedule_request, inquiry). Default: general/1"),
|
|
87
|
-
content: z
|
|
88
|
-
.record(z.unknown())
|
|
89
|
-
.optional()
|
|
90
|
-
.describe("Message content object. Structure depends on type — see tool description for guidance."),
|
|
91
|
-
}, async ({ recipientId, recipientX25519PublicKey, type, content }) => {
|
|
271
|
+
recipientX25519PublicKey: z.string().describe("Recipient's X25519 public key (base64) \u2014 get this from signet_discover or signet_profile results"),
|
|
272
|
+
type: z.string().optional().describe("Message type (e.g. service/request, coordination/schedule_request, inquiry). Default: general/1"),
|
|
273
|
+
content: z.record(z.unknown()).optional().describe("Message content object. Structure depends on type \u2014 see tool description for guidance.")
|
|
274
|
+
},
|
|
275
|
+
async ({ recipientId, recipientX25519PublicKey, type, content }) => {
|
|
92
276
|
const out = await send({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
277
|
+
recipientId,
|
|
278
|
+
recipientX25519PublicKey,
|
|
279
|
+
payload: { type: type ?? "general/1", content: content ?? {} }
|
|
96
280
|
});
|
|
97
|
-
if (out.error)
|
|
98
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
281
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
99
282
|
return { content: [resultContent({ id: out.id, status: out.status })] };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
server.tool(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
server.tool(
|
|
286
|
+
"signet_receive",
|
|
287
|
+
`Check for incoming messages from other agents and website visitors. The daemon queues messages for you. Use this after sending a message and waiting for a response, or periodically when expecting inbound requests (service offers, scheduling responses, etc.). Messages include the sender's identity, message type, content, and timestamp. Web chat messages from visitors on your profile page are also included \u2014 reply to those with signet_reply_chat.`,
|
|
288
|
+
{
|
|
289
|
+
limit: z.number().int().min(1).max(500).optional().describe("Max messages to return (default: all pending)")
|
|
290
|
+
},
|
|
291
|
+
async (args) => {
|
|
292
|
+
const [agentMessages, webChat] = await Promise.all([
|
|
293
|
+
getMessages(args?.limit),
|
|
294
|
+
getWebChatIncoming()
|
|
295
|
+
]);
|
|
296
|
+
if (agentMessages.error) return { content: [errorContent(agentMessages.error, agentMessages.code)], isError: true };
|
|
297
|
+
const webChatMessages = (webChat.messages ?? []).map((m) => ({
|
|
298
|
+
id: m.sessionId,
|
|
299
|
+
from: `web_visitor (${m.visitorAuthLevel})`,
|
|
300
|
+
to: "you",
|
|
301
|
+
payload: { type: "web_chat", content: m.content },
|
|
302
|
+
timestamp: m.receivedAt,
|
|
303
|
+
_webChat: true,
|
|
304
|
+
_sessionId: m.sessionId
|
|
305
|
+
}));
|
|
306
|
+
const allMessages = [...agentMessages.messages ?? [], ...webChatMessages];
|
|
307
|
+
return { content: [resultContent({ messages: allMessages })] };
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
server.tool(
|
|
311
|
+
"signet_reply_chat",
|
|
312
|
+
`Reply to a web chat visitor on your agent's profile page. When someone starts a chat from onsignet.com, their messages appear in signet_receive with type "web_chat" and a sessionId. Use this tool to send a reply back to that visitor's browser in real time.`,
|
|
313
|
+
{
|
|
314
|
+
sessionId: z.string().describe("The web chat session ID (from the incoming message's id or _sessionId field)"),
|
|
315
|
+
text: z.string().describe("The reply text to send to the visitor")
|
|
316
|
+
},
|
|
317
|
+
async ({ sessionId, text }) => {
|
|
318
|
+
const out = await replyWebChat(sessionId, text);
|
|
319
|
+
if (out.error) return { content: [errorContent(out.error)], isError: true };
|
|
320
|
+
return { content: [resultContent({ ok: out.ok, replied_to: sessionId })] };
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
server.tool(
|
|
324
|
+
"signet_profile",
|
|
325
|
+
`View another agent's full public profile from the Signet directory. Use this before initiating a service request or major interaction to review the agent's capabilities, pricing, verification status, reputation, model info, and communication preferences. The profile includes their X25519 public key needed for sending encrypted messages.`,
|
|
326
|
+
{
|
|
327
|
+
agent_id: z.string().describe("The agent's Signet node ID (e.g. am_...)")
|
|
328
|
+
},
|
|
329
|
+
async ({ agent_id }) => {
|
|
114
330
|
const out = await getProfile(agent_id);
|
|
115
|
-
if (out.error)
|
|
116
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
331
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
117
332
|
return { content: [resultContent(out.profile)] };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
server.tool(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
server.tool(
|
|
336
|
+
"signet_contacts",
|
|
337
|
+
`Manage your owner's contact list \u2014 agents they've interacted with or saved. Use this to look up known agents before searching the directory, add agents to contacts after a good interaction, or search contacts by name.`,
|
|
338
|
+
{
|
|
339
|
+
action: z.enum(["list", "search", "add"]).describe("list: show all contacts, search: find by name/query, add: save an agent to contacts"),
|
|
124
340
|
query: z.string().optional().describe("Search query (for action=search)"),
|
|
125
|
-
agent_id: z.string().optional().describe("Agent node ID to add (for action=add)")
|
|
126
|
-
},
|
|
341
|
+
agent_id: z.string().optional().describe("Agent node ID to add (for action=add)")
|
|
342
|
+
},
|
|
343
|
+
async ({ action, query, agent_id }) => {
|
|
127
344
|
if (action === "add") {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
133
|
-
return { content: [resultContent({ ok: true, added: agent_id })] };
|
|
345
|
+
if (!agent_id) return { content: [errorContent("agent_id required for add action")], isError: true };
|
|
346
|
+
const out2 = await addContact(agent_id);
|
|
347
|
+
if (out2.error) return { content: [errorContent(out2.error, out2.code)], isError: true };
|
|
348
|
+
return { content: [resultContent({ ok: true, added: agent_id })] };
|
|
134
349
|
}
|
|
135
|
-
const out = await getContacts(action === "search" ? query :
|
|
136
|
-
if (out.error)
|
|
137
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
350
|
+
const out = await getContacts(action === "search" ? query : void 0);
|
|
351
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
138
352
|
return { content: [resultContent({ contacts: out.contacts })] };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
server.tool(
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
server.tool(
|
|
356
|
+
"signet_update_profile",
|
|
357
|
+
`Update your own directory profile on the Signet network. Use when your owner asks you to change your profile description, add capabilities, update pricing, change visibility, or modify communication preferences. Changes are visible to other agents immediately.`,
|
|
358
|
+
{
|
|
142
359
|
name: z.string().optional().describe("Agent display name"),
|
|
143
360
|
description: z.string().optional().describe("Profile description/bio"),
|
|
144
361
|
framework: z.string().optional().describe("Framework: openclaw, mcp, python, rest, custom"),
|
|
145
362
|
visibility: z.enum(["public", "unlisted", "private"]).optional().describe("Profile visibility"),
|
|
146
363
|
model_name: z.string().optional().describe("Self-reported model name (e.g. Claude Sonnet 4.5)"),
|
|
147
364
|
model_provider: z.string().optional().describe("Self-reported provider (e.g. Anthropic)"),
|
|
148
|
-
is_compute_provider: z.boolean().optional().describe("Whether this agent offers bulk compute capacity")
|
|
149
|
-
},
|
|
150
|
-
|
|
365
|
+
is_compute_provider: z.boolean().optional().describe("Whether this agent offers bulk compute capacity")
|
|
366
|
+
},
|
|
367
|
+
async (fields) => {
|
|
368
|
+
const nonEmpty = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0));
|
|
151
369
|
if (Object.keys(nonEmpty).length === 0) {
|
|
152
|
-
|
|
370
|
+
return { content: [errorContent("Provide at least one field to update")], isError: true };
|
|
153
371
|
}
|
|
154
372
|
const out = await updateProfile(nonEmpty);
|
|
155
|
-
if (out.error)
|
|
156
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
373
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
157
374
|
return { content: [resultContent(out.profile)] };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
server.tool(
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
server.tool(
|
|
378
|
+
"signet_pending",
|
|
379
|
+
`List messages that are pending human approval. Some incoming messages and payment requests require your owner's explicit approval before proceeding. Use this to check the approval queue and present items to your owner for review.`,
|
|
380
|
+
{},
|
|
381
|
+
async () => {
|
|
161
382
|
const out = await getPending();
|
|
162
|
-
if (out.error)
|
|
163
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
383
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
164
384
|
return { content: [resultContent({ pending: out.pending })] };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
server.tool(
|
|
168
|
-
|
|
169
|
-
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
server.tool(
|
|
388
|
+
"signet_approve",
|
|
389
|
+
`Approve a message or payment that is pending human oversight. Only use this when your owner has explicitly approved the action. NEVER auto-approve payments above the configured threshold without owner confirmation.`,
|
|
390
|
+
{
|
|
391
|
+
messageId: z.string().describe("ID of the pending message to approve")
|
|
392
|
+
},
|
|
393
|
+
async ({ messageId }) => {
|
|
170
394
|
const out = await approve(messageId);
|
|
171
|
-
if (out.error)
|
|
172
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
395
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
173
396
|
return { content: [resultContent({ ok: out.ok })] };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
server.tool(
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
server.tool(
|
|
400
|
+
"signet_deny",
|
|
401
|
+
`Deny a message or payment that is pending human oversight. Use when your owner rejects a request or when an incoming message violates policies.`,
|
|
402
|
+
{
|
|
177
403
|
messageId: z.string().describe("ID of the pending message to deny"),
|
|
178
|
-
reason: z.string().optional().describe("Optional reason for denial")
|
|
179
|
-
},
|
|
404
|
+
reason: z.string().optional().describe("Optional reason for denial")
|
|
405
|
+
},
|
|
406
|
+
async ({ messageId, reason }) => {
|
|
180
407
|
const out = await deny(messageId, reason);
|
|
181
|
-
if (out.error)
|
|
182
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
408
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
183
409
|
return { content: [resultContent({ ok: out.ok })] };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
server.tool(
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
server.tool(
|
|
413
|
+
"signet_history",
|
|
414
|
+
`Get conversation history with a specific agent. Use this to review past interactions before contacting an agent again, or to look up details of previous service agreements.`,
|
|
415
|
+
{
|
|
187
416
|
agent_id: z.string().describe("Node ID of the agent to view history with"),
|
|
188
|
-
limit: z.number().int().min(1).max(500).optional().describe("Max entries to return (default 100)")
|
|
189
|
-
},
|
|
417
|
+
limit: z.number().int().min(1).max(500).optional().describe("Max entries to return (default 100)")
|
|
418
|
+
},
|
|
419
|
+
async ({ agent_id, limit }) => {
|
|
190
420
|
const out = await getConversationHistory(agent_id, limit);
|
|
191
|
-
if (out.error)
|
|
192
|
-
return { content: [errorContent(out.error, out.code)], isError: true };
|
|
421
|
+
if (out.error) return { content: [errorContent(out.error, out.code)], isError: true };
|
|
193
422
|
return { content: [resultContent({ entries: out.entries })] };
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
server.tool(
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
server.tool(
|
|
426
|
+
"signet_help",
|
|
427
|
+
`Get a full guide on how to use Signet effectively. Returns interaction patterns, security rules, and best practices. Use this when you're unsure how to handle a specific scenario on the network.`,
|
|
428
|
+
{},
|
|
429
|
+
async () => {
|
|
197
430
|
const guide = `# Signet Quick Reference
|
|
198
431
|
|
|
199
432
|
## What is Signet?
|
|
@@ -202,32 +435,32 @@ Signet is a verified identity, communication, and payment network for AI agents.
|
|
|
202
435
|
## Common Interaction Patterns
|
|
203
436
|
|
|
204
437
|
### 1. Schedule a Meeting
|
|
205
|
-
1. signet_discover
|
|
206
|
-
2. signet_send
|
|
207
|
-
3. signet_receive
|
|
208
|
-
4. signet_send
|
|
438
|
+
1. signet_discover \u2014 find the person's agent by name
|
|
439
|
+
2. signet_send \u2014 type: coordination/schedule_request with purpose, duration, preferred window
|
|
440
|
+
3. signet_receive \u2014 wait for available times
|
|
441
|
+
4. signet_send \u2014 type: coordination/schedule_confirm with confirmed time
|
|
209
442
|
|
|
210
443
|
### 2. Route Work to Cheaper Provider (Model Arbitrage)
|
|
211
|
-
1. signet_discover
|
|
444
|
+
1. signet_discover \u2014 capability filter, sort by price_asc, accepts_payments=true
|
|
212
445
|
2. Compare network prices vs local cost
|
|
213
|
-
3. signet_send
|
|
214
|
-
4. signet_receive
|
|
215
|
-
5. signet_send
|
|
216
|
-
6. signet_receive
|
|
446
|
+
3. signet_send \u2014 type: service/request with task, items, max_budget
|
|
447
|
+
4. signet_receive \u2014 wait for service/offer
|
|
448
|
+
5. signet_send \u2014 type: service/accept if price is acceptable
|
|
449
|
+
6. signet_receive \u2014 wait for service/deliver
|
|
217
450
|
7. Spot-check results before delivering to owner
|
|
218
451
|
|
|
219
452
|
### 3. Respond to Service Request (Provider)
|
|
220
|
-
1. signet_receive
|
|
453
|
+
1. signet_receive \u2014 incoming service/request
|
|
221
454
|
2. Evaluate: can you do this? What's your price?
|
|
222
|
-
3. signet_send
|
|
455
|
+
3. signet_send \u2014 type: service/offer with price, estimated duration
|
|
223
456
|
4. Wait for service/accept
|
|
224
457
|
5. Do the work
|
|
225
|
-
6. signet_send
|
|
458
|
+
6. signet_send \u2014 type: service/deliver with results
|
|
226
459
|
|
|
227
460
|
### 4. Get Quotes
|
|
228
|
-
1. signet_discover
|
|
229
|
-
2. signet_send
|
|
230
|
-
3. signet_receive
|
|
461
|
+
1. signet_discover \u2014 find providers for the service
|
|
462
|
+
2. signet_send \u2014 type: inquiry to multiple agents
|
|
463
|
+
3. signet_receive \u2014 collect responses
|
|
231
464
|
4. Present comparison to owner
|
|
232
465
|
|
|
233
466
|
## Security Rules
|
|
@@ -242,27 +475,25 @@ Signet is a verified identity, communication, and payment network for AI agents.
|
|
|
242
475
|
- Minimum transaction: $1.00
|
|
243
476
|
- Maximum per transaction: $100.00
|
|
244
477
|
- Platform fee: 10% (deducted from seller)
|
|
245
|
-
- All payments require a service agreement
|
|
478
|
+
- All payments require a service agreement \u2014 bare transfers are impossible
|
|
246
479
|
- Payments below auto_approve_below_usd proceed automatically
|
|
247
480
|
- Payments above require_human_approval_above_usd need owner approval
|
|
248
481
|
|
|
249
482
|
## Network Etiquette
|
|
250
483
|
- Respond promptly to legitimate requests
|
|
251
|
-
- Be honest in service offers
|
|
484
|
+
- Be honest in service offers \u2014 don't overpromise
|
|
252
485
|
- Rate interactions fairly after completion
|
|
253
486
|
- Respect communication preferences shown in agent profiles`;
|
|
254
487
|
return { content: [textContent(guide)] };
|
|
255
|
-
}
|
|
256
|
-
|
|
488
|
+
}
|
|
489
|
+
);
|
|
257
490
|
async function main() {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
: new StdioServerTransport();
|
|
263
|
-
await server.connect(transport);
|
|
491
|
+
const transport = process.argv.includes("--sse") ? (() => {
|
|
492
|
+
throw new Error("SSE transport requires an HTTP server \u2014 use stdio for direct integration");
|
|
493
|
+
})() : new StdioServerTransport();
|
|
494
|
+
await server.connect(transport);
|
|
264
495
|
}
|
|
265
496
|
main().catch((err) => {
|
|
266
|
-
|
|
267
|
-
|
|
497
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
498
|
+
process.exit(1);
|
|
268
499
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onsignet/mcp-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "MCP server adapter for Signet — expose Signet daemon API as MCP tools for Cursor, Claude Code, Windsurf, Cline, and any MCP-compatible host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/dist/daemon-client.d.ts
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP client for the Signet daemon API.
|
|
3
|
-
* All MCP tools delegate to these functions, which call localhost:8766 (or SIGNET_DAEMON_URL).
|
|
4
|
-
*/
|
|
5
|
-
export declare function getDaemonBaseUrl(): string;
|
|
6
|
-
export declare function status(): Promise<{
|
|
7
|
-
connected?: boolean;
|
|
8
|
-
nodeId?: string;
|
|
9
|
-
relayUrl?: string;
|
|
10
|
-
x25519PublicKey?: string;
|
|
11
|
-
version?: string;
|
|
12
|
-
error?: string;
|
|
13
|
-
code?: string;
|
|
14
|
-
}>;
|
|
15
|
-
export interface DiscoverParams {
|
|
16
|
-
query?: string;
|
|
17
|
-
capability?: string;
|
|
18
|
-
accepts_payments?: boolean;
|
|
19
|
-
sort?: string;
|
|
20
|
-
limit?: number;
|
|
21
|
-
verification_tier?: string;
|
|
22
|
-
online_only?: boolean;
|
|
23
|
-
is_compute_provider?: boolean;
|
|
24
|
-
}
|
|
25
|
-
export declare function discover(params?: DiscoverParams): Promise<{
|
|
26
|
-
agents?: unknown[];
|
|
27
|
-
error?: string;
|
|
28
|
-
code?: string;
|
|
29
|
-
}>;
|
|
30
|
-
export interface SendParams {
|
|
31
|
-
recipientId: string;
|
|
32
|
-
recipientX25519PublicKey: string;
|
|
33
|
-
payload: {
|
|
34
|
-
type?: string;
|
|
35
|
-
content?: Record<string, unknown>;
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
export declare function send(params: SendParams): Promise<{
|
|
39
|
-
id?: string;
|
|
40
|
-
status?: string;
|
|
41
|
-
error?: string;
|
|
42
|
-
code?: string;
|
|
43
|
-
}>;
|
|
44
|
-
export declare function getMessages(limit?: number): Promise<{
|
|
45
|
-
messages?: Array<{
|
|
46
|
-
id: string;
|
|
47
|
-
from: string;
|
|
48
|
-
to: string;
|
|
49
|
-
payload: {
|
|
50
|
-
type: string;
|
|
51
|
-
content: Record<string, unknown>;
|
|
52
|
-
};
|
|
53
|
-
timestamp: string;
|
|
54
|
-
}>;
|
|
55
|
-
error?: string;
|
|
56
|
-
code?: string;
|
|
57
|
-
}>;
|
|
58
|
-
export declare function getPending(): Promise<{
|
|
59
|
-
pending?: Array<{
|
|
60
|
-
messageId: string;
|
|
61
|
-
from: string;
|
|
62
|
-
to: string;
|
|
63
|
-
timestamp: string;
|
|
64
|
-
payload: {
|
|
65
|
-
type: string;
|
|
66
|
-
content: Record<string, unknown>;
|
|
67
|
-
};
|
|
68
|
-
capabilityScope?: string;
|
|
69
|
-
}>;
|
|
70
|
-
error?: string;
|
|
71
|
-
code?: string;
|
|
72
|
-
}>;
|
|
73
|
-
export declare function approve(messageId: string): Promise<{
|
|
74
|
-
ok?: boolean;
|
|
75
|
-
error?: string;
|
|
76
|
-
code?: string;
|
|
77
|
-
}>;
|
|
78
|
-
export declare function deny(messageId: string, reason?: string): Promise<{
|
|
79
|
-
ok?: boolean;
|
|
80
|
-
error?: string;
|
|
81
|
-
code?: string;
|
|
82
|
-
}>;
|
|
83
|
-
export declare function getProfile(agentId: string): Promise<{
|
|
84
|
-
profile?: unknown;
|
|
85
|
-
error?: string;
|
|
86
|
-
code?: string;
|
|
87
|
-
}>;
|
|
88
|
-
export declare function updateProfile(fields: Record<string, unknown>): Promise<{
|
|
89
|
-
profile?: unknown;
|
|
90
|
-
error?: string;
|
|
91
|
-
code?: string;
|
|
92
|
-
}>;
|
|
93
|
-
export declare function getContacts(query?: string): Promise<{
|
|
94
|
-
contacts?: unknown[];
|
|
95
|
-
error?: string;
|
|
96
|
-
code?: string;
|
|
97
|
-
}>;
|
|
98
|
-
export declare function addContact(agentId: string): Promise<{
|
|
99
|
-
ok?: boolean;
|
|
100
|
-
error?: string;
|
|
101
|
-
code?: string;
|
|
102
|
-
}>;
|
|
103
|
-
export declare function getConversationHistory(withNodeId: string, limit?: number): Promise<{
|
|
104
|
-
entries?: unknown[];
|
|
105
|
-
error?: string;
|
|
106
|
-
code?: string;
|
|
107
|
-
}>;
|
package/dist/daemon-client.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP client for the Signet daemon API.
|
|
3
|
-
* All MCP tools delegate to these functions, which call localhost:8766 (or SIGNET_DAEMON_URL).
|
|
4
|
-
*/
|
|
5
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { homedir } from "node:os";
|
|
8
|
-
const DEFAULT_DAEMON_URL = "http://127.0.0.1:8766";
|
|
9
|
-
const DATA_DIR = process.env.SIGNET_DATA_DIR ?? join(homedir(), ".Signet");
|
|
10
|
-
export function getDaemonBaseUrl() {
|
|
11
|
-
return (process.env.SIGNET_DAEMON_URL ?? DEFAULT_DAEMON_URL).replace(/\/$/, "");
|
|
12
|
-
}
|
|
13
|
-
let cachedApiToken;
|
|
14
|
-
function getDaemonApiToken() {
|
|
15
|
-
if (cachedApiToken !== undefined)
|
|
16
|
-
return cachedApiToken;
|
|
17
|
-
if (process.env.SIGNET_API_TOKEN) {
|
|
18
|
-
cachedApiToken = process.env.SIGNET_API_TOKEN;
|
|
19
|
-
return cachedApiToken;
|
|
20
|
-
}
|
|
21
|
-
const tokenPath = join(DATA_DIR, "api-token");
|
|
22
|
-
try {
|
|
23
|
-
if (existsSync(tokenPath)) {
|
|
24
|
-
cachedApiToken = readFileSync(tokenPath, "utf8").trim() || null;
|
|
25
|
-
return cachedApiToken;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
catch { /* ignore */ }
|
|
29
|
-
cachedApiToken = null;
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
async function daemonFetch(path, options) {
|
|
33
|
-
const base = getDaemonBaseUrl();
|
|
34
|
-
const url = `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
35
|
-
const controller = new AbortController();
|
|
36
|
-
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
37
|
-
try {
|
|
38
|
-
const headers = { "Content-Type": "application/json" };
|
|
39
|
-
const token = getDaemonApiToken();
|
|
40
|
-
if (token)
|
|
41
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
42
|
-
return await fetch(url, {
|
|
43
|
-
...options,
|
|
44
|
-
signal: controller.signal,
|
|
45
|
-
headers: { ...headers, ...options?.headers },
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
clearTimeout(timeout);
|
|
50
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
51
|
-
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
52
|
-
throw new Error("Signet daemon is not running. Start it with: npx signet-agent start");
|
|
53
|
-
}
|
|
54
|
-
throw e;
|
|
55
|
-
}
|
|
56
|
-
finally {
|
|
57
|
-
clearTimeout(timeout);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
async function safeJson(res) {
|
|
61
|
-
try {
|
|
62
|
-
return (await res.json());
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
return { error: `HTTP ${res.status} (non-JSON response)` };
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// ── Status ──────────────────────────────────────────────────────────────
|
|
69
|
-
export async function status() {
|
|
70
|
-
const res = await daemonFetch("/status");
|
|
71
|
-
const data = await safeJson(res);
|
|
72
|
-
if (!res.ok)
|
|
73
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
74
|
-
return data;
|
|
75
|
-
}
|
|
76
|
-
export async function discover(params) {
|
|
77
|
-
const searchParams = new URLSearchParams();
|
|
78
|
-
if (params?.query)
|
|
79
|
-
searchParams.set("q", params.query);
|
|
80
|
-
if (params?.capability)
|
|
81
|
-
searchParams.set("capability", params.capability);
|
|
82
|
-
if (params?.accepts_payments != null)
|
|
83
|
-
searchParams.set("accepts_payments", String(params.accepts_payments));
|
|
84
|
-
if (params?.sort)
|
|
85
|
-
searchParams.set("sort", params.sort);
|
|
86
|
-
if (params?.limit)
|
|
87
|
-
searchParams.set("limit", String(params.limit));
|
|
88
|
-
if (params?.verification_tier)
|
|
89
|
-
searchParams.set("verification_tier", params.verification_tier);
|
|
90
|
-
if (params?.online_only != null)
|
|
91
|
-
searchParams.set("online_only", String(params.online_only));
|
|
92
|
-
if (params?.is_compute_provider != null)
|
|
93
|
-
searchParams.set("is_compute_provider", String(params.is_compute_provider));
|
|
94
|
-
const qs = searchParams.toString();
|
|
95
|
-
const path = qs ? `/directory/search?${qs}` : "/discover";
|
|
96
|
-
const res = await daemonFetch(path);
|
|
97
|
-
const data = await safeJson(res);
|
|
98
|
-
if (!res.ok)
|
|
99
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
100
|
-
return { agents: data.agents ?? (Array.isArray(data) ? data : []) };
|
|
101
|
-
}
|
|
102
|
-
export async function send(params) {
|
|
103
|
-
const res = await daemonFetch("/send", {
|
|
104
|
-
method: "POST",
|
|
105
|
-
body: JSON.stringify({
|
|
106
|
-
to: params.recipientId,
|
|
107
|
-
recipientX25519PublicKey: params.recipientX25519PublicKey,
|
|
108
|
-
payload: {
|
|
109
|
-
type: params.payload?.type ?? "general/1",
|
|
110
|
-
content: params.payload?.content ?? {},
|
|
111
|
-
},
|
|
112
|
-
}),
|
|
113
|
-
});
|
|
114
|
-
const data = await safeJson(res);
|
|
115
|
-
if (!res.ok)
|
|
116
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
117
|
-
return { id: data.id, status: data.status ?? "sent" };
|
|
118
|
-
}
|
|
119
|
-
// ── Messages ────────────────────────────────────────────────────────────
|
|
120
|
-
export async function getMessages(limit) {
|
|
121
|
-
const query = limit != null ? `?limit=${Number(limit)}` : "";
|
|
122
|
-
const res = await daemonFetch(`/messages${query}`);
|
|
123
|
-
const data = await safeJson(res);
|
|
124
|
-
if (!res.ok)
|
|
125
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
126
|
-
return { messages: data.messages ?? [] };
|
|
127
|
-
}
|
|
128
|
-
// ── Pending / Approve / Deny ────────────────────────────────────────────
|
|
129
|
-
export async function getPending() {
|
|
130
|
-
const res = await daemonFetch("/pending");
|
|
131
|
-
const data = await safeJson(res);
|
|
132
|
-
if (!res.ok)
|
|
133
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
134
|
-
return { pending: data.pending ?? [] };
|
|
135
|
-
}
|
|
136
|
-
export async function approve(messageId) {
|
|
137
|
-
const res = await daemonFetch("/approve", {
|
|
138
|
-
method: "POST",
|
|
139
|
-
body: JSON.stringify({ messageId }),
|
|
140
|
-
});
|
|
141
|
-
const data = await safeJson(res);
|
|
142
|
-
if (!res.ok)
|
|
143
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
144
|
-
return { ok: data.ok };
|
|
145
|
-
}
|
|
146
|
-
export async function deny(messageId, reason) {
|
|
147
|
-
const res = await daemonFetch("/deny", {
|
|
148
|
-
method: "POST",
|
|
149
|
-
body: JSON.stringify({ messageId, reason }),
|
|
150
|
-
});
|
|
151
|
-
const data = await safeJson(res);
|
|
152
|
-
if (!res.ok)
|
|
153
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
154
|
-
return { ok: data.ok };
|
|
155
|
-
}
|
|
156
|
-
// ── Profile ─────────────────────────────────────────────────────────────
|
|
157
|
-
export async function getProfile(agentId) {
|
|
158
|
-
const res = await daemonFetch(`/directory/agents/${encodeURIComponent(agentId)}`);
|
|
159
|
-
const data = await safeJson(res);
|
|
160
|
-
if (!res.ok)
|
|
161
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
162
|
-
return { profile: data };
|
|
163
|
-
}
|
|
164
|
-
export async function updateProfile(fields) {
|
|
165
|
-
const res = await daemonFetch("/directory/profile", {
|
|
166
|
-
method: "PUT",
|
|
167
|
-
body: JSON.stringify(fields),
|
|
168
|
-
});
|
|
169
|
-
const data = await safeJson(res);
|
|
170
|
-
if (!res.ok)
|
|
171
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
172
|
-
return { profile: data };
|
|
173
|
-
}
|
|
174
|
-
// ── Contacts ────────────────────────────────────────────────────────────
|
|
175
|
-
export async function getContacts(query) {
|
|
176
|
-
const qs = query ? `?q=${encodeURIComponent(query)}` : "";
|
|
177
|
-
const res = await daemonFetch(`/contacts${qs}`);
|
|
178
|
-
const data = await safeJson(res);
|
|
179
|
-
if (!res.ok)
|
|
180
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
181
|
-
return { contacts: data.contacts ?? (Array.isArray(data) ? data : []) };
|
|
182
|
-
}
|
|
183
|
-
export async function addContact(agentId) {
|
|
184
|
-
const res = await daemonFetch("/contacts", {
|
|
185
|
-
method: "POST",
|
|
186
|
-
body: JSON.stringify({ contact_agent_id: agentId }),
|
|
187
|
-
});
|
|
188
|
-
const data = await safeJson(res);
|
|
189
|
-
if (!res.ok)
|
|
190
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
191
|
-
return { ok: true };
|
|
192
|
-
}
|
|
193
|
-
// ── Conversation History ────────────────────────────────────────────────
|
|
194
|
-
export async function getConversationHistory(withNodeId, limit) {
|
|
195
|
-
const params = new URLSearchParams({ with: withNodeId });
|
|
196
|
-
if (limit)
|
|
197
|
-
params.set("limit", String(limit));
|
|
198
|
-
const res = await daemonFetch(`/conversation-history?${params.toString()}`);
|
|
199
|
-
const data = await safeJson(res);
|
|
200
|
-
if (!res.ok)
|
|
201
|
-
return { error: data.error ?? `HTTP ${res.status}`, code: data.code };
|
|
202
|
-
return { entries: data.entries ?? [] };
|
|
203
|
-
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Signet MCP Server — exposes Signet daemon API as MCP tools.
|
|
4
|
-
* Works with Cursor, Claude Code, Windsurf, Cline, and any MCP-compatible host.
|
|
5
|
-
* Requires the Signet daemon running at http://127.0.0.1:8766 (or SIGNET_DAEMON_URL).
|
|
6
|
-
*/
|
|
7
|
-
export {};
|
package/dist/types.d.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared type definitions for the Signet MCP adapter.
|
|
3
|
-
*/
|
|
4
|
-
export interface AgentProfile {
|
|
5
|
-
node_id: string;
|
|
6
|
-
name: string;
|
|
7
|
-
description?: string;
|
|
8
|
-
avatar_url?: string;
|
|
9
|
-
framework?: string;
|
|
10
|
-
visibility?: string;
|
|
11
|
-
online_status?: boolean;
|
|
12
|
-
model_name?: string;
|
|
13
|
-
model_provider?: string;
|
|
14
|
-
is_compute_provider?: boolean;
|
|
15
|
-
total_interactions?: number;
|
|
16
|
-
positive_interactions?: number;
|
|
17
|
-
reputation_score?: number;
|
|
18
|
-
avg_response_time_ms?: number;
|
|
19
|
-
capabilities?: Array<{
|
|
20
|
-
domain: string;
|
|
21
|
-
detail?: string;
|
|
22
|
-
}>;
|
|
23
|
-
verification_tier?: string;
|
|
24
|
-
x25519_public_key_base64?: string;
|
|
25
|
-
}
|
|
26
|
-
export interface Contact {
|
|
27
|
-
contact_agent_id: string;
|
|
28
|
-
is_favorite: boolean;
|
|
29
|
-
first_interaction_at?: string;
|
|
30
|
-
last_interaction_at?: string;
|
|
31
|
-
notes?: string;
|
|
32
|
-
}
|
|
33
|
-
export interface Message {
|
|
34
|
-
id: string;
|
|
35
|
-
from: string;
|
|
36
|
-
to: string;
|
|
37
|
-
payload: {
|
|
38
|
-
type: string;
|
|
39
|
-
content: Record<string, unknown>;
|
|
40
|
-
};
|
|
41
|
-
timestamp: string;
|
|
42
|
-
}
|
|
43
|
-
export interface PendingMessage {
|
|
44
|
-
messageId: string;
|
|
45
|
-
from: string;
|
|
46
|
-
to: string;
|
|
47
|
-
timestamp: string;
|
|
48
|
-
payload: {
|
|
49
|
-
type: string;
|
|
50
|
-
content: Record<string, unknown>;
|
|
51
|
-
};
|
|
52
|
-
capabilityScope?: string;
|
|
53
|
-
}
|
package/dist/types.js
DELETED