| 1 | package plugin |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/json" |
| 6 | "fmt" |
| 7 | "strings" |
| 8 | ) |
| 9 | |
| 10 | // Resource is an MCP resource a server exposes. In chat it is referenced as |
| 11 | // "@<server>:<uri>" (e.g. "@docs:file://README.md"); the referenced content is |
| 12 | // fetched and prepended to the message sent to the model. |
| 13 | type Resource struct { |
| 14 | Server string // owning server name |
| 15 | URI string // canonical resource uri |
| 16 | Name string // human-readable label |
| 17 | Description string |
| 18 | MimeType string |
| 19 | } |
| 20 | |
| 21 | func (c *Client) listResources(ctx context.Context) ([]Resource, error) { |
| 22 | res, err := c.call(ctx, "resources/list", map[string]any{}) |
| 23 | if err != nil { |
| 24 | return nil, err |
| 25 | } |
| 26 | var out struct { |
| 27 | Resources []struct { |
| 28 | URI string `json:"uri"` |
| 29 | Name string `json:"name"` |
| 30 | Description string `json:"description"` |
| 31 | MimeType string `json:"mimeType"` |
| 32 | } `json:"resources"` |
| 33 | } |
| 34 | if err := json.Unmarshal(res, &out); err != nil { |
| 35 | return nil, fmt.Errorf("plugin %q: decode resources/list: %w", c.name, err) |
| 36 | } |
| 37 | resources := make([]Resource, 0, len(out.Resources)) |
| 38 | for _, r := range out.Resources { |
| 39 | resources = append(resources, Resource{ |
| 40 | Server: c.name, |
| 41 | URI: r.URI, |
| 42 | Name: r.Name, |
| 43 | Description: r.Description, |
| 44 | MimeType: r.MimeType, |
| 45 | }) |
| 46 | } |
| 47 | return resources, nil |
| 48 | } |
| 49 | |
| 50 | // readResource fetches a resource by uri and flattens its text contents. Binary |
| 51 | // (blob) contents are noted but not decoded — a coding agent consumes text. |
| 52 | func (c *Client) readResource(ctx context.Context, uri string) (string, error) { |
| 53 | res, err := c.call(ctx, "resources/read", map[string]any{"uri": uri}) |
| 54 | if err != nil { |
| 55 | return "", err |
| 56 | } |
| 57 | var out struct { |
| 58 | Contents []struct { |
| 59 | URI string `json:"uri"` |
| 60 | MimeType string `json:"mimeType"` |
| 61 | Text string `json:"text"` |
| 62 | Blob string `json:"blob"` |
| 63 | } `json:"contents"` |
| 64 | } |
| 65 | if err := json.Unmarshal(res, &out); err != nil { |
| 66 | return "", fmt.Errorf("plugin %q: decode resources/read: %w", c.name, err) |
| 67 | } |
| 68 | var sb strings.Builder |
| 69 | for _, ct := range out.Contents { |
| 70 | if sb.Len() > 0 { |
| 71 | sb.WriteString("\n\n") |
| 72 | } |
| 73 | switch { |
| 74 | case ct.Text != "": |
| 75 | sb.WriteString(ct.Text) |
| 76 | case ct.Blob != "": |
| 77 | fmt.Fprintf(&sb, "[binary resource %s, %s — %d base64 bytes omitted]", ct.URI, ct.MimeType, len(ct.Blob)) |
| 78 | } |
| 79 | } |
| 80 | return sb.String(), nil |
| 81 | } |
| 82 |