#
tokens: 23849/50000 17/17 files
lines: on (toggle) GitHub
raw markdown copy reset
# 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); 
```