feat(mcp): add MCP Server Integration — Property Search, Analytics, Valuation
Implement 3 MCP servers in libs/mcp-servers/ using @modelcontextprotocol/sdk: - Property Search: NL search via Typesense, property comparison, detail lookup - Market Analytics: market reports, price trends, district comparison - Valuation: AVM integration with Python AI service, feature extraction, batch valuation Includes NestJS integration module with SSE transport for in-process hosting. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
63
libs/mcp-servers/src/nestjs/mcp-registry.service.ts
Normal file
63
libs/mcp-servers/src/nestjs/mcp-registry.service.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Injectable, Inject, type OnModuleInit } from '@nestjs/common';
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { MCP_MODULE_OPTIONS, type McpModuleOptions } from './mcp.module';
|
||||
|
||||
@Injectable()
|
||||
export class McpRegistryService implements OnModuleInit {
|
||||
private readonly servers = new Map<string, McpServer>();
|
||||
private typesenseClient: import('typesense').Client | null = null;
|
||||
|
||||
constructor(
|
||||
@Inject(MCP_MODULE_OPTIONS) private readonly options: McpModuleOptions,
|
||||
) {}
|
||||
|
||||
async onModuleInit(): Promise<void> {
|
||||
// Lazy import to avoid hard dependency at module load time
|
||||
const { createPropertySearchServer } = await import('../property-search/property-search.server');
|
||||
const { createMarketAnalyticsServer } = await import('../market-analytics/market-analytics.server');
|
||||
const { createValuationServer } = await import('../valuation/valuation.server');
|
||||
|
||||
// Typesense client is injected from the host app via setTypesenseClient
|
||||
// If not set by the time servers are needed, tools that require it will fail gracefully
|
||||
if (this.typesenseClient) {
|
||||
this.servers.set(
|
||||
'property-search',
|
||||
createPropertySearchServer({
|
||||
typesenseClient: this.typesenseClient,
|
||||
collectionName: this.options.typesenseCollectionName,
|
||||
}),
|
||||
);
|
||||
|
||||
this.servers.set(
|
||||
'market-analytics',
|
||||
createMarketAnalyticsServer({
|
||||
typesenseClient: this.typesenseClient,
|
||||
collectionName: this.options.typesenseCollectionName,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.servers.set(
|
||||
'valuation',
|
||||
createValuationServer({
|
||||
aiServiceBaseUrl: this.options.aiServiceBaseUrl,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
setTypesenseClient(client: import('typesense').Client): void {
|
||||
this.typesenseClient = client;
|
||||
}
|
||||
|
||||
getServer(name: string): McpServer | undefined {
|
||||
return this.servers.get(name);
|
||||
}
|
||||
|
||||
getServerNames(): string[] {
|
||||
return Array.from(this.servers.keys());
|
||||
}
|
||||
|
||||
getAllServers(): Map<string, McpServer> {
|
||||
return this.servers;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user