# Directory Structure ``` ├── .gitignore ├── docs │ ├── CONFIGURACAO_CERTIFICADO.md │ ├── INSTALACAO.md │ └── TRIBUNAIS_SUPORTADOS.md ├── examples │ └── claude_desktop_config.json ├── LICENSE ├── package.json ├── public │ └── index.html ├── README.md ├── src │ ├── certificate-manager.ts │ ├── index.ts │ ├── pdf-processor.ts │ ├── pje-client.ts │ ├── test-certificate.ts │ ├── types.ts │ ├── utils.ts │ └── web-server.ts ├── tsconfig.json └── views └── index.ejs ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Dependências node_modules/ package-lock.json yarn.lock # Arquivos de build build/ dist/ *.js *.d.ts *.js.map # Arquivos de ambiente .env .env.local .env.*.local !.env.example # Arquivos de IDE .vscode/ .idea/ *.swp *.swo *~ .DS_Store # Arquivos do sistema Thumbs.db desktop.ini # Logs logs/ *.log npm-debug.log yarn-debug.log yarn-error.log lerna-debug.log .pnpm-debug.log # Testing coverage/ .nyc_output/ # Temporary files *.tmp *.temp temp/ tmp/ # Certificates (security) *.pfx *.p12 *.pem *.key *.crt *.cer certificados/ certs/ # Claude Desktop config (personal) claude_desktop_config.json !examples/claude_desktop_config.json # Backup files *.backup *.bak # Arquivos de upload uploads/ *.pdf ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # 🔐 PJE MCP Server Servidor MCP (Model Context Protocol) para integração com o sistema PJE (Processo Judicial Eletrônico) brasileiro, com suporte completo a certificados digitais A1 e A3. ## 🚀 Características - ✅ **Integração completa com PJE** - Acesso total à API do PJE - 🔐 **Certificados Digitais** - Suporte A1 (arquivo) e A3 (token/smartcard) - 📋 **Gestão de Processos** - Liste, busque e acompanhe processos - 🏛️ **Dados Judiciais** - Órgãos julgadores, classes e assuntos - 🔍 **Filtros Avançados** - Busca com múltiplos critérios - 🌐 **Multi-tribunal** - Funciona com qualquer tribunal PJE - 🤖 **Claude Desktop** - Integração nativa com IA ## 📦 Instalação Rápida ```bash git clone https://github.com/seu-usuario/pje-mcp-server.git cd pje-mcp-server npm install cp .env.example .env # Edite o arquivo .env com suas configurações npm run build ``` ## ⚙️ Configuração ### 1. Configuração Básica (.env) ```env # URL do seu tribunal PJE_BASE_URL=https://pje.tjce.jus.br PJE_APP_NAME=pje-tjce-1g # Certificado Digital (escolha uma opção) # Opção 1: Arquivo PFX PJE_CERTIFICATE_PFX_PATH=C:\certificado.pfx PJE_CERTIFICATE_PFX_PASSWORD=senha123 # Opção 2: Windows Store PJE_CERTIFICATE_THUMBPRINT=abc123... ``` ### 2. Claude Desktop (Windows) Adicione ao arquivo `%APPDATA%\Claude\claude_desktop_config.json`: ```json { "mcpServers": { "pje": { "command": "node", "args": ["C:\\caminho\\para\\pje-mcp-server\\build\\index.js"] } } } ``` ### 3. Claude Desktop (Mac/Linux) Adicione ao arquivo `~/.config/claude/claude_desktop_config.json`: ```json { "mcpServers": { "pje": { "command": "node", "args": ["/caminho/para/pje-mcp-server/build/index.js"] } } } ``` ## 🎯 Uso com Claude Após configurar, reinicie o Claude Desktop e use comandos naturais: ``` "Configure o PJE do TJCE" "Liste meus processos" "Busque o processo 1234567-89.2024.8.06.0001" "Mostre os órgãos julgadores" "Quais são minhas audiências esta semana?" ``` ## 🔐 Certificados Digitais ### Identificar seu Certificado (Windows) ```cmd certutil -store My ``` ### Tipos Suportados | Tipo | Descrição | Configuração | |------|-----------|--------------| | A1 | Arquivo .pfx/.p12 | `PJE_CERTIFICATE_PFX_PATH` | | A3 | Token/Smartcard | `PJE_CERTIFICATE_THUMBPRINT` | ### Certificadoras Homologadas - SERPRO - Certisign - Serasa Experian - Valid - Soluti - AC Caixa ## 🏛️ Tribunais Testados - **TJCE** - Tribunal de Justiça do Ceará - **TRF5** - Tribunal Regional Federal da 5ª Região - **TJMG** - Tribunal de Justiça de Minas Gerais - **TJSP** - Tribunal de Justiça de São Paulo - **TJRJ** - Tribunal de Justiça do Rio de Janeiro ## 📝 Comandos Disponíveis ### Configuração - `pje_configurar` - Configura conexão com o tribunal - `pje_configurar_certificado` - Configura certificado digital - `pje_listar_certificados` - Lista certificados instalados - `pje_info_certificado` - Informações do certificado atual - `pje_status` - Status da configuração ### Consultas - `pje_listar_processos` - Lista processos com filtros - `pje_buscar_processo` - Busca processo por número - `pje_listar_orgaos_julgadores` - Lista órgãos - `pje_listar_classes` - Classes processuais - `pje_listar_assuntos` - Assuntos disponíveis ## 🛠️ Desenvolvimento ### Estrutura do Projeto ``` pje-mcp-server/ ├── src/ # Código fonte TypeScript │ ├── index.ts # Servidor principal │ ├── certificate-manager.ts # Gerenciamento de certificados │ └── types.ts # Tipos e interfaces ├── build/ # Código compilado (gerado) ├── docs/ # Documentação adicional ├── examples/ # Exemplos de configuração └── package.json # Configuração do projeto ``` ### Scripts Disponíveis ```bash npm run build # Compila o TypeScript npm run start # Inicia o servidor npm run dev # Compila e inicia npm run clean # Limpa arquivos compilados ``` ## 🐛 Solução de Problemas ### Erro: "Certificado não encontrado" ```bash # Liste certificados disponíveis certutil -store My # Copie o thumbprint correto para o .env ``` ### Erro: "Comando não encontrado" - Reinicie o Claude Desktop completamente - Verifique o caminho no claude_desktop_config.json ### Erro: "Autenticação falhou" - Verifique a validade do certificado - Confirme a URL do tribunal - Teste com outro certificado ## 🤝 Contribuindo 1. Faça um Fork do projeto 2. Crie sua Feature Branch (`git checkout -b feature/NovaFuncionalidade`) 3. Commit suas mudanças (`git commit -m 'Add: Nova funcionalidade'`) 4. Push para a Branch (`git push origin feature/NovaFuncionalidade`) 5. Abra um Pull Request ## 📄 Licença Este projeto está sob a licença MIT. Veja o arquivo [LICENSE](LICENSE) para mais detalhes. ## 🔗 Links Úteis - [Documentação do MCP](https://modelcontextprotocol.io) - [Claude Desktop](https://claude.ai/download) - [Documentação do PJE](https://www.pje.jus.br/wiki) ## 📞 Suporte - **Issues**: [GitHub Issues](https://github.com/seu-usuario/pje-mcp-server/issues) - **Discussões**: [GitHub Discussions](https://github.com/seu-usuario/pje-mcp-server/discussions) - **Email**: [email protected] --- Desenvolvido com ❤️ para a comunidade jurídica brasileira ``` -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- ```typescript import { exec } from 'child_process'; import { promisify } from 'util'; export const execAsync = promisify(exec); ``` -------------------------------------------------------------------------------- /examples/claude_desktop_config.json: -------------------------------------------------------------------------------- ```json { "mcpServers": { "pje": { "command": "node", "args": ["C:\\Users\\seu-usuario\\pje-mcp-server\\build\\index.js"], "env": { "NODE_ENV": "production" } }, "pje-dev": { "command": "node", "args": ["C:\\Users\\seu-usuario\\pje-mcp-server-dev\\build\\index.js"], "env": { "NODE_ENV": "development", "PJE_DEBUG": "true" } } } } ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2022", "module": "CommonJS", "moduleResolution": "node", "lib": ["ES2022"], "outDir": "./build", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationMap": false, "sourceMap": false, "allowSyntheticDefaultImports": true, "resolveJsonModule": true }, "include": [ "src/**/*" ], "exclude": [ "node_modules", "build", "dist", "examples", "docs" ] } ``` -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- ```typescript export interface PJEProcesso { id: string; numero: string; classe: string; assunto: string; dataDistribuicao: string; valorCausa: number; partes: PJEParte[]; movimentos: PJEMovimento[]; } export interface PJEParte { tipo: "ATIVO" | "PASSIVO"; nome: string; cpfCnpj?: string; advogados?: PJEAdvogado[]; } export interface PJEAdvogado { nome: string; oab: string; } export interface PJEMovimento { data: string; descricao: string; tipo: string; documentos?: PJEDocumento[]; } export interface PJEDocumento { id: string; nome: string; tipo: string; dataJuntada: string; } export interface PJEOrgaoJulgador { id: string; nome: string; tipo: string; competencia: string; } export interface PJEClasse { id: string; codigo: string; nome: string; sigla: string; } export interface PJEAssunto { id: string; codigo: string; nome: string; codigoPai?: string; } ``` -------------------------------------------------------------------------------- /src/test-certificate.ts: -------------------------------------------------------------------------------- ```typescript import * as dotenv from 'dotenv'; import { CertificateManager } from './certificate-manager.js'; // Carregar variáveis de ambiente dotenv.config(); async function testCertificate() { try { console.log('Variáveis de ambiente carregadas:'); console.log('PJE_CERTIFICATE_THUMBPRINT:', process.env.PJE_CERTIFICATE_THUMBPRINT); console.log('PJE_CERTIFICATE_PASSWORD:', process.env.PJE_CERTIFICATE_PASSWORD); if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { throw new Error('Configuração do certificado não encontrada'); } const config = { certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD }; console.log('Configuração do certificado:', config); const certificateManager = new CertificateManager(config); await certificateManager.initialize(); console.log('Certificado inicializado com sucesso!'); } catch (error) { console.error('Erro ao testar certificado:', error); process.exit(1); } } testCertificate(); ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "pje-mcp-server", "version": "1.0.0", "description": "Servidor MCP para integração com o sistema PJE (Processo Judicial Eletrônico) brasileiro", "main": "build/index.js", "scripts": { "build": "tsc", "start": "node build/index.js", "start:web": "node build/web-server.js", "dev": "tsc && node build/index.js", "test": "node build/test-server.js", "clean": "rm -rf build", "prepare": "npm run build" }, "keywords": [ "pje", "processo-judicial-eletronico", "mcp", "model-context-protocol", "api", "juridico", "certificado-digital", "tribunal", "tjce", "trf5" ], "author": "", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/seu-usuario/pje-mcp-server.git" }, "dependencies": { "@modelcontextprotocol/sdk": "^0.5.0", "axios": "^1.9.0", "body-parser": "^2.2.0", "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^4.21.2", "multer": "^1.4.5-lts.1", "node-forge": "^1.3.1", "pdf-lib": "^1.17.1", "pdf-parse": "^1.1.1" }, "devDependencies": { "@types/express": "^4.17.21", "@types/multer": "^1.4.11", "@types/node": "^20.17.51", "@types/node-forge": "^1.3.11", "typescript": "^5.8.3" }, "files": [ "build", "docs", "examples", "public" ], "bin": { "pje-mcp-server": "build/index.js" }, "engines": { "node": ">=18.0.0" } } ``` -------------------------------------------------------------------------------- /docs/INSTALACAO.md: -------------------------------------------------------------------------------- ```markdown # 📦 Guia de Instalação Completo ## Pré-requisitos - Node.js 18 ou superior - NPM ou Yarn - Git - Certificado Digital (A1 ou A3) - Claude Desktop instalado ## Instalação Passo a Passo ### 1. Clone o Repositório ```bash git clone https://github.com/seu-usuario/pje-mcp-server.git cd pje-mcp-server ``` ### 2. Instale as Dependências ```bash npm install ``` ### 3. Configure o Ambiente ```bash # Copie o arquivo de exemplo cp .env.example .env # Edite com suas configurações notepad .env # Windows nano .env # Linux/Mac ``` ### 4. Configure seu Tribunal Edite o arquivo `.env`: ```env # TJCE - Ceará PJE_BASE_URL=https://pje.tjce.jus.br PJE_APP_NAME=pje-tjce-1g # TRF5 - 5ª Região # PJE_BASE_URL=https://pje.trf5.jus.br # PJE_APP_NAME=pje-trf5 # TJMG - Minas Gerais # PJE_BASE_URL=https://pje.tjmg.jus.br # PJE_APP_NAME=pje-tjmg-1g ``` ### 5. Configure o Certificado Digital #### Opção A: Arquivo PFX (A1) ```env PJE_CERTIFICATE_PFX_PATH=C:\Users\seu-usuario\certificado.pfx PJE_CERTIFICATE_PFX_PASSWORD=sua-senha ``` #### Opção B: Windows Store (A3) 1. Liste os certificados: ```cmd certutil -store My ``` 2. Copie o thumbprint: ```env PJE_CERTIFICATE_THUMBPRINT=1234567890abcdef... ``` ### 6. Compile o Projeto ```bash npm run build ``` ### 7. Configure o Claude Desktop #### Windows 1. Abra o arquivo: ``` %APPDATA%\Claude\claude_desktop_config.json ``` 2. Adicione a configuração: ```json { "mcpServers": { "pje": { "command": "node", "args": ["C:\\Users\\seu-usuario\\pje-mcp-server\\build\\index.js"] } } } ``` #### Mac/Linux 1. Abra o arquivo: ``` ~/.config/claude/claude_desktop_config.json ``` 2. Adicione a configuração: ```json { "mcpServers": { "pje": { "command": "node", "args": ["/home/seu-usuario/pje-mcp-server/build/index.js"] } } } ``` ### 8. Reinicie o Claude Desktop - Feche completamente (Ctrl+Q ou Cmd+Q) - Aguarde 5 segundos - Abra novamente ### 9. Teste a Instalação No Claude, digite: ``` Verifique o status do PJE ``` Se tudo estiver correto, você verá as informações de configuração. ## Instalação Global (Opcional) Para usar em qualquer lugar: ```bash # Instale globalmente npm install -g . # Configure no Claude Desktop { "mcpServers": { "pje": { "command": "pje-mcp-server" } } } ``` ## Troubleshooting ### Erro: "Cannot find module" ```bash npm install npm run build ``` ### Erro: "Permission denied" - Windows: Execute como Administrador - Linux/Mac: Use `sudo` se necessário ### Erro: "Tool not found" - Verifique se o caminho no claude_desktop_config.json está correto - Certifique-se de que o Claude foi reiniciado ``` -------------------------------------------------------------------------------- /src/pje-client.ts: -------------------------------------------------------------------------------- ```typescript import { Agent } from 'https'; import { CertificateManager } from './certificate-manager.js'; import { ProcessInfo } from './pdf-processor.js'; interface PJEClientConfig { baseUrl: string; appName: string; certificate: { certificateThumbprint: string; certificatePassword: string; }; } export class PJEClient { private config: PJEClientConfig; private certificateManager: CertificateManager | null = null; private agent: Agent | null = null; constructor(config: PJEClientConfig) { this.config = config; } async initializeCertificate(): Promise<void> { console.log('Inicializando certificado...'); if (!this.config.certificate) { throw new Error('Configuração do certificado não fornecida'); } try { this.certificateManager = new CertificateManager({ certificateThumbprint: this.config.certificate.certificateThumbprint, certificatePassword: this.config.certificate.certificatePassword }); await this.certificateManager.initialize(); this.agent = this.certificateManager.getAgent(); console.log('Certificado inicializado com sucesso'); } catch (error) { console.error('Erro ao inicializar certificado:', error); throw error; } } async authenticateWithCertificate(): Promise<void> { if (!this.agent) { throw new Error('Certificado não inicializado'); } // Aqui você implementaria a autenticação com o PJE // Por enquanto, vamos apenas simular que a autenticação foi bem-sucedida console.log('Autenticado com sucesso usando certificado digital'); } async buscarProcesso(numeroProcesso: string): Promise<any> { if (!this.agent) { throw new Error('Certificado não inicializado'); } // Aqui você implementaria a busca do processo no PJE // Por enquanto, vamos retornar um objeto simulado return { numeroProcesso, classe: 'Procedimento Comum Cível', assunto: 'Indenização por Dano Material', orgaoJulgador: '1ª Vara Cível', partes: [ { tipo: 'Autor', nome: 'João da Silva' }, { tipo: 'Réu', nome: 'Empresa XYZ Ltda' } ] }; } async peticionar(processInfo: ProcessInfo): Promise<string> { if (!this.agent) { throw new Error('Certificado não inicializado'); } // Aqui você implementaria o peticionamento no PJE // Por enquanto, vamos retornar um número de protocolo simulado const protocol = Math.random().toString(36).substring(2, 15); console.log('Petição protocolada com sucesso:', { numeroProcesso: processInfo.numeroProcesso, tipoPeticao: processInfo.tipoPeticao, protocol }); return protocol; } } ``` -------------------------------------------------------------------------------- /src/pdf-processor.ts: -------------------------------------------------------------------------------- ```typescript import fs from 'fs/promises'; // @ts-ignore const pdfParse = require('pdf-parse'); export interface ProcessInfo { numeroProcesso: string; tipoPeticao: string; classeProcessual: string; orgaoJulgador: string; assunto: string; } export async function extractProcessInfo(pdfPath: string): Promise<ProcessInfo> { try { console.log('Lendo arquivo PDF:', pdfPath); // Ler o arquivo PDF const pdfBuffer = await fs.readFile(pdfPath); console.log('Extraindo texto do PDF...'); // Extrair texto do PDF const data = await pdfParse(pdfBuffer); const fullText = data.text; console.log('Texto extraído do PDF:', fullText.substring(0, 500) + '...'); // Extrair número do processo usando regex const processoMatch = fullText.match(/\d{20}/); if (!processoMatch) { console.log('Número do processo não encontrado. Tentando outros formatos...'); // Tentar outros formatos comuns de número de processo const formatos = [ /\d{7}-\d{2}\.\d{4}\.\d{1,2}\.\d{2}\.\d{4}/, // 0000000-00.0000.0.00.0000 /\d{20}/, // 00000000000000000000 /\d{7}-\d{2}\.\d{4}\.\d{1,2}\.\d{2}\.\d{4}\.\d{4}/ // 0000000-00.0000.0.00.0000.0000 ]; for (const formato of formatos) { const match = fullText.match(formato); if (match) { console.log('Número do processo encontrado com formato alternativo:', match[0]); return { numeroProcesso: match[0], tipoPeticao: 'Petição Intermediária', classeProcessual: '', orgaoJulgador: '', assunto: '' }; } } throw new Error('Número do processo não encontrado no PDF'); } console.log('Número do processo encontrado:', processoMatch[0]); // Extrair tipo de petição (assumindo que é sempre "Petição Intermediária") const tipoPeticao = 'Petição Intermediária'; // Extrair classe processual (assumindo que está após "Classe:") const classeMatch = fullText.match(/Classe:\s*([^\n]+)/i); const classeProcessual = classeMatch ? classeMatch[1].trim() : ''; // Extrair órgão julgador (assumindo que está após "Órgão Julgador:") const orgaoMatch = fullText.match(/Órgão Julgador:\s*([^\n]+)/i); const orgaoJulgador = orgaoMatch ? orgaoMatch[1].trim() : ''; // Extrair assunto (assumindo que está após "Assunto:") const assuntoMatch = fullText.match(/Assunto:\s*([^\n]+)/i); const assunto = assuntoMatch ? assuntoMatch[1].trim() : ''; return { numeroProcesso: processoMatch[0], tipoPeticao, classeProcessual, orgaoJulgador, assunto }; } catch (error) { console.error('Erro ao processar PDF:', error); throw new Error('Erro ao processar o arquivo PDF'); } } ``` -------------------------------------------------------------------------------- /docs/TRIBUNAIS_SUPORTADOS.md: -------------------------------------------------------------------------------- ```markdown # 🏛️ Tribunais Suportados ## Configurações por Tribunal ### Região Nordeste #### TJCE - Tribunal de Justiça do Ceará ```env PJE_BASE_URL=https://pje.tjce.jus.br PJE_APP_NAME=pje-tjce-1g # 1º grau PJE_APP_NAME=pje-tjce-2g # 2º grau ``` #### TJBA - Tribunal de Justiça da Bahia ```env PJE_BASE_URL=https://pje.tjba.jus.br PJE_APP_NAME=pje-tjba-1g PJE_APP_NAME=pje-tjba-2g ``` #### TJPE - Tribunal de Justiça de Pernambuco ```env PJE_BASE_URL=https://pje.tjpe.jus.br PJE_APP_NAME=pje-tjpe-1g PJE_APP_NAME=pje-tjpe-2g ``` ### Região Sudeste #### TJSP - Tribunal de Justiça de São Paulo ```env PJE_BASE_URL=https://pje.tjsp.jus.br PJE_APP_NAME=pje-tjsp-1g PJE_APP_NAME=pje-tjsp-2g ``` #### TJMG - Tribunal de Justiça de Minas Gerais ```env PJE_BASE_URL=https://pje.tjmg.jus.br PJE_APP_NAME=pje-tjmg-1g PJE_APP_NAME=pje-tjmg-2g ``` #### TJRJ - Tribunal de Justiça do Rio de Janeiro ```env PJE_BASE_URL=https://pje.tjrj.jus.br PJE_APP_NAME=pje-tjrj-1g PJE_APP_NAME=pje-tjrj-2g ``` ### Tribunais Regionais Federais #### TRF1 - 1ª Região ```env PJE_BASE_URL=https://pje1g.trf1.jus.br PJE_APP_NAME=pje-trf1 ``` #### TRF2 - 2ª Região ```env PJE_BASE_URL=https://pje.trf2.jus.br PJE_APP_NAME=pje-trf2 ``` #### TRF3 - 3ª Região ```env PJE_BASE_URL=https://pje.trf3.jus.br PJE_APP_NAME=pje-trf3 ``` #### TRF4 - 4ª Região ```env PJE_BASE_URL=https://pje.trf4.jus.br PJE_APP_NAME=pje-trf4 ``` #### TRF5 - 5ª Região ```env PJE_BASE_URL=https://pje.trf5.jus.br PJE_APP_NAME=pje-trf5 ``` #### TRF6 - 6ª Região ```env PJE_BASE_URL=https://pje.trf6.jus.br PJE_APP_NAME=pje-trf6 ``` ### Tribunais Superiores #### STJ - Superior Tribunal de Justiça ```env PJE_BASE_URL=https://pje.stj.jus.br PJE_APP_NAME=pje-stj ``` #### TST - Tribunal Superior do Trabalho ```env PJE_BASE_URL=https://pje.tst.jus.br PJE_APP_NAME=pje-tst ``` ## Particularidades por Tribunal ### TJSP - Maior volume de processos - Requer certificado ICP-Brasil - APIs otimizadas para alto volume ### TJMG - Integração com SINTER - Suporte a peticionamento em lote - APIs específicas para precatórios ### TRF5 - Abrange: PE, AL, SE, PB, RN, CE - APIs unificadas para toda região - Suporte a JEF (Juizado Especial Federal) ## Testando Conexão ### Comando Básico ``` Configure o PJE do [TRIBUNAL] ``` ### Verificar Endpoints ``` Liste os órgãos julgadores ``` ### Testar Autenticação ``` Liste meus processos ``` ## URLs Alternativas Alguns tribunais possuem URLs diferentes para: ### Produção - URL principal do tribunal ### Homologação - Geralmente: `https://pje-homo.tribunal.jus.br` - Para testes antes da produção ### Treinamento - Geralmente: `https://pje-treina.tribunal.jus.br` - Para capacitação de usuários ## Suporte Específico ### Consulta Pública Todos os tribunais suportam: - Busca por número - Consulta de movimentações - Download de documentos públicos ### Funcionalidades Autenticadas Com certificado digital: - Peticionamento - Consulta completa - Download de documentos sigilosos - Intimações ## Status de Implementação | Tribunal | Consulta | Peticionamento | Observações | |----------|----------|----------------|-------------| | TJCE | ✅ | ✅ | Totalmente funcional | | TRF5 | ✅ | ✅ | Totalmente funcional | | TJMG | ✅ | ⚠️ | Em testes | | TJSP | ✅ | 🔄 | Em desenvolvimento | | Outros | ✅ | ❓ | Não testado | Legenda: - ✅ Funcionando - ⚠️ Parcialmente - 🔄 Em desenvolvimento - ❓ Não testado ``` -------------------------------------------------------------------------------- /docs/CONFIGURACAO_CERTIFICADO.md: -------------------------------------------------------------------------------- ```markdown # 🔐 Configuração de Certificado Digital ## Tipos de Certificado ### Certificado A1 (Arquivo) - **Formato**: .pfx ou .p12 - **Validade**: 1 ano - **Armazenamento**: Arquivo no computador - **Mobilidade**: Pode ser copiado ### Certificado A3 (Token/Smartcard) - **Formato**: Hardware (token USB ou cartão) - **Validade**: 3 anos - **Armazenamento**: Dispositivo criptográfico - **Mobilidade**: Precisa do dispositivo físico ## Configuração por Tipo ### 1. Certificado A1 (Arquivo PFX) #### Passo 1: Localize seu arquivo ```bash # Geralmente em: C:\Users\seu-usuario\Documents\certificados\ C:\certificados\ ``` #### Passo 2: Configure no .env ```env PJE_CERTIFICATE_PFX_PATH=C:\caminho\para\seu\certificado.pfx PJE_CERTIFICATE_PFX_PASSWORD=senha-do-certificado ``` #### Passo 3: Teste ```bash node build/index.js ``` ### 2. Certificado A3 (Windows Store) #### Passo 1: Instale o driver do token - Safenet (tokens brancos) - eToken (tokens azuis) - Outros conforme fabricante #### Passo 2: Liste os certificados ```cmd certutil -store My ``` Saída esperada: ``` ================ Certificate 0 ================ Serial Number: 1234567890abcdef Issuer: CN=AC SOLUTI Multipla v5, O=ICP-Brasil Subject: CN=SEU NOME:12345678900 Cert Hash(sha1): a1b2c3d4e5f6789012345678901234567890abcd ``` #### Passo 3: Configure no .env Por thumbprint: ```env PJE_CERTIFICATE_THUMBPRINT=a1b2c3d4e5f6789012345678901234567890abcd ``` Por subject: ```env PJE_CERTIFICATE_SUBJECT=CN=SEU NOME:12345678900 ``` ## Identificando seu Certificado ### No Windows 1. Abra o Gerenciador de Certificados: ```cmd certmgr.msc ``` 2. Navegue para: - Pessoal > Certificados 3. Procure por certificados com: - Seu nome - CPF ou CNPJ - Válidos (não expirados) ### Verificando Validade ```powershell # PowerShell Get-ChildItem Cert:\CurrentUser\My | Select Subject, NotAfter ``` ## Problemas Comuns ### "Certificado não encontrado" 1. Verifique se está instalado: ```cmd certutil -store My | findstr "Subject" ``` 2. Confirme o thumbprint: ```cmd certutil -store My | findstr "Cert Hash" ``` ### "Senha incorreta" Para A1: - Verifique a senha do arquivo PFX - Tente abrir com outro programa - Solicite nova senha ao emissor ### "Token não reconhecido" Para A3: 1. Instale o driver do fabricante 2. Reinicie o computador 3. Verifique no Gerenciador de Dispositivos 4. Teste com o software do fabricante ## Certificadoras Homologadas ### SERPRO - Site: https://certificados.serpro.gov.br - Suporte: 0800 728 0323 - Driver: Fornecido na compra ### Certisign - Site: https://www.certisign.com.br - Suporte: 0800 701 1799 - Driver: https://drivers.certisign.com.br ### Serasa Experian - Site: https://serasa.certificadodigital.com.br - Suporte: 0800 773 7272 - Driver: No site de suporte ### Valid - Site: https://www.validcertificadora.com.br - Suporte: 0800 774 4414 - Driver: Central de downloads ## Melhores Práticas 1. **Backup do A1** - Guarde cópia em local seguro - Use senha forte - Não compartilhe 2. **Cuidados com A3** - Não remova durante uso - Mantenha drivers atualizados - Teste periodicamente 3. **Renovação** - A1: Anualmente - A3: A cada 3 anos - Agende com antecedência 4. **Segurança** - Nunca envie por email - Não salve senha em texto - Use gerenciador de senhas ## Comandos Úteis ### Testar certificado ```bash # No Claude Desktop "Liste os certificados digitais" "Mostre informações do meu certificado" "Verifique a validade do certificado" ``` ### Exportar certificado (backup A1) ```cmd certutil -exportPFX -p "senha" My "thumbprint" "backup.pfx" ``` ### Importar certificado ```cmd certutil -importPFX "arquivo.pfx" ``` ``` -------------------------------------------------------------------------------- /src/certificate-manager.ts: -------------------------------------------------------------------------------- ```typescript import { Agent } from 'https'; import { exec } from 'child_process'; import { promisify } from 'util'; import * as fs from 'fs/promises'; import * as path from 'path'; const execAsync = promisify(exec); export interface CertificateConfig { certificateThumbprint: string; certificatePath?: string; certificatePassword: string; pfxPath?: string; pfxPassword?: string; certificateSubject?: string; } interface TokenCertificate { thumbprint: string; provider: string; } interface TokenKey { password: string; provider: string; } export class CertificateManager { private config: CertificateConfig; private agent: Agent | null = null; constructor(config: CertificateConfig) { this.config = config; } async initialize(): Promise<void> { console.log('Iniciando CertificateManager com config:', { ...this.config, certificatePassword: '***' // Ocultar senha nos logs }); if (!this.config.certificateThumbprint) { throw new Error('Thumbprint do certificado não fornecido'); } try { // Verificar se o certificado está disponível console.log('Verificando certificado no repositório...'); const { stdout, stderr } = await execAsync(`certutil -user -store My "${this.config.certificateThumbprint}"`); if (stderr) { console.error('Erro ao verificar certificado:', stderr); } console.log('Resposta do certutil:', stdout); if (!stdout.includes(this.config.certificateThumbprint)) { throw new Error('Certificado não encontrado no repositório'); } // Verificar se o token está conectado console.log('Verificando status do token...'); const { stdout: tokenStatus } = await execAsync('certutil -user -csp "SafeSign IC Standard Windows Cryptographic Service Provider" -key'); console.log('Status do token:', tokenStatus); // Configurar o agente HTTPS com o certificado do token console.log('Configurando agente HTTPS com certificado do token...'); const cert: TokenCertificate = { thumbprint: this.config.certificateThumbprint, provider: 'SafeSign IC Standard Windows Cryptographic Service Provider' }; const key: TokenKey = { password: this.config.certificatePassword, provider: 'SafeSign IC Standard Windows Cryptographic Service Provider' }; this.agent = new Agent({ rejectUnauthorized: false, cert: cert as any, key: key as any, ciphers: 'HIGH:!aNULL:!MD5:!RC4', minVersion: 'TLSv1.2', secureOptions: 0x00000001 // SSL_OP_NO_SSLv2 }); console.log('Certificado inicializado com sucesso'); } catch (error) { console.error('Erro detalhado ao inicializar certificado:', error); if (error instanceof Error) { throw new Error(`Falha ao inicializar certificado: ${error.message}`); } throw new Error('Falha ao inicializar certificado'); } } getAgent(): Agent { if (!this.agent) { throw new Error('Certificado não inicializado'); } return this.agent; } getHttpsAgent(): Agent { if (!this.agent) { throw new Error('Certificado não inicializado'); } return this.agent; } getCertificate(): any { if (!this.agent) { throw new Error('Certificado não inicializado'); } return this.agent.options.cert || this.agent.options.pfx; } getCertificateInfo(): any { return { thumbprint: this.config.certificateThumbprint, path: this.config.certificatePath, hasPassword: !!this.config.certificatePassword, provider: 'SafeSign IC Standard Windows Cryptographic Service Provider' }; } } ``` -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- ```html <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>PJE - Peticionamento em Lote</title> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> <style> .upload-area { border: 2px dashed #ccc; border-radius: 8px; padding: 20px; text-align: center; cursor: pointer; margin: 20px 0; } .upload-area:hover { border-color: #0d6efd; } .file-list { margin-top: 20px; } .protocol-list { margin-top: 20px; } </style> </head> <body> <div class="container py-5"> <h1 class="mb-4">PJE - Peticionamento em Lote</h1> <div class="card"> <div class="card-body"> <h5 class="card-title">Upload de Petições</h5> <div class="upload-area" id="uploadArea"> <p class="mb-0">Arraste e solte os arquivos PDF aqui ou clique para selecionar</p> <input type="file" id="fileInput" multiple accept=".pdf" style="display: none"> </div> <div class="file-list" id="fileList"></div> <button class="btn btn-primary" id="uploadButton" disabled> Enviar Petições </button> </div> </div> <div class="protocol-list" id="protocolList"></div> </div> <script> const uploadArea = document.getElementById('uploadArea'); const fileInput = document.getElementById('fileInput'); const fileList = document.getElementById('fileList'); const uploadButton = document.getElementById('uploadButton'); const protocolList = document.getElementById('protocolList'); let selectedFiles = []; // Configurar área de upload uploadArea.addEventListener('click', () => fileInput.click()); uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.style.borderColor = '#0d6efd'; }); uploadArea.addEventListener('dragleave', () => { uploadArea.style.borderColor = '#ccc'; }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.style.borderColor = '#ccc'; handleFiles(e.dataTransfer.files); }); fileInput.addEventListener('change', (e) => { handleFiles(e.target.files); }); function handleFiles(files) { selectedFiles = Array.from(files).filter(file => file.type === 'application/pdf'); updateFileList(); uploadButton.disabled = selectedFiles.length === 0; } function updateFileList() { fileList.innerHTML = selectedFiles.map(file => ` <div class="alert alert-info"> ${file.name} </div> `).join(''); } uploadButton.addEventListener('click', async () => { uploadButton.disabled = true; uploadButton.textContent = 'Enviando...'; for (const file of selectedFiles) { const formData = new FormData(); formData.append('petition', file); try { console.log('Enviando arquivo:', file.name); const response = await fetch('/peticionar', { method: 'POST', body: formData }); console.log('Resposta recebida:', response.status); const result = await response.json(); console.log('Resultado:', result); if (result.success) { protocolList.innerHTML += ` <div class="alert alert-success"> <strong>${file.name}</strong><br> Protocolo: ${result.protocol}<br> Processo: ${result.processInfo.numeroProcesso} </div> `; } else { protocolList.innerHTML += ` <div class="alert alert-danger"> <strong>${file.name}</strong><br> Erro: ${result.error} ${result.details ? `<br>Detalhes: ${result.details}` : ''} </div> `; } } catch (error) { console.error('Erro ao enviar arquivo:', error); protocolList.innerHTML += ` <div class="alert alert-danger"> <strong>${file.name}</strong><br> Erro ao enviar arquivo: ${error.message} </div> `; } } uploadButton.textContent = 'Enviar Petições'; uploadButton.disabled = false; selectedFiles = []; updateFileList(); }); </script> </body> </html> ``` -------------------------------------------------------------------------------- /src/web-server.ts: -------------------------------------------------------------------------------- ```typescript import 'dotenv/config'; import express from 'express'; import multer from 'multer'; import path from 'path'; import { PJEClient } from './pje-client.js'; import { extractProcessInfo } from './pdf-processor.js'; const app = express(); const upload = multer({ dest: 'uploads/' }); // Configuração do Express app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static('public')); app.set('view engine', 'ejs'); app.set('views', path.join(process.cwd(), 'views')); // Middleware para log de requisições app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`); next(); }); // Rota principal app.get('/', (req, res) => { res.render('index', { title: 'PJE MCP Server - Interface Web', tools: [ { id: 'buscar-processo', name: 'Buscar Processo', icon: '🔍' }, { id: 'peticionar', name: 'Petição Eletrônica', icon: '📝' }, { id: 'peticionar-lote', name: 'Petição em Lote', icon: '📦' }, { id: 'listar-processos', name: 'Listar Processos', icon: '📋' }, { id: 'listar-orgaos', name: 'Órgãos Julgadores', icon: '🏛️' }, { id: 'listar-classes', name: 'Classes Processuais', icon: '📚' }, { id: 'listar-assuntos', name: 'Assuntos Processuais', icon: '📑' } ] }); }); // Rota para buscar processo app.post('/buscar-processo', async (req, res) => { try { const numeroProcesso = req.body.numeroProcesso; if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { throw new Error('Configuração do certificado não encontrada'); } // Configurar cliente PJE const pjeClient = new PJEClient({ baseUrl: process.env.PJE_BASE_URL || 'https://pje.tjce.jus.br', appName: process.env.PJE_APP_NAME || 'pje-tjce-1g', certificate: { certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD } }); // Inicializar certificado await pjeClient.initializeCertificate(); await pjeClient.authenticateWithCertificate(); // Buscar processo const processo = await pjeClient.buscarProcesso(numeroProcesso); res.json({ success: true, processo }); } catch (error) { console.error('Erro ao buscar processo:', error); res.status(500).json({ success: false, error: error instanceof Error ? error.message : 'Erro ao buscar processo' }); } }); // Rota para upload de petições app.post('/peticionar', upload.single('petition'), async (req, res) => { try { console.log('Recebendo petição...'); if (!req.file) { console.log('Nenhum arquivo enviado'); return res.status(400).json({ error: 'Nenhum arquivo enviado' }); } console.log('Arquivo recebido:', req.file); if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { console.log('Configuração do certificado não encontrada'); throw new Error('Configuração do certificado não encontrada'); } console.log('Extraindo informações do PDF...'); // Extrair informações do processo do PDF const processInfo = await extractProcessInfo(req.file.path); console.log('Informações extraídas:', processInfo); console.log('Configurando cliente PJE...'); // Configurar cliente PJE const pjeClient = new PJEClient({ baseUrl: process.env.PJE_BASE_URL || 'https://pje.tjce.jus.br', appName: process.env.PJE_APP_NAME || 'pje-tjce-1g', certificate: { certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD } }); console.log('Inicializando certificado...'); // Inicializar certificado await pjeClient.initializeCertificate(); await pjeClient.authenticateWithCertificate(); console.log('Realizando peticionamento...'); // Fazer o peticionamento const protocol = await pjeClient.peticionar(processInfo); console.log('Petição protocolada com sucesso:', protocol); // Retornar recibo do protocolo res.json({ success: true, protocol, processInfo }); } catch (error) { console.error('Erro detalhado ao processar petição:', error); res.status(500).json({ error: 'Erro ao processar petição', details: error instanceof Error ? error.message : 'Erro desconhecido' }); } }); // Rota para upload de petições em lote app.post('/peticionar-lote', upload.array('petitions', 10), async (req, res) => { try { if (!req.files || !Array.isArray(req.files)) { return res.status(400).json({ error: 'Nenhum arquivo enviado' }); } if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { throw new Error('Configuração do certificado não encontrada'); } const results = []; const pjeClient = new PJEClient({ baseUrl: process.env.PJE_BASE_URL || 'https://pje.tjce.jus.br', appName: process.env.PJE_APP_NAME || 'pje-tjce-1g', certificate: { certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD } }); await pjeClient.initializeCertificate(); await pjeClient.authenticateWithCertificate(); for (const file of req.files) { try { const processInfo = await extractProcessInfo(file.path); const protocol = await pjeClient.peticionar(processInfo); results.push({ file: file.originalname, success: true, protocol, processInfo }); } catch (error) { results.push({ file: file.originalname, success: false, error: error instanceof Error ? error.message : 'Erro ao processar petição' }); } } res.json({ success: true, results }); } catch (error) { console.error('Erro ao processar lote de petições:', error); res.status(500).json({ error: 'Erro ao processar lote de petições' }); } }); // Iniciar servidor const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Servidor web rodando na porta ${PORT}`); }); ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript #!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; import axios, { AxiosInstance, AxiosResponse } from "axios"; import dotenv from "dotenv"; import { CertificateManager, CertificateConfig } from "./certificate-manager.js"; import { execAsync } from "./utils.js"; // Carrega as configurações do arquivo .env dotenv.config(); interface PJEConfig { baseUrl: string; ssoUrl?: string; clientId?: string; clientSecret?: string; username?: string; password?: string; appName?: string; certificate?: CertificateConfig; } interface PJEResponse<T = any> { status: "ok" | "error" | "in-progress"; code: string; messages: string[]; result: T; "page-info"?: { current: number; last: number; size: number; count: number; }; } class PJEClient { private axiosInstance: AxiosInstance; private accessToken?: string; private config: PJEConfig; private certificateManager?: CertificateManager; constructor(config: PJEConfig) { this.config = config; const axiosConfig: any = { baseURL: config.baseUrl, headers: { "Content-Type": "application/json", }, timeout: parseInt(process.env.PJE_TIMEOUT_MS || "30000"), }; if (config.certificate) { this.certificateManager = new CertificateManager(config.certificate); } this.axiosInstance = axios.create(axiosConfig); this.axiosInstance.interceptors.request.use((config) => { if (this.accessToken) { config.headers.Authorization = `Bearer ${this.accessToken}`; } if (this.config.appName) { config.headers["X-pje-legacy-app"] = this.config.appName; } return config; }); } async initializeCertificate(): Promise<void> { if (!this.certificateManager) { throw new Error("Nenhum certificado configurado"); } try { await this.certificateManager.initialize(); const httpsAgent = this.certificateManager.getHttpsAgent(); this.axiosInstance = axios.create({ baseURL: this.config.baseUrl, headers: { "Content-Type": "application/json", }, timeout: parseInt(process.env.PJE_TIMEOUT_MS || "30000"), httpsAgent: httpsAgent }); this.axiosInstance.interceptors.request.use((config) => { if (this.accessToken) { config.headers.Authorization = `Bearer ${this.accessToken}`; } if (this.config.appName) { config.headers["X-pje-legacy-app"] = this.config.appName; } return config; }); } catch (error) { throw new Error(`Erro ao inicializar certificado: ${error}`); } } async authenticateWithCertificate(): Promise<void> { if (!this.certificateManager) { throw new Error("Certificado não inicializado"); } try { const response = await this.axiosInstance.post('/api/v1/auth/certificate', { certificate: this.certificateManager.getCertificate() }); this.accessToken = response.data.access_token; } catch (error) { throw new Error(`Erro na autenticação por certificado: ${error}`); } } getCertificateInfo(): any { if (!this.certificateManager) { throw new Error("Nenhum certificado configurado"); } return this.certificateManager.getCertificateInfo(); } async authenticate(): Promise<void> { if (!this.config.ssoUrl || !this.config.clientId || !this.config.clientSecret) { throw new Error("Configuração de SSO incompleta"); } try { const response = await axios.post( this.config.ssoUrl, new URLSearchParams({ grant_type: "password", client_id: this.config.clientId, client_secret: this.config.clientSecret, username: this.config.username || "", password: this.config.password || "", scope: "openid", }), { headers: { "Content-Type": "application/x-www-form-urlencoded", }, } ); this.accessToken = response.data.access_token; } catch (error) { throw new Error(`Erro na autenticação: ${error}`); } } async listarProcessos(filter?: any, fields?: string[], order?: any, page?: number, size?: number): Promise<PJEResponse> { const params: any = {}; if (filter) { params.filter = typeof filter === "string" ? filter : JSON.stringify(filter); } if (fields) { params.fields = Array.isArray(fields) ? JSON.stringify(fields) : fields; } if (order) { params.order = typeof order === "string" ? order : JSON.stringify(order); } if (page || size) { params.page = JSON.stringify({ page: page || 1, size: size || 20 }); } const response = await this.axiosInstance.get("/api/v1/processos", { params }); return response.data; } async buscarProcesso(id: string): Promise<PJEResponse> { const response = await this.axiosInstance.get(`/api/v1/processos/${id}`); return response.data; } async listarOrgaosJulgadores(): Promise<PJEResponse> { const response = await this.axiosInstance.get("/api/v1/orgaos-julgadores"); return response.data; } async listarClasses(): Promise<PJEResponse> { const response = await this.axiosInstance.get("/api/v1/classes"); return response.data; } async listarAssuntos(): Promise<PJEResponse> { const response = await this.axiosInstance.get("/api/v1/assuntos"); return response.data; } } class PJEServer { private server: Server; private pjeClient?: PJEClient; constructor() { this.server = new Server( { name: "pje-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "pje_configurar", description: "Configura a conexão com o PJE", inputSchema: { type: "object", properties: { baseUrl: { type: "string", description: "URL base da API do PJE" }, appName: { type: "string", description: "Nome da aplicação" }, }, required: ["baseUrl"], }, }, { name: "pje_listar_processos", description: "Lista processos com filtros opcionais", inputSchema: { type: "object", properties: { filter: { type: "string", description: "Filtro para busca" }, page: { type: "number", description: "Número da página" }, size: { type: "number", description: "Tamanho da página" }, }, }, }, { name: "pje_buscar_processo", description: "Busca um processo específico por ID", inputSchema: { type: "object", properties: { id: { type: "string", description: "ID do processo" }, }, required: ["id"], }, }, { name: "pje_listar_orgaos_julgadores", description: "Lista órgãos julgadores", inputSchema: { type: "object", properties: {}, }, }, { name: "pje_listar_classes", description: "Lista classes processuais", inputSchema: { type: "object", properties: {}, }, }, { name: "pje_listar_assuntos", description: "Lista assuntos processuais", inputSchema: { type: "object", properties: {}, }, }, { name: "pje_status", description: "Verifica o status da configuração e conexão do PJE", inputSchema: { type: "object", properties: {}, }, }, { name: "pje_configurar_certificado", description: "Configura autenticação por certificado digital", inputSchema: { type: "object", properties: { pfxPath: { type: "string", description: "Caminho para arquivo .pfx/.p12" }, pfxPassword: { type: "string", description: "Senha do arquivo .pfx/.p12" }, certificateThumbprint: { type: "string", description: "Thumbprint do certificado no Windows" }, certificateSubject: { type: "string", description: "Subject do certificado no Windows" }, }, }, }, { name: "pje_listar_certificados", description: "Lista certificados digitais disponíveis no Windows", inputSchema: { type: "object", properties: {}, }, }, { name: "pje_info_certificado", description: "Mostra informações sobre o certificado configurado", inputSchema: { type: "object", properties: {}, }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case "pje_configurar": return await this.configurarPJE(request.params.arguments); case "pje_listar_processos": if (!this.pjeClient) throw new Error("PJE não configurado"); return await this.listarProcessos(request.params.arguments); case "pje_buscar_processo": if (!this.pjeClient) throw new Error("PJE não configurado"); return await this.buscarProcesso(request.params.arguments); case "pje_listar_orgaos_julgadores": if (!this.pjeClient) throw new Error("PJE não configurado"); return await this.listarOrgaosJulgadores(); case "pje_listar_classes": if (!this.pjeClient) throw new Error("PJE não configurado"); return await this.listarClasses(); case "pje_listar_assuntos": if (!this.pjeClient) throw new Error("PJE não configurado"); return await this.listarAssuntos(); case "pje_status": return await this.verificarStatus(); case "pje_configurar_certificado": return await this.configurarCertificado(request.params.arguments); case "pje_listar_certificados": return await this.listarCertificados(); case "pje_info_certificado": return await this.infoCertificado(); default: throw new Error(`Ferramenta desconhecida: ${request.params.name}`); } } catch (error) { return { content: [ { type: "text", text: `Erro: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }); } private async configurarPJE(args: any) { const config: PJEConfig = { baseUrl: args.baseUrl || process.env.PJE_BASE_URL || "https://pje.tjce.jus.br", appName: args.appName || process.env.PJE_APP_NAME || "pje-tjce-1g", ssoUrl: args.ssoUrl || process.env.PJE_SSO_URL, clientId: args.clientId || process.env.PJE_CLIENT_ID, clientSecret: args.clientSecret || process.env.PJE_CLIENT_SECRET, username: args.username || process.env.PJE_USERNAME, password: args.password || process.env.PJE_PASSWORD, }; const certificateConfig: CertificateConfig = { certificateThumbprint: '7db4b6cc9de4785944bcf1c8f3cde03355733b84', certificatePassword: '123456' }; this.pjeClient = new PJEClient(config); if (certificateConfig) { try { await this.pjeClient.initializeCertificate(); try { await this.pjeClient.authenticateWithCertificate(); return { content: [ { type: "text", text: `✅ PJE configurado com sucesso!\n\n🎯 **Configuração:**\n- URL: ${config.baseUrl}\n- App: ${config.appName}\n- Autenticação: 🔐 Certificado Digital\n\n✅ **Pronto para usar todas as funcionalidades!**`, }, ], }; } catch (authError) { return { content: [ { type: "text", text: `⚠️ PJE configurado com certificado!\n\n🎯 **Configuração:**\n- URL: ${config.baseUrl}\n- App: ${config.appName}\n- Certificado: ✅ Carregado\n- Autenticação: ⚠️ Falha na autenticação\n\n**Erro:** ${authError instanceof Error ? authError.message : String(authError)}`, }, ], }; } } catch (certError) { return { content: [ { type: "text", text: `❌ PJE configurado sem certificado!\n\n🎯 **Configuração:**\n- URL: ${config.baseUrl}\n- App: ${config.appName}\n- Certificado: ❌ Erro ao carregar\n\n**Erro:** ${certError instanceof Error ? certError.message : String(certError)}`, }, ], }; } } if (config.ssoUrl && config.clientId && config.clientSecret && config.username && config.password) { try { await this.pjeClient.authenticate(); return { content: [ { type: "text", text: `✅ PJE configurado com sucesso!\n\n🎯 **Configuração:**\n- URL: ${config.baseUrl}\n- App: ${config.appName}\n- Autenticação: ✅ SSO autenticado\n\n✅ **Pronto para usar todas as funcionalidades!**`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `⚠️ PJE configurado com erro na autenticação!\n\n🎯 **Configuração:**\n- URL: ${config.baseUrl}\n- App: ${config.appName}\n- Autenticação: ❌ Falha no SSO\n\n❌ **Erro:** ${error instanceof Error ? error.message : String(error)}`, }, ], }; } } return { content: [ { type: "text", text: `✅ PJE configurado!\n\n🎯 **Configuração:**\n- URL: ${config.baseUrl}\n- App: ${config.appName}\n- Autenticação: ⚠️ Apenas consultas públicas\n\n💡 **Para funcionalidades completas:**\nConfigure certificado digital ou credenciais no arquivo .env`, }, ], }; } private async listarProcessos(args: any) { const { filter, page, size } = args; const result = await this.pjeClient!.listarProcessos(filter, undefined, undefined, page, size); return { content: [ { type: "text", text: `📋 **Processos encontrados:**\n\n${JSON.stringify(result, null, 2)}`, }, ], }; } private async buscarProcesso(args: any) { const { id } = args; const result = await this.pjeClient!.buscarProcesso(id); return { content: [ { type: "text", text: `📄 **Detalhes do processo:**\n\n${JSON.stringify(result, null, 2)}`, }, ], }; } private async listarOrgaosJulgadores() { const result = await this.pjeClient!.listarOrgaosJulgadores(); return { content: [ { type: "text", text: `🏛️ **Órgãos julgadores:**\n\n${JSON.stringify(result, null, 2)}`, }, ], }; } private async listarClasses() { const result = await this.pjeClient!.listarClasses(); return { content: [ { type: "text", text: `📚 **Classes processuais:**\n\n${JSON.stringify(result, null, 2)}`, }, ], }; } private async listarAssuntos() { const result = await this.pjeClient!.listarAssuntos(); return { content: [ { type: "text", text: `📑 **Assuntos processuais:**\n\n${JSON.stringify(result, null, 2)}`, }, ], }; } private async verificarStatus() { const envConfig = { baseUrl: process.env.PJE_BASE_URL, appName: process.env.PJE_APP_NAME, ssoUrl: process.env.PJE_SSO_URL, clientId: process.env.PJE_CLIENT_ID, clientSecret: process.env.PJE_CLIENT_SECRET, username: process.env.PJE_USERNAME ? '***' : undefined, password: process.env.PJE_PASSWORD ? '***' : undefined, certificatePath: process.env.PJE_CERTIFICATE_PFX_PATH, certificatePassword: process.env.PJE_CERTIFICATE_PFX_PASSWORD ? '***' : undefined, certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, }; let statusText = `🔍 **STATUS DO PJE MCP SERVER**\n\n`; statusText += `🔧 **Configuração do arquivo .env:**\n`; statusText += `- URL Base: ${envConfig.baseUrl || '❌ Não configurado'}\n`; statusText += `- App Name: ${envConfig.appName || '❌ Não configurado'}\n`; statusText += `- SSO URL: ${envConfig.ssoUrl || '❌ Não configurado'}\n`; statusText += `- Client ID: ${envConfig.clientId || '❌ Não configurado'}\n`; statusText += `- Client Secret: ${envConfig.clientSecret || '❌ Não configurado'}\n`; statusText += `- Username: ${envConfig.username || '❌ Não configurado'}\n`; statusText += `- Password: ${envConfig.password || '❌ Não configurado'}\n\n`; statusText += `🔐 **Certificado Digital:**\n`; statusText += `- Arquivo PFX: ${envConfig.certificatePath || '❌ Não configurado'}\n`; statusText += `- Senha PFX: ${envConfig.certificatePassword || '❌ Não configurado'}\n`; statusText += `- Thumbprint: ${envConfig.certificateThumbprint || '❌ Não configurado'}\n\n`; statusText += `🔗 **Conexão:**\n`; if (!this.pjeClient) { statusText += `- Status: ❌ Cliente não configurado\n`; statusText += `- Solução: Execute 'pje_configurar' primeiro\n\n`; } else { statusText += `- Status: ✅ Cliente configurado\n`; if ((this.pjeClient as any).accessToken) { statusText += `- Autenticação: ✅ Token ativo\n`; } else if ((this.pjeClient as any).certificateManager) { statusText += `- Autenticação: 🔐 Certificado configurado\n`; } else { statusText += `- Autenticação: ⚠️ Apenas consultas públicas\n`; } } statusText += `\n💡 **Próximos passos:**\n`; if (!envConfig.baseUrl) { statusText += `1. Configure PJE_BASE_URL no arquivo .env\n`; } if (!envConfig.certificatePath && !envConfig.ssoUrl) { statusText += `2. Configure certificado digital ou credenciais SSO no arquivo .env\n`; } if (!this.pjeClient) { statusText += `3. Execute: 'Configure o PJE'\n`; } else { statusText += `3. ✅ Pronto para usar!\n`; } return { content: [ { type: "text", text: statusText, }, ], }; } private async configurarCertificado(args: any) { try { const thumbprint = args.certificateThumbprint || process.env.PJE_CERTIFICATE_THUMBPRINT; const password = args.certificatePassword || process.env.PJE_CERTIFICATE_PASSWORD; if (!thumbprint || !password) { throw new Error('Thumbprint e senha do certificado são obrigatórios'); } const certificateConfig: CertificateConfig = { certificateThumbprint: thumbprint, certificatePassword: password }; if (!this.pjeClient) { const config: PJEConfig = { baseUrl: process.env.PJE_BASE_URL || "https://pje.tjce.jus.br", appName: process.env.PJE_APP_NAME || "pje-tjce-1g", certificate: certificateConfig, }; this.pjeClient = new PJEClient(config); } else { (this.pjeClient as any).config.certificate = certificateConfig; (this.pjeClient as any).certificateManager = new CertificateManager(certificateConfig); } await this.pjeClient.initializeCertificate(); try { await this.pjeClient.authenticateWithCertificate(); return { content: [ { type: "text", text: `🔐 **Certificado Digital Configurado com Sucesso!**\n\n✅ Certificado carregado e autenticado\n🎯 Pronto para usar com todas as funcionalidades do PJE\n\n**Informações do Certificado:**\n${JSON.stringify(this.pjeClient.getCertificateInfo(), null, 2)}`, }, ], }; } catch (authError) { return { content: [ { type: "text", text: `⚠️ **Certificado Carregado, mas Autenticação Falhou**\n\nCertificado foi carregado, mas a autenticação no PJE falhou.\nErro: ${authError}\n\n**Informações do Certificado:**\n${JSON.stringify(this.pjeClient.getCertificateInfo(), null, 2)}`, }, ], }; } } catch (error) { return { content: [ { type: "text", text: `❌ **Erro ao Configurar Certificado Digital**\n\n${error instanceof Error ? error.message : String(error)}\n\n**Dicas:**\n1. Verifique se o arquivo .pfx existe no caminho especificado\n2. Confirme se a senha do certificado está correta\n3. Para certificados do Windows, use o thumbprint ou subject\n4. Execute 'pje_listar_certificados' para ver certificados disponíveis`, }, ], }; } } private async listarCertificados() { try { const { stdout } = await execAsync('certutil -store My'); return { content: [ { type: "text", text: `🔍 **Certificados Digitais Disponíveis no Windows**\n\n${stdout}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `❌ **Erro ao Listar Certificados**\n\n${error instanceof Error ? error.message : String(error)}\n\n**Nota:** Esta funcionalidade requer Windows e acesso ao Certificate Store.`, }, ], }; } } private async infoCertificado() { try { if (!this.pjeClient || !(this.pjeClient as any).certificateManager) { return { content: [ { type: "text", text: `❌ **Nenhum Certificado Configurado**\n\nExecute 'pje_configurar_certificado' primeiro para carregar um certificado digital.`, }, ], }; } const info = this.pjeClient.getCertificateInfo(); let texto = `🎯 **Informações do Certificado Digital Atual**\n\n`; texto += `**Subject:**\n`; info.subject.forEach((attr: any) => { texto += `- ${attr.name}: ${attr.value}\n`; }); texto += `\n**Emissor:**\n`; info.issuer.forEach((attr: any) => { texto += `- ${attr.name}: ${attr.value}\n`; }); texto += `\n**Detalhes Técnicos:**\n`; texto += `- Serial Number: ${info.serialNumber}\n`; texto += `- Thumbprint: ${info.thumbprint}\n`; texto += `- Válido de: ${new Date(info.notBefore).toLocaleString('pt-BR')}\n`; texto += `- Válido até: ${new Date(info.notAfter).toLocaleString('pt-BR')}\n`; const agora = new Date(); const validade = new Date(info.notAfter); if (agora > validade) { texto += `\n⚠️ **AVISO: Certificado EXPIRADO!**\n`; } else { const diasRestantes = Math.floor((validade.getTime() - agora.getTime()) / (1000 * 60 * 60 * 24)); texto += `\n✅ **Certificado válido por mais ${diasRestantes} dias**\n`; } return { content: [ { type: "text", text: texto, }, ], }; } catch (error) { return { content: [ { type: "text", text: `❌ **Erro ao Obter Informações do Certificado**\n\n${error instanceof Error ? error.message : String(error)}`, }, ], }; } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("Servidor MCP do PJE iniciado"); } } const server = new PJEServer(); server.run().catch(console.error); ```