Skip to content

[#67] Fix Client's ReadResource method #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
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
8 changes: 2 additions & 6 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,17 +235,13 @@ func (c *Client) ReadResource(ctx context.Context, uri string) (*ResourceRespons
return nil, errors.New("invalid response type")
}

var resourceResponse resourceResponseSent
var resourceResponse ResourceResponse
err = json.Unmarshal(responseBytes, &resourceResponse)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal resource response")
}

if resourceResponse.Error != nil {
return nil, resourceResponse.Error
}

return resourceResponse.Response, nil
return &resourceResponse, nil
}

// Ping sends a ping request to the server to check connectivity
Expand Down
41 changes: 39 additions & 2 deletions content_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,51 @@ type EmbeddedResource struct {

// Custom JSON marshaling for EmbeddedResource
func (c EmbeddedResource) MarshalJSON() ([]byte, error) {
type wrapper struct {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes in this file are written to satisfy the following constraint:

  1. Don't break the existing marshaling/unmarshaling API for EmbeddedResource.
    1. Add an embeddedResourceType field (don't touch existing fields).
    2. Continue to allow the multiple content types and schemas (BlobResourceContents, TextResourceContents) over one endpoint.

Type string `json:"embeddedResourceType"`
*TextResourceContents
*BlobResourceContents
}

w := wrapper{Type: string(c.EmbeddedResourceType)}

switch c.EmbeddedResourceType {
case embeddedResourceTypeBlob:
return json.Marshal(c.BlobResourceContents)
w.BlobResourceContents = c.BlobResourceContents
case embeddedResourceTypeText:
return json.Marshal(c.TextResourceContents)
w.TextResourceContents = c.TextResourceContents
default:
return nil, fmt.Errorf("unknown embedded resource type: %s", c.EmbeddedResourceType)
}

return json.Marshal(w)
}

func (c *EmbeddedResource) UnmarshalJSON(data []byte) error {
var wrapper struct {
Type string `json:"embeddedResourceType"`
Raw json.RawMessage `json:"-"`
}

wrapper.Raw = data
if err := json.Unmarshal(data, &wrapper); err != nil {
return err
}

c.EmbeddedResourceType = embeddedResourceType(wrapper.Type)

switch c.EmbeddedResourceType {
case embeddedResourceTypeText:
c.TextResourceContents = new(TextResourceContents)
return json.Unmarshal(wrapper.Raw, c.TextResourceContents)
Copy link
Author

@rvoh-emccaleb rvoh-emccaleb Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like unmarshaling twice, but given the API supports multiple types/schemas that are identified in the body (not in headers), I don't currently see a way around it.


case embeddedResourceTypeBlob:
c.BlobResourceContents = new(BlobResourceContents)
return json.Unmarshal(wrapper.Raw, c.BlobResourceContents)

default:
return fmt.Errorf("unknown resource type: %s", wrapper.Type)
}
}

type ContentType string
Expand Down
51 changes: 4 additions & 47 deletions transport/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,14 @@ import (
"encoding/json"
"fmt"
"net/http"
"sync"

"github.com/metoro-io/mcp-golang/transport"
)

// HTTPTransport implements a stateless HTTP transport for MCP
type HTTPTransport struct {
*baseTransport
server *http.Server
endpoint string
messageHandler func(ctx context.Context, message *transport.BaseJsonRpcMessage)
errorHandler func(error)
closeHandler func()
mu sync.RWMutex
addr string
responseMap map[int64]chan *transport.BaseJsonRpcMessage
server *http.Server
endpoint string
addr string
}

// NewHTTPTransport creates a new HTTP transport that listens on the specified endpoint
Expand All @@ -29,7 +21,6 @@ func NewHTTPTransport(endpoint string) *HTTPTransport {
baseTransport: newBaseTransport(),
endpoint: endpoint,
addr: ":8080", // Default port
responseMap: make(map[int64]chan *transport.BaseJsonRpcMessage),
}
}

Expand All @@ -52,49 +43,15 @@ func (t *HTTPTransport) Start(ctx context.Context) error {
return t.server.ListenAndServe()
}

// Send implements Transport.Send
func (t *HTTPTransport) Send(ctx context.Context, message *transport.BaseJsonRpcMessage) error {
key := message.JsonRpcResponse.Id
responseChannel := t.responseMap[int64(key)]
if responseChannel == nil {
return fmt.Errorf("no response channel found for key: %d", key)
}
responseChannel <- message
return nil
}

// Close implements Transport.Close
func (t *HTTPTransport) Close() error {
if t.server != nil {
if err := t.server.Close(); err != nil {
return err
}
}
if t.closeHandler != nil {
t.closeHandler()
}
return nil
}

// SetCloseHandler implements Transport.SetCloseHandler
func (t *HTTPTransport) SetCloseHandler(handler func()) {
t.mu.Lock()
defer t.mu.Unlock()
t.closeHandler = handler
}

// SetErrorHandler implements Transport.SetErrorHandler
func (t *HTTPTransport) SetErrorHandler(handler func(error)) {
t.mu.Lock()
defer t.mu.Unlock()
t.errorHandler = handler
}

// SetMessageHandler implements Transport.SetMessageHandler
func (t *HTTPTransport) SetMessageHandler(handler func(ctx context.Context, message *transport.BaseJsonRpcMessage)) {
t.mu.Lock()
defer t.mu.Unlock()
t.messageHandler = handler
return t.baseTransport.Close()
}

func (t *HTTPTransport) handleRequest(w http.ResponseWriter, r *http.Request) {
Expand Down
Loading