-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathhandler.go
More file actions
288 lines (237 loc) · 10.2 KB
/
Copy pathhandler.go
File metadata and controls
288 lines (237 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
// Tencent is pleased to support the open source community by making trpc-mcp-go available.
//
// Copyright (C) 2025 Tencent. All rights reserved.
//
// trpc-mcp-go is licensed under the Apache License Version 2.0.
package mcp
import (
"context"
)
const (
// defaultServerName is the default name for the server
defaultServerName = "Go-MCP-Server"
// defaultServerVersion is the default version for the server
defaultServerVersion = "0.1.0"
)
// HandlerFunc defines a simplified handler function for middleware.
// It uses only ctx and req parameters, session can be retrieved from ctx using ClientSessionFromContext.
type HandlerFunc func(ctx context.Context, req *JSONRPCRequest) (JSONRPCMessage, error)
// Middleware defines a function that wraps a HandlerFunc to add cross-cutting concerns.
// Middlewares can be chained together to form a processing pipeline.
type Middleware func(next HandlerFunc) HandlerFunc
// handler interface defines the MCP protocol handler
type handler interface {
// HandleRequest processes requests
handleRequest(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error)
// HandleNotification processes notifications.
handleNotification(ctx context.Context, notification *JSONRPCNotification, session Session) error
}
// mcpHandler implements the default MCP protocol handler
type mcpHandler struct {
// Tool manager
toolManager *toolManager
// Lifecycle manager
lifecycleManager *lifecycleManager
// Resource manager
resourceManager *resourceManager
// Prompt manager
promptManager *promptManager
// Server reference for notification handling.
server serverNotificationDispatcher
// Middleware chain for request processing.
middlewares []Middleware
}
// serverNotificationDispatcher defines the interface for dispatching notifications to handlers.
type serverNotificationDispatcher interface {
// handleServerNotification dispatches a notification to registered handlers.
handleServerNotification(ctx context.Context, notification *JSONRPCNotification) error
}
// withServer sets the server reference.
func withServer(server serverNotificationDispatcher) func(*mcpHandler) {
return func(h *mcpHandler) {
h.server = server
}
}
// newMCPHandler creates an MCP protocol handler
func newMCPHandler(options ...func(*mcpHandler)) *mcpHandler {
h := &mcpHandler{}
// Apply options
for _, option := range options {
option(h)
}
// Create default managers if not set
if h.toolManager == nil {
h.toolManager = newToolManager()
}
// Create default resource and prompt managers if not set
if h.resourceManager == nil {
h.resourceManager = newResourceManager()
}
if h.promptManager == nil {
h.promptManager = newPromptManager()
}
if h.lifecycleManager == nil {
h.lifecycleManager = newLifecycleManager(Implementation{
Name: defaultServerName,
Version: defaultServerVersion,
})
}
// Pass managers to lifecycle manager
h.lifecycleManager.withToolManager(h.toolManager)
h.lifecycleManager.withResourceManager(h.resourceManager)
h.lifecycleManager.withPromptManager(h.promptManager)
return h
}
// withToolManager sets the tool manager
func withToolManager(manager *toolManager) func(*mcpHandler) {
return func(h *mcpHandler) {
h.toolManager = manager
}
}
// withLifecycleManager sets the lifecycle manager
func withLifecycleManager(manager *lifecycleManager) func(*mcpHandler) {
return func(h *mcpHandler) {
h.lifecycleManager = manager
}
}
// withResourceManager sets the resource manager
func withResourceManager(manager *resourceManager) func(*mcpHandler) {
return func(h *mcpHandler) {
h.resourceManager = manager
}
}
// withPromptManager sets the prompt manager
func withPromptManager(manager *promptManager) func(*mcpHandler) {
return func(h *mcpHandler) {
h.promptManager = manager
}
}
// Definition: request dispatch table type
type requestHandlerFunc func(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error)
// Initialization: request dispatch table
func (h *mcpHandler) requestDispatchTable() map[string]requestHandlerFunc {
return map[string]requestHandlerFunc{
MethodInitialize: h.handleInitialize,
MethodPing: h.handlePing,
MethodToolsList: h.handleToolsList,
MethodToolsCall: h.handleToolsCall,
MethodResourcesList: h.handleResourcesList,
MethodResourcesRead: h.handleResourcesRead,
MethodResourcesTemplatesList: h.handleResourcesTemplatesList,
MethodResourcesSubscribe: h.handleResourcesSubscribe,
MethodResourcesUnsubscribe: h.handleResourcesUnsubscribe,
MethodPromptsList: h.handlePromptsList,
MethodPromptsGet: h.handlePromptsGet,
MethodCompletionComplete: h.handleCompletionComplete,
}
}
// handleRequest processes a JSON-RPC request with optional middleware support.
// If middlewares are registered, it adapts the request to use the simplified HandlerFunc signature.
func (h *mcpHandler) handleRequest(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
// If middlewares are registered, use the middleware chain.
if len(h.middlewares) > 0 {
// Create core handler that adapts from HandlerFunc (2 params) to internal handler (3 params).
coreHandler := func(ctx context.Context, req *JSONRPCRequest) (JSONRPCMessage, error) {
// Try to get session from context (should already be injected by outer layer).
sessionFromCtx := ClientSessionFromContext(ctx)
if sessionFromCtx != nil {
// Use session from context.
return h.dispatchRequest(ctx, req, sessionFromCtx)
}
// Fallback: use session parameter (for backward compatibility).
return h.dispatchRequest(ctx, req, session)
}
// Apply middleware chain (with read lock).
wrappedHandler := h.applyMiddlewares(coreHandler)
// Execute with simplified signature (only ctx and req).
return wrappedHandler(ctx, req)
}
// No middlewares: use original dispatch logic directly.
return h.dispatchRequest(ctx, req, session)
}
// dispatchRequest is the core request dispatcher (original logic).
func (h *mcpHandler) dispatchRequest(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
dispatchTable := h.requestDispatchTable()
if handler, ok := dispatchTable[req.Method]; ok {
return handler(ctx, req, session)
}
return newJSONRPCErrorResponse(req.ID, ErrCodeMethodNotFound, "method not found", nil), nil
}
// applyMiddlewares applies the middleware chain to a handler.
// Middlewares are applied in reverse order (last registered = outermost layer).
func (h *mcpHandler) applyMiddlewares(handler HandlerFunc) HandlerFunc {
// Apply from last to first (onion model).
for i := len(h.middlewares) - 1; i >= 0; i-- {
handler = h.middlewares[i](handler)
}
return handler
}
// use registers a middleware to the handler.
// This is only called during initialization (via WithMiddleware option),
// so no locking is needed.
func (h *mcpHandler) use(middleware Middleware) {
h.middlewares = append(h.middlewares, middleware)
}
// Private methods for each case branch
func (h *mcpHandler) handleInitialize(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.lifecycleManager.handleInitialize(ctx, req, session)
}
func (h *mcpHandler) handlePing(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return map[string]interface{}{}, nil
}
func (h *mcpHandler) handleToolsList(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.toolManager.handleListTools(ctx, req, session)
}
func (h *mcpHandler) handleToolsCall(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.toolManager.handleCallTool(ctx, req, session)
}
func (h *mcpHandler) handleResourcesList(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.resourceManager.handleListResources(ctx, req)
}
func (h *mcpHandler) handleResourcesRead(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.resourceManager.handleReadResource(ctx, req)
}
func (h *mcpHandler) handleResourcesTemplatesList(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.resourceManager.handleListTemplates(ctx, req)
}
func (h *mcpHandler) handleResourcesSubscribe(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.resourceManager.handleSubscribe(ctx, req)
}
func (h *mcpHandler) handleResourcesUnsubscribe(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.resourceManager.handleUnsubscribe(ctx, req)
}
func (h *mcpHandler) handlePromptsList(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.promptManager.handleListPrompts(ctx, req)
}
func (h *mcpHandler) handlePromptsGet(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.promptManager.handleGetPrompt(ctx, req)
}
func (h *mcpHandler) handleCompletionComplete(ctx context.Context, req *JSONRPCRequest, session Session) (JSONRPCMessage, error) {
return h.promptManager.handleCompletionComplete(ctx, req)
}
// handleNotification implements the handler interface's handleNotification method
func (h *mcpHandler) handleNotification(ctx context.Context, notification *JSONRPCNotification, session Session) error {
// Dispatch notification based on method
switch notification.Method {
case MethodNotificationsInitialized:
if err := h.lifecycleManager.handleInitialized(ctx, notification, session); err != nil {
return err
}
// Then also dispatch to any custom handler if registered.
if h.server != nil {
return h.server.handleServerNotification(ctx, notification)
}
return nil
default:
// For other notifications, dispatch to server if available.
if h.server != nil {
return h.server.handleServerNotification(ctx, notification)
}
return nil
}
}
// onSessionTerminated implements the sessionEventNotifier interface's OnSessionTerminated method
func (h *mcpHandler) onSessionTerminated(sessionID string) {
// Notify lifecycle manager that session has terminated
h.lifecycleManager.onSessionTerminated(sessionID)
}