Skip to content

fix: type mismatch for request/response ID #291

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

Merged
merged 4 commits into from
May 16, 2025

Conversation

pottekkat
Copy link
Collaborator

@pottekkat pottekkat commented May 14, 2025

Description

Note: Using any causes issues with using an int request ID because of how the unmarshal function works (it unmarshals numbers into floats) which will break this for integer IDs. This needs to be fixed before we merge it. I'm working on a fix but I made the PR public so that others can also look into how we can handle this properly.

Use the correct type for the request ID.

Request ID can be an int or a string but right now we handle it as any in mcp.RequestId which is fine. We can come back to it later if needed for more specific type handling.

The int is converted to string for lookups.

You can test the SDK by adding this to your go.mod file:

replace github.com/mark3labs/mcp-go => github.com/pottekkat/mcp-go v0.0.0-20250515063657-25a3e3faf757

Ref: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/c87a0da6d8c2436d56a6398023c80b0562224454/schema/2025-03-26/schema.json#L1533

Fixes #154

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • MCP spec compatibility implementation
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Performance improvement
  • Tests only (no functional changes)
  • Other (please describe):

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly

Summary by CodeRabbit

  • Refactor
    • Enhanced request ID handling by introducing a custom typed ID format across client, server, and tests for improved consistency and robustness without impacting user experience.

Copy link
Contributor

coderabbitai bot commented May 14, 2025

