Skip to content

Commit fdae66b

Browse files
committed
internal/mcp: design.md: polishing
Various typo and style changes. Change-Id: Ifd4322dcf0bf2b2653bb38a023a51056424cca39 Reviewed-on: https://go-review.googlesource.com/c/tools/+/672815 Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Sam Thanawalla <[email protected]> Commit-Queue: Jonathan Amsterdam <[email protected]> TryBot-Bypass: Jonathan Amsterdam <[email protected]>
1 parent 5eb0d1f commit fdae66b

File tree

1 file changed

+36
-47
lines changed

1 file changed

+36
-47
lines changed

internal/mcp/design/design.md

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Go MCP SDK design
22

3-
This file discusses the design of a Go SDK for the [model context
3+
This document discusses the design of a Go SDK for the [model context
44
protocol](https://modelcontextprotocol.io/specification/2025-03-26). It is
55
intended to seed a GitHub discussion about the official Go MCP SDK.
66

@@ -18,7 +18,7 @@ writing, it is imported by over 400 packages that span over 200 modules.
1818
We admire mcp-go, and seriously considered simply adopting it as a starting
1919
point for this SDK. However, as we looked at doing so, we realized that a
2020
significant amount of its API would probably need to change. In some cases,
21-
mcp-go has older APIs that predated newer variations--an obvious opportunity
21+
mcp-go has older APIs that predated newer variationsan obvious opportunity
2222
for cleanup. In others, it took a batteries-included approach that is probably
2323
not viable for an official SDK. In yet others, we simply think there is room for
2424
API refinement, and we should take this opportunity to consider our options.
@@ -332,7 +332,7 @@ marshalling/unmarshalling can be delegated to the business logic of the client
332332
or server.
333333
334334
For union types, which can't be represented in Go (specifically `Content` and
335-
`Resource`), we prefer distinguished unions: struct types with fields
335+
`ResourceContents`), we prefer distinguished unions: struct types with fields
336336
corresponding to the union of all properties for union elements.
337337
338338
For brevity, only a few examples are shown here:
@@ -353,11 +353,11 @@ type CallToolResult struct {
353353
// The Type field distinguishes the type of the content.
354354
// At most one of Text, MIMEType, Data, and Resource is non-zero.
355355
type Content struct {
356-
Type string `json:"type"`
357-
Text string `json:"text,omitempty"`
358-
MIMEType string `json:"mimeType,omitempty"`
359-
Data []byte `json:"data,omitempty"`
360-
Resource *Resource `json:"resource,omitempty"`
356+
Type string `json:"type"`
357+
Text string `json:"text,omitempty"`
358+
MIMEType string `json:"mimeType,omitempty"`
359+
Data []byte `json:"data,omitempty"`
360+
Resource *ResourceContents `json:"resource,omitempty"`
361361
}
362362
```
363363
@@ -386,7 +386,7 @@ change.
386386
387387
Following the terminology of the
388388
[spec](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management),
389-
we call the logical connection between a client and server a "session". There
389+
we call the logical connection between a client and server a "session." There
390390
must necessarily be a `ClientSession` and a `ServerSession`, corresponding to
391391
the APIs available from the client and server perspective, respectively.
392392
@@ -436,7 +436,7 @@ transport := mcp.NewCommandTransport(exec.Command("myserver"))
436436
session, err := client.Connect(ctx, transport)
437437
if err != nil { ... }
438438
// Call a tool on the server.
439-
content, err := session.CallTool(ctx, "greet", map[string]any{"name": "you"}, nil})
439+
content, err := session.CallTool(ctx, "greet", map[string]any{"name": "you"}, nil)
440440
...
441441
return session.Close()
442442
```
@@ -446,7 +446,7 @@ A server that can handle that client call would look like this:
446446
```go
447447
// Create a server with a single tool.
448448
server := mcp.NewServer("greeter", "v1.0.0", nil)
449-
server.AddTool(mcp.NewTool("greet", "say hi", SayHi))
449+
server.AddTools(mcp.NewTool("greet", "say hi", SayHi))
450450
// Run the server over stdin/stdout, until the client disconnects.
451451
transport := mcp.NewStdIOTransport()
452452
session, err := server.Connect(ctx, transport)
@@ -541,8 +541,8 @@ func (*ClientSession) ResourceTemplates(context.Context, *ListResourceTemplatesP
541541

542542
### Middleware
543543

544-
We provide a mechanism to add MCP-level middleware, which runs after the
545-
request has been parsed, but before any normal handling.
544+
We provide a mechanism to add MCP-level middleware on the server side, which runs after the
545+
request has been parsed but before any normal handling.
546546

547547
```go
548548
// A Dispatcher dispatches an MCP message to the appropriate handler.
@@ -551,14 +551,14 @@ request has been parsed, but before any normal handling.
551551
type Dispatcher func(ctx context.Context, s *ServerSession, method string, params any) (result any, err error)
552552
553553
// AddDispatchers calls each function from right to left on the previous result, beginning
554-
// with the server's current dispatcher, and installs the result as the new handler.
555-
func (*Server) AddDispatchers(middleware ...func(Handler) Handler))
554+
// with the server's current dispatcher, and installs the result as the new dispatcher.
555+
func (*Server) AddDispatchers(middleware ...func(Dispatcher) Dispatcher))
556556
```
557557

558558
As an example, this code adds server-side logging:
559559

560560
```go
561-
func withLogging(h mcp.Handler) mcp.Handler {
561+
func withLogging(h mcp.Dispatcher) mcp.Dispatcher {
562562
return func(ctx context.Context, s *mcp.ServerSession, method string, params any) (res any, err error) {
563563
log.Printf("request: %s %v", method, params)
564564
defer func() { log.Printf("response: %v, %v", res, err) }()
@@ -621,11 +621,9 @@ The server observes a client cancellation as a cancelled context.
621621
A caller can request progress notifications by setting the `ProgressToken` field on any request.
622622

623623
```go
624-
type ProgressToken any // string or int
625-
626624
type XXXParams struct { // where XXX is each type of call
627625
...
628-
ProgressToken ProgressToken
626+
ProgressToken any // string or int
629627
}
630628
```
631629

@@ -681,15 +679,15 @@ Roots can be added and removed from a `Client` with `AddRoots` and `RemoveRoots`
681679
// AddRoots adds the given roots to the client,
682680
// replacing any with the same URIs,
683681
// and notifies any connected servers.
684-
func (*Client) AddRoots(roots ...Root)
682+
func (*Client) AddRoots(roots ...*Root)
685683
686684
// RemoveRoots removes the roots with the given URIs.
687685
// and notifies any connected servers if the list has changed.
688686
// It is not an error to remove a nonexistent root.
689687
func (*Client) RemoveRoots(uris ...string)
690688
```
691689

692-
Servers can call the spec method `ListRoots` to get the roots. If a server installs a
690+
Server sessions can call the spec method `ListRoots` to get the roots. If a server installs a
693691
`RootsChangedHandler`, it will be called when the client sends a roots-changed
694692
notification, which happens whenever the list of roots changes after a
695693
connection has been established.
@@ -705,7 +703,7 @@ type ServerOptions {
705703
### Sampling
706704

707705
Clients that support sampling are created with a `CreateMessageHandler` option
708-
for handling server calls. To perform sampling, a server calls the spec method `CreateMessage`.
706+
for handling server calls. To perform sampling, a server session calls the spec method `CreateMessage`.
709707

710708
```go
711709
type ClientOptions struct {
@@ -742,7 +740,7 @@ Add tools to a server with `AddTools`:
742740
```go
743741
server.AddTools(
744742
mcp.NewTool("add", "add numbers", addHandler),
745-
mcp.NewTools("subtract, subtract numbers", subHandler))
743+
mcp.NewTool("subtract, subtract numbers", subHandler))
746744
```
747745

748746
Remove them by name with `RemoveTools`:
@@ -836,7 +834,7 @@ Schemas are validated on the server before the tool handler is called.
836834
Since all the fields of the Tool struct are exported, a Tool can also be created
837835
directly with assignment or a struct literal.
838836

839-
Clients can call the spec method `ListTools` or an iterator method `Tools`
837+
Client sessions can call the spec method `ListTools` or an iterator method `Tools`
840838
to list the available tools.
841839

842840
**Differences from mcp-go**: using variadic options to configure tools was
@@ -862,7 +860,7 @@ each occur only once (and in an SDK that wraps mcp-go).
862860
863861
For registering tools, we provide only `AddTools`; mcp-go's `SetTools`,
864862
`AddTool`, `AddSessionTool`, and `AddSessionTools` are deemed unnecessary.
865-
(similarly for Delete/Remove).
863+
(Similarly for Delete/Remove).
866864

867865
### Prompts
868866

@@ -893,8 +891,8 @@ server.AddPrompts(
893891
server.RemovePrompts("code_review")
894892
```
895893

896-
Clients can call the spec method `ListPrompts` or an iterator method `Prompts`
897-
to list the available prompts and the spec method `GetPrompt` to get one.
894+
Client sessions can call the spec method `ListPrompts` or the iterator method `Prompts`
895+
to list the available prompts, and the spec method `GetPrompt` to get one.
898896

899897
**Differences from mcp-go**: We provide a `NewPrompt` helper to bind a prompt
900898
handler to a Go function using reflection to derive its arguments. We provide
@@ -926,7 +924,8 @@ type ServerResourceTemplate struct {
926924
```
927925
928926
To add a resource or resource template to a server, users call the `AddResources` and
929-
`AddResourceTemplates` methods with one or more `ServerResource`s or `ServerResourceTemplate`s:
927+
`AddResourceTemplates` methods with one or more `ServerResource`s or `ServerResourceTemplate`s.
928+
We also provide methods to remove them.
930929
931930
```go
932931
func (*Server) AddResources(...*ServerResource)
@@ -938,14 +937,12 @@ func (s *Server) RemoveResourceTemplates(uriTemplates ...string)
938937
939938
The `ReadResource` method finds a resource or resource template matching the argument URI and calls
940939
its assocated handler.
941-
If the argument URI matches a template, the `Resource` argument to the handler is constructed
942-
from the fields in the `ResourceTemplate`.
943940
944941
To read files from the local filesystem, we recommend using `FileResourceHandler` to construct a handler:
945942
```go
946943
// FileResourceHandler returns a ResourceHandler that reads paths using dir as a root directory.
947944
// It protects against path traversal attacks.
948-
// It will not read any file that is not in the root set of the client requesting the resource.
945+
// It will not read any file that is not in the root set of the client session requesting the resource.
949946
func (*Server) FileResourceHandler(dir string) ResourceHandler
950947
```
951948
Here is an example:
@@ -957,17 +954,8 @@ s.AddResources(&mcp.ServerResource{
957954
Handler: s.FileReadResourceHandler("/public")})
958955
```
959956
960-
Servers support all of the resource-related spec methods:
961-
962-
- `ListResources` and `ListResourceTemplates` for listings.
963-
- `ReadResource` to get the contents of a resource.
964-
- `Subscribe` and `Unsubscribe` to manage subscriptions on resources.
965-
966-
We also provide iterator methods `Resources` and `ResourceTemplates`.
967-
968-
`ReadResource` checks the incoming URI against the server's list of
969-
resources and resource templates to make sure it matches one of them,
970-
then returns the result of calling the associated reader function.
957+
Server sessions also support the spec methods `ListResources` and `ListResourceTemplates`,
958+
and the corresponding iterator methods `Resources` and `ResourceTemplates`.
971959
972960
#### Subscriptions
973961
@@ -1017,6 +1005,7 @@ type ClientOptions struct {
10171005
...
10181006
ToolListChangedHandler func(context.Context, *ClientSession, *ToolListChangedParams)
10191007
PromptListChangedHandler func(context.Context, *ClientSession, *PromptListChangedParams)
1008+
// For both resources and resource templates.
10201009
ResourceListChangedHandler func(context.Context, *ClientSession, *ResourceListChangedParams)
10211010
}
10221011
```
@@ -1049,8 +1038,8 @@ type ServerOptions {
10491038
}
10501039
```
10511040

1052-
ServerSessions have access to a `slog.Logger` that writes to the client. A call to
1053-
a log method like `Info` is translated to a `LoggingMessageNotification` as
1041+
Server sessions have a field `Logger` holding a `slog.Logger` that writes to the client session.
1042+
A call to a log method like `Info` is translated to a `LoggingMessageNotification` as
10541043
follows:
10551044

10561045
- The attributes and the message populate the "data" property with the
@@ -1060,11 +1049,11 @@ follows:
10601049
- If the `LoggerName` server option is set, it populates the "logger" property.
10611050

10621051
- The standard slog levels `Info`, `Debug`, `Warn` and `Error` map to the
1063-
corresponding levels in the MCP spec. The other spec levels will be mapped
1052+
corresponding levels in the MCP spec. The other spec levels map
10641053
to integers between the slog levels. For example, "notice" is level 2 because
10651054
it is between "warning" (slog value 4) and "info" (slog value 0).
10661055
The `mcp` package defines consts for these levels. To log at the "notice"
1067-
level, a handler would call `session.Log(ctx, mcp.LevelNotice, "message")`.
1056+
level, a handler would call `session.Logger.Log(ctx, mcp.LevelNotice, "message")`.
10681057

10691058
A client that wishes to receive log messages must provide a handler:
10701059

@@ -1080,7 +1069,7 @@ type ClientOptions struct {
10801069
Servers initiate pagination for `ListTools`, `ListPrompts`, `ListResources`,
10811070
and `ListResourceTemplates`, dictating the page size and providing a
10821071
`NextCursor` field in the Result if more pages exist. The SDK implements keyset
1083-
pagination, using the `unique ID` as the key for a stable sort order and encoding
1072+
pagination, using the unique ID of the feature as the key for a stable sort order and encoding
10841073
the cursor as an opaque string.
10851074

10861075
For server implementations, the page size for the list operation may be

0 commit comments

Comments
 (0)