Skip to content

Commit d11cf8e

Browse files
author
Marek Safarik
committed
Simple initial MCP client implementation
Signed-off-by: Marek Safarik <[email protected]>
1 parent bf81763 commit d11cf8e

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

mcp-client/go.mod

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module github.com/keylime/keylime-mcp/mcp-client
2+
3+
go 1.23.0
4+
5+
require (
6+
github.com/anthropics/anthropic-sdk-go v1.18.0
7+
github.com/joho/godotenv v1.5.1
8+
github.com/modelcontextprotocol/go-sdk v1.1.0
9+
)
10+
11+
require (
12+
github.com/google/jsonschema-go v0.3.0 // indirect
13+
github.com/tidwall/gjson v1.18.0 // indirect
14+
github.com/tidwall/match v1.1.1 // indirect
15+
github.com/tidwall/pretty v1.2.1 // indirect
16+
github.com/tidwall/sjson v1.2.5 // indirect
17+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
18+
golang.org/x/oauth2 v0.30.0 // indirect
19+
)

mcp-client/go.sum

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
github.com/anthropics/anthropic-sdk-go v1.18.0 h1:jfxRA7AqZoCm83nHO/OVQp8xuwjUKtBziEdMbfmofHU=
2+
github.com/anthropics/anthropic-sdk-go v1.18.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
3+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
6+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
7+
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
8+
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
9+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
10+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
11+
github.com/modelcontextprotocol/go-sdk v1.1.0 h1:Qjayg53dnKC4UZ+792W21e4BpwEZBzwgRW6LrjLWSwA=
12+
github.com/modelcontextprotocol/go-sdk v1.1.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
13+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
14+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
16+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
17+
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
18+
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
19+
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
20+
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
21+
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
22+
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
23+
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
24+
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
25+
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
26+
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
27+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
28+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
29+
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
30+
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
31+
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
32+
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
33+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
34+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

mcp-client/main.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"log"
6+
"os"
7+
"os/exec"
8+
9+
"github.com/anthropics/anthropic-sdk-go"
10+
"github.com/anthropics/anthropic-sdk-go/option"
11+
"github.com/joho/godotenv"
12+
"github.com/modelcontextprotocol/go-sdk/mcp"
13+
)
14+
15+
const serverPath = "../backend/server"
16+
17+
func main() {
18+
ctx := context.Background()
19+
godotenv.Load("../.env")
20+
21+
apiKey := os.Getenv("ANTHROPIC_API_KEY")
22+
if apiKey == "" {
23+
log.Fatal("ANTHROPIC_API_KEY environment variable not set")
24+
}
25+
26+
if _, err := os.Stat(serverPath); os.IsNotExist(err) {
27+
log.Fatalf("Server not found: %s", serverPath)
28+
}
29+
30+
// Connect to MCP server
31+
client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)
32+
transport := &mcp.CommandTransport{Command: exec.Command(serverPath)}
33+
session, err := client.Connect(ctx, transport, nil)
34+
if err != nil {
35+
log.Fatal(err)
36+
}
37+
defer session.Close()
38+
39+
tools, _ := session.ListTools(ctx, &mcp.ListToolsParams{})
40+
41+
claudeTools := make([]anthropic.ToolUnionParam, 0)
42+
for _, tool := range tools.Tools {
43+
inputSchemaMap, ok := tool.InputSchema.(map[string]interface{})
44+
if !ok || inputSchemaMap == nil {
45+
inputSchemaMap = map[string]interface{}{}
46+
}
47+
48+
properties := inputSchemaMap["properties"]
49+
required, _ := inputSchemaMap["required"].([]string)
50+
51+
toolParam := anthropic.ToolUnionParamOfTool(
52+
anthropic.ToolInputSchemaParam{
53+
Type: "object",
54+
Properties: properties,
55+
Required: required,
56+
},
57+
tool.Name,
58+
)
59+
60+
toolParam.OfTool.Description = anthropic.String(tool.Description)
61+
62+
claudeTools = append(claudeTools, toolParam)
63+
}
64+
anthropicClient := anthropic.NewClient(
65+
option.WithAPIKey(apiKey),
66+
)
67+
message, err := anthropicClient.Messages.New(ctx, anthropic.MessageNewParams{
68+
Model: anthropic.ModelClaude_3_Haiku_20240307,
69+
MaxTokens: 256,
70+
Messages: []anthropic.MessageParam{
71+
anthropic.NewUserMessage(anthropic.NewTextBlock("Give me all failed agents")),
72+
},
73+
Tools: claudeTools,
74+
})
75+
if err != nil {
76+
log.Fatal(err)
77+
}
78+
79+
for _, block := range message.Content {
80+
switch block := block.AsAny().(type) {
81+
case anthropic.TextBlock:
82+
println(block.Text)
83+
println()
84+
case anthropic.ToolUseBlock:
85+
params := &mcp.CallToolParams{
86+
Name: block.Name,
87+
Arguments: block.Input,
88+
}
89+
res, err := session.CallTool(ctx, params)
90+
if err != nil {
91+
log.Fatalf("CallTool failed: %v", err)
92+
}
93+
if res.IsError {
94+
log.Fatal("tool failed")
95+
}
96+
for _, c := range res.Content {
97+
log.Print(c.(*mcp.TextContent).Text)
98+
println()
99+
}
100+
}
101+
}
102+
}

mcp-client/mcp-client

12.8 MB
Binary file not shown.

0 commit comments

Comments
 (0)