Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 113 additions & 7 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
name: Publish Docs

# Publishes the versioned docs site to GitHub Pages.
#
# Discovers the versions to publish from the repository's GitHub releases (the
# latest release, by date, for each major >= 1), builds each release tag into
# its own sub-path (e.g. /v1/ and /v2/) with make generate-docs, injects a
# version-picker widget into each page, adds a root redirect to the default
# version, and deploys the combined site. A manual dispatch can also rebuild
# the matching major-version path from a specified branch, tag, or commit.
# Triggers on release publish and manual dispatch.

on:
release:
types: [published]
workflow_dispatch:
inputs:
docs_ref:
description: Branch, tag, or commit whose docs should refresh its matching major version
required: false
type: string

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write # Required for actions/deploy-pages

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
# Allow only one concurrent deployment. Cancel any in-progress run when a newer one
# starts: every run rediscovers the current releases and rebuilds the whole site from
# scratch, so a newer run fully supersedes the work of the one it cancels.
concurrency:
group: "pages"
cancel-in-progress: false
cancel-in-progress: true

jobs:
publish-docs:
Expand All @@ -26,8 +42,14 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
- name: Checkout docs orchestration
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# A release event otherwise checks out the released tag. Keep the
# orchestration scripts and picker assets current with main.
ref: main
# Full history + tags so we can add a worktree for each version's release tag.
fetch-depth: 0
Comment thread
jeffhandley marked this conversation as resolved.

- name: .NET Setup
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
Expand All @@ -36,13 +58,97 @@ jobs:
10.0.x
9.0.x

- name: Generate documentation
run: make generate-docs
- name: Discover versions from GitHub releases
shell: bash
env:
GH_TOKEN: ${{ github.token }}
# For every major version >= 1, take the most recently published (by date)
# non-draft release tagged v{major}.*. The vN prefix becomes the URL slug;
# the site root redirects to the newest release, including prereleases.
run: |
set -euo pipefail
gh release list --repo "${{ github.repository }}" --limit 200 \
--json tagName,isPrerelease,isDraft,publishedAt \
--jq '
[ .[]
| select((.isDraft | not) and (.tagName | test("^v[1-9][0-9]*\\.")))
| { slug: (.tagName | match("^v[0-9]+").string),
ref: .tagName, label: .tagName,
prerelease: .isPrerelease, published: .publishedAt }
]
| group_by(.slug)
| map(max_by(.published))
| sort_by(.published) | reverse
| { default: .[0].slug,
versions: map({ slug, ref, label, prerelease }) }
' | tee "$RUNNER_TEMP/docs-versions.json"

- name: Build all versions
shell: bash
env:
CONTENT_REF: ${{ inputs.docs_ref }}
run: |
set -euo pipefail
ROOT="$PWD"
COMBINED="$ROOT/combined"
rm -rf "$COMBINED"
mkdir -p "$COMBINED"

# Orchestration (discovered docs-versions.json, scripts, picker assets)
# always comes from THIS branch. Normally each version's HTML is produced
# by its release tag. A manual docs_ref replaces the matching major's
# HTML with content built from that ref, allowing content-only refreshes
# without minting a product release.
git fetch --tags --force origin
CONTENT_REF="${CONTENT_REF:-}"
CONTENT_WORKTREE=""
CONTENT_SLUG=""
if [[ -n "$CONTENT_REF" ]]; then
CONTENT_WORKTREE="$ROOT/../work-content"
git worktree add --force --detach "$CONTENT_WORKTREE" "$CONTENT_REF"
CONTENT_SLUG="$(node "$ROOT/scripts/get-docs-version-slug.mjs" "$CONTENT_WORKTREE/src/Directory.Build.props")"
if ! node "$ROOT/scripts/list-versions.mjs" | cut -f1 | grep -Fxq "$CONTENT_SLUG"; then
echo "::error::The docs ref '$CONTENT_REF' has major '$CONTENT_SLUG', which has no published release."
exit 1
fi
echo "Refreshing $CONTENT_SLUG docs from $CONTENT_REF"
fi

while IFS=$'\t' read -r slug ref; do
if [[ "$slug" == "$CONTENT_SLUG" ]]; then
echo "::group::Build $slug (from ref $CONTENT_REF)"
wt="$CONTENT_WORKTREE"
else
echo "::group::Build $slug (from tag $ref)"
wt="$ROOT/../work-$slug"
git worktree add --force --detach "$wt" "refs/tags/$ref"
fi

make -C "$wt" generate-docs

mkdir -p "$COMBINED/$slug"
cp -a "$wt/artifacts/_site/." "$COMBINED/$slug/"
node "$ROOT/scripts/inject-version-picker.mjs" "$COMBINED/$slug" "$slug" --base /

if [[ "$wt" != "$CONTENT_WORKTREE" ]]; then
git worktree remove --force "$wt"
fi
echo "::endgroup::"
done < <(node "$ROOT/scripts/list-versions.mjs")

if [[ -n "$CONTENT_WORKTREE" ]]; then
git worktree remove --force "$CONTENT_WORKTREE"
fi

