MCP Experiments
An experimental dotnet MCP server that returns the current time, based on Laurent Kempé's tutorial.
MCP Experiments
These MCP experiments are a playground for better understanding the technology.
aspire run
This system makes the following endpoints available via its gateway:
- /my-mcp-server/mcp Experimental MCP Server
- /my-mcp-web-client Experimental MCP Host
- /identity Identity Server
Deploy as Azure Container Apps
$env:ASPIRE_CONTAINER_RUNTIME='podman'; aspire deploy
ASPIRE_CONTAINER_RUNTIME=podman aspire deploy
Clients & Compatibility
I have tested MCP authentication with the following clients.
csharp-sdk
var httpClientTransport = new HttpClientTransport(new()
{
Name = "Vibe MCP Server",
Endpoint = new Uri("https://gateway.gentlemeadow-305c776b.germanywestcentral.azurecontainerapps.io/my-mcp-server/mcp"),
TransportMode = HttpTransportMode.StreamableHttp,
OAuth = new()
{
ClientId = "mcp_console",
RedirectUri = new Uri("http://localhost:1179/callback"),
AuthorizationRedirectDelegate = AuthorizationUrl.Handle,
TokenCache = tokenCache,
},
}, http);
await using var mcpClient = await McpClient.CreateAsync(httpClientTransport);
Works great.
MCP Inspector
npx @modelcontextprotocol/inspector
⚠️ MCP inspector currently does not follow the resource_metadata URI of the WWW-Authenticate response header to locate the protected resource metadata according to Section 5 of RFC9728 (OAuth flow does not support resourceMetadataUrl #576). Instead it follows a set of hardcoded rules or permutations to find one:
- http://localhost:5253/.well-known/oauth-protected-resource/my-mcp-server
- http://localhost:5253/.well-known/oauth-protected-resource
- http://localhost:5253/.well-known/oauth-authorization-server
- http://localhost:5253/.well-known/openid-configuration
When the returned WWW-Authenticate contains Bearer realm="McpAuth", resource_metadata="http://localhost:5253/my-mcp-server/.well-known/oauth-protected-resource", MCP inspector should immediately acquire the protected resource metadata from http://localhost:5253/my-mcp-server/.well-known/oauth-protected-resource. If no resource_metadata is provided, then it may fall back to trying permutations.
We can add a proxy endpoint at root level, that proxies the request to the subresource:
yarp.AddRoute("/.well-known/oauth-protected-resource/my-mcp-server/mcp", myMcpServer);
Now MCP inspector successfully connects to the gatewayed MCP server at /my-mcp-sever.
MCPJam Inspector
npx @mcpjam/inspector@latest
If you run it against self-signed certs locally:
$env:NODE_TLS_REJECT_UNAUTHORIZED=0; npx @mcpjam/inspector@latest -v
NODE_TLS_REJECT_UNAUTHORIZED=0 npx @mcpjam/inspector@latest -v
Authentication is a little bit flakey, but the OAuth Debugger works great.
Claude
Get the desktop app form Claude.
Web (Claude.ai)
Similar to the MCP Inspector, Claude.ai does not seem to support PRM behind a path like /my-mcp-server/.well-known/oauth-protected-resource/mcp. That means we need to make PRM available on root level like /.well-known/oauth-protected-resource/my-mcp-server/mcp and proxy it to /my-mcp-server/.well-known/oauth-protected-resource/mcp. Now Claude.ai authentication works.
Desktop
To install local MCP servers (stdio), we can easily add them to the claude_desktop_config.json like this:
{
"mcpServers": {
"getTime": {
"command": "D:\\McpExperiments\\MyMCPServer.Stdio\\bin\\Debug\\net9.0\\MyMCPServer.Stdio.exe"
},
"getCli": {
"command": "D:\\McpExperiments\\MyMCPServer.Stdio.Cli\\bin\\Debug\\net9.0\\MyMCPServer.Stdio.Cli.exe",
"args": [
"mcp"
]
}
}
}
Custom connectors as remote MCP servers can also be added, with a OAuth ClientId & Secret. However custom connectors try to do auth at /authorize, not at at the authorize endpoint configured in the metadata of the authorization server configured in the protected resource metadata of the MCP server.
We can use mcp-remote for that. By default it uses Dynamic Client Registration and stores its client credentials in ~\.mcp-auth. But we can provide static oauth metadata:
$env:NODE_OPTIONS='--use-system-ca'
npx mcp-remote 'http://localhost:5253/my-mcp-server' 63113 --static-oauth-client-info '{\"client_id\":\"mcp-remote\"}'
set NODE_OPTIONS=--use-system-ca
npx mcp-remote http://localhost:5253/my-mcp-server 63113 --static-oauth-client-info "{\"client_id\":\"mcp-remote\"}"
If set NODE_OPTIONS=--use-system-ca does not work anymore (--use-system-ca is not allowed in NODE_OPTIONS), consider $env:NODE_TLS_REJECT_UNAUTHORIZED = "0".
Powershell does have an escaping problem, so we best put the oauth data in a separate json file and reference it like this:
npx mcp-remote 'http://localhost:5253/my-mcp-server' 63113 --static-oauth-client-info "@D:\McpExperiments\MyMCPServer.Sse\mcp-remote-oauth-client-info.json"
In the claude_desktop_config.json it looks like this:
{
"mcpServers": {
"getVibe": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:5253/my-mcp-server",
"63113",
"--static-oauth-client-info",
"@D:\\McpExperiments\\MyMCPServer.Sse\\mcp-remote-oauth-client-info.json"
],
"env": {
"NODE_OPTIONS": "--use-system-ca"
}
}
},
"isUsingBuiltInNodeForMcp": false
}
Or via script claude_desktop.cmd:
{
"mcpServers": {
"getVibe": {
"command": "D:\\McpExperiments\\MyMCPServer.Sse\\claude_desktop.cmd"
}
}
}
However, this currently fails during "Completing authorization" with a 404. What endpoint is it trying to call?
The protected resource metadata is detected with a testTransport, but not fed forward into the actual transport in connectToRemoteServer():
const transport = sseTransport ? new SSEClientTransport(url, {
authProvider,
requestInit: { headers },
eventSourceInit
}) : new StreamableHTTPClientTransport(url, {
authProvider,
requestInit: { headers }
});
try {
debugLog("Attempting to connect to remote server", { sseTransport });
if (client) {
debugLog("Connecting client to transport");
await client.connect(transport);
} else {
debugLog("Starting transport directly");
await transport.start();
if (!sseTransport) {
debugLog("Creating test transport for HTTP-only connection test");
const testTransport = new StreamableHTTPClientTransport(url, { authProvider, requestInit: { headers } });
const testClient = new Client({ name: "mcp-remote-fallback-test", version: "0.0.0" }, { capabilities: {} });
await testClient.connect(testTransport);
}
}
return transport;
} catch (error) {
transport._resourceMetadataUrl = testTransport._resourceMetadataUrl;//this line would fix it (todo: pr!)
//...interactive authentication
}
I have proposed the fix with Resource metadata is remembered throughout the entire login flow. #167. Until this is merged, we can to compile mcp-remote locally and set it up like this:
git clone https://github.com/halllo/mcp-remote.git
cd mcp-remote
git checkout -b remembers_resource_metadata origin/remembers_resource_metadata
pnpm install
pnpm build
npm link #make it available everywhere
npm list -g --depth=0 #to verify its actually available
npx mcp-remote #use linked version everywhere
Or without npm link:
npx --package=/Users/Manuel.Naujoks/Projects/mcp-remote mcp-remote https://…
Make sure your Claude Desktop instance does not use its built-in Node.js, but instead uses your operating system's version of Node.js. Under Settings / Extensions / Advanced Settings you should see the same Node.js version that you used when you ran npm link.
ChatGPT
MCP support requires ChatGPT Plus. Then users can enable "Developer mode" (which is still in BETA) and create a new connector. Custom OAuth client_id is not supported.
Adding a localhost hosted MCP server only resulted in "Error fetching OAuth configuration".
Nanobot
To better test the MCP servers of this project, we can use a local MCP host like nanobot. It seems to support OAuth and mcp-ui.
export OPENAI_API_KEY=sk-proj-...
nanobot run ./nanobot.yaml
It seems to require client_secret and auth_endpoint, even though the client config does not require a secret and the authorize endpoint can be determined based on PRM and authorization server metadata.
However nanobot still fails with a weird error:
failed to setup auth: failed to create oauth proxy: invalid mode: middleware
Resources
相关服务器
Alpha Vantage MCP Server
赞助Access financial market data: realtime & historical stock, ETF, options, forex, crypto, commodities, fundamentals, technical indicators, & more
MCP Server Creator
A meta-server for dynamically generating MCP server configurations and Python code.
Octopus Deploy Official MCP Server
The Octopus MCP Server provides your AI assistant with powerful tools that allow it to inspect, query, and diagnose problems within your Octopus instance, transforming it into your ultimate DevOps wingmate.
Remote MCP Server (Authless)
An example of a remote MCP server deployable on Cloudflare Workers without authentication.
onlybrains-mcp
Big Brain, Bigger MCP
MCP Server Boilerplate
A TypeScript boilerplate for building MCP servers with streamable HTTP and OAuth proxy support.
MCP Servers
A collection of reference implementations for the Model Context Protocol (MCP), demonstrating how to give LLMs secure access to tools and data using Typescript and Python SDKs.
AAP Enterprise MCP Server
An MCP server that allows AI assistants to interact with Ansible Automation Platform (AAP) and Event-Driven Ansible (EDA) infrastructure.
ALTER Identity
Identity infrastructure for the AI economy. 33-trait psychometric engine — belonging probability, trait vectors, attunement depth. Remote streamable-HTTP. Free tier: 16 tools, 10 req/min.
Remote MCP Server (Authless)
A template for deploying a remote, auth-less MCP server on Cloudflare Workers.
Data Engineering Tutor MCP Server
A tutor for Data Engineering that provides personalized updates on concepts, patterns, and technologies.