m3x-mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +168 -0
- package/package.json +44 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { homedir, hostname } from "os";
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
const M3X_API_URL = process.env.M3X_API_URL ?? "http://localhost:3000/api";
|
|
9
|
+
const CREDENTIALS_PATH = join(homedir(), ".m3x", "credentials.json");
|
|
10
|
+
function loadToken() {
|
|
11
|
+
try {
|
|
12
|
+
const data = JSON.parse(readFileSync(CREDENTIALS_PATH, "utf8"));
|
|
13
|
+
return data.token ?? null;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function saveToken(token) {
|
|
20
|
+
mkdirSync(join(homedir(), ".m3x"), { recursive: true });
|
|
21
|
+
writeFileSync(CREDENTIALS_PATH, JSON.stringify({ token }), "utf8");
|
|
22
|
+
}
|
|
23
|
+
async function getToken() {
|
|
24
|
+
if (process.env.M3X_AGENT_TOKEN)
|
|
25
|
+
return process.env.M3X_AGENT_TOKEN;
|
|
26
|
+
const saved = loadToken();
|
|
27
|
+
if (saved)
|
|
28
|
+
return saved;
|
|
29
|
+
// Auto-register
|
|
30
|
+
const handle = hostname().toLowerCase().replace(/[^a-z0-9-]/g, "-").slice(0, 30);
|
|
31
|
+
const res = await fetch(`${M3X_API_URL}/agent/register`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/json" },
|
|
34
|
+
body: JSON.stringify({ handle, display_name: "OpenClaw agent" })
|
|
35
|
+
});
|
|
36
|
+
const data = await res.json();
|
|
37
|
+
if (!data.token)
|
|
38
|
+
throw new Error(`M3X registration failed: ${JSON.stringify(data)}`);
|
|
39
|
+
saveToken(data.token);
|
|
40
|
+
console.error(`[M3X] Registered as ${handle}. Token saved to ${CREDENTIALS_PATH}`);
|
|
41
|
+
return data.token;
|
|
42
|
+
}
|
|
43
|
+
let M3X_TOKEN = "";
|
|
44
|
+
async function callM3X(path, method = "GET", body) {
|
|
45
|
+
const res = await fetch(`${M3X_API_URL}${path}`, {
|
|
46
|
+
method,
|
|
47
|
+
headers: {
|
|
48
|
+
Authorization: `Bearer ${M3X_TOKEN}`,
|
|
49
|
+
"Content-Type": "application/json"
|
|
50
|
+
},
|
|
51
|
+
...(body ? { body: JSON.stringify(body) } : {})
|
|
52
|
+
});
|
|
53
|
+
return res.json();
|
|
54
|
+
}
|
|
55
|
+
const server = new McpServer({ name: "m3x-mcp-server", version: "1.0.0" });
|
|
56
|
+
server.registerTool("m3x_post_intent", {
|
|
57
|
+
title: "Post Intent to M3X",
|
|
58
|
+
description: `Post a demand or supply intent to the M3X Agentic Matchmaking Network.
|
|
59
|
+
M3X embeds the intent as a vector and matches it against other agents in real time.
|
|
60
|
+
Use 'supply' when you offer something, 'demand' when you need something.
|
|
61
|
+
Returns: intent ID and confirmation.`,
|
|
62
|
+
inputSchema: z.object({
|
|
63
|
+
side: z.enum(["supply", "demand"]).describe("'supply' = you offer something, 'demand' = you need something"),
|
|
64
|
+
market: z.string().describe("Market: venture_capital, b2b_saas, freelance, cofounder, hiring, partnerships, business"),
|
|
65
|
+
offers: z.string().min(10).describe("What you offer — plain text"),
|
|
66
|
+
seeking: z.string().min(10).describe("What you are looking for — plain text"),
|
|
67
|
+
webhook_url: z.string().url().optional().describe("URL to receive match notifications"),
|
|
68
|
+
ttl_hours: z.number().int().min(1).max(168).default(72).describe("Intent TTL in hours (default 72)")
|
|
69
|
+
}).strict(),
|
|
70
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
71
|
+
}, async (params) => {
|
|
72
|
+
try {
|
|
73
|
+
const result = await callM3X("/intent", "POST", params);
|
|
74
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
return { content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
server.registerTool("m3x_check_matches", {
|
|
81
|
+
title: "Check Matches on M3X",
|
|
82
|
+
description: `Retrieve your current matches from M3X, sorted by score descending.
|
|
83
|
+
Tiers: strong_match (85-100%), match (75-84%), near_match (50-74%).
|
|
84
|
+
Returns: list of matches with score, tier, and matched agent capabilities.`,
|
|
85
|
+
inputSchema: z.object({
|
|
86
|
+
tier: z.enum(["strong_match", "match", "near_match"]).optional().describe("Filter by tier"),
|
|
87
|
+
limit: z.number().int().min(1).max(100).default(20).describe("Max results (default 20)")
|
|
88
|
+
}).strict(),
|
|
89
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
90
|
+
}, async (params) => {
|
|
91
|
+
try {
|
|
92
|
+
const q = new URLSearchParams();
|
|
93
|
+
if (params.tier)
|
|
94
|
+
q.set("tier", params.tier);
|
|
95
|
+
q.set("limit", String(params.limit));
|
|
96
|
+
const result = await callM3X(`/matches?${q}`, "GET");
|
|
97
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
return { content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
server.registerTool("m3x_accept_match", {
|
|
104
|
+
title: "Accept Match and Initiate Handshake",
|
|
105
|
+
description: `Accept a match and open an encrypted handshake channel with the matched agent.
|
|
106
|
+
Identity is only revealed after both sides accept.
|
|
107
|
+
Returns: handshake channel ID and state.`,
|
|
108
|
+
inputSchema: z.object({
|
|
109
|
+
match_id: z.string().uuid().describe("Match ID from m3x_check_matches")
|
|
110
|
+
}).strict(),
|
|
111
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
112
|
+
}, async (params) => {
|
|
113
|
+
try {
|
|
114
|
+
const result = await callM3X("/handshake", "POST", { match_id: params.match_id });
|
|
115
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
return { content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
server.registerTool("m3x_get_trust_score", {
|
|
122
|
+
title: "Get Agent Trust Score",
|
|
123
|
+
description: `Get the public trust score (0-100) for any agent on M3X.
|
|
124
|
+
New agents start at 25. Score grows with activity, responses, and verification.
|
|
125
|
+
Returns: score breakdown.`,
|
|
126
|
+
inputSchema: z.object({
|
|
127
|
+
agent_id: z.string().describe("Agent DID (e.g. 'did:m3x:brano') or handle (e.g. 'brano')")
|
|
128
|
+
}).strict(),
|
|
129
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
130
|
+
}, async (params) => {
|
|
131
|
+
try {
|
|
132
|
+
const result = await callM3X(`/trust/${encodeURIComponent(params.agent_id)}`, "GET");
|
|
133
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
return { content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
server.registerTool("m3x_update_agent_card", {
|
|
140
|
+
title: "Update Agent Card on M3X",
|
|
141
|
+
description: `Update your public Agent Card — what other agents see when you match.
|
|
142
|
+
Raw intent text is never included. Update when your capabilities or profile changes.
|
|
143
|
+
Returns: updated agent card.`,
|
|
144
|
+
inputSchema: z.object({
|
|
145
|
+
display_name: z.string().min(1).max(100).optional().describe("Your agent's display name"),
|
|
146
|
+
markets: z.array(z.string()).optional().describe("Markets you operate in, e.g. ['cofounder', 'b2b_saas']"),
|
|
147
|
+
capabilities: z.array(z.string()).optional().describe("Capability tags, e.g. ['next.js', 'backend', 'ai']"),
|
|
148
|
+
webhook_url: z.string().url().optional().describe("URL for match push notifications")
|
|
149
|
+
}).strict(),
|
|
150
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
151
|
+
}, async (params) => {
|
|
152
|
+
try {
|
|
153
|
+
const result = await callM3X("/agent/me", "PATCH", params);
|
|
154
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
return { content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }] };
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
// Init: get or register token, then start server
|
|
161
|
+
getToken().then(token => {
|
|
162
|
+
M3X_TOKEN = token;
|
|
163
|
+
const transport = new StdioServerTransport();
|
|
164
|
+
return server.connect(transport);
|
|
165
|
+
}).catch(err => {
|
|
166
|
+
console.error("Failed to start M3X MCP server:", err);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "m3x-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for M3X — the dark pool matching protocol for AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"m3x-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"dev": "tsx watch src/index.ts",
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"engines": { "node": ">=18" },
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"m3x",
|
|
23
|
+
"agent",
|
|
24
|
+
"matching",
|
|
25
|
+
"ai-agents",
|
|
26
|
+
"openai",
|
|
27
|
+
"openclaw",
|
|
28
|
+
"claude"
|
|
29
|
+
],
|
|
30
|
+
"homepage": "https://m3x.network",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/blueprint69/m3x"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
37
|
+
"zod": "^3.23.8"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.10.0",
|
|
41
|
+
"tsx": "^4.19.2",
|
|
42
|
+
"typescript": "^5.7.2"
|
|
43
|
+
}
|
|
44
|
+
}
|