"""

Walkthrough

The changes update the types used for JSON-RPC request and response IDs from raw int64 or *int64 to a custom mcp.RequestId type across the transport interface and implementations. All relevant struct fields, map keys, and related logic are updated accordingly in the interface, SSE, Stdio, streamable HTTP, and server layers, including test code. Additional methods on RequestId support JSON marshaling, unmarshaling, string conversion, and nil checks.

Changes

Files/Paths Change Summary
client/transport/interface.go Changed ID fields in JSONRPCRequest and JSONRPCResponse from int64/*int64 to mcp.RequestId.
client/transport/sse.go Changed responses map key in SSE struct and related initializations from int64 to string (using RequestId.String()). Updated nil checks to use IsNil().
client/transport/stdio.go Changed responses map key in Stdio struct and related initializations from int64 to string. Updated nil checks to use IsNil().
client/transport/streamable_http.go Replaced direct nil checks on ID with calls to IsNil() method in response and notification handling.
client/client.go Wrapped raw integer IDs with mcp.NewRequestId when constructing JSON-RPC requests.
server/server.go Wrapped raw integer IDs with mcp.NewRequestId in JSON-RPC response and error message construction.
server/sse.go Wrapped incremented request ID with mcp.NewRequestId in SSE ping request construction.
testdata/mockstdio_server.go Changed ID fields in JSONRPCRequest and JSONRPCResponse from *int64 to *mcp.RequestId.
client/transport/sse_test.go Updated tests to use mcp.NewRequestId and extract underlying values with type assertions. Added error checks for ID type correctness.
client/transport/stdio_test.go Updated tests to use mcp.NewRequestId for IDs, added type assertion checks, improved error handling, and added test for string ID support.
client/transport/streamable_http_test.go Updated tests to use mcp.NewRequestId and added type assertion checks for IDs in requests and responses.
server/server_test.go Updated tests to use mcp.NewRequestId for request IDs instead of raw integers.
server/sse_test.go Improved type assertion safety for ping message ID in tests, adding fallback and error reporting.
mcp/types.go Changed RequestId from alias of any to struct wrapping any with methods: NewRequestId, Value, String, IsNil, JSON marshal/unmarshal.

Assessment against linked issues

Objective Addressed Explanation
Fix unmarshaling error: allow JSON-RPC .id field to accept string (not just int64) (#154)
"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


Note

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between fa1be94 and 8691a79.

📒 Files selected for processing (3)
  • mcp/types.go (2 hunks)
  • server/server.go (3 hunks)
  • server/server_test.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/server_test.go
  • server/server.go
  • mcp/types.go
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@rwjblue-glean rwjblue-glean marked this pull request as draft May 14, 2025 21:30
@pottekkat pottekkat force-pushed the fix/type-mismatch/154 branch from 0a54ebb to 5e0b31e Compare May 15, 2025 03:57
@pottekkat pottekkat force-pushed the fix/type-mismatch/154 branch from 5e0b31e to 25a3e3f Compare May 15, 2025 06:37
@@ -62,7 +62,7 @@ func NewSSE(baseURL string, options ...ClientOption) (*SSE, error) {
smc := &SSE{
baseURL: parsedURL,
httpClient: &http.Client{},
responses: make(map[int64]chan *JSONRPCResponse),
responses: make(map[string]chan *JSONRPCResponse),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always use string version of the id for lookups to ensure consistency.

Comment on lines +190 to +192
idValue, ok := result.ID.Value().(int64)
if !ok {
t.Errorf("Expected ID to be int64, got %T", result.ID.Value())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also check the type.

Comment on lines +326 to +341
t.Run("SendRequestWithStringID", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

params := map[string]any{
"string": "string id test",
"array": []any{4, 5, 6},
}

// Use a string ID instead of an integer
request := JSONRPCRequest{
JSONRPC: "2.0",
ID: mcp.NewRequestId("request-123"),
Method: "debug/echo",
Params: params,
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure string Ids work. Only this test uses a string ID now but I guess that's sufficient.

mcp/types.go Outdated
Comment on lines 237 to 257
func (r *RequestId) UnmarshalJSON(data []byte) error {
// Try unmarshaling as string first
var s string
if err := json.Unmarshal(data, &s); err == nil {
r.value = s
return nil
}

// JSON numbers are unmarshaled as float64 in Go
var f float64
if err := json.Unmarshal(data, &f); err == nil {
if f == float64(int64(f)) {
r.value = int64(f)
} else {
r.value = f
}
return nil
}

return fmt.Errorf("invalid request id: %s", string(data))
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Custom unmarshal function to make sure numbers converted to float are converted to int. This ensures both string and int ids work.

@pottekkat pottekkat marked this pull request as ready for review May 15, 2025 07:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
client/transport/sse_test.go (1)

190-195: Deduplicate repeated ID-assertion logic via a helper

The exact same 6-line block for asserting an int64 request-ID appears four times in this test. Re-using a small helper reduces noise and makes future type-changes easier:

+func assertInt64ID(t *testing.T, got mcp.RequestId, want int64, context string) {
+	t.Helper()
+	v, ok := got.Value().(int64)
+	if !ok {
+		t.Fatalf("%s: expected ID to be int64, got %T", context, got.Value())
+	}
+	if v != want {
+		t.Fatalf("%s: expected ID %d, got %d", context, want, v)
+	}
+}

Then replace each call site, e.g.

-	idValue, ok := result.ID.Value().(int64)
-	...
-	} else if idValue != 1 {
-		t.Errorf("Expected ID 1, got %d", idValue)
+	assertInt64ID(t, result.ID, 1, "SendRequest/result")

This keeps the test focused on behaviour rather than bookkeeping.

Also applies to: 323-334, 352-357, 398-401

client/transport/stdio_test.go (1)

110-115: Extract common ID checks to a helper (same as SSE tests)

The integer-ID validation block is repeated five times here and once more in SendRequestWithStringID (string variant). Consider introducing a pair of helpers like assertInt64ID and assertStringID in this file (or a shared testutil package) to keep each sub-test concise and DRY.

Also applies to: 248-254, 272-276, 315-319, 365-368

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5e0b31e and 25a3e3f.

📒 Files selected for processing (14)
  • client/client.go (1 hunks)
  • client/transport/interface.go (1 hunks)
  • client/transport/sse.go (6 hunks)
  • client/transport/sse_test.go (11 hunks)
  • client/transport/stdio.go (6 hunks)
  • client/transport/stdio_test.go (12 hunks)
  • client/transport/streamable_http.go (2 hunks)
  • client/transport/streamable_http_test.go (13 hunks)
  • mcp/types.go (2 hunks)
  • server/server.go (3 hunks)
  • server/server_test.go (5 hunks)
  • server/sse.go (1 hunks)
  • server/sse_test.go (1 hunks)
  • testdata/mockstdio_server.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • server/server_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • client/transport/stdio.go
  • client/transport/sse.go
🧰 Additional context used
🧬 Code Graph Analysis (7)
client/client.go (1)
mcp/types.go (1)
  • NewRequestId (202-204)
client/transport/interface.go (1)
mcp/types.go (1)
  • RequestId (197-199)
server/sse.go (1)
mcp/types.go (1)
  • NewRequestId (202-204)
testdata/mockstdio_server.go (1)
mcp/types.go (1)
  • RequestId (197-199)
client/transport/streamable_http_test.go (1)
mcp/types.go (2)
  • NewRequestId (202-204)
  • RequestId (197-199)
server/server.go (1)
mcp/types.go (1)
  • NewRequestId (202-204)
client/transport/sse_test.go (1)
mcp/types.go (2)
  • NewRequestId (202-204)
  • RequestId (197-199)
🔇 Additional comments (28)
server/sse.go (1)

341-341: Correctly using the type-safe mcp.RequestId wrapper

This change wraps the request ID with mcp.NewRequestId() to ensure type safety when dealing with request IDs. This addresses the issue mentioned in the PR where integer IDs were causing type mismatches due to JSON unmarshaling converting numbers to floats.

client/client.go (1)

107-107: Correctly using typed RequestId instead of raw integer

This change aligns with the PR objective to fix type mismatches for request/response IDs by wrapping the raw integer ID with mcp.NewRequestId(). This ensures consistent type handling throughout the JSON-RPC communication.

testdata/mockstdio_server.go (3)

9-10: Adding necessary mcp package import

Added the import needed for mcp.RequestId type.


15-15: Updated request ID type to match new type system

Changed from raw integer to *mcp.RequestId for type safety and consistency with the PR objective of fixing request/response ID type handling.


21-23: Updated response ID and result types to match new type system

The JSONRPCResponse struct has been updated to use *mcp.RequestId for the ID field and any for the Result field, ensuring type safety and consistency.

client/transport/interface.go (2)

29-34: Updated request structure to use typed RequestId

The JSONRPCRequest struct now uses the strongly typed mcp.RequestId for the ID field instead of a raw integer. This is part of the type mismatch fix described in the PR objective and ensures consistent handling of request IDs in JSON-RPC communication.


38-38: Updated response ID to use typed RequestId

The JSONRPCResponse struct now uses the strongly typed mcp.RequestId for the ID field, maintaining consistency with the request structure and ensuring proper type handling during JSON marshaling/unmarshaling.

client/transport/streamable_http.go (2)

219-221: Changed nil check for response ID to use IsNil() method.

This change updates the nil check to use the IsNil() method of the mcp.RequestId type instead of a direct nil comparison. This addresses the core issue in the PR of properly handling request/response IDs of different types.


260-262: Updated notification detection to use ID.IsNil() method.

This modification properly handles the nil check for notification messages (which lack an ID) using the IsNil() method rather than a direct nil pointer comparison, consistent with the new mcp.RequestId type.

server/sse_test.go (1)

813-822: Improved type checking for ping message IDs.

The updated code adds proper type assertion logic for handling the ping message ID, checking for both int64 and float64 types instead of assuming it's always a float64. This handles the case where request IDs might be unmarshaled from JSON as different numeric types.

The change correctly addresses the issue described in the PR where numbers could be interpreted as different types when unmarshaled from JSON.

server/server.go (3)

104-105: Updated ID field to use RequestId type for error responses.

This change wraps the raw ID value with mcp.NewRequestId() when constructing a JSON-RPC error response, standardizing ID handling throughout the transport interface.


931-932: Updated response ID creation to use RequestId wrapper.

The change uses mcp.NewRequestId() to wrap the raw ID value when constructing a JSON-RPC response, ensuring consistent ID type handling across the codebase.


943-944: Updated error response ID creation to use RequestId wrapper.

Similar to the other changes, this modification wraps the raw ID value with mcp.NewRequestId() when creating an error response, maintaining type consistency for JSON-RPC IDs.

client/transport/streamable_http_test.go (14)

150-151: Updated test to use typed RequestId for initialization request.

Modified the test code to use mcp.NewRequestId(int64(0)) instead of a raw integer value, aligning with the new type system for request IDs.


171-172: Updated test request to use typed RequestId.

Modified the test code to use mcp.NewRequestId(int64(1)) instead of a raw integer value, ensuring proper testing of the new ID type system.


185-186: Updated result struct to use RequestId type.

Changed the ID field type in the test result struct to mcp.RequestId to match the updated type system for JSON-RPC messages.


198-203: Added type-safe ID value extraction and validation.

This change adds proper type assertion when extracting the ID value, with error reporting if the assertion fails. This enhances type safety and allows for better test diagnostics.


225-226: Updated timeout test to use RequestId type.

Modified the test request to use mcp.NewRequestId(int64(3)) for consistency with the type changes throughout the codebase.


253-254: Updated notification test to use RequestId type.

Modified the test request to use mcp.NewRequestId(int64(1)) to align with the new type system for request IDs.


272-273: Updated notification check to handle ID type conversion.

The code now properly converts the float64 value from JSON to int64 for comparison with the request ID value, addressing the core issue of the PR regarding type handling.


308-309: Updated concurrent request test to use RequestId type.

Changed the code to use mcp.NewRequestId(int64(100 + idx)) for proper ID typing in the concurrent request test.


333-345: Added robust response ID validation in multi-request test.

This change adds proper null checks, type assertions, and value comparisons for response IDs, improving test robustness and error reporting. This ensures that the response IDs are correctly handled in all concurrent test cases.


351-352: Updated result struct ID type in multi-request test.

Changed the ID field type in the multi-request test result struct to mcp.RequestId for consistency with the type system changes.


362-364: Updated ID value comparison in multi-request test.

Modified the test to directly extract and compare the ID value from the RequestId object rather than comparing raw integer values.


384-385: Updated error test to use RequestId type.

Changed the error test to use mcp.NewRequestId(int64(100)) for the request ID, maintaining consistency with the type system changes.


406-411: Added robust ID verification in error response test.

This change adds proper type assertion and value checks when verifying the ID in error responses, improving test robustness and error reporting.


440-441: Updated error test URL to use RequestId type.

Modified the non-existent URL test to use mcp.NewRequestId(int64(1)) for consistency with the type system changes throughout the test file.

client/transport/stdio_test.go (1)

326-382: 👍 Good addition covering string IDs

The new SendRequestWithStringID sub-test proves that non-numeric IDs round-trip correctly through the transport layer. This complements the earlier int64 coverage and guards against regressions.

@pottekkat pottekkat changed the title [draft] fix: type mismatch for request/response ID fix: type mismatch for request/response ID May 15, 2025
@pottekkat pottekkat added type: enhancement New feature or enhancement request area: sdk SDK improvements unrelated to MCP specification labels May 16, 2025
@rwjblue-glean rwjblue-glean merged commit 09c23b5 into mark3labs:main May 16, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: sdk SDK improvements unrelated to MCP specification type: enhancement New feature or enhancement request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Error unmarshaling message: json: cannot unmarshal string into Go struct field .id of type int64
2 participants