Especificação da Interface do Plugin Companion do Qwen Code
Última atualização: 15 de setembro de 2025
Este documento define o contrato para a construção de um plugin companion para habilitar o modo IDE do Qwen Code. Para VS Code, esses recursos (diff nativo, consciência de contexto) são fornecidos pela extensão oficial (marketplace ). Esta especificação é destinada a colaboradores que desejam trazer funcionalidades semelhantes para outros editores, como IDEs JetBrains, Sublime Text, etc.
I. A Interface de Comunicação
O Qwen Code e o plugin do IDE se comunicam por meio de um canal de comunicação local.
1. Camada de Transporte: MCP sobre HTTP
O plugin DEVE executar um servidor HTTP local que implementa o Model Context Protocol (MCP).
- Protocolo: O servidor deve ser um servidor MCP válido. Recomendamos o uso de um SDK MCP existente para a linguagem de sua escolha, se disponível.
- Endpoint: O servidor deve expor um único endpoint (ex.:
/mcp) para toda a comunicação MCP. - Porta: O servidor DEVE escutar em uma porta atribuída dinamicamente (ou seja, escutar na porta
0).
2. Mecanismo de Descoberta: O Arquivo de Bloqueio (Lock File)
Para que o Qwen Code se conecte, ele precisa descobrir qual porta seu servidor está usando. O plugin DEVE facilitar isso criando um “arquivo de bloqueio” e definindo a variável de ambiente da porta.
-
Como a CLI Encontra o Arquivo: A CLI lê a porta de
QWEN_CODE_IDE_SERVER_PORTe, em seguida, lê~/.qwen/ide/<PORT>.lock. (Fallbacks legados existem para extensões mais antigas; veja a nota abaixo.) -
Localização do Arquivo: O arquivo deve ser criado em um diretório específico:
~/.qwen/ide/. Seu plugin deve criar este diretório se ele não existir. -
Convenção de Nomenclatura do Arquivo: O nome do arquivo é crítico e DEVE seguir o padrão:
<PORT>.lock<PORT>: A porta na qual seu servidor MCP está escutando.
-
Conteúdo do Arquivo e Validação do Workspace: O arquivo DEVE conter um objeto JSON com a seguinte estrutura:
{ "port": 12345, "workspacePath": "/caminho/para/projeto1:/caminho/para/projeto2", "authToken": "um-token-muito-secreto", "ppid": 1234, "ideName": "VS Code" }port(número, obrigatório): A porta do servidor MCP.workspacePath(string, obrigatório): Uma lista de todos os caminhos raiz do workspace abertos, delimitada pelo separador de caminho específico do SO (:para Linux/macOS,;para Windows). A CLI usa este caminho para garantir que está sendo executada na mesma pasta do projeto que está aberta no IDE. Se o diretório de trabalho atual da CLI não for um subdiretório deworkspacePath, a conexão será rejeitada. Seu plugin DEVE fornecer o(s) caminho(s) absoluto(s) correto(s) para a raiz do(s) workspace(s) aberto(s).authToken(string, obrigatório): Um token secreto para proteger a conexão. A CLI incluirá este token em um cabeçalhoAuthorization: Bearer <token>em todas as requisições.ppid(número, obrigatório): O ID do processo pai do processo do IDE.ideName(string, obrigatório): Um nome amigável para o IDE (ex.:VS Code,JetBrains IDE).
-
Autenticação: Para proteger a conexão, o plugin DEVE gerar um token único e secreto e incluí-lo no arquivo de descoberta. A CLI então incluirá este token no cabeçalho
Authorizationpara todas as requisições ao servidor MCP (ex.:Authorization: Bearer um-token-muito-secreto). Seu servidor DEVE validar este token em cada requisição e rejeitar qualquer requisição não autorizada. -
Variáveis de Ambiente (Obrigatório): Seu plugin DEVE definir
QWEN_CODE_IDE_SERVER_PORTno terminal integrado para que a CLI possa localizar o arquivo<PORT>.lockcorreto.
Nota legada: Para extensões anteriores à v0.5.1, o Qwen Code pode recorrer à leitura de arquivos JSON no diretório temporário do sistema chamados qwen-code-ide-server-<PID>.json ou qwen-code-ide-server-<PORT>.json. Novas integrações não devem depender desses arquivos legados.
II. A Interface de Contexto
Para habilitar a consciência de contexto, o plugin PODE fornecer à CLI informações em tempo real sobre a atividade do usuário no IDE.
Notificação ide/contextUpdate
O plugin PODE enviar uma notificação ide/contextUpdate para a CLI sempre que o contexto do usuário mudar.
-
Eventos Disparadores: Esta notificação deve ser enviada (com um debounce recomendado de 50ms) quando:
- Um arquivo é aberto, fechado ou focado.
- A posição do cursor ou a seleção de texto do usuário muda no arquivo ativo.
-
Payload (
IdeContext): Os parâmetros da notificação DEVEM ser um objetoIdeContext:interface IdeContext { workspaceState?: { openFiles?: File[]; isTrusted?: boolean; }; } interface File { // Caminho absoluto para o arquivo path: string; // Timestamp Unix do último foco (para ordenação) timestamp: number; // Verdadeiro se este for o arquivo atualmente focado isActive?: boolean; cursor?: { // Número da linha baseado em 1 line: number; // Número do caractere baseado em 1 character: number; }; // O texto atualmente selecionado pelo usuário selectedText?: string; }Nota: A lista
openFilesdeve incluir apenas arquivos que existem em disco. Arquivos virtuais (ex.: arquivos não salvos sem caminho, páginas de configuração do editor) DEVEM ser excluídos.
Como a CLI Usa Este Contexto
Após receber o objeto IdeContext, a CLI executa várias etapas de normalização e truncamento antes de enviar as informações para o modelo.
- Ordenação de Arquivos: A CLI usa o campo
timestamppara determinar os arquivos usados mais recentemente. Ela ordena a listaopenFilescom base neste valor. Portanto, seu plugin DEVE fornecer um timestamp Unix preciso de quando um arquivo foi focado pela última vez. - Arquivo Ativo: A CLI considera apenas o arquivo mais recente (após a ordenação) como o arquivo “ativo”. Ela ignorará a flag
isActiveem todos os outros arquivos e limpará seus camposcursoreselectedText. Seu plugin deve focar em definirisActive: truee fornecer detalhes de cursor/seleção apenas para o arquivo atualmente focado. - Truncamento: Para gerenciar os limites de token, a CLI trunca tanto a lista de arquivos (para 10 arquivos) quanto o
selectedText(para 16KB).
Embora a CLI lide com o truncamento final, é altamente recomendável que seu plugin também limite a quantidade de contexto que envia.
III. A Interface de Diff
Para permitir modificações interativas de código, o plugin PODE expor uma interface de diff. Isso permite que a CLI solicite que o IDE abra uma visualização de diff, mostrando alterações propostas em um arquivo. O usuário pode então revisar, editar e, por fim, aceitar ou rejeitar essas alterações diretamente no IDE.
Ferramenta openDiff
O plugin DEVE registrar uma ferramenta openDiff em seu servidor MCP.
-
Descrição: Esta ferramenta instrui o IDE a abrir uma visualização de diff modificável para um arquivo específico.
-
Requisição (
OpenDiffRequest): A ferramenta é invocada por meio de uma requisiçãotools/call. O campoargumentsdentro dosparamsda requisição DEVE ser um objetoOpenDiffRequest.interface OpenDiffRequest { // O caminho absoluto para o arquivo a ser comparado. filePath: string; // O novo conteúdo proposto para o arquivo. newContent: string; } -
Resposta (
CallToolResult): A ferramenta DEVE retornar imediatamente umCallToolResultpara reconhecer a requisição e relatar se a visualização de diff foi aberta com sucesso.- Em caso de Sucesso: Se a visualização de diff foi aberta com sucesso, a resposta DEVE conter conteúdo vazio (ou seja,
content: []). - Em caso de Falha: Se um erro impediu a abertura da visualização de diff, a resposta DEVE ter
isError: truee incluir um blocoTextContentno arraycontentdescrevendo o erro.
O resultado real do diff (aceitação ou rejeição) é comunicado de forma assíncrona por meio de notificações.
- Em caso de Sucesso: Se a visualização de diff foi aberta com sucesso, a resposta DEVE conter conteúdo vazio (ou seja,
Ferramenta closeDiff
O plugin DEVE registrar uma ferramenta closeDiff em seu servidor MCP.
-
Descrição: Esta ferramenta instrui o IDE a fechar uma visualização de diff aberta para um arquivo específico.
-
Requisição (
CloseDiffRequest): A ferramenta é invocada por meio de uma requisiçãotools/call. O campoargumentsdentro dosparamsda requisição DEVE ser um objetoCloseDiffRequest.interface CloseDiffRequest { // O caminho absoluto para o arquivo cuja visualização de diff deve ser fechada. filePath: string; } -
Resposta (
CallToolResult): A ferramenta DEVE retornar umCallToolResult.- Em caso de Sucesso: Se a visualização de diff foi fechada com sucesso, a resposta DEVE incluir um único bloco TextContent no array de conteúdo contendo o conteúdo final do arquivo antes do fechamento.
- Em caso de Falha: Se um erro impediu o fechamento da visualização de diff, a resposta DEVE ter
isError: truee incluir um blocoTextContentno arraycontentdescrevendo o erro.
Notificação ide/diffAccepted
Quando o usuário aceita as alterações em uma visualização de diff (ex.: clicando em um botão “Aplicar” ou “Salvar”), o plugin DEVE enviar uma notificação ide/diffAccepted para a CLI.
-
Payload: Os parâmetros da notificação DEVEM incluir o caminho do arquivo e o conteúdo final do arquivo. O conteúdo pode diferir do
newContentoriginal se o usuário tiver feito edições manuais na visualização de diff.{ // O caminho absoluto para o arquivo que foi comparado. filePath: string; // O conteúdo completo do arquivo após a aceitação. content: string; }
Notificação ide/diffRejected
Quando o usuário rejeita as alterações (ex.: fechando a visualização de diff sem aceitar), o plugin DEVE enviar uma notificação ide/diffRejected para a CLI.
-
Payload: Os parâmetros da notificação DEVEM incluir o caminho do arquivo do diff rejeitado.
{ // O caminho absoluto para o arquivo que foi comparado. filePath: string; }
IV. A Interface de Ciclo de Vida
O plugin DEVE gerenciar seus recursos e o arquivo de descoberta corretamente com base no ciclo de vida do IDE.
- Na Ativação (inicialização do IDE / plugin habilitado):
- Iniciar o servidor MCP.
- Criar o arquivo de descoberta.
- Na Desativação (desligamento do IDE / plugin desabilitado):
- Parar o servidor MCP.
- Excluir o arquivo de descoberta.