-
Notifications
You must be signed in to change notification settings - Fork 26
mcp: add sequential thinking server example #51
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
base: main
Are you sure you want to change the base?
mcp: add sequential thinking server example #51
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: Though we might not need to prepare for README.md for each example, I created this file, just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's definitely helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. I appreciate your input.
- This example shows dynamic and reflective problem-solving through structured thinking processes. - The server provides tools for starting thinking sessions, adding sequential thoughts with step tracking, revising previous thoughts, creating alternative reasoning branches, and reviewing complete processes. - Features include thread-safe session management, adaptive planning that adjusts step counts dynamically. - Add comprehensive testing with 8 test functions covering all functionality. For modelcontextprotocol#33
a71abdb
to
9efbd13
Compare
After this lands, it would be great to also add it to the servers repo. See #52. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a first round, focusing on local issues like naming.
I still don't really understand what this is doing; once I read the README more carefully I'll do another pass, probably tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's definitely helpful.
- **Branch relationships**: Links to alternative reasoning paths | ||
- **Status management**: Active, completed, or paused sessions | ||
|
||
## Testing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this section. It is already understood.
examples/sequentialthinking/main.go
Outdated
|
||
var httpAddr = flag.String("http", "", "if set, use streamable HTTP at this address, instead of stdin/stdout") | ||
|
||
// Thought represents a single step in the thinking process |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: period at end of sentence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is my careless. Just updated it.
examples/sequentialthinking/main.go
Outdated
} | ||
} | ||
|
||
func (s *SessionStore) GetSession(id string) (*ThinkingSession, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the Get (idiomatic Go)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the Get by following idiomatic Go.
examples/sequentialthinking/main.go
Outdated
|
||
var httpAddr = flag.String("http", "", "if set, use streamable HTTP at this address, instead of stdin/stdout") | ||
|
||
// Thought represents a single step in the thinking process |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"A Thought is..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the comment by following the format you suggested.
examples/sequentialthinking/main.go
Outdated
session.Status = "completed" | ||
} | ||
|
||
store.SetSession(session) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed It.
examples/sequentialthinking/main.go
Outdated
} | ||
|
||
var review strings.Builder | ||
review.WriteString(fmt.Sprintf("=== Thinking Review: %s ===\n", session.ID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fmt.Fprintf(&review, "...",...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. Updated these parts to using fmt.Fprint in ReviewThinking function.
examples/sequentialthinking/main.go
Outdated
} | ||
|
||
// Resource handler for thinking sessions | ||
func GetThinkingHistory(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove Get
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed it.
examples/sequentialthinking/main.go
Outdated
// Resource handler for thinking sessions | ||
func GetThinkingHistory(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { | ||
// Extract session ID from URI (e.g., "thinking://session_123") | ||
parts := strings.Split(params.URI, "://") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use url.Parse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated it to use url.Parse.
examples/sequentialthinking/main.go
Outdated
|
||
// Add thinking tools | ||
server.AddTools( | ||
mcp.NewServerTool( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this will soon be a bug.
According to the spec, tools with an output schema must provide a StructuredOutput field in the result.
We don't check for that today, but I'm working on the PR.
NewServerTool creates both an input and output schema.
Can you try creating these tools without NewServerTool, using the part of NewServerTool that does input schemas, and leaving out the outputschema? It would be useful to see how complicated that gets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your explanation. I understand the current status of the PR you're working on.
Yes, I can. I tried to implement these tools without NewServerTool. As I may have missed something, I would appreciate it if you could provide me with further feedback on the implementation.
- Remove "Get" prefix from getter methods (idiomatic Go) - Rename Thought.ID to Thought.Index (more accurate) - Change EstimatedTotal and CreateBranch from pointers to plain types - Use zero values as "not provided" sentinel values - Fix struct comments. - Replace strings.Builder with fmt.Fprintf - Use url.Parse instead of strings.Split for URI parsing - Use slices.Collect(maps.Values()) for cleaner code Concurrency fixes: - Implement Copy-on-Write pattern to fix race conditions - Add Version field for optimistic concurrency control - Create CompareAndSwap method for atomic updates - Ensure thread-safe session modifications Tool creation: - Replace NewServerTool with manual tool creation - Avoid output schemas to prevent structured output requirement - Add explicit type conversions for JSON unmarshaling Documentation: - Remove Testing section from README per feedback - Clarify that Thought.Index is per-session, not global - Add godoc comments for all exported types and functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for your feedback. I addressed the feedback. Could you please take a look at it again at your convenience?
examples/sequentialthinking/main.go
Outdated
Thought string `json:"thought"` | ||
NextNeeded *bool `json:"nextNeeded,omitempty"` | ||
ReviseStep *int `json:"reviseStep,omitempty"` | ||
CreateBranch *bool `json:"createBranch,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The defaults I expected are actually:
NextNeeded
: default behavior istrue
for continue thinking.CreateBranch
: default behavior isfalse
for don't create a branch.
Since only CreateBranch doesn't need a pointer in this case, I updated the field from *bool to bool.
examples/sequentialthinking/main.go
Outdated
|
||
sessionID := args.SessionID | ||
if sessionID == "" { | ||
sessionID = fmt.Sprintf("session_%d", time.Now().Unix()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your suggestion. Since I didn't know the randText function, I am glad to have found a way to generate sufficient uniqueness. I copied randText() from util.go.
examples/sequentialthinking/main.go
Outdated
session := &ThinkingSession{ | ||
ID: sessionID, | ||
Problem: args.Problem, | ||
Thoughts: []Thought{}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're totally right. Just omitted the field.
examples/sequentialthinking/main.go
Outdated
ID: sessionID, | ||
Problem: args.Problem, | ||
Thoughts: []Thought{}, | ||
CurrentThought: 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also you're right. Just omitted the field.
examples/sequentialthinking/main.go
Outdated
Status: "active", | ||
Created: time.Now(), | ||
LastActivity: time.Now(), | ||
Branches: []string{}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just omitted the field, too.
examples/sequentialthinking/main.go
Outdated
} | ||
} | ||
|
||
func (s *SessionStore) GetSession(id string) (*ThinkingSession, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the Get by following idiomatic Go.
examples/sequentialthinking/main.go
Outdated
s.sessions[session.ID] = session | ||
} | ||
|
||
func (s *SessionStore) ListSessions() []*ThinkingSession { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the function name to Sessions.
examples/sequentialthinking/main.go
Outdated
func (s *SessionStore) ListSessions() []*ThinkingSession { | ||
s.mu.RLock() | ||
defer s.mu.RUnlock() | ||
sessions := make([]*ThinkingSession, 0, len(s.sessions)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the part to use slices.Clone.
examples/sequentialthinking/main.go
Outdated
|
||
var store = NewSessionStore() | ||
|
||
// Tool argument structures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the doc for each struct respectively.
examples/sequentialthinking/main.go
Outdated
|
||
// Add thinking tools | ||
server.AddTools( | ||
mcp.NewServerTool( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your explanation. I understand the current status of the PR you're working on.
Yes, I can. I tried to implement these tools without NewServerTool. As I may have missed something, I would appreciate it if you could provide me with further feedback on the implementation.
Motivation and Context
This is part of #33. This PR adds example of implementing mcp on sequentialthinking server, from Example Servers.
The PR includes dynamic and reflective problem-solving through structured thinking processes.
The server provides tools for:
Also, features include thread-safe session management, adaptive planning that adjusts step counts dynamically.
How Has This Been Tested?
Testing with 8 test functions covering all functionality with test code.
Breaking Changes
N/A
Types of changes
Checklist
Additional context