Skip to content

Commit 3356446

Browse files
committed
Merge branch 'main' into http-sampling-improvements and resolve conflicts
2 parents e65ac96 + 1737192 commit 3356446

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+5907
-499
lines changed

.github/workflows/release.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ jobs:
1212
uses: actions/checkout@v3
1313

1414
- name: Create GitHub Release
15+
id: create_release
1516
uses: actions/create-release@v1
1617
env:
1718
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -20,3 +21,38 @@ jobs:
2021
release_name: Release ${{ github.ref }}
2122
draft: false
2223
prerelease: false
24+
25+
- name: Send Discord Notification
26+
if: success()
27+
env:
28+
DISCORD_WEBHOOK: ${{ secrets.RELEASES_WEBHOOK }}
29+
TAG_NAME: ${{ github.ref_name }}
30+
RELEASE_URL: ${{ steps.create_release.outputs.html_url }}
31+
run: |
32+
curl -H "Content-Type: application/json" \
33+
-X POST \
34+
-d "{
35+
\"embeds\": [{
36+
\"title\": \"🚀 New Release: $TAG_NAME\",
37+
\"description\": \"A new version of mcp-go has been released!\",
38+
\"color\": 5814783,
39+
\"fields\": [
40+
{
41+
\"name\": \"Version\",
42+
\"value\": \"$TAG_NAME\",
43+
\"inline\": true
44+
},
45+
{
46+
\"name\": \"Repository\",
47+
\"value\": \"[mcp-go](https://github.com/${{ github.repository }})\",
48+
\"inline\": true
49+
}
50+
],
51+
\"footer\": {
52+
\"text\": \"Released via GitHub Actions\"
53+
},
54+
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\",
55+
\"url\": \"$RELEASE_URL\"
56+
}]
57+
}" \
58+
$DISCORD_WEBHOOK

client/client.go

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package client
33
import (
44
"context"
55
"encoding/json"
6-
"errors"
76
"fmt"
87
"slices"
98
"sync"
@@ -25,6 +24,7 @@ type Client struct {
2524
serverCapabilities mcp.ServerCapabilities
2625
protocolVersion string
2726
samplingHandler SamplingHandler
27+
elicitationHandler ElicitationHandler
2828
}
2929

3030
type ClientOption func(*Client)
@@ -44,6 +44,14 @@ func WithSamplingHandler(handler SamplingHandler) ClientOption {
4444
}
4545
}
4646

