arrow-return

Como criar um MCP e submetê-lo ao Claude + Open AI

Como criar um MCP e submetê-lo ao Claude + Open AI
An image of Gean Ribeiro, the author of this post
3 min de leitura

Figura 1. O fluxo completo. Os passos a ciano vêm com o Passport e o laravel/mcp. Os passos a laranja são seus.

Passo 1. O Passport 13 é o servidor de autorização

O Passport 13 fala OAuth 2.1 de origem: authorization code com PKCE, e os grants password e implicit ficam desligados a menos que os ative. Não os ative. Instale o Passport, corra as migrações e defina um scope para o endpoint MCP.

// app/Providers/AppServiceProvider.php

use Laravel\Passport\Passport;

Passport::tokensCan([

'mcp:use' => 'Use the MCP tools for the selected workspaces',

]);

Um scope chega. A restrição de tenant vive no token, não no scope. Isso é o passo 3.

Passo 2. Discovery e registo de clientes, zero código

O laravel/mcp traz o documento de discovery e o endpoint de registo. Uma chamada publica os dois, uma segunda monta o servidor.

// routes/ai.php

use Laravel\Mcp\Facades\Mcp;

Mcp::oauthRoutes();

Mcp::web('/mcp', AppMcpServer::class)

->middleware(AuthenticateMcpRequest::class);

Depois disto, GET /.well-known/oauth-authorization-server responde, POST /oauth/register cria um cliente Passport, e o Claude e o ChatGPT encontram o caminho sozinhos. Resista a registar os dois clientes à mão. Qualquer outro cliente MCP que apareça no próximo ano deixaria de funcionar.

Passo 3. O ecrã de consentimento e a armadilha

O Passport dá-lhe o /oauth/authorize. O ecrã é seu. Numa aplicação single-tenant é um botão. No Semantika, um workspace guarda as marcas que uma equipa monitoriza, por isso o utilizador escolhe que workspaces o assistente pode ler. Um ou mais.

Aqui está a armadilha. O consentimento acontece no browser, dentro da sessão do utilizador. A troca de token que vem a seguir é um POST /oauth/token de servidor para servidor, vindo do backend da Anthropic ou da OpenAI. Sem cookie, sem sessão, sem memória do que o utilizador escolheu. Guarde a escolha na sessão e leia-a quando o token é criado, e não lê nada.

A solução é carimbar a escolha no código de autorização, no momento do consentimento, no pedido que cria o código.

// app/Http/Controllers/OAuth/ApproveAuthorizationController.php

public function approve(Request $request)

{

// Only workspaces this user can actually access.

$allowed = $request->user()->workspaces()

->whereIn('id', $request->input('workspace_ids', []))

->pluck('id')

->all();

abort_if($allowed === [], 422, 'Select at least one workspace.');

$request->session()->put('oauth.consent.workspace_ids', $allowed);

return parent::approve($request);

}
// app/Models/Passport/AuthCode.php

protected static function booted(): void

{

static::creating(function (self $code) {

$code->workspace_ids = session('oauth.consent.workspace_ids', []);

});

}

Registe o modelo com Passport::useAuthCodeModel(AuthCode::class) e acrescente uma coluna JSON. A linha do código passa a transportar a lista de tenants, e o código é a única coisa que o cliente devolve. Valide os ids contra o acesso real do utilizador, no servidor. O formulário pode ser editado.

Subscreve a
nossa newsletter

Junta-te a 1.000+ pessoas e recebe semanalmente dicas,
boas práticas e insights.