Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ mcp tools npx -y @modelcontextprotocol/server-filesystem ~
Uses HTTP and Server-Sent Events (SSE) to communicate with an MCP server via JSON-RPC 2.0. This is useful for connecting to remote servers that implement the MCP protocol.

```bash
mcp tools http://127.0.0.1:3001
mcp tools http://localhost:3001/sse

# Example: Use the everything sample server
# docker run -p 3001:3001 --rm -it tzolov/mcp-everything-server:v1
Expand Down Expand Up @@ -340,7 +340,7 @@ mcp web https://ne.tools
The web interface includes:

- A sidebar listing all available tools, resources, and prompts
- Form-based and JSON-based parameter editing
- Form-based and JSON-based parameter editing
- Formatted and raw JSON response views
- Interactive parameter forms automatically generated from tool schemas
- Support for complex parameter types (arrays, objects, nested structures)
Expand Down Expand Up @@ -626,7 +626,7 @@ mcp proxy tool count_lines "Counts lines in a file" "file:string" -e "wc -l < \"
The guard mode allows you to restrict access to specific tools, prompts, and resources based on pattern matching. This is useful for security purposes when:

- Restricting potentially dangerous operations (file writes, deletions, etc.)
- Limiting the capabilities of AI assistants or applications
- Limiting the capabilities of AI assistants or applications
- Providing read-only access to sensitive systems
- Creating sandboxed environments for testing or demonstrations

Expand Down
33 changes: 30 additions & 3 deletions cmd/mcptools/commands/call.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package commands

import (
"context"
"encoding/json"
"fmt"
"os"
"strings"

"github.com/mark3labs/mcp-go/mcp"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -104,11 +106,36 @@ func CallCmd() *cobra.Command {

switch entityType {
case EntityTypeTool:
resp, execErr = mcpClient.CallTool(entityName, params)
var toolResponse *mcp.CallToolResult
request := mcp.CallToolRequest{}
request.Params.Name = entityName
request.Params.Arguments = params
toolResponse, execErr = mcpClient.CallTool(context.Background(), request)
if execErr == nil && toolResponse != nil {
resp = ConvertJSONToMap(toolResponse)
} else {
resp = map[string]any{}
}
case EntityTypeRes:
resp, execErr = mcpClient.ReadResource(entityName)
var resourceResponse *mcp.ReadResourceResult
request := mcp.ReadResourceRequest{}
request.Params.URI = entityName
resourceResponse, execErr = mcpClient.ReadResource(context.Background(), request)
if execErr == nil && resourceResponse != nil {
resp = ConvertJSONToMap(resourceResponse)
} else {
resp = map[string]any{}
}
case EntityTypePrompt:
resp, execErr = mcpClient.GetPrompt(entityName)
var promptResponse *mcp.GetPromptResult
request := mcp.GetPromptRequest{}
request.Params.Name = entityName
promptResponse, execErr = mcpClient.GetPrompt(context.Background(), request)
if execErr == nil && promptResponse != nil {
resp = ConvertJSONToMap(promptResponse)
} else {
resp = map[string]any{}
}
default:
fmt.Fprintf(os.Stderr, "Error: unsupported entity type: %s\n", entityType)
os.Exit(1)
Expand Down
16 changes: 14 additions & 2 deletions cmd/mcptools/commands/get_prompt.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package commands

import (
"context"
"encoding/json"
"fmt"
"os"

"github.com/mark3labs/mcp-go/mcp"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -81,8 +83,18 @@ func GetPromptCmd() *cobra.Command {
os.Exit(1)
}

resp, execErr := mcpClient.GetPrompt(promptName)
if formatErr := FormatAndPrintResponse(thisCmd, resp, execErr); formatErr != nil {
request := mcp.GetPromptRequest{}
request.Params.Name = promptName
resp, execErr := mcpClient.GetPrompt(context.Background(), request)

var responseMap map[string]any
if execErr == nil && resp != nil {
responseMap = ConvertJSONToMap(resp)
} else {
responseMap = map[string]any{}
}

if formatErr := FormatAndPrintResponse(thisCmd, responseMap, execErr); formatErr != nil {
fmt.Fprintf(os.Stderr, "%v\n", formatErr)
os.Exit(1)
}
Expand Down
7 changes: 3 additions & 4 deletions cmd/mcptools/commands/guard.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"strings"

"github.com/f/mcptools/pkg/alias"
"github.com/f/mcptools/pkg/client"
"github.com/f/mcptools/pkg/guard"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -36,10 +35,10 @@ Examples:
mcp guard --allow tools:read_* --deny edit_*,write_*,create_* npx run @modelcontextprotocol/server-filesystem ~
mcp guard --allow prompts:system_* --deny tools:execute_* npx run @modelcontextprotocol/server-filesystem ~
mcp guard --allow tools:read_* fs # Using an alias

Patterns can include wildcards:
* matches any sequence of characters

Entity types:
tools: filter available tools
prompts: filter available prompts
Expand Down Expand Up @@ -76,7 +75,7 @@ Entity types:
if found {
fmt.Fprintf(os.Stderr, "Expanding alias '%s' to '%s'\n", aliasName, serverCmd)
// Replace the alias with the actual command
parsedArgs = client.ParseCommandString(serverCmd)
parsedArgs = ParseCommandString(serverCmd)
}
}

Expand Down
13 changes: 11 additions & 2 deletions cmd/mcptools/commands/prompts.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package commands

import (
"context"
"fmt"
"os"

"github.com/mark3labs/mcp-go/mcp"
"github.com/spf13/cobra"
)

Expand All @@ -29,8 +31,15 @@ func PromptsCmd() *cobra.Command {
os.Exit(1)
}

resp, listErr := mcpClient.ListPrompts()
if formatErr := FormatAndPrintResponse(thisCmd, resp, listErr); formatErr != nil {
resp, listErr := mcpClient.ListPrompts(context.Background(), mcp.ListPromptsRequest{})

var prompts []any
if listErr == nil && resp != nil {
prompts = ConvertJSONToSlice(resp.Prompts)
}

promptsMap := map[string]any{"prompts": prompts}
if formatErr := FormatAndPrintResponse(thisCmd, promptsMap, listErr); formatErr != nil {
fmt.Fprintf(os.Stderr, "%v\n", formatErr)
os.Exit(1)
}
Expand Down
16 changes: 14 additions & 2 deletions cmd/mcptools/commands/read_resource.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package commands

import (
"context"
"fmt"
"os"

"github.com/mark3labs/mcp-go/mcp"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -69,8 +71,18 @@ func ReadResourceCmd() *cobra.Command {
os.Exit(1)
}

resp, execErr := mcpClient.ReadResource(resourceName)
if formatErr := FormatAndPrintResponse(thisCmd, resp, execErr); formatErr != nil {
request := mcp.ReadResourceRequest{}
request.Params.URI = resourceName
resp, execErr := mcpClient.ReadResource(context.Background(), request)

var responseMap map[string]any
if execErr == nil && resp != nil {
responseMap = ConvertJSONToMap(resp)
} else {
responseMap = map[string]any{}
}

if formatErr := FormatAndPrintResponse(thisCmd, responseMap, execErr); formatErr != nil {
fmt.Fprintf(os.Stderr, "%v\n", formatErr)
os.Exit(1)
}
Expand Down
12 changes: 5 additions & 7 deletions cmd/mcptools/commands/read_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,11 @@ func TestReadResourceCmdRun_Success(t *testing.T) {

// Given: a mock client that returns a successful read resource response
mockResponse := map[string]any{
"result": map[string]any{
"contents": []any{
map[string]any{
"uri": "test://foo",
"mimeType": "text/plain",
"text": "bar",
},
"contents": []any{
map[string]any{
"uri": "test://foo",
"mimeType": "text/plain",
"text": "bar",
},
},
}
Expand Down
13 changes: 11 additions & 2 deletions cmd/mcptools/commands/resources.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package commands

import (
"context"
"fmt"
"os"

"github.com/mark3labs/mcp-go/mcp"
"github.com/spf13/cobra"
)

Expand All @@ -29,8 +31,15 @@ func ResourcesCmd() *cobra.Command {
os.Exit(1)
}

resp, listErr := mcpClient.ListResources()
if formatErr := FormatAndPrintResponse(thisCmd, resp, listErr); formatErr != nil {
resp, listErr := mcpClient.ListResources(context.Background(), mcp.ListResourcesRequest{})

var resources []any
if listErr == nil && resp != nil {
resources = ConvertJSONToSlice(resp.Resources)
}

resourcesMap := map[string]any{"resources": resources}
if formatErr := FormatAndPrintResponse(thisCmd, resourcesMap, listErr); formatErr != nil {
fmt.Fprintf(os.Stderr, "%v\n", formatErr)
os.Exit(1)
}
Expand Down
73 changes: 59 additions & 14 deletions cmd/mcptools/commands/shell.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package commands

import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/f/mcptools/pkg/client"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/mcp"
"github.com/peterh/liner"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -48,18 +50,12 @@ func ShellCmd() *cobra.Command { //nolint:gocyclo
os.Exit(1)
}

mcpClient, clientErr := CreateClientFunc(parsedArgs, client.CloseTransportAfterExecute(false))
mcpClient, clientErr := CreateClientFunc(parsedArgs)
if clientErr != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", clientErr)
os.Exit(1)
}

_, listErr := mcpClient.ListTools()
if listErr != nil {
fmt.Fprintf(os.Stderr, "Error connecting to MCP server: %v\n", listErr)
os.Exit(1)
}

fmt.Fprintf(thisCmd.OutOrStdout(), "mcp > MCP Tools Shell (%s)\n", Version)
fmt.Fprintf(thisCmd.OutOrStdout(), "mcp > Connected to Server: %s\n", strings.Join(parsedArgs, " "))
fmt.Fprintf(thisCmd.OutOrStdout(), "\nmcp > Type '/h' for help or '/q' to quit\n")
Expand Down Expand Up @@ -111,19 +107,43 @@ func ShellCmd() *cobra.Command { //nolint:gocyclo

switch command {
case "tools":
resp, listErr = mcpClient.ListTools()
var listToolsResult *mcp.ListToolsResult
listToolsResult, listErr = mcpClient.ListTools(context.Background(), mcp.ListToolsRequest{})

var tools []any
if listErr == nil && listToolsResult != nil {
tools = ConvertJSONToSlice(listToolsResult.Tools)
}

resp = map[string]any{"tools": tools}
if formatErr := FormatAndPrintResponse(thisCmd, resp, listErr); formatErr != nil {
fmt.Fprintf(os.Stderr, "%v\n", formatErr)
continue
}
case "resources":
resp, listErr = mcpClient.ListResources()
var listResourcesResult *mcp.ListResourcesResult
listResourcesResult, listErr = mcpClient.ListResources(context.Background(), mcp.ListResourcesRequest{})

var resources []any
if listErr == nil && listResourcesResult != nil {
resources = ConvertJSONToSlice(listResourcesResult.Resources)
}

resp = map[string]any{"resources": resources}
if formatErr := FormatAndPrintResponse(thisCmd, resp, listErr); formatErr != nil {
fmt.Fprintf(os.Stderr, "%v\n", formatErr)
continue
}
case "prompts":
resp, listErr = mcpClient.ListPrompts()
var listPromptsResult *mcp.ListPromptsResult
listPromptsResult, listErr = mcpClient.ListPrompts(context.Background(), mcp.ListPromptsRequest{})

var prompts []any
if listErr == nil && listPromptsResult != nil {
prompts = ConvertJSONToSlice(listPromptsResult.Prompts)
}

resp = map[string]any{"prompts": prompts}
if formatErr := FormatAndPrintResponse(thisCmd, resp, listErr); formatErr != nil {
fmt.Fprintf(os.Stderr, "%v\n", formatErr)
continue
Expand Down Expand Up @@ -208,11 +228,36 @@ func callCommand(thisCmd *cobra.Command, mcpClient *client.Client, commandArgs [

switch entityType {
case EntityTypeTool:
resp, execErr = mcpClient.CallTool(entityName, params)
var toolResponse *mcp.CallToolResult
request := mcp.CallToolRequest{}
request.Params.Name = entityName
request.Params.Arguments = params
toolResponse, execErr = mcpClient.CallTool(context.Background(), request)
if execErr == nil && toolResponse != nil {
resp = ConvertJSONToMap(toolResponse)
} else {
resp = map[string]any{}
}
case EntityTypeRes:
resp, execErr = mcpClient.ReadResource(entityName)
var resourceResponse *mcp.ReadResourceResult
request := mcp.ReadResourceRequest{}
request.Params.URI = entityName
resourceResponse, execErr = mcpClient.ReadResource(context.Background(), request)
if execErr == nil && resourceResponse != nil {
resp = ConvertJSONToMap(resourceResponse)
} else {
resp = map[string]any{}
}
case EntityTypePrompt:
resp, execErr = mcpClient.GetPrompt(entityName)
var promptResponse *mcp.GetPromptResult
request := mcp.GetPromptRequest{}
request.Params.Name = entityName
promptResponse, execErr = mcpClient.GetPrompt(context.Background(), request)
if execErr == nil && promptResponse != nil {
resp = ConvertJSONToMap(promptResponse)
} else {
resp = map[string]any{}
}
default:
fmt.Fprintf(os.Stderr, "Error: unsupported entity type: %s\n", entityType)
}
Expand Down
Loading