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:

  1. http://localhost:5253/.well-known/oauth-protected-resource/my-mcp-server
  2. http://localhost:5253/.well-known/oauth-protected-resource
  3. http://localhost:5253/.well-known/oauth-authorization-server
  4. 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

Recursos