Skip to content

feature: Support log/cancelled/progress notification #329

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

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
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
56 changes: 46 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func main() {
}
}

func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
func helloHandler(ctx context.Context, requestSession server.RequestSession, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
name, err := request.RequireString("name")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
Expand Down Expand Up @@ -92,6 +92,7 @@ MCP Go handles all the complex protocol details and server management, so you ca
- [Resources](#resources)
- [Tools](#tools)
- [Prompts](#prompts)
- [RequestSession](#requestSession)
- [Examples](#examples)
- [Extras](#extras)
- [Transports](#transports)
Expand Down Expand Up @@ -153,7 +154,7 @@ func main() {
)

// Add the calculator handler
s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
s.AddTool(calculatorTool, func(ctx context.Context, requestSession server.RequestSession, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Using helper functions for type-safe argument access
op, err := request.RequireString("operation")
if err != nil {
Expand Down Expand Up @@ -251,7 +252,7 @@ resource := mcp.NewResource(
)

// Add resource with its handler
s.AddResource(resource, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
s.AddResource(resource, func(ctx context.Context, requestSession server.RequestSession, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
content, err := os.ReadFile("README.md")
if err != nil {
return nil, err
Expand Down Expand Up @@ -279,7 +280,7 @@ template := mcp.NewResourceTemplate(
)

// Add template with its handler
s.AddResourceTemplate(template, func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
s.AddResourceTemplate(template, func(ctx context.Context, requestSession server.RequestSession, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
// Extract ID from the URI using regex matching
// The server automatically matches URIs to templates
userID := extractIDFromURI(request.Params.URI)
Expand Down Expand Up @@ -328,7 +329,7 @@ calculatorTool := mcp.NewTool("calculate",
),
)

s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
s.AddTool(calculatorTool, func(ctx context.Context, requestSession server.RequestSession, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args := request.GetArguments()
op := args["operation"].(string)
x := args["x"].(float64)
Expand Down Expand Up @@ -372,7 +373,7 @@ httpTool := mcp.NewTool("http_request",
),
)

s.AddTool(httpTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
s.AddTool(httpTool, func(ctx context.Context, requestSession server.RequestSession, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args := request.GetArguments()
method := args["method"].(string)
url := args["url"].(string)
Expand Down Expand Up @@ -440,7 +441,7 @@ s.AddPrompt(mcp.NewPrompt("greeting",
mcp.WithArgument("name",
mcp.ArgumentDescription("Name of the person to greet"),
),
), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
), func(ctx context.Context, requestSession server.RequestSession, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
name := request.Params.Arguments["name"]
if name == "" {
name = "friend"
Expand All @@ -464,7 +465,7 @@ s.AddPrompt(mcp.NewPrompt("code_review",
mcp.ArgumentDescription("Pull request number to review"),
mcp.RequiredArgument(),
),
), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
), func(ctx context.Context, requestSession server.RequestSession, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
prNumber := request.Params.Arguments["pr_number"]
if prNumber == "" {
return nil, fmt.Errorf("pr_number is required")
Expand Down Expand Up @@ -495,7 +496,7 @@ s.AddPrompt(mcp.NewPrompt("query_builder",
mcp.ArgumentDescription("Name of the table to query"),
mcp.RequiredArgument(),
),
), func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
), func(ctx context.Context, requestSession server.RequestSession, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
tableName := request.Params.Arguments["table"]
if tableName == "" {
return nil, fmt.Errorf("table name is required")
Expand Down Expand Up @@ -530,6 +531,41 @@ Prompts can include:

</details>

### requestSession
<details>
<summary>Show RequestSession Examples</summary>

The RequestSession object provides capabilities to interact with the client, such as sending logging notification and progress notification.

```go
// Example of using RequestSession to send logging notifications and progress notifications
mcpServer.AddTool(mcp.NewTool(
"test-RequestSession",
mcp.WithDescription("test RequestSession"),
), func(ctx context.Context, requestSession server.RequestSession, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// you could invoke `requestSession.IsLoggingNotificationSupported()` first the check if server supports logging notification
// if server does not support logging notification, this method will do nothing.
_ = requestSession.SendLoggingNotification(ctx, mcp.LoggingLevelInfo, map[string]any{
"testLog": "test send log notification",
})

// server should send progress notification if request metadata includes a progressToken
total := float64(100)
progressMessage := "human readable progress information"
_ = requestSession.SendProgressNotification(ctx, float64(50), &total, &progressMessage)

return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: "context from header: " + ctx.Value(testHeaderKey).(string) + ", " + ctx.Value(testHeaderFuncKey).(string),
},
},
}, nil
})
```
</details>

## Examples

For examples, see the [`examples/`](examples/) directory.
Expand Down Expand Up @@ -723,7 +759,7 @@ s := server.NewMCPServer(
The session context is automatically passed to tool and resource handlers:

```go
s.AddTool(mcp.NewTool("session_aware"), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
s.AddTool(mcp.NewTool("session_aware"), func(ctx context.Context, requestSession server.RequestSession, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Get the current session from context
session := server.ClientSessionFromContext(ctx)
if session == nil {
Expand Down
1 change: 1 addition & 0 deletions client/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestHTTPClient(t *testing.T) {
mcp.NewTool("notify"),
func(
ctx context.Context,
requestSession server.RequestSession,
request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
server := server.ServerFromContext(ctx)
Expand Down
6 changes: 3 additions & 3 deletions client/inprocess_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestInProcessMCPClient(t *testing.T) {
mcp.WithDestructiveHintAnnotation(false),
mcp.WithIdempotentHintAnnotation(true),
mcp.WithOpenWorldHintAnnotation(false),
), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
), func(ctx context.Context, requestSession server.RequestSession, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Expand All @@ -48,7 +48,7 @@ func TestInProcessMCPClient(t *testing.T) {
URI: "resource://testresource",
Name: "My Resource",
},
func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
func(ctx context.Context, requestSession server.RequestSession, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: "resource://testresource",
Expand All @@ -70,7 +70,7 @@ func TestInProcessMCPClient(t *testing.T) {
},
},
},
func(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
func(ctx context.Context, requestSession server.RequestSession, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
return &mcp.GetPromptResult{
Messages: []mcp.PromptMessage{
{
Expand Down
Loading
Loading