Skip to content

Commit 5910f30

Browse files
authored
Merge pull request #40 from ayakut16/fix/show-logs
Add --server-logs flag
2 parents e0737ed + 611c2b6 commit 5910f30

File tree

9 files changed

+106
-25
lines changed

9 files changed

+106
-25
lines changed

README.md

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,16 @@ mcp tools npx -y @modelcontextprotocol/server-filesystem ~
166166
The default format now displays tools in a colorized man-page style:
167167

168168
```
169-
read_file(path:str, [limit:int], [offset:int])
170-
Reads a file from the filesystem
171-
169+
read_file(path:str)
170+
Read the complete contents of a file from the file system.
171+
read_multiple_files(paths:str[])
172+
Read the contents of multiple files simultaneously.
172173
list_dir(path:str)
173-
Lists directory contents
174-
174+
Lists the contents of a directory.
175+
write_file(path:str, content:str)
176+
Writes content to a file.
175177
grep_search(pattern:str, [excludePatterns:str[]])
176-
Search files with pattern
177-
178+
Search files with pattern.
178179
edit_file(edits:{newText:str,oldText:str}[], path:str)
179180
Edit a file with multiple text replacements
180181
```
@@ -236,7 +237,7 @@ mcp call read_file --params '{"path":"/path/to/file"}' npx -y @modelcontextproto
236237
mcp call resource:test://static/resource/1 npx -y @modelcontextprotocol/server-everything -f json | jq ".contents[0].text"
237238
```
238239

239-
or
240+
or
240241

241242
```bash
242243
mcp read-resource test://static/resource/1 npx -y @modelcontextprotocol/server-everything -f json | jq ".contents[0].text"
@@ -248,6 +249,28 @@ mcp read-resource test://static/resource/1 npx -y @modelcontextprotocol/server-e
248249
mcp get-prompt simple_prompt npx -y @modelcontextprotocol/server-everything -f json | jq ".messages[0].content.text"
249250
```
250251

252+
#### Viewing Server Logs
253+
254+
When using client commands that make calls to the server, you can add the `--server-logs` flag to see the server logs related to your request:
255+
256+
```bash
257+
# View server logs when listing tools
258+
mcp tools --server-logs npx -y @modelcontextprotocol/server-filesystem ~
259+
```
260+
261+
Output:
262+
```
263+
[>] Secure MCP Filesystem Server running on stdio
264+
[>] Allowed directories: [ '/Users/fka/' ]
265+
read_file(path:str)
266+
Read the complete contents of a file from the file system.
267+
read_multiple_files(paths:str[])
268+
Read the contents of multiple files simultaneously.
269+
... and the other tools available on this server
270+
```
271+
272+
This can be helpful for debugging or understanding what's happening on the server side when executing these commands.
273+
251274
### Interactive Shell
252275

253276
The interactive shell mode allows you to run multiple MCP commands in a single session:
@@ -330,7 +353,7 @@ After scaffolding, you can build and run your MCP server:
330353
npm install
331354

332355
# Build the TypeScript code
333-
npm run build
356+
npm run build
334357

335358
# Test the server with MCP Tools
336359
mcp tools node build/index.js

cmd/mcptools/commands/get_prompt.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ func GetPromptCmd() *cobra.Command {
4545
case (cmdArgs[i] == FlagParams || cmdArgs[i] == FlagParamsShort) && i+1 < len(cmdArgs):
4646
ParamsString = cmdArgs[i+1]
4747
i += 2
48+
case cmdArgs[i] == FlagServerLogs:
49+
ShowServerLogs = true
50+
i++
4851
case !promptExtracted:
4952
promptName = cmdArgs[i]
5053
promptExtracted = true

cmd/mcptools/commands/read_resource.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func ReadResourceCmd() *cobra.Command {
2424
fmt.Fprintln(os.Stderr, "Error: resource name is required")
2525
fmt.Fprintln(
2626
os.Stderr,
27-
"Example: mcp read-resource npx -y @modelcontextprotocol/server-filesystem ~",
27+
"Example: mcp read-resource test://static/resource/1 npx -y @modelcontextprotocol/server-filesystem ~",
2828
)
2929
os.Exit(1)
3030
}
@@ -58,7 +58,7 @@ func ReadResourceCmd() *cobra.Command {
5858
fmt.Fprintln(os.Stderr, "Error: resource name is required")
5959
fmt.Fprintln(
6060
os.Stderr,
61-
"Example: mcp read-resource npx -y @modelcontextprotocol/server-filesystem ~",
61+
"Example: mcp read-resource test://static/resource/1 npx -y @modelcontextprotocol/server-filesystem ~",
6262
)
6363
os.Exit(1)
6464
}

cmd/mcptools/commands/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const (
1515
FlagParamsShort = "-p"
1616
FlagHelp = "--help"
1717
FlagHelpShort = "-h"
18+
FlagServerLogs = "--server-logs"
1819
)
1920

2021
// entity types.
@@ -31,6 +32,8 @@ var (
3132
FormatOption = "table"
3233
// ParamsString is the params for the command.
3334
ParamsString string
35+
// ShowServerLogs is a flag to show server logs.
36+
ShowServerLogs bool
3437
)
3538

3639
// RootCmd creates the root command.

cmd/mcptools/commands/shell.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ func ShellCmd() *cobra.Command { //nolint:gocyclo
3030
parsedArgs := []string{}
3131

3232
for i := 0; i < len(cmdArgs); i++ {
33-
if (cmdArgs[i] == FlagFormat || cmdArgs[i] == FlagFormatShort) && i+1 < len(cmdArgs) {
33+
switch {
34+
case (cmdArgs[i] == FlagFormat || cmdArgs[i] == FlagFormatShort) && i+1 < len(cmdArgs):
3435
FormatOption = cmdArgs[i+1]
3536
i++
36-
} else {
37+
case cmdArgs[i] == FlagServerLogs:
38+
ShowServerLogs = true
39+
i++
40+
default:
3741
parsedArgs = append(parsedArgs, cmdArgs[i])
3842
}
3943
}

cmd/mcptools/commands/utils.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ var CreateClientFunc = func(args []string, opts ...client.Option) (*client.Clien
2727
return nil, ErrCommandRequired
2828
}
2929

30+
opts = append(opts, client.SetShowServerLogs(ShowServerLogs))
31+
3032
// Check if the first argument is an alias
3133
if len(args) == 1 {
3234
server, found := alias.GetServerCommand(args[0])
@@ -64,6 +66,9 @@ func ProcessFlags(args []string) []string {
6466
case (args[i] == FlagFormat || args[i] == FlagFormatShort) && i+1 < len(args):
6567
FormatOption = args[i+1]
6668
i += 2
69+
case args[i] == FlagServerLogs:
70+
ShowServerLogs = true
71+
i++
6772
default:
6873
parsedArgs = append(parsedArgs, args[i])
6974
i++

cmd/mcptools/commands/utils_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,42 +21,56 @@ func TestProcessFlags(t *testing.T) {
2121
args []string
2222
wantArgs []string
2323
wantFormat string
24+
showLogs bool
2425
}{
2526
{
2627
name: "no flags",
2728
args: []string{"cmd", "arg1", "arg2"},
2829
wantArgs: []string{"cmd", "arg1", "arg2"},
2930
wantFormat: "",
31+
showLogs: false,
3032
},
3133
{
3234
name: "with long format flag",
3335
args: []string{"cmd", "--format", "json", "arg1"},
3436
wantArgs: []string{"cmd", "arg1"},
3537
wantFormat: "json",
38+
showLogs: false,
3639
},
3740
{
3841
name: "with short format flag",
3942
args: []string{"cmd", "-f", "pretty", "arg1"},
4043
wantArgs: []string{"cmd", "arg1"},
4144
wantFormat: "pretty",
45+
showLogs: false,
4246
},
4347
{
4448
name: "with format flag at end",
4549
args: []string{"cmd", "arg1", "--format", "table"},
4650
wantArgs: []string{"cmd", "arg1"},
4751
wantFormat: "table",
52+
showLogs: false,
4853
},
4954
{
5055
name: "with invalid format option",
5156
args: []string{"cmd", "--format", "invalid", "arg1"},
5257
wantArgs: []string{"cmd", "arg1"},
5358
wantFormat: "invalid",
59+
showLogs: false,
5460
},
5561
{
5662
name: "with format flag without value",
5763
args: []string{"cmd", "--format", "json"},
5864
wantArgs: []string{"cmd"},
5965
wantFormat: "json",
66+
showLogs: false,
67+
},
68+
{
69+
name: "with server logs flag",
70+
args: []string{"cmd", "--server-logs", "arg1"},
71+
wantArgs: []string{"cmd", "arg1"},
72+
wantFormat: "",
73+
showLogs: true,
6074
},
6175
}
6276

pkg/client/client.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ func CloseTransportAfterExecute(closeTransport bool) Option {
3333
}
3434
}
3535

36+
// SetShowServerLogs sets whether to show server logs.
37+
func SetShowServerLogs(showLogs bool) Option {
38+
return func(c *Client) {
39+
t, ok := c.transport.(interface{ SetShowServerLogs(bool) })
40+
if ok {
41+
t.SetShowServerLogs(showLogs)
42+
}
43+
}
44+
}
45+
3646
// NewWithTransport creates a new MCP client using the provided transport.
3747
// This allows callers to provide a custom transport implementation.
3848
func NewWithTransport(t transport.Transport) *Client {

pkg/transport/stdio.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@ import (
88
"io"
99
"os"
1010
"os/exec"
11+
"strings"
1112
"time"
1213
)
1314

1415
// Stdio implements the Transport interface by executing a command
1516
// and communicating with it via stdin/stdout using JSON-RPC.
1617
type Stdio struct {
17-
process *stdioProcess
18-
command []string
19-
nextID int
20-
debug bool
18+
process *stdioProcess
19+
command []string
20+
nextID int
21+
debug bool
22+
showServerLogs bool
2123
}
2224

2325
// stdioProcess reflects the state of a running command.
@@ -50,6 +52,11 @@ func (t *Stdio) SetCloseAfterExecute(v bool) {
5052
}
5153
}
5254

55+
// SetShowServerLogs toggles whether to print server logs.
56+
func (t *Stdio) SetShowServerLogs(v bool) {
57+
t.showServerLogs = v
58+
}
59+
5360
// Execute implements the Transport interface by spawning a subprocess
5461
// and communicating with it via JSON-RPC over stdin/stdout.
5562
func (t *Stdio) Execute(method string, params any) (map[string]any, error) {
@@ -72,14 +79,13 @@ func (t *Stdio) Execute(method string, params any) (map[string]any, error) {
7279

7380
if !process.isInitializeSent {
7481
if initErr := t.initialize(process.stdin, process.stdout); initErr != nil {
82+
t.printStderr(process)
7583
if t.debug {
7684
fmt.Fprintf(os.Stderr, "DEBUG: Initialization failed: %v\n", initErr)
77-
if process.stderrBuf.Len() > 0 {
78-
fmt.Fprintf(os.Stderr, "DEBUG: stderr during init: %s\n", process.stderrBuf.String())
79-
}
8085
}
8186
return nil, initErr
8287
}
88+
t.printStderr(process)
8389
process.isInitializeSent = true
8490
}
8591

@@ -100,10 +106,10 @@ func (t *Stdio) Execute(method string, params any) (map[string]any, error) {
100106
}
101107

102108
response, err := t.readResponse(process.stdout)
109+
t.printStderr(process)
103110
if err != nil {
104111
return nil, err
105112
}
106-
107113
err = t.closeProcess(process)
108114
if err != nil {
109115
return nil, err
@@ -112,6 +118,22 @@ func (t *Stdio) Execute(method string, params any) (map[string]any, error) {
112118
return response.Result, nil
113119
}
114120

121+
// printStderr prints and clears any accumulated stderr output.
122+
func (t *Stdio) printStderr(process *stdioProcess) {
123+
if !t.showServerLogs {
124+
return
125+
}
126+
if process.stderrBuf.Len() > 0 {
127+
for _, line := range strings.SplitAfter(process.stderrBuf.String(), "\n") {
128+
line = strings.TrimSuffix(line, "\n")
129+
if line != "" {
130+
fmt.Fprintf(os.Stderr, "[>] %s\n", line)
131+
}
132+
}
133+
process.stderrBuf.Reset() // Clear the buffer after reading
134+
}
135+
}
136+
115137
// closeProcess waits for the command to finish, returning any error.
116138
func (t *Stdio) closeProcess(process *stdioProcess) error {
117139
if t.process != nil {
@@ -130,13 +152,10 @@ func (t *Stdio) closeProcess(process *stdioProcess) error {
130152
case waitErr := <-done:
131153
if t.debug {
132154
fmt.Fprintf(os.Stderr, "DEBUG: Command completed with err: %v\n", waitErr)
133-
if process.stderrBuf.Len() > 0 {
134-
fmt.Fprintf(os.Stderr, "DEBUG: stderr output:\n%s\n", process.stderrBuf.String())
135-
}
136155
}
137156

138157
if waitErr != nil && process.stderrBuf.Len() > 0 {
139-
return fmt.Errorf("command error: %w, stderr: %s", waitErr, process.stderrBuf.String())
158+
return fmt.Errorf("command error: %w", waitErr)
140159
}
141160
case <-time.After(1 * time.Second):
142161
if t.debug {

0 commit comments

Comments
 (0)