node "$ROOT/scripts/finalize-docs-site.mjs" "$COMBINED"

echo "Combined site contents:"
ls -la "$COMBINED"

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: 'artifacts/_site'
path: 'combined'

- name: Deploy to GitHub Pages
id: deployment
Expand Down
20 changes: 10 additions & 10 deletions docs/concepts/elicitation/elicitation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ The protocol supports two modes of elicitation:
- **Form (In-Band)**: The server requests structured data (strings, numbers, Booleans, enums) which the client collects via a form interface and returns to the server.
- **URL Mode**: The server provides a URL for the user to visit (for example, for OAuth, payments, or sensitive data entry). The interaction happens outside the MCP client.

### Server Support for Elicitation
### Server support for elicitation

Servers request information from users with the <xref:ModelContextProtocol.Server.McpServer.ElicitAsync*> extension method on <xref:ModelContextProtocol.Server.McpServer>.
The C# SDK registers an instance of <xref:ModelContextProtocol.Server.McpServer> with the dependency injection container,
so tools can simply add a parameter of type <xref:ModelContextProtocol.Server.McpServer> to their method signature to access it.

#### Form Mode Elicitation (In-Band)
#### Form mode elicitation (in-band)

For form-based elicitation, the MCP Server must specify the schema of each input value it's requesting from the user.
Primitive types (string, number, Boolean) and enum types are supported for elicitation requests.
Expand Down Expand Up @@ -115,7 +115,7 @@ The following example demonstrates how a server could request a Boolean response

[!code-csharp[](samples/server/Tools/InteractiveTools.cs?name=snippet_GuessTheNumber)]

#### URL Mode Elicitation (Out-of-Band)
#### URL mode elicitation (out-of-band)

For URL mode elicitation, the server provides a URL that the user must visit to complete an action. This is useful for scenarios like OAuth flows, payment processing, or collecting sensitive credentials that should not be exposed to the MCP client.

Expand All @@ -134,7 +134,7 @@ var result = await server.ElicitAsync(
cancellationToken);
```

### Client Support for Elicitation
### Client support for elicitation

Clients declare their support for elicitation in their capabilities as part of the `initialize` request. Clients can support `Form` (in-band), `Url` (out-of-band), or both.

Expand Down Expand Up @@ -170,7 +170,7 @@ Here's an example implementation of how a console application might handle elici

[!code-csharp[](samples/client/Program.cs?name=snippet_ElicitationHandler)]

### Multi Round-Trip Requests (MRTR)
### Multi round-trip requests (MRTR)

[MRTR](xref:mrtr) is the SEP-2322 mechanism for server-driven input requests, finalized in protocol revision `2026-07-28`. In that revision, the server-to-client `elicitation/create` request method is removed; the recommended way to ask the user for input from a server handler is to throw <xref:ModelContextProtocol.Protocol.InputRequiredException> and let the SDK emit an <xref:ModelContextProtocol.Protocol.InputRequiredResult> on the wire.

Expand All @@ -196,7 +196,7 @@ public static string ElicitWithMrtr(

if (!server.IsMrtrSupported)
{
return "This tool requires MRTR support (2026-07-28, or a stateful current-protocol session).";
return "This tool requires MRTR support (2026-07-28, or a stateful session using protocol revision 2025-11-25).";
}

// First call — request user input
Expand Down Expand Up @@ -225,11 +225,11 @@ public static string ElicitWithMrtr(
> [!TIP]
> For the full protocol details, including multiple round trips, concurrent input requests, and the compatibility matrix, see [Multi Round-Trip Requests (MRTR)](xref:mrtr).

### URL Elicitation Required Error
### URL elicitation required error

When a tool cannot proceed without first completing a URL-mode elicitation (for example, when third-party OAuth authorization is needed), and calling `ElicitAsync` is not practical (for example, in [stateless](xref:stateless) mode where server-to-client requests are disabled), the server might throw a <xref:ModelContextProtocol.UrlElicitationRequiredException>. This is a specialized error (JSON-RPC error code `-32042`) that signals to the client that one or more URL-mode elicitations must be completed before the original request can be retried.

#### Throwing UrlElicitationRequiredException on the Server
#### Throwing `UrlElicitationRequiredException` on the server

A server tool can throw `UrlElicitationRequiredException` when it detects that authorization or other out-of-band interaction is required:

Expand Down Expand Up @@ -267,7 +267,7 @@ public async Task<string> AccessThirdPartyResource(McpServer server, Cancellatio

The exception can include multiple elicitations if the operation requires authorization from multiple services.

#### Catching UrlElicitationRequiredException on the Client
#### Catching `UrlElicitationRequiredException` on the client

When the client calls a tool and receives a `UrlElicitationRequiredException`, it should:

Expand Down Expand Up @@ -317,7 +317,7 @@ catch (UrlElicitationRequiredException ex)
}
```

#### Listening for Elicitation Completion Notifications
#### Listening for elicitation completion notifications

Servers can optionally send a `notifications/elicitation/complete` notification when the out-of-band interaction is complete. Clients can register a handler to receive these notifications:

Expand Down
Loading