@purplesquirrel/watsonx-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/.github/dependabot.yml +21 -0
- package/.github/workflows/ci.yml +36 -0
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/TROUBLESHOOTING.md +176 -0
- package/batch-processor.js +345 -0
- package/batch-results/classify-1765765720041.json +106 -0
- package/batch-results/full-analysis-1765765676586.json +193 -0
- package/docs/index.html +572 -0
- package/docs/specs.html +613 -0
- package/document-analyzer.js +353 -0
- package/embedding-index.js +318 -0
- package/embeddings-index.json +38761 -0
- package/index.js +551 -0
- package/linkedin-post.md +92 -0
- package/package.json +28 -0
- package/test-watsonx.js +29 -0
package/index.js
ADDED
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { WatsonXAI } from "@ibm-cloud/watsonx-ai";
|
|
10
|
+
import { IamAuthenticator } from "ibm-cloud-sdk-core";
|
|
11
|
+
// import KeyProtectV2 from "@ibm-cloud/key-protect"; // Temporarily disabled - package not available
|
|
12
|
+
|
|
13
|
+
// Configuration from environment
|
|
14
|
+
const WATSONX_API_KEY = process.env.WATSONX_API_KEY;
|
|
15
|
+
const WATSONX_PROJECT_ID = process.env.WATSONX_PROJECT_ID;
|
|
16
|
+
const WATSONX_SPACE_ID = process.env.WATSONX_SPACE_ID; // Deployment space (preferred)
|
|
17
|
+
const WATSONX_URL = process.env.WATSONX_URL || "https://us-south.ml.cloud.ibm.com";
|
|
18
|
+
|
|
19
|
+
// IBM Z / Key Protect configuration
|
|
20
|
+
const KEY_PROTECT_API_KEY = process.env.KEY_PROTECT_API_KEY || process.env.WATSONX_API_KEY;
|
|
21
|
+
const KEY_PROTECT_INSTANCE_ID = process.env.KEY_PROTECT_INSTANCE_ID;
|
|
22
|
+
const KEY_PROTECT_URL = process.env.KEY_PROTECT_URL || "https://us-south.kms.cloud.ibm.com";
|
|
23
|
+
|
|
24
|
+
// z/OS Connect configuration (optional - requires mainframe access)
|
|
25
|
+
const ZOS_CONNECT_URL = process.env.ZOS_CONNECT_URL;
|
|
26
|
+
const ZOS_CONNECT_API_KEY = process.env.ZOS_CONNECT_API_KEY;
|
|
27
|
+
|
|
28
|
+
// Initialize watsonx.ai client
|
|
29
|
+
let watsonxClient = null;
|
|
30
|
+
let keyProtectClient = null;
|
|
31
|
+
|
|
32
|
+
function getWatsonxClient() {
|
|
33
|
+
if (!watsonxClient && WATSONX_API_KEY) {
|
|
34
|
+
watsonxClient = WatsonXAI.newInstance({
|
|
35
|
+
version: "2024-05-31",
|
|
36
|
+
serviceUrl: WATSONX_URL,
|
|
37
|
+
authenticator: new IamAuthenticator({
|
|
38
|
+
apikey: WATSONX_API_KEY,
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return watsonxClient;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Initialize Key Protect client (IBM Z HSM-backed key management)
|
|
46
|
+
function getKeyProtectClient() {
|
|
47
|
+
// Temporarily disabled - @ibm-cloud/key-protect package not available
|
|
48
|
+
// if (!keyProtectClient && KEY_PROTECT_API_KEY && KEY_PROTECT_INSTANCE_ID) {
|
|
49
|
+
// keyProtectClient = KeyProtectV2.newInstance({
|
|
50
|
+
// authenticator: new IamAuthenticator({
|
|
51
|
+
// apikey: KEY_PROTECT_API_KEY,
|
|
52
|
+
// }),
|
|
53
|
+
// serviceUrl: KEY_PROTECT_URL,
|
|
54
|
+
// });
|
|
55
|
+
// keyProtectClient.setServiceUrl(KEY_PROTECT_URL);
|
|
56
|
+
// }
|
|
57
|
+
return null; // Disabled
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// z/OS Connect API caller (for mainframe integration)
|
|
61
|
+
async function callZosConnect(endpoint, method = "GET", body = null) {
|
|
62
|
+
if (!ZOS_CONNECT_URL) {
|
|
63
|
+
throw new Error("z/OS Connect not configured. Set ZOS_CONNECT_URL environment variable.");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const url = `${ZOS_CONNECT_URL}${endpoint}`;
|
|
67
|
+
const headers = {
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
"Accept": "application/json",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (ZOS_CONNECT_API_KEY) {
|
|
73
|
+
headers["Authorization"] = `Bearer ${ZOS_CONNECT_API_KEY}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const options = { method, headers };
|
|
77
|
+
if (body && (method === "POST" || method === "PUT")) {
|
|
78
|
+
options.body = JSON.stringify(body);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const response = await fetch(url, options);
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`z/OS Connect error: ${response.status} ${response.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
return response.json();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Create MCP server
|
|
89
|
+
const server = new Server(
|
|
90
|
+
{
|
|
91
|
+
name: "watsonx-ibmz-mcp-server",
|
|
92
|
+
version: "2.0.0",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
capabilities: {
|
|
96
|
+
tools: {},
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Define available tools
|
|
102
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
103
|
+
return {
|
|
104
|
+
tools: [
|
|
105
|
+
{
|
|
106
|
+
name: "watsonx_generate",
|
|
107
|
+
description: "Generate text using IBM watsonx.ai foundation models (Granite, Llama, Mistral, etc.)",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {
|
|
111
|
+
prompt: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "The prompt to send to the model",
|
|
114
|
+
},
|
|
115
|
+
model_id: {
|
|
116
|
+
type: "string",
|
|
117
|
+
description: "Model ID (e.g., 'ibm/granite-3-3-8b-instruct', 'meta-llama/llama-3-70b-instruct')",
|
|
118
|
+
default: "ibm/granite-3-3-8b-instruct",
|
|
119
|
+
},
|
|
120
|
+
max_new_tokens: {
|
|
121
|
+
type: "number",
|
|
122
|
+
description: "Maximum number of tokens to generate",
|
|
123
|
+
default: 500,
|
|
124
|
+
},
|
|
125
|
+
temperature: {
|
|
126
|
+
type: "number",
|
|
127
|
+
description: "Temperature for sampling (0-2)",
|
|
128
|
+
default: 0.7,
|
|
129
|
+
},
|
|
130
|
+
top_p: {
|
|
131
|
+
type: "number",
|
|
132
|
+
description: "Top-p nucleus sampling",
|
|
133
|
+
default: 1.0,
|
|
134
|
+
},
|
|
135
|
+
top_k: {
|
|
136
|
+
type: "number",
|
|
137
|
+
description: "Top-k sampling",
|
|
138
|
+
default: 50,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
required: ["prompt"],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "watsonx_list_models",
|
|
146
|
+
description: "List available foundation models in watsonx.ai",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
type: "object",
|
|
149
|
+
properties: {},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "watsonx_embeddings",
|
|
154
|
+
description: "Generate text embeddings using watsonx.ai embedding models",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: "object",
|
|
157
|
+
properties: {
|
|
158
|
+
texts: {
|
|
159
|
+
type: "array",
|
|
160
|
+
items: { type: "string" },
|
|
161
|
+
description: "Array of texts to embed",
|
|
162
|
+
},
|
|
163
|
+
model_id: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: "Embedding model ID",
|
|
166
|
+
default: "ibm/slate-125m-english-rtrvr-v2",
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
required: ["texts"],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "watsonx_chat",
|
|
174
|
+
description: "Have a conversation with watsonx.ai chat models",
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: "object",
|
|
177
|
+
properties: {
|
|
178
|
+
messages: {
|
|
179
|
+
type: "array",
|
|
180
|
+
items: {
|
|
181
|
+
type: "object",
|
|
182
|
+
properties: {
|
|
183
|
+
role: { type: "string", enum: ["system", "user", "assistant"] },
|
|
184
|
+
content: { type: "string" },
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
description: "Array of chat messages",
|
|
188
|
+
},
|
|
189
|
+
model_id: {
|
|
190
|
+
type: "string",
|
|
191
|
+
description: "Chat model ID",
|
|
192
|
+
default: "ibm/granite-3-3-8b-instruct",
|
|
193
|
+
},
|
|
194
|
+
max_new_tokens: {
|
|
195
|
+
type: "number",
|
|
196
|
+
default: 500,
|
|
197
|
+
},
|
|
198
|
+
temperature: {
|
|
199
|
+
type: "number",
|
|
200
|
+
default: 0.7,
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
required: ["messages"],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
// IBM Z / Key Protect Tools
|
|
207
|
+
{
|
|
208
|
+
name: "key_protect_list_keys",
|
|
209
|
+
description: "List encryption keys from IBM Key Protect (HSM-backed key management on IBM Z infrastructure)",
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
limit: {
|
|
214
|
+
type: "number",
|
|
215
|
+
description: "Maximum number of keys to return",
|
|
216
|
+
default: 100,
|
|
217
|
+
},
|
|
218
|
+
offset: {
|
|
219
|
+
type: "number",
|
|
220
|
+
description: "Offset for pagination",
|
|
221
|
+
default: 0,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: "key_protect_create_key",
|
|
228
|
+
description: "Create a new encryption key in IBM Key Protect (stored in FIPS 140-2 Level 3 HSM)",
|
|
229
|
+
inputSchema: {
|
|
230
|
+
type: "object",
|
|
231
|
+
properties: {
|
|
232
|
+
name: {
|
|
233
|
+
type: "string",
|
|
234
|
+
description: "Name for the new key",
|
|
235
|
+
},
|
|
236
|
+
description: {
|
|
237
|
+
type: "string",
|
|
238
|
+
description: "Description of the key's purpose",
|
|
239
|
+
},
|
|
240
|
+
type: {
|
|
241
|
+
type: "string",
|
|
242
|
+
enum: ["root_key", "standard_key"],
|
|
243
|
+
description: "Key type: root_key (for wrapping) or standard_key (for encryption)",
|
|
244
|
+
default: "standard_key",
|
|
245
|
+
},
|
|
246
|
+
extractable: {
|
|
247
|
+
type: "boolean",
|
|
248
|
+
description: "Whether the key material can be extracted",
|
|
249
|
+
default: false,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
required: ["name"],
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "key_protect_get_key",
|
|
257
|
+
description: "Get details of a specific key from IBM Key Protect",
|
|
258
|
+
inputSchema: {
|
|
259
|
+
type: "object",
|
|
260
|
+
properties: {
|
|
261
|
+
key_id: {
|
|
262
|
+
type: "string",
|
|
263
|
+
description: "The ID of the key to retrieve",
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
required: ["key_id"],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "key_protect_wrap_key",
|
|
271
|
+
description: "Wrap (encrypt) data using a root key in IBM Key Protect - for envelope encryption",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
key_id: {
|
|
276
|
+
type: "string",
|
|
277
|
+
description: "The ID of the root key to use for wrapping",
|
|
278
|
+
},
|
|
279
|
+
plaintext: {
|
|
280
|
+
type: "string",
|
|
281
|
+
description: "Base64-encoded data encryption key to wrap",
|
|
282
|
+
},
|
|
283
|
+
aad: {
|
|
284
|
+
type: "array",
|
|
285
|
+
items: { type: "string" },
|
|
286
|
+
description: "Additional authentication data (AAD) for AEAD encryption",
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
required: ["key_id", "plaintext"],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
name: "key_protect_unwrap_key",
|
|
294
|
+
description: "Unwrap (decrypt) data using a root key in IBM Key Protect",
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
key_id: {
|
|
299
|
+
type: "string",
|
|
300
|
+
description: "The ID of the root key to use for unwrapping",
|
|
301
|
+
},
|
|
302
|
+
ciphertext: {
|
|
303
|
+
type: "string",
|
|
304
|
+
description: "Base64-encoded wrapped data encryption key",
|
|
305
|
+
},
|
|
306
|
+
aad: {
|
|
307
|
+
type: "array",
|
|
308
|
+
items: { type: "string" },
|
|
309
|
+
description: "Additional authentication data (must match wrap AAD)",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
required: ["key_id", "ciphertext"],
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "key_protect_delete_key",
|
|
317
|
+
description: "Delete an encryption key from IBM Key Protect (irreversible)",
|
|
318
|
+
inputSchema: {
|
|
319
|
+
type: "object",
|
|
320
|
+
properties: {
|
|
321
|
+
key_id: {
|
|
322
|
+
type: "string",
|
|
323
|
+
description: "The ID of the key to delete",
|
|
324
|
+
},
|
|
325
|
+
force: {
|
|
326
|
+
type: "boolean",
|
|
327
|
+
description: "Force deletion even if key has associated resources",
|
|
328
|
+
default: false,
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
required: ["key_id"],
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
// z/OS Connect Tools (requires mainframe access)
|
|
335
|
+
{
|
|
336
|
+
name: "zos_connect_list_services",
|
|
337
|
+
description: "List available z/OS Connect services (RESTful APIs to mainframe programs). Requires ZOS_CONNECT_URL to be configured.",
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: "object",
|
|
340
|
+
properties: {},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: "zos_connect_call_service",
|
|
345
|
+
description: "Call a z/OS Connect service to interact with mainframe programs (CICS, IMS, batch). Requires ZOS_CONNECT_URL to be configured.",
|
|
346
|
+
inputSchema: {
|
|
347
|
+
type: "object",
|
|
348
|
+
properties: {
|
|
349
|
+
service_name: {
|
|
350
|
+
type: "string",
|
|
351
|
+
description: "Name of the z/OS Connect service to call",
|
|
352
|
+
},
|
|
353
|
+
operation: {
|
|
354
|
+
type: "string",
|
|
355
|
+
description: "Operation/method to invoke (e.g., GET, POST)",
|
|
356
|
+
default: "POST",
|
|
357
|
+
},
|
|
358
|
+
payload: {
|
|
359
|
+
type: "object",
|
|
360
|
+
description: "JSON payload to send to the mainframe service",
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
required: ["service_name"],
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "zos_connect_get_service_info",
|
|
368
|
+
description: "Get detailed information about a z/OS Connect service including its OpenAPI specification",
|
|
369
|
+
inputSchema: {
|
|
370
|
+
type: "object",
|
|
371
|
+
properties: {
|
|
372
|
+
service_name: {
|
|
373
|
+
type: "string",
|
|
374
|
+
description: "Name of the z/OS Connect service",
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
required: ["service_name"],
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
};
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Handle tool calls
|
|
385
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
386
|
+
const { name, arguments: args } = request.params;
|
|
387
|
+
const client = getWatsonxClient();
|
|
388
|
+
|
|
389
|
+
if (!client) {
|
|
390
|
+
return {
|
|
391
|
+
content: [
|
|
392
|
+
{
|
|
393
|
+
type: "text",
|
|
394
|
+
text: "Error: watsonx.ai not configured. Set WATSONX_API_KEY environment variable.",
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
switch (name) {
|
|
402
|
+
case "watsonx_generate": {
|
|
403
|
+
const params = {
|
|
404
|
+
input: args.prompt,
|
|
405
|
+
modelId: args.model_id || "ibm/granite-3-3-8b-instruct",
|
|
406
|
+
parameters: {
|
|
407
|
+
max_new_tokens: args.max_new_tokens || 500,
|
|
408
|
+
temperature: args.temperature || 0.7,
|
|
409
|
+
top_p: args.top_p || 1.0,
|
|
410
|
+
top_k: args.top_k || 50,
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Add spaceId (preferred) or projectId
|
|
415
|
+
if (WATSONX_SPACE_ID) {
|
|
416
|
+
params.spaceId = WATSONX_SPACE_ID;
|
|
417
|
+
} else if (WATSONX_PROJECT_ID) {
|
|
418
|
+
params.projectId = WATSONX_PROJECT_ID;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const response = await client.generateText(params);
|
|
422
|
+
|
|
423
|
+
const generatedText = response.result.results?.[0]?.generated_text || "";
|
|
424
|
+
return {
|
|
425
|
+
content: [
|
|
426
|
+
{
|
|
427
|
+
type: "text",
|
|
428
|
+
text: generatedText,
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
case "watsonx_list_models": {
|
|
435
|
+
const response = await client.listFoundationModelSpecs({
|
|
436
|
+
limit: 100,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
const models = response.result.resources?.map((m) => ({
|
|
440
|
+
id: m.model_id,
|
|
441
|
+
name: m.label,
|
|
442
|
+
provider: m.provider,
|
|
443
|
+
tasks: m.tasks,
|
|
444
|
+
})) || [];
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
content: [
|
|
448
|
+
{
|
|
449
|
+
type: "text",
|
|
450
|
+
text: JSON.stringify(models, null, 2),
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
case "watsonx_embeddings": {
|
|
457
|
+
const params = {
|
|
458
|
+
inputs: args.texts,
|
|
459
|
+
modelId: args.model_id || "ibm/slate-125m-english-rtrvr-v2",
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// Add spaceId (preferred) or projectId
|
|
463
|
+
if (WATSONX_SPACE_ID) {
|
|
464
|
+
params.spaceId = WATSONX_SPACE_ID;
|
|
465
|
+
} else if (WATSONX_PROJECT_ID) {
|
|
466
|
+
params.projectId = WATSONX_PROJECT_ID;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const response = await client.embedText(params);
|
|
470
|
+
|
|
471
|
+
return {
|
|
472
|
+
content: [
|
|
473
|
+
{
|
|
474
|
+
type: "text",
|
|
475
|
+
text: JSON.stringify(response.result, null, 2),
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
case "watsonx_chat": {
|
|
482
|
+
// Format messages for chat completion
|
|
483
|
+
const formattedPrompt = args.messages
|
|
484
|
+
.map((m) => {
|
|
485
|
+
if (m.role === "system") return `System: ${m.content}`;
|
|
486
|
+
if (m.role === "user") return `User: ${m.content}`;
|
|
487
|
+
if (m.role === "assistant") return `Assistant: ${m.content}`;
|
|
488
|
+
return m.content;
|
|
489
|
+
})
|
|
490
|
+
.join("\n\n");
|
|
491
|
+
|
|
492
|
+
const params = {
|
|
493
|
+
input: formattedPrompt + "\n\nAssistant:",
|
|
494
|
+
modelId: args.model_id || "ibm/granite-3-3-8b-instruct",
|
|
495
|
+
parameters: {
|
|
496
|
+
max_new_tokens: args.max_new_tokens || 500,
|
|
497
|
+
temperature: args.temperature || 0.7,
|
|
498
|
+
stop_sequences: ["User:", "System:"],
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// Add spaceId (preferred) or projectId
|
|
503
|
+
if (WATSONX_SPACE_ID) {
|
|
504
|
+
params.spaceId = WATSONX_SPACE_ID;
|
|
505
|
+
} else if (WATSONX_PROJECT_ID) {
|
|
506
|
+
params.projectId = WATSONX_PROJECT_ID;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const response = await client.generateText(params);
|
|
510
|
+
|
|
511
|
+
const generatedText = response.result.results?.[0]?.generated_text || "";
|
|
512
|
+
return {
|
|
513
|
+
content: [
|
|
514
|
+
{
|
|
515
|
+
type: "text",
|
|
516
|
+
text: generatedText.trim(),
|
|
517
|
+
},
|
|
518
|
+
],
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
default:
|
|
523
|
+
return {
|
|
524
|
+
content: [
|
|
525
|
+
{
|
|
526
|
+
type: "text",
|
|
527
|
+
text: `Unknown tool: ${name}`,
|
|
528
|
+
},
|
|
529
|
+
],
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
} catch (error) {
|
|
533
|
+
return {
|
|
534
|
+
content: [
|
|
535
|
+
{
|
|
536
|
+
type: "text",
|
|
537
|
+
text: `Error calling watsonx.ai: ${error.message}`,
|
|
538
|
+
},
|
|
539
|
+
],
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// Start server
|
|
545
|
+
async function main() {
|
|
546
|
+
const transport = new StdioServerTransport();
|
|
547
|
+
await server.connect(transport);
|
|
548
|
+
console.error("watsonx MCP server running on stdio");
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
main().catch(console.error);
|
package/linkedin-post.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# LinkedIn Post - watsonx MCP Server
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Post Option 1 (Technical Focus)
|
|
6
|
+
|
|
7
|
+
**Just shipped: watsonx MCP Server** 🚀
|
|
8
|
+
|
|
9
|
+
I built a Model Context Protocol server that lets Claude delegate tasks to IBM watsonx.ai foundation models.
|
|
10
|
+
|
|
11
|
+
**The result?** A two-agent AI system where Claude (Opus 4.5) handles complex reasoning while delegating specific workloads to Granite, Llama 3, and Mistral models.
|
|
12
|
+
|
|
13
|
+
**What it does:**
|
|
14
|
+
• `watsonx_generate` - Text generation with enterprise models
|
|
15
|
+
• `watsonx_chat` - Multi-turn conversations
|
|
16
|
+
• `watsonx_embeddings` - Vector embeddings for RAG pipelines
|
|
17
|
+
• `watsonx_list_models` - Model discovery
|
|
18
|
+
|
|
19
|
+
**Why it matters:**
|
|
20
|
+
→ Cost optimization by routing to smaller models
|
|
21
|
+
→ Enterprise compliance with IBM infrastructure
|
|
22
|
+
→ Specialized model capabilities (Granite for enterprise, Llama for reasoning)
|
|
23
|
+
→ Embedding generation for semantic search
|
|
24
|
+
|
|
25
|
+
Built with Node.js, the @IBM watsonx-ai SDK, and the MCP protocol.
|
|
26
|
+
|
|
27
|
+
📖 Live Demo: https://purplesquirrelmedia.github.io/watsonx-mcp-server/
|
|
28
|
+
💻 Source: https://github.com/PurpleSquirrelMedia/watsonx-mcp-server
|
|
29
|
+
|
|
30
|
+
This is what multi-agent AI looks like in practice—not replacing models, but orchestrating them.
|
|
31
|
+
|
|
32
|
+
#AI #watsonx #IBM #Claude #MCP #MachineLearning #Anthropic #LLM #OpenSource
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Post Option 2 (Narrative Focus)
|
|
37
|
+
|
|
38
|
+
**What if Claude could talk to other AI models?**
|
|
39
|
+
|
|
40
|
+
That's exactly what I built.
|
|
41
|
+
|
|
42
|
+
The watsonx MCP Server creates a bridge between Claude and IBM's foundation models. Now when I'm working in Claude Code, it can:
|
|
43
|
+
|
|
44
|
+
✅ Delegate text generation to IBM Granite models
|
|
45
|
+
✅ Generate embeddings with Slate for RAG pipelines
|
|
46
|
+
✅ Use Llama 3 70B for specific reasoning tasks
|
|
47
|
+
✅ Route to cost-effective models for simple tasks
|
|
48
|
+
|
|
49
|
+
**The architecture is simple:**
|
|
50
|
+
Claude (primary agent) → MCP Server → watsonx.ai API → Foundation Models
|
|
51
|
+
|
|
52
|
+
**The implications are profound:**
|
|
53
|
+
This isn't just API integration. It's the beginning of AI systems that can intelligently select which model to use for which task.
|
|
54
|
+
|
|
55
|
+
Claude handles the orchestration. Watson handles the specialized workloads.
|
|
56
|
+
|
|
57
|
+
**Try it yourself:**
|
|
58
|
+
📖 https://purplesquirrelmedia.github.io/watsonx-mcp-server/
|
|
59
|
+
💻 https://github.com/PurpleSquirrelMedia/watsonx-mcp-server
|
|
60
|
+
|
|
61
|
+
MIT licensed. Built in an afternoon with Claude Code.
|
|
62
|
+
|
|
63
|
+
The future of AI isn't one model to rule them all—it's intelligent orchestration.
|
|
64
|
+
|
|
65
|
+
#AI #TwoAgentArchitecture #watsonx #Claude #IBM #Anthropic #MCP
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Post Option 3 (Short & Punchy)
|
|
70
|
+
|
|
71
|
+
**New drop: watsonx MCP Server** âš¡
|
|
72
|
+
|
|
73
|
+
Claude can now delegate tasks to IBM watsonx.ai.
|
|
74
|
+
|
|
75
|
+
Two agents. One workflow. Infinite possibilities.
|
|
76
|
+
|
|
77
|
+
→ Text generation with Granite & Llama
|
|
78
|
+
→ Embeddings for RAG
|
|
79
|
+
→ Model-specific routing
|
|
80
|
+
→ Enterprise compliance
|
|
81
|
+
|
|
82
|
+
Demo: https://purplesquirrelmedia.github.io/watsonx-mcp-server/
|
|
83
|
+
Code: https://github.com/PurpleSquirrelMedia/watsonx-mcp-server
|
|
84
|
+
|
|
85
|
+
This is the future of AI development—not choosing between models, but orchestrating them.
|
|
86
|
+
|
|
87
|
+
#watsonx #Claude #MCP #AI #IBM #Anthropic
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Suggested Hashtags (pick 5-7):
|
|
92
|
+
#AI #ArtificialIntelligence #watsonx #IBM #Claude #Anthropic #MCP #ModelContextProtocol #LLM #MachineLearning #OpenSource #TwoAgentAI #RAG #Embeddings #FoundationModels #NodeJS #Developer #TechInnovation
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@purplesquirrel/watsonx-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for IBM watsonx.ai integration with Claude",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"watsonx-mcp-server": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node index.js",
|
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"watsonx",
|
|
17
|
+
"ibm",
|
|
18
|
+
"claude",
|
|
19
|
+
"ai"
|
|
20
|
+
],
|
|
21
|
+
"author": "Matthew Karsten",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@ibm-cloud/watsonx-ai": "^1.7.5",
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
26
|
+
"ibm-cloud-sdk-core": "^5.4.5"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/test-watsonx.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { WatsonXAI } from '@ibm-cloud/watsonx-ai';
|
|
3
|
+
import { IamAuthenticator } from 'ibm-cloud-sdk-core';
|
|
4
|
+
|
|
5
|
+
const client = WatsonXAI.newInstance({
|
|
6
|
+
version: '2024-05-31',
|
|
7
|
+
serviceUrl: process.env.WATSONX_URL || 'https://us-south.ml.cloud.ibm.com',
|
|
8
|
+
authenticator: new IamAuthenticator({
|
|
9
|
+
apikey: process.env.WATSONX_API_KEY,
|
|
10
|
+
}),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
console.log('=== watsonx.ai MCP Server Test ===\n');
|
|
14
|
+
|
|
15
|
+
// List available models
|
|
16
|
+
console.log('1. Listing foundation models...');
|
|
17
|
+
const modelsResp = await client.listFoundationModelSpecs({ limit: 10 });
|
|
18
|
+
const models = modelsResp.result.resources || [];
|
|
19
|
+
console.log(` Found ${models.length} models:`);
|
|
20
|
+
models.slice(0, 5).forEach(m => {
|
|
21
|
+
console.log(` - ${m.model_id}`);
|
|
22
|
+
});
|
|
23
|
+
if (models.length > 5) {
|
|
24
|
+
console.log(` ... and ${models.length - 5} more`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log('\n✅ watsonx.ai connection successful!');
|
|
28
|
+
console.log('\nNote: Text generation requires a Project ID. Create a project at:');
|
|
29
|
+
console.log('https://dataplatform.cloud.ibm.com');
|