Skip to content

Conversation

@dask-58
Copy link
Contributor

@dask-58 dask-58 commented Dec 11, 2025

Description

Implemented full support for URL-based elicitation (out-of-band authentication/interaction) as defined in the MCP specification.

Key changes:

  • mcp package: Added ElicitationModeURL constant, URLElicitationRequiredError type, and updated ElicitationParams/ElicitationCapability to support URL mode.
  • server package: Added RequestURLElicitation and SendElicitationComplete helper methods for easier implementation of out-of-band flows.
  • examples: Added a comprehensive example in examples/elicitation/main.go demonstrating:
    • Standard URL mode elicitation (auth_via_url).
    • Error-triggered elicitation (protected_action returning URLElicitationRequiredError).
    • Form mode elicitation.
  • client: Updated client capability negotiation to support URL mode.
  • Tests:
    • Added unit tests for new server helpers and error types.
    • Refactored mcp/elicitation_test.go, mcp/errors_test.go, and server/elicitation_test.go to use the testify package for more robust assertions.

Fixes #655

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

MCP Spec Compliance

  • This PR implements a feature defined in the MCP specification
  • Link to relevant spec section: Elicitation
  • Implementation follows the specification exactly

Summary by CodeRabbit

  • New Features

    • URL-based elicitation flow with explicit start and completion notifications; example tools demonstrate the flow and tracking.
  • Bug Fixes / Behavioral

    • Capabilities now advertise explicit elicitation modes (form and URL).
    • Elicitation requests are validated earlier and return clear errors when URL-based elicitation is required.
  • Tests

    • Added unit and integration tests for elicitation modes, validation, serialization, URL flows, and completion notifications.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds URL-mode elicitation to MCP: new mode constants, typed ElicitationCapability, extended ElicitationParams (meta, mode, optional requestedSchema, elicitationId, url), URLElicitationRequiredError with JSON-RPC mapping, completion notification, client/server helpers and validation, tests, and example URL-flow tooling.

Changes

Cohort / File(s) Summary
MCP core types & constants
mcp/consts.go, mcp/types.go, mcp/elicitation_test.go, mcp/elicitation_validation_test.go
Add ElicitationModeForm / ElicitationModeURL; introduce ElicitationCapability{Form,URL}; extend ElicitationParams with Meta, Mode, RequestedSchema (omitempty), ElicitationID, URL; add MethodNotificationElicitationComplete and NewElicitationCompleteNotification; add serialization and validation tests.
MCP errors & tests
mcp/errors.go, mcp/errors_test.go
Add URL_ELICITATION_REQUIRED code and URLElicitationRequiredError (fields, Error(), JSONRPCError(), Is()); update JSONRPCErrorDetails.AsError() to rebuild this error from Data via JSON round-trip; add tests for error serialization and AsError behavior.
Client changes & tests
client/client.go, client/elicitation.go, client/inprocess_elicitation_test.go
Initialize client capabilities with *mcp.ElicitationCapability instead of bare &struct{}{}; add pre-validation params.Validate() in handleElicitationRequestTransport; update elicitation handler doc-comments; update test setup to use mcp.ElicitationCapability.
Server helpers & tests
server/server.go, server/elicitation.go, server/elicitation_test.go
Initialize server capabilities with *mcp.ElicitationCapability; add RequestURLElicitation and SendElicitationComplete methods; validate ElicitationParams in RequestElicitation; add/adjust mock session scaffolding and tests TestRequestURLElicitation / TestSendElicitationComplete.
Examples
examples/elicitation/main.go
Add URL-mode example tools (auth_via_url, protected_action), import github.com/google/uuid, generate elicitation IDs, request URL-mode elicitation and send completion; tool handlers return URLElicitationRequiredError where appropriate.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to review closely:
    • JSON tag semantics, omitempty behavior, and backward compatibility in mcp/types.go.
    • JSON-RPC mapping and reconstruction logic in mcp/errors.go (JSONRPCErrorDetails.AsError).
    • Client-side validation addition in client/client.go and its effect on transport flow.
    • Server notification construction and delivery in server/elicitation.go and tests.
    • Example tools' use of elicitation flows and error shapes in examples/elicitation/main.go.

Possibly related PRs

Suggested labels

type: enhancement, area: sdk

Suggested reviewers

  • pottekkat
  • rwjblue-glean
  • robert-jackson-glean

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: implementing elicitation URL mode as defined in the MCP specification.
Description check ✅ Passed The description follows the template structure with appropriate sections (Type of Change, Checklist, MCP Spec Compliance) and provides comprehensive details about the implementation.
Linked Issues check ✅ Passed The PR successfully implements all primary objectives from issue #655: adds ElicitationModeURL and ElicitationModeForm constants, URLElicitationRequiredError type with JSON-RPC code -32042, ElicitationCompleteNotification, RequestURLElicitation and SendElicitationComplete server helpers, updated ElicitationParams with mode discriminator and URL-mode fields, updated client capability negotiation, and includes comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing URL mode elicitation per issue #655 requirements. The modifications to mcp/, client/, server/, and examples/ packages all support the elicitation URL mode feature without unrelated additions.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mcp/errors.go (1)

81-109: Add case for URL_ELICITATION_REQUIRED in AsError() to reconstruct URLElicitationRequiredError.

When the client receives a JSON-RPC error with code -32042, AsError() falls through to the default case and returns a generic error, losing the elicitations data. Add a case that reconstructs URLElicitationRequiredError from e.Data so clients can access elicitation details through the standard error conversion path (lines 81–109).

🧹 Nitpick comments (7)
server/elicitation.go (1)

35-66: URL-mode elicitation helper is well-shaped; capability checks remain caller responsibility

RequestURLElicitation correctly constructs URL-mode ElicitationParams (Mode, Message, ElicitationID, URL) and routes through the existing SessionWithElicitation path. It doesn’t itself validate that the client advertised URL-mode support or that the URL uses HTTPS; those checks are reasonable to leave to higher-level call sites that have access to negotiated capabilities and deployment policy.

mcp/errors.go (1)

35-42: Consider adding Is() method for errors.Is compatibility.

UnsupportedProtocolVersionError implements Is() (lines 66-70) for better error handling with errors.Is. For consistency and as per coding guidelines, URLElicitationRequiredError could benefit from the same pattern.

+// Is implements the errors.Is interface for better error handling
+func (e URLElicitationRequiredError) Is(target error) bool {
+	_, ok := target.(URLElicitationRequiredError)
+	return ok
+}
server/elicitation_test.go (2)

