Skip to content

Commit

Permalink
🔧 improve SSE transport port configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
wesen committed Feb 22, 2025
1 parent 984b18d commit 14947dd
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 309 deletions.
10 changes: 5 additions & 5 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
version: 2
project_name: go-go-mcp
project_name: mcp

before:
hooks:
Expand All @@ -11,7 +11,7 @@ builds:
- env:
- CGO_ENABLED=0
main: ./cmd/go-go-mcp
binary: go-go-mcp
binary: mcp
goos:
- linux
# I am not able to test windows at the time
Expand All @@ -34,8 +34,8 @@ signs:
args: [ "--batch", "-u", "{{ .Env.GPG_FINGERPRINT }}", "--output", "${signature}", "--detach-sign", "${artifact}" ]

brews:
- name: go-go-mcp
description: "go-go-mcp is a tool to serve and run MCPs"
- name: mcp
description: "mcp is a tool to serve and run MCPs"
homepage: "https://github.com/go-go-golems/go-go-mcp"
repository:
owner: go-go-golems
Expand All @@ -51,7 +51,7 @@ nfpms:
maintainer: Manuel Odendahl <wesen@ruinwesen.com>

description: |-
go-go-mcp is a tool to serve and run MCPs
mcp is a tool to serve and run MCPs
license: MIT

Expand Down
20 changes: 19 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/go-go-mcp",
"args": ["start", "--transport", "sse", "--profile", "html-extraction", "--log-level", "debug", "--port", "3000"],
"args": ["server", "start", "--profile", "all", "--log-level", "debug"],
"cwd": "${workspaceFolder}"
},
{
"name": "Launch MCP Server (sse)",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/go-go-mcp",
"args": ["server", "start", "--transport", "sse", "--profile", "html-extraction", "--log-level", "debug", "--port", "3000"],
"cwd": "${workspaceFolder}"
},
{
Expand All @@ -28,6 +37,15 @@
"program": "${workspaceFolder}/cmd/go-go-mcp",
"args": ["config", "add-tool", "html-extraction", "--dir", "~/code/wesen/corporate-headquarters/go-go-mcp/examples/html-extract"],
"cwd": "${workspaceFolder}"
},
{
"name": "Launch UI Server",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/ui-server",
"args": ["examples/pages"],
"cwd": "${workspaceFolder}"
}
]
}
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ bump-glazed:
go get github.com/go-go-golems/parka@latest
go mod tidy

go-go-mcp_BINARY=$(shell which go-go-mcp)
mcp_BINARY=$(shell which mcp)
install:
go build -o ./dist/go-go-mcp ./cmd/go-go-mcp && \
cp ./dist/go-go-mcp $(go-go-mcp_BINARY)
go build -o ./dist/mcp ./cmd/go-go-mcp && \
cp ./dist/mcp $(mcp_BINARY)
24 changes: 23 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -1146,4 +1146,26 @@ Added a new UI server that can render YAML UI definitions using HTMX and Bootstr
- Created a new command `ui-server` that serves UI definitions from YAML files
- Implemented templ templates for rendering UI components
- Added support for various UI components like buttons, inputs, forms, etc.
- Used HTMX for dynamic interactions and Bootstrap for styling
- Used HTMX for dynamic interactions and Bootstrap for styling

# Halloween-themed UI Examples
Added a collection of Halloween-themed example pages using the UI DSL:
- Created welcome page with spooky navigation
- Added haunted house tour booking form
- Created costume contest voting interface
- Added Halloween party RSVP form with fun options
- Created trick-or-treat checklist for safety

# UI DSL Structure Update
Updated the UI DSL to use a top-level components list for better sequence handling:
- Changed UIDefinition to use a list of components instead of a map
- Updated all example pages to use the new structure
- Modified templates to handle the new component list format
- Improved component rendering to handle nested components

# SSE Transport Port Configuration

Improved port configuration handling in SSE transport by properly parsing the provided address.

- Added proper port parsing from SSE options address
- Ensures port configuration is correctly propagated from command line to transport
2 changes: 1 addition & 1 deletion cmd/go-go-mcp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var (
)

