3gpp-mcp-server
Version:
MCP Server for querying 3GPP telecom protocol specifications
441 lines (430 loc) • 18.7 kB
JavaScript
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const zod_1 = require("zod");
const specification_manager_js_1 = require("./utils/specification-manager.js");
const search_engine_js_1 = require("./utils/search-engine.js");
const document_processor_js_1 = require("./utils/document-processor.js");
const SearchQuerySchema = zod_1.z.object({
query: zod_1.z.string().min(1),
series: zod_1.z.array(zod_1.z.string()).optional(),
releases: zod_1.z.array(zod_1.z.string()).optional(),
limit: zod_1.z.number().min(1).max(50).default(10),
offset: zod_1.z.number().min(0).default(0)
});
const SpecificationLookupSchema = zod_1.z.object({
specId: zod_1.z.string().min(1),
format: zod_1.z.enum(['summary', 'detailed', 'raw']).default('summary')
});
const ProtocolQuerySchema = zod_1.z.object({
protocolName: zod_1.z.string().min(1),
queryType: zod_1.z.enum(['procedures', 'messages', 'overview']).default('overview')
});
class GPPMCPServer {
server;
specManager;
searchEngine;
documentProcessor;
constructor() {
this.server = new index_js_1.Server({
name: '3gpp-mcp-server',
version: '1.0.0',
}, {
capabilities: {
tools: {},
resources: {},
prompts: {},
},
});
this.specManager = new specification_manager_js_1.GPPSpecificationManager();
this.searchEngine = new search_engine_js_1.GPPSearchEngine();
this.documentProcessor = new document_processor_js_1.GPPDocumentProcessor();
this.setupHandlers();
}
setupHandlers() {
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'search_3gpp_specs',
description: 'Search through 3GPP specification documents using semantic search',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query text' },
series: {
type: 'array',
items: { type: 'string' },
description: 'Filter by specification series (e.g., ["24", "25"])',
},
releases: {
type: 'array',
items: { type: 'string' },
description: 'Filter by 3GPP releases (e.g., ["Rel-15", "Rel-16"])',
},
limit: { type: 'number', description: 'Maximum number of results (1-50)', default: 10 },
offset: { type: 'number', description: 'Offset for pagination', default: 0 }
},
required: ['query']
}
},
{
name: 'get_specification_details',
description: 'Get detailed information about a specific 3GPP specification',
inputSchema: {
type: 'object',
properties: {
specId: { type: 'string', description: 'Specification ID (e.g., "TS 24.301")' },
format: {
type: 'string',
enum: ['summary', 'detailed', 'raw'],
description: 'Level of detail to return',
default: 'summary'
}
},
required: ['specId']
}
},
{
name: 'query_protocol_info',
description: 'Get information about specific 3GPP protocols and procedures',
inputSchema: {
type: 'object',
properties: {
protocolName: { type: 'string', description: 'Name of the protocol (e.g., "NAS", "RRC")' },
queryType: {
type: 'string',
enum: ['procedures', 'messages', 'overview'],
description: 'Type of information to retrieve',
default: 'overview'
}
},
required: ['protocolName']
}
},
{
name: 'find_related_specs',
description: 'Find specifications related to a given topic or specification',
inputSchema: {
type: 'object',
properties: {
topic: { type: 'string', description: 'Topic or specification to find related documents for' },
maxResults: { type: 'number', description: 'Maximum number of related specs to return', default: 5 }
},
required: ['topic']
}
}
]
};
});
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'search_3gpp_specs':
return await this.handleSearchSpecs(args);
case 'get_specification_details':
return await this.handleGetSpecDetails(args);
case 'query_protocol_info':
return await this.handleQueryProtocol(args);
case 'find_related_specs':
return await this.handleFindRelatedSpecs(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`
}
]
};
}
});
this.server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: '3gpp://specifications/catalog',
name: '3GPP Specifications Catalog',
description: 'Complete catalog of available 3GPP specifications'
},
{
uri: '3gpp://protocols/list',
name: '3GPP Protocols List',
description: 'List of all 3GPP protocols with descriptions'
},
{
uri: '3gpp://releases/timeline',
name: '3GPP Releases Timeline',
description: 'Timeline of 3GPP releases and their major features'
}
]
};
});
this.server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
switch (uri) {
case '3gpp://specifications/catalog':
return {
contents: [
{
uri,
mimeType: 'application/json',
text: await this.generateSpecificationsCatalog()
}
]
};
case '3gpp://protocols/list':
return {
contents: [
{
uri,
mimeType: 'application/json',
text: await this.generateProtocolsList()
}
]
};
case '3gpp://releases/timeline':
return {
contents: [
{
uri,
mimeType: 'application/json',
text: await this.generateReleasesTimeline()
}
]
};
default:
throw new Error(`Unknown resource: ${uri}`);
}
});
this.server.setRequestHandler(types_js_1.ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: 'explain_3gpp_procedure',
description: 'Explain a specific 3GPP procedure in detail',
arguments: [
{
name: 'procedure_name',
description: 'Name of the 3GPP procedure to explain',
required: true
},
{
name: 'detail_level',
description: 'Level of detail (basic, intermediate, expert)',
required: false
}
]
},
{
name: 'compare_specifications',
description: 'Compare two 3GPP specifications highlighting differences',
arguments: [
{
name: 'spec1',
description: 'First specification to compare',
required: true
},
{
name: 'spec2',
description: 'Second specification to compare',
required: true
}
]
}
]
};
});
this.server.setRequestHandler(types_js_1.GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'explain_3gpp_procedure':
return {
description: 'Explain a specific 3GPP procedure in detail',
messages: [
{
role: 'user',
content: {
type: 'text',
text: await this.generateProcedureExplanationPrompt(args?.procedure_name, args?.detail_level)
}
}
]
};
case 'compare_specifications':
return {
description: 'Compare two 3GPP specifications',
messages: [
{
role: 'user',
content: {
type: 'text',
text: await this.generateSpecComparisonPrompt(args?.spec1, args?.spec2)
}
}
]
};
default:
throw new Error(`Unknown prompt: ${name}`);
}
});
}
async handleSearchSpecs(args) {
const params = SearchQuerySchema.parse(args);
const results = await this.searchEngine.search(params);
const resultText = results.map(result => {
const spec = result.specification;
return `**${spec.id}** - ${spec.title}
Series: ${spec.series} | Release: ${spec.release} | Version: ${spec.version}
Relevance: ${(result.relevanceScore * 100).toFixed(1)}%
${result.matchedContent ? `\nMatched content: "${result.matchedContent}"` : ''}
${result.contextSnippets?.length ? `\nContext: ${result.contextSnippets.join(' ... ')}` : ''}
---`;
}).join('\n\n');
return {
content: [
{
type: 'text',
text: `Found ${results.length} specifications matching "${params.query}":\n\n${resultText}`
}
]
};
}
async handleGetSpecDetails(args) {
const params = SpecificationLookupSchema.parse(args);
const spec = await this.specManager.getSpecification(params.specId);
if (!spec) {
throw new Error(`Specification ${params.specId} not found`);
}
let content = '';
switch (params.format) {
case 'summary':
content = `# ${spec.title} (${spec.id})
**Series:** ${spec.series}
**Release:** ${spec.release}
**Version:** ${spec.version}
**Size:** ${Math.round(spec.size / 1024)} KB
**Last Modified:** ${spec.lastModified.toISOString().split('T')[0]}
${spec.metadata?.abstract || 'No abstract available'}
${spec.metadata?.keywords?.length ? `**Keywords:** ${spec.metadata.keywords.join(', ')}` : ''}
`;
break;
case 'detailed':
content = await this.documentProcessor.getDetailedSummary(spec.filePath);
break;
case 'raw':
content = await this.documentProcessor.getRawContent(spec.filePath);
break;
}
return {
content: [
{
type: 'text',
text: content
}
]
};
}
async handleQueryProtocol(args) {
const params = ProtocolQuerySchema.parse(args);
const protocolInfo = await this.searchEngine.queryProtocol(params.protocolName, params.queryType);
return {
content: [
{
type: 'text',
text: protocolInfo
}
]
};
}
async handleFindRelatedSpecs(args) {
const topic = args.topic;
const maxResults = args.maxResults || 5;
const relatedSpecs = await this.searchEngine.findRelatedSpecifications(topic, maxResults);
const resultText = relatedSpecs.map(spec => `**${spec.id}** - ${spec.title}\nSeries: ${spec.series} | Release: ${spec.release}`).join('\n\n');
return {
content: [
{
type: 'text',
text: `Related specifications for "${topic}":\n\n${resultText}`
}
]
};
}
async generateSpecificationsCatalog() {
const catalog = await this.specManager.getCatalog();
return JSON.stringify(catalog, null, 2);
}
async generateProtocolsList() {
const protocols = await this.specManager.getProtocolsList();
return JSON.stringify(protocols, null, 2);
}
async generateReleasesTimeline() {
const timeline = await this.specManager.getReleasesTimeline();
return JSON.stringify(timeline, null, 2);
}
async generateProcedureExplanationPrompt(procedureName, detailLevel = 'intermediate') {
if (!procedureName) {
throw new Error('Procedure name is required');
}
const procedureInfo = await this.searchEngine.queryProtocol(procedureName, 'procedures');
return `Please explain the 3GPP procedure "${procedureName}" at a ${detailLevel} level of detail.
Based on the following information from the 3GPP specifications:
${procedureInfo}
Please provide:
1. Overview of the procedure's purpose
2. Step-by-step breakdown of the procedure
3. Key messages and parameters involved
4. Common use cases and scenarios
5. Related specifications and dependencies
Format your response in a clear, structured manner appropriate for someone with ${detailLevel} knowledge of 3GPP protocols.`;
}
async generateSpecComparisonPrompt(spec1, spec2) {
if (!spec1 || !spec2) {
throw new Error('Both specifications are required for comparison');
}
const spec1Info = await this.specManager.getSpecification(spec1);
const spec2Info = await this.specManager.getSpecification(spec2);
if (!spec1Info || !spec2Info) {
throw new Error('One or both specifications not found');
}
return `Please compare the following 3GPP specifications:
**Specification 1: ${spec1Info.title} (${spec1Info.id})**
- Series: ${spec1Info.series}
- Release: ${spec1Info.release}
- Version: ${spec1Info.version}
${spec1Info.metadata?.abstract ? `- Abstract: ${spec1Info.metadata.abstract}` : ''}
**Specification 2: ${spec2Info.title} (${spec2Info.id})**
- Series: ${spec2Info.series}
- Release: ${spec2Info.release}
- Version: ${spec2Info.version}
${spec2Info.metadata?.abstract ? `- Abstract: ${spec2Info.metadata.abstract}` : ''}
Please analyze and compare these specifications focusing on:
1. Purpose and scope differences
2. Technical approach variations
3. Protocol differences (if applicable)
4. Release evolution changes
5. Dependencies and relationships
6. Use case scenarios
Highlight the key differences and similarities in a structured format.`;
}
async run() {
const transport = new stdio_js_1.StdioServerTransport();
await this.server.connect(transport);
console.error('3GPP MCP Server running on stdio');
}
}
const server = new GPPMCPServer();
server.run().catch((error) => {
console.error('Server error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map