Write implements the [Connection] interface.
(ctx context.Context, msg jsonrpc.Message)
| 1779 | |
| 1780 | // Write implements the [Connection] interface. |
| 1781 | func (c *streamableClientConn) Write(ctx context.Context, msg jsonrpc.Message) error { |
| 1782 | if err := c.failure(); err != nil { |
| 1783 | return err |
| 1784 | } |
| 1785 | |
| 1786 | var requestSummary string |
| 1787 | var forCall *jsonrpc.Request |
| 1788 | switch msg := msg.(type) { |
| 1789 | case *jsonrpc.Request: |
| 1790 | requestSummary = fmt.Sprintf("sending %q", msg.Method) |
| 1791 | if msg.IsCall() { |
| 1792 | forCall = msg |
| 1793 | } |
| 1794 | case *jsonrpc.Response: |
| 1795 | requestSummary = fmt.Sprintf("sending jsonrpc response #%d", msg.ID) |
| 1796 | default: |
| 1797 | panic("unreachable") |
| 1798 | } |
| 1799 | |
| 1800 | data, err := jsonrpc.EncodeMessage(msg) |
| 1801 | if err != nil { |
| 1802 | return fmt.Errorf("%s: %v", requestSummary, err) |
| 1803 | } |
| 1804 | |
| 1805 | doRequest := func() (*http.Request, *http.Response, error) { |
| 1806 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.url, bytes.NewReader(data)) |
| 1807 | if err != nil { |
| 1808 | return nil, nil, err |
| 1809 | } |
| 1810 | req.Header.Set("Content-Type", "application/json") |
| 1811 | req.Header.Set("Accept", "application/json, text/event-stream") |
| 1812 | if err := c.setMCPHeaders(req); err != nil { |
| 1813 | // Failure to set headers means that the request was not sent. |
| 1814 | // Wrap with ErrRejected so the jsonrpc2 connection doesn't set writeErr |
| 1815 | // and permanently break the connection. |
| 1816 | return nil, nil, fmt.Errorf("%s: %w: %w", requestSummary, jsonrpc2.ErrRejected, err) |
| 1817 | } |
| 1818 | // Keep this after the setMCPHeaders call to ensure that the |
| 1819 | // protocol version header is set. |
| 1820 | setStandardHeaders(req.Header, msg) |
| 1821 | resp, err := c.client.Do(req) |
| 1822 | if err != nil { |
| 1823 | // Any error from client.Do means the request didn't reach the server. |
| 1824 | // Wrap with ErrRejected so the jsonrpc2 connection doesn't set writeErr |
| 1825 | // and permanently break the connection. |
| 1826 | err = fmt.Errorf("%s: %w: %w", requestSummary, jsonrpc2.ErrRejected, err) |
| 1827 | } |
| 1828 | return req, resp, err |
| 1829 | } |
| 1830 | |
| 1831 | req, resp, err := doRequest() |
| 1832 | if err != nil { |
| 1833 | return err |
| 1834 | } |
| 1835 | |
| 1836 | if (resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden) && c.oauthHandler != nil { |
| 1837 | if err := c.oauthHandler.Authorize(ctx, req, resp); err != nil { |
| 1838 | // If the caller's context was cancelled while we were running the |
nothing calls this directly
no test coverage detected