wxo-builder-mcp-server 1.0.8

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 ADDED
@@ -0,0 +1,466 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Watson Orchestrate MCP Server
4
+ * Parities vscode-extension functionality: tools, agents, connections, flows.
5
+ *
6
+ * @author Markus van Kempen
7
+ * @license Apache-2.0
8
+ */
9
+ import * as z from 'zod';
10
+ import * as dotenv from 'dotenv';
11
+ import { readFileSync } from 'fs';
12
+ import { dirname, join } from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
15
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
16
+ import { config, validateConfig } from './config.js';
17
+ import { listSkills, listToolsWithConnections, listStandardTools, getSkill, deleteSkill, deploySkill, updateSkill, copySkill, resolveToolByName, executeTool, deployToolFromUrl, createToolAndAssignToAgent, testToolLocal, } from './skills.js';
18
+ import { listAgents, createAgent, invokeAgentByNameOrId, getAgent, getChatStarterSettings, updateChatStarterSettings, updateAgentByNameOrId, updateAgentInstructionsFromTools, deleteAgent, listAgentTools, assignToolToAgent, resolveAgentByName, } from './agents.js';
19
+ import { listFlows, createFlow, deleteFlow, getFlow } from './flows.js';
20
+ import { listConnectors, listConnections, listConnectionsAll, listActiveLiveConnections, getConnection, createConnection, deleteConnection, createConnectionConfiguration, setApiKeyCredentials, setBasicCredentials, setBearerCredentials, } from './connections.js';
21
+ dotenv.config();
22
+ const __dirname = dirname(fileURLToPath(import.meta.url));
23
+ const OPENAPI_PATH = join(__dirname, '..', 'watson-orchestrate-openapi.json');
24
+ const optionalStr = () => z.string().optional();
25
+ /** Resolve agent_id from args or WO_AGENT_IDs/WO_AGENT_ID default. Use when user omits agent_id/agent_name. */
26
+ async function resolveAgentId(args) {
27
+ if (args?.agent_id)
28
+ return args.agent_id;
29
+ if (args?.agent_name) {
30
+ const r = await resolveAgentByName(args.agent_name);
31
+ if (!r)
32
+ throw new Error(`Agent not found: "${args.agent_name}"`);
33
+ return r;
34
+ }
35
+ if (config.agentId)
36
+ return config.agentId;
37
+ throw new Error('Provide agent_id or agent_name, or set WO_AGENT_IDs (or WO_AGENT_ID) in .env');
38
+ }
39
+ /** Flatten an object to a dict with only primitive values (Langflow DataFrame requires plain dicts). */
40
+ function flattenRow(obj) {
41
+ const out = {};
42
+ for (const [k, v] of Object.entries(obj)) {
43
+ if (v === null || v === undefined)
44
+ out[k] = '';
45
+ else if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')
46
+ out[k] = v;
47
+ else if (Array.isArray(v))
48
+ out[k] = JSON.stringify(v);
49
+ else if (typeof v === 'object')
50
+ out[k] = JSON.stringify(v);
51
+ else
52
+ out[k] = String(v);
53
+ }
54
+ return out;
55
+ }
56
+ /** Normalize result to a list of flat dicts for Langflow DataFrame compatibility. */
57
+ function toDataFrameSafe(result) {
58
+ let list = [];
59
+ if (Array.isArray(result)) {
60
+ list = result.map((x) => (x !== null && typeof x === 'object' && !Array.isArray(x) ? x : { value: x }));
61
+ }
62
+ else if (result !== null && typeof result === 'object') {
63
+ const obj = result;
64
+ let arr = null;
65
+ if (Array.isArray(obj.assistants))
66
+ arr = obj.assistants;
67
+ else if (Array.isArray(obj.data))
68
+ arr = obj.data;
69
+ else if (Array.isArray(obj.items))
70
+ arr = obj.items;
71
+ else if (Array.isArray(obj.tools))
72
+ arr = obj.tools;
73
+ else if (Array.isArray(obj.skills))
74
+ arr = obj.skills;
75
+ else if (Array.isArray(obj.results))
76
+ arr = obj.results;
77
+ else if (Array.isArray(obj.tools_with_connections) || Array.isArray(obj.standard_tools)) {
78
+ const withConn = Array.isArray(obj.tools_with_connections) ? obj.tools_with_connections : [];
79
+ const standard = Array.isArray(obj.standard_tools) ? obj.standard_tools : [];
80
+ arr = [...withConn, ...standard];
81
+ }
82
+ if (arr)
83
+ list = arr.filter((x) => x !== null && typeof x === 'object' && !Array.isArray(x));
84
+ }
85
+ if (list.length === 0) {
86
+ return [{ output: typeof result === 'string' ? result : JSON.stringify(result) }];
87
+ }
88
+ return list.map((x) => flattenRow(x));
89
+ }
90
+ async function runTool(handler) {
91
+ if (!validateConfig()) {
92
+ throw new Error('Missing configuration. Set WO_API_KEY and WO_INSTANCE_URL in .env');
93
+ }
94
+ const result = await handler();
95
+ const safe = toDataFrameSafe(result);
96
+ return { content: [{ type: 'text', text: JSON.stringify(safe, null, 2) }] };
97
+ }
98
+ async function main() {
99
+ const server = new McpServer({ name: 'wxo-builder-mcp-server', version: '1.0.1' }, { capabilities: {} });
100
+ // --- SKILLS ---
101
+ server.registerTool('get_api_spec', {
102
+ description: 'Get the Watson Orchestrate OpenAPI spec. Describes all REST endpoints the MCP server uses (tools, agents, connections, flows, runs). Use to understand what operations the Watson Orchestrate instance supports.',
103
+ inputSchema: { format: z.enum(['full', 'summary']).optional() },
104
+ }, async (args, _extra) => {
105
+ try {
106
+ const spec = JSON.parse(readFileSync(OPENAPI_PATH, 'utf-8'));
107
+ if (args?.format === 'summary') {
108
+ const summary = {
109
+ title: spec.info?.title,
110
+ paths: Object.keys(spec.paths || {}),
111
+ operations: Object.entries(spec.paths || {}).flatMap(([path, methods]) => Object.keys(methods)
112
+ .filter((m) => ['get', 'post', 'patch', 'put', 'delete'].includes(m))
113
+ .map((m) => `${m.toUpperCase()} ${path}`)),
114
+ };
115
+ return { content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }] };
116
+ }
117
+ return { content: [{ type: 'text', text: JSON.stringify(spec, null, 2) }] };
118
+ }
119
+ catch (e) {
120
+ const msg = e instanceof Error ? e.message : String(e);
121
+ return {
122
+ content: [{ type: 'text', text: `Failed to load OpenAPI spec: ${msg}` }],
123
+ isError: true,
124
+ };
125
+ }
126
+ });
127
+ server.registerTool('list_skills', {
128
+ description: 'List all available tools/skills in the Watson Orchestrate catalog (default limit 100)',
129
+ inputSchema: {},
130
+ }, async (_args, _extra) => runTool(() => listSkills(100, 0)));
131
+ server.registerTool('list_tools_with_connections', {
132
+ description: 'List Watson Orchestrate tools grouped by connection status: tools that require an API key/OAuth connection vs standard tools. Matches the extension Tools view. Use for prompts like "list my tools with active connections".',
133
+ inputSchema: {},
134
+ }, async (_args, _extra) => runTool(() => listToolsWithConnections(100)));
135
+ server.registerTool('list_standard_tools', {
136
+ description: "List only standard tools (tools with no connections). Returns accurate count and list from Watson Orchestrate. Use for 'list standard tools' or 'tools with no connections' – tools with empty/missing security and connection_id. Do not guess; use this tool for accurate data.",
137
+ inputSchema: {},
138
+ }, async (_args, _extra) => runTool(() => listStandardTools(100)));
139
+ server.registerTool('execute_tool', {
140
+ description: 'Execute a Watson Orchestrate tool by name or ID. agent_id optional; uses WO_AGENT_IDs/WO_AGENT_ID when omitted. Pass parameters as JSON if the tool requires them.',
141
+ inputSchema: {
142
+ tool_name: z.string().optional(),
143
+ tool_id: z.string().optional(),
144
+ parameters: z.record(z.string(), z.any()).optional(),
145
+ agent_id: z.string().optional(),
146
+ },
147
+ }, async (args, _extra) => runTool(() => executeTool({
148
+ tool_name: args?.tool_name,
149
+ tool_id: args?.tool_id,
150
+ parameters: args?.parameters,
151
+ agent_id: args?.agent_id || config.agentId || undefined,
152
+ })));
153
+ server.registerTool('get_skill', {
154
+ description: 'Get a specific skill/tool by ID. Returns tool details including display name, description, and binding.',
155
+ inputSchema: { skill_id: z.string() },
156
+ }, async (args, _extra) => runTool(() => getSkill(args.skill_id)));
157
+ server.registerTool('delete_skill', {
158
+ description: 'Delete a skill by ID',
159
+ inputSchema: { skill_id: z.string() },
160
+ }, async (args, _extra) => runTool(() => deleteSkill(args.skill_id)));
161
+ server.registerTool('deploy_skill', {
162
+ description: 'Deploy a tool from OpenAPI spec. Set openapi_spec["x-ibm-connection-id"] to bind a connection.',
163
+ inputSchema: {
164
+ tool_spec: z.record(z.string(), z.any()),
165
+ openapi_spec: z.record(z.string(), z.any()),
166
+ },
167
+ }, async (args, _extra) => runTool(() => deploySkill({ toolSpec: args.tool_spec, openApiSpec: args.openapi_spec })));
168
+ server.registerTool('update_skill', {
169
+ description: 'Update a tool (name, display_name, description, permission)',
170
+ inputSchema: {
171
+ skill_id: z.string(),
172
+ skill_json: z.record(z.string(), z.any()),
173
+ },
174
+ }, async (args, _extra) => runTool(() => updateSkill(args.skill_id, args.skill_json)));
175
+ server.registerTool('copy_skill', {
176
+ description: 'Copy a tool. Creates a new tool with same spec and connection. Use new_name (e.g. "MVKWeatherV2") for a custom name. Names must use only letters, digits, underscores.',
177
+ inputSchema: {
178
+ skill_id: z.string(),
179
+ skill_name: z.string().optional(),
180
+ new_name: z.string().optional(),
181
+ },
182
+ }, async (args, _extra) => {
183
+ const skillId = args?.skill_id;
184
+ const skillName = args?.skill_name;
185
+ if (!skillId && !skillName)
186
+ throw new Error('Provide skill_id or skill_name');
187
+ return runTool(async () => {
188
+ let id = skillId ?? null;
189
+ if (!id && skillName) {
190
+ id = await resolveToolByName(skillName);
191
+ if (!id)
192
+ throw new Error(`Tool not found: "${skillName}"`);
193
+ }
194
+ if (!id)
195
+ throw new Error('Provide skill_id or skill_name');
196
+ return copySkill(id, args?.new_name);
197
+ });
198
+ });
199
+ server.registerTool('deploy_tool_from_url', {
200
+ description: 'Create a tool from a URL. Works with: (1) APIs with API key – auto-creates connection, (2) Public APIs (REST Countries, Open-Meteo) – no auth needed. Use this MCP tool, NOT ADK.',
201
+ inputSchema: {
202
+ url: z.string(),
203
+ tool_name: z.string(),
204
+ description: optionalStr(),
205
+ },
206
+ }, async (args, _extra) => runTool(() => deployToolFromUrl({
207
+ url: args.url,
208
+ tool_name: args.tool_name,
209
+ description: args?.description,
210
+ })));
211
+ server.registerTool('create_tool_and_assign_to_agent', {
212
+ description: "Create a tool from URL and assign to agent – ONE step. Use for 'create REST Countries tool and assign to TimeWeatherAgent'. Works with public APIs (no auth) and APIs with keys. Use this MCP tool, do NOT use ADK. agent_name/agent_id optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.",
213
+ inputSchema: {
214
+ url: z.string(),
215
+ tool_name: z.string(),
216
+ agent_id: z.string().optional(),
217
+ agent_name: z.string().optional(),
218
+ description: optionalStr(),
219
+ },
220
+ }, async (args, _extra) => {
221
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
222
+ return runTool(() => createToolAndAssignToAgent({
223
+ url: args.url,
224
+ tool_name: args.tool_name,
225
+ agent_id: agentId,
226
+ description: args?.description,
227
+ }));
228
+ });
229
+ server.registerTool('assign_tool_to_agent', {
230
+ description: 'Assign a tool to an agent (add to toolkit). agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.',
231
+ inputSchema: {
232
+ tool_id: z.string().optional(),
233
+ tool_name: z.string().optional(),
234
+ agent_id: z.string().optional(),
235
+ agent_name: z.string().optional(),
236
+ },
237
+ }, async (args, _extra) => {
238
+ if (!args?.tool_id && !args?.tool_name)
239
+ throw new Error('Provide tool_id or tool_name');
240
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
241
+ return runTool(() => assignToolToAgent({ ...args, agent_id: agentId }));
242
+ });
243
+ server.registerTool('test_tool_local', {
244
+ description: 'Test an API endpoint locally (direct HTTP GET). Use for "run locally" or "test locally". Does not go through Watson Orchestrate.',
245
+ inputSchema: {
246
+ url: z.string(),
247
+ params: z.record(z.string(), z.string()).optional(),
248
+ },
249
+ }, async (args, _extra) => runTool(() => testToolLocal({ url: args.url, params: args?.params })));
250
+ // --- AGENTS ---
251
+ server.registerTool('list_agents', {
252
+ description: 'List all available agents (default limit 50)',
253
+ inputSchema: {},
254
+ }, async (_args, _extra) => runTool(() => listAgents(50, 0)));
255
+ server.registerTool('create_agent', {
256
+ description: 'Create an agent. Pass tools array to assign tool IDs.',
257
+ inputSchema: {
258
+ name: z.string(),
259
+ description: z.string(),
260
+ model_id: z.string(),
261
+ instructions: z.string(),
262
+ tools: z.array(z.string()).optional(),
263
+ },
264
+ }, async (args, _extra) => runTool(() => createAgent(args.name, args.description, args.model_id, args.instructions, args?.tools)));
265
+ server.registerTool('get_agent', {
266
+ description: 'Get agent details by ID or name. Returns config, assigned tools, instructions, and model. agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.',
267
+ inputSchema: {
268
+ agent_id: z.string().optional(),
269
+ agent_name: z.string().optional(),
270
+ },
271
+ }, async (args, _extra) => {
272
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
273
+ return runTool(() => getAgent(agentId));
274
+ });
275
+ server.registerTool('get_agent_chat_starter_settings', {
276
+ description: 'Get chat starter settings for an agent: welcome message and quick prompts. agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.',
277
+ inputSchema: {
278
+ agent_id: z.string().optional(),
279
+ agent_name: z.string().optional(),
280
+ },
281
+ }, async (args, _extra) => {
282
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
283
+ return runTool(() => getChatStarterSettings(agentId));
284
+ });
285
+ server.registerTool('update_agent_chat_starter_settings', {
286
+ description: 'Update chat starter settings: welcome_message, quick_prompts (array of {title, prompt}). agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.',
287
+ inputSchema: {
288
+ agent_id: z.string().optional(),
289
+ agent_name: z.string().optional(),
290
+ welcome_message: z.string().optional(),
291
+ quick_prompts: z.array(z.object({
292
+ title: z.string(),
293
+ prompt: z.string(),
294
+ subtitle: z.string().optional(),
295
+ })).optional(),
296
+ },
297
+ }, async (args, _extra) => {
298
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
299
+ return runTool(async () => {
300
+ const payload = {};
301
+ if (args?.welcome_message !== undefined) {
302
+ payload.welcome_content = { welcome_message: args.welcome_message || null };
303
+ }
304
+ if (args?.quick_prompts !== undefined) {
305
+ payload.starter_prompts = {
306
+ customize: args.quick_prompts.map((p) => ({
307
+ title: p.title,
308
+ prompt: p.prompt,
309
+ ...(p.subtitle ? { subtitle: p.subtitle } : {}),
310
+ })),
311
+ };
312
+ }
313
+ if (Object.keys(payload).length === 0)
314
+ throw new Error('Provide welcome_message or quick_prompts');
315
+ return updateChatStarterSettings(agentId, payload);
316
+ });
317
+ });
318
+ server.registerTool('list_agent_tools', {
319
+ description: 'List tools assigned to an agent, with display names and descriptions. agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.',
320
+ inputSchema: {
321
+ agent_id: z.string().optional(),
322
+ agent_name: z.string().optional(),
323
+ },
324
+ }, async (args, _extra) => {
325
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
326
+ return runTool(() => listAgentTools({ agent_id: agentId }));
327
+ });
328
+ server.registerTool('update_agent', {
329
+ description: 'Update an agent. Pass payload with instructions, tools, description, style, welcome_message, quick_prompts. agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.',
330
+ inputSchema: {
331
+ agent_id: z.string().optional(),
332
+ agent_name: z.string().optional(),
333
+ payload: z.record(z.string(), z.any()),
334
+ },
335
+ }, async (args, _extra) => {
336
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
337
+ return runTool(() => updateAgentByNameOrId({
338
+ agent_id: agentId,
339
+ payload: args.payload,
340
+ }));
341
+ });
342
+ server.registerTool('update_agent_instructions_from_tools', {
343
+ description: "Update an agent's instructions based on its assigned tools. agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set.",
344
+ inputSchema: {
345
+ agent_id: z.string().optional(),
346
+ agent_name: z.string().optional(),
347
+ },
348
+ }, async (args, _extra) => {
349
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
350
+ return runTool(() => updateAgentInstructionsFromTools({
351
+ agent_id: agentId,
352
+ }));
353
+ });
354
+ server.registerTool('invoke_agent', {
355
+ description: 'Chat with an agent. agent_id/agent_name optional if WO_AGENT_IDs (or WO_AGENT_ID) is set. Runs behind the scenes, no script needed.',
356
+ inputSchema: {
357
+ agent_id: z.string().optional(),
358
+ agent_name: z.string().optional(),
359
+ message: z.string(),
360
+ },
361
+ }, async (args, _extra) => {
362
+ if (!args?.message)
363
+ throw new Error('message is required');
364
+ const agentId = await resolveAgentId({ agent_id: args?.agent_id, agent_name: args?.agent_name });
365
+ return runTool(() => invokeAgentByNameOrId({ agent_id: agentId, message: args.message }));
366
+ });
367
+ server.registerTool('delete_agent', {
368
+ description: 'Delete an agent by ID',
369
+ inputSchema: { agent_id: z.string() },
370
+ }, async (args, _extra) => runTool(() => deleteAgent(args.agent_id)));
371
+ // --- CONNECTIONS ---
372
+ server.registerTool('list_connectors', {
373
+ description: 'List available connector applications from the catalog',
374
+ inputSchema: {},
375
+ }, async (_args, _extra) => runTool(() => listConnectors()));
376
+ server.registerTool('list_connections', {
377
+ description: 'List configured connections (scope: draft, live, or all)',
378
+ inputSchema: { scope: z.enum(['draft', 'live', 'all']).optional() },
379
+ }, async (args, _extra) => {
380
+ const scope = args?.scope || 'all';
381
+ return runTool(() => (scope === 'all' ? listConnectionsAll() : listConnections(scope)));
382
+ });
383
+ server.registerTool('list_active_live_connections', {
384
+ description: "List only active and live connections (not tools). Returns deduplicated connection names. Use for 'list all connections which are active and live, just the connections not the tools'.",
385
+ inputSchema: {},
386
+ }, async (_args, _extra) => runTool(() => listActiveLiveConnections()));
387
+ server.registerTool('get_connection', {
388
+ description: 'Get a connection by app_id',
389
+ inputSchema: { app_id: z.string() },
390
+ }, async (args, _extra) => runTool(() => getConnection(args.app_id)));
391
+ server.registerTool('create_connection', {
392
+ description: 'Create a connection. Then use configure_connection for credentials.',
393
+ inputSchema: { app_id: z.string(), display_name: optionalStr() },
394
+ }, async (args, _extra) => runTool(() => createConnection({ app_id: args.app_id, display_name: args?.display_name || args.app_id })));
395
+ server.registerTool('delete_connection', {
396
+ description: 'Delete a connection by app_id',
397
+ inputSchema: { app_id: z.string() },
398
+ }, async (args, _extra) => runTool(() => deleteConnection(args.app_id)));
399
+ server.registerTool('configure_connection', {
400
+ description: 'Configure connection credentials (api_key, basic, bearer)',
401
+ inputSchema: {
402
+ app_id: z.string(),
403
+ kind: z.enum(['api_key', 'basic', 'bearer']),
404
+ env: z.enum(['draft', 'live']).optional(),
405
+ api_key: optionalStr(),
406
+ username: optionalStr(),
407
+ password: optionalStr(),
408
+ token: optionalStr(),
409
+ server_url: optionalStr(),
410
+ },
411
+ }, async (args, _extra) => {
412
+ if (!validateConfig())
413
+ throw new Error('Missing configuration. Set WO_API_KEY and WO_INSTANCE_URL in .env');
414
+ const appId = args.app_id;
415
+ const kind = args.kind;
416
+ const env = (args?.env || 'draft');
417
+ await createConnectionConfiguration(appId, env, kind, 'team', args?.server_url);
418
+ if (kind === 'api_key') {
419
+ if (!args?.api_key)
420
+ throw new Error('api_key required for kind=api_key');
421
+ await setApiKeyCredentials(appId, args.api_key, env);
422
+ }
423
+ else if (kind === 'basic') {
424
+ if (!args?.username || !args?.password)
425
+ throw new Error('username and password required for kind=basic');
426
+ await setBasicCredentials(appId, args.username, args.password, env);
427
+ }
428
+ else if (kind === 'bearer') {
429
+ if (!args?.token)
430
+ throw new Error('token required for kind=bearer');
431
+ await setBearerCredentials(appId, args.token, env);
432
+ }
433
+ return {
434
+ content: [
435
+ {
436
+ type: 'text',
437
+ text: JSON.stringify({ success: true, message: `Connection ${appId} configured for ${env}` }, null, 2),
438
+ },
439
+ ],
440
+ };
441
+ });
442
+ // --- FLOWS ---
443
+ server.registerTool('list_flows', {
444
+ description: 'List all available flows (default limit 50)',
445
+ inputSchema: {},
446
+ }, async (_args, _extra) => runTool(() => listFlows(50, 0)));
447
+ server.registerTool('create_flow', {
448
+ description: 'Create or update a flow',
449
+ inputSchema: { flow_json: z.record(z.string(), z.any()) },
450
+ }, async (args, _extra) => runTool(() => createFlow(args.flow_json)));
451
+ server.registerTool('get_flow', {
452
+ description: 'Get a flow by ID',
453
+ inputSchema: { flow_id: z.string() },
454
+ }, async (args, _extra) => runTool(() => getFlow(args.flow_id)));
455
+ server.registerTool('delete_flow', {
456
+ description: 'Delete a flow by ID',
457
+ inputSchema: { flow_id: z.string() },
458
+ }, async (args, _extra) => runTool(() => deleteFlow(args.flow_id)));
459
+ const transport = new StdioServerTransport();
460
+ await server.connect(transport);
461
+ console.error('Watson Orchestrate (WXO) Builder MCP Server running on stdio');
462
+ }
463
+ main().catch((err) => {
464
+ console.error('Fatal error:', err);
465
+ process.exit(1);
466
+ });
package/dist/models.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WxO MCP Server - Models
3
+ * Default model for agents (mirrors extension models.ts).
4
+ */
5
+ import { woFetch } from './auth.js';
6
+ const PREFERRED_DEFAULT_MODEL_ID = 'groq/openai/gpt-oss-120b';
7
+ export async function listModels() {
8
+ try {
9
+ const res = await woFetch('/v1/models/list', { method: 'GET' });
10
+ if (!res.ok)
11
+ return [];
12
+ const data = await res.json().catch(() => ({}));
13
+ const list = data.resources ?? data.data ?? (Array.isArray(data) ? data : []);
14
+ return Array.isArray(list) ? list : [];
15
+ }
16
+ catch {
17
+ return [];
18
+ }
19
+ }
20
+ export async function getDefaultModelId() {
21
+ try {
22
+ const models = await listModels();
23
+ if (!models.length)
24
+ return PREFERRED_DEFAULT_MODEL_ID;
25
+ const preferred = models.find((m) => m.id === PREFERRED_DEFAULT_MODEL_ID);
26
+ if (preferred)
27
+ return PREFERRED_DEFAULT_MODEL_ID;
28
+ return models[0]?.id ?? PREFERRED_DEFAULT_MODEL_ID;
29
+ }
30
+ catch {
31
+ return PREFERRED_DEFAULT_MODEL_ID;
32
+ }
33
+ }