# 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: -------------------------------------------------------------------------------- ``` 1 | # Dependências 2 | node_modules/ 3 | package-lock.json 4 | yarn.lock 5 | 6 | # Arquivos de build 7 | build/ 8 | dist/ 9 | *.js 10 | *.d.ts 11 | *.js.map 12 | 13 | # Arquivos de ambiente 14 | .env 15 | .env.local 16 | .env.*.local 17 | !.env.example 18 | 19 | # Arquivos de IDE 20 | .vscode/ 21 | .idea/ 22 | *.swp 23 | *.swo 24 | *~ 25 | .DS_Store 26 | 27 | # Arquivos do sistema 28 | Thumbs.db 29 | desktop.ini 30 | 31 | # Logs 32 | logs/ 33 | *.log 34 | npm-debug.log 35 | yarn-debug.log 36 | yarn-error.log 37 | lerna-debug.log 38 | .pnpm-debug.log 39 | 40 | # Testing 41 | coverage/ 42 | .nyc_output/ 43 | 44 | # Temporary files 45 | *.tmp 46 | *.temp 47 | temp/ 48 | tmp/ 49 | 50 | # Certificates (security) 51 | *.pfx 52 | *.p12 53 | *.pem 54 | *.key 55 | *.crt 56 | *.cer 57 | certificados/ 58 | certs/ 59 | 60 | # Claude Desktop config (personal) 61 | claude_desktop_config.json 62 | !examples/claude_desktop_config.json 63 | 64 | # Backup files 65 | *.backup 66 | *.bak 67 | 68 | # Arquivos de upload 69 | uploads/ 70 | *.pdf ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # 🔐 PJE MCP Server 2 | 3 | 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. 4 | 5 | ## 🚀 Características 6 | 7 | - ✅ **Integração completa com PJE** - Acesso total à API do PJE 8 | - 🔐 **Certificados Digitais** - Suporte A1 (arquivo) e A3 (token/smartcard) 9 | - 📋 **Gestão de Processos** - Liste, busque e acompanhe processos 10 | - 🏛️ **Dados Judiciais** - Órgãos julgadores, classes e assuntos 11 | - 🔍 **Filtros Avançados** - Busca com múltiplos critérios 12 | - 🌐 **Multi-tribunal** - Funciona com qualquer tribunal PJE 13 | - 🤖 **Claude Desktop** - Integração nativa com IA 14 | 15 | ## 📦 Instalação Rápida 16 | 17 | ```bash 18 | git clone https://github.com/seu-usuario/pje-mcp-server.git 19 | cd pje-mcp-server 20 | npm install 21 | cp .env.example .env 22 | # Edite o arquivo .env com suas configurações 23 | npm run build 24 | ``` 25 | 26 | ## ⚙️ Configuração 27 | 28 | ### 1. Configuração Básica (.env) 29 | 30 | ```env 31 | # URL do seu tribunal 32 | PJE_BASE_URL=https://pje.tjce.jus.br 33 | PJE_APP_NAME=pje-tjce-1g 34 | 35 | # Certificado Digital (escolha uma opção) 36 | # Opção 1: Arquivo PFX 37 | PJE_CERTIFICATE_PFX_PATH=C:\certificado.pfx 38 | PJE_CERTIFICATE_PFX_PASSWORD=senha123 39 | 40 | # Opção 2: Windows Store 41 | PJE_CERTIFICATE_THUMBPRINT=abc123... 42 | ``` 43 | 44 | ### 2. Claude Desktop (Windows) 45 | 46 | Adicione ao arquivo `%APPDATA%\Claude\claude_desktop_config.json`: 47 | 48 | ```json 49 | { 50 | "mcpServers": { 51 | "pje": { 52 | "command": "node", 53 | "args": ["C:\\caminho\\para\\pje-mcp-server\\build\\index.js"] 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | ### 3. Claude Desktop (Mac/Linux) 60 | 61 | Adicione ao arquivo `~/.config/claude/claude_desktop_config.json`: 62 | 63 | ```json 64 | { 65 | "mcpServers": { 66 | "pje": { 67 | "command": "node", 68 | "args": ["/caminho/para/pje-mcp-server/build/index.js"] 69 | } 70 | } 71 | } 72 | ``` 73 | 74 | ## 🎯 Uso com Claude 75 | 76 | Após configurar, reinicie o Claude Desktop e use comandos naturais: 77 | 78 | ``` 79 | "Configure o PJE do TJCE" 80 | "Liste meus processos" 81 | "Busque o processo 1234567-89.2024.8.06.0001" 82 | "Mostre os órgãos julgadores" 83 | "Quais são minhas audiências esta semana?" 84 | ``` 85 | 86 | ## 🔐 Certificados Digitais 87 | 88 | ### Identificar seu Certificado (Windows) 89 | 90 | ```cmd 91 | certutil -store My 92 | ``` 93 | 94 | ### Tipos Suportados 95 | 96 | | Tipo | Descrição | Configuração | 97 | |------|-----------|--------------| 98 | | A1 | Arquivo .pfx/.p12 | `PJE_CERTIFICATE_PFX_PATH` | 99 | | A3 | Token/Smartcard | `PJE_CERTIFICATE_THUMBPRINT` | 100 | 101 | ### Certificadoras Homologadas 102 | 103 | - SERPRO 104 | - Certisign 105 | - Serasa Experian 106 | - Valid 107 | - Soluti 108 | - AC Caixa 109 | 110 | ## 🏛️ Tribunais Testados 111 | 112 | - **TJCE** - Tribunal de Justiça do Ceará 113 | - **TRF5** - Tribunal Regional Federal da 5ª Região 114 | - **TJMG** - Tribunal de Justiça de Minas Gerais 115 | - **TJSP** - Tribunal de Justiça de São Paulo 116 | - **TJRJ** - Tribunal de Justiça do Rio de Janeiro 117 | 118 | ## 📝 Comandos Disponíveis 119 | 120 | ### Configuração 121 | - `pje_configurar` - Configura conexão com o tribunal 122 | - `pje_configurar_certificado` - Configura certificado digital 123 | - `pje_listar_certificados` - Lista certificados instalados 124 | - `pje_info_certificado` - Informações do certificado atual 125 | - `pje_status` - Status da configuração 126 | 127 | ### Consultas 128 | - `pje_listar_processos` - Lista processos com filtros 129 | - `pje_buscar_processo` - Busca processo por número 130 | - `pje_listar_orgaos_julgadores` - Lista órgãos 131 | - `pje_listar_classes` - Classes processuais 132 | - `pje_listar_assuntos` - Assuntos disponíveis 133 | 134 | ## 🛠️ Desenvolvimento 135 | 136 | ### Estrutura do Projeto 137 | 138 | ``` 139 | pje-mcp-server/ 140 | ├── src/ # Código fonte TypeScript 141 | │ ├── index.ts # Servidor principal 142 | │ ├── certificate-manager.ts # Gerenciamento de certificados 143 | │ └── types.ts # Tipos e interfaces 144 | ├── build/ # Código compilado (gerado) 145 | ├── docs/ # Documentação adicional 146 | ├── examples/ # Exemplos de configuração 147 | └── package.json # Configuração do projeto 148 | ``` 149 | 150 | ### Scripts Disponíveis 151 | 152 | ```bash 153 | npm run build # Compila o TypeScript 154 | npm run start # Inicia o servidor 155 | npm run dev # Compila e inicia 156 | npm run clean # Limpa arquivos compilados 157 | ``` 158 | 159 | ## 🐛 Solução de Problemas 160 | 161 | ### Erro: "Certificado não encontrado" 162 | ```bash 163 | # Liste certificados disponíveis 164 | certutil -store My 165 | # Copie o thumbprint correto para o .env 166 | ``` 167 | 168 | ### Erro: "Comando não encontrado" 169 | - Reinicie o Claude Desktop completamente 170 | - Verifique o caminho no claude_desktop_config.json 171 | 172 | ### Erro: "Autenticação falhou" 173 | - Verifique a validade do certificado 174 | - Confirme a URL do tribunal 175 | - Teste com outro certificado 176 | 177 | ## 🤝 Contribuindo 178 | 179 | 1. Faça um Fork do projeto 180 | 2. Crie sua Feature Branch (`git checkout -b feature/NovaFuncionalidade`) 181 | 3. Commit suas mudanças (`git commit -m 'Add: Nova funcionalidade'`) 182 | 4. Push para a Branch (`git push origin feature/NovaFuncionalidade`) 183 | 5. Abra um Pull Request 184 | 185 | ## 📄 Licença 186 | 187 | Este projeto está sob a licença MIT. Veja o arquivo [LICENSE](LICENSE) para mais detalhes. 188 | 189 | ## 🔗 Links Úteis 190 | 191 | - [Documentação do MCP](https://modelcontextprotocol.io) 192 | - [Claude Desktop](https://claude.ai/download) 193 | - [Documentação do PJE](https://www.pje.jus.br/wiki) 194 | 195 | ## 📞 Suporte 196 | 197 | - **Issues**: [GitHub Issues](https://github.com/seu-usuario/pje-mcp-server/issues) 198 | - **Discussões**: [GitHub Discussions](https://github.com/seu-usuario/pje-mcp-server/discussions) 199 | - **Email**: [email protected] 200 | 201 | --- 202 | 203 | Desenvolvido com ❤️ para a comunidade jurídica brasileira ``` -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { exec } from 'child_process'; 2 | import { promisify } from 'util'; 3 | 4 | export const execAsync = promisify(exec); ``` -------------------------------------------------------------------------------- /examples/claude_desktop_config.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "mcpServers": { 3 | "pje": { 4 | "command": "node", 5 | "args": ["C:\\Users\\seu-usuario\\pje-mcp-server\\build\\index.js"], 6 | "env": { 7 | "NODE_ENV": "production" 8 | } 9 | }, 10 | "pje-dev": { 11 | "command": "node", 12 | "args": ["C:\\Users\\seu-usuario\\pje-mcp-server-dev\\build\\index.js"], 13 | "env": { 14 | "NODE_ENV": "development", 15 | "PJE_DEBUG": "true" 16 | } 17 | } 18 | } 19 | } ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "CommonJS", 5 | "moduleResolution": "node", 6 | "lib": ["ES2022"], 7 | "outDir": "./build", 8 | "rootDir": "./src", 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "declaration": true, 14 | "declarationMap": false, 15 | "sourceMap": false, 16 | "allowSyntheticDefaultImports": true, 17 | "resolveJsonModule": true 18 | }, 19 | "include": [ 20 | "src/**/*" 21 | ], 22 | "exclude": [ 23 | "node_modules", 24 | "build", 25 | "dist", 26 | "examples", 27 | "docs" 28 | ] 29 | } ``` -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- ```typescript 1 | export interface PJEProcesso { 2 | id: string; 3 | numero: string; 4 | classe: string; 5 | assunto: string; 6 | dataDistribuicao: string; 7 | valorCausa: number; 8 | partes: PJEParte[]; 9 | movimentos: PJEMovimento[]; 10 | } 11 | 12 | export interface PJEParte { 13 | tipo: "ATIVO" | "PASSIVO"; 14 | nome: string; 15 | cpfCnpj?: string; 16 | advogados?: PJEAdvogado[]; 17 | } 18 | 19 | export interface PJEAdvogado { 20 | nome: string; 21 | oab: string; 22 | } 23 | 24 | export interface PJEMovimento { 25 | data: string; 26 | descricao: string; 27 | tipo: string; 28 | documentos?: PJEDocumento[]; 29 | } 30 | 31 | export interface PJEDocumento { 32 | id: string; 33 | nome: string; 34 | tipo: string; 35 | dataJuntada: string; 36 | } 37 | 38 | export interface PJEOrgaoJulgador { 39 | id: string; 40 | nome: string; 41 | tipo: string; 42 | competencia: string; 43 | } 44 | 45 | export interface PJEClasse { 46 | id: string; 47 | codigo: string; 48 | nome: string; 49 | sigla: string; 50 | } 51 | 52 | export interface PJEAssunto { 53 | id: string; 54 | codigo: string; 55 | nome: string; 56 | codigoPai?: string; 57 | } ``` -------------------------------------------------------------------------------- /src/test-certificate.ts: -------------------------------------------------------------------------------- ```typescript 1 | import * as dotenv from 'dotenv'; 2 | import { CertificateManager } from './certificate-manager.js'; 3 | 4 | // Carregar variáveis de ambiente 5 | dotenv.config(); 6 | 7 | async function testCertificate() { 8 | try { 9 | console.log('Variáveis de ambiente carregadas:'); 10 | console.log('PJE_CERTIFICATE_THUMBPRINT:', process.env.PJE_CERTIFICATE_THUMBPRINT); 11 | console.log('PJE_CERTIFICATE_PASSWORD:', process.env.PJE_CERTIFICATE_PASSWORD); 12 | 13 | if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { 14 | throw new Error('Configuração do certificado não encontrada'); 15 | } 16 | 17 | const config = { 18 | certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, 19 | certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD 20 | }; 21 | 22 | console.log('Configuração do certificado:', config); 23 | 24 | const certificateManager = new CertificateManager(config); 25 | await certificateManager.initialize(); 26 | 27 | console.log('Certificado inicializado com sucesso!'); 28 | } catch (error) { 29 | console.error('Erro ao testar certificado:', error); 30 | process.exit(1); 31 | } 32 | } 33 | 34 | testCertificate(); ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "pje-mcp-server", 3 | "version": "1.0.0", 4 | "description": "Servidor MCP para integração com o sistema PJE (Processo Judicial Eletrônico) brasileiro", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "start": "node build/index.js", 9 | "start:web": "node build/web-server.js", 10 | "dev": "tsc && node build/index.js", 11 | "test": "node build/test-server.js", 12 | "clean": "rm -rf build", 13 | "prepare": "npm run build" 14 | }, 15 | "keywords": [ 16 | "pje", 17 | "processo-judicial-eletronico", 18 | "mcp", 19 | "model-context-protocol", 20 | "api", 21 | "juridico", 22 | "certificado-digital", 23 | "tribunal", 24 | "tjce", 25 | "trf5" 26 | ], 27 | "author": "", 28 | "license": "MIT", 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/seu-usuario/pje-mcp-server.git" 32 | }, 33 | "dependencies": { 34 | "@modelcontextprotocol/sdk": "^0.5.0", 35 | "axios": "^1.9.0", 36 | "body-parser": "^2.2.0", 37 | "dotenv": "^16.5.0", 38 | "ejs": "^3.1.10", 39 | "express": "^4.21.2", 40 | "multer": "^1.4.5-lts.1", 41 | "node-forge": "^1.3.1", 42 | "pdf-lib": "^1.17.1", 43 | "pdf-parse": "^1.1.1" 44 | }, 45 | "devDependencies": { 46 | "@types/express": "^4.17.21", 47 | "@types/multer": "^1.4.11", 48 | "@types/node": "^20.17.51", 49 | "@types/node-forge": "^1.3.11", 50 | "typescript": "^5.8.3" 51 | }, 52 | "files": [ 53 | "build", 54 | "docs", 55 | "examples", 56 | "public" 57 | ], 58 | "bin": { 59 | "pje-mcp-server": "build/index.js" 60 | }, 61 | "engines": { 62 | "node": ">=18.0.0" 63 | } 64 | } 65 | ``` -------------------------------------------------------------------------------- /docs/INSTALACAO.md: -------------------------------------------------------------------------------- ```markdown 1 | # 📦 Guia de Instalação Completo 2 | 3 | ## Pré-requisitos 4 | 5 | - Node.js 18 ou superior 6 | - NPM ou Yarn 7 | - Git 8 | - Certificado Digital (A1 ou A3) 9 | - Claude Desktop instalado 10 | 11 | ## Instalação Passo a Passo 12 | 13 | ### 1. Clone o Repositório 14 | 15 | ```bash 16 | git clone https://github.com/seu-usuario/pje-mcp-server.git 17 | cd pje-mcp-server 18 | ``` 19 | 20 | ### 2. Instale as Dependências 21 | 22 | ```bash 23 | npm install 24 | ``` 25 | 26 | ### 3. Configure o Ambiente 27 | 28 | ```bash 29 | # Copie o arquivo de exemplo 30 | cp .env.example .env 31 | 32 | # Edite com suas configurações 33 | notepad .env # Windows 34 | nano .env # Linux/Mac 35 | ``` 36 | 37 | ### 4. Configure seu Tribunal 38 | 39 | Edite o arquivo `.env`: 40 | 41 | ```env 42 | # TJCE - Ceará 43 | PJE_BASE_URL=https://pje.tjce.jus.br 44 | PJE_APP_NAME=pje-tjce-1g 45 | 46 | # TRF5 - 5ª Região 47 | # PJE_BASE_URL=https://pje.trf5.jus.br 48 | # PJE_APP_NAME=pje-trf5 49 | 50 | # TJMG - Minas Gerais 51 | # PJE_BASE_URL=https://pje.tjmg.jus.br 52 | # PJE_APP_NAME=pje-tjmg-1g 53 | ``` 54 | 55 | ### 5. Configure o Certificado Digital 56 | 57 | #### Opção A: Arquivo PFX (A1) 58 | 59 | ```env 60 | PJE_CERTIFICATE_PFX_PATH=C:\Users\seu-usuario\certificado.pfx 61 | PJE_CERTIFICATE_PFX_PASSWORD=sua-senha 62 | ``` 63 | 64 | #### Opção B: Windows Store (A3) 65 | 66 | 1. Liste os certificados: 67 | ```cmd 68 | certutil -store My 69 | ``` 70 | 71 | 2. Copie o thumbprint: 72 | ```env 73 | PJE_CERTIFICATE_THUMBPRINT=1234567890abcdef... 74 | ``` 75 | 76 | ### 6. Compile o Projeto 77 | 78 | ```bash 79 | npm run build 80 | ``` 81 | 82 | ### 7. Configure o Claude Desktop 83 | 84 | #### Windows 85 | 86 | 1. Abra o arquivo: 87 | ``` 88 | %APPDATA%\Claude\claude_desktop_config.json 89 | ``` 90 | 91 | 2. Adicione a configuração: 92 | ```json 93 | { 94 | "mcpServers": { 95 | "pje": { 96 | "command": "node", 97 | "args": ["C:\\Users\\seu-usuario\\pje-mcp-server\\build\\index.js"] 98 | } 99 | } 100 | } 101 | ``` 102 | 103 | #### Mac/Linux 104 | 105 | 1. Abra o arquivo: 106 | ``` 107 | ~/.config/claude/claude_desktop_config.json 108 | ``` 109 | 110 | 2. Adicione a configuração: 111 | ```json 112 | { 113 | "mcpServers": { 114 | "pje": { 115 | "command": "node", 116 | "args": ["/home/seu-usuario/pje-mcp-server/build/index.js"] 117 | } 118 | } 119 | } 120 | ``` 121 | 122 | ### 8. Reinicie o Claude Desktop 123 | 124 | - Feche completamente (Ctrl+Q ou Cmd+Q) 125 | - Aguarde 5 segundos 126 | - Abra novamente 127 | 128 | ### 9. Teste a Instalação 129 | 130 | No Claude, digite: 131 | ``` 132 | Verifique o status do PJE 133 | ``` 134 | 135 | Se tudo estiver correto, você verá as informações de configuração. 136 | 137 | ## Instalação Global (Opcional) 138 | 139 | Para usar em qualquer lugar: 140 | 141 | ```bash 142 | # Instale globalmente 143 | npm install -g . 144 | 145 | # Configure no Claude Desktop 146 | { 147 | "mcpServers": { 148 | "pje": { 149 | "command": "pje-mcp-server" 150 | } 151 | } 152 | } 153 | ``` 154 | 155 | ## Troubleshooting 156 | 157 | ### Erro: "Cannot find module" 158 | ```bash 159 | npm install 160 | npm run build 161 | ``` 162 | 163 | ### Erro: "Permission denied" 164 | - Windows: Execute como Administrador 165 | - Linux/Mac: Use `sudo` se necessário 166 | 167 | ### Erro: "Tool not found" 168 | - Verifique se o caminho no claude_desktop_config.json está correto 169 | - Certifique-se de que o Claude foi reiniciado ``` -------------------------------------------------------------------------------- /src/pje-client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { Agent } from 'https'; 2 | import { CertificateManager } from './certificate-manager.js'; 3 | import { ProcessInfo } from './pdf-processor.js'; 4 | 5 | interface PJEClientConfig { 6 | baseUrl: string; 7 | appName: string; 8 | certificate: { 9 | certificateThumbprint: string; 10 | certificatePassword: string; 11 | }; 12 | } 13 | 14 | export class PJEClient { 15 | private config: PJEClientConfig; 16 | private certificateManager: CertificateManager | null = null; 17 | private agent: Agent | null = null; 18 | 19 | constructor(config: PJEClientConfig) { 20 | this.config = config; 21 | } 22 | 23 | async initializeCertificate(): Promise<void> { 24 | console.log('Inicializando certificado...'); 25 | if (!this.config.certificate) { 26 | throw new Error('Configuração do certificado não fornecida'); 27 | } 28 | 29 | try { 30 | this.certificateManager = new CertificateManager({ 31 | certificateThumbprint: this.config.certificate.certificateThumbprint, 32 | certificatePassword: this.config.certificate.certificatePassword 33 | }); 34 | 35 | await this.certificateManager.initialize(); 36 | this.agent = this.certificateManager.getAgent(); 37 | console.log('Certificado inicializado com sucesso'); 38 | } catch (error) { 39 | console.error('Erro ao inicializar certificado:', error); 40 | throw error; 41 | } 42 | } 43 | 44 | async authenticateWithCertificate(): Promise<void> { 45 | if (!this.agent) { 46 | throw new Error('Certificado não inicializado'); 47 | } 48 | 49 | // Aqui você implementaria a autenticação com o PJE 50 | // Por enquanto, vamos apenas simular que a autenticação foi bem-sucedida 51 | console.log('Autenticado com sucesso usando certificado digital'); 52 | } 53 | 54 | async buscarProcesso(numeroProcesso: string): Promise<any> { 55 | if (!this.agent) { 56 | throw new Error('Certificado não inicializado'); 57 | } 58 | 59 | // Aqui você implementaria a busca do processo no PJE 60 | // Por enquanto, vamos retornar um objeto simulado 61 | return { 62 | numeroProcesso, 63 | classe: 'Procedimento Comum Cível', 64 | assunto: 'Indenização por Dano Material', 65 | orgaoJulgador: '1ª Vara Cível', 66 | partes: [ 67 | { tipo: 'Autor', nome: 'João da Silva' }, 68 | { tipo: 'Réu', nome: 'Empresa XYZ Ltda' } 69 | ] 70 | }; 71 | } 72 | 73 | async peticionar(processInfo: ProcessInfo): Promise<string> { 74 | if (!this.agent) { 75 | throw new Error('Certificado não inicializado'); 76 | } 77 | 78 | // Aqui você implementaria o peticionamento no PJE 79 | // Por enquanto, vamos retornar um número de protocolo simulado 80 | const protocol = Math.random().toString(36).substring(2, 15); 81 | console.log('Petição protocolada com sucesso:', { 82 | numeroProcesso: processInfo.numeroProcesso, 83 | tipoPeticao: processInfo.tipoPeticao, 84 | protocol 85 | }); 86 | 87 | return protocol; 88 | } 89 | } ``` -------------------------------------------------------------------------------- /src/pdf-processor.ts: -------------------------------------------------------------------------------- ```typescript 1 | import fs from 'fs/promises'; 2 | // @ts-ignore 3 | const pdfParse = require('pdf-parse'); 4 | 5 | export interface ProcessInfo { 6 | numeroProcesso: string; 7 | tipoPeticao: string; 8 | classeProcessual: string; 9 | orgaoJulgador: string; 10 | assunto: string; 11 | } 12 | 13 | export async function extractProcessInfo(pdfPath: string): Promise<ProcessInfo> { 14 | try { 15 | console.log('Lendo arquivo PDF:', pdfPath); 16 | // Ler o arquivo PDF 17 | const pdfBuffer = await fs.readFile(pdfPath); 18 | 19 | console.log('Extraindo texto do PDF...'); 20 | // Extrair texto do PDF 21 | const data = await pdfParse(pdfBuffer); 22 | const fullText = data.text; 23 | 24 | console.log('Texto extraído do PDF:', fullText.substring(0, 500) + '...'); 25 | 26 | // Extrair número do processo usando regex 27 | const processoMatch = fullText.match(/\d{20}/); 28 | if (!processoMatch) { 29 | console.log('Número do processo não encontrado. Tentando outros formatos...'); 30 | // Tentar outros formatos comuns de número de processo 31 | const formatos = [ 32 | /\d{7}-\d{2}\.\d{4}\.\d{1,2}\.\d{2}\.\d{4}/, // 0000000-00.0000.0.00.0000 33 | /\d{20}/, // 00000000000000000000 34 | /\d{7}-\d{2}\.\d{4}\.\d{1,2}\.\d{2}\.\d{4}\.\d{4}/ // 0000000-00.0000.0.00.0000.0000 35 | ]; 36 | 37 | for (const formato of formatos) { 38 | const match = fullText.match(formato); 39 | if (match) { 40 | console.log('Número do processo encontrado com formato alternativo:', match[0]); 41 | return { 42 | numeroProcesso: match[0], 43 | tipoPeticao: 'Petição Intermediária', 44 | classeProcessual: '', 45 | orgaoJulgador: '', 46 | assunto: '' 47 | }; 48 | } 49 | } 50 | 51 | throw new Error('Número do processo não encontrado no PDF'); 52 | } 53 | 54 | console.log('Número do processo encontrado:', processoMatch[0]); 55 | 56 | // Extrair tipo de petição (assumindo que é sempre "Petição Intermediária") 57 | const tipoPeticao = 'Petição Intermediária'; 58 | 59 | // Extrair classe processual (assumindo que está após "Classe:") 60 | const classeMatch = fullText.match(/Classe:\s*([^\n]+)/i); 61 | const classeProcessual = classeMatch ? classeMatch[1].trim() : ''; 62 | 63 | // Extrair órgão julgador (assumindo que está após "Órgão Julgador:") 64 | const orgaoMatch = fullText.match(/Órgão Julgador:\s*([^\n]+)/i); 65 | const orgaoJulgador = orgaoMatch ? orgaoMatch[1].trim() : ''; 66 | 67 | // Extrair assunto (assumindo que está após "Assunto:") 68 | const assuntoMatch = fullText.match(/Assunto:\s*([^\n]+)/i); 69 | const assunto = assuntoMatch ? assuntoMatch[1].trim() : ''; 70 | 71 | return { 72 | numeroProcesso: processoMatch[0], 73 | tipoPeticao, 74 | classeProcessual, 75 | orgaoJulgador, 76 | assunto 77 | }; 78 | } catch (error) { 79 | console.error('Erro ao processar PDF:', error); 80 | throw new Error('Erro ao processar o arquivo PDF'); 81 | } 82 | } ``` -------------------------------------------------------------------------------- /docs/TRIBUNAIS_SUPORTADOS.md: -------------------------------------------------------------------------------- ```markdown 1 | # 🏛️ Tribunais Suportados 2 | 3 | ## Configurações por Tribunal 4 | 5 | ### Região Nordeste 6 | 7 | #### TJCE - Tribunal de Justiça do Ceará 8 | ```env 9 | PJE_BASE_URL=https://pje.tjce.jus.br 10 | PJE_APP_NAME=pje-tjce-1g # 1º grau 11 | PJE_APP_NAME=pje-tjce-2g # 2º grau 12 | ``` 13 | 14 | #### TJBA - Tribunal de Justiça da Bahia 15 | ```env 16 | PJE_BASE_URL=https://pje.tjba.jus.br 17 | PJE_APP_NAME=pje-tjba-1g 18 | PJE_APP_NAME=pje-tjba-2g 19 | ``` 20 | 21 | #### TJPE - Tribunal de Justiça de Pernambuco 22 | ```env 23 | PJE_BASE_URL=https://pje.tjpe.jus.br 24 | PJE_APP_NAME=pje-tjpe-1g 25 | PJE_APP_NAME=pje-tjpe-2g 26 | ``` 27 | 28 | ### Região Sudeste 29 | 30 | #### TJSP - Tribunal de Justiça de São Paulo 31 | ```env 32 | PJE_BASE_URL=https://pje.tjsp.jus.br 33 | PJE_APP_NAME=pje-tjsp-1g 34 | PJE_APP_NAME=pje-tjsp-2g 35 | ``` 36 | 37 | #### TJMG - Tribunal de Justiça de Minas Gerais 38 | ```env 39 | PJE_BASE_URL=https://pje.tjmg.jus.br 40 | PJE_APP_NAME=pje-tjmg-1g 41 | PJE_APP_NAME=pje-tjmg-2g 42 | ``` 43 | 44 | #### TJRJ - Tribunal de Justiça do Rio de Janeiro 45 | ```env 46 | PJE_BASE_URL=https://pje.tjrj.jus.br 47 | PJE_APP_NAME=pje-tjrj-1g 48 | PJE_APP_NAME=pje-tjrj-2g 49 | ``` 50 | 51 | ### Tribunais Regionais Federais 52 | 53 | #### TRF1 - 1ª Região 54 | ```env 55 | PJE_BASE_URL=https://pje1g.trf1.jus.br 56 | PJE_APP_NAME=pje-trf1 57 | ``` 58 | 59 | #### TRF2 - 2ª Região 60 | ```env 61 | PJE_BASE_URL=https://pje.trf2.jus.br 62 | PJE_APP_NAME=pje-trf2 63 | ``` 64 | 65 | #### TRF3 - 3ª Região 66 | ```env 67 | PJE_BASE_URL=https://pje.trf3.jus.br 68 | PJE_APP_NAME=pje-trf3 69 | ``` 70 | 71 | #### TRF4 - 4ª Região 72 | ```env 73 | PJE_BASE_URL=https://pje.trf4.jus.br 74 | PJE_APP_NAME=pje-trf4 75 | ``` 76 | 77 | #### TRF5 - 5ª Região 78 | ```env 79 | PJE_BASE_URL=https://pje.trf5.jus.br 80 | PJE_APP_NAME=pje-trf5 81 | ``` 82 | 83 | #### TRF6 - 6ª Região 84 | ```env 85 | PJE_BASE_URL=https://pje.trf6.jus.br 86 | PJE_APP_NAME=pje-trf6 87 | ``` 88 | 89 | ### Tribunais Superiores 90 | 91 | #### STJ - Superior Tribunal de Justiça 92 | ```env 93 | PJE_BASE_URL=https://pje.stj.jus.br 94 | PJE_APP_NAME=pje-stj 95 | ``` 96 | 97 | #### TST - Tribunal Superior do Trabalho 98 | ```env 99 | PJE_BASE_URL=https://pje.tst.jus.br 100 | PJE_APP_NAME=pje-tst 101 | ``` 102 | 103 | ## Particularidades por Tribunal 104 | 105 | ### TJSP 106 | - Maior volume de processos 107 | - Requer certificado ICP-Brasil 108 | - APIs otimizadas para alto volume 109 | 110 | ### TJMG 111 | - Integração com SINTER 112 | - Suporte a peticionamento em lote 113 | - APIs específicas para precatórios 114 | 115 | ### TRF5 116 | - Abrange: PE, AL, SE, PB, RN, CE 117 | - APIs unificadas para toda região 118 | - Suporte a JEF (Juizado Especial Federal) 119 | 120 | ## Testando Conexão 121 | 122 | ### Comando Básico 123 | ``` 124 | Configure o PJE do [TRIBUNAL] 125 | ``` 126 | 127 | ### Verificar Endpoints 128 | ``` 129 | Liste os órgãos julgadores 130 | ``` 131 | 132 | ### Testar Autenticação 133 | ``` 134 | Liste meus processos 135 | ``` 136 | 137 | ## URLs Alternativas 138 | 139 | Alguns tribunais possuem URLs diferentes para: 140 | 141 | ### Produção 142 | - URL principal do tribunal 143 | 144 | ### Homologação 145 | - Geralmente: `https://pje-homo.tribunal.jus.br` 146 | - Para testes antes da produção 147 | 148 | ### Treinamento 149 | - Geralmente: `https://pje-treina.tribunal.jus.br` 150 | - Para capacitação de usuários 151 | 152 | ## Suporte Específico 153 | 154 | ### Consulta Pública 155 | Todos os tribunais suportam: 156 | - Busca por número 157 | - Consulta de movimentações 158 | - Download de documentos públicos 159 | 160 | ### Funcionalidades Autenticadas 161 | Com certificado digital: 162 | - Peticionamento 163 | - Consulta completa 164 | - Download de documentos sigilosos 165 | - Intimações 166 | 167 | ## Status de Implementação 168 | 169 | | Tribunal | Consulta | Peticionamento | Observações | 170 | |----------|----------|----------------|-------------| 171 | | TJCE | ✅ | ✅ | Totalmente funcional | 172 | | TRF5 | ✅ | ✅ | Totalmente funcional | 173 | | TJMG | ✅ | ⚠️ | Em testes | 174 | | TJSP | ✅ | 🔄 | Em desenvolvimento | 175 | | Outros | ✅ | ❓ | Não testado | 176 | 177 | Legenda: 178 | - ✅ Funcionando 179 | - ⚠️ Parcialmente 180 | - 🔄 Em desenvolvimento 181 | - ❓ Não testado ``` -------------------------------------------------------------------------------- /docs/CONFIGURACAO_CERTIFICADO.md: -------------------------------------------------------------------------------- ```markdown 1 | # 🔐 Configuração de Certificado Digital 2 | 3 | ## Tipos de Certificado 4 | 5 | ### Certificado A1 (Arquivo) 6 | 7 | - **Formato**: .pfx ou .p12 8 | - **Validade**: 1 ano 9 | - **Armazenamento**: Arquivo no computador 10 | - **Mobilidade**: Pode ser copiado 11 | 12 | ### Certificado A3 (Token/Smartcard) 13 | 14 | - **Formato**: Hardware (token USB ou cartão) 15 | - **Validade**: 3 anos 16 | - **Armazenamento**: Dispositivo criptográfico 17 | - **Mobilidade**: Precisa do dispositivo físico 18 | 19 | ## Configuração por Tipo 20 | 21 | ### 1. Certificado A1 (Arquivo PFX) 22 | 23 | #### Passo 1: Localize seu arquivo 24 | ```bash 25 | # Geralmente em: 26 | C:\Users\seu-usuario\Documents\certificados\ 27 | C:\certificados\ 28 | ``` 29 | 30 | #### Passo 2: Configure no .env 31 | ```env 32 | PJE_CERTIFICATE_PFX_PATH=C:\caminho\para\seu\certificado.pfx 33 | PJE_CERTIFICATE_PFX_PASSWORD=senha-do-certificado 34 | ``` 35 | 36 | #### Passo 3: Teste 37 | ```bash 38 | node build/index.js 39 | ``` 40 | 41 | ### 2. Certificado A3 (Windows Store) 42 | 43 | #### Passo 1: Instale o driver do token 44 | - Safenet (tokens brancos) 45 | - eToken (tokens azuis) 46 | - Outros conforme fabricante 47 | 48 | #### Passo 2: Liste os certificados 49 | ```cmd 50 | certutil -store My 51 | ``` 52 | 53 | Saída esperada: 54 | ``` 55 | ================ Certificate 0 ================ 56 | Serial Number: 1234567890abcdef 57 | Issuer: CN=AC SOLUTI Multipla v5, O=ICP-Brasil 58 | Subject: CN=SEU NOME:12345678900 59 | Cert Hash(sha1): a1b2c3d4e5f6789012345678901234567890abcd 60 | ``` 61 | 62 | #### Passo 3: Configure no .env 63 | 64 | Por thumbprint: 65 | ```env 66 | PJE_CERTIFICATE_THUMBPRINT=a1b2c3d4e5f6789012345678901234567890abcd 67 | ``` 68 | 69 | Por subject: 70 | ```env 71 | PJE_CERTIFICATE_SUBJECT=CN=SEU NOME:12345678900 72 | ``` 73 | 74 | ## Identificando seu Certificado 75 | 76 | ### No Windows 77 | 78 | 1. Abra o Gerenciador de Certificados: 79 | ```cmd 80 | certmgr.msc 81 | ``` 82 | 83 | 2. Navegue para: 84 | - Pessoal > Certificados 85 | 86 | 3. Procure por certificados com: 87 | - Seu nome 88 | - CPF ou CNPJ 89 | - Válidos (não expirados) 90 | 91 | ### Verificando Validade 92 | 93 | ```powershell 94 | # PowerShell 95 | Get-ChildItem Cert:\CurrentUser\My | Select Subject, NotAfter 96 | ``` 97 | 98 | ## Problemas Comuns 99 | 100 | ### "Certificado não encontrado" 101 | 102 | 1. Verifique se está instalado: 103 | ```cmd 104 | certutil -store My | findstr "Subject" 105 | ``` 106 | 107 | 2. Confirme o thumbprint: 108 | ```cmd 109 | certutil -store My | findstr "Cert Hash" 110 | ``` 111 | 112 | ### "Senha incorreta" 113 | 114 | Para A1: 115 | - Verifique a senha do arquivo PFX 116 | - Tente abrir com outro programa 117 | - Solicite nova senha ao emissor 118 | 119 | ### "Token não reconhecido" 120 | 121 | Para A3: 122 | 1. Instale o driver do fabricante 123 | 2. Reinicie o computador 124 | 3. Verifique no Gerenciador de Dispositivos 125 | 4. Teste com o software do fabricante 126 | 127 | ## Certificadoras Homologadas 128 | 129 | ### SERPRO 130 | - Site: https://certificados.serpro.gov.br 131 | - Suporte: 0800 728 0323 132 | - Driver: Fornecido na compra 133 | 134 | ### Certisign 135 | - Site: https://www.certisign.com.br 136 | - Suporte: 0800 701 1799 137 | - Driver: https://drivers.certisign.com.br 138 | 139 | ### Serasa Experian 140 | - Site: https://serasa.certificadodigital.com.br 141 | - Suporte: 0800 773 7272 142 | - Driver: No site de suporte 143 | 144 | ### Valid 145 | - Site: https://www.validcertificadora.com.br 146 | - Suporte: 0800 774 4414 147 | - Driver: Central de downloads 148 | 149 | ## Melhores Práticas 150 | 151 | 1. **Backup do A1** 152 | - Guarde cópia em local seguro 153 | - Use senha forte 154 | - Não compartilhe 155 | 156 | 2. **Cuidados com A3** 157 | - Não remova durante uso 158 | - Mantenha drivers atualizados 159 | - Teste periodicamente 160 | 161 | 3. **Renovação** 162 | - A1: Anualmente 163 | - A3: A cada 3 anos 164 | - Agende com antecedência 165 | 166 | 4. **Segurança** 167 | - Nunca envie por email 168 | - Não salve senha em texto 169 | - Use gerenciador de senhas 170 | 171 | ## Comandos Úteis 172 | 173 | ### Testar certificado 174 | ```bash 175 | # No Claude Desktop 176 | "Liste os certificados digitais" 177 | "Mostre informações do meu certificado" 178 | "Verifique a validade do certificado" 179 | ``` 180 | 181 | ### Exportar certificado (backup A1) 182 | ```cmd 183 | certutil -exportPFX -p "senha" My "thumbprint" "backup.pfx" 184 | ``` 185 | 186 | ### Importar certificado 187 | ```cmd 188 | certutil -importPFX "arquivo.pfx" 189 | ``` ``` -------------------------------------------------------------------------------- /src/certificate-manager.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { Agent } from 'https'; 2 | import { exec } from 'child_process'; 3 | import { promisify } from 'util'; 4 | import * as fs from 'fs/promises'; 5 | import * as path from 'path'; 6 | 7 | const execAsync = promisify(exec); 8 | 9 | export interface CertificateConfig { 10 | certificateThumbprint: string; 11 | certificatePath?: string; 12 | certificatePassword: string; 13 | pfxPath?: string; 14 | pfxPassword?: string; 15 | certificateSubject?: string; 16 | } 17 | 18 | interface TokenCertificate { 19 | thumbprint: string; 20 | provider: string; 21 | } 22 | 23 | interface TokenKey { 24 | password: string; 25 | provider: string; 26 | } 27 | 28 | export class CertificateManager { 29 | private config: CertificateConfig; 30 | private agent: Agent | null = null; 31 | 32 | constructor(config: CertificateConfig) { 33 | this.config = config; 34 | } 35 | 36 | async initialize(): Promise<void> { 37 | console.log('Iniciando CertificateManager com config:', { 38 | ...this.config, 39 | certificatePassword: '***' // Ocultar senha nos logs 40 | }); 41 | 42 | if (!this.config.certificateThumbprint) { 43 | throw new Error('Thumbprint do certificado não fornecido'); 44 | } 45 | 46 | try { 47 | // Verificar se o certificado está disponível 48 | console.log('Verificando certificado no repositório...'); 49 | const { stdout, stderr } = await execAsync(`certutil -user -store My "${this.config.certificateThumbprint}"`); 50 | 51 | if (stderr) { 52 | console.error('Erro ao verificar certificado:', stderr); 53 | } 54 | 55 | console.log('Resposta do certutil:', stdout); 56 | 57 | if (!stdout.includes(this.config.certificateThumbprint)) { 58 | throw new Error('Certificado não encontrado no repositório'); 59 | } 60 | 61 | // Verificar se o token está conectado 62 | console.log('Verificando status do token...'); 63 | const { stdout: tokenStatus } = await execAsync('certutil -user -csp "SafeSign IC Standard Windows Cryptographic Service Provider" -key'); 64 | console.log('Status do token:', tokenStatus); 65 | 66 | // Configurar o agente HTTPS com o certificado do token 67 | console.log('Configurando agente HTTPS com certificado do token...'); 68 | const cert: TokenCertificate = { 69 | thumbprint: this.config.certificateThumbprint, 70 | provider: 'SafeSign IC Standard Windows Cryptographic Service Provider' 71 | }; 72 | 73 | const key: TokenKey = { 74 | password: this.config.certificatePassword, 75 | provider: 'SafeSign IC Standard Windows Cryptographic Service Provider' 76 | }; 77 | 78 | this.agent = new Agent({ 79 | rejectUnauthorized: false, 80 | cert: cert as any, 81 | key: key as any, 82 | ciphers: 'HIGH:!aNULL:!MD5:!RC4', 83 | minVersion: 'TLSv1.2', 84 | secureOptions: 0x00000001 // SSL_OP_NO_SSLv2 85 | }); 86 | 87 | console.log('Certificado inicializado com sucesso'); 88 | } catch (error) { 89 | console.error('Erro detalhado ao inicializar certificado:', error); 90 | if (error instanceof Error) { 91 | throw new Error(`Falha ao inicializar certificado: ${error.message}`); 92 | } 93 | throw new Error('Falha ao inicializar certificado'); 94 | } 95 | } 96 | 97 | getAgent(): Agent { 98 | if (!this.agent) { 99 | throw new Error('Certificado não inicializado'); 100 | } 101 | return this.agent; 102 | } 103 | 104 | getHttpsAgent(): Agent { 105 | if (!this.agent) { 106 | throw new Error('Certificado não inicializado'); 107 | } 108 | return this.agent; 109 | } 110 | 111 | getCertificate(): any { 112 | if (!this.agent) { 113 | throw new Error('Certificado não inicializado'); 114 | } 115 | return this.agent.options.cert || this.agent.options.pfx; 116 | } 117 | 118 | getCertificateInfo(): any { 119 | return { 120 | thumbprint: this.config.certificateThumbprint, 121 | path: this.config.certificatePath, 122 | hasPassword: !!this.config.certificatePassword, 123 | provider: 'SafeSign IC Standard Windows Cryptographic Service Provider' 124 | }; 125 | } 126 | } ``` -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- ```html 1 | <!DOCTYPE html> 2 | <html lang="pt-BR"> 3 | <head> 4 | <meta charset="UTF-8"> 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 | <title>PJE - Peticionamento em Lote</title> 7 | <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> 8 | <style> 9 | .upload-area { 10 | border: 2px dashed #ccc; 11 | border-radius: 8px; 12 | padding: 20px; 13 | text-align: center; 14 | cursor: pointer; 15 | margin: 20px 0; 16 | } 17 | .upload-area:hover { 18 | border-color: #0d6efd; 19 | } 20 | .file-list { 21 | margin-top: 20px; 22 | } 23 | .protocol-list { 24 | margin-top: 20px; 25 | } 26 | </style> 27 | </head> 28 | <body> 29 | <div class="container py-5"> 30 | <h1 class="mb-4">PJE - Peticionamento em Lote</h1> 31 | 32 | <div class="card"> 33 | <div class="card-body"> 34 | <h5 class="card-title">Upload de Petições</h5> 35 | <div class="upload-area" id="uploadArea"> 36 | <p class="mb-0">Arraste e solte os arquivos PDF aqui ou clique para selecionar</p> 37 | <input type="file" id="fileInput" multiple accept=".pdf" style="display: none"> 38 | </div> 39 | 40 | <div class="file-list" id="fileList"></div> 41 | 42 | <button class="btn btn-primary" id="uploadButton" disabled> 43 | Enviar Petições 44 | </button> 45 | </div> 46 | </div> 47 | 48 | <div class="protocol-list" id="protocolList"></div> 49 | </div> 50 | 51 | <script> 52 | const uploadArea = document.getElementById('uploadArea'); 53 | const fileInput = document.getElementById('fileInput'); 54 | const fileList = document.getElementById('fileList'); 55 | const uploadButton = document.getElementById('uploadButton'); 56 | const protocolList = document.getElementById('protocolList'); 57 | 58 | let selectedFiles = []; 59 | 60 | // Configurar área de upload 61 | uploadArea.addEventListener('click', () => fileInput.click()); 62 | uploadArea.addEventListener('dragover', (e) => { 63 | e.preventDefault(); 64 | uploadArea.style.borderColor = '#0d6efd'; 65 | }); 66 | uploadArea.addEventListener('dragleave', () => { 67 | uploadArea.style.borderColor = '#ccc'; 68 | }); 69 | uploadArea.addEventListener('drop', (e) => { 70 | e.preventDefault(); 71 | uploadArea.style.borderColor = '#ccc'; 72 | handleFiles(e.dataTransfer.files); 73 | }); 74 | 75 | fileInput.addEventListener('change', (e) => { 76 | handleFiles(e.target.files); 77 | }); 78 | 79 | function handleFiles(files) { 80 | selectedFiles = Array.from(files).filter(file => file.type === 'application/pdf'); 81 | updateFileList(); 82 | uploadButton.disabled = selectedFiles.length === 0; 83 | } 84 | 85 | function updateFileList() { 86 | fileList.innerHTML = selectedFiles.map(file => ` 87 | <div class="alert alert-info"> 88 | ${file.name} 89 | </div> 90 | `).join(''); 91 | } 92 | 93 | uploadButton.addEventListener('click', async () => { 94 | uploadButton.disabled = true; 95 | uploadButton.textContent = 'Enviando...'; 96 | 97 | for (const file of selectedFiles) { 98 | const formData = new FormData(); 99 | formData.append('petition', file); 100 | 101 | try { 102 | console.log('Enviando arquivo:', file.name); 103 | const response = await fetch('/peticionar', { 104 | method: 'POST', 105 | body: formData 106 | }); 107 | 108 | console.log('Resposta recebida:', response.status); 109 | const result = await response.json(); 110 | console.log('Resultado:', result); 111 | 112 | if (result.success) { 113 | protocolList.innerHTML += ` 114 | <div class="alert alert-success"> 115 | <strong>${file.name}</strong><br> 116 | Protocolo: ${result.protocol}<br> 117 | Processo: ${result.processInfo.numeroProcesso} 118 | </div> 119 | `; 120 | } else { 121 | protocolList.innerHTML += ` 122 | <div class="alert alert-danger"> 123 | <strong>${file.name}</strong><br> 124 | Erro: ${result.error} 125 | ${result.details ? `<br>Detalhes: ${result.details}` : ''} 126 | </div> 127 | `; 128 | } 129 | } catch (error) { 130 | console.error('Erro ao enviar arquivo:', error); 131 | protocolList.innerHTML += ` 132 | <div class="alert alert-danger"> 133 | <strong>${file.name}</strong><br> 134 | Erro ao enviar arquivo: ${error.message} 135 | </div> 136 | `; 137 | } 138 | } 139 | 140 | uploadButton.textContent = 'Enviar Petições'; 141 | uploadButton.disabled = false; 142 | selectedFiles = []; 143 | updateFileList(); 144 | }); 145 | </script> 146 | </body> 147 | </html> ``` -------------------------------------------------------------------------------- /src/web-server.ts: -------------------------------------------------------------------------------- ```typescript 1 | import 'dotenv/config'; 2 | import express from 'express'; 3 | import multer from 'multer'; 4 | import path from 'path'; 5 | import { PJEClient } from './pje-client.js'; 6 | import { extractProcessInfo } from './pdf-processor.js'; 7 | 8 | const app = express(); 9 | const upload = multer({ dest: 'uploads/' }); 10 | 11 | // Configuração do Express 12 | app.use(express.json()); 13 | app.use(express.urlencoded({ extended: true })); 14 | app.use(express.static('public')); 15 | app.set('view engine', 'ejs'); 16 | app.set('views', path.join(process.cwd(), 'views')); 17 | 18 | // Middleware para log de requisições 19 | app.use((req, res, next) => { 20 | console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`); 21 | next(); 22 | }); 23 | 24 | // Rota principal 25 | app.get('/', (req, res) => { 26 | res.render('index', { 27 | title: 'PJE MCP Server - Interface Web', 28 | tools: [ 29 | { id: 'buscar-processo', name: 'Buscar Processo', icon: '🔍' }, 30 | { id: 'peticionar', name: 'Petição Eletrônica', icon: '📝' }, 31 | { id: 'peticionar-lote', name: 'Petição em Lote', icon: '📦' }, 32 | { id: 'listar-processos', name: 'Listar Processos', icon: '📋' }, 33 | { id: 'listar-orgaos', name: 'Órgãos Julgadores', icon: '🏛️' }, 34 | { id: 'listar-classes', name: 'Classes Processuais', icon: '📚' }, 35 | { id: 'listar-assuntos', name: 'Assuntos Processuais', icon: '📑' } 36 | ] 37 | }); 38 | }); 39 | 40 | // Rota para buscar processo 41 | app.post('/buscar-processo', async (req, res) => { 42 | try { 43 | const numeroProcesso = req.body.numeroProcesso; 44 | 45 | if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { 46 | throw new Error('Configuração do certificado não encontrada'); 47 | } 48 | 49 | // Configurar cliente PJE 50 | const pjeClient = new PJEClient({ 51 | baseUrl: process.env.PJE_BASE_URL || 'https://pje.tjce.jus.br', 52 | appName: process.env.PJE_APP_NAME || 'pje-tjce-1g', 53 | certificate: { 54 | certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, 55 | certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD 56 | } 57 | }); 58 | 59 | // Inicializar certificado 60 | await pjeClient.initializeCertificate(); 61 | await pjeClient.authenticateWithCertificate(); 62 | 63 | // Buscar processo 64 | const processo = await pjeClient.buscarProcesso(numeroProcesso); 65 | 66 | res.json({ 67 | success: true, 68 | processo 69 | }); 70 | } catch (error) { 71 | console.error('Erro ao buscar processo:', error); 72 | res.status(500).json({ 73 | success: false, 74 | error: error instanceof Error ? error.message : 'Erro ao buscar processo' 75 | }); 76 | } 77 | }); 78 | 79 | // Rota para upload de petições 80 | app.post('/peticionar', upload.single('petition'), async (req, res) => { 81 | try { 82 | console.log('Recebendo petição...'); 83 | 84 | if (!req.file) { 85 | console.log('Nenhum arquivo enviado'); 86 | return res.status(400).json({ error: 'Nenhum arquivo enviado' }); 87 | } 88 | 89 | console.log('Arquivo recebido:', req.file); 90 | 91 | if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { 92 | console.log('Configuração do certificado não encontrada'); 93 | throw new Error('Configuração do certificado não encontrada'); 94 | } 95 | 96 | console.log('Extraindo informações do PDF...'); 97 | // Extrair informações do processo do PDF 98 | const processInfo = await extractProcessInfo(req.file.path); 99 | console.log('Informações extraídas:', processInfo); 100 | 101 | console.log('Configurando cliente PJE...'); 102 | // Configurar cliente PJE 103 | const pjeClient = new PJEClient({ 104 | baseUrl: process.env.PJE_BASE_URL || 'https://pje.tjce.jus.br', 105 | appName: process.env.PJE_APP_NAME || 'pje-tjce-1g', 106 | certificate: { 107 | certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, 108 | certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD 109 | } 110 | }); 111 | 112 | console.log('Inicializando certificado...'); 113 | // Inicializar certificado 114 | await pjeClient.initializeCertificate(); 115 | await pjeClient.authenticateWithCertificate(); 116 | 117 | console.log('Realizando peticionamento...'); 118 | // Fazer o peticionamento 119 | const protocol = await pjeClient.peticionar(processInfo); 120 | 121 | console.log('Petição protocolada com sucesso:', protocol); 122 | 123 | // Retornar recibo do protocolo 124 | res.json({ 125 | success: true, 126 | protocol, 127 | processInfo 128 | }); 129 | 130 | } catch (error) { 131 | console.error('Erro detalhado ao processar petição:', error); 132 | res.status(500).json({ 133 | error: 'Erro ao processar petição', 134 | details: error instanceof Error ? error.message : 'Erro desconhecido' 135 | }); 136 | } 137 | }); 138 | 139 | // Rota para upload de petições em lote 140 | app.post('/peticionar-lote', upload.array('petitions', 10), async (req, res) => { 141 | try { 142 | if (!req.files || !Array.isArray(req.files)) { 143 | return res.status(400).json({ error: 'Nenhum arquivo enviado' }); 144 | } 145 | 146 | if (!process.env.PJE_CERTIFICATE_THUMBPRINT || !process.env.PJE_CERTIFICATE_PASSWORD) { 147 | throw new Error('Configuração do certificado não encontrada'); 148 | } 149 | 150 | const results = []; 151 | const pjeClient = new PJEClient({ 152 | baseUrl: process.env.PJE_BASE_URL || 'https://pje.tjce.jus.br', 153 | appName: process.env.PJE_APP_NAME || 'pje-tjce-1g', 154 | certificate: { 155 | certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, 156 | certificatePassword: process.env.PJE_CERTIFICATE_PASSWORD 157 | } 158 | }); 159 | 160 | await pjeClient.initializeCertificate(); 161 | await pjeClient.authenticateWithCertificate(); 162 | 163 | for (const file of req.files) { 164 | try { 165 | const processInfo = await extractProcessInfo(file.path); 166 | const protocol = await pjeClient.peticionar(processInfo); 167 | results.push({ 168 | file: file.originalname, 169 | success: true, 170 | protocol, 171 | processInfo 172 | }); 173 | } catch (error) { 174 | results.push({ 175 | file: file.originalname, 176 | success: false, 177 | error: error instanceof Error ? error.message : 'Erro ao processar petição' 178 | }); 179 | } 180 | } 181 | 182 | res.json({ 183 | success: true, 184 | results 185 | }); 186 | 187 | } catch (error) { 188 | console.error('Erro ao processar lote de petições:', error); 189 | res.status(500).json({ error: 'Erro ao processar lote de petições' }); 190 | } 191 | }); 192 | 193 | // Iniciar servidor 194 | const PORT = process.env.PORT || 3000; 195 | app.listen(PORT, () => { 196 | console.log(`Servidor web rodando na porta ${PORT}`); 197 | }); ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | #!/usr/bin/env node 2 | 3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js"; 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 5 | import { 6 | CallToolRequestSchema, 7 | ListToolsRequestSchema, 8 | Tool, 9 | } from "@modelcontextprotocol/sdk/types.js"; 10 | import axios, { AxiosInstance, AxiosResponse } from "axios"; 11 | import dotenv from "dotenv"; 12 | import { CertificateManager, CertificateConfig } from "./certificate-manager.js"; 13 | import { execAsync } from "./utils.js"; 14 | 15 | // Carrega as configurações do arquivo .env 16 | dotenv.config(); 17 | 18 | interface PJEConfig { 19 | baseUrl: string; 20 | ssoUrl?: string; 21 | clientId?: string; 22 | clientSecret?: string; 23 | username?: string; 24 | password?: string; 25 | appName?: string; 26 | certificate?: CertificateConfig; 27 | } 28 | 29 | interface PJEResponse<T = any> { 30 | status: "ok" | "error" | "in-progress"; 31 | code: string; 32 | messages: string[]; 33 | result: T; 34 | "page-info"?: { 35 | current: number; 36 | last: number; 37 | size: number; 38 | count: number; 39 | }; 40 | } 41 | 42 | class PJEClient { 43 | private axiosInstance: AxiosInstance; 44 | private accessToken?: string; 45 | private config: PJEConfig; 46 | private certificateManager?: CertificateManager; 47 | 48 | constructor(config: PJEConfig) { 49 | this.config = config; 50 | 51 | const axiosConfig: any = { 52 | baseURL: config.baseUrl, 53 | headers: { 54 | "Content-Type": "application/json", 55 | }, 56 | timeout: parseInt(process.env.PJE_TIMEOUT_MS || "30000"), 57 | }; 58 | 59 | if (config.certificate) { 60 | this.certificateManager = new CertificateManager(config.certificate); 61 | } 62 | 63 | this.axiosInstance = axios.create(axiosConfig); 64 | 65 | this.axiosInstance.interceptors.request.use((config) => { 66 | if (this.accessToken) { 67 | config.headers.Authorization = `Bearer ${this.accessToken}`; 68 | } 69 | if (this.config.appName) { 70 | config.headers["X-pje-legacy-app"] = this.config.appName; 71 | } 72 | return config; 73 | }); 74 | } 75 | 76 | async initializeCertificate(): Promise<void> { 77 | if (!this.certificateManager) { 78 | throw new Error("Nenhum certificado configurado"); 79 | } 80 | 81 | try { 82 | await this.certificateManager.initialize(); 83 | 84 | const httpsAgent = this.certificateManager.getHttpsAgent(); 85 | 86 | this.axiosInstance = axios.create({ 87 | baseURL: this.config.baseUrl, 88 | headers: { 89 | "Content-Type": "application/json", 90 | }, 91 | timeout: parseInt(process.env.PJE_TIMEOUT_MS || "30000"), 92 | httpsAgent: httpsAgent 93 | }); 94 | 95 | this.axiosInstance.interceptors.request.use((config) => { 96 | if (this.accessToken) { 97 | config.headers.Authorization = `Bearer ${this.accessToken}`; 98 | } 99 | if (this.config.appName) { 100 | config.headers["X-pje-legacy-app"] = this.config.appName; 101 | } 102 | return config; 103 | }); 104 | } catch (error) { 105 | throw new Error(`Erro ao inicializar certificado: ${error}`); 106 | } 107 | } 108 | 109 | async authenticateWithCertificate(): Promise<void> { 110 | if (!this.certificateManager) { 111 | throw new Error("Certificado não inicializado"); 112 | } 113 | 114 | try { 115 | const response = await this.axiosInstance.post('/api/v1/auth/certificate', { 116 | certificate: this.certificateManager.getCertificate() 117 | }); 118 | 119 | this.accessToken = response.data.access_token; 120 | } catch (error) { 121 | throw new Error(`Erro na autenticação por certificado: ${error}`); 122 | } 123 | } 124 | 125 | getCertificateInfo(): any { 126 | if (!this.certificateManager) { 127 | throw new Error("Nenhum certificado configurado"); 128 | } 129 | return this.certificateManager.getCertificateInfo(); 130 | } 131 | 132 | async authenticate(): Promise<void> { 133 | if (!this.config.ssoUrl || !this.config.clientId || !this.config.clientSecret) { 134 | throw new Error("Configuração de SSO incompleta"); 135 | } 136 | 137 | try { 138 | const response = await axios.post( 139 | this.config.ssoUrl, 140 | new URLSearchParams({ 141 | grant_type: "password", 142 | client_id: this.config.clientId, 143 | client_secret: this.config.clientSecret, 144 | username: this.config.username || "", 145 | password: this.config.password || "", 146 | scope: "openid", 147 | }), 148 | { 149 | headers: { 150 | "Content-Type": "application/x-www-form-urlencoded", 151 | }, 152 | } 153 | ); 154 | 155 | this.accessToken = response.data.access_token; 156 | } catch (error) { 157 | throw new Error(`Erro na autenticação: ${error}`); 158 | } 159 | } 160 | 161 | async listarProcessos(filter?: any, fields?: string[], order?: any, page?: number, size?: number): Promise<PJEResponse> { 162 | const params: any = {}; 163 | 164 | if (filter) { 165 | params.filter = typeof filter === "string" ? filter : JSON.stringify(filter); 166 | } 167 | if (fields) { 168 | params.fields = Array.isArray(fields) ? JSON.stringify(fields) : fields; 169 | } 170 | if (order) { 171 | params.order = typeof order === "string" ? order : JSON.stringify(order); 172 | } 173 | if (page || size) { 174 | params.page = JSON.stringify({ page: page || 1, size: size || 20 }); 175 | } 176 | 177 | const response = await this.axiosInstance.get("/api/v1/processos", { params }); 178 | return response.data; 179 | } 180 | 181 | async buscarProcesso(id: string): Promise<PJEResponse> { 182 | const response = await this.axiosInstance.get(`/api/v1/processos/${id}`); 183 | return response.data; 184 | } 185 | 186 | async listarOrgaosJulgadores(): Promise<PJEResponse> { 187 | const response = await this.axiosInstance.get("/api/v1/orgaos-julgadores"); 188 | return response.data; 189 | } 190 | 191 | async listarClasses(): Promise<PJEResponse> { 192 | const response = await this.axiosInstance.get("/api/v1/classes"); 193 | return response.data; 194 | } 195 | 196 | async listarAssuntos(): Promise<PJEResponse> { 197 | const response = await this.axiosInstance.get("/api/v1/assuntos"); 198 | return response.data; 199 | } 200 | } 201 | 202 | class PJEServer { 203 | private server: Server; 204 | private pjeClient?: PJEClient; 205 | 206 | constructor() { 207 | this.server = new Server( 208 | { 209 | name: "pje-mcp-server", 210 | version: "1.0.0", 211 | }, 212 | { 213 | capabilities: { 214 | tools: {}, 215 | }, 216 | } 217 | ); 218 | 219 | this.setupToolHandlers(); 220 | } 221 | 222 | private setupToolHandlers() { 223 | this.server.setRequestHandler(ListToolsRequestSchema, async () => { 224 | return { 225 | tools: [ 226 | { 227 | name: "pje_configurar", 228 | description: "Configura a conexão com o PJE", 229 | inputSchema: { 230 | type: "object", 231 | properties: { 232 | baseUrl: { type: "string", description: "URL base da API do PJE" }, 233 | appName: { type: "string", description: "Nome da aplicação" }, 234 | }, 235 | required: ["baseUrl"], 236 | }, 237 | }, 238 | { 239 | name: "pje_listar_processos", 240 | description: "Lista processos com filtros opcionais", 241 | inputSchema: { 242 | type: "object", 243 | properties: { 244 | filter: { type: "string", description: "Filtro para busca" }, 245 | page: { type: "number", description: "Número da página" }, 246 | size: { type: "number", description: "Tamanho da página" }, 247 | }, 248 | }, 249 | }, 250 | { 251 | name: "pje_buscar_processo", 252 | description: "Busca um processo específico por ID", 253 | inputSchema: { 254 | type: "object", 255 | properties: { 256 | id: { type: "string", description: "ID do processo" }, 257 | }, 258 | required: ["id"], 259 | }, 260 | }, 261 | { 262 | name: "pje_listar_orgaos_julgadores", 263 | description: "Lista órgãos julgadores", 264 | inputSchema: { 265 | type: "object", 266 | properties: {}, 267 | }, 268 | }, 269 | { 270 | name: "pje_listar_classes", 271 | description: "Lista classes processuais", 272 | inputSchema: { 273 | type: "object", 274 | properties: {}, 275 | }, 276 | }, 277 | { 278 | name: "pje_listar_assuntos", 279 | description: "Lista assuntos processuais", 280 | inputSchema: { 281 | type: "object", 282 | properties: {}, 283 | }, 284 | }, 285 | { 286 | name: "pje_status", 287 | description: "Verifica o status da configuração e conexão do PJE", 288 | inputSchema: { 289 | type: "object", 290 | properties: {}, 291 | }, 292 | }, 293 | { 294 | name: "pje_configurar_certificado", 295 | description: "Configura autenticação por certificado digital", 296 | inputSchema: { 297 | type: "object", 298 | properties: { 299 | pfxPath: { type: "string", description: "Caminho para arquivo .pfx/.p12" }, 300 | pfxPassword: { type: "string", description: "Senha do arquivo .pfx/.p12" }, 301 | certificateThumbprint: { type: "string", description: "Thumbprint do certificado no Windows" }, 302 | certificateSubject: { type: "string", description: "Subject do certificado no Windows" }, 303 | }, 304 | }, 305 | }, 306 | { 307 | name: "pje_listar_certificados", 308 | description: "Lista certificados digitais disponíveis no Windows", 309 | inputSchema: { 310 | type: "object", 311 | properties: {}, 312 | }, 313 | }, 314 | { 315 | name: "pje_info_certificado", 316 | description: "Mostra informações sobre o certificado configurado", 317 | inputSchema: { 318 | type: "object", 319 | properties: {}, 320 | }, 321 | }, 322 | ], 323 | }; 324 | }); 325 | 326 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => { 327 | try { 328 | switch (request.params.name) { 329 | case "pje_configurar": 330 | return await this.configurarPJE(request.params.arguments); 331 | case "pje_listar_processos": 332 | if (!this.pjeClient) throw new Error("PJE não configurado"); 333 | return await this.listarProcessos(request.params.arguments); 334 | case "pje_buscar_processo": 335 | if (!this.pjeClient) throw new Error("PJE não configurado"); 336 | return await this.buscarProcesso(request.params.arguments); 337 | case "pje_listar_orgaos_julgadores": 338 | if (!this.pjeClient) throw new Error("PJE não configurado"); 339 | return await this.listarOrgaosJulgadores(); 340 | case "pje_listar_classes": 341 | if (!this.pjeClient) throw new Error("PJE não configurado"); 342 | return await this.listarClasses(); 343 | case "pje_listar_assuntos": 344 | if (!this.pjeClient) throw new Error("PJE não configurado"); 345 | return await this.listarAssuntos(); 346 | case "pje_status": 347 | return await this.verificarStatus(); 348 | case "pje_configurar_certificado": 349 | return await this.configurarCertificado(request.params.arguments); 350 | case "pje_listar_certificados": 351 | return await this.listarCertificados(); 352 | case "pje_info_certificado": 353 | return await this.infoCertificado(); 354 | default: 355 | throw new Error(`Ferramenta desconhecida: ${request.params.name}`); 356 | } 357 | } catch (error) { 358 | return { 359 | content: [ 360 | { 361 | type: "text", 362 | text: `Erro: ${error instanceof Error ? error.message : String(error)}`, 363 | }, 364 | ], 365 | }; 366 | } 367 | }); 368 | } 369 | 370 | private async configurarPJE(args: any) { 371 | const config: PJEConfig = { 372 | baseUrl: args.baseUrl || process.env.PJE_BASE_URL || "https://pje.tjce.jus.br", 373 | appName: args.appName || process.env.PJE_APP_NAME || "pje-tjce-1g", 374 | ssoUrl: args.ssoUrl || process.env.PJE_SSO_URL, 375 | clientId: args.clientId || process.env.PJE_CLIENT_ID, 376 | clientSecret: args.clientSecret || process.env.PJE_CLIENT_SECRET, 377 | username: args.username || process.env.PJE_USERNAME, 378 | password: args.password || process.env.PJE_PASSWORD, 379 | }; 380 | 381 | const certificateConfig: CertificateConfig = { 382 | certificateThumbprint: '7db4b6cc9de4785944bcf1c8f3cde03355733b84', 383 | certificatePassword: '123456' 384 | }; 385 | 386 | this.pjeClient = new PJEClient(config); 387 | 388 | if (certificateConfig) { 389 | try { 390 | await this.pjeClient.initializeCertificate(); 391 | try { 392 | await this.pjeClient.authenticateWithCertificate(); 393 | return { 394 | content: [ 395 | { 396 | type: "text", 397 | 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!**`, 398 | }, 399 | ], 400 | }; 401 | } catch (authError) { 402 | return { 403 | content: [ 404 | { 405 | type: "text", 406 | 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)}`, 407 | }, 408 | ], 409 | }; 410 | } 411 | } catch (certError) { 412 | return { 413 | content: [ 414 | { 415 | type: "text", 416 | 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)}`, 417 | }, 418 | ], 419 | }; 420 | } 421 | } 422 | 423 | if (config.ssoUrl && config.clientId && config.clientSecret && config.username && config.password) { 424 | try { 425 | await this.pjeClient.authenticate(); 426 | return { 427 | content: [ 428 | { 429 | type: "text", 430 | 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!**`, 431 | }, 432 | ], 433 | }; 434 | } catch (error) { 435 | return { 436 | content: [ 437 | { 438 | type: "text", 439 | 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)}`, 440 | }, 441 | ], 442 | }; 443 | } 444 | } 445 | 446 | return { 447 | content: [ 448 | { 449 | type: "text", 450 | 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`, 451 | }, 452 | ], 453 | }; 454 | } 455 | 456 | private async listarProcessos(args: any) { 457 | const { filter, page, size } = args; 458 | const result = await this.pjeClient!.listarProcessos(filter, undefined, undefined, page, size); 459 | 460 | return { 461 | content: [ 462 | { 463 | type: "text", 464 | text: `📋 **Processos encontrados:**\n\n${JSON.stringify(result, null, 2)}`, 465 | }, 466 | ], 467 | }; 468 | } 469 | 470 | private async buscarProcesso(args: any) { 471 | const { id } = args; 472 | const result = await this.pjeClient!.buscarProcesso(id); 473 | 474 | return { 475 | content: [ 476 | { 477 | type: "text", 478 | text: `📄 **Detalhes do processo:**\n\n${JSON.stringify(result, null, 2)}`, 479 | }, 480 | ], 481 | }; 482 | } 483 | 484 | private async listarOrgaosJulgadores() { 485 | const result = await this.pjeClient!.listarOrgaosJulgadores(); 486 | 487 | return { 488 | content: [ 489 | { 490 | type: "text", 491 | text: `🏛️ **Órgãos julgadores:**\n\n${JSON.stringify(result, null, 2)}`, 492 | }, 493 | ], 494 | }; 495 | } 496 | 497 | private async listarClasses() { 498 | const result = await this.pjeClient!.listarClasses(); 499 | 500 | return { 501 | content: [ 502 | { 503 | type: "text", 504 | text: `📚 **Classes processuais:**\n\n${JSON.stringify(result, null, 2)}`, 505 | }, 506 | ], 507 | }; 508 | } 509 | 510 | private async listarAssuntos() { 511 | const result = await this.pjeClient!.listarAssuntos(); 512 | 513 | return { 514 | content: [ 515 | { 516 | type: "text", 517 | text: `📑 **Assuntos processuais:**\n\n${JSON.stringify(result, null, 2)}`, 518 | }, 519 | ], 520 | }; 521 | } 522 | 523 | private async verificarStatus() { 524 | const envConfig = { 525 | baseUrl: process.env.PJE_BASE_URL, 526 | appName: process.env.PJE_APP_NAME, 527 | ssoUrl: process.env.PJE_SSO_URL, 528 | clientId: process.env.PJE_CLIENT_ID, 529 | clientSecret: process.env.PJE_CLIENT_SECRET, 530 | username: process.env.PJE_USERNAME ? '***' : undefined, 531 | password: process.env.PJE_PASSWORD ? '***' : undefined, 532 | certificatePath: process.env.PJE_CERTIFICATE_PFX_PATH, 533 | certificatePassword: process.env.PJE_CERTIFICATE_PFX_PASSWORD ? '***' : undefined, 534 | certificateThumbprint: process.env.PJE_CERTIFICATE_THUMBPRINT, 535 | }; 536 | 537 | let statusText = `🔍 **STATUS DO PJE MCP SERVER**\n\n`; 538 | statusText += `🔧 **Configuração do arquivo .env:**\n`; 539 | statusText += `- URL Base: ${envConfig.baseUrl || '❌ Não configurado'}\n`; 540 | statusText += `- App Name: ${envConfig.appName || '❌ Não configurado'}\n`; 541 | statusText += `- SSO URL: ${envConfig.ssoUrl || '❌ Não configurado'}\n`; 542 | statusText += `- Client ID: ${envConfig.clientId || '❌ Não configurado'}\n`; 543 | statusText += `- Client Secret: ${envConfig.clientSecret || '❌ Não configurado'}\n`; 544 | statusText += `- Username: ${envConfig.username || '❌ Não configurado'}\n`; 545 | statusText += `- Password: ${envConfig.password || '❌ Não configurado'}\n\n`; 546 | 547 | statusText += `🔐 **Certificado Digital:**\n`; 548 | statusText += `- Arquivo PFX: ${envConfig.certificatePath || '❌ Não configurado'}\n`; 549 | statusText += `- Senha PFX: ${envConfig.certificatePassword || '❌ Não configurado'}\n`; 550 | statusText += `- Thumbprint: ${envConfig.certificateThumbprint || '❌ Não configurado'}\n\n`; 551 | 552 | statusText += `🔗 **Conexão:**\n`; 553 | if (!this.pjeClient) { 554 | statusText += `- Status: ❌ Cliente não configurado\n`; 555 | statusText += `- Solução: Execute 'pje_configurar' primeiro\n\n`; 556 | } else { 557 | statusText += `- Status: ✅ Cliente configurado\n`; 558 | if ((this.pjeClient as any).accessToken) { 559 | statusText += `- Autenticação: ✅ Token ativo\n`; 560 | } else if ((this.pjeClient as any).certificateManager) { 561 | statusText += `- Autenticação: 🔐 Certificado configurado\n`; 562 | } else { 563 | statusText += `- Autenticação: ⚠️ Apenas consultas públicas\n`; 564 | } 565 | } 566 | 567 | statusText += `\n💡 **Próximos passos:**\n`; 568 | if (!envConfig.baseUrl) { 569 | statusText += `1. Configure PJE_BASE_URL no arquivo .env\n`; 570 | } 571 | if (!envConfig.certificatePath && !envConfig.ssoUrl) { 572 | statusText += `2. Configure certificado digital ou credenciais SSO no arquivo .env\n`; 573 | } 574 | if (!this.pjeClient) { 575 | statusText += `3. Execute: 'Configure o PJE'\n`; 576 | } else { 577 | statusText += `3. ✅ Pronto para usar!\n`; 578 | } 579 | 580 | return { 581 | content: [ 582 | { 583 | type: "text", 584 | text: statusText, 585 | }, 586 | ], 587 | }; 588 | } 589 | 590 | private async configurarCertificado(args: any) { 591 | try { 592 | const thumbprint = args.certificateThumbprint || process.env.PJE_CERTIFICATE_THUMBPRINT; 593 | const password = args.certificatePassword || process.env.PJE_CERTIFICATE_PASSWORD; 594 | if (!thumbprint || !password) { 595 | throw new Error('Thumbprint e senha do certificado são obrigatórios'); 596 | } 597 | const certificateConfig: CertificateConfig = { 598 | certificateThumbprint: thumbprint, 599 | certificatePassword: password 600 | }; 601 | 602 | if (!this.pjeClient) { 603 | const config: PJEConfig = { 604 | baseUrl: process.env.PJE_BASE_URL || "https://pje.tjce.jus.br", 605 | appName: process.env.PJE_APP_NAME || "pje-tjce-1g", 606 | certificate: certificateConfig, 607 | }; 608 | this.pjeClient = new PJEClient(config); 609 | } else { 610 | (this.pjeClient as any).config.certificate = certificateConfig; 611 | (this.pjeClient as any).certificateManager = new CertificateManager(certificateConfig); 612 | } 613 | 614 | await this.pjeClient.initializeCertificate(); 615 | 616 | try { 617 | await this.pjeClient.authenticateWithCertificate(); 618 | return { 619 | content: [ 620 | { 621 | type: "text", 622 | 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)}`, 623 | }, 624 | ], 625 | }; 626 | } catch (authError) { 627 | return { 628 | content: [ 629 | { 630 | type: "text", 631 | 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)}`, 632 | }, 633 | ], 634 | }; 635 | } 636 | } catch (error) { 637 | return { 638 | content: [ 639 | { 640 | type: "text", 641 | 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`, 642 | }, 643 | ], 644 | }; 645 | } 646 | } 647 | 648 | private async listarCertificados() { 649 | try { 650 | const { stdout } = await execAsync('certutil -store My'); 651 | return { 652 | content: [ 653 | { 654 | type: "text", 655 | text: `🔍 **Certificados Digitais Disponíveis no Windows**\n\n${stdout}`, 656 | }, 657 | ], 658 | }; 659 | } catch (error) { 660 | return { 661 | content: [ 662 | { 663 | type: "text", 664 | 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.`, 665 | }, 666 | ], 667 | }; 668 | } 669 | } 670 | 671 | private async infoCertificado() { 672 | try { 673 | if (!this.pjeClient || !(this.pjeClient as any).certificateManager) { 674 | return { 675 | content: [ 676 | { 677 | type: "text", 678 | text: `❌ **Nenhum Certificado Configurado**\n\nExecute 'pje_configurar_certificado' primeiro para carregar um certificado digital.`, 679 | }, 680 | ], 681 | }; 682 | } 683 | 684 | const info = this.pjeClient.getCertificateInfo(); 685 | 686 | let texto = `🎯 **Informações do Certificado Digital Atual**\n\n`; 687 | 688 | texto += `**Subject:**\n`; 689 | info.subject.forEach((attr: any) => { 690 | texto += `- ${attr.name}: ${attr.value}\n`; 691 | }); 692 | 693 | texto += `\n**Emissor:**\n`; 694 | info.issuer.forEach((attr: any) => { 695 | texto += `- ${attr.name}: ${attr.value}\n`; 696 | }); 697 | 698 | texto += `\n**Detalhes Técnicos:**\n`; 699 | texto += `- Serial Number: ${info.serialNumber}\n`; 700 | texto += `- Thumbprint: ${info.thumbprint}\n`; 701 | texto += `- Válido de: ${new Date(info.notBefore).toLocaleString('pt-BR')}\n`; 702 | texto += `- Válido até: ${new Date(info.notAfter).toLocaleString('pt-BR')}\n`; 703 | 704 | const agora = new Date(); 705 | const validade = new Date(info.notAfter); 706 | 707 | if (agora > validade) { 708 | texto += `\n⚠️ **AVISO: Certificado EXPIRADO!**\n`; 709 | } else { 710 | const diasRestantes = Math.floor((validade.getTime() - agora.getTime()) / (1000 * 60 * 60 * 24)); 711 | texto += `\n✅ **Certificado válido por mais ${diasRestantes} dias**\n`; 712 | } 713 | 714 | return { 715 | content: [ 716 | { 717 | type: "text", 718 | text: texto, 719 | }, 720 | ], 721 | }; 722 | } catch (error) { 723 | return { 724 | content: [ 725 | { 726 | type: "text", 727 | text: `❌ **Erro ao Obter Informações do Certificado**\n\n${error instanceof Error ? error.message : String(error)}`, 728 | }, 729 | ], 730 | }; 731 | } 732 | } 733 | 734 | async run() { 735 | const transport = new StdioServerTransport(); 736 | await this.server.connect(transport); 737 | console.error("Servidor MCP do PJE iniciado"); 738 | } 739 | } 740 | 741 | const server = new PJEServer(); 742 | server.run().catch(console.error); ```