mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
Added a Skill for MCP
This commit is contained in:
parent
cf382122e9
commit
7b415d5388
7 changed files with 367 additions and 3 deletions
|
|
@ -66,9 +66,11 @@ public sealed class MccEmbeddedMcpHost
|
|||
builder.Logging.AddFilter(_ => false);
|
||||
builder.Services.AddSingleton(capabilities);
|
||||
builder.Services.AddSingleton(config);
|
||||
builder.Services.AddSingleton<MccMcpGuidanceProvider>();
|
||||
builder.Services.AddMcpServer()
|
||||
.WithHttpTransport()
|
||||
.WithTools<MccMcpToolSet>();
|
||||
.WithTools<MccMcpToolSet>()
|
||||
.WithPrompts<MccMcpPromptSet>();
|
||||
|
||||
builder.WebHost.UseUrls($"http://{bindHost}:{config.Transport.Port}");
|
||||
WebApplication builtApp = builder.Build();
|
||||
|
|
|
|||
236
MinecraftClient/Mcp/MccMcpGuidanceProvider.cs
Normal file
236
MinecraftClient/Mcp/MccMcpGuidanceProvider.cs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MinecraftClient.Mcp;
|
||||
|
||||
public sealed class MccMcpGuidanceProvider
|
||||
{
|
||||
private const string EmbeddedSkillResourceSuffix = "MccMcpOperatorSkill.md";
|
||||
private const string BestPracticesHeading = "## Best Practices";
|
||||
private const string ExampleScenariosHeading = "## Example Scenarios";
|
||||
|
||||
private readonly MccMcpConfig config;
|
||||
private readonly Lazy<GuidanceDocument> guidanceDocument;
|
||||
|
||||
public MccMcpGuidanceProvider(MccMcpConfig config)
|
||||
{
|
||||
this.config = config;
|
||||
guidanceDocument = new Lazy<GuidanceDocument>(LoadGuidanceDocument);
|
||||
}
|
||||
|
||||
public string SkillName => "mcc-mcp-operator";
|
||||
|
||||
public string GetSystemPrompt()
|
||||
{
|
||||
GuidanceDocument document = guidanceDocument.Value;
|
||||
MccMcpAgentCapabilityStatus capabilityStatus = BuildCapabilityStatus();
|
||||
StringBuilder builder = new();
|
||||
builder.AppendLine("You are an external agent controlling Minecraft Console Client (MCC) through its built-in MCP server.");
|
||||
builder.AppendLine("Use the following operator guide as your system prompt. Treat the capability snapshot as authoritative and do not invent unsupported actions.");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(document.BodyMarkdown);
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("Current capability snapshot");
|
||||
builder.AppendLine($"- sessionStatus: {FormatCapability(capabilityStatus.SessionStatus)}");
|
||||
builder.AppendLine($"- chatAndCommands: {FormatCapability(capabilityStatus.ChatAndCommands)}");
|
||||
builder.AppendLine($"- movement: {FormatCapability(capabilityStatus.Movement)}");
|
||||
builder.AppendLine($"- inventory: {FormatCapability(capabilityStatus.Inventory)}");
|
||||
builder.AppendLine($"- entityWorld: {FormatCapability(capabilityStatus.EntityWorld)}");
|
||||
return builder.ToString().Trim();
|
||||
}
|
||||
|
||||
public MccMcpAgentGuidancePayload GetToolPayload()
|
||||
{
|
||||
GuidanceDocument document = guidanceDocument.Value;
|
||||
return new MccMcpAgentGuidancePayload
|
||||
{
|
||||
SkillName = SkillName,
|
||||
SkillMarkdown = document.SkillMarkdown,
|
||||
SystemPrompt = GetSystemPrompt(),
|
||||
BestPractices = document.BestPractices,
|
||||
ExampleScenarios = document.ExampleScenarios,
|
||||
CapabilityStatus = BuildCapabilityStatus()
|
||||
};
|
||||
}
|
||||
|
||||
private GuidanceDocument LoadGuidanceDocument()
|
||||
{
|
||||
Assembly assembly = typeof(MccMcpGuidanceProvider).Assembly;
|
||||
string resourceName = assembly.GetManifestResourceNames()
|
||||
.FirstOrDefault(name => name.EndsWith(EmbeddedSkillResourceSuffix, StringComparison.Ordinal))
|
||||
?? throw new InvalidOperationException($"Embedded MCP skill resource '{EmbeddedSkillResourceSuffix}' was not found.");
|
||||
|
||||
using Stream? stream = assembly.GetManifestResourceStream(resourceName);
|
||||
if (stream is null)
|
||||
throw new InvalidOperationException($"Embedded MCP skill resource '{resourceName}' could not be opened.");
|
||||
|
||||
using StreamReader reader = new(stream, Encoding.UTF8);
|
||||
string skillMarkdown = reader.ReadToEnd();
|
||||
string bodyMarkdown = StripFrontmatter(skillMarkdown);
|
||||
string bestPracticesSection = ExtractSection(bodyMarkdown, BestPracticesHeading);
|
||||
string exampleScenariosSection = ExtractSection(bodyMarkdown, ExampleScenariosHeading);
|
||||
|
||||
return new GuidanceDocument(
|
||||
skillMarkdown.Replace("\r\n", "\n").Trim(),
|
||||
bodyMarkdown,
|
||||
ExtractBulletList(bestPracticesSection),
|
||||
ExtractExampleScenarios(exampleScenariosSection));
|
||||
}
|
||||
|
||||
private MccMcpAgentCapabilityStatus BuildCapabilityStatus()
|
||||
{
|
||||
return new MccMcpAgentCapabilityStatus
|
||||
{
|
||||
SessionStatus = config.Capabilities.SessionStatus,
|
||||
ChatAndCommands = config.Capabilities.ChatAndCommands,
|
||||
Movement = config.Capabilities.Movement,
|
||||
Inventory = config.Capabilities.Inventory,
|
||||
EntityWorld = config.Capabilities.EntityWorld
|
||||
};
|
||||
}
|
||||
|
||||
private static string StripFrontmatter(string markdown)
|
||||
{
|
||||
string normalized = markdown.Replace("\r\n", "\n");
|
||||
if (!normalized.StartsWith("---\n", StringComparison.Ordinal))
|
||||
return normalized.Trim();
|
||||
|
||||
int endOfFrontmatter = normalized.IndexOf("\n---\n", 4, StringComparison.Ordinal);
|
||||
if (endOfFrontmatter < 0)
|
||||
return normalized.Trim();
|
||||
|
||||
return normalized[(endOfFrontmatter + 5)..].Trim();
|
||||
}
|
||||
|
||||
private static string ExtractSection(string markdownBody, string heading)
|
||||
{
|
||||
int headingIndex = markdownBody.IndexOf(heading, StringComparison.Ordinal);
|
||||
if (headingIndex < 0)
|
||||
return string.Empty;
|
||||
|
||||
int sectionStart = headingIndex + heading.Length;
|
||||
int nextHeading = markdownBody.IndexOf("\n## ", sectionStart, StringComparison.Ordinal);
|
||||
string section = nextHeading >= 0
|
||||
? markdownBody[sectionStart..nextHeading]
|
||||
: markdownBody[sectionStart..];
|
||||
|
||||
return section.Trim();
|
||||
}
|
||||
|
||||
private static string[] ExtractBulletList(string section)
|
||||
{
|
||||
return section
|
||||
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Where(line => line.StartsWith("- ", StringComparison.Ordinal))
|
||||
.Select(line => line[2..].Trim())
|
||||
.Where(line => line.Length > 0)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static MccMcpAgentScenario[] ExtractExampleScenarios(string section)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(section))
|
||||
return [];
|
||||
|
||||
List<MccMcpAgentScenario> scenarios = [];
|
||||
string? currentTitle = null;
|
||||
List<string> currentBodyLines = [];
|
||||
|
||||
foreach (string rawLine in section.Split('\n'))
|
||||
{
|
||||
string line = rawLine.TrimEnd();
|
||||
if (line.StartsWith("### ", StringComparison.Ordinal))
|
||||
{
|
||||
AddScenario(scenarios, currentTitle, currentBodyLines);
|
||||
currentTitle = line[4..].Trim();
|
||||
currentBodyLines = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentTitle is not null)
|
||||
currentBodyLines.Add(line);
|
||||
}
|
||||
|
||||
AddScenario(scenarios, currentTitle, currentBodyLines);
|
||||
return scenarios.ToArray();
|
||||
}
|
||||
|
||||
private static void AddScenario(List<MccMcpAgentScenario> scenarios, string? title, List<string> bodyLines)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
return;
|
||||
|
||||
string guidance = string.Join('\n', bodyLines)
|
||||
.Trim();
|
||||
|
||||
scenarios.Add(new MccMcpAgentScenario
|
||||
{
|
||||
Title = title,
|
||||
Guidance = guidance
|
||||
});
|
||||
}
|
||||
|
||||
private static string FormatCapability(bool enabled)
|
||||
{
|
||||
return enabled ? "enabled" : "disabled";
|
||||
}
|
||||
|
||||
private sealed record GuidanceDocument(
|
||||
string SkillMarkdown,
|
||||
string BodyMarkdown,
|
||||
string[] BestPractices,
|
||||
MccMcpAgentScenario[] ExampleScenarios);
|
||||
}
|
||||
|
||||
public sealed class MccMcpAgentGuidancePayload
|
||||
{
|
||||
[JsonPropertyName("skillName")]
|
||||
public string SkillName { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("skillMarkdown")]
|
||||
public string SkillMarkdown { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("systemPrompt")]
|
||||
public string SystemPrompt { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("bestPractices")]
|
||||
public string[] BestPractices { get; init; } = [];
|
||||
|
||||
[JsonPropertyName("exampleScenarios")]
|
||||
public MccMcpAgentScenario[] ExampleScenarios { get; init; } = [];
|
||||
|
||||
[JsonPropertyName("capabilityStatus")]
|
||||
public MccMcpAgentCapabilityStatus CapabilityStatus { get; init; } = new();
|
||||
}
|
||||
|
||||
public sealed class MccMcpAgentScenario
|
||||
{
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("guidance")]
|
||||
public string Guidance { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class MccMcpAgentCapabilityStatus
|
||||
{
|
||||
[JsonPropertyName("sessionStatus")]
|
||||
public bool SessionStatus { get; init; }
|
||||
|
||||
[JsonPropertyName("chatAndCommands")]
|
||||
public bool ChatAndCommands { get; init; }
|
||||
|
||||
[JsonPropertyName("movement")]
|
||||
public bool Movement { get; init; }
|
||||
|
||||
[JsonPropertyName("inventory")]
|
||||
public bool Inventory { get; init; }
|
||||
|
||||
[JsonPropertyName("entityWorld")]
|
||||
public bool EntityWorld { get; init; }
|
||||
}
|
||||
20
MinecraftClient/Mcp/MccMcpPromptSet.cs
Normal file
20
MinecraftClient/Mcp/MccMcpPromptSet.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System.ComponentModel;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace MinecraftClient.Mcp;
|
||||
|
||||
public sealed class MccMcpPromptSet
|
||||
{
|
||||
private readonly MccMcpGuidanceProvider guidanceProvider;
|
||||
|
||||
public MccMcpPromptSet(MccMcpGuidanceProvider guidanceProvider)
|
||||
{
|
||||
this.guidanceProvider = guidanceProvider;
|
||||
}
|
||||
|
||||
[McpServerPrompt(Name = "mcc_operator_guide"), Description("Get the canonical MCC operator guidance prompt for external agents using this MCP server.")]
|
||||
public string OperatorGuide()
|
||||
{
|
||||
return guidanceProvider.GetSystemPrompt();
|
||||
}
|
||||
}
|
||||
|
|
@ -7,10 +7,12 @@ namespace MinecraftClient.Mcp;
|
|||
public sealed class MccMcpToolSet
|
||||
{
|
||||
private readonly IMccMcpCapabilities capabilities;
|
||||
private readonly MccMcpGuidanceProvider guidanceProvider;
|
||||
|
||||
public MccMcpToolSet(IMccMcpCapabilities capabilities)
|
||||
public MccMcpToolSet(IMccMcpCapabilities capabilities, MccMcpGuidanceProvider guidanceProvider)
|
||||
{
|
||||
this.capabilities = capabilities;
|
||||
this.guidanceProvider = guidanceProvider;
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_session_status"), Description("Get current MCC session and feature status.")]
|
||||
|
|
@ -49,6 +51,12 @@ public sealed class MccMcpToolSet
|
|||
return capabilities.GetInternalCommands();
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_agent_guidance"), Description("Get the canonical MCC operator guidance bundle for external agents using this MCP server.")]
|
||||
public object AgentGuidance()
|
||||
{
|
||||
return guidanceProvider.GetToolPayload();
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "mcc_materials_list"), Description("List known MCC material names with optional filtering.")]
|
||||
public object MaterialsList(string? filter = null, int maxCount = 500)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Physics\BlockShapeData.json" LogicalName="BlockShapeData.json" />
|
||||
<EmbeddedResource Include="..\.skills\mcc-mcp-operator\SKILL.md" Link="Mcp\EmbeddedSkills\SKILL.md" LogicalName="MccMcpOperatorSkill.md" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Protocol\Handlers\Compression\**" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue