code-engine-mcp-server 1.0.3 → 1.0.6

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/README.md CHANGED
@@ -5,32 +5,40 @@
5
5
  Model Context Protocol (MCP) server for IBM Code Engine and Docker/Podman integration.
6
6
  It enables AI assistants to build, run, push, and deploy containerized workloads with a single MCP server.
7
7
 
8
- [![MCP](https://img.shields.io/badge/MCP-Server-blue)](#)
9
- [![IBM Cloud](https://img.shields.io/badge/IBM%20Cloud-Code%20Engine-1261FE)](#)
8
+ [![MCP](https://img.shields.io/badge/MCP-Server-blue)](https://github.com/markusvankempen/code-engine-mcp-server)
9
+ [![IBM Cloud](https://img.shields.io/badge/IBM%20Cloud-Code%20Engine-1261FE)](https://cloud.ibm.com/codeengine/overview)
10
10
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933?logo=nodedotjs&logoColor=white)](#prerequisites)
11
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
12
12
 
13
- ## 📚 Table of Contents
14
-
15
- - [What You Get](#what-you-get)
16
- - [Quick Start](#quick-start)
17
- - [Deploy Your First App](#deploy-your-first-app)
18
- - [Documentation](#documentation)
19
- - [Project Structure](#project-structure)
20
- - [Features](#features)
21
- - [Configuration](#configuration)
22
- - [npm and MCP Registry](#npm-and-mcp-registry)
23
- - [Example Prompts](#example-prompts)
24
- - [Available Tools](#available-tools)
25
- - [Environment Variables](#environment-variables)
26
- - [Prerequisites](#prerequisites)
27
- - [Development](#development)
28
- - [Troubleshooting](#troubleshooting)
29
- - [Security](#security)
30
- - [License](#license)
31
- - [Contributing](#contributing)
32
- - [Author](#author)
33
- - [Support](#support)
13
+ ## How It Works
14
+
15
+ ```mermaid
16
+ flowchart TD
17
+ A([AI Assistant\nCopilot / Claude / Cline]) -->|MCP JSON-RPC| B[Code Engine MCP Server]
18
+
19
+ B --> C{Tool Category}
20
+
21
+ C -->|Container Tools| D[Docker / Podman]
22
+ C -->|Registry Tools| E[IBM Container Registry\nus.icr.io]
23
+ C -->|Code Engine Tools| F[IBM Code Engine\nREST API]
24
+ C -->|Procedures| G[Multi-step Workflows]
25
+
26
+ D -->|build / push / validate| E
27
+ E -->|image reference| F
28
+
29
+ G -->|proc_build_push_deploy| D
30
+ G -->|proc_build_run_and_deploy| F
31
+
32
+ F --> H[(Projects\nApps\nBuilds\nJobs\nSecrets\nDomains)]
33
+
34
+ H -->|ready| I([Live App\nhttps://app.region.codeengine.appdomain.cloud])
35
+
36
+ style A fill:#1261FE,color:#fff
37
+ style B fill:#0f3460,color:#fff
38
+ style G fill:#7b2d8b,color:#fff
39
+ style I fill:#198038,color:#fff
40
+ ```
41
+
34
42
 
35
43
  ## ✨ What You Get
36
44
 
@@ -55,7 +63,29 @@ node build/index.js
55
63
 
56
64
  Then configure your MCP client using one of the examples in the Configuration section below.
57
65
 
58
- ## Deploy Your First App
66
+ ## 🤖 The Agentic Experience (No Docker/Code Engine Lingo Needed)
67
+
68
+ The true power of this MCP server is that **you don't need to know Docker or Code Engine commands**. You can simply state your goal, and the LLM will discover the environment, run the pipeline, and fix issues autonomously.
69
+
70
+ Here is an example interaction from a real session:
71
+
72
+ **User:**
73
+ > "I have an app in the `developer-splash` folder. Deploy it to my Code Engine project."
74
+
75
+ **Assistant's Autonomous Thought Process:**
76
+ 1. *Discovery:* "Let me check what projects and namespaces exist... Found project `markus-app-v2-toronto` and namespace `mvk-code-engine`."
77
+ 2. *Prerequisites:* "No pull secret in this project yet. I'll get the API key from the `.env` file and create the secret `icr-pull-secret`."
78
+ 3. *Execution:* "Now I'll run the full pipeline: build → push → deploy → wait."
79
+ 4. *Validation:* "It's deployed but stuck at 'deploying'. Let me check the logs... The container is running but the readiness probe is failing."
80
+ 5. *Self-Correction:* "Ah, the `sed` pattern in the Dockerfile didn't match nginx alpine's default config format for port 80. I'll fix the Dockerfile with a regex-safe `sed` and rebuild."
81
+ 6. *Completion:* "App is live! 🎉 Status: ready."
82
+
83
+ **Assistant Response to User:**
84
+ > "Your app is deployed and live at: `https://developer-splash.29m5mrru3s3n.ca-tor.codeengine.appdomain.cloud`. I had to fix a minor port configuration issue in your Dockerfile, but it is successfully running now!"
85
+
86
+ With this MCP server, the AI acts as an expert DevOps engineer pairing with you.
87
+
88
+ ## Deploy Your First App
59
89
 
60
90
  This walks through deploying the included [Star Wars splash page example](./examples/starwars-splash/) — a static nginx container — entirely through the MCP server.
61
91
 
@@ -301,14 +331,216 @@ push it, then deploy it to Code Engine project <project-id> with pull secret icr
301
331
  Tell me the public URL and confirm the instance is running.
302
332
  ```
303
333
 
304
- ## �📖 Documentation
334
+ ---
335
+
336
+ ## 🌐 Host Any MCP Server on Code Engine
337
+
338
+ You can use **this** MCP server to deploy **another** MCP server to Code Engine — no CLI, no Dockerfile, no YAML. The key ingredient is [`supergateway`](https://github.com/supercorp-ai/supergateway): a tiny bridge that wraps any STDIO-based MCP server as an HTTP + SSE endpoint, making it accessible to any remote client.
339
+
340
+ > Credit: [Jeremias Werner & Enrico Regge — IBM Cloud Code Engine](https://community.ibm.com/community/user/blogs/jeremias-werner/2025/04/30/code-engine-mcp-server)
341
+
342
+ ```
343
+ Your AI Assistant
344
+ │ MCP JSON-RPC (STDIO, local)
345
+
346
+ code-engine-mcp-server ──► ce_create_application
347
+
348
+
349
+ Code Engine App
350
+ image: docker.io/supercorp/supergateway
351
+ args: --stdio "npx -y <any-mcp-server>"
352
+ --outputTransport sse
353
+ │ HTTPS + SSE (public URL)
354
+
355
+ Any remote MCP client
356
+ (Claude Desktop, Cursor, VS Code, …)
357
+ ```
358
+
359
+ Any STDIO MCP server becomes a remotely accessible, auto-scaling cloud service — with no custom infrastructure.
360
+
361
+ This example deploys [`@tokenizin/mcp-npx-fetch`](https://www.npmjs.com/package/@tokenizin/mcp-npx-fetch), an MCP server that lets an AI assistant fetch content from public URLs.
362
+
363
+ The example files live in [examples/mcp-server-supergateway/](./examples/mcp-server-supergateway/).
364
+
365
+ ### Step 1 — Deploy the hosted MCP server
366
+
367
+ Ask your assistant:
368
+ ```
369
+ Deploy a hosted MCP fetch server to my Code Engine project <project-id>.
370
+ Use image docker.io/supercorp/supergateway on port 8000.
371
+ Startup args: --stdio "npx -y @tokenizin/mcp-npx-fetch" --outputTransport sse
372
+ Name it "mcp-fetch-server". No pull secret needed.
373
+ ```
374
+
375
+ This calls `ce_create_application`:
376
+ ```json
377
+ {
378
+ "project_id": "<your-project-id>",
379
+ "name": "mcp-fetch-server",
380
+ "image": "docker.io/supercorp/supergateway",
381
+ "port": 8000,
382
+ "run_args": ["--stdio", "npx -y @tokenizin/mcp-npx-fetch", "--outputTransport", "sse"]
383
+ }
384
+ ```
385
+
386
+ **MCP response — `ce_create_application`:**
387
+ ```json
388
+ {
389
+ "name": "mcp-fetch-server",
390
+ "resource_type": "app_v2",
391
+ "status": "deploying",
392
+ "image_reference": "docker.io/supercorp/supergateway",
393
+ "image_port": 8000,
394
+ "scale_min_instances": 0,
395
+ "scale_max_instances": 10,
396
+ "endpoint": "https://mcp-fetch-server.<subdomain>.<region>.codeengine.appdomain.cloud",
397
+ "status_details": {
398
+ "latest_created_revision": "mcp-fetch-server-00001",
399
+ "latest_ready_revision": null
400
+ }
401
+ }
402
+ ```
403
+
404
+ > No pull secret is needed — `docker.io/supercorp/supergateway` is a public image. Code Engine scales to zero when idle; you pay only for actual requests.
405
+
406
+ ### Step 2 — Wait for the app to be ready
407
+
408
+ Ask your assistant:
409
+ ```
410
+ Wait for mcp-fetch-server in project <project-id> to be ready
411
+ ```
412
+
413
+ This calls `ce_wait_for_app_ready`:
414
+ ```json
415
+ {
416
+ "project_id": "<your-project-id>",
417
+ "app_name": "mcp-fetch-server",
418
+ "timeout_seconds": 120
419
+ }
420
+ ```
421
+
422
+ **MCP response — `ce_wait_for_app_ready`:**
423
+ ```json
424
+ {
425
+ "app_name": "mcp-fetch-server",
426
+ "status": "ready",
427
+ "endpoint": "https://mcp-fetch-server.<subdomain>.<region>.codeengine.appdomain.cloud",
428
+ "elapsed_seconds": 34,
429
+ "poll_history": [
430
+ { "attempt": 1, "status": "deploying", "elapsed_seconds": 10 },
431
+ { "attempt": 2, "status": "deploying", "elapsed_seconds": 20 },
432
+ { "attempt": 3, "status": "ready", "elapsed_seconds": 34 }
433
+ ]
434
+ }
435
+ ```
436
+
437
+ ### Step 3 — Verify the running instance
438
+
439
+ Ask your assistant:
440
+ ```
441
+ List the running instances of mcp-fetch-server in project <project-id>
442
+ ```
443
+
444
+ This calls `ce_list_app_instances`:
445
+
446
+ **MCP response — `ce_list_app_instances`:**
447
+ ```json
448
+ {
449
+ "instances": [
450
+ {
451
+ "name": "mcp-fetch-server-00001-deployment-abc123",
452
+ "revision": "mcp-fetch-server-00001",
453
+ "status": "running",
454
+ "restart_count": 0,
455
+ "started_at": "2026-05-09T12:01:44Z"
456
+ }
457
+ ]
458
+ }
459
+ ```
460
+
461
+ ### Step 4 — Connect your MCP client
305
462
 
463
+ Use [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) to bridge the HTTP+SSE endpoint back to STDIO for local clients.
464
+
465
+ **VS Code `mcp.json`:**
466
+ ```json
467
+ {
468
+ "servers": {
469
+ "fetch": {
470
+ "command": "npx",
471
+ "args": [
472
+ "mcp-remote",
473
+ "https://mcp-fetch-server.<subdomain>.<region>.codeengine.appdomain.cloud/sse"
474
+ ]
475
+ }
476
+ }
477
+ }
478
+ ```
479
+
480
+ **Claude Desktop `claude_desktop_config.json`:**
481
+ ```json
482
+ {
483
+ "mcpServers": {
484
+ "fetch": {
485
+ "command": "npx",
486
+ "args": [
487
+ "mcp-remote",
488
+ "https://mcp-fetch-server.<subdomain>.<region>.codeengine.appdomain.cloud/sse"
489
+ ]
490
+ }
491
+ }
492
+ }
493
+ ```
494
+
495
+ ### Step 5 — Test the endpoint
496
+
497
+ Verify the server is live and streaming:
498
+ ```bash
499
+ curl -N https://mcp-fetch-server.<subdomain>.<region>.codeengine.appdomain.cloud/sse
500
+ ```
501
+
502
+ Or open it in the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
503
+ ```bash
504
+ npx @modelcontextprotocol/inspector
505
+ # Connect via SSE → paste the Code Engine URL
506
+ ```
507
+
508
+ Once connected, you will see the `fetch` tool listed and can invoke it directly from the inspector.
509
+
510
+ ### Full one-shot prompt
511
+
512
+ ```
513
+ Deploy a hosted MCP fetch server to my Code Engine project <project-id>.
514
+ Use image docker.io/supercorp/supergateway on port 8000 with no pull secret.
515
+ run_args: --stdio "npx -y @tokenizin/mcp-npx-fetch" --outputTransport sse
516
+ Name it "mcp-fetch-server", wait for it to be ready, and give me the /sse URL
517
+ so I can add it to my mcp.json.
518
+ ```
519
+
520
+ See [examples/mcp-server-supergateway/](./examples/mcp-server-supergateway/) for the ready-to-use client config file.
521
+
522
+ ### Deploy any other STDIO MCP server
523
+
524
+ The same pattern works for any `npx`-runnable MCP server — just swap the `--stdio` argument:
525
+
526
+ | MCP Server | `--stdio` argument |
527
+ |---|---|
528
+ | Fetch | `npx -y @tokenizin/mcp-npx-fetch` |
529
+ | Filesystem | `npx -y @modelcontextprotocol/server-filesystem /data` |
530
+ | Brave Search | `npx -y @modelcontextprotocol/server-brave-search` |
531
+ | Your own server | `node /app/server.js` |
532
+
533
+ ---
534
+
535
+ ## Documentation
536
+
537
+ - [Setup Instructions](./docs/SETUP_INSTRUCTIONS.md)
538
+ - [MCP Inspector Troubleshooting](./docs/MCP_INSPECTOR_TROUBLESHOOTING.md)
306
539
  - [Code Engine API Reference](./docs/CODE_ENGINE_API_REFERENCE.md)
307
540
  - [API Call Scenarios](./docs/API_CALL_SCENARIOS.md)
308
- - [Setup Instructions](./docs/SETUP_INSTRUCTIONS.md)
309
541
  - [Client README](./docs/CLIENT_README.md)
310
542
  - [Cline MCP Config Example](./docs/CLINE_CONFIG_EXAMPLE.json)
311
- - [VS Code MCP extension](./vscode-extension/README.md) (registers this server via settings; no `mcp.json` required for the default `npx` flow)
543
+ - [VS Code MCP extension](./vscode-extension/README.md)
312
544
  - [Code of Conduct](./docs/CODE_OF_CONDUCT.md)
313
545
  - [Contributing Guide](./docs/CONTRIBUTING.md)
314
546
  - [Maintainers](./docs/MAINTAINERS.md)
@@ -317,20 +549,27 @@ Tell me the public URL and confirm the instance is running.
317
549
 
318
550
  ```text
319
551
  code-engine-mcp-server/
320
- ├── api/
321
- │ └── code-engine-openapi.yaml # OpenAPI reference used for API coverage
552
+ ├── api/ # OpenAPI reference used for API coverage
322
553
  ├── build/ # Compiled JavaScript output
323
- ├── docs/ # API references and setup guides
324
- ├── internal/ # Internal release/publishing notes
554
+ ├── docs/ # API references, client guides, community files
555
+ ├── API_CALL_SCENARIOS.md
556
+ │ ├── CODE_ENGINE_API_REFERENCE.md
557
+ │ ├── MCP_INSPECTOR_TROUBLESHOOTING.md
558
+ │ ├── SETUP_INSTRUCTIONS.md
559
+ │ ├── CODE_OF_CONDUCT.md
560
+ │ ├── CONTRIBUTING.md
561
+ │ └── MAINTAINERS.md
562
+ ├── examples/
563
+ │ ├── developer-splash/ # nginx static container example
564
+ │ ├── starwars-splash/ # nginx Star Wars crawl example
565
+ │ └── mcp-server-supergateway/ # Host any MCP server on Code Engine via supergateway
566
+ ├── internal/ # Internal release notes
325
567
  ├── src/ # Main TypeScript source code
326
568
  ├── CHANGELOG.md # Release history
327
- ├── CODE_OF_CONDUCT.md # Community guidelines
328
- ├── CONTRIBUTING.md # Contribution workflow
329
569
  ├── LICENSE # Project license
330
- ├── MAINTAINERS.md # Maintainer list
331
570
  ├── README.md # Project overview and usage
332
571
  ├── mcp.example.json # Example MCP client configuration
333
- ├── vscode-extension/ # Optional VS Code extension (MCP definition provider)
572
+ ├── vscode-extension/ # Optional VS Code extension
334
573
  ├── package.json # npm package metadata and scripts
335
574
  ├── server.json # MCP Registry metadata
336
575
  └── tsconfig.json # TypeScript configuration
@@ -468,7 +707,7 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
468
707
  }
469
708
  ```
470
709
 
471
- ## npm and MCP Registry
710
+ ## One-Click Install
472
711
 
473
712
  Use the published package from npm or browse the MCP Registry listing:
474
713
 
@@ -579,8 +818,8 @@ Tell me what CNAME value to set in DNS.
579
818
  |------|-------------|----------------|
580
819
  | `ce_list_applications` | List applications in a project | `project_id` |
581
820
  | `ce_get_application` | Get application details and public URL | `project_id`, `app_name` |
582
- | `ce_create_application` | Deploy a new application | `project_id`, `name`, `image`, `image_secret`, `port`, `env_vars` |
583
- | `ce_update_application` | Update image, scaling, env, pull secret | `project_id`, `app_name`, `image`, `image_secret`, `scale_*` |
821
+ | `ce_create_application` | Deploy a new application | `project_id`, `name`, `image`, `image_secret`, `port`, `env_vars`, `run_args`, `run_commands` |
822
+ | `ce_update_application` | Update image, scaling, env, pull secret, run args | `project_id`, `app_name`, `image`, `image_secret`, `scale_*`, `run_args`, `run_commands` |
584
823
  | `ce_delete_application` | Delete an application | `project_id`, `app_name` |
585
824
  | `ce_list_app_instances` | List all running instances with status | `project_id`, `app_name` |
586
825
  | `ce_get_app_instance` | Get status details for a specific instance | `project_id`, `app_name`, `instance_name` |
package/build/index.js CHANGED
@@ -86,7 +86,7 @@ async function resolveProjectId(nameOrId, token) {
86
86
  // Create MCP server
87
87
  const server = new Server({
88
88
  name: 'code-engine-mcp-server',
89
- version: '1.0.3',
89
+ version: '1.0.6',
90
90
  }, {
91
91
  capabilities: {
92
92
  tools: {},
@@ -322,6 +322,8 @@ const codeEngineTools = [
322
322
  scale_cpu_limit: { type: 'string', description: 'CPU limit (e.g. 1, 0.5)' },
323
323
  scale_memory_limit: { type: 'string', description: 'Memory limit (e.g. 4G, 2G)' },
324
324
  env_vars: { type: 'object', description: 'Key/value environment variables' },
325
+ run_args: { type: 'array', items: { type: 'string' }, description: 'Arguments passed to the container entrypoint (run_arguments). Required for supergateway: ["--stdio", "npx -y <mcp-server>", "--outputTransport", "sse"]' },
326
+ run_commands: { type: 'array', items: { type: 'string' }, description: 'Override the container entrypoint (run_commands). Rarely needed — use run_args for passing flags.' },
325
327
  },
326
328
  required: ['project_id', 'name', 'image'],
327
329
  },
@@ -340,6 +342,8 @@ const codeEngineTools = [
340
342
  scale_max_instances: { type: 'number' },
341
343
  scale_cpu_limit: { type: 'string' },
342
344
  scale_memory_limit: { type: 'string' },
345
+ run_args: { type: 'array', items: { type: 'string' }, description: 'Arguments passed to the container entrypoint (run_arguments)' },
346
+ run_commands: { type: 'array', items: { type: 'string' }, description: 'Override the container entrypoint (run_commands)' },
343
347
  },
344
348
  required: ['project_id', 'app_name'],
345
349
  },
@@ -383,15 +387,16 @@ const codeEngineTools = [
383
387
  },
384
388
  {
385
389
  name: 'ce_get_app_logs',
386
- description: 'Get logs for a specific Code Engine application instance',
390
+ description: 'Get logs for a Code Engine application. Retrieves logs from all running pods (or a specific instance) via the Code Engine Kubernetes API proxy.',
387
391
  inputSchema: {
388
392
  type: 'object',
389
393
  properties: {
390
- project_id: { type: 'string' },
391
- app_name: { type: 'string' },
392
- instance_name: { type: 'string', description: 'Instance name (e.g. my-app-00001-deployment-abcde)' },
394
+ project_id: { type: 'string', description: 'Project ID or name' },
395
+ app_name: { type: 'string', description: 'Application name' },
396
+ instance_name: { type: 'string', description: 'Optional: specific pod/instance name to filter to (e.g. my-app-00001-deployment-abcde). If omitted, logs from all pods are returned.' },
397
+ tail_lines: { type: 'number', description: 'Number of log lines to return per pod (default: 100)' },
393
398
  },
394
- required: ['project_id', 'app_name', 'instance_name'],
399
+ required: ['project_id', 'app_name'],
395
400
  },
396
401
  },
397
402
  // --- Builds ---
@@ -1259,6 +1264,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1259
1264
  type: 'literal', name, value,
1260
1265
  }));
1261
1266
  }
1267
+ if (args.run_args)
1268
+ body.run_arguments = args.run_args;
1269
+ if (args.run_commands)
1270
+ body.run_commands = args.run_commands;
1262
1271
  const response = await axios.post(`${base}/apps`, body, { headers });
1263
1272
  return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] };
1264
1273
  }
@@ -1283,6 +1292,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1283
1292
  patch.scale_cpu_limit = args.scale_cpu_limit;
1284
1293
  if (args.scale_memory_limit)
1285
1294
  patch.scale_memory_limit = args.scale_memory_limit;
1295
+ if (args.run_args)
1296
+ patch.run_arguments = args.run_args;
1297
+ if (args.run_commands)
1298
+ patch.run_commands = args.run_commands;
1286
1299
  const response = await axios.patch(`${base}/apps/${args.app_name}`, patch, {
1287
1300
  headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/merge-patch+json', 'If-Match': entityTag },
1288
1301
  });
@@ -1313,22 +1326,46 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1313
1326
  }
1314
1327
  case 'ce_get_app_logs': {
1315
1328
  const token = await getIAMToken(getApiKey());
1316
- const { base, headers } = await ceApi(args.project_id, token);
1317
- try {
1318
- const response = await axios.get(`${base}/apps/${args.app_name}/instances/${args.instance_name}/logs`, { headers });
1319
- return { content: [{ type: 'text', text: JSON.stringify(response.data, null, 2) }] };
1329
+ const projectId = await resolveProjectId(args.project_id, token);
1330
+ const region = await getProjectRegion(projectId, token);
1331
+ const ceHeaders = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' };
1332
+ // Get app details to extract namespace/subdomain from the endpoint URL
1333
+ const appRes = await axios.get(`https://api.${region}.codeengine.cloud.ibm.com/v2/projects/${projectId}/apps/${args.app_name}`, { headers: ceHeaders });
1334
+ // Endpoint pattern: https://{app}.{subdomain}.{region}.codeengine.appdomain.cloud
1335
+ const endpoint = appRes.data.endpoint || '';
1336
+ const subdomainMatch = endpoint.match(/https?:\/\/[^.]+\.([^.]+)\.[^.]+\.codeengine/);
1337
+ if (!subdomainMatch) {
1338
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Could not determine project namespace from app endpoint', endpoint }, null, 2) }] };
1320
1339
  }
1321
- catch (err) {
1322
- if (err.response?.status === 403) {
1323
- return { content: [{ type: 'text', text: JSON.stringify({
1324
- note: 'App instance logs are not accessible via the Code Engine REST API v2. ' +
1325
- 'Configure IBM Log Analysis (IBM Cloud Logging) for your project to retrieve logs via the IBM Cloud Logs API.',
1326
- docs: 'https://cloud.ibm.com/docs/codeengine?topic=codeengine-view-logs',
1327
- status: 403,
1328
- }, null, 2) }] };
1340
+ const namespace = subdomainMatch[1];
1341
+ const proxyBase = `https://proxy.${region}.codeengine.cloud.ibm.com`;
1342
+ const tailLines = args.tail_lines ?? 100;
1343
+ const kubeHeaders = { Authorization: `Bearer ${token}` };
1344
+ // List pods for the app via Kubernetes API proxy
1345
+ const labelSelector = encodeURIComponent(`serving.knative.dev/service=${args.app_name}`);
1346
+ const podsRes = await axios.get(`${proxyBase}/api/v1/namespaces/${namespace}/pods?labelSelector=${labelSelector}`, { headers: kubeHeaders });
1347
+ const pods = podsRes.data.items || [];
1348
+ if (pods.length === 0) {
1349
+ return { content: [{ type: 'text', text: JSON.stringify({ message: `No pods found for app '${args.app_name}'`, namespace, app: args.app_name }, null, 2) }] };
1350
+ }
1351
+ // Filter to a specific instance if requested
1352
+ const targetPods = args.instance_name
1353
+ ? pods.filter((p) => p.metadata.name === args.instance_name || p.metadata.name.startsWith(String(args.instance_name)))
1354
+ : pods;
1355
+ // Fetch logs for each pod
1356
+ const results = [];
1357
+ for (const pod of targetPods) {
1358
+ const podName = pod.metadata.name;
1359
+ const podPhase = pod.status?.phase ?? 'Unknown';
1360
+ try {
1361
+ const logRes = await axios.get(`${proxyBase}/api/v1/namespaces/${namespace}/pods/${podName}/log?container=user-container&tailLines=${tailLines}`, { headers: kubeHeaders });
1362
+ results.push({ pod: podName, status: podPhase, logs: logRes.data });
1363
+ }
1364
+ catch (logErr) {
1365
+ results.push({ pod: podName, status: podPhase, error: logErr.response?.data?.message ?? logErr.message });
1329
1366
  }
1330
- throw err;
1331
1367
  }
1368
+ return { content: [{ type: 'text', text: JSON.stringify({ app: args.app_name, namespace, region, pods_found: pods.length, results }, null, 2) }] };
1332
1369
  }
1333
1370
  case 'ce_list_builds': {
1334
1371
  const token = await getIAMToken(getApiKey());