@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 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
- try {
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
- const json = await res.json();
128
- const manifest = json.manifest || json.data?.manifest || json;
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({ input: params }),
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
- throw new Error(`Unknown tool: ${toolName}`);
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**Price:** ${schema.price_type}\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.`,
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.1",
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
+ });