Add framework for conceptual docs (#708)
Add framework for conceptual docs plus article on elicitation Co-authored-by: Stephen Halter <halter73@gmail.com>
This commit is contained in:
parent
b067261ca7
commit
15f8e89ac3
13 changed files with 441 additions and 3 deletions
4
.github/workflows/markdown-link-check.yml
vendored
4
.github/workflows/markdown-link-check.yml
vendored
|
|
@ -21,5 +21,5 @@ jobs:
|
||||||
- name: Markup Link Checker (mlc)
|
- name: Markup Link Checker (mlc)
|
||||||
uses: becheran/mlc@c925f90a9a25e16e4c4bfa29058f6f9ffa9f0d8c # v0.21.0
|
uses: becheran/mlc@c925f90a9a25e16e4c4bfa29058f6f9ffa9f0d8c # v0.21.0
|
||||||
with:
|
with:
|
||||||
# Ignore external links that result in 403 errors during CI. Do not warn for redirects where we want to keep the vanity URL in the markdown or for GitHub links that redirect to the login.
|
# Ignore external links that result in 403 errors during CI. Do not warn for redirects where we want to keep the vanity URL in the markdown or for GitHub links that redirect to the login, and DocFX snippet links.
|
||||||
args: --ignore-links "https://www.anthropic.com/*,https://hackerone.com/anthropic-vdp/*" --do-not-warn-for-redirect-to "https://modelcontextprotocol.io/*,https://github.com/login?*" ./
|
args: --ignore-links "https://www.anthropic.com/*,https://hackerone.com/anthropic-vdp/*" --do-not-warn-for-redirect-to "https://modelcontextprotocol.io/*,https://github.com/login?*" --ignore-links "*samples/*?name=snippet_*" ./docs
|
||||||
|
|
|
||||||
51
docs/concepts/elicitation/elicitation.md
Normal file
51
docs/concepts/elicitation/elicitation.md
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
title: Elicitation
|
||||||
|
author: mikekistler
|
||||||
|
description: Learn about the telemetry collected by the HttpRepl.
|
||||||
|
uid: elicitation
|
||||||
|
---
|
||||||
|
|
||||||
|
The **elicitation** feature allows servers to request additional information from users during interactions. This enables more dynamic and interactive AI experiences, making it easier to gather necessary context before executing tasks.
|
||||||
|
|
||||||
|
## Server Support for Elicitation
|
||||||
|
|
||||||
|
Servers request structured data from users with the [ElicitAsync] extension method on [IMcpServer].
|
||||||
|
The C# SDK registers an instance of [IMcpServer] with the dependency injection container,
|
||||||
|
so tools can simply add a parameter of type [IMcpServer] to their method signature to access it.
|
||||||
|
|
||||||
|
[ElicitAsync]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.McpServerExtensions.html#ModelContextProtocol_Server_McpServerExtensions_ElicitAsync_ModelContextProtocol_Server_IMcpServer_ModelContextProtocol_Protocol_ElicitRequestParams_System_Threading_CancellationToken_
|
||||||
|
[IMcpServer]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.IMcpServer.html
|
||||||
|
|
||||||
|
The MCP Server must specify the schema of each input value it is requesting from the user.
|
||||||
|
Only primitive types (string, number, boolean) are supported for elicitation requests.
|
||||||
|
The schema may include a description to help the user understand what is being requested.
|
||||||
|
|
||||||
|
The server can request a single input or multiple inputs at once.
|
||||||
|
To help distinguish multiple inputs, each input has a unique name.
|
||||||
|
|
||||||
|
The following example demonstrates how a server could request a boolean response from the user.
|
||||||
|
|
||||||
|
[!code-csharp[](samples/server/Tools/InteractiveTools.cs?name=snippet_GuessTheNumber)]
|
||||||
|
|
||||||
|
## Client Support for Elicitation
|
||||||
|
|
||||||
|
Elicitation is an optional feature so clients declare their support for it in their capabilities as part of the `initialize` request. In the MCP C# SDK, this is done by configuring an [ElicitationHandler] in the [McpClientOptions]:
|
||||||
|
|
||||||
|
[ElicitationHandler]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Protocol.ElicitationCapability.html#ModelContextProtocol_Protocol_ElicitationCapability_ElicitationHandler
|
||||||
|
[McpClientOptions]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Client.McpClientOptions.html
|
||||||
|
|
||||||
|
[!code-csharp[](samples/client/Program.cs?name=snippet_McpInitialize)]
|
||||||
|
|
||||||
|
The ElicitationHandler is an asynchronous method that will be called when the server requests additional information.
|
||||||
|
The ElicitationHandler must request input from the user and return the data in a format that matches the requested schema.
|
||||||
|
This will be highly dependent on the client application and how it interacts with the user.
|
||||||
|
|
||||||
|
If the user provides the requested information, the ElicitationHandler should return an [ElicitResult] with the action set to "accept" and the content containing the user's input.
|
||||||
|
If the user does not provide the requested information, the ElicitationHandler should return an [ElicitResult] with the action set to "reject" and no content.
|
||||||
|
|
||||||
|
[ElicitResult]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Protocol.ElicitResult.html
|
||||||
|
|
||||||
|
Below is an example of how a console application might handle elicitation requests.
|
||||||
|
Here's an example implementation:
|
||||||
|
|
||||||
|
[!code-csharp[](samples/client/Program.cs?name=snippet_ElicitationHandler)]
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ModelContextProtocol.Core" Version="0.3.0-preview.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
121
docs/concepts/elicitation/samples/client/Program.cs
Normal file
121
docs/concepts/elicitation/samples/client/Program.cs
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using ModelContextProtocol.Client;
|
||||||
|
using ModelContextProtocol.Protocol;
|
||||||
|
|
||||||
|
var endpoint = Environment.GetEnvironmentVariable("ENDPOINT") ?? "http://localhost:3001";
|
||||||
|
|
||||||
|
var clientTransport = new SseClientTransport(new()
|
||||||
|
{
|
||||||
|
Endpoint = new Uri(endpoint),
|
||||||
|
TransportMode = HttpTransportMode.StreamableHttp,
|
||||||
|
});
|
||||||
|
|
||||||
|
// <snippet_McpInitialize>
|
||||||
|
McpClientOptions options = new()
|
||||||
|
{
|
||||||
|
ClientInfo = new()
|
||||||
|
{
|
||||||
|
Name = "ElicitationClient",
|
||||||
|
Version = "1.0.0"
|
||||||
|
},
|
||||||
|
Capabilities = new()
|
||||||
|
{
|
||||||
|
Elicitation = new()
|
||||||
|
{
|
||||||
|
ElicitationHandler = HandleElicitationAsync
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await using var mcpClient = await McpClientFactory.CreateAsync(clientTransport, options);
|
||||||
|
// </snippet_McpInitialize>
|
||||||
|
|
||||||
|
var tools = await mcpClient.ListToolsAsync();
|
||||||
|
foreach (var tool in tools)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Connected to server with tools: {tool.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Calling tool: {tools.First().Name}");
|
||||||
|
|
||||||
|
var result = await mcpClient.CallToolAsync(toolName: tools.First().Name);
|
||||||
|
|
||||||
|
foreach (var block in result.Content)
|
||||||
|
{
|
||||||
|
if (block is TextContentBlock textBlock)
|
||||||
|
{
|
||||||
|
Console.WriteLine(textBlock.Text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Received unexpected result content of type {block.GetType()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <snippet_ElicitationHandler>
|
||||||
|
async ValueTask<ElicitResult> HandleElicitationAsync(ElicitRequestParams? requestParams, CancellationToken token)
|
||||||
|
{
|
||||||
|
// Bail out if the requestParams is null or if the requested schema has no properties
|
||||||
|
if (requestParams?.RequestedSchema?.Properties == null)
|
||||||
|
{
|
||||||
|
return new ElicitResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the elicitation request
|
||||||
|
if (requestParams?.Message is not null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(requestParams.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = new Dictionary<string, JsonElement>();
|
||||||
|
|
||||||
|
// Loop through requestParams.requestSchema.Properties dictionary requesting values for each property
|
||||||
|
foreach (var property in requestParams.RequestedSchema.Properties)
|
||||||
|
{
|
||||||
|
if (property.Value is ElicitRequestParams.BooleanSchema booleanSchema)
|
||||||
|
{
|
||||||
|
Console.Write($"{booleanSchema.Description}: ");
|
||||||
|
var clientInput = Console.ReadLine();
|
||||||
|
bool parsedBool;
|
||||||
|
|
||||||
|
// Try standard boolean parsing first
|
||||||
|
if (bool.TryParse(clientInput, out parsedBool))
|
||||||
|
{
|
||||||
|
content[property.Key] = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(parsedBool));
|
||||||
|
}
|
||||||
|
// Also accept "yes"/"no" as valid boolean inputs
|
||||||
|
else if (string.Equals(clientInput?.Trim(), "yes", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
content[property.Key] = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(true));
|
||||||
|
}
|
||||||
|
else if (string.Equals(clientInput?.Trim(), "no", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
content[property.Key] = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (property.Value is ElicitRequestParams.NumberSchema numberSchema)
|
||||||
|
{
|
||||||
|
Console.Write($"{numberSchema.Description}: ");
|
||||||
|
var clientInput = Console.ReadLine();
|
||||||
|
double parsedNumber;
|
||||||
|
if (double.TryParse(clientInput, out parsedNumber))
|
||||||
|
{
|
||||||
|
content[property.Key] = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(parsedNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (property.Value is ElicitRequestParams.StringSchema stringSchema)
|
||||||
|
{
|
||||||
|
Console.Write($"{stringSchema.Description}: ");
|
||||||
|
var clientInput = Console.ReadLine();
|
||||||
|
content[property.Key] = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(clientInput));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the user's input
|
||||||
|
return new ElicitResult
|
||||||
|
{
|
||||||
|
Action = "accept",
|
||||||
|
Content = content
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// </snippet_ElicitationHandler>
|
||||||
13
docs/concepts/elicitation/samples/server/Elicitation.csproj
Normal file
13
docs/concepts/elicitation/samples/server/Elicitation.csproj
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.3.0-preview.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
58
docs/concepts/elicitation/samples/server/Elicitation.http
Normal file
58
docs/concepts/elicitation/samples/server/Elicitation.http
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
@HostAddress = http://localhost:3001
|
||||||
|
|
||||||
|
# No session ID, so elicitation capabilities not declared.
|
||||||
|
|
||||||
|
POST {{HostAddress}}/
|
||||||
|
Accept: application/json, text/event-stream
|
||||||
|
Content-Type: application/json
|
||||||
|
MCP-Protocol-Version: 2025-06-18
|
||||||
|
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 2,
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "guess_the_number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
POST {{HostAddress}}/
|
||||||
|
Accept: application/json, text/event-stream
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "initialize",
|
||||||
|
"params": {
|
||||||
|
"clientInfo": {
|
||||||
|
"name": "RestClient",
|
||||||
|
"version": "0.1.0"
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"elicitation": {}
|
||||||
|
},
|
||||||
|
"protocolVersion": "2025-06-18"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
@SessionId = lgEu87uKTy8kLffZayO5rQ
|
||||||
|
|
||||||
|
POST {{HostAddress}}/
|
||||||
|
Accept: application/json, text/event-stream
|
||||||
|
Content-Type: application/json
|
||||||
|
Mcp-Session-Id: {{SessionId}}
|
||||||
|
MCP-Protocol-Version: 2025-06-18
|
||||||
|
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 2,
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "guess_the_number"
|
||||||
|
}
|
||||||
|
}
|
||||||
24
docs/concepts/elicitation/samples/server/Program.cs
Normal file
24
docs/concepts/elicitation/samples/server/Program.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
using Elicitation.Tools;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.AddMcpServer()
|
||||||
|
.WithHttpTransport(options =>
|
||||||
|
options.IdleTimeout = Timeout.InfiniteTimeSpan // Never timeout
|
||||||
|
)
|
||||||
|
.WithTools<InteractiveTools>();
|
||||||
|
|
||||||
|
builder.Logging.AddConsole(options =>
|
||||||
|
{
|
||||||
|
options.LogToStandardErrorThreshold = LogLevel.Information;
|
||||||
|
});
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.MapMcp();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "http://localhost:3001",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "https://localhost:7133;http://localhost:3001",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Text.Json;
|
||||||
|
using ModelContextProtocol;
|
||||||
|
using ModelContextProtocol.Protocol;
|
||||||
|
using ModelContextProtocol.Server;
|
||||||
|
using static ModelContextProtocol.Protocol.ElicitRequestParams;
|
||||||
|
|
||||||
|
namespace Elicitation.Tools;
|
||||||
|
|
||||||
|
[McpServerToolType]
|
||||||
|
public sealed class InteractiveTools
|
||||||
|
{
|
||||||
|
// <snippet_GuessTheNumber>
|
||||||
|
[McpServerTool, Description("A simple game where the user has to guess a number between 1 and 10.")]
|
||||||
|
public async Task<string> GuessTheNumber(
|
||||||
|
IMcpServer server, // Get the McpServer from DI container
|
||||||
|
CancellationToken token
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Check if the client supports elicitation
|
||||||
|
if (server.ClientCapabilities?.Elicitation == null)
|
||||||
|
{
|
||||||
|
// fail the tool call
|
||||||
|
throw new McpException("Client does not support elicitation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// First ask the user if they want to play
|
||||||
|
var playSchema = new RequestSchema
|
||||||
|
{
|
||||||
|
Properties =
|
||||||
|
{
|
||||||
|
["Answer"] = new BooleanSchema()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var playResponse = await server.ElicitAsync(new ElicitRequestParams
|
||||||
|
{
|
||||||
|
Message = "Do you want to play a game?",
|
||||||
|
RequestedSchema = playSchema
|
||||||
|
}, token);
|
||||||
|
|
||||||
|
// Check if user wants to play
|
||||||
|
if (playResponse.Action != "accept" || playResponse.Content?["Answer"].ValueKind != JsonValueKind.True)
|
||||||
|
{
|
||||||
|
return "Maybe next time!";
|
||||||
|
}
|
||||||
|
// </snippet_GuessTheNumber>
|
||||||
|
|
||||||
|
// Now ask the user to enter their name
|
||||||
|
var nameSchema = new RequestSchema
|
||||||
|
{
|
||||||
|
Properties =
|
||||||
|
{
|
||||||
|
["Name"] = new StringSchema()
|
||||||
|
{
|
||||||
|
Description = "Name of the player",
|
||||||
|
MinLength = 2,
|
||||||
|
MaxLength = 50,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var nameResponse = await server.ElicitAsync(new ElicitRequestParams
|
||||||
|
{
|
||||||
|
Message = "What is your name?",
|
||||||
|
RequestedSchema = nameSchema
|
||||||
|
}, token);
|
||||||
|
|
||||||
|
if (nameResponse.Action != "accept")
|
||||||
|
{
|
||||||
|
return "Maybe next time!";
|
||||||
|
}
|
||||||
|
string? playerName = nameResponse.Content?["Name"].GetString();
|
||||||
|
|
||||||
|
// Generate a random number between 1 and 10
|
||||||
|
Random random = new Random();
|
||||||
|
int targetNumber = random.Next(1, 11); // 1 to 10 inclusive
|
||||||
|
int attempts = 0;
|
||||||
|
|
||||||
|
var message = "Guess a number between 1 and 10";
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
attempts++;
|
||||||
|
|
||||||
|
var guessSchema = new RequestSchema
|
||||||
|
{
|
||||||
|
Properties =
|
||||||
|
{
|
||||||
|
["Guess"] = new NumberSchema()
|
||||||
|
{
|
||||||
|
Type = "integer",
|
||||||
|
Minimum = 1,
|
||||||
|
Maximum = 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var guessResponse = await server.ElicitAsync(new ElicitRequestParams
|
||||||
|
{
|
||||||
|
Message = message,
|
||||||
|
RequestedSchema = guessSchema
|
||||||
|
}, token);
|
||||||
|
|
||||||
|
if (guessResponse.Action != "accept")
|
||||||
|
{
|
||||||
|
return "Maybe next time!";
|
||||||
|
}
|
||||||
|
int guess = (int)(guessResponse.Content?["Guess"].GetInt32())!;
|
||||||
|
|
||||||
|
// Check if the guess is correct
|
||||||
|
if (guess == targetNumber)
|
||||||
|
{
|
||||||
|
return $"Congratulations {playerName}! You guessed the number {targetNumber} in {attempts} attempts!";
|
||||||
|
}
|
||||||
|
else if (guess < targetNumber)
|
||||||
|
{
|
||||||
|
message = $"Your guess is too low! Try again (Attempt #{attempts}):";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = $"Your guess is too high! Try again (Attempt #{attempts}):";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
docs/concepts/index.md
Normal file
2
docs/concepts/index.md
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
Welcome to the conceptual documentation for the Model Context Protocol SDK. Here you'll find high-level overviews, explanations, and guides to help you understand how the SDK implements the Model Context Protocol.
|
||||||
5
docs/concepts/toc.yml
Normal file
5
docs/concepts/toc.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
items:
|
||||||
|
- name: Overview
|
||||||
|
href: index.md
|
||||||
|
- name: Elicitation
|
||||||
|
uid: elicitation
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
"_appLogoPath": "images/mcp.svg",
|
"_appLogoPath": "images/mcp.svg",
|
||||||
"_appFaviconPath": "images/favicon.ico",
|
"_appFaviconPath": "images/favicon.ico",
|
||||||
"_enableSearch": true,
|
"_enableSearch": true,
|
||||||
|
"_disableNextArticle": true,
|
||||||
"pdf": false
|
"pdf": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
items:
|
items:
|
||||||
- name: API Docs
|
- name: Documentation
|
||||||
|
href: concepts/index.md
|
||||||
|
- name: API Reference
|
||||||
href: api/ModelContextProtocol.yml
|
href: api/ModelContextProtocol.yml
|
||||||
- name: Github
|
- name: Github
|
||||||
href: https://github.com/ModelContextProtocol/csharp-sdk
|
href: https://github.com/ModelContextProtocol/csharp-sdk
|
||||||
Loading…
Reference in a new issue