var rootCmd = &cobra.Command{
Use: "go-go-mcp",
Use: "mcp",
Short: "MCP client and server implementation in Go",
Long: `A Model Context Protocol (MCP) client and server implementation in Go.
Supports both stdio and SSE transports for client-server communication.
Expand Down
2 changes: 1 addition & 1 deletion cmd/ui-server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Server struct {
}

type UIDefinition struct {
Components map[string]interface{} `yaml:",inline"`
Components []map[string]interface{} `yaml:"components"`
}

func NewServer(dir string) *Server {
Expand Down
149 changes: 96 additions & 53 deletions cmd/ui-server/templates.templ
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ templ base(title string) {
hljs.highlightElement(el);
});
});

function logToConsole(message) {
const console = document.getElementById('interaction-console');
const entry = document.createElement('div');
entry.className = 'console-entry';
entry.textContent = message;
console.appendChild(entry);
console.scrollTop = console.scrollHeight;
if (console.children.length > 50) {
console.removeChild(console.firstChild);
}
}

function logFormSubmit(formId, formData) {
const data = Object.fromEntries(new FormData(formData));
const yaml = Object.entries(data)
.map(([key, value]) => ` ${key}: ${value}`)
.join('\n');
logToConsole(`Form ${formId} submitted with data:\n\`\`\`yaml\n${yaml}\n\`\`\``);
}
</script>
<style>
.source-yaml {
Expand All @@ -36,6 +56,27 @@ templ base(title string) {
pre {
margin: 0;
}
#interaction-console {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: #2d2d2d;
color: #fff;
font-family: monospace;
padding: 10px;
overflow-y: auto;
z-index: 1000;
border-top: 2px solid #444;
}
#interaction-console .console-entry {
margin: 5px 0;
white-space: pre-wrap;
}
.console-spacer {
height: 170px;
}
</style>
</head>
<body class="container py-4">
Expand All @@ -45,6 +86,8 @@ templ base(title string) {
</div>
</nav>
{ children... }
<div class="console-spacer"></div>
<div id="interaction-console"></div>
</body>
</html>
}
Expand Down Expand Up @@ -100,28 +143,26 @@ templ pageTemplate(name string, def UIDefinition) {
templ renderComponent(typ string, props map[string]interface{}) {
switch typ {
case "button":
<button
if id, ok := props["id"].(string); ok {
if id, ok := props["id"].(string); ok {
<button
id={ id }
}
if disabled, ok := props["disabled"].(bool); ok && disabled {
disabled="disabled"
}
if onclick, ok := props["onclick"].(string); ok {
data-hx-on:click={ onclick }
}
class={
"btn",
templ.KV("btn-primary", props["type"] == "primary"),
templ.KV("btn-secondary", props["type"] == "secondary"),
templ.KV("btn-danger", props["type"] == "danger"),
templ.KV("btn-success", props["type"] == "success"),
}
>
if text, ok := props["text"].(string); ok {
{ text }
}
</button>
data-hx-on:click={ fmt.Sprintf("logToConsole('%s clicked')", id) }
if disabled, ok := props["disabled"].(bool); ok && disabled {
disabled="disabled"
}
class={
"btn",
templ.KV("btn-primary", props["type"] == "primary"),
templ.KV("btn-secondary", props["type"] == "secondary"),
templ.KV("btn-danger", props["type"] == "danger"),
templ.KV("btn-success", props["type"] == "success"),
}
>
if text, ok := props["text"].(string); ok {
{ text }
}
</button>
}
case "title":
<h1
if id, ok := props["id"].(string); ok {
Expand Down Expand Up @@ -182,27 +223,28 @@ templ renderComponent(typ string, props map[string]interface{}) {
}
</textarea>
case "checkbox":
<div class="form-check">
<input
type="checkbox"
if id, ok := props["id"].(string); ok {
if id, ok := props["id"].(string); ok {
<div class="form-check">
<input
type="checkbox"
id={ id }
data-hx-on:change={ fmt.Sprintf("logToConsole('%s ' + (this.checked ? 'checked' : 'unchecked'))", id) }
if name, ok := props["name"].(string); ok {
name={ name }
}
if checked, ok := props["checked"].(bool); ok && checked {
checked="checked"
}
if required, ok := props["required"].(bool); ok && required {
required="required"
}
class="form-check-input"
/>
if label, ok := props["label"].(string); ok {
<label class="form-check-label" for={ id }>{ label }</label>
}
if name, ok := props["name"].(string); ok {
name={ name }
}
if checked, ok := props["checked"].(bool); ok && checked {
checked="checked"
}
if required, ok := props["required"].(bool); ok && required {
required="required"
}
class="form-check-input"
/>
if label, ok := props["label"].(string); ok {
<label class="form-check-label" for={ props["id"].(string) }>{ label }</label>
}
</div>
</div>
}
case "list":
if typ, ok := props["type"].(string); ok {
if typ == "ul" {
Expand Down Expand Up @@ -242,23 +284,24 @@ templ renderComponent(typ string, props map[string]interface{}) {
}
}
case "form":
<form
if id, ok := props["id"].(string); ok {
if id, ok := props["id"].(string); ok {
<form
id={ id }
}
class="needs-validation"
novalidate
>
if components, ok := props["components"].([]interface{}); ok {
for _, comp := range components {
if c, ok := comp.(map[string]interface{}); ok {
for typ, props := range c {
@renderComponent(typ, props.(map[string]interface{}))
data-hx-on:submit={ fmt.Sprintf("event.preventDefault(); logFormSubmit('%s', this)", id) }
class="needs-validation"
novalidate
>
if components, ok := props["components"].([]interface{}); ok {
for _, comp := range components {
if c, ok := comp.(map[string]interface{}); ok {
for typ, props := range c {
@renderComponent(typ, props.(map[string]interface{}))
}
}
}
}
}
</form>
</form>
}
}
}

Expand Down
Loading

0 comments on commit 14947dd

Please sign in to comment.