MCP Experiments
Um servidor MCP dotnet experimental que retorna a hora atual, baseado no tutorial de Laurent Kempé.
Documentação
MCP Experiments
Estes experimentos MCP são um playground para melhor compreensão da tecnologia.
aspire run
Este sistema disponibiliza os seguintes endpoints por meio de seu gateway:
- /my-mcp-server/mcp Servidor MCP Experimental
- /my-mcp-web-client Host MCP Experimental
- /identity Servidor de Identidade
Implantar como Azure Container Apps
$env:ASPIRE_CONTAINER_RUNTIME='podman'; aspire deploy
ASPIRE_CONTAINER_RUNTIME=podman aspire deploy
Clientes e Compatibilidade
Testei a autenticação MCP com os seguintes clientes.
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);
Funciona muito bem.
MCP Inspector
npx @modelcontextprotocol/inspector
⚠️ O MCP inspector atualmente não segue o URI resource_metadata do cabeçalho de resposta WWW-Authenticate para localizar os metadados do recurso protegido, de acordo com a Seção 5 da RFC9728 (O fluxo OAuth não suporta resourceMetadataUrl #576). Em vez disso, ele segue um conjunto de regras codificadas ou permutações para encontrá-lo:
- 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
Quando o WWW-Authenticate retornado contém Bearer realm="McpAuth", resource_metadata="http://localhost:5253/my-mcp-server/.well-known/oauth-protected-resource", o MCP inspector deve adquirir imediatamente os metadados do recurso protegido de http://localhost:5253/my-mcp-server/.well-known/oauth-protected-resource. Se nenhum resource_metadata for fornecido, ele poderá recorrer à tentativa de permutações.
Podemos adicionar um endpoint proxy no nível raiz, que encaminha a solicitação para o subrecurso:
yarp.AddRoute("/.well-known/oauth-protected-resource/my-mcp-server/mcp", myMcpServer);
Agora o MCP inspector se conecta com sucesso ao servidor MCP com gateway em /my-mcp-sever.
MCPJam Inspector
npx @mcpjam/inspector@latest
Se você executá-lo contra certificados autoassinados localmente:
$env:NODE_TLS_REJECT_UNAUTHORIZED=0; npx @mcpjam/inspector@latest -v
NODE_TLS_REJECT_UNAUTHORIZED=0 npx @mcpjam/inspector@latest -v
A autenticação é um pouco instável, mas o OAuth Debugger funciona muito bem.
Claude
Baixe o aplicativo de desktop em Claude.
Web (Claude.ai)
Semelhante ao MCP Inspector, o Claude.ai parece não suportar PRM em um caminho como /my-mcp-server/.well-known/oauth-protected-resource/mcp. Isso significa que precisamos disponibilizar o PRM no nível raiz, como /.well-known/oauth-protected-resource/my-mcp-server/mcp, e fazer proxy para /my-mcp-server/.well-known/oauth-protected-resource/mcp. Agora a autenticação do Claude.ai funciona.
Desktop
Para instalar servidores MCP locais (stdio), podemos adicioná-los facilmente ao claude_desktop_config.json assim:
{
"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"
]
}
}
}
Conectores personalizados como servidores MCP remotos também podem ser adicionados, com um OAuth ClientId e Secret. No entanto, conectores personalizados tentam fazer autenticação em /authorize, e não no endpoint authorize configurado nos metadados do servidor de autorização configurado nos metadados do recurso protegido do servidor MCP.
Podemos usar mcp-remote para isso. Por padrão, ele usa Dynamic Client Registration e armazena suas credenciais de cliente em ~\.mcp-auth. Mas podemos fornecer metadados OAuth estáticos:
$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\"}"
Se set NODE_OPTIONS=--use-system-ca não funcionar mais (--use-system-ca is not allowed in NODE_OPTIONS), considere $env:NODE_TLS_REJECT_UNAUTHORIZED = "0".
O Powershell tem um problema de escape, então é melhor colocar os dados OAuth em um arquivo JSON separado e referenciá-lo assim:
npx mcp-remote 'http://localhost:5253/my-mcp-server' 63113 --static-oauth-client-info "@D:\McpExperiments\MyMCPServer.Sse\mcp-remote-oauth-client-info.json"
No claude_desktop_config.json, fica assim:
{
"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
}
Ou por meio do script claude_desktop.cmd:
{
"mcpServers": {
"getVibe": {
"command": "D:\\McpExperiments\\MyMCPServer.Sse\\claude_desktop.cmd"
}
}
}
No entanto, isso atualmente falha durante "Completing authorization" com um 404. Qual endpoint ele está tentando chamar?
Os metadados do recurso protegido são detectados com um testTransport, mas não são encaminhados para o transporte real em 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
}
Propus a correção com Resource metadata is remembered throughout the entire login flow. #167. Até que isso seja mesclado, podemos compilar mcp-remote localmente e configurá-lo assim:
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
Ou sem npm link:
npx --package=/Users/Manuel.Naujoks/Projects/mcp-remote mcp-remote https://…
Certifique-se de que sua instância do Claude Desktop não use o Node.js integrado, mas sim a versão do Node.js do seu sistema operacional. Em Settings / Extensions / Advanced Settings, você deve ver a mesma versão do Node.js que usou ao executar npm link.
ChatGPT
O suporte a MCP requer ChatGPT Plus. Então os usuários podem ativar o "Developer mode" (que ainda está em BETA) e criar um novo conector. OAuth personalizado client_id não é suportado.
Adicionar um servidor MCP hospedado em localhost resultou apenas em "Error fetching OAuth configuration".
Nanobot
Para testar melhor os servidores MCP deste projeto, podemos usar um host MCP local como nanobot. Ele parece suportar OAuth e mcp-ui.
export OPENAI_API_KEY=sk-proj-...
nanobot run ./nanobot.yaml
Parece exigir client_secret e auth_endpoint, mesmo que a configuração do cliente não exija um segredo e o endpoint authorize possa ser determinado com base no PRM e nos metadados do servidor de autorização.
No entanto, o nanobot ainda falha com um erro estranho:
failed to setup auth: failed to create oauth proxy: invalid mode: middleware