Skip to main content
Glama
derikfernandes

BCB Payment Methods MCP Server

http-server.ts24 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; import axios from "axios"; import express from "express"; import cors from "cors"; const API_BASE_URL = "https://olinda.bcb.gov.br/olinda/servico/MPV_DadosAbertos/versao/v1/odata"; // Interface para parâmetros de consulta interface QueryParams { formato?: string; top?: number; skip?: number; filter?: string; orderby?: string; } // Função auxiliar para construir URL com parâmetros function buildUrl(endpoint: string, params: QueryParams = {}): string { const url = new URL(`${API_BASE_URL}/${endpoint}`); if (params.formato) url.searchParams.append("$format", params.formato); if (params.top) url.searchParams.append("$top", params.top.toString()); if (params.skip) url.searchParams.append("$skip", params.skip.toString()); if (params.filter) url.searchParams.append("$filter", params.filter); if (params.orderby) url.searchParams.append("$orderby", params.orderby); return url.toString(); } // Função auxiliar para fazer requisições à API async function fetchBCBData(endpoint: string, params: QueryParams = {}) { try { const url = buildUrl(endpoint, params); const response = await axios.get(url, { headers: { "Accept": "application/json" } }); return response.data; } catch (error) { if (axios.isAxiosError(error)) { throw new Error(`Erro ao consultar API do BCB: ${error.message}`); } throw error; } } // Definição das ferramentas com metadados para ChatGPT const tools: Tool[] = [ { name: "consultar_meios_pagamento_mensal", description: "Consulta dados mensais sobre meios de pagamento, incluindo operações com boletos bancários, PIX, TED, DOC e outros. Use o formato YYYYMM para o parâmetro ano_mes (exemplo: '202312' para dezembro de 2023).", inputSchema: { type: "object", properties: { ano_mes: { type: "string", description: "Ano e mês no formato YYYYMM (exemplo: '202312')", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, skip: { type: "number", description: "Número de registros a pular para paginação", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta (exemplo: \"Modalidade eq 'PIX'\")", }, }, required: ["ano_mes"], }, }, { name: "consultar_meios_pagamento_trimestral", description: "Consulta dados trimestrais sobre operações com cartões de pagamento e transferências de crédito. Use o formato YYYYQ para o parâmetro trimestre (exemplo: '20234' para o 4º trimestre de 2023).", inputSchema: { type: "object", properties: { trimestre: { type: "string", description: "Ano e trimestre no formato YYYYQ (exemplo: '20234' para 4º trimestre de 2023)", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, skip: { type: "number", description: "Número de registros a pular para paginação", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta", }, }, required: ["trimestre"], }, }, { name: "consultar_transacoes_cartoes", description: "Consulta estoque e transações de cartões de pagamento por trimestre. Retorna dados sobre quantidade e valor das transações realizadas com cartões.", inputSchema: { type: "object", properties: { trimestre: { type: "string", description: "Ano e trimestre no formato YYYYQ (exemplo: '20234')", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, ordenar_por: { type: "string", description: "Campo para ordenação (exemplo: 'Trimestre desc')", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta", }, }, required: ["trimestre"], }, }, { name: "consultar_estabelecimentos_credenciados", description: "Consulta quantidade de estabelecimentos credenciados para aceitar meios de pagamento eletrônico por trimestre.", inputSchema: { type: "object", properties: { trimestre: { type: "string", description: "Ano e trimestre no formato YYYYQ (exemplo: '20234')", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, ordenar_por: { type: "string", description: "Campo para ordenação", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta", }, }, required: ["trimestre"], }, }, { name: "consultar_taxas_intercambio", description: "Consulta taxas de intercâmbio praticadas no mercado de meios de pagamento por trimestre.", inputSchema: { type: "object", properties: { trimestre: { type: "string", description: "Ano e trimestre no formato YYYYQ (exemplo: '20234')", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta", }, }, required: ["trimestre"], }, }, { name: "consultar_taxas_desconto", description: "Consulta taxas de desconto cobradas de estabelecimentos comerciais por operações com meios de pagamento.", inputSchema: { type: "object", properties: { trimestre: { type: "string", description: "Ano e trimestre no formato YYYYQ (exemplo: '20234')", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta", }, }, required: ["trimestre"], }, }, { name: "consultar_terminais_atm", description: "Consulta estatísticas sobre terminais de autoatendimento (ATM/caixas eletrônicos) por trimestre.", inputSchema: { type: "object", properties: { trimestre: { type: "string", description: "Ano e trimestre no formato YYYYQ (exemplo: '20234')", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta", }, }, required: ["trimestre"], }, }, { name: "consultar_portadores_cartao", description: "Consulta informações sobre portadores de cartões de pagamento por trimestre.", inputSchema: { type: "object", properties: { trimestre: { type: "string", description: "Ano e trimestre no formato YYYYQ (exemplo: '20234')", }, top: { type: "number", description: "Número máximo de registros a retornar (padrão: 100)", }, filtro: { type: "string", description: "Filtro OData para refinar a consulta", }, }, required: ["trimestre"], }, }, ]; // Criar servidor MCP function createMCPServer() { const server = new Server( { name: "bcb-meios-pagamento-mcp", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // Handler para listagem de ferramentas server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools, }; }); // Handler para execução de ferramentas server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "consultar_meios_pagamento_mensal": { const { ano_mes, top = 100, skip, filtro } = args as { ano_mes: string; top?: number; skip?: number; filtro?: string; }; const data = await fetchBCBData(`MeiosdePagamentosMensalDA(AnoMes=@AnoMes)?@AnoMes='${ano_mes}'`, { formato: "json", top, skip, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_meios_pagamento_trimestral": { const { trimestre, top = 100, skip, filtro } = args as { trimestre: string; top?: number; skip?: number; filtro?: string; }; const data = await fetchBCBData(`MeiosdePagamentosTrimestralDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, skip, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_transacoes_cartoes": { const { trimestre, top = 100, ordenar_por, filtro } = args as { trimestre: string; top?: number; ordenar_por?: string; filtro?: string; }; const data = await fetchBCBData(`Quantidadeetransacoesdecartoes(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, orderby: ordenar_por, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_estabelecimentos_credenciados": { const { trimestre, top = 100, ordenar_por, filtro } = args as { trimestre: string; top?: number; ordenar_por?: string; filtro?: string; }; const data = await fetchBCBData(`EstabCredTransDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, orderby: ordenar_por, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_taxas_intercambio": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; const data = await fetchBCBData(`TaxasIntercambioDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_taxas_desconto": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; const data = await fetchBCBData(`TaxasDescontoDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_terminais_atm": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; const data = await fetchBCBData(`TerminaisATMDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_portadores_cartao": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; const data = await fetchBCBData(`PortadoresCartaoDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } default: throw new Error(`Ferramenta desconhecida: ${name}`); } } catch (error) { return { content: [ { type: "text", text: `Erro ao executar ${name}: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); return server; } // Função auxiliar para listar tools (usada em endpoints REST) async function listTools() { return { tools }; } // Função auxiliar para chamar uma tool (usada em endpoints REST) async function callTool(name: string, args: any) { try { switch (name) { case "consultar_meios_pagamento_mensal": { const { ano_mes, top = 100, skip, filtro } = args as { ano_mes: string; top?: number; skip?: number; filtro?: string; }; if (!ano_mes) { throw new Error("Parâmetro 'ano_mes' é obrigatório"); } const data = await fetchBCBData(`MeiosdePagamentosMensalDA(AnoMes=@AnoMes)?@AnoMes='${ano_mes}'`, { formato: "json", top, skip, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_meios_pagamento_trimestral": { const { trimestre, top = 100, skip, filtro } = args as { trimestre: string; top?: number; skip?: number; filtro?: string; }; if (!trimestre) { throw new Error("Parâmetro 'trimestre' é obrigatório"); } const data = await fetchBCBData(`MeiosdePagamentosTrimestralDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, skip, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_transacoes_cartoes": { const { trimestre, top = 100, ordenar_por, filtro } = args as { trimestre: string; top?: number; ordenar_por?: string; filtro?: string; }; if (!trimestre) { throw new Error("Parâmetro 'trimestre' é obrigatório"); } const data = await fetchBCBData(`Quantidadeetransacoesdecartoes(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, orderby: ordenar_por, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_estabelecimentos_credenciados": { const { trimestre, top = 100, ordenar_por, filtro } = args as { trimestre: string; top?: number; ordenar_por?: string; filtro?: string; }; if (!trimestre) { throw new Error("Parâmetro 'trimestre' é obrigatório"); } const data = await fetchBCBData(`EstabCredTransDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, orderby: ordenar_por, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_taxas_intercambio": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; if (!trimestre) { throw new Error("Parâmetro 'trimestre' é obrigatório"); } const data = await fetchBCBData(`TaxasIntercambioDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_taxas_desconto": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; if (!trimestre) { throw new Error("Parâmetro 'trimestre' é obrigatório"); } const data = await fetchBCBData(`TaxasDescontoDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_terminais_atm": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; if (!trimestre) { throw new Error("Parâmetro 'trimestre' é obrigatório"); } const data = await fetchBCBData(`TerminaisATMDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } case "consultar_portadores_cartao": { const { trimestre, top = 100, filtro } = args as { trimestre: string; top?: number; filtro?: string; }; if (!trimestre) { throw new Error("Parâmetro 'trimestre' é obrigatório"); } const data = await fetchBCBData(`PortadoresCartaoDA(trimestre=@trimestre)?@trimestre='${trimestre}'`, { formato: "json", top, filter: filtro, }); return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } default: throw new Error(`Ferramenta desconhecida: ${name}`); } } catch (error) { return { content: [ { type: "text", text: `Erro ao executar ${name}: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } // Iniciar servidor HTTP com SSE async function main() { const app = express(); const PORT = process.env.PORT || 3000; // Middleware app.use(cors({ origin: '*', methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], })); app.use(express.json()); // Handler para OPTIONS (preflight) app.options('*', (req, res) => { res.sendStatus(200); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'ok', service: 'bcb-meios-pagamento-mcp' }); }); // Endpoint raiz com informações do servidor app.get('/', (req, res) => { res.json({ name: 'bcb-meios-pagamento-mcp', version: '1.0.0', protocol: 'mcp', description: 'Servidor MCP para API de Dados Abertos de Meios de Pagamento do Banco Central do Brasil', endpoints: { tools: '/tools', callTool: '/tools/call', sse: '/sse', health: '/health', info: '/mcp/info' }, capabilities: { tools: {}, }, }); }); // Endpoint para informações do servidor MCP app.get('/mcp/info', (req, res) => { res.json({ name: 'bcb-meios-pagamento-mcp', version: '1.0.0', protocol: 'mcp', capabilities: { tools: {}, }, }); }); // Endpoint REST para listar tools (compatível com OpenAI MCP) app.get('/tools', async (req, res) => { try { const response = await listTools(); // Garantir que o formato está correto conforme especificação MCP res.setHeader('Content-Type', 'application/json'); res.setHeader('X-MCP-Server', 'bcb-meios-pagamento-mcp'); res.setHeader('X-MCP-Version', '1.0.0'); res.json(response); } catch (error) { console.error('Erro ao listar tools:', error); res.status(500).json({ error: 'Erro ao listar tools', message: error instanceof Error ? error.message : String(error) }); } }); // Endpoint REST para chamar uma tool (compatível com OpenAI MCP) app.post('/tools/call', async (req, res) => { try { const { name, arguments: args } = req.body; if (!name) { return res.status(400).json({ error: 'Nome da tool é obrigatório', isError: true }); } const response = await callTool(name, args || {}); // Garantir headers corretos res.setHeader('Content-Type', 'application/json'); res.setHeader('X-MCP-Server', 'bcb-meios-pagamento-mcp'); res.json(response); } catch (error) { console.error('Erro ao chamar tool:', error); res.status(500).json({ error: 'Erro ao executar tool', message: error instanceof Error ? error.message : String(error), isError: true }); } }); // SSE endpoint para MCP (compatível com clientes MCP padrão) app.get('/sse', async (req, res) => { console.error('Nova conexão SSE estabelecida'); const server = createMCPServer(); const transport = new SSEServerTransport('/message', res); await server.connect(transport); // Cleanup quando a conexão fechar req.on('close', () => { console.error('Conexão SSE fechada'); }); }); // Endpoint para mensagens MCP app.post('/message', async (req, res) => { // Este endpoint é usado pelo SSEServerTransport para receber mensagens res.status(405).json({ error: 'Use SSE endpoint' }); }); app.listen(PORT, () => { console.error(`Servidor MCP BCB rodando na porta ${PORT}`); console.error(`SSE endpoint: http://localhost:${PORT}/sse`); console.error(`Tools endpoint: http://localhost:${PORT}/tools`); console.error(`Call tool endpoint: http://localhost:${PORT}/tools/call`); }); } main();

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/derikfernandes/bcb-meios-pagamento-mcp_2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server