46-51: Potential goroutine leak: unbuffered notification channel discarded.

When notifyChan is nil, a new buffered channel is created but never stored or returned to the caller for draining. If notifications are sent to this channel and it fills up, senders could block. Consider documenting this as intentional for tests where notifications aren't expected, or use a larger buffer.


239-241: Redundant assertion: result.Action is a string type, never nil.

ElicitationResponseAction is a string type alias. The assert.NotNil check is redundant since result.Action is already validated by assert.Equal(t, tt.expectedType, result.Action) on line 237.

 			if tt.expectedType == mcp.ElicitationResponseActionAccept {
-				assert.NotNil(t, result.Action)
+				assert.NotNil(t, result.Content)
 			}

Or remove this block entirely if content validation isn't needed.

examples/elicitation/main.go (2)

271-287: Misleading flow: completion notification sent before browser authentication completes.

In a real URL elicitation flow, SendElicitationComplete should be called after the user completes authentication in the browser (e.g., via webhook or polling). Here, it's called immediately after the user consents to open the URL, before they've actually authenticated. This is fine for a demo, but consider adding a comment clarifying this is simulated.

 		if result.Action == mcp.ElicitationResponseActionAccept {
 			// User consented to open the URL
 			// They will complete the flow in their browser
 			// Server will store credentials when user submits the form

-			// Simulate sending completion notification
+			// NOTE: In production, this notification would be sent after
+			// the server receives the authentication callback from the browser.
+			// Here we simulate immediate completion for demonstration purposes.
 			if err := mcpServer.SendElicitationComplete(ctx, session, elicitationID); err != nil {

303-329: Dead code: success path is unreachable with hardcoded isAuthorized := false.

The code at lines 324-328 is never executed. While this is likely intentional for demonstration, consider using a TODO comment or restructuring to make the intent clearer.

 		func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
-			// Check if identified
-			isAuthorized := false 
+			// TODO: In production, check actual authorization state
+			// For demo purposes, we always trigger elicitation
+			isAuthorized := false // Always false to demonstrate error flow
 			
 			if !isAuthorized {
mcp/types.go (1)

1311-1320: Consider extracting method string to a constant.

The method "notifications/elicitation/complete" is hardcoded. For consistency with other methods defined as constants (lines 17-82), consider adding a constant like MethodNotificationElicitationComplete.

+// MethodNotificationElicitationComplete notifies when a URL mode elicitation completes.
+MethodNotificationElicitationComplete MCPMethod = "notifications/elicitation/complete"

Then use it in the constructor:

 func NewElicitationCompleteNotification(elicitationID string) *ElicitationCompleteNotification {
 	return &ElicitationCompleteNotification{
 		Notification: Notification{
-			Method: "notifications/elicitation/complete",
+			Method: string(MethodNotificationElicitationComplete),
 		},
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 919c4bf and 77245bc.

📒 Files selected for processing (12)
  • client/client.go (1 hunks)
  • client/elicitation.go (1 hunks)
  • client/inprocess_elicitation_test.go (1 hunks)
  • examples/elicitation/main.go (4 hunks)
  • mcp/consts.go (1 hunks)
  • mcp/elicitation_test.go (1 hunks)
  • mcp/errors.go (1 hunks)
  • mcp/errors_test.go (1 hunks)
  • mcp/types.go (4 hunks)
  • server/elicitation.go (1 hunks)
  • server/elicitation_test.go (8 hunks)
  • server/server.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • mcp/errors_test.go
  • mcp/consts.go
  • server/server.go
  • mcp/elicitation_test.go
  • client/inprocess_elicitation_test.go
  • server/elicitation.go
  • mcp/errors.go
  • server/elicitation_test.go
  • client/client.go
  • examples/elicitation/main.go
  • mcp/types.go
  • client/elicitation.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Testing: use testify/assert and testify/require
Write table-driven tests using a tests := []struct{ name, ... } pattern
Go test files must end with _test.go

Files:

  • mcp/errors_test.go
  • mcp/elicitation_test.go
  • client/inprocess_elicitation_test.go
  • server/elicitation_test.go
🧠 Learnings (10)
📚 Learning: 2025-04-21T21:26:32.945Z
Learnt from: octo
Repo: mark3labs/mcp-go PR: 149
File: mcptest/mcptest.go:0-0
Timestamp: 2025-04-21T21:26:32.945Z
Learning: In the mcptest package, prefer returning errors from helper functions rather than calling t.Fatalf() directly, giving callers flexibility in how to handle errors.

Applied to files:

  • mcp/errors_test.go
  • mcp/elicitation_test.go
  • server/elicitation_test.go
📚 Learning: 2025-06-30T07:13:17.052Z
Learnt from: ezynda3
Repo: mark3labs/mcp-go PR: 461
File: server/sampling.go:22-26
Timestamp: 2025-06-30T07:13:17.052Z
Learning: In the mark3labs/mcp-go project, the MCPServer.capabilities field is a struct value (serverCapabilities), not a pointer, so it cannot be nil and doesn't require nil checking. Only pointer fields within the capabilities struct should be checked for nil.

Applied to files:

  • server/server.go
  • mcp/elicitation_test.go
  • client/inprocess_elicitation_test.go
  • client/client.go
  • examples/elicitation/main.go
  • mcp/types.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Testing: use testify/assert and testify/require

Applied to files:

  • mcp/elicitation_test.go
  • server/elicitation_test.go
📚 Learning: 2025-03-04T07:00:57.111Z
Learnt from: xinwo
Repo: mark3labs/mcp-go PR: 35
File: mcp/tools.go:0-0
Timestamp: 2025-03-04T07:00:57.111Z
Learning: The Tool struct in the mark3labs/mcp-go project should handle both InputSchema and RawInputSchema consistently between MarshalJSON and UnmarshalJSON methods, even though the tools response from MCP server typically doesn't contain rawInputSchema.

Applied to files:

  • mcp/elicitation_test.go
  • examples/elicitation/main.go
📚 Learning: 2025-08-24T00:52:47.610Z
Learnt from: semistrict
Repo: mark3labs/mcp-go PR: 495
File: server/elicitation_test.go:229-231
Timestamp: 2025-08-24T00:52:47.610Z
Learning: In Go, test files within the same package can access types and functions defined in other test files in that package. When analyzing test code that references undefined types, check other test files in the same package for shared test utilities and mock types.

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*.go : Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Write table-driven tests using a tests := []struct{ name, ... } pattern

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*.go : Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments

Applied to files:

  • examples/elicitation/main.go
📚 Learning: 2025-06-23T11:10:42.948Z
Learnt from: floatingIce91
Repo: mark3labs/mcp-go PR: 401
File: server/server.go:1082-1092
Timestamp: 2025-06-23T11:10:42.948Z
Learning: In Go MCP server, ServerTool.Tool field is only used for tool listing and indexing, not for tool execution or middleware. During handleToolCall, only the Handler field is used, so dynamic tools don't need the Tool field populated.

Applied to files:

  • examples/elicitation/main.go
📚 Learning: 2025-03-04T06:59:43.882Z
Learnt from: xinwo
Repo: mark3labs/mcp-go PR: 35
File: mcp/tools.go:107-137
Timestamp: 2025-03-04T06:59:43.882Z
Learning: Tool responses from the MCP server shouldn't contain RawInputSchema, which is why the UnmarshalJSON method for the Tool struct is implemented to handle only the structured InputSchema format.

Applied to files:

  • examples/elicitation/main.go
  • mcp/types.go
🧬 Code graph analysis (9)
mcp/errors_test.go (2)
mcp/errors.go (2)
  • URLElicitationRequiredError (36-38)
  • URL_ELICITATION_REQUIRED (32-32)
mcp/types.go (2)
  • ElicitationParams (888-906)
  • JSONRPCError (356-360)
server/server.go (1)
mcp/types.go (1)
  • ElicitationCapability (1296-1299)
mcp/elicitation_test.go (2)
mcp/types.go (2)
  • ElicitationParams (888-906)
  • ElicitationCapability (1296-1299)
mcp/consts.go (2)
  • ElicitationModeForm (10-10)
  • ElicitationModeURL (11-11)
client/inprocess_elicitation_test.go (1)
mcp/types.go (1)
  • ElicitationCapability (1296-1299)
server/elicitation.go (2)
mcp/types.go (8)
  • ElicitationResult (909-912)
  • ElicitationParams (888-906)
  • ElicitationRequest (882-885)
  • Request (179-182)
  • MethodElicitationCreate (60-60)
  • Params (188-188)
  • JSONRPCNotification (343-346)
  • Notification (190-193)
mcp/consts.go (1)
  • ElicitationModeURL (11-11)
mcp/errors.go (2)
mcp/types.go (3)
  • ElicitationParams (888-906)
  • JSONRPCError (356-360)
  • JSONRPCErrorDetails (364-373)
client/transport/error.go (1)
  • Error (6-8)
server/elicitation_test.go (4)
mcp/types.go (7)
  • ElicitationResult (909-912)
  • ElicitationRequest (882-885)
  • JSONRPCNotification (343-346)
  • ElicitationResponseActionAccept (928-928)
  • Content (1006-1008)
  • ElicitationResponse (915-921)
  • Params (188-188)
server/elicitation.go (2)
  • ErrNoActiveSession (12-12)
  • ErrElicitationNotSupported (14-14)
server/server.go (2)
  • NewMCPServer (345-373)
  • WithElicitation (324-328)
mcp/consts.go (1)
  • ElicitationModeURL (11-11)
client/client.go (1)
mcp/types.go (1)
  • ElicitationCapability (1296-1299)
examples/elicitation/main.go (5)
server/session.go (1)
  • ClientSessionFromContext (112-117)
mcp/types.go (3)
  • ElicitationResponseActionAccept (928-928)
  • Content (1006-1008)
  • ElicitationParams (888-906)
mcp/utils.go (1)
  • NewTextContent (225-230)
mcp/errors.go (1)
  • URLElicitationRequiredError (36-38)
mcp/consts.go (1)
  • ElicitationModeURL (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (12)
server/server.go (1)

703-705: Typed ElicitationCapability wiring in initialize looks correct

Using &mcp.ElicitationCapability{} here matches the new public type and keeps the JSON payload backward-compatible ({} today, with room to set Form/URL flags later if desired). No issues from a server capability perspective.

mcp/consts.go (1)

3-11: Elicitation mode constants are well-defined

The new ElicitationModeForm / ElicitationModeURL constants are appropriately named and align with their usage in ElicitationParams and tests.

client/inprocess_elicitation_test.go (1)

126-128: Test capability setup matches new ElicitationCapability type

Switching to &mcp.ElicitationCapability{} keeps this in-process test consistent with the updated capabilities schema and client initialization.

mcp/elicitation_test.go (1)

1-94: Elicitation params & capability serialization tests are solid

The table-driven coverage for form vs URL modes and the capability JSON shapes (empty/form/url) is clear and uses testify as per test guidelines. This gives good confidence in wire-format stability of the new types.

mcp/errors_test.go (1)

172-203: URLElicitationRequiredError behavior test is appropriate

The test thoroughly checks the error string, JSON-RPC code/message, and the typed elicitations payload in Data, covering the main semantics of the new error type.

client/client.go (1)

200-203: Client now advertises elicitation via concrete ElicitationCapability

Using &mcp.ElicitationCapability{} when an ElicitationHandler is set keeps capability negotiation consistent with the new type and mirrors the server side. Note this still intentionally overwrites any pre-set Capabilities.Elicitation, matching the existing pattern for sampling/roots.

server/elicitation.go (1)

20-33: Explicit ErrElicitationNotSupported return improves error signaling

Returning ErrElicitationNotSupported when the session doesn’t implement SessionWithElicitation makes the behavior explicit and lets callers distinguish “no active session” from “session doesn’t support elicitation”.

server/elicitation_test.go (1)

246-281: LGTM with minor suggestion: Consider splitting into separate test cases.

The test correctly validates RequestURLElicitation and SendElicitationComplete. For better isolation and readability per coding guidelines (table-driven tests pattern), consider splitting into separate test functions or using subtests with t.Run().

examples/elicitation/main.go (1)

240-295: Good example demonstrating URL elicitation flow.

The auth_via_url tool effectively demonstrates the URL elicitation pattern: generating a unique ID, constructing a tracking URL, requesting elicitation, and handling the response. The error handling for SendElicitationComplete (log and continue) is appropriate for this use case.

mcp/types.go (3)

1295-1299: LGTM: Capability struct follows established patterns.

The ElicitationCapability struct using *struct{} pointers for Form and URL is consistent with other capability definitions (e.g., Sampling *struct{} at line 491). This allows proper distinction between unset and explicitly enabled modes.


888-906: LGTM: ElicitationParams properly extended for URL mode.

The struct extension is well-designed:

  • Mode defaults to "form" for backward compatibility
  • Fields are grouped by mode with clear comments
  • All optional fields use omitempty appropriately
  • Meta field added for protocol extensibility

493-493: and

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: 0

🧹 Nitpick comments (2)
mcp/errors.go (2)

31-32: Use a const for the error code and avoid mixing it into the sentinel error var block

URL_ELICITATION_REQUIRED represents a fixed JSON-RPC error code, so defining it as a const (and keeping the var block scoped to sentinel error values) would be more idiomatic and prevent accidental mutation.

For example:

const URL_ELICITATION_REQUIRED = -32042

You can place this near the other JSON-RPC error code constants for consistency.


35-55: URLElicitationRequiredError struct and JSON-RPC mapping look correct and spec-aligned

The exported type, GoDoc, and JSON tag on Elicitations are consistent with the rest of the package, and Error() provides a clear, deterministic message. JSONRPCError() now correctly sets JSONRPC: JSONRPC_VERSION and wraps the elicitation payload under data.elicitations, matching the intended JSON-RPC integration for this new error code.

If, in the future, you want client-side callers of JSONRPCErrorDetails.AsError() to be able to detect this condition via errors.As, consider adding a dedicated branch for URL_ELICITATION_REQUIRED that reconstructs a URLElicitationRequiredError from Data, but that’s optional and can be deferred.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a1509d and b5f7361.

📒 Files selected for processing (1)
  • mcp/errors.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • mcp/errors.go
🧬 Code graph analysis (1)
mcp/errors.go (2)
mcp/types.go (4)
  • ElicitationParams (888-906)
  • JSONRPCError (356-360)
  • JSONRPC_VERSION (122-122)
  • JSONRPCErrorDetails (364-373)
client/transport/error.go (1)
  • Error (6-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test

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: 0

🧹 Nitpick comments (3)
mcp/errors.go (1)

105-122: Consider renaming shadowed error variable for clarity.

The err variable inside the if blocks (lines 110, 114) shadows the outer err declared at line 88. While this is scoped correctly and works, it could be confusing during maintenance.

Consider renaming to avoid shadowing:

 	case URL_ELICITATION_REQUIRED:
 		// Attempt to reconstruct URLElicitationRequiredError from Data
 		if e.Data != nil {
 			// Round-trip through JSON to parse into struct
 			// This handles both map[string]any (from unmarshal) and other forms
-			if dataBytes, err := json.Marshal(e.Data); err == nil {
+			if dataBytes, marshalErr := json.Marshal(e.Data); marshalErr == nil {
 				var data struct {
 					Elicitations []ElicitationParams `json:"elicitations"`
 				}
-				if err := json.Unmarshal(dataBytes, &data); err == nil {
+				if unmarshalErr := json.Unmarshal(dataBytes, &data); unmarshalErr == nil {
 					return URLElicitationRequiredError{
 						Elicitations: data.Elicitations,
 					}
 				}
 			}
 		}
 		// Fallback if data is missing or invalid
 		return URLElicitationRequiredError{}
server/elicitation_test.go (2)

46-51: Consider documenting the buffer size choice.

The notification channel buffer size was changed from 1 to 100. While 100 is reasonable for test scenarios, a brief comment explaining why this size was chosen would aid future maintenance.

 func (m *mockElicitationSession) NotificationChannel() chan<- mcp.JSONRPCNotification {
 	if m.notifyChan == nil {
+		// Buffer of 100 to avoid blocking during tests with multiple notifications
 		m.notifyChan = make(chan mcp.JSONRPCNotification, 100)
 	}
 	return m.notifyChan
 }

275-280: Consider validating elicitationId in notification params.

The test validates the notification method but doesn't verify that the elicitationId is correctly included in the notification params. This would ensure complete end-to-end validation.

 	select {
 	case notif := <-notifyChan:
 		assert.Equal(t, "notifications/elicitation/complete", notif.Method)
+		// Validate elicitationId is included in params
+		elicitationID, ok := notif.Params.AdditionalFields["elicitationId"]
+		assert.True(t, ok, "expected elicitationId in notification params")
+		assert.Equal(t, "id-123", elicitationID)
 	case <-time.After(100 * time.Millisecond):
 		t.Error("Expected notification not received")
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5f7361 and e4b059b.

📒 Files selected for processing (6)
  • examples/elicitation/main.go (4 hunks)
  • mcp/errors.go (4 hunks)
  • mcp/errors_test.go (1 hunks)
  • mcp/types.go (6 hunks)
  • server/elicitation.go (1 hunks)
  • server/elicitation_test.go (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • mcp/errors_test.go
  • server/elicitation.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • examples/elicitation/main.go
  • server/elicitation_test.go
  • mcp/errors.go
  • mcp/types.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Testing: use testify/assert and testify/require
Write table-driven tests using a tests := []struct{ name, ... } pattern
Go test files must end with _test.go

Files:

  • server/elicitation_test.go
🧠 Learnings (13)
📚 Learning: 2025-06-30T07:13:17.052Z
Learnt from: ezynda3
Repo: mark3labs/mcp-go PR: 461
File: server/sampling.go:22-26
Timestamp: 2025-06-30T07:13:17.052Z
Learning: In the mark3labs/mcp-go project, the MCPServer.capabilities field is a struct value (serverCapabilities), not a pointer, so it cannot be nil and doesn't require nil checking. Only pointer fields within the capabilities struct should be checked for nil.

Applied to files:

  • examples/elicitation/main.go
  • mcp/errors.go
  • mcp/types.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*.go : Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments

Applied to files:

  • examples/elicitation/main.go
📚 Learning: 2025-06-23T11:10:42.948Z
Learnt from: floatingIce91
Repo: mark3labs/mcp-go PR: 401
File: server/server.go:1082-1092
Timestamp: 2025-06-23T11:10:42.948Z
Learning: In Go MCP server, ServerTool.Tool field is only used for tool listing and indexing, not for tool execution or middleware. During handleToolCall, only the Handler field is used, so dynamic tools don't need the Tool field populated.

Applied to files:

  • examples/elicitation/main.go
📚 Learning: 2025-03-04T06:59:43.882Z
Learnt from: xinwo
Repo: mark3labs/mcp-go PR: 35
File: mcp/tools.go:107-137
Timestamp: 2025-03-04T06:59:43.882Z
Learning: Tool responses from the MCP server shouldn't contain RawInputSchema, which is why the UnmarshalJSON method for the Tool struct is implemented to handle only the structured InputSchema format.

Applied to files:

  • examples/elicitation/main.go
  • mcp/errors.go
  • mcp/types.go
📚 Learning: 2025-03-04T07:00:57.111Z
Learnt from: xinwo
Repo: mark3labs/mcp-go PR: 35
File: mcp/tools.go:0-0
Timestamp: 2025-03-04T07:00:57.111Z
Learning: The Tool struct in the mark3labs/mcp-go project should handle both InputSchema and RawInputSchema consistently between MarshalJSON and UnmarshalJSON methods, even though the tools response from MCP server typically doesn't contain rawInputSchema.

Applied to files:

  • examples/elicitation/main.go
  • mcp/errors.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Testing: use testify/assert and testify/require

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-04-21T21:26:32.945Z
Learnt from: octo
Repo: mark3labs/mcp-go PR: 149
File: mcptest/mcptest.go:0-0
Timestamp: 2025-04-21T21:26:32.945Z
Learning: In the mcptest package, prefer returning errors from helper functions rather than calling t.Fatalf() directly, giving callers flexibility in how to handle errors.

Applied to files:

  • server/elicitation_test.go
  • mcp/errors.go
📚 Learning: 2025-08-24T00:52:47.610Z
Learnt from: semistrict
Repo: mark3labs/mcp-go PR: 495
File: server/elicitation_test.go:229-231
Timestamp: 2025-08-24T00:52:47.610Z
Learning: In Go, test files within the same package can access types and functions defined in other test files in that package. When analyzing test code that references undefined types, check other test files in the same package for shared test utilities and mock types.

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*.go : Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Write table-driven tests using a tests := []struct{ name, ... } pattern

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-08-08T15:37:18.458Z
Learnt from: ezynda3
Repo: mark3labs/mcp-go PR: 0
File: :0-0
Timestamp: 2025-08-08T15:37:18.458Z
Learning: In mark3labs/mcp-go stdio transport, using bufio.Scanner without calling Scanner.Buffer imposes a 64 KiB per-line limit; to support very long lines safely, set a larger max via Scanner.Buffer or use a streaming Reader/json.Decoder approach.

Applied to files:

  • mcp/errors.go
📚 Learning: 2025-06-20T20:39:51.870Z
Learnt from: lariel-fernandes
Repo: mark3labs/mcp-go PR: 428
File: www/docs/pages/servers/prompts.mdx:218-234
Timestamp: 2025-06-20T20:39:51.870Z
Learning: In the mcp-go library, the GetPromptParams.Arguments field is of type map[string]string, not map[string]interface{}, so direct string access without type assertions is safe and correct.

Applied to files:

  • mcp/errors.go
📚 Learning: 2025-06-26T09:38:18.629Z
Learnt from: davidleitw
Repo: mark3labs/mcp-go PR: 451
File: mcp/tools.go:1192-1217
Timestamp: 2025-06-26T09:38:18.629Z
Learning: In mcp-go project, the maintainer prefers keeping builder pattern APIs simple without excessive validation for edge cases. The WithOutput* functions are designed to assume correct usage rather than defensive programming, following the principle of API simplicity over comprehensive validation.

Applied to files:

  • mcp/errors.go
🧬 Code graph analysis (1)
mcp/errors.go (1)
mcp/types.go (5)
  • ElicitationParams (894-912)
  • JSONRPCError (359-363)
  • JSONRPC_VERSION (125-125)
  • JSONRPCErrorDetails (367-376)
  • URL_ELICITATION_REQUIRED (405-405)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (11)
examples/elicitation/main.go (3)

240-298: LGTM! Well-documented URL elicitation demo.

The auth_via_url tool implementation correctly demonstrates the URL elicitation flow:

  • Generates a unique elicitation ID using UUID
  • Constructs a tracking URL with the elicitation ID
  • Calls RequestURLElicitation with proper parameters
  • Handles both accept and decline responses
  • Includes helpful comments explaining that in production, SendElicitationComplete would be called after receiving the authentication callback

The immediate call to SendElicitationComplete is appropriately documented as a simulation for demonstration purposes.


300-334: LGTM! Clear demonstration of error-triggered elicitation.

The protected_action tool correctly demonstrates returning URLElicitationRequiredError to signal that authorization is required. The ElicitationParams is properly populated with all URL-mode fields (Mode, ElicitationID, URL, Message).

The TODO comment at line 307 appropriately documents that the isAuthorized := false is for demonstration purposes.


3-14: Import ordering follows guidelines.

Standard library imports are listed first, followed by third-party packages (github.com/google/uuid), then local packages (github.com/mark3labs/mcp-go/*). This follows the Go import conventions enforced by goimports.

mcp/errors.go (2)

34-54: LGTM! URLElicitationRequiredError properly implemented.

The error type correctly:

  • Implements Error() with a descriptive message including elicitation count
  • Sets JSONRPC: JSONRPC_VERSION in JSONRPCError() (addressing the previous review feedback)
  • Uses the correct error code URL_ELICITATION_REQUIRED (-32042)
  • Includes the elicitations payload in the error data

66-70: LGTM! Is() method follows established pattern.

The Is() implementation enables errors.Is() matching for URLElicitationRequiredError, following the same pattern as UnsupportedProtocolVersionError.

mcp/types.go (4)

61-63: LGTM! Method constant for elicitation complete notification.

The new MethodNotificationElicitationComplete constant follows the established naming pattern for MCP methods and correctly defines the notification method name.


893-912: LGTM! ElicitationParams extended for URL mode support.

The struct now properly supports both elicitation modes:

  • Mode defaults to "form" when omitted (backward compatible)
  • Form-mode field RequestedSchema made optional for URL mode
  • URL-mode fields ElicitationID and URL added with appropriate omitempty tags
  • Added Meta field for optional metadata

The structure maintains backward compatibility with existing form-mode usage.


1301-1320: LGTM! ElicitationCapability and notification helper well implemented.

ElicitationCapability correctly uses the pointer-to-empty-struct pattern (*struct{}) for presence signaling, consistent with other capability structs in this file (e.g., Sampling, Logging).

NewElicitationCompleteNotification properly constructs a JSONRPCNotification using AdditionalFields in NotificationParams to carry the elicitationId, which aligns with how NotificationParams handles extra fields via its custom JSON marshaling.


499-499: Verify the backward compatibility interpretation for empty ElicitationCapability.

The Elicitation field was changed from *struct{} to *ElicitationCapability. The implementation creates empty ElicitationCapability{} structs (with both Form and URL nil), but there is no visible logic that interprets nil fields as "form-only support." Per PR objectives, this should maintain backward compatibility, but the current approach is ambiguous—an empty struct with nil fields doesn't explicitly declare which modes are supported. Confirm whether nil Form/URL fields should be interpreted as form-only, or if the struct should explicitly set Form = &struct{}{} to properly indicate form mode support.

Also applies to: 531-531

server/elicitation_test.go (2)

246-281: LGTM! Comprehensive URL elicitation test.

The test properly validates:

  1. RequestURLElicitation populates Mode, ElicitationID, and URL in the request params
  2. SendElicitationComplete delivers the notification through the session's channel
  3. The notification method matches "notifications/elicitation/complete"

The use of select with a 100ms timeout is appropriate for async notification verification in test environments.


82-83: LGTM! Test assertions follow testify guidelines.

Tests correctly use require for fatal assertions (e.g., error checks) and assert for value comparisons. This follows the coding guidelines for test files. The table-driven test pattern in TestRequestElicitation is well-structured.

Also applies to: 106-107, 146-152

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

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4b059b and 8a894ff.

📒 Files selected for processing (4)
  • mcp/errors.go (4 hunks)
  • mcp/types.go (6 hunks)
  • server/elicitation_test.go (8 hunks)
  • server/server.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • mcp/errors.go
  • server/server.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • mcp/types.go
  • server/elicitation_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Testing: use testify/assert and testify/require
Write table-driven tests using a tests := []struct{ name, ... } pattern
Go test files must end with _test.go

Files:

  • server/elicitation_test.go
🧠 Learnings (8)
📚 Learning: 2025-06-30T07:13:17.052Z
Learnt from: ezynda3
Repo: mark3labs/mcp-go PR: 461
File: server/sampling.go:22-26
Timestamp: 2025-06-30T07:13:17.052Z
Learning: In the mark3labs/mcp-go project, the MCPServer.capabilities field is a struct value (serverCapabilities), not a pointer, so it cannot be nil and doesn't require nil checking. Only pointer fields within the capabilities struct should be checked for nil.

Applied to files:

  • mcp/types.go
📚 Learning: 2025-03-04T06:59:43.882Z
Learnt from: xinwo
Repo: mark3labs/mcp-go PR: 35
File: mcp/tools.go:107-137
Timestamp: 2025-03-04T06:59:43.882Z
Learning: Tool responses from the MCP server shouldn't contain RawInputSchema, which is why the UnmarshalJSON method for the Tool struct is implemented to handle only the structured InputSchema format.

Applied to files:

  • mcp/types.go
📚 Learning: 2025-06-23T11:10:42.948Z
Learnt from: floatingIce91
Repo: mark3labs/mcp-go PR: 401
File: server/server.go:1082-1092
Timestamp: 2025-06-23T11:10:42.948Z
Learning: In Go MCP server, ServerTool.Tool field is only used for tool listing and indexing, not for tool execution or middleware. During handleToolCall, only the Handler field is used, so dynamic tools don't need the Tool field populated.

Applied to files:

  • mcp/types.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Testing: use testify/assert and testify/require

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-04-21T21:26:32.945Z
Learnt from: octo
Repo: mark3labs/mcp-go PR: 149
File: mcptest/mcptest.go:0-0
Timestamp: 2025-04-21T21:26:32.945Z
Learning: In the mcptest package, prefer returning errors from helper functions rather than calling t.Fatalf() directly, giving callers flexibility in how to handle errors.

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-08-24T00:52:47.610Z
Learnt from: semistrict
Repo: mark3labs/mcp-go PR: 495
File: server/elicitation_test.go:229-231
Timestamp: 2025-08-24T00:52:47.610Z
Learning: In Go, test files within the same package can access types and functions defined in other test files in that package. When analyzing test code that references undefined types, check other test files in the same package for shared test utilities and mock types.

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*.go : Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Write table-driven tests using a tests := []struct{ name, ... } pattern

Applied to files:

  • server/elicitation_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (9)
mcp/types.go (5)

61-63: LGTM!

The new method constant follows the established pattern for notification methods and includes appropriate documentation.


424-425: LGTM!

The new error code follows MCP conventions and is properly documented.


519-519: LGTM! Breaking change is intentional.

The change from *struct{} to *ElicitationCapability enables proper capability negotiation for form and URL modes, aligning with the MCP spec. This follows the pattern established by other structured capabilities like TasksCapability.

Also applies to: 553-553


1483-1487: LGTM!

The ElicitationCapability struct follows established patterns for capability types. Using optional pointer fields enables clear declaration of form and/or URL support while maintaining backward compatibility.


1489-1502: LGTM!

The notification constructor follows Go conventions and correctly uses AdditionalFields to include the elicitation ID in the notification payload.

server/elicitation_test.go (4)

7-7: LGTM!

The time import is necessary for timeout handling in the new URL elicitation test.


35-40: Good test mock improvements.

The additions enable better test verification:

  • lastRequest field allows asserting on exact parameters passed
  • Buffered notification channel (capacity 100) prevents blocking
  • Lazy initialization is efficient

These changes support the new URL elicitation test requirements.

Also applies to: 46-52, 61-61


83-84: LGTM!

The migration to testify assertions follows coding guidelines and improves test clarity. The use of require for fatal conditions and assert for multiple checks is appropriate.

Based on coding guidelines.

Also applies to: 107-109, 147-154, 231-242


212-212: Good consistency improvement.

Using the defined mockBasicSession type improves maintainability and keeps test setup consistent.

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)
server/elicitation_test.go (1)

247-286: Test scope doesn't match the test name.

TestRequestURLElicitation actually tests both RequestURLElicitation (lines 260-265) and SendElicitationComplete (lines 273-285). The test name should reflect the full scope, or the test should be split into two separate focused tests.

server/elicitation.go (1)

74-75: Remove duplicate comment.

Lines 74 and 75 contain identical comments. Remove one of them.

🔎 Proposed fix
-// SendElicitationComplete sends a notification that a URL mode elicitation has completed
 // SendElicitationComplete sends a notification that a URL mode elicitation has completed
 func (s *MCPServer) SendElicitationComplete(
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a894ff and 296e781.

📒 Files selected for processing (5)
  • client/client.go (2 hunks)
  • mcp/elicitation_validation_test.go (1 hunks)
  • mcp/types.go (6 hunks)
  • server/elicitation.go (1 hunks)
  • server/elicitation_test.go (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • client/client.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • server/elicitation_test.go
  • server/elicitation.go
  • mcp/elicitation_validation_test.go
  • mcp/types.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Testing: use testify/assert and testify/require
Write table-driven tests using a tests := []struct{ name, ... } pattern
Go test files must end with _test.go

Files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
🧠 Learnings (10)
📚 Learning: 2025-04-28T00:14:49.263Z
Learnt from: robert-jackson-glean
Repo: mark3labs/mcp-go PR: 214
File: server/sse.go:0-0
Timestamp: 2025-04-28T00:14:49.263Z
Learning: The SSE server in mcp-go implements path sanitization within the `WithDynamicBasePath` function that ensures the dynamic base path starts with "/" and has no trailing "/" to prevent double slashes in URL construction.

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Testing: use testify/assert and testify/require

Applied to files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
📚 Learning: 2025-04-21T21:26:32.945Z
Learnt from: octo
Repo: mark3labs/mcp-go PR: 149
File: mcptest/mcptest.go:0-0
Timestamp: 2025-04-21T21:26:32.945Z
Learning: In the mcptest package, prefer returning errors from helper functions rather than calling t.Fatalf() directly, giving callers flexibility in how to handle errors.

Applied to files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
📚 Learning: 2025-08-24T00:52:47.610Z
Learnt from: semistrict
Repo: mark3labs/mcp-go PR: 495
File: server/elicitation_test.go:229-231
Timestamp: 2025-08-24T00:52:47.610Z
Learning: In Go, test files within the same package can access types and functions defined in other test files in that package. When analyzing test code that references undefined types, check other test files in the same package for shared test utilities and mock types.

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*.go : Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Write table-driven tests using a tests := []struct{ name, ... } pattern

Applied to files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
📚 Learning: 2025-06-30T07:13:17.052Z
Learnt from: ezynda3
Repo: mark3labs/mcp-go PR: 461
File: server/sampling.go:22-26
Timestamp: 2025-06-30T07:13:17.052Z
Learning: In the mark3labs/mcp-go project, the MCPServer.capabilities field is a struct value (serverCapabilities), not a pointer, so it cannot be nil and doesn't require nil checking. Only pointer fields within the capabilities struct should be checked for nil.

Applied to files:

  • server/elicitation.go
  • mcp/types.go
📚 Learning: 2025-06-20T20:39:51.870Z
Learnt from: lariel-fernandes
Repo: mark3labs/mcp-go PR: 428
File: www/docs/pages/servers/prompts.mdx:218-234
Timestamp: 2025-06-20T20:39:51.870Z
Learning: In the mcp-go library, the GetPromptParams.Arguments field is of type map[string]string, not map[string]interface{}, so direct string access without type assertions is safe and correct.

Applied to files:

  • mcp/elicitation_validation_test.go
📚 Learning: 2025-03-04T06:59:43.882Z
Learnt from: xinwo
Repo: mark3labs/mcp-go PR: 35
File: mcp/tools.go:107-137
Timestamp: 2025-03-04T06:59:43.882Z
Learning: Tool responses from the MCP server shouldn't contain RawInputSchema, which is why the UnmarshalJSON method for the Tool struct is implemented to handle only the structured InputSchema format.

Applied to files:

  • mcp/types.go
📚 Learning: 2025-06-23T11:10:42.948Z
Learnt from: floatingIce91
Repo: mark3labs/mcp-go PR: 401
File: server/server.go:1082-1092
Timestamp: 2025-06-23T11:10:42.948Z
Learning: In Go MCP server, ServerTool.Tool field is only used for tool listing and indexing, not for tool execution or middleware. During handleToolCall, only the Handler field is used, so dynamic tools don't need the Tool field populated.

Applied to files:

  • mcp/types.go
🧬 Code graph analysis (2)
server/elicitation.go (4)
mcp/types.go (7)
  • Params (211-211)
  • ElicitationResult (965-968)
  • ElicitationParams (918-936)
  • ElicitationRequest (912-915)
  • Request (202-205)
  • MethodElicitationCreate (60-60)
  • NewElicitationCompleteNotification (1516-1528)
server/server.go (1)
  • MCPServer (154-183)
server/session.go (2)
  • ClientSession (12-21)
  • SessionWithElicitation (79-83)
mcp/consts.go (1)
  • ElicitationModeURL (11-11)
mcp/elicitation_validation_test.go (2)
mcp/types.go (1)
  • ElicitationParams (918-936)
mcp/consts.go (2)
  • ElicitationModeForm (10-10)
  • ElicitationModeURL (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (5)
server/elicitation.go (1)

28-30: LGTM!

The validation call ensures ElicitationParams are properly validated before being sent to the client, addressing the previous review feedback about mode-dependent field validation.

mcp/types.go (4)

61-63: LGTM!

The new notification method constant follows MCP naming conventions and is properly documented.


919-936: LGTM!

The ElicitationParams structure properly supports both form and URL modes with clear field separation and appropriate JSON tags. The mode discriminator pattern follows MCP conventions.


938-962: LGTM!

The Validate method correctly implements mode-specific validation, treating empty mode as form mode and enforcing required fields for each mode. This addresses the validation requirements from previous review feedback.


1509-1528: LGTM!

The ElicitationCapability type follows the MCP capability pattern (using *struct{} for presence indicators), and the NewElicitationCompleteNotification helper correctly constructs the notification with proper structure.

…peration and improve `ElicitationParams.Validate` error assertions.
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: 0

🧹 Nitpick comments (1)
server/elicitation_test.go (1)

247-286: Consider splitting or renaming test for clarity.

TestRequestURLElicitation tests both RequestURLElicitation (lines 260-265) and SendElicitationComplete (lines 267-285). Consider either:

  • Splitting into two separate test functions for better failure isolation, or
  • Renaming to TestRequestURLElicitation_WithNotificationComplete to reflect the full scope
🔎 Option 1: Split into two tests
 func TestRequestURLElicitation(t *testing.T) {
 	s := NewMCPServer("test", "1.0", WithElicitation())
 
 	mockSession := &mockElicitationSession{
 		sessionID: "test-url-1",
 		result: &mcp.ElicitationResult{
 			ElicitationResponse: mcp.ElicitationResponse{
 				Action: mcp.ElicitationResponseActionAccept,
 			},
 		},
 	}
 
 	ctx := context.Background()
 	_, err := s.RequestURLElicitation(ctx, mockSession, "id-123", "https://example.com/auth", "Please auth")
 	require.NoError(t, err)
 
 	assert.Equal(t, mcp.ElicitationModeURL, mockSession.lastRequest.Params.Mode)
 	assert.Equal(t, "id-123", mockSession.lastRequest.Params.ElicitationID)
 	assert.Equal(t, "https://example.com/auth", mockSession.lastRequest.Params.URL)
+}
+
+func TestSendElicitationComplete_WithNotification(t *testing.T) {
+	s := NewMCPServer("test", "1.0", WithElicitation())
 
 	notifyChan := make(chan mcp.JSONRPCNotification, 1)
-	mockSessionWithChan := &mockElicitationSession{
-		sessionID:  "test-url-2",
+	mockSession := &mockElicitationSession{
+		sessionID:  "test-session",
 		notifyChan: notifyChan,
 	}
 
-	err = s.SendElicitationComplete(ctx, mockSessionWithChan, "id-123")
+	err := s.SendElicitationComplete(context.Background(), mockSession, "id-123")
 	require.NoError(t, err)
 
 	select {
 	case notif := <-notifyChan:
 		assert.Equal(t, "notifications/elicitation/complete", notif.Method)
-		// Validate elicitationId is included in params
 		elicitationID, ok := notif.Params.AdditionalFields["elicitationId"]
 		assert.True(t, ok, "expected elicitationId in notification params")
 		assert.Equal(t, "id-123", elicitationID)
 	case <-time.After(100 * time.Millisecond):
 		t.Error("Expected notification not received")
 	}
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 296e781 and 9d571d3.

📒 Files selected for processing (2)
  • mcp/elicitation_validation_test.go (1 hunks)
  • server/elicitation_test.go (8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Order imports: standard library first, then third-party, then local packages (goimports enforces this)
Follow Go naming conventions: exported identifiers in PascalCase; unexported in camelCase; acronyms uppercase (HTTP, JSON, MCP)
Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As
Prefer explicit types and strongly-typed structs; avoid using any except where protocol flexibility is required (e.g., Arguments any)
All exported types and functions must have GoDoc comments starting with the identifier name; avoid inline comments unless necessary
Functions that are handlers or long-running must accept context.Context as the first parameter
Ensure thread safety for shared state using sync.Mutex and document thread-safety requirements in comments
For JSON: use json struct tags with omitempty for optional fields; use json.RawMessage for flexible/deferred parsing

Files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Testing: use testify/assert and testify/require
Write table-driven tests using a tests := []struct{ name, ... } pattern
Go test files must end with _test.go

Files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
🧠 Learnings (8)
📚 Learning: 2025-04-28T00:14:49.263Z
Learnt from: robert-jackson-glean
Repo: mark3labs/mcp-go PR: 214
File: server/sse.go:0-0
Timestamp: 2025-04-28T00:14:49.263Z
Learning: The SSE server in mcp-go implements path sanitization within the `WithDynamicBasePath` function that ensures the dynamic base path starts with "/" and has no trailing "/" to prevent double slashes in URL construction.

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Testing: use testify/assert and testify/require

Applied to files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
📚 Learning: 2025-04-21T21:26:32.945Z
Learnt from: octo
Repo: mark3labs/mcp-go PR: 149
File: mcptest/mcptest.go:0-0
Timestamp: 2025-04-21T21:26:32.945Z
Learning: In the mcptest package, prefer returning errors from helper functions rather than calling t.Fatalf() directly, giving callers flexibility in how to handle errors.

Applied to files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
📚 Learning: 2025-08-24T00:52:47.610Z
Learnt from: semistrict
Repo: mark3labs/mcp-go PR: 495
File: server/elicitation_test.go:229-231
Timestamp: 2025-08-24T00:52:47.610Z
Learning: In Go, test files within the same package can access types and functions defined in other test files in that package. When analyzing test code that references undefined types, check other test files in the same package for shared test utilities and mock types.

Applied to files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*.go : Error handling: return sentinel errors, wrap with fmt.Errorf("context: %w", err), and check with errors.Is/As

Applied to files:

  • server/elicitation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Applies to **/*_test.go : Write table-driven tests using a tests := []struct{ name, ... } pattern

Applied to files:

  • server/elicitation_test.go
  • mcp/elicitation_validation_test.go
📚 Learning: 2025-10-13T09:35:20.180Z
Learnt from: CR
Repo: mark3labs/mcp-go PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-13T09:35:20.180Z
Learning: Use the provided commands for testing, coverage, linting, and code generation (go test ./... -race, coverage command with exclusions, golangci-lint run, go generate ./...)

Applied to files:

  • mcp/elicitation_validation_test.go
📚 Learning: 2025-06-20T20:39:51.870Z
Learnt from: lariel-fernandes
Repo: mark3labs/mcp-go PR: 428
File: www/docs/pages/servers/prompts.mdx:218-234
Timestamp: 2025-06-20T20:39:51.870Z
Learning: In the mcp-go library, the GetPromptParams.Arguments field is of type map[string]string, not map[string]interface{}, so direct string access without type assertions is safe and correct.

Applied to files:

  • mcp/elicitation_validation_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (5)
mcp/elicitation_validation_test.go (1)

10-89: LGTM! Comprehensive validation test coverage.

The test follows all coding guidelines: table-driven pattern, testify assertions, and comprehensive coverage of both Form and URL mode validation scenarios. The previous review comment regarding testify usage has been addressed.

server/elicitation_test.go (4)

34-66: LGTM! Mock session improvements enable better test verification.

The addition of lastRequest and notifyChan fields with lazy channel initialization provides better test observability. The buffered channel (capacity 100) prevents blocking during tests with multiple notifications.


68-245: LGTM! Test updates follow coding guidelines.

Existing tests properly updated to use testify assertions (require.Error, require.NoError, assert.Equal) and follow error handling best practices with errors.Is.


288-311: LGTM! Independent notification scenario well-tested.

This test verifies SendElicitationComplete can send notifications independently without prior request state, addressing the previous review comment about duplicate tests. The test correctly validates the notification structure and elicitationId propagation.


247-286: Add security constraint tests for URL elicitation.

The PR implements server URL elicitation but the test at lines 247-286 only validates happy-path functionality. Per MCP specification requirements, servers must securely associate elicitation state with individual users (not session IDs alone), and user identification must be derived from MCP authorization credentials. Add tests covering:

  • URL format validation (parsing, scheme verification)
  • State binding to user identity rather than session alone
  • Rejection of HTTP URLs (HTTPS enforcement per security best practices)
  • Prevention of sensitive data in URL parameters

@ezynda3 ezynda3 merged commit 1e5bacc into mark3labs:main Dec 22, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Elicitation URL mode for MCP spec 2025-11-25

2 participants