Skip to content

Commit c8ff070

Browse files
More tests
1 parent a79e6b0 commit c8ff070

File tree

2 files changed

+231
-10
lines changed

2 files changed

+231
-10
lines changed

cmd/main.go

Lines changed: 231 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,260 @@
11
package main
22

3-
import "github.com/metoro-io/mcp-golang"
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/metoro-io/mcp-golang"
8+
"io"
9+
"net/http"
10+
"strings"
11+
)
412

513
type Content struct {
614
Title string `json:"title" jsonschema:"description=The title to submit"`
715
Description *string `json:"description,omitempty" jsonschema:"description=The description to submit"`
816
}
917
type MyFunctionsArguments struct {
10-
Submitter string `json:"submitter" jsonschema:"description=The name of the person using the tool"`
18+
Submitter string `json:"submitter" jsonschema:"description=The name of the thing calling this tool (openai, google, claude, etc)"`
1119
Content Content `json:"content" jsonschema:"description=The content of the message"`
1220
}
1321

22+
type ToggleLights struct {
23+
EntityID string `json:"entity_id,omitempty"`
24+
}
25+
26+
type None struct{}
27+
1428
func main() {
1529
done := make(chan struct{})
1630

1731
s := mcp.NewServer(mcp.NewStdioServerTransport())
1832
err := s.Tool("hello", "Say hello to a person", func(arguments MyFunctionsArguments) (mcp.ToolResponse, error) {
19-
// ... handle the tool logic
2033
return mcp.ToolResponse{Content: []mcp.Content{{Type: "text", Text: "Hello, " + arguments.Submitter + "!"}}}, nil
2134
})
2235
if err != nil {
2336
panic(err)
2437
}
2538

39+
err = s.Tool("get_lights", "Get the state of all lights in the house", func(arguments None) (mcp.ToolResponse, error) {
40+
// Configuration
41+
hassURL := "http://home.net:8123"
42+
// Replace with your actual token
43+
token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI1YmZkOGMwMmJjNGI0Y2ZkODdiZmExMTA5ZDQwZTg5YSIsImlhdCI6MTczMzY5NTc3MSwiZXhwIjoyMDQ5MDU1NzcxfQ.cCmgbnC_kXOIXgrmf59GPw8cYZYGx6pHjzQIZkYc72Q"
44+
45+
lights, err := getLights(hassURL, token)
46+
if err != nil {
47+
return mcp.ToolResponse{}, err
48+
}
49+
output := displayLights(lights)
50+
return mcp.ToolResponse{Content: []mcp.Content{{Type: "text", Text: output}}}, nil
51+
})
52+
53+
// Tool function to toggle lights
54+
err = s.Tool("control_light", "Toggle a specific light", func(arguments ToggleLights) (mcp.ToolResponse, error) {
55+
hassURL := "http://home.net:8123"
56+
token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI1YmZkOGMwMmJjNGI0Y2ZkODdiZmExMTA5ZDQwZTg5YSIsImlhdCI6MTczMzY5NTc3MSwiZXhwIjoyMDQ5MDU1NzcxfQ.cCmgbnC_kXOIXgrmf59GPw8cYZYGx6pHjzQIZkYc72Q"
57+
58+
err := toggleLight(hassURL, token, arguments.EntityID)
59+
if err != nil {
60+
return mcp.ToolResponse{}, err
61+
}
62+
63+
output := fmt.Sprintf("Successfully toggled %s", arguments.EntityID)
64+
return mcp.ToolResponse{Content: []mcp.Content{{Type: "text", Text: output}}}, nil
65+
})
66+
2667
err = s.Serve()
2768
if err != nil {
2869
panic(err)
2970
}
3071

3172
<-done
3273
}
74+
75+
type Entity struct {
76+
EntityID string `json:"entity_id"`
77+
State string `json:"state"`
78+
Attributes map[string]interface{} `json:"attributes"`
79+
LastChanged string `json:"last_changed"`
80+
}
81+
82+
func controlLight(baseURL, token, entityID, state string, brightness int) error {
83+
if !strings.HasPrefix(entityID, "light.") {
84+
return fmt.Errorf("invalid entity ID format. Must start with 'light.'")
85+
}
86+
87+
state = strings.ToLower(state)
88+
if state != "on" && state != "off" {
89+
return fmt.Errorf("invalid state. Must be 'on' or 'off'")
90+
}
91+
92+
service := "turn_" + state
93+
endpoint := fmt.Sprintf("%s/api/services/light/%s", baseURL, service)
94+
95+
command := struct {
96+
Entity_ID string `json:"entity_id"`
97+
Data map[string]interface{} `json:"data,omitempty"`
98+
}{
99+
Entity_ID: entityID,
100+
}
101+
102+
if state == "on" && brightness >= 0 {
103+
if brightness < 0 || brightness > 100 {
104+
return fmt.Errorf("brightness must be between 0 and 100")
105+
}
106+
hassbrightness := int(float64(brightness) / 100 * 255)
107+
command.Data = map[string]interface{}{
108+
"brightness": hassbrightness,
109+
}
110+
}
111+
112+
jsonData, err := json.Marshal(command)
113+
if err != nil {
114+
return fmt.Errorf("error creating JSON: %v", err)
115+
}
116+
117+
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonData))
118+
if err != nil {
119+
return fmt.Errorf("error creating request: %v", err)
120+
}
121+
122+
req.Header.Add("Authorization", "Bearer "+token)
123+
req.Header.Add("Content-Type", "application/json")
124+
125+
client := &http.Client{}
126+
resp, err := client.Do(req)
127+
if err != nil {
128+
return fmt.Errorf("error making request: %v", err)
129+
}
130+
defer resp.Body.Close()
131+
132+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
133+
// Parse the error message
134+
body := resp.Body
135+
all, err := io.ReadAll(body)
136+
if err != nil {
137+
return err
138+
}
139+
140+
return fmt.Errorf("unexpected status code: %d. Response body: %s. Request body: %s", resp.StatusCode, string(all), string(jsonData))
141+
}
142+
143+
return nil
144+
}
145+
146+
func getLights(baseURL, token string) ([]Entity, error) {
147+
client := &http.Client{}
148+
req, err := http.NewRequest("GET", baseURL+"/api/states", nil)
149+
if err != nil {
150+
return nil, fmt.Errorf("error creating request: %v", err)
151+
}
152+
153+
req.Header.Add("Authorization", "Bearer "+token)
154+
req.Header.Add("Content-Type", "application/json")
155+
156+
resp, err := client.Do(req)
157+
if err != nil {
158+
return nil, fmt.Errorf("error making request: %v", err)
159+
}
160+
defer resp.Body.Close()
161+
162+
if resp.StatusCode != http.StatusOK {
163+
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
164+
}
165+
166+
body, err := io.ReadAll(resp.Body)
167+
if err != nil {
168+
return nil, fmt.Errorf("error reading response: %v", err)
169+
}
170+
171+
var entities []Entity
172+
if err := json.Unmarshal(body, &entities); err != nil {
173+
return nil, fmt.Errorf("error parsing JSON: %v", err)
174+
}
175+
176+
// Filter only light entities
177+
var lights []Entity
178+
for _, entity := range entities {
179+
if strings.HasPrefix(entity.EntityID, "light.") {
180+
lights = append(lights, entity)
181+
}
182+
}
183+
184+
return lights, nil
185+
}
186+
187+
func displayLights(lights []Entity) string {
188+
var output strings.Builder
189+
190+
output.WriteString("Active Lights Status:\n")
191+
output.WriteString("=====================\n")
192+
193+
for _, light := range lights {
194+
name := light.Attributes["friendly_name"]
195+
brightness := light.Attributes["brightness"]
196+
197+
output.WriteString(fmt.Sprintf("\nLight: %s (%s)\n", name, light.EntityID))
198+
output.WriteString(fmt.Sprintf("State: %s\n", light.State))
199+
200+
if brightness != nil {
201+
output.WriteString(fmt.Sprintf("Brightness: %.0f%%\n", float64(brightness.(float64))/255*100))
202+
}
203+
204+
if light.State == "on" {
205+
if colorMode, ok := light.Attributes["color_mode"].(string); ok {
206+
output.WriteString(fmt.Sprintf("Color Mode: %s\n", colorMode))
207+
}
208+
209+
if rgb, ok := light.Attributes["rgb_color"].([]interface{}); ok {
210+
output.WriteString(fmt.Sprintf("RGB Color: R:%v G:%v B:%v\n",
211+
int(rgb[0].(float64)),
212+
int(rgb[1].(float64)),
213+
int(rgb[2].(float64))))
214+
}
215+
}
216+
output.WriteString(fmt.Sprintf("Last Changed: %s\n", light.LastChanged))
217+
}
218+
219+
return output.String()
220+
}
221+
222+
func toggleLight(baseURL, token, entityID string) error {
223+
if !strings.HasPrefix(entityID, "light.") {
224+
return fmt.Errorf("invalid entity ID format. Must start with 'light.'")
225+
}
226+
227+
endpoint := fmt.Sprintf("%s/api/services/light/toggle", baseURL)
228+
229+
command := struct {
230+
Entity_ID string `json:"entity_id"`
231+
}{
232+
Entity_ID: entityID,
233+
}
234+
235+
jsonData, err := json.Marshal(command)
236+
if err != nil {
237+
return fmt.Errorf("error creating JSON: %v", err)
238+
}
239+
240+
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(jsonData))
241+
if err != nil {
242+
return fmt.Errorf("error creating request: %v", err)
243+
}
244+
245+
req.Header.Add("Authorization", "Bearer "+token)
246+
req.Header.Add("Content-Type", "application/json")
247+
248+
client := &http.Client{}
249+
resp, err := client.Do(req)
250+
if err != nil {
251+
return fmt.Errorf("error making request: %v", err)
252+
}
253+
defer resp.Body.Close()
254+
255+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
256+
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
257+
}
258+
259+
return nil
260+
}

examples/weather/main.go

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)