-
Notifications
You must be signed in to change notification settings - Fork 34.5k
Description
Version: 1.101.0-insider (user setup)
Commit: b491282
Date: 2025-05-28T05:45:00.228Z
Electron: 35.4.0
ElectronBuildId: 11602177
Chromium: 134.0.6998.205
Node.js: 22.15.0
V8: 13.4.114.21-electron.0
OS: Windows_NT x64 10.0.19045
Steps to Reproduce:
I have created a small MCP server with webapi .net with minimal API with the code in the last part of this message.
On vscode insider I have setup the User settings.json file like this :
And also on vscode insider I have setup the mcp.json like this:
vscode calls the endpoint POST "/" with method = initialize and then method = notifications/initialized, it also start to negotiate SSE but I return 405, expecting that the endpoint for the manifest.json is called as a REST service. is the assumption correct ? do I need to do this only with SSE ?
after this initialization the settings.json on vscode insider shows that the server is running but it has 0 tools, as I understand this is a consequence due the fact the manifest was not called.
From the logs on vscode insider I have this :
Im asking this because vscode insider does not make any more call to my server after ending the negotiation of SSE.
I have test this also with http files like this :
POST http://localhost:5046/
Accept: application/json
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"roots":{"listChanged":true}},"clientInfo":{"name":"Visual Studio Code - Insiders","version":"1.101.0-insider"}}}
with the response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"resourceProvider": true
},
"serverInfo": {
"name": "kubernetes-mcp-server",
"version": "1.0.0"
}
}
}
and getting the manifest like this :
GET http://localhost:5046/.well-known/mcp/manifest.json
Accept: application/json
with the result :
{
"name": "kubernetes-mcp-server",
"resources": [
{
"id": "k8s-namespaces",
"displayName": "Kubernetes Namespaces",
"description": "Lists all namespaces in the connected Kubernetes cluster.",
"endpoint": "/mcp/k8s/namespaces",
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the namespace"
},
"uid": {
"type": "string",
"description": "Unique ID of the namespace"
}
},
"required": [
"name",
"uid"
],
"additionalProperties": null,
"description": "Represents a Kubernetes namespace"
},
"properties": null,
"required": null,
"additionalProperties": null,
"description": null
}
},
{
"id": "k8s-pods",
"displayName": "Kubernetes Pods",
"description": "Lists all pods across all namespaces.",
"endpoint": "/mcp/k8s/pods",
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Pod name"
},
"namespace": {
"type": "string",
"description": "Namespace the pod belongs to"
},
"status": {
"type": "string",
"description": "Current status of the pod"
}
},
"required": [
"name",
"namespace",
"status"
],
"additionalProperties": null,
"description": "Basic information about a pod"
},
"properties": null,
"required": null,
"additionalProperties": null,
"description": null
}
},
{
"id": "k8s-pod-details",
"displayName": "Pod Details",
"description": "Shows details about a specific pod given its name and namespace.",
"endpoint": "/mcp/k8s/pods/{namespace}/{name}",
"schema": {
"type": "object",
"items": null,
"properties": {
"name": {
"type": "string",
"description": "Pod name"
},
"namespace": {
"type": "string",
"description": "Namespace"
},
"containers": {
"type": "array",
"description": "List of container specs"
},
"status": {
"type": "string",
"description": "Pod status"
},
"nodeName": {
"type": "string",
"description": "Node where the pod is running"
}
},
"required": [
"name",
"namespace",
"containers",
"status"
],
"additionalProperties": null,
"description": "Detailed information about a specific pod"
}
}
],
"schemaVersion": "https://json-schema.org/draft/2020-12/schema"
}
I have enabled logs from the application to see if the request arrived to the server but for some reason it was not being delivered to the endpoint. from the logs its possible to see that there is no request to that endpoint :
dbug: Microsoft.AspNetCore.Hosting.Diagnostics[13]
Loaded hosting startup assembly KubernetesServer
dbug: Microsoft.AspNetCore.Hosting.Diagnostics[13]
Loaded hosting startup assembly Microsoft.WebTools.ApiEndpointDiscovery
dbug: Microsoft.AspNetCore.Hosting.Diagnostics[13]
Loaded hosting startup assembly Microsoft.AspNetCore.Watch.BrowserRefresh
dbug: Microsoft.AspNetCore.Hosting.Diagnostics[13]
Loaded hosting startup assembly Microsoft.WebTools.BrowserLink.Net
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 POST http://localhost:5046/ - application/json 216
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
1 candidate(s) found for the request path '/'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
Request matched endpoint 'HTTP: POST /'
[28/05/2025 16:28:54] POST /
Header: Accept = text/event-stream, application/json
Header: Connection = keep-alive
Header: Host = localhost:5046
Header: User-Agent = node
Header: Accept-Encoding = gzip, deflate
Header: Accept-Language = *
Header: Content-Type = application/json
Header: Content-Length = 216
Header: sec-fetch-mode = cors
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'HTTP: POST /'
Received JSON-RPC message:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"roots":{"listChanged":true}},"clientInfo":{"name":"Visual Studio Code - Insiders","version":"1.101.0-insider"}}}
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'HTTP: POST /'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 POST http://localhost:5046/ - 200 - application/json;+charset=utf-8 4736.2305ms
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 POST http://localhost:5046/ - application/json 54
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://localhost:5046/ - - -
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
1 candidate(s) found for the request path '/'
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
1 candidate(s) found for the request path '/'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
Request matched endpoint 'HTTP: GET /'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
Request matched endpoint 'HTTP: POST /'
[28/05/2025 16:28:59] GET /
[28/05/2025 16:28:59] POST /
Header: Accept = text/event-stream
Header: Connection = keep-alive
Header: Host = localhost:5046
Header: User-Agent = node
Header: Accept-Encoding = gzip, deflate
Header: Accept-Language = *
Header: Accept = text/event-stream, application/json
Header: Connection = keep-alive
Header: Host = localhost:5046
Header: User-Agent = node
Header: sec-fetch-mode = cors
Header: Accept-Encoding = gzip, deflate
Header: Accept-Language = *
Header: Content-Type = application/json
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'HTTP: GET /'
Received GET Root message:
Header: Content-Length = 54
Header: sec-fetch-mode = cors
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'HTTP: POST /'
Received JSON-RPC message:
{"method":"notifications/initialized","jsonrpc":"2.0"}
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'HTTP: GET /'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET http://localhost:5046/ - 405 0 - 3021.6077ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'HTTP: POST /'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 POST http://localhost:5046/ - 200 0 - 3023.6109ms
this is the webapi minimal apis implementation on program.cs running with visual studio .Net 8.0
using KubernetesServer.ApiModels;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole(); // Show logs in terminal
builder.Logging.SetMinimumLevel(LogLevel.Debug); // Set to Debug for detailed logs
builder.Services.AddCors();
var app = builder.Build();
app.Use(async (context, next) =>
{
Console.WriteLine($"[{DateTime.Now}] {context.Request.Method} {context.Request.Path}");
foreach (var header in context.Request.Headers)
{
Console.WriteLine($"Header: {header.Key} = {header.Value}");
}
await next();
});
app.MapPost("/", async (HttpContext context) =>
{
using var reader = new StreamReader(context.Request.Body);
var requestBody = await reader.ReadToEndAsync();
Console.WriteLine("Received JSON-RPC message:");
Console.WriteLine(requestBody);
// Try to parse method name from body
using var jsonDoc = JsonDocument.Parse(requestBody);
var root = jsonDoc.RootElement;
var method = root.GetProperty("method").GetString();
if (method == "notifications/initialized")
{
return Results.Ok();
}
if (method == "initialize")
{
var version = root.GetProperty("params").GetProperty("protocolVersion").GetString();
var id =Int32.Parse(root.GetProperty("id").ToString());
var response = new JsonRpcInitializeResponse
{
Id = id,
Result = new InitializeResult
{
ProtocolVersion = version,
Capabilities = new Capabilities { ResourceProvider = true },
ServerInfo = new ServerInfo
{
Name = "kubernetes-mcp-server",
Version = "1.0.0"
}
}
};
var result = Results.Json(response, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
return result;
}
// If unknown method, return method-not-found
return Results.Json(new
{
jsonrpc = "2.0",
error = new
{
code = -32601,
message = $"Method '{method}' not found"
}
});
});
app.MapGet("/", async (HttpContext context) =>
{
using var reader = new StreamReader(context.Request.Body);
var requestBody = await reader.ReadToEndAsync();
Console.WriteLine("Received GET Root message:");
Console.WriteLine(requestBody);
return Results.StatusCode(405);
});
app.MapGet("/.well-known/mcp/manifest.json", () =>
{
var namespaceSchema = new McpSchema(
Type: "array",
Items: new McpSchemaItems(
Type: "object",
Description: "Represents a Kubernetes namespace",
Properties: new Dictionary<string, McpSchemaProperty>
{
{ "name", new McpSchemaProperty("string", "The name of the namespace") },
{ "uid", new McpSchemaProperty("string", "Unique ID of the namespace") }
},
Required: new List<string> { "name", "uid" }
)
);
var podSchema = new McpSchema(
Type: "array",
Items: new McpSchemaItems(
Type: "object",
Description: "Basic information about a pod",
Properties: new Dictionary<string, McpSchemaProperty>
{
{ "name", new McpSchemaProperty("string", "Pod name") },
{ "namespace", new McpSchemaProperty("string", "Namespace the pod belongs to") },
{ "status", new McpSchemaProperty("string", "Current status of the pod") }
},
Required: new List<string> { "name", "namespace", "status" }
)
);
var podDetailSchema = new McpSchema(
Type: "object",
Description: "Detailed information about a specific pod",
Properties: new Dictionary<string, McpSchemaProperty>
{
{ "name", new McpSchemaProperty("string", "Pod name") },
{ "namespace", new McpSchemaProperty("string", "Namespace") },
{ "containers", new McpSchemaProperty("array", "List of container specs") },
{ "status", new McpSchemaProperty("string", "Pod status") },
{ "nodeName", new McpSchemaProperty("string", "Node where the pod is running") }
},
Required: new List<string> { "name", "namespace", "containers", "status" }
);
var resources = new[]
{
new McpResource(
Id: "k8s-namespaces",
DisplayName: "Kubernetes Namespaces",
Description: "Lists all namespaces in the connected Kubernetes cluster.",
Endpoint: "/mcp/k8s/namespaces",
Schema: namespaceSchema
),
new McpResource(
Id: "k8s-pods",
DisplayName: "Kubernetes Pods",
Description: "Lists all pods across all namespaces.",
Endpoint: "/mcp/k8s/pods",
Schema: podSchema
),
new McpResource(
Id: "k8s-pod-details",
DisplayName: "Pod Details",
Description: "Shows details about a specific pod given its name and namespace.",
Endpoint: "/mcp/k8s/pods/{namespace}/{name}",
Schema: podDetailSchema
)
};
var manifest = new McpManifest(
Name: "kubernetes-mcp-server",
Resources: resources,
SchemaVersion: "https://json-schema.org/draft/2020-12/schema"
);
return Results.Json(manifest, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
});
app.MapGet("/mcp/k8s/pods", () =>
{
var result = new[]
{
new { name="pod1" }
};
return Results.Ok(result);
});
app.MapGet("/mcp/k8s/namespaces", () =>
{
var result = new[]
{
new { name = "nginx-pod", ns = "default", status = "Running" },
new { name = "dns-pod", ns = "kube-system", status = "Running" },
new { name = "backend-api", ns= "my-namespace", status = "Pending" }
};
return Results.Ok(result);
});
app.MapGet("/mcp/k8s/pods/{namespace}/{name}", (string @namespace, string name) =>
{
var mockDetails = new
{
name = name,
@namespace = @namespace,
containers = new[]
{
new { name = "main", image = "nginx:latest", ports = new[] { 80 } },
new { name = "sidecar", image = "log-collector:1.0", ports = new[] { 514 } }
},
status = "Running",
nodeName = "node-01"
};
return Results.Ok(mockDetails);
});
app.UseCors(policy =>
policy
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
);
app.Run();