@nexusbloom/mcp-server 1.0.1 → 1.0.3
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/index.js +43 -36
- package/package.json +1 -1
- package/test-mcp-client.mjs +104 -0
package/index.js
CHANGED
|
@@ -73,9 +73,9 @@ async function fetchTools() {
|
|
|
73
73
|
tags: (t.tags || []).flat().filter(Boolean),
|
|
74
74
|
category: t.category || "Platform Tools",
|
|
75
75
|
icon: t.icon || "🔧",
|
|
76
|
-
price_type: t.price_type || t.pricing || "free",
|
|
77
76
|
version: t.version || "1.0.0",
|
|
78
77
|
type: "builtin",
|
|
78
|
+
input_schema: t.input_schema || null,
|
|
79
79
|
}));
|
|
80
80
|
toolsCacheTime = now;
|
|
81
81
|
return toolsCache;
|
|
@@ -90,42 +90,16 @@ async function fetchTools() {
|
|
|
90
90
|
// ---------------------------------------------------------------------------
|
|
91
91
|
|
|
92
92
|
async function fetchToolSchema(slug) {
|
|
93
|
-
|
|
94
|
-
const res = await fetch(`${API_URL}/tools/${encodeURIComponent(slug)}`, {
|
|
95
|
-
headers: apiHeaders(),
|
|
96
|
-
...FETCH_OPTS,
|
|
97
|
-
});
|
|
98
|
-
if (res.ok) {
|
|
99
|
-
const json = await res.json();
|
|
100
|
-
const tool = json.tool || json.data || json;
|
|
101
|
-
if (tool && tool.slug) {
|
|
102
|
-
return {
|
|
103
|
-
slug: tool.slug,
|
|
104
|
-
name: tool.name,
|
|
105
|
-
description: tool.short_description || tool.full_description || tool.description || "",
|
|
106
|
-
tags: (tool.tags || []).flat().filter(Boolean),
|
|
107
|
-
input_schema: tool.input_schema || {},
|
|
108
|
-
output_schema: tool.output_schema || {},
|
|
109
|
-
icon: tool.icon || "🔧",
|
|
110
|
-
price_type: tool.price_type || tool.pricing || "free",
|
|
111
|
-
version: tool.version || "1.0.0",
|
|
112
|
-
category: tool.category || "Platform Tools",
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
} catch (err) {
|
|
117
|
-
console.error(`Failed to fetch schema for ${slug}:`, err.message);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Fallback: try the manifest endpoint
|
|
93
|
+
// Primary: fetch manifest from /api/run/{slug} (the reliable endpoint)
|
|
121
94
|
try {
|
|
122
95
|
const res = await fetch(`${API_URL}/run/${encodeURIComponent(slug)}`, {
|
|
123
96
|
headers: apiHeaders(),
|
|
124
97
|
...FETCH_OPTS,
|
|
125
98
|
});
|
|
126
99
|
if (res.ok) {
|
|
127
|
-
|
|
128
|
-
const
|
|
100
|
+
// API returns { success: true, data: { manifest: { ... } } }
|
|
101
|
+
const body = await res.json();
|
|
102
|
+
const manifest = body.data?.manifest || body.manifest || body;
|
|
129
103
|
if (manifest && manifest.slug) {
|
|
130
104
|
return {
|
|
131
105
|
slug: manifest.slug,
|
|
@@ -135,7 +109,6 @@ async function fetchToolSchema(slug) {
|
|
|
135
109
|
input_schema: manifest.input_schema || {},
|
|
136
110
|
output_schema: manifest.output_schema || {},
|
|
137
111
|
icon: manifest.icon || "🔧",
|
|
138
|
-
price_type: manifest.pricing || "free",
|
|
139
112
|
version: manifest.version || "1.0.0",
|
|
140
113
|
category: manifest.category || "Platform Tools",
|
|
141
114
|
};
|
|
@@ -156,7 +129,7 @@ async function executeTool(slug, params) {
|
|
|
156
129
|
const res = await fetch(`${API_URL}/run/${encodeURIComponent(slug)}`, {
|
|
157
130
|
method: "POST",
|
|
158
131
|
headers: apiHeaders(),
|
|
159
|
-
body: JSON.stringify(
|
|
132
|
+
body: JSON.stringify(params),
|
|
160
133
|
...FETCH_OPTS,
|
|
161
134
|
});
|
|
162
135
|
|
|
@@ -199,6 +172,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
199
172
|
description: `${t.name}: ${t.short_description || "No description"}${
|
|
200
173
|
t.category ? ` [${t.category}]` : ""
|
|
201
174
|
}`,
|
|
175
|
+
inputSchema: t.input_schema || {
|
|
176
|
+
type: "object",
|
|
177
|
+
properties: {},
|
|
178
|
+
},
|
|
179
|
+
}));
|
|
180
|
+
|
|
181
|
+
// Also expose the meta-tool for list/schema/run
|
|
182
|
+
toolList.push({
|
|
183
|
+
name: "nexusbloom",
|
|
184
|
+
description: `Meta-tool to list, inspect, or run any NexusBloom tool. Use this when you need to discover tools or get schema details.`,
|
|
202
185
|
inputSchema: {
|
|
203
186
|
type: "object",
|
|
204
187
|
properties: {
|
|
@@ -221,7 +204,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
221
204
|
},
|
|
222
205
|
required: ["command"],
|
|
223
206
|
},
|
|
224
|
-
})
|
|
207
|
+
});
|
|
225
208
|
|
|
226
209
|
return { tools: toolList };
|
|
227
210
|
});
|
|
@@ -230,10 +213,34 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
230
213
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
231
214
|
const { name: toolName, arguments: args } = request.params;
|
|
232
215
|
|
|
216
|
+
// ── Direct tool execution by slug ──────────────────────────────
|
|
217
|
+
// AI agents can call any tool directly by its slug name.
|
|
218
|
+
// The tool's own input_schema defines the expected parameters.
|
|
219
|
+
// We verify the slug exists by checking against our tool list.
|
|
233
220
|
if (toolName !== "nexusbloom") {
|
|
234
|
-
|
|
221
|
+
const tools = await fetchTools();
|
|
222
|
+
const matched = tools.find((t) => t.slug === toolName);
|
|
223
|
+
if (!matched) {
|
|
224
|
+
return {
|
|
225
|
+
content: [{ type: "text", text: `Unknown tool: "${toolName}". Use the "nexusbloom" tool with command "list" to see available tools.` }],
|
|
226
|
+
isError: true,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const result = await executeTool(toolName, args || {});
|
|
231
|
+
const formatted = JSON.stringify(result.data, null, 2);
|
|
232
|
+
return {
|
|
233
|
+
content: [
|
|
234
|
+
{ type: "text", text: `# ${matched.name} (${toolName})\n\n\`\`\`json\n${formatted}\n\`\`\`` },
|
|
235
|
+
{ type: "resource", resource: { text: formatted, uri: "data:application/json,", mimeType: "application/json" } },
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
} catch (err) {
|
|
239
|
+
return { content: [{ type: "text", text: `Error executing "${toolName}": ${err.message}` }], isError: true };
|
|
240
|
+
}
|
|
235
241
|
}
|
|
236
242
|
|
|
243
|
+
// ── Legacy meta-tool: list/schema/run via sub-commands ─────────
|
|
237
244
|
const command = args?.command;
|
|
238
245
|
|
|
239
246
|
if (!command || !["list", "schema", "run"].includes(command)) {
|
|
@@ -307,7 +314,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
307
314
|
content: [
|
|
308
315
|
{
|
|
309
316
|
type: "text",
|
|
310
|
-
text: `# ${schema.icon} ${schema.name}\n\n**Slug:** \`${schema.slug}\`\n**Description:** ${schema.description}\n**Category:** ${schema.category || "Uncategorized"}\n**
|
|
317
|
+
text: `# ${schema.icon} ${schema.name}\n\n**Slug:** \`${schema.slug}\`\n**Description:** ${schema.description}\n**Category:** ${schema.category || "Uncategorized"}\n**Version:** ${schema.version}\n\n## Input Parameters\n${paramDocs || " _(no parameters)_"}\n\nTo run this tool, call with \`command: "run"\`, \`slug: "${schema.slug}"\`, and \`params\` matching the above schema.`,
|
|
311
318
|
},
|
|
312
319
|
{
|
|
313
320
|
type: "resource",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexusbloom/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "MCP server for NexusBloom — AI agents discover and execute tools through a single interface. No Supabase credentials needed, just connect to our API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server Test — uses the MCP SDK Client directly.
|
|
4
|
+
* Tests both direct slug execution and meta-tool paths.
|
|
5
|
+
*
|
|
6
|
+
* Usage: node test-mcp-client.mjs
|
|
7
|
+
*/
|
|
8
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
console.log("═══ MCP Server Test ═══\n");
|
|
13
|
+
|
|
14
|
+
const transport = new StdioClientTransport({
|
|
15
|
+
command: "npx",
|
|
16
|
+
args: ["@nexusbloom/mcp-server"],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const client = new Client(
|
|
20
|
+
{ name: "mcp-test-client", version: "1.0.0" },
|
|
21
|
+
{ capabilities: {} }
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
await client.connect(transport);
|
|
25
|
+
console.log("✅ Connected to MCP server\n");
|
|
26
|
+
|
|
27
|
+
// ── Test 1: ListTools ─────────────────────────────────────────
|
|
28
|
+
const { tools } = await client.listTools();
|
|
29
|
+
console.log(`✅ Tools available: ${tools.length}`);
|
|
30
|
+
const slugs = tools.filter((t) => t.name !== "nexusbloom").map((t) => t.name);
|
|
31
|
+
const metaTool = tools.find((t) => t.name === "nexusbloom");
|
|
32
|
+
if (metaTool) console.log(`✅ Meta-tool "nexusbloom" registered`);
|
|
33
|
+
console.log(` Example tools: ${slugs.slice(0, 5).join(", ")}...`);
|
|
34
|
+
console.log();
|
|
35
|
+
|
|
36
|
+
// ── Test 2: Direct slug execution ─────────────────────────────
|
|
37
|
+
// AI agents call tools by their slug name directly
|
|
38
|
+
console.log("── Test: Direct slug execution ──");
|
|
39
|
+
const cronResult = await client.callTool({
|
|
40
|
+
name: "cron-expression-builder",
|
|
41
|
+
arguments: { expression: "0 0 * * *" },
|
|
42
|
+
});
|
|
43
|
+
const cronText = cronResult.content.find((c) => c.type === "text")?.text || "";
|
|
44
|
+
if (cronText.includes(`"valid": true`)) {
|
|
45
|
+
console.log("✅ cron-expression-builder executed directly by slug");
|
|
46
|
+
console.log(` → ${cronText.split("\n")[1] || "valid=true"}`);
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`⚠ Unexpected: ${cronText.substring(0, 100)}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const dataResult = await client.callTool({
|
|
52
|
+
name: "data-generator",
|
|
53
|
+
arguments: { count: 2 },
|
|
54
|
+
});
|
|
55
|
+
const dataText = dataResult.content.find((c) => c.type === "text")?.text || "";
|
|
56
|
+
if (dataText.includes("data-generator")) {
|
|
57
|
+
console.log("✅ data-generator executed directly by slug");
|
|
58
|
+
}
|
|
59
|
+
console.log();
|
|
60
|
+
|
|
61
|
+
// ── Test 3: Meta-tool "list" ──────────────────────────────────
|
|
62
|
+
console.log("── Test: Meta-tool list ──");
|
|
63
|
+
const listResult = await client.callTool({
|
|
64
|
+
name: "nexusbloom",
|
|
65
|
+
arguments: { command: "list" },
|
|
66
|
+
});
|
|
67
|
+
const listText = listResult.content.find((c) => c.type === "text")?.text || "";
|
|
68
|
+
if (listText.includes("NexusBloom Tools")) {
|
|
69
|
+
console.log("✅ Meta-tool list works");
|
|
70
|
+
}
|
|
71
|
+
console.log();
|
|
72
|
+
|
|
73
|
+
// ── Test 4: Meta-tool "schema" ────────────────────────────────
|
|
74
|
+
console.log("── Test: Meta-tool schema ──");
|
|
75
|
+
const schemaResult = await client.callTool({
|
|
76
|
+
name: "nexusbloom",
|
|
77
|
+
arguments: { command: "schema", slug: "cron-expression-builder" },
|
|
78
|
+
});
|
|
79
|
+
const schemaText = schemaResult.content.find((c) => c.type === "text")?.text || "";
|
|
80
|
+
if (schemaText.includes("Cron Expression Builder")) {
|
|
81
|
+
console.log("✅ Meta-tool schema works");
|
|
82
|
+
}
|
|
83
|
+
console.log();
|
|
84
|
+
|
|
85
|
+
// ── Test 5: Meta-tool "run" ───────────────────────────────────
|
|
86
|
+
console.log("── Test: Meta-tool run ──");
|
|
87
|
+
const runResult = await client.callTool({
|
|
88
|
+
name: "nexusbloom",
|
|
89
|
+
arguments: { command: "run", slug: "data-generator", params: { count: 3 } },
|
|
90
|
+
});
|
|
91
|
+
const runText = runResult.content.find((c) => c.type === "text")?.text || "";
|
|
92
|
+
if (runText.includes("data-generator")) {
|
|
93
|
+
console.log("✅ Meta-tool run works");
|
|
94
|
+
}
|
|
95
|
+
console.log();
|
|
96
|
+
|
|
97
|
+
await client.close();
|
|
98
|
+
console.log("═══ All MCP tests passed! ═══");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main().catch((err) => {
|
|
102
|
+
console.error("❌ MCP test failed:", err.message);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
});
|