Skip to content

Commit 053fb2e

Browse files
iamsudipmanusa
andauthored
feat(pods): add optional tail parameter to pod logs retrieval (#335)
* feat(pods): add tailLines parameter to pod logs retrieval with default 256 lines Signed-off-by: iamsudip <[email protected]> * address review comments Signed-off-by: iamsudip <[email protected]> * test(pods): add tailLines parameter to pod logs retrieval with default 256 lines Signed-off-by: Marc Nuri <[email protected]> --------- Signed-off-by: iamsudip <[email protected]> Signed-off-by: Marc Nuri <[email protected]> Co-authored-by: Marc Nuri <[email protected]>
1 parent 8e666d4 commit 053fb2e

File tree

7 files changed

+96
-6
lines changed

7 files changed

+96
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ The following sets of tools are available (all on by default):
261261
- `name` (`string`) **(required)** - Name of the Pod to get the logs from
262262
- `namespace` (`string`) - Namespace to get the Pod logs from
263263
- `previous` (`boolean`) - Return previous terminated container logs (Optional)
264+
- `tail` (`number`) - Number of lines to retrieve from the end of the logs (Optional, default: 100)
264265

265266
- **pods_run** - Run a Kubernetes Pod in the current or provided namespace with the provided container image and optional name
266267
- `image` (`string`) **(required)** - Container Image to run in the Pod

pkg/kubernetes/pods.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ import (
1717
"k8s.io/client-go/tools/remotecommand"
1818
"k8s.io/metrics/pkg/apis/metrics"
1919
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
20+
"k8s.io/utils/ptr"
2021

2122
"github.com/containers/kubernetes-mcp-server/pkg/version"
2223
)
2324

25+
// Default number of lines to retrieve from the end of the logs
26+
const DefaultTailLines = int64(100)
27+
2428
type PodsTopOptions struct {
2529
metav1.ListOptions
2630
AllNamespaces bool
@@ -92,17 +96,26 @@ func (k *Kubernetes) PodsDelete(ctx context.Context, namespace, name string) (st
9296
k.ResourcesDelete(ctx, &schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, namespace, name)
9397
}
9498

95-
func (k *Kubernetes) PodsLog(ctx context.Context, namespace, name, container string, previous bool) (string, error) {
96-
tailLines := int64(256)
99+
func (k *Kubernetes) PodsLog(ctx context.Context, namespace, name, container string, previous bool, tail int64) (string, error) {
97100
pods, err := k.manager.accessControlClientSet.Pods(k.NamespaceOrDefault(namespace))
98101
if err != nil {
99102
return "", err
100103
}
101-
req := pods.GetLogs(name, &v1.PodLogOptions{
102-
TailLines: &tailLines,
104+
105+
logOptions := &v1.PodLogOptions{
103106
Container: container,
104107
Previous: previous,
105-
})
108+
}
109+
110+
// Only set tailLines if a value is provided (non-zero)
111+
if tail > 0 {
112+
logOptions.TailLines = &tail
113+
} else {
114+
// Default to DefaultTailLines lines when not specified
115+
logOptions.TailLines = ptr.To(DefaultTailLines)
116+
}
117+
118+
req := pods.GetLogs(name, logOptions)
106119
res := req.Do(ctx)
107120
if res.Error() != nil {
108121
return "", res.Error()

pkg/mcp/pods_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,41 @@ func TestPodsLog(t *testing.T) {
756756
return
757757
}
758758
})
759+
760+
// Test with tail parameter
761+
podsTailLines, err := c.callTool("pods_log", map[string]interface{}{
762+
"namespace": "ns-1",
763+
"name": "a-pod-in-ns-1",
764+
"tail": 50,
765+
})
766+
t.Run("pods_log with tail=50 returns pod log", func(t *testing.T) {
767+
if err != nil {
768+
t.Fatalf("call tool failed %v", err)
769+
return
770+
}
771+
if podsTailLines.IsError {
772+
t.Fatalf("call tool failed")
773+
return
774+
}
775+
})
776+
777+
// Test with invalid tail parameter
778+
podsInvalidTailLines, _ := c.callTool("pods_log", map[string]interface{}{
779+
"namespace": "ns-1",
780+
"name": "a-pod-in-ns-1",
781+
"tail": "invalid",
782+
})
783+
t.Run("pods_log with invalid tail returns error", func(t *testing.T) {
784+
if !podsInvalidTailLines.IsError {
785+
t.Fatalf("call tool should fail")
786+
return
787+
}
788+
expectedErrorMsg := "failed to parse tail parameter: expected integer"
789+
if errMsg := podsInvalidTailLines.Content[0].(mcp.TextContent).Text; !strings.Contains(errMsg, expectedErrorMsg) {
790+
t.Fatalf("unexpected error message, expected to contain '%s', got '%s'", expectedErrorMsg, errMsg)
791+
return
792+
}
793+
})
759794
})
760795
}
761796

pkg/mcp/testdata/toolsets-core-tools.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@
202202
"previous": {
203203
"description": "Return previous terminated container logs (Optional)",
204204
"type": "boolean"
205+
},
206+
"tail": {
207+
"default": 100,
208+
"description": "Number of lines to retrieve from the end of the logs (Optional, default: 100)",
209+
"minimum": 0,
210+
"type": "integer"
205211
}
206212
},
207213
"required": [

pkg/mcp/testdata/toolsets-full-tools-openshift.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,12 @@
308308
"previous": {
309309
"description": "Return previous terminated container logs (Optional)",
310310
"type": "boolean"
311+
},
312+
"tail": {
313+
"default": 100,
314+
"description": "Number of lines to retrieve from the end of the logs (Optional, default: 100)",
315+
"minimum": 0,
316+
"type": "integer"
311317
}
312318
},
313319
"required": [

pkg/mcp/testdata/toolsets-full-tools.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,12 @@
308308
"previous": {
309309
"description": "Return previous terminated container logs (Optional)",
310310
"type": "boolean"
311+
},
312+
"tail": {
313+
"default": 100,
314+
"description": "Number of lines to retrieve from the end of the logs (Optional, default: 100)",
315+
"minimum": 0,
316+
"type": "integer"
311317
}
312318
},
313319
"required": [

pkg/toolsets/core/pods.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,12 @@ func initPods() []api.ServerTool {
201201
Type: "string",
202202
Description: "Name of the Pod container to get the logs from (Optional)",
203203
},
204+
"tail": {
205+
Type: "integer",
206+
Description: "Number of lines to retrieve from the end of the logs (Optional, default: 100)",
207+
Default: api.ToRawMessage(kubernetes.DefaultTailLines),
208+
Minimum: ptr.To(float64(0)),
209+
},
204210
"previous": {
205211
Type: "boolean",
206212
Description: "Return previous terminated container logs (Optional)",
@@ -396,7 +402,24 @@ func podsLog(params api.ToolHandlerParams) (*api.ToolCallResult, error) {
396402
if previous != nil {
397403
previousBool = previous.(bool)
398404
}
399-
ret, err := params.PodsLog(params, ns.(string), name.(string), container.(string), previousBool)
405+
// Extract tailLines parameter
406+
tail := params.GetArguments()["tail"]
407+
var tailInt int64
408+
if tail != nil {
409+
// Convert to int64 - safely handle both float64 (JSON number) and int types
410+
switch v := tail.(type) {
411+
case float64:
412+
tailInt = int64(v)
413+
case int:
414+
tailInt = int64(v)
415+
case int64:
416+
tailInt = v
417+
default:
418+
return api.NewToolCallResult("", fmt.Errorf("failed to parse tail parameter: expected integer, got %T", tail)), nil
419+
}
420+
}
421+
422+
ret, err := params.PodsLog(params.Context, ns.(string), name.(string), container.(string), previousBool, tailInt)
400423
if err != nil {
401424
return api.NewToolCallResult("", fmt.Errorf("failed to get pod %s log in namespace %s: %v", name, ns, err)), nil
402425
} else if ret == "" {

0 commit comments

Comments
 (0)