Aprenda como expor dados através de recursos MCP, manipular diferentes tipos de dados e construir sistemas de recursos dinâmicos
Recursos são a base do acesso a dados no MCP. Eles representam qualquer dado que uma aplicação de IA possa precisar ler - desde arquivos de configuração simples até consultas complexas de banco de dados. Pense em recursos como uma API universal para expor informações.
Um recurso bem projetado deve ser:
<CodeExample title="Estrutura de Recurso" language="typescript" code={`interface Resource { uri: string; // Identificador único (ex: "file:///caminho/para/arquivo") name: string; // Nome legível por humanos description?: string; // Descrição opcional mimeType?: string; // Tipo de conteúdo (padrão: text/plain) }
interface ResourceContent { uri: string; mimeType: string; text?: string; // Conteúdo de texto blob?: string; // Binário codificado em Base64 }`} highlightLines={[2, 9, 10, 11]} />
Entender o ciclo de vida do recurso ajuda você a construir provedores robustos:
resources/list
resources/read
com URI específicoMelhor Prática: Projete seu esquema URI para ser hierárquico e significativo. Use prefixos como config://
, db://
ou file://
para categorizar recursos.
A anatomia de um recurso MCP
Recursos estáticos são o tipo mais simples - eles retornam conteúdo fixo que não muda com frequência. São perfeitos para configuração, metadados e informações do sistema.
<CodeExample title="Implementação de Recurso Estático" language="typescript" code={`// Definir recursos estáticos const staticResources = [ { uri: 'config://app/settings', name: 'Configurações da Aplicação', description: 'Configuração atual da aplicação', mimeType: 'application/json' }, { uri: 'config://app/version', name: 'Informações de Versão', description: 'Versão da aplicação e informações de build', mimeType: 'application/json' } ];
// Implementar manipulador de listagem server.setRequestHandler('resources/list', async () => { return { resources: staticResources }; });
// Implementar manipulador de leitura server.setRequestHandler('resources/read', async (request) => { const { uri } = request.params;
switch (uri) { case 'config://app/settings': return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify({ theme: 'dark', language: 'pt-BR', debugMode: false }, null, 2) }] };
case 'config://app/version':
return {
contents: [{
uri,
mimeType: 'application/json',
text: JSON.stringify({
version: '1.0.0',
buildDate: new Date().toISOString(),
nodeVersion: process.version
}, null, 2)
}]
};
default:
throw new Error(\`Recurso não encontrado: \${uri}\`);
} });`} />
Conforme sua aplicação cresce, organize recursos estáticos logicamente:
Dica Profissional: Use padrões de nomenclatura consistentes. Por exemplo, prefixe toda configuração com config://
e toda informação do sistema com system://
.
Expor configuração e metadados como recursos
Module content not available.
À medida que seus servidores MCP se tornam mais sofisticados, você precisará de padrões avançados para lidar com grandes conjuntos de dados e requisitos complexos.
Quando você tem muitos recursos, a paginação evita sobrecarregar os clientes:
<CodeExample title="Listas de Recursos Paginadas" language="typescript" code={`interface PaginatedListRequest { params: { cursor?: string; limit?: number; }; }
server.setRequestHandler('resources/list', async (request: PaginatedListRequest) => { const { cursor, limit = 100 } = request.params;
// Obter todos os recursos (na prática, do banco de dados com LIMIT/OFFSET) const allResources = await getAllResources();
// Encontrar posição inicial const startIndex = cursor ? allResources.findIndex(r => r.uri === cursor) + 1 : 0;
// Obter página de recursos const pageResources = allResources.slice(startIndex, startIndex + limit);
// Preparar resposta com nextCursor const response = { resources: pageResources };
// Adicionar próximo cursor se houver mais recursos if (startIndex + limit < allResources.length) { response.nextCursor = pageResources[pageResources.length - 1].uri; }
return response; });`} highlightLines={[8, 9, 15, 16, 19, 26, 27, 28]} />
Permita que clientes filtrem recursos por tipo, padrão ou atributos:
<CodeExample title="Listas de Recursos Filtradas" language="typescript" code={`interface FilteredListRequest { params: { filter?: { mimeType?: string; uriPattern?: string; tags?: string[]; }; }; }
server.setRequestHandler('resources/list', async (request: FilteredListRequest) => { const { filter } = request.params; let resources = await getAllResources();
if (filter) { // Filtrar por tipo MIME if (filter.mimeType) { resources = resources.filter(r => r.mimeType === filter.mimeType); }
// Filtrar por padrão URI
if (filter.uriPattern) {
const pattern = new RegExp(filter.uriPattern);
resources = resources.filter(r => pattern.test(r.uri));
}
// Filtrar por tags (se seus recursos tiverem tags)
if (filter.tags && filter.tags.length > 0) {
resources = resources.filter(r =>
r.tags && filter.tags.some(tag => r.tags.includes(tag))
);
}
}
return { resources }; });`} />
Para operações caras, implemente cache inteligente:
<CodeExample title="Estratégia de Cache de Recursos" language="typescript" code={`class CachedResourceProvider { private cache = new Map<string, { content: any; timestamp: number }>(); private cacheTimeout = 5 * 60 * 1000; // 5 minutos
async readResource(uri: string) { // Verificar cache primeiro const cached = this.cache.get(uri); if (cached && Date.now() - cached.timestamp < this.cacheTimeout) { return cached.content; }
// Gerar conteúdo novo
const content = await this.generateContent(uri);
// Armazenar em cache o resultado
this.cache.set(uri, {
content,
timestamp: Date.now()
});
return content;
}
// Invalidar cache quando recursos mudam invalidateCache(uri?: string) { if (uri) { this.cache.delete(uri); } else { this.cache.clear(); } } }`} />
Manipular grandes coleções de recursos com paginação
Suportar filtragem de recursos por tipo ou atributos
Para aplicações em tempo real, implemente assinaturas de recursos para notificar clientes quando dados mudam.
<CodeExample title="Notificações de Atualização de Recursos" language="typescript" code={`import { EventEmitter } from 'events';
class ResourceManager extends EventEmitter { private resources = new Map<string, Resource>();
updateResource(uri: string, updates: Partial<Resource>) { const existing = this.resources.get(uri); if (!existing) throw new Error('Recurso não encontrado');
const updated = { ...existing, ...updates };
this.resources.set(uri, updated);
// Emitir evento de atualização
this.emit('resource:updated', {
uri,
resource: updated
});
}
deleteResource(uri: string) { if (!this.resources.delete(uri)) { throw new Error('Recurso não encontrado'); }
// Emitir evento de exclusão
this.emit('resource:deleted', { uri });
} }
// Na configuração do seu servidor const resourceManager = new ResourceManager();
// Assinar mudanças resourceManager.on('resource:updated', ({ uri, resource }) => { // Enviar notificação para clientes conectados server.sendNotification('resources/updated', { uri, resource }); });
resourceManager.on('resource:deleted', ({ uri }) => { server.sendNotification('resources/deleted', { uri }); });`} />
Monitore mudanças no sistema de arquivos para atualizações automáticas:
<CodeExample title="Monitoramento do Sistema de Arquivos" language="typescript" code={`import { watch } from 'fs';
class WatchedFileProvider { private watchers = new Map<string, any>();
constructor(private basePath: string, private server: Server) { this.setupWatcher(); }
private setupWatcher() { const watcher = watch(this.basePath, { recursive: true }, (eventType, filename) => { if (!filename) return;
const fullPath = path.join(this.basePath, filename);
const uri = \`file://\${fullPath}\`;
if (eventType === 'change') {
// Arquivo modificado
this.server.sendNotification('resources/updated', { uri });
} else if (eventType === 'rename') {
// Arquivo criado ou deletado
this.checkFileExists(fullPath).then(exists => {
if (exists) {
this.server.sendNotification('resources/created', { uri });
} else {
this.server.sendNotification('resources/deleted', { uri });
}
});
}
});
this.watchers.set(this.basePath, watcher);
}
private async checkFileExists(path: string): Promise<boolean> { try { await fs.access(path); return true; } catch { return false; } }
cleanup() { this.watchers.forEach(watcher => watcher.close()); this.watchers.clear(); } }`} />
Notificar clientes quando recursos mudam
Verifique sua compreensão sobre provedores de recursos MCP
1. Qual é o propósito principal dos recursos MCP?
2. Quais campos são obrigatórios para um recurso?
3. Recursos podem retornar apenas conteúdo de texto, não dados binários.
True or False question
Correct Answer: A
Falso! Recursos podem retornar dados binários usando o campo 'blob' com codificação base64.
4. Complete o manipulador de leitura de recursos para retornar conteúdo JSON: