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.
Files changed (2) hide show
  1. package/build/index.js +114 -249
  2. 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}/{tool?}", {
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
- const tool = params.tool;
791
- const docs = await getToolDocumentation(category, tool);
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": "4.1.15",
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",