47+
// WithElicitationHandler sets the elicitation handler for the client.
48+
// When set, the client will declare elicitation capability during initialization.
49+
func WithElicitationHandler(handler ElicitationHandler) ClientOption {
50+
return func(c *Client) {
51+
c.elicitationHandler = handler
52+
}
53+
}
54+
4755
// WithSession assumes a MCP Session has already been initialized
4856
func WithSession() ClientOption {
4957
return func(c *Client) {
@@ -77,9 +85,16 @@ func (c *Client) Start(ctx context.Context) error {
7785
if c.transport == nil {
7886
return fmt.Errorf("transport is nil")
7987
}
80-
err := c.transport.Start(ctx)
81-
if err != nil {
82-
return err
88+
89+
if _, ok := c.transport.(*transport.Stdio); !ok {
90+
// the stdio transport from NewStdioMCPClientWithOptions
91+
// is already started, dont start again.
92+
//
93+
// Start the transport for other transport types
94+
err := c.transport.Start(ctx)
95+
if err != nil {
96+
return err
97+
}
8398
}
8499

85100
c.transport.SetNotificationHandler(func(notification mcp.JSONRPCNotification) {
@@ -150,7 +165,7 @@ func (c *Client) sendRequest(
150165
}
151166

152167
if response.Error != nil {
153-
return nil, errors.New(response.Error.Message)
168+
return nil, response.Error.AsError()
154169
}
155170

156171
return &response.Result, nil
@@ -167,6 +182,10 @@ func (c *Client) Initialize(
167182
if c.samplingHandler != nil {
168183
capabilities.Sampling = &struct{}{}
169184
}
185+
// Add elicitation capability if handler is configured
186+
if c.elicitationHandler != nil {
187+
capabilities.Elicitation = &struct{}{}
188+
}
170189

171190
// Ensure we send a params object with all required fields
172191
params := struct {
@@ -451,11 +470,15 @@ func (c *Client) Complete(
451470
}
452471

453472
// handleIncomingRequest processes incoming requests from the server.
454-
// This is the main entry point for server-to-client requests like sampling.
473+
// This is the main entry point for server-to-client requests like sampling and elicitation.
455474
func (c *Client) handleIncomingRequest(ctx context.Context, request transport.JSONRPCRequest) (*transport.JSONRPCResponse, error) {
456475
switch request.Method {
457476
case string(mcp.MethodSamplingCreateMessage):
458477
return c.handleSamplingRequestTransport(ctx, request)
478+
case string(mcp.MethodElicitationCreate):
479+
return c.handleElicitationRequestTransport(ctx, request)
480+
case string(mcp.MethodPing):
481+
return c.handlePingRequestTransport(ctx, request)
459482
default:
460483
return nil, fmt.Errorf("unsupported request method: %s", request.Method)
461484
}
@@ -516,14 +539,60 @@ func (c *Client) handleSamplingRequestTransport(ctx context.Context, request tra
516539
}
517540

518541
// Create the transport response
519-
response := &transport.JSONRPCResponse{
520-
JSONRPC: mcp.JSONRPC_VERSION,
521-
ID: request.ID,
522-
Result: json.RawMessage(resultBytes),
542+
response := transport.NewJSONRPCResultResponse(request.ID, json.RawMessage(resultBytes))
543+
544+
return response, nil
545+
}
546+
547+
// handleElicitationRequestTransport handles elicitation requests at the transport level.
548+
func (c *Client) handleElicitationRequestTransport(ctx context.Context, request transport.JSONRPCRequest) (*transport.JSONRPCResponse, error) {
549+
if c.elicitationHandler == nil {
550+
return nil, fmt.Errorf("no elicitation handler configured")
523551
}
524552

553+
// Parse the request parameters
554+
var params mcp.ElicitationParams
555+
if request.Params != nil {
556+
paramsBytes, err := json.Marshal(request.Params)
557+
if err != nil {
558+
return nil, fmt.Errorf("failed to marshal params: %w", err)
559+
}
560+
if err := json.Unmarshal(paramsBytes, &params); err != nil {
561+
return nil, fmt.Errorf("failed to unmarshal params: %w", err)
562+
}
563+
}
564+
565+
// Create the MCP request
566+
mcpRequest := mcp.ElicitationRequest{
567+
Request: mcp.Request{
568+
Method: string(mcp.MethodElicitationCreate),
569+
},
570+
Params: params,
571+
}
572+
573+
// Call the elicitation handler
574+
result, err := c.elicitationHandler.Elicit(ctx, mcpRequest)
575+
if err != nil {
576+
return nil, err
577+
}
578+
579+
// Marshal the result
580+
resultBytes, err := json.Marshal(result)
581+
if err != nil {
582+
return nil, fmt.Errorf("failed to marshal result: %w", err)
583+
}
584+
585+
// Create the transport response
586+
response := transport.NewJSONRPCResultResponse(request.ID, resultBytes)
587+
525588
return response, nil
526589
}
590+
591+
func (c *Client) handlePingRequestTransport(ctx context.Context, request transport.JSONRPCRequest) (*transport.JSONRPCResponse, error) {
592+
b, _ := json.Marshal(&mcp.EmptyResult{})
593+
return transport.NewJSONRPCResultResponse(request.ID, b), nil
594+
}
595+
527596
func listByPage[T any](
528597
ctx context.Context,
529598
client *Client,

client/elicitation.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package client
2+
3+
import (
4+
"context"
5+
6+
"github.com/mark3labs/mcp-go/mcp"
7+
)
8+
9+
// ElicitationHandler defines the interface for handling elicitation requests from servers.
10+
// Clients can implement this interface to request additional information from users.
11+
type ElicitationHandler interface {
12+
// Elicit handles an elicitation request from the server and returns the user's response.
13+
// The implementation should:
14+
// 1. Present the request message to the user
15+
// 2. Validate input against the requested schema
16+
// 3. Allow the user to accept, decline, or cancel
17+
// 4. Return the appropriate response
18+
Elicit(ctx context.Context, request mcp.ElicitationRequest) (*mcp.ElicitationResult, error)
19+
}

0 commit comments

Comments
 (0)