it-tools-mcp 4.1.15 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.js +114 -249
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -475,250 +475,6 @@ export function mcpToolHandler(handler, identifier = 'default') {
|
|
|
475
475
|
}
|
|
476
476
|
};
|
|
477
477
|
}
|
|
478
|
-
// Demo tools for MCP utilities
|
|
479
|
-
server.registerTool("mcp_utilities_demo", {
|
|
480
|
-
description: "Demonstrate MCP utilities: ping, progress tracking, and cancellation support",
|
|
481
|
-
inputSchema: {
|
|
482
|
-
operation: z.enum(['ping', 'long_task', 'cancellable_task']).describe("The MCP utility operation to demonstrate"),
|
|
483
|
-
duration: z.number().optional().describe("Duration in seconds for long-running tasks (default: 10)"),
|
|
484
|
-
steps: z.number().optional().describe("Number of progress steps for demonstrating progress tracking (default: 5)")
|
|
485
|
-
}
|
|
486
|
-
}, async (args) => {
|
|
487
|
-
const { operation, duration = 10, steps = 5 } = args;
|
|
488
|
-
if (operation === 'ping') {
|
|
489
|
-
return {
|
|
490
|
-
content: [{
|
|
491
|
-
type: "text",
|
|
492
|
-
text: JSON.stringify({
|
|
493
|
-
operation: 'ping',
|
|
494
|
-
status: 'success',
|
|
495
|
-
message: 'MCP ping utility is working correctly',
|
|
496
|
-
timestamp: new Date().toISOString(),
|
|
497
|
-
usage: 'Send a "ping" request to test connection health'
|
|
498
|
-
}, null, 2)
|
|
499
|
-
}]
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
if (operation === 'long_task') {
|
|
503
|
-
// Simulate a long-running task with progress updates
|
|
504
|
-
const totalMs = duration * 1000;
|
|
505
|
-
const stepMs = totalMs / steps;
|
|
506
|
-
return {
|
|
507
|
-
content: [{
|
|
508
|
-
type: "text",
|
|
509
|
-
text: JSON.stringify({
|
|
510
|
-
operation: 'long_task',
|
|
511
|
-
status: 'completed',
|
|
512
|
-
message: `Simulated ${duration}s task with ${steps} progress updates`,
|
|
513
|
-
note: 'Use _meta.progressToken in your request to receive progress notifications',
|
|
514
|
-
example: {
|
|
515
|
-
request: {
|
|
516
|
-
jsonrpc: "2.0",
|
|
517
|
-
id: 1,
|
|
518
|
-
method: "tools/call",
|
|
519
|
-
params: {
|
|
520
|
-
name: "mcp_utilities_demo",
|
|
521
|
-
arguments: { operation: "long_task", duration: 5, steps: 3 },
|
|
522
|
-
_meta: { progressToken: "demo123" }
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}, null, 2)
|
|
527
|
-
}]
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
if (operation === 'cancellable_task') {
|
|
531
|
-
return {
|
|
532
|
-
content: [{
|
|
533
|
-
type: "text",
|
|
534
|
-
text: JSON.stringify({
|
|
535
|
-
operation: 'cancellable_task',
|
|
536
|
-
status: 'completed',
|
|
537
|
-
message: 'Simulated cancellable task',
|
|
538
|
-
note: 'Send a notifications/cancelled message to cancel in-progress requests',
|
|
539
|
-
example: {
|
|
540
|
-
cancel_notification: {
|
|
541
|
-
jsonrpc: "2.0",
|
|
542
|
-
method: "notifications/cancelled",
|
|
543
|
-
params: {
|
|
544
|
-
requestId: "your_request_id",
|
|
545
|
-
reason: "User requested cancellation"
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}, null, 2)
|
|
550
|
-
}]
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
return {
|
|
554
|
-
content: [{
|
|
555
|
-
type: "text",
|
|
556
|
-
text: JSON.stringify({ error: 'Unknown operation' }, null, 2)
|
|
557
|
-
}]
|
|
558
|
-
};
|
|
559
|
-
});
|
|
560
|
-
// Sampling demo tool
|
|
561
|
-
server.registerTool("mcp_sampling_demo", {
|
|
562
|
-
description: "Demonstrate MCP sampling capabilities and test sampling/createMessage requests",
|
|
563
|
-
inputSchema: {
|
|
564
|
-
message: z.string().describe("The message to send in the sampling request"),
|
|
565
|
-
modelPreference: z.enum(['claude', 'gpt', 'gemini', 'generic']).optional().describe("Preferred model family for demonstration"),
|
|
566
|
-
systemPrompt: z.string().optional().describe("System prompt to include in the sampling request"),
|
|
567
|
-
maxTokens: z.number().positive().optional().describe("Maximum tokens for the response (default: 100)"),
|
|
568
|
-
intelligence: z.number().min(0).max(1).optional().describe("Intelligence priority (0-1, higher = more capable models)"),
|
|
569
|
-
speed: z.number().min(0).max(1).optional().describe("Speed priority (0-1, higher = faster models)"),
|
|
570
|
-
cost: z.number().min(0).max(1).optional().describe("Cost priority (0-1, higher = cheaper models)")
|
|
571
|
-
}
|
|
572
|
-
}, async (args) => {
|
|
573
|
-
const { message, modelPreference, systemPrompt, maxTokens = 100, intelligence = 0.7, speed = 0.5, cost = 0.3 } = args;
|
|
574
|
-
// Build model preferences based on user input
|
|
575
|
-
const modelPreferences = {
|
|
576
|
-
intelligencePriority: intelligence,
|
|
577
|
-
speedPriority: speed,
|
|
578
|
-
costPriority: cost
|
|
579
|
-
};
|
|
580
|
-
// Add model hints based on preference
|
|
581
|
-
if (modelPreference) {
|
|
582
|
-
const hintMap = {
|
|
583
|
-
'claude': [{ name: 'claude-4-sonnet' }, { name: 'claude' }],
|
|
584
|
-
'gpt': [{ name: 'gpt-4' }, { name: 'gpt' }],
|
|
585
|
-
'gemini': [{ name: 'gemini-1.5-pro' }, { name: 'gemini' }],
|
|
586
|
-
'generic': [{ name: 'general-purpose' }]
|
|
587
|
-
};
|
|
588
|
-
modelPreferences.hints = hintMap[modelPreference];
|
|
589
|
-
}
|
|
590
|
-
// Create the sampling request
|
|
591
|
-
const samplingRequest = {
|
|
592
|
-
method: "sampling/createMessage",
|
|
593
|
-
params: {
|
|
594
|
-
messages: [
|
|
595
|
-
{
|
|
596
|
-
role: "user",
|
|
597
|
-
content: {
|
|
598
|
-
type: "text",
|
|
599
|
-
text: message
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
],
|
|
603
|
-
modelPreferences,
|
|
604
|
-
...(systemPrompt && { systemPrompt }),
|
|
605
|
-
maxTokens
|
|
606
|
-
}
|
|
607
|
-
};
|
|
608
|
-
return {
|
|
609
|
-
content: [{
|
|
610
|
-
type: "text",
|
|
611
|
-
text: JSON.stringify({
|
|
612
|
-
demo: 'MCP Sampling Protocol Demonstration',
|
|
613
|
-
status: 'request_prepared',
|
|
614
|
-
message: 'Here is the sampling request that would be sent to the MCP client',
|
|
615
|
-
request: samplingRequest,
|
|
616
|
-
explanation: {
|
|
617
|
-
protocol: 'MCP 2025-06-18 sampling/createMessage',
|
|
618
|
-
purpose: 'This demonstrates how servers can request LLM completions from clients',
|
|
619
|
-
modelSelection: modelPreferences.hints ?
|
|
620
|
-
`Prefers ${modelPreference} models with intelligence=${intelligence}, speed=${speed}, cost=${cost}` :
|
|
621
|
-
`No specific model preference, using priorities: intelligence=${intelligence}, speed=${speed}, cost=${cost}`,
|
|
622
|
-
flow: [
|
|
623
|
-
'1. Server sends sampling/createMessage request to client',
|
|
624
|
-
'2. Client selects appropriate model based on preferences',
|
|
625
|
-
'3. Client processes the message through the selected LLM',
|
|
626
|
-
'4. Client returns the LLM response to the server',
|
|
627
|
-
'5. Server can use the response for its tool operations'
|
|
628
|
-
],
|
|
629
|
-
security: 'Clients SHOULD implement user approval controls for sampling requests'
|
|
630
|
-
},
|
|
631
|
-
nextSteps: 'In production, this request would be sent to the MCP client for actual LLM processing'
|
|
632
|
-
}, null, 2)
|
|
633
|
-
}]
|
|
634
|
-
};
|
|
635
|
-
});
|
|
636
|
-
// MCP Sampling Implementation - Server-side LLM request handling
|
|
637
|
-
server.server.setRequestHandler(z.object({
|
|
638
|
-
method: z.literal("sampling/createMessage"),
|
|
639
|
-
params: z.object({
|
|
640
|
-
messages: z.array(z.object({
|
|
641
|
-
role: z.enum(["user", "assistant", "system"]),
|
|
642
|
-
content: z.union([
|
|
643
|
-
z.object({
|
|
644
|
-
type: z.literal("text"),
|
|
645
|
-
text: z.string()
|
|
646
|
-
}),
|
|
647
|
-
z.object({
|
|
648
|
-
type: z.literal("image"),
|
|
649
|
-
data: z.string(),
|
|
650
|
-
mimeType: z.string()
|
|
651
|
-
}),
|
|
652
|
-
z.object({
|
|
653
|
-
type: z.literal("audio"),
|
|
654
|
-
data: z.string(),
|
|
655
|
-
mimeType: z.string()
|
|
656
|
-
})
|
|
657
|
-
])
|
|
658
|
-
})),
|
|
659
|
-
modelPreferences: z.object({
|
|
660
|
-
hints: z.array(z.object({
|
|
661
|
-
name: z.string()
|
|
662
|
-
})).optional(),
|
|
663
|
-
costPriority: z.number().min(0).max(1).optional(),
|
|
664
|
-
speedPriority: z.number().min(0).max(1).optional(),
|
|
665
|
-
intelligencePriority: z.number().min(0).max(1).optional()
|
|
666
|
-
}).optional(),
|
|
667
|
-
systemPrompt: z.string().optional(),
|
|
668
|
-
maxTokens: z.number().positive().optional(),
|
|
669
|
-
temperature: z.number().min(0).max(2).optional(),
|
|
670
|
-
stopSequences: z.array(z.string()).optional(),
|
|
671
|
-
metadata: z.record(z.any()).optional()
|
|
672
|
-
})
|
|
673
|
-
}), async (request) => {
|
|
674
|
-
const { messages, modelPreferences, systemPrompt, maxTokens, temperature, stopSequences, metadata } = request.params;
|
|
675
|
-
mcpLog('info', 'Sampling request received', {
|
|
676
|
-
messageCount: messages.length,
|
|
677
|
-
modelPreferences: modelPreferences ? Object.keys(modelPreferences) : undefined,
|
|
678
|
-
hasSystemPrompt: !!systemPrompt,
|
|
679
|
-
maxTokens
|
|
680
|
-
});
|
|
681
|
-
// In a real implementation, this would:
|
|
682
|
-
// 1. Forward the request to the client's LLM service
|
|
683
|
-
// 2. Apply model preferences and selection logic
|
|
684
|
-
// 3. Handle different content types (text, image, audio)
|
|
685
|
-
// 4. Return the LLM response
|
|
686
|
-
// For this MCP server implementation, we return a helpful response
|
|
687
|
-
// explaining that this is a demonstration of the sampling protocol
|
|
688
|
-
// and that the actual LLM processing would be handled by the client
|
|
689
|
-
const demoResponse = {
|
|
690
|
-
role: "assistant",
|
|
691
|
-
content: {
|
|
692
|
-
type: "text",
|
|
693
|
-
text: `This is a demonstration of MCP sampling protocol support.
|
|
694
|
-
|
|
695
|
-
In a production environment, this request would be forwarded to an LLM service based on your model preferences:
|
|
696
|
-
${modelPreferences?.hints?.length ? `- Preferred models: ${modelPreferences.hints.map(h => h.name).join(', ')}` : '- No specific model preferences'}
|
|
697
|
-
${modelPreferences?.intelligencePriority ? `- Intelligence priority: ${modelPreferences.intelligencePriority}` : ''}
|
|
698
|
-
${modelPreferences?.speedPriority ? `- Speed priority: ${modelPreferences.speedPriority}` : ''}
|
|
699
|
-
${modelPreferences?.costPriority ? `- Cost priority: ${modelPreferences.costPriority}` : ''}
|
|
700
|
-
|
|
701
|
-
Your message: "${messages[messages.length - 1]?.content?.type === 'text' ? messages[messages.length - 1].content.text : 'Non-text content'}"
|
|
702
|
-
|
|
703
|
-
${systemPrompt ? `System prompt: "${systemPrompt}"` : 'No system prompt provided'}
|
|
704
|
-
${maxTokens ? `Max tokens: ${maxTokens}` : 'No token limit specified'}
|
|
705
|
-
|
|
706
|
-
This server supports the full MCP 2025-06-18 sampling specification and is ready for production use with proper LLM integration.`
|
|
707
|
-
},
|
|
708
|
-
model: "mcp-demo-server",
|
|
709
|
-
stopReason: "endTurn",
|
|
710
|
-
usage: {
|
|
711
|
-
inputTokens: messages.reduce((sum, msg) => sum + (msg.content.type === 'text' ? msg.content.text.length / 4 : 100), 0),
|
|
712
|
-
outputTokens: 150
|
|
713
|
-
}
|
|
714
|
-
};
|
|
715
|
-
mcpLog('debug', 'Sampling response generated', {
|
|
716
|
-
model: demoResponse.model,
|
|
717
|
-
stopReason: demoResponse.stopReason,
|
|
718
|
-
outputTokens: demoResponse.usage.outputTokens
|
|
719
|
-
});
|
|
720
|
-
return demoResponse;
|
|
721
|
-
});
|
|
722
478
|
// VS Code MCP Compliance: Implement Resources
|
|
723
479
|
server.registerResource("server-manifest", new ResourceTemplate("manifest://{type}", {
|
|
724
480
|
list: async () => ({
|
|
@@ -766,7 +522,7 @@ server.registerResource("system-logs", new ResourceTemplate("logs://{type}", {
|
|
|
766
522
|
}]
|
|
767
523
|
};
|
|
768
524
|
});
|
|
769
|
-
server.registerResource("tool-documentation", new ResourceTemplate("docs://{category}
|
|
525
|
+
server.registerResource("tool-documentation", new ResourceTemplate("docs://{category}", {
|
|
770
526
|
list: async () => {
|
|
771
527
|
const { toolCategories } = await discoverTools();
|
|
772
528
|
const resources = Object.keys(toolCategories).map(category => ({
|
|
@@ -787,8 +543,8 @@ server.registerResource("tool-documentation", new ResourceTemplate("docs://{cate
|
|
|
787
543
|
description: "Documentation for available tools by category"
|
|
788
544
|
}, async (uri, params) => {
|
|
789
545
|
const category = params.category;
|
|
790
|
-
|
|
791
|
-
const docs = await getToolDocumentation(category
|
|
546
|
+
// For this simpler template, we only handle category-level documentation
|
|
547
|
+
const docs = await getToolDocumentation(category);
|
|
792
548
|
return {
|
|
793
549
|
contents: [{
|
|
794
550
|
uri: uri.href,
|
|
@@ -924,16 +680,125 @@ async function getManifestContent(type) {
|
|
|
924
680
|
};
|
|
925
681
|
return manifests[type] || { error: "Manifest type not found" };
|
|
926
682
|
}
|
|
683
|
+
// Helper function to extract tool documentation from the main README table
|
|
684
|
+
function extractToolFromReadme(readmeContent, toolName) {
|
|
685
|
+
// Look for the tool in the Available Tools table
|
|
686
|
+
const lines = readmeContent.split('\n');
|
|
687
|
+
const toolRegex = new RegExp(`\\|\s*\`${toolName}\`\\s*\\|`, 'i');
|
|
688
|
+
for (let i = 0; i < lines.length; i++) {
|
|
689
|
+
const line = lines[i];
|
|
690
|
+
if (toolRegex.test(line)) {
|
|
691
|
+
// Found the tool, extract the row
|
|
692
|
+
const parts = line.split('|').map(part => part.trim()).filter(part => part.length > 0);
|
|
693
|
+
if (parts.length >= 2) {
|
|
694
|
+
const cleanToolName = parts[0].replace(/`/g, ''); // Remove backticks from tool name
|
|
695
|
+
const descCell = parts[1]; // Description cell
|
|
696
|
+
const paramsCell = parts.length > 2 ? parts[2] : ''; // Parameters cell
|
|
697
|
+
return `# ${cleanToolName} Documentation\n\n**Description:** ${descCell}\n\n${paramsCell ? `**Parameters:** ${paramsCell}\n\n` : ''}**Usage:** ${descCell}`;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
// Helper function to extract category section from the main README
|
|
704
|
+
function extractCategoryFromReadme(readmeContent, category) {
|
|
705
|
+
// Map category names to README section names
|
|
706
|
+
const categoryMappings = {
|
|
707
|
+
'ansible': 'Ansible Tools',
|
|
708
|
+
'color': 'Color Tools',
|
|
709
|
+
'data_format': 'Data Format',
|
|
710
|
+
'development': 'Development Tools',
|
|
711
|
+
'docker': 'Docker Tools',
|
|
712
|
+
'encoding': 'Encoding & Decoding',
|
|
713
|
+
'forensic': 'Forensic Tools',
|
|
714
|
+
'id_generators': 'ID & Code Generators',
|
|
715
|
+
'math': 'Math & Calculations',
|
|
716
|
+
'network': 'Network & System',
|
|
717
|
+
'physics': 'Physics',
|
|
718
|
+
'crypto': 'Security & Crypto',
|
|
719
|
+
'text': 'Text Processing',
|
|
720
|
+
'utility': 'Utility Tools'
|
|
721
|
+
};
|
|
722
|
+
const sectionName = categoryMappings[category];
|
|
723
|
+
if (!sectionName) {
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
// Find the section in the README table
|
|
727
|
+
const lines = readmeContent.split('\n');
|
|
728
|
+
let inTargetSection = false;
|
|
729
|
+
let tableRows = [];
|
|
730
|
+
for (let i = 0; i < lines.length; i++) {
|
|
731
|
+
const line = lines[i];
|
|
732
|
+
// Check if we're entering the target section
|
|
733
|
+
if (line.includes(`**${sectionName}**`)) {
|
|
734
|
+
inTargetSection = true;
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
// Check if we're entering a new section (exit current)
|
|
738
|
+
if (inTargetSection && line.includes('**') && line.includes('**') && !line.includes(sectionName)) {
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
// Collect table rows while in target section
|
|
742
|
+
if (inTargetSection && line.includes('|') && !line.includes('---')) {
|
|
743
|
+
tableRows.push(line);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (tableRows.length === 0) {
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
// Parse and clean up the table
|
|
750
|
+
const cleanedContent = [`# ${sectionName} Documentation\n`];
|
|
751
|
+
for (const row of tableRows) {
|
|
752
|
+
const cells = row.split('|').map(cell => cell.trim()).filter(cell => cell.length > 0);
|
|
753
|
+
if (cells.length >= 2) {
|
|
754
|
+
const toolName = cells[0].replace(/`/g, ''); // Remove backticks
|
|
755
|
+
const description = cells[1];
|
|
756
|
+
const parameters = cells.length > 2 ? cells[2] : '';
|
|
757
|
+
cleanedContent.push(`## ${toolName}`);
|
|
758
|
+
cleanedContent.push(`**Description:** ${description}`);
|
|
759
|
+
if (parameters) {
|
|
760
|
+
cleanedContent.push(`**Parameters:** ${parameters}`);
|
|
761
|
+
}
|
|
762
|
+
cleanedContent.push(''); // Add empty line for spacing
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return cleanedContent.join('\n');
|
|
766
|
+
}
|
|
927
767
|
async function getToolDocumentation(category, tool) {
|
|
768
|
+
// When compiled, __dirname will be the build/ directory, so we need to go up one level
|
|
769
|
+
const readmePath = path.join(__dirname, '../README.md');
|
|
928
770
|
if (tool) {
|
|
771
|
+
// For specific tools, try to find them in the README table
|
|
772
|
+
try {
|
|
773
|
+
const readmeContent = fs.readFileSync(readmePath, 'utf-8');
|
|
774
|
+
const toolSection = extractToolFromReadme(readmeContent, tool);
|
|
775
|
+
if (toolSection) {
|
|
776
|
+
return toolSection;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
catch (error) {
|
|
780
|
+
mcpLog('warning', `Failed to read README for tool documentation: ${readmePath}`, error instanceof Error ? error.message : 'Unknown error');
|
|
781
|
+
}
|
|
929
782
|
return `# ${tool} Documentation\n\nCategory: ${category}\n\nThis tool provides ${category} functionality.\n\nUsage: See tool description for specific parameters and examples.`;
|
|
930
783
|
}
|
|
784
|
+
// Try to read category documentation from README
|
|
785
|
+
try {
|
|
786
|
+
const readmeContent = fs.readFileSync(readmePath, 'utf-8');
|
|
787
|
+
const categorySection = extractCategoryFromReadme(readmeContent, category);
|
|
788
|
+
if (categorySection) {
|
|
789
|
+
return categorySection;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
mcpLog('warning', `Failed to read README for category documentation: ${readmePath}`, error instanceof Error ? error.message : 'Unknown error');
|
|
794
|
+
}
|
|
795
|
+
// Fallback to dynamic generation
|
|
931
796
|
const { toolCategories } = await discoverTools();
|
|
932
797
|
const categoryInfo = toolCategories[category];
|
|
933
798
|
if (!categoryInfo) {
|
|
934
|
-
return `# Category Not Found\n\nThe category '${category}' was not found
|
|
799
|
+
return `# Category Not Found\n\nThe category '${category}' was not found.\n\nAvailable categories: ${Object.keys(toolCategories).join(', ')}`;
|
|
935
800
|
}
|
|
936
|
-
return `# ${category} Category Documentation\n\n${categoryInfo.description}\n\n## Available Tools\n\n${categoryInfo.tools.map(t => `- ${t}`).join('\n')}
|
|
801
|
+
return `# ${category} Category Documentation\n\n${categoryInfo.description}\n\n## Available Tools\n\n${categoryInfo.tools.map(t => `- ${t}`).join('\n')}\n\n*This documentation was generated automatically. For detailed documentation, see the main README.md file.*`;
|
|
937
802
|
}
|
|
938
803
|
function generateWorkflowPrompt(taskType, context) {
|
|
939
804
|
const workflows = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "it-tools-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Full MCP 2025-06-18 compliant server with 121+ IT tools, logging, ping, progress tracking, cancellation, and sampling utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/index.js",
|