mcp-perforce-server 3.0.0 → 3.2.0

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/dist/server.js CHANGED
@@ -84,6 +84,10 @@ const TOOL_HANDLERS = {
84
84
  'p4.review.bundle': tools.p4ReviewBundle,
85
85
  'p4.change.inspect': tools.p4ChangeInspect,
86
86
  'p4.path.synccheck': tools.p4PathSyncCheck,
87
+ 'p4.file.inspect': tools.p4FileInspect,
88
+ 'p4.workspace.snapshot': tools.p4WorkspaceSnapshot,
89
+ 'p4.search.inspect': tools.p4SearchInspect,
90
+ 'p4.review.prepare': tools.p4ReviewPrepare,
87
91
  'p4.blame': tools.p4Blame,
88
92
  'p4.annotate': tools.p4Annotate,
89
93
  'p4.copy': tools.p4Copy,
@@ -141,6 +145,10 @@ const LOW_LATENCY_CACHE_TTL_TOOLS = new Set([
141
145
  'p4.review.bundle',
142
146
  'p4.change.inspect',
143
147
  'p4.path.synccheck',
148
+ 'p4.file.inspect',
149
+ 'p4.workspace.snapshot',
150
+ 'p4.search.inspect',
151
+ 'p4.review.prepare',
144
152
  ]);
145
153
  const STABLE_CACHE_TTL_TOOLS = new Set([
146
154
  'p4.info',
@@ -471,17 +479,21 @@ class MCPPerforceServer {
471
479
  log.info('Performance snapshot:', topEntries);
472
480
  }
473
481
  async executeTool(name, args) {
474
- const handler = TOOL_HANDLERS[name];
482
+ // Normalize tool name: support both dot notation (p4.changes) and underscore (p4_changes)
483
+ const normalizedName = name.replace(/_/g, '.');
484
+ const handler = TOOL_HANDLERS[normalizedName];
475
485
  if (!handler) {
476
486
  throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
477
487
  }
478
488
  return handler(this.context, args);
479
489
  }
480
490
  async executeToolWithCaching(name, args) {
481
- if (!this.responseCacheEnabled || !CACHEABLE_TOOLS.has(name)) {
491
+ // Normalize tool name for consistency in caching
492
+ const normalizedName = name.replace(/_/g, '.');
493
+ if (!this.responseCacheEnabled || !CACHEABLE_TOOLS.has(normalizedName)) {
482
494
  return { result: await this.executeTool(name, args), cacheStatus: 'uncacheable' };
483
495
  }
484
- const cacheKey = this.buildCacheKey(name, args);
496
+ const cacheKey = this.buildCacheKey(normalizedName, args);
485
497
  const cachedResult = this.getCachedResult(cacheKey);
486
498
  if (cachedResult) {
487
499
  return cachedResult;
@@ -496,7 +508,7 @@ class MCPPerforceServer {
496
508
  try {
497
509
  const result = await pending;
498
510
  if (startedEpoch === this.cacheEpoch) {
499
- const toolTtlMs = this.getToolCacheTtlMs(name);
511
+ const toolTtlMs = this.getToolCacheTtlMs(normalizedName);
500
512
  if (result && typeof result === 'object' && result.ok === true) {
501
513
  this.setCachedResult(cacheKey, result, toolTtlMs, false);
502
514
  }
@@ -652,6 +664,11 @@ class MCPPerforceServer {
652
664
  type: 'string',
653
665
  description: 'Filespec to sync (optional, defaults to current directory)',
654
666
  },
667
+ filespecs: {
668
+ type: 'array',
669
+ items: { type: 'string' },
670
+ description: 'Filespecs to sync in one command (optional)',
671
+ },
655
672
  force: {
656
673
  type: 'boolean',
657
674
  description: 'Force sync (optional, defaults to false)',
@@ -660,6 +677,42 @@ class MCPPerforceServer {
660
677
  type: 'boolean',
661
678
  description: 'Preview sync without executing (optional, defaults to false)',
662
679
  },
680
+ summaryPreview: {
681
+ type: 'boolean',
682
+ description: 'Preview only the sync summary using p4 sync -N (optional)',
683
+ },
684
+ quiet: {
685
+ type: 'boolean',
686
+ description: 'Suppress normal sync messages using -q (optional)',
687
+ },
688
+ metadataOnly: {
689
+ type: 'boolean',
690
+ description: 'Update have-list metadata only using -k (optional)',
691
+ },
692
+ safeSync: {
693
+ type: 'boolean',
694
+ description: 'Enable digest safety checks using -s (optional)',
695
+ },
696
+ populateOnly: {
697
+ type: 'boolean',
698
+ description: 'Populate without updating server state using -p (optional)',
699
+ },
700
+ reopenMoved: {
701
+ type: 'boolean',
702
+ description: 'Reopen moved files in new depot locations using -r (optional)',
703
+ },
704
+ useListOptimization: {
705
+ type: 'boolean',
706
+ description: 'Use the -L file list optimization for exact depot revisions (optional)',
707
+ },
708
+ max: {
709
+ type: 'number',
710
+ description: 'Limit sync to the first max files (optional)',
711
+ },
712
+ parallel: {
713
+ type: 'string',
714
+ description: 'Parallel sync specification, for example threads=4,batch=8 (optional)',
715
+ },
663
716
  workspacePath: {
664
717
  type: 'string',
665
718
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -678,6 +731,11 @@ class MCPPerforceServer {
678
731
  type: 'string',
679
732
  description: 'Changelist number (optional, shows all opened files if not specified)',
680
733
  },
734
+ files: {
735
+ type: 'array',
736
+ items: { type: 'string' },
737
+ description: 'Optional files/filespecs to check for opened state',
738
+ },
681
739
  workspacePath: {
682
740
  type: 'string',
683
741
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -872,6 +930,11 @@ class MCPPerforceServer {
872
930
  type: 'string',
873
931
  description: 'Filespec to show history for (required)',
874
932
  },
933
+ filespecs: {
934
+ type: 'array',
935
+ items: { type: 'string' },
936
+ description: 'Filespecs to show history for in one command (required if filespec is omitted)',
937
+ },
875
938
  maxRevisions: {
876
939
  type: 'number',
877
940
  description: 'Maximum number of revisions to show (optional)',
@@ -881,7 +944,10 @@ class MCPPerforceServer {
881
944
  description: 'Path to workspace directory (optional, defaults to current directory)',
882
945
  },
883
946
  },
884
- required: ['filespec'],
947
+ anyOf: [
948
+ { required: ['filespec'] },
949
+ { required: ['filespecs'] },
950
+ ],
885
951
  additionalProperties: false,
886
952
  },
887
953
  },
@@ -895,6 +961,30 @@ class MCPPerforceServer {
895
961
  type: 'string',
896
962
  description: 'Filter by user (optional)',
897
963
  },
964
+ stream: {
965
+ type: 'string',
966
+ description: 'Limit workspaces to a dedicated stream using -S (optional)',
967
+ },
968
+ nameFilter: {
969
+ type: 'string',
970
+ description: 'Workspace name filter using -e (optional)',
971
+ },
972
+ caseInsensitiveNameFilter: {
973
+ type: 'string',
974
+ description: 'Case-insensitive workspace name filter using -E (optional)',
975
+ },
976
+ unloaded: {
977
+ type: 'boolean',
978
+ description: 'List unloaded clients using -U (optional)',
979
+ },
980
+ allServers: {
981
+ type: 'boolean',
982
+ description: 'List clients across all servers using -a (optional)',
983
+ },
984
+ serverId: {
985
+ type: 'string',
986
+ description: 'Limit clients to a specific server using -s (optional)',
987
+ },
898
988
  max: {
899
989
  type: 'number',
900
990
  description: 'Maximum number of results (optional)',
@@ -1033,6 +1123,11 @@ class MCPPerforceServer {
1033
1123
  type: 'string',
1034
1124
  description: 'Filter by filespec (optional)',
1035
1125
  },
1126
+ filespecs: {
1127
+ type: 'array',
1128
+ items: { type: 'string' },
1129
+ description: 'Filter by multiple filespecs (optional)',
1130
+ },
1036
1131
  workspacePath: {
1037
1132
  type: 'string',
1038
1133
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -1100,6 +1195,27 @@ class MCPPerforceServer {
1100
1195
  type: 'string',
1101
1196
  description: 'Target depot filespec/path (required)',
1102
1197
  },
1198
+ targetPaths: {
1199
+ type: 'array',
1200
+ items: { type: 'string' },
1201
+ description: 'Additional target depot filespecs for branch or path modes (optional)',
1202
+ },
1203
+ branch: {
1204
+ type: 'string',
1205
+ description: 'Branch spec name for p4 interchanges -b mode (optional)',
1206
+ },
1207
+ stream: {
1208
+ type: 'string',
1209
+ description: 'Stream path for p4 interchanges -S mode (optional)',
1210
+ },
1211
+ parentStream: {
1212
+ type: 'string',
1213
+ description: 'Parent stream override for stream mode using -P (optional)',
1214
+ },
1215
+ useBranchSource: {
1216
+ type: 'boolean',
1217
+ description: 'Use branch -s mode where sourcePath becomes fromFile (optional)',
1218
+ },
1103
1219
  max: {
1104
1220
  type: 'number',
1105
1221
  description: 'Maximum number of changelists to return (optional)',
@@ -1108,12 +1224,33 @@ class MCPPerforceServer {
1108
1224
  type: 'boolean',
1109
1225
  description: 'Include long descriptions (optional, equivalent to -l)',
1110
1226
  },
1227
+ reverse: {
1228
+ type: 'boolean',
1229
+ description: 'Reverse branch or stream mapping direction using -r (optional)',
1230
+ },
1231
+ time: {
1232
+ type: 'boolean',
1233
+ description: 'Display the time as well as the date using -t (optional)',
1234
+ },
1235
+ user: {
1236
+ type: 'string',
1237
+ description: 'Limit results to changes submitted by a specific user (optional)',
1238
+ },
1239
+ forceStreamFlow: {
1240
+ type: 'boolean',
1241
+ description: 'Force stream-mode interchanges to ignore expected flow using -F (optional)',
1242
+ },
1111
1243
  workspacePath: {
1112
1244
  type: 'string',
1113
1245
  description: 'Path to workspace directory (optional, defaults to current directory)',
1114
1246
  },
1115
1247
  },
1116
- required: ['sourcePath', 'targetPath'],
1248
+ anyOf: [
1249
+ { required: ['sourcePath', 'targetPath'] },
1250
+ { required: ['sourcePath', 'targetPaths'] },
1251
+ { required: ['branch'] },
1252
+ { required: ['stream'] },
1253
+ ],
1117
1254
  additionalProperties: false,
1118
1255
  },
1119
1256
  },
@@ -1131,12 +1268,20 @@ class MCPPerforceServer {
1131
1268
  type: 'string',
1132
1269
  description: 'Optional target depot filespec/path',
1133
1270
  },
1271
+ files: {
1272
+ type: 'array',
1273
+ items: { type: 'string' },
1274
+ description: 'Optional file/filespec list for native p4 integrated filtering',
1275
+ },
1134
1276
  workspacePath: {
1135
1277
  type: 'string',
1136
1278
  description: 'Path to workspace directory (optional, defaults to current directory)',
1137
1279
  },
1138
1280
  },
1139
- required: ['sourcePath'],
1281
+ anyOf: [
1282
+ { required: ['sourcePath'] },
1283
+ { required: ['files'] },
1284
+ ],
1140
1285
  additionalProperties: false,
1141
1286
  },
1142
1287
  },
@@ -1249,6 +1394,209 @@ class MCPPerforceServer {
1249
1394
  additionalProperties: false,
1250
1395
  },
1251
1396
  },
1397
+ {
1398
+ name: 'p4.file.inspect',
1399
+ description: 'Composite file inspection: fstat + filelog + optional print/blame in one call',
1400
+ inputSchema: {
1401
+ type: 'object',
1402
+ properties: {
1403
+ filespec: {
1404
+ type: 'string',
1405
+ description: 'Single filespec to inspect',
1406
+ },
1407
+ filespecs: {
1408
+ type: 'array',
1409
+ items: { type: 'string' },
1410
+ description: 'Multiple filespecs to inspect in one call',
1411
+ },
1412
+ includeFstat: {
1413
+ type: 'boolean',
1414
+ description: 'Include p4 fstat metadata (optional, default true)',
1415
+ },
1416
+ includeHistory: {
1417
+ type: 'boolean',
1418
+ description: 'Include p4 filelog history (optional, default true)',
1419
+ },
1420
+ includeContent: {
1421
+ type: 'boolean',
1422
+ description: 'Include p4 print content (optional, default false)',
1423
+ },
1424
+ includeBlame: {
1425
+ type: 'boolean',
1426
+ description: 'Include p4 annotate/blame output (optional, default false)',
1427
+ },
1428
+ maxFiles: {
1429
+ type: 'number',
1430
+ description: 'Maximum files to inspect in one call (optional, default requested count capped at 25)',
1431
+ },
1432
+ maxRevisions: {
1433
+ type: 'number',
1434
+ description: 'Maximum revisions per filelog call (optional, default 5)',
1435
+ },
1436
+ workspacePath: {
1437
+ type: 'string',
1438
+ description: 'Path to workspace directory (optional, defaults to current directory)',
1439
+ },
1440
+ },
1441
+ anyOf: [
1442
+ { required: ['filespec'] },
1443
+ { required: ['filespecs'] },
1444
+ ],
1445
+ additionalProperties: false,
1446
+ },
1447
+ },
1448
+ {
1449
+ name: 'p4.workspace.snapshot',
1450
+ description: 'Composite workspace snapshot: info + status + optional config/opened/recent changes',
1451
+ inputSchema: {
1452
+ type: 'object',
1453
+ properties: {
1454
+ includeConfig: {
1455
+ type: 'boolean',
1456
+ description: 'Include p4.config.detect output (optional, default true)',
1457
+ },
1458
+ includeOpened: {
1459
+ type: 'boolean',
1460
+ description: 'Include full p4.opened output (optional, default false)',
1461
+ },
1462
+ includeRecentChanges: {
1463
+ type: 'boolean',
1464
+ description: 'Include recent changelists via p4.changes (optional, default false)',
1465
+ },
1466
+ recentChangesMax: {
1467
+ type: 'number',
1468
+ description: 'Maximum recent changelists to include (optional, default 10)',
1469
+ },
1470
+ workspacePath: {
1471
+ type: 'string',
1472
+ description: 'Path to workspace directory (optional, defaults to current directory)',
1473
+ },
1474
+ },
1475
+ additionalProperties: false,
1476
+ },
1477
+ },
1478
+ {
1479
+ name: 'p4.search.inspect',
1480
+ description: 'Composite search helper: grep + optional fstat + optional content previews',
1481
+ inputSchema: {
1482
+ type: 'object',
1483
+ properties: {
1484
+ pattern: {
1485
+ type: 'string',
1486
+ description: 'Search pattern for p4 grep (required)',
1487
+ },
1488
+ filespec: {
1489
+ type: 'string',
1490
+ description: 'Single filespec to search',
1491
+ },
1492
+ filespecs: {
1493
+ type: 'array',
1494
+ items: { type: 'string' },
1495
+ description: 'Multiple filespecs to search in one call',
1496
+ },
1497
+ caseInsensitive: {
1498
+ type: 'boolean',
1499
+ description: 'Case insensitive search (optional, default false)',
1500
+ },
1501
+ maxFiles: {
1502
+ type: 'number',
1503
+ description: 'Maximum matched files to include (optional, default 20)',
1504
+ },
1505
+ maxMatchesPerFile: {
1506
+ type: 'number',
1507
+ description: 'Maximum grep matches to retain per file (optional, default 20)',
1508
+ },
1509
+ includeFstat: {
1510
+ type: 'boolean',
1511
+ description: 'Include p4 fstat metadata for matched files (optional, default true)',
1512
+ },
1513
+ includeContentPreview: {
1514
+ type: 'boolean',
1515
+ description: 'Include p4 print context snippets for matched files (optional, default false)',
1516
+ },
1517
+ previewContextLines: {
1518
+ type: 'number',
1519
+ description: 'Context lines before and after each match when includeContentPreview=true (optional, default 2)',
1520
+ },
1521
+ workspacePath: {
1522
+ type: 'string',
1523
+ description: 'Path to workspace directory (optional, defaults to current directory)',
1524
+ },
1525
+ },
1526
+ required: ['pattern'],
1527
+ additionalProperties: false,
1528
+ },
1529
+ },
1530
+ {
1531
+ name: 'p4.review.prepare',
1532
+ description: 'Composite review preparation: discover or accept changelists, then build review-ready inspection bundles',
1533
+ inputSchema: {
1534
+ type: 'object',
1535
+ properties: {
1536
+ changelist: {
1537
+ type: 'string',
1538
+ description: 'Single changelist to inspect directly',
1539
+ },
1540
+ changelists: {
1541
+ type: 'array',
1542
+ items: { type: 'string' },
1543
+ description: 'Multiple changelists to inspect directly',
1544
+ },
1545
+ status: {
1546
+ type: 'string',
1547
+ enum: ['submitted', 'pending', 'shelved'],
1548
+ description: 'Status filter used when discovering changelists',
1549
+ },
1550
+ user: {
1551
+ type: 'string',
1552
+ description: 'User filter used when discovering changelists',
1553
+ },
1554
+ client: {
1555
+ type: 'string',
1556
+ description: 'Client/workspace filter used when discovering changelists',
1557
+ },
1558
+ filespec: {
1559
+ type: 'string',
1560
+ description: 'Single filespec filter used when discovering changelists',
1561
+ },
1562
+ filespecs: {
1563
+ type: 'array',
1564
+ items: { type: 'string' },
1565
+ description: 'Multiple filespec filters used when discovering changelists',
1566
+ },
1567
+ maxChanges: {
1568
+ type: 'number',
1569
+ description: 'Maximum changelists to inspect (optional, default 10)',
1570
+ },
1571
+ includeDiff: {
1572
+ type: 'boolean',
1573
+ description: 'Include changelist diff content in each inspection bundle (optional, default false)',
1574
+ },
1575
+ diffFormat: {
1576
+ type: 'string',
1577
+ enum: ['u', 'c', 'n', 's'],
1578
+ description: 'Diff format for inspection describe output when includeDiff=true',
1579
+ },
1580
+ includeFileHistory: {
1581
+ type: 'boolean',
1582
+ description: 'Include file history for files touched by each changelist (optional, default false)',
1583
+ },
1584
+ maxFilesWithHistory: {
1585
+ type: 'number',
1586
+ description: 'Maximum files per changelist to include history for (optional, default 5)',
1587
+ },
1588
+ maxRevisions: {
1589
+ type: 'number',
1590
+ description: 'Maximum revisions per file history call (optional, default 5)',
1591
+ },
1592
+ workspacePath: {
1593
+ type: 'string',
1594
+ description: 'Path to workspace directory (optional, defaults to current directory)',
1595
+ },
1596
+ },
1597
+ additionalProperties: false,
1598
+ },
1599
+ },
1252
1600
  {
1253
1601
  name: 'p4.blame',
1254
1602
  description: 'Show file annotations with change history (like git blame)',
@@ -1259,12 +1607,20 @@ class MCPPerforceServer {
1259
1607
  type: 'string',
1260
1608
  description: 'File to show blame for (required)',
1261
1609
  },
1610
+ files: {
1611
+ type: 'array',
1612
+ items: { type: 'string' },
1613
+ description: 'Files to show blame for in one command (required if file is omitted)',
1614
+ },
1262
1615
  workspacePath: {
1263
1616
  type: 'string',
1264
1617
  description: 'Path to workspace directory (optional, defaults to current directory)',
1265
1618
  },
1266
1619
  },
1267
- required: ['file'],
1620
+ anyOf: [
1621
+ { required: ['file'] },
1622
+ { required: ['files'] },
1623
+ ],
1268
1624
  additionalProperties: false,
1269
1625
  },
1270
1626
  },
@@ -1278,12 +1634,20 @@ class MCPPerforceServer {
1278
1634
  type: 'string',
1279
1635
  description: 'File to annotate (required)',
1280
1636
  },
1637
+ files: {
1638
+ type: 'array',
1639
+ items: { type: 'string' },
1640
+ description: 'Files to annotate in one command (required if file is omitted)',
1641
+ },
1281
1642
  workspacePath: {
1282
1643
  type: 'string',
1283
1644
  description: 'Path to workspace directory (optional, defaults to current directory)',
1284
1645
  },
1285
1646
  },
1286
- required: ['file'],
1647
+ anyOf: [
1648
+ { required: ['file'] },
1649
+ { required: ['files'] },
1650
+ ],
1287
1651
  additionalProperties: false,
1288
1652
  },
1289
1653
  },
@@ -1406,6 +1770,11 @@ class MCPPerforceServer {
1406
1770
  type: 'string',
1407
1771
  description: 'Depot filespec to print (required)',
1408
1772
  },
1773
+ filespecs: {
1774
+ type: 'array',
1775
+ items: { type: 'string' },
1776
+ description: 'Depot filespecs to print in one command (required if filespec is omitted)',
1777
+ },
1409
1778
  quiet: {
1410
1779
  type: 'boolean',
1411
1780
  description: 'Suppress file headers (optional, defaults to true)',
@@ -1415,7 +1784,10 @@ class MCPPerforceServer {
1415
1784
  description: 'Path to workspace directory (optional, defaults to current directory)',
1416
1785
  },
1417
1786
  },
1418
- required: ['filespec'],
1787
+ anyOf: [
1788
+ { required: ['filespec'] },
1789
+ { required: ['filespecs'] },
1790
+ ],
1419
1791
  additionalProperties: false,
1420
1792
  },
1421
1793
  },
@@ -1429,16 +1801,64 @@ class MCPPerforceServer {
1429
1801
  type: 'string',
1430
1802
  description: 'Filespec to inspect (required)',
1431
1803
  },
1804
+ filespecs: {
1805
+ type: 'array',
1806
+ items: { type: 'string' },
1807
+ description: 'Filespecs to inspect in one command (required if filespec is omitted)',
1808
+ },
1432
1809
  max: {
1433
1810
  type: 'number',
1434
1811
  description: 'Maximum number of results (optional)',
1435
1812
  },
1813
+ filter: {
1814
+ type: 'string',
1815
+ description: 'fstat filter expression for -F (optional)',
1816
+ },
1817
+ fields: {
1818
+ type: 'array',
1819
+ items: { type: 'string' },
1820
+ description: 'Specific tagged fields to return using -T (optional)',
1821
+ },
1822
+ reverseOrder: {
1823
+ type: 'boolean',
1824
+ description: 'Reverse the output sort order using -r (optional)',
1825
+ },
1826
+ attributePattern: {
1827
+ type: 'string',
1828
+ description: 'Restrict attributes using -A pattern (optional)',
1829
+ },
1830
+ changeAfter: {
1831
+ type: 'string',
1832
+ description: 'Show files modified by or after a submitted changelist using -c (optional)',
1833
+ },
1834
+ changelist: {
1835
+ type: 'string',
1836
+ description: 'Show files modified by a specific changelist using -e (optional)',
1837
+ },
1838
+ outputOptions: {
1839
+ type: 'array',
1840
+ items: { type: 'string' },
1841
+ description: 'Raw -O option suffixes such as l, p, r, s (optional)',
1842
+ },
1843
+ limitOptions: {
1844
+ type: 'array',
1845
+ items: { type: 'string' },
1846
+ description: 'Raw -R option suffixes such as c, h, o, u (optional)',
1847
+ },
1848
+ sortOptions: {
1849
+ type: 'array',
1850
+ items: { type: 'string' },
1851
+ description: 'Raw -S option suffixes such as d, h, r, s, t (optional)',
1852
+ },
1436
1853
  workspacePath: {
1437
1854
  type: 'string',
1438
1855
  description: 'Path to workspace directory (optional, defaults to current directory)',
1439
1856
  },
1440
1857
  },
1441
- required: ['filespec'],
1858
+ anyOf: [
1859
+ { required: ['filespec'] },
1860
+ { required: ['filespecs'] },
1861
+ ],
1442
1862
  additionalProperties: false,
1443
1863
  },
1444
1864
  },
@@ -1452,6 +1872,24 @@ class MCPPerforceServer {
1452
1872
  type: 'string',
1453
1873
  description: 'Optional stream path filter',
1454
1874
  },
1875
+ streams: {
1876
+ type: 'array',
1877
+ items: { type: 'string' },
1878
+ description: 'Optional stream path filters to query in one command',
1879
+ },
1880
+ unloaded: {
1881
+ type: 'boolean',
1882
+ description: 'Include unloaded task streams using -U (optional)',
1883
+ },
1884
+ filter: {
1885
+ type: 'string',
1886
+ description: 'Stream filter expression for -F (optional)',
1887
+ },
1888
+ viewMatch: {
1889
+ type: 'array',
1890
+ items: { type: 'string' },
1891
+ description: 'One or more depot paths to pass with --viewmatch (optional)',
1892
+ },
1455
1893
  max: {
1456
1894
  type: 'number',
1457
1895
  description: 'Maximum number of results (optional)',
@@ -1497,6 +1935,11 @@ class MCPPerforceServer {
1497
1935
  type: 'string',
1498
1936
  description: 'Filespec to search in (optional)',
1499
1937
  },
1938
+ filespecs: {
1939
+ type: 'array',
1940
+ items: { type: 'string' },
1941
+ description: 'Filespecs to search in (optional)',
1942
+ },
1500
1943
  caseInsensitive: {
1501
1944
  type: 'boolean',
1502
1945
  description: 'Case insensitive search (optional, defaults to false)',
@@ -1520,10 +1963,31 @@ class MCPPerforceServer {
1520
1963
  type: 'string',
1521
1964
  description: 'Filespec to list (optional, defaults to all files)',
1522
1965
  },
1966
+ filespecs: {
1967
+ type: 'array',
1968
+ items: { type: 'string' },
1969
+ description: 'Filespecs to list in one command (optional)',
1970
+ },
1523
1971
  max: {
1524
1972
  type: 'number',
1525
1973
  description: 'Maximum number of results (optional)',
1526
1974
  },
1975
+ allRevisions: {
1976
+ type: 'boolean',
1977
+ description: 'Display all revisions in range using -a (optional)',
1978
+ },
1979
+ archiveDepot: {
1980
+ type: 'boolean',
1981
+ description: 'Include files in archive depots using -A (optional)',
1982
+ },
1983
+ existingOnly: {
1984
+ type: 'boolean',
1985
+ description: 'Show only revisions available for sync/integrate using -e (optional)',
1986
+ },
1987
+ ignoreCase: {
1988
+ type: 'boolean',
1989
+ description: 'Ignore case of the file argument using -i (optional)',
1990
+ },
1527
1991
  workspacePath: {
1528
1992
  type: 'string',
1529
1993
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -1542,6 +2006,31 @@ class MCPPerforceServer {
1542
2006
  type: 'string',
1543
2007
  description: 'Filespec to list directories for (optional, defaults to all directories)',
1544
2008
  },
2009
+ filespecs: {
2010
+ type: 'array',
2011
+ items: { type: 'string' },
2012
+ description: 'Filespecs to list directories for in one command (optional)',
2013
+ },
2014
+ ignoreCase: {
2015
+ type: 'boolean',
2016
+ description: 'Ignore case using -i (optional; incompatible with onlyClientMapped)',
2017
+ },
2018
+ onlyClientMapped: {
2019
+ type: 'boolean',
2020
+ description: 'List only directories in the current client view using -C (optional)',
2021
+ },
2022
+ includeDeleted: {
2023
+ type: 'boolean',
2024
+ description: 'Include directories containing only deleted files using -D (optional)',
2025
+ },
2026
+ onlyHave: {
2027
+ type: 'boolean',
2028
+ description: 'List directories containing files synced to the current workspace using -H (optional)',
2029
+ },
2030
+ stream: {
2031
+ type: 'string',
2032
+ description: 'Limit to directories mapped in a stream view using -S (optional)',
2033
+ },
1545
2034
  workspacePath: {
1546
2035
  type: 'string',
1547
2036
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -1561,10 +2050,19 @@ class MCPPerforceServer {
1561
2050
  type: 'string',
1562
2051
  description: 'Specific user to show (optional)',
1563
2052
  },
2053
+ users: {
2054
+ type: 'array',
2055
+ items: { type: 'string' },
2056
+ description: 'Specific users to show in one command (optional)',
2057
+ },
1564
2058
  max: {
1565
2059
  type: 'number',
1566
2060
  description: 'Maximum number of results (optional)',
1567
2061
  },
2062
+ includeServiceUsers: {
2063
+ type: 'boolean',
2064
+ description: 'Include service and operator users using -a (optional)',
2065
+ },
1568
2066
  workspacePath: {
1569
2067
  type: 'string',
1570
2068
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -1619,6 +2117,23 @@ class MCPPerforceServer {
1619
2117
  type: 'string',
1620
2118
  description: 'Specific job to show (optional)',
1621
2119
  },
2120
+ files: {
2121
+ type: 'array',
2122
+ items: { type: 'string' },
2123
+ description: 'Optional file/filespec filters for jobs',
2124
+ },
2125
+ jobView: {
2126
+ type: 'string',
2127
+ description: 'Jobview expression for -e (optional)',
2128
+ },
2129
+ includeIntegrated: {
2130
+ type: 'boolean',
2131
+ description: 'Include fixes from integrated changelists using -i (optional)',
2132
+ },
2133
+ reverseOrder: {
2134
+ type: 'boolean',
2135
+ description: 'Reverse job sort order using -r (optional)',
2136
+ },
1622
2137
  max: {
1623
2138
  type: 'number',
1624
2139
  description: 'Maximum number of results (optional)',
@@ -1664,6 +2179,11 @@ class MCPPerforceServer {
1664
2179
  type: 'string',
1665
2180
  description: 'Filter by changelist (optional)',
1666
2181
  },
2182
+ files: {
2183
+ type: 'array',
2184
+ items: { type: 'string' },
2185
+ description: 'Optional file/filespec filters for fixes',
2186
+ },
1667
2187
  workspacePath: {
1668
2188
  type: 'string',
1669
2189
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -1686,6 +2206,39 @@ class MCPPerforceServer {
1686
2206
  type: 'string',
1687
2207
  description: 'Filter by user (optional)',
1688
2208
  },
2209
+ filespec: {
2210
+ type: 'string',
2211
+ description: 'Single filespec filter for labels containing matching files (optional)',
2212
+ },
2213
+ filespecs: {
2214
+ type: 'array',
2215
+ items: { type: 'string' },
2216
+ description: 'Multiple filespec filters for labels in one command (optional)',
2217
+ },
2218
+ nameFilter: {
2219
+ type: 'string',
2220
+ description: 'Label name filter using -e (optional)',
2221
+ },
2222
+ caseInsensitiveNameFilter: {
2223
+ type: 'string',
2224
+ description: 'Case-insensitive label name filter using -E (optional)',
2225
+ },
2226
+ unloaded: {
2227
+ type: 'boolean',
2228
+ description: 'List unloaded labels using -U (optional)',
2229
+ },
2230
+ autoreloadOnly: {
2231
+ type: 'boolean',
2232
+ description: 'List only autoreload labels using -R (optional)',
2233
+ },
2234
+ allServers: {
2235
+ type: 'boolean',
2236
+ description: 'List labels across all servers using -a (optional)',
2237
+ },
2238
+ serverId: {
2239
+ type: 'string',
2240
+ description: 'Limit labels to a specific server using -s (optional)',
2241
+ },
1689
2242
  max: {
1690
2243
  type: 'number',
1691
2244
  description: 'Maximum number of results (optional)',
@@ -1727,6 +2280,31 @@ class MCPPerforceServer {
1727
2280
  type: 'string',
1728
2281
  description: 'Filespec to get sizes for (optional, defaults to current directory)',
1729
2282
  },
2283
+ filespecs: {
2284
+ type: 'array',
2285
+ items: { type: 'string' },
2286
+ description: 'Filespecs to get sizes for in one command (optional)',
2287
+ },
2288
+ allRevisions: {
2289
+ type: 'boolean',
2290
+ description: 'List all revisions in the range using -a (optional)',
2291
+ },
2292
+ shelvedOnly: {
2293
+ type: 'boolean',
2294
+ description: 'Display size info for shelved files only using -S (optional)',
2295
+ },
2296
+ omitLazyCopies: {
2297
+ type: 'boolean',
2298
+ description: 'Omit lazy copies from totals using -z (optional)',
2299
+ },
2300
+ max: {
2301
+ type: 'number',
2302
+ description: 'Limit output to the first max files using -m (optional)',
2303
+ },
2304
+ blockSize: {
2305
+ type: 'number',
2306
+ description: 'Round sizes up to a block size in bytes using -b (optional)',
2307
+ },
1730
2308
  workspacePath: {
1731
2309
  type: 'string',
1732
2310
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -1745,6 +2323,11 @@ class MCPPerforceServer {
1745
2323
  type: 'string',
1746
2324
  description: 'Filespec to check (optional)',
1747
2325
  },
2326
+ filespecs: {
2327
+ type: 'array',
2328
+ items: { type: 'string' },
2329
+ description: 'Filespecs to check in one command (optional)',
2330
+ },
1748
2331
  workspacePath: {
1749
2332
  type: 'string',
1750
2333
  description: 'Path to workspace directory (optional, defaults to current directory)',
@@ -1830,19 +2413,21 @@ class MCPPerforceServer {
1830
2413
  this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1831
2414
  const startTime = Date.now();
1832
2415
  const { name, arguments: args } = request.params;
2416
+ // Normalize tool name: support both dot notation (p4.changes) and underscore (p4_changes)
2417
+ const normalizedName = name.replace(/_/g, '.');
1833
2418
  try {
1834
- log.debug(`Calling tool: ${name}`);
2419
+ log.debug(`Calling tool: ${name}${name !== normalizedName ? ` (normalized to ${normalizedName})` : ''}`);
1835
2420
  // Rate limiting check
1836
- const rateLimitResult = this.context.security.checkRateLimit(request.params.name);
2421
+ const rateLimitResult = this.context.security.checkRateLimit(normalizedName);
1837
2422
  if (!rateLimitResult.allowed) {
1838
- const errorMsg = `Rate limit exceeded for tool ${name}. Try again after ${new Date(rateLimitResult.resetTime).toISOString()}`;
1839
- log.warn(`Rate limit exceeded: ${name}`);
2423
+ const errorMsg = `Rate limit exceeded for tool ${normalizedName}. Try again after ${new Date(rateLimitResult.resetTime).toISOString()}`;
2424
+ log.warn(`Rate limit exceeded: ${normalizedName}`);
1840
2425
  // Audit log the blocked request
1841
2426
  this.context.security.logAuditEntry({
1842
- tool: request.params.name,
2427
+ tool: normalizedName,
1843
2428
  user: 'unknown', // Could be enhanced to extract from P4 config
1844
2429
  client: 'unknown',
1845
- operation: request.params.name,
2430
+ operation: normalizedName,
1846
2431
  args: request.params.arguments || {},
1847
2432
  result: 'blocked',
1848
2433
  errorCode: 'RATE_LIMIT_EXCEEDED',
@@ -1860,18 +2445,18 @@ class MCPPerforceServer {
1860
2445
  }
1861
2446
  }
1862
2447
  const toolArgs = (args || {});
1863
- const execution = await this.executeToolWithCaching(name, toolArgs);
2448
+ const execution = await this.executeToolWithCaching(normalizedName, toolArgs);
1864
2449
  const result = execution.result;
1865
- if (WRITE_TOOLS.has(name) && result && typeof result === 'object' && result.ok) {
2450
+ if (WRITE_TOOLS.has(normalizedName) && result && typeof result === 'object' && result.ok) {
1866
2451
  this.clearReadCache();
1867
2452
  }
1868
- this.recordToolPerformance(name, Date.now() - startTime, result, execution.cacheStatus, this.extractSubcallCounts(result));
2453
+ this.recordToolPerformance(normalizedName, Date.now() - startTime, result, execution.cacheStatus, this.extractSubcallCounts(result));
1869
2454
  // Audit log successful operation
1870
2455
  this.context.security.logAuditEntry({
1871
- tool: name,
2456
+ tool: normalizedName,
1872
2457
  user: result?.configUsed?.P4USER || 'unknown',
1873
2458
  client: result?.configUsed?.P4CLIENT || 'unknown',
1874
- operation: name,
2459
+ operation: normalizedName,
1875
2460
  args: toolArgs,
1876
2461
  result: 'success',
1877
2462
  duration: Date.now() - startTime,
@@ -1881,14 +2466,14 @@ class MCPPerforceServer {
1881
2466
  catch (error) {
1882
2467
  const duration = Date.now() - startTime;
1883
2468
  const errorCode = error instanceof types_js_1.McpError ? error.code : 'INTERNAL_ERROR';
1884
- this.recordToolPerformance(name, duration, { ok: false, error: { code: String(errorCode) } }, 'uncacheable');
2469
+ this.recordToolPerformance(normalizedName, duration, { ok: false, error: { code: String(errorCode) } }, 'uncacheable');
1885
2470
  log.error('Tool execution error:', error);
1886
2471
  // Audit log failed operation
1887
2472
  this.context.security.logAuditEntry({
1888
- tool: name,
2473
+ tool: normalizedName,
1889
2474
  user: 'unknown', // Could be enhanced to extract from context
1890
2475
  client: 'unknown',
1891
- operation: name,
2476
+ operation: normalizedName,
1892
2477
  args: args || {},
1893
2478
  result: 'error',
1894
2479
  errorCode: typeof errorCode === 'string' ? errorCode : errorCode.toString(),
@@ -1919,17 +2504,17 @@ if (require.main === module) {
1919
2504
  }
1920
2505
  // Handle help flag
1921
2506
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
1922
- console.log(`
1923
- MCP Perforce Server v${packageJson.version}
1924
- ===========================
1925
-
1926
- A production-ready MCP (Model Context Protocol) server for Perforce operations.
1927
-
1928
- Usage:
1929
- mcp-perforce-server Start the MCP server (stdio transport)
1930
- mcp-perforce-server --help Show this help message
1931
- mcp-perforce-server --version Show version information
1932
-
2507
+ console.log(`
2508
+ MCP Perforce Server v${packageJson.version}
2509
+ ===========================
2510
+
2511
+ A production-ready MCP (Model Context Protocol) server for Perforce operations.
2512
+
2513
+ Usage:
2514
+ mcp-perforce-server Start the MCP server (stdio transport)
2515
+ mcp-perforce-server --help Show this help message
2516
+ mcp-perforce-server --version Show version information
2517
+
1933
2518
  Environment Variables:
1934
2519
  P4_READONLY_MODE=false Enable write operations (default: read-only enabled)
1935
2520
  P4_DISABLE_DELETE=false Enable delete operations (default: delete disabled)
@@ -1956,23 +2541,23 @@ Compliance & Security:
1956
2541
  P4_ENABLE_MEMORY_LIMITS=true|false Override memory limits (default in fast mode: false)
1957
2542
  P4_ENABLE_INPUT_SANITIZATION=false Disable input sanitization (default: enabled)
1958
2543
  P4_MAX_MEMORY_MB=1024 Memory limit in MB (default: 512)
1959
- P4_AUDIT_RETENTION_DAYS=365 Audit log retention days (default: 90)
1960
- P4_RATE_LIMIT_REQUESTS=100 Max requests per window (default: 100)
1961
- P4_RATE_LIMIT_WINDOW_MS=600000 Rate limit window ms (default: 10min)
1962
- P4_RATE_LIMIT_BLOCK_MS=3600000 Rate limit block duration ms (default: 1hr)
1963
-
1964
- Configuration:
1965
- Place a .p4config file in your project root or parent directories:
1966
-
1967
- P4PORT=your-server:1666
1968
- P4USER=your-username
1969
- P4CLIENT=your-workspace-name
1970
-
1971
- IDE Integration:
1972
- Configure your IDE's MCP client to use this server.
1973
- See README.md for VS Code and Cursor setup instructions.
1974
-
1975
- For more information: https://github.com/iPraBhu/mcp-perforce-server
2544
+ P4_AUDIT_RETENTION_DAYS=365 Audit log retention days (default: 90)
2545
+ P4_RATE_LIMIT_REQUESTS=100 Max requests per window (default: 100)
2546
+ P4_RATE_LIMIT_WINDOW_MS=600000 Rate limit window ms (default: 10min)
2547
+ P4_RATE_LIMIT_BLOCK_MS=3600000 Rate limit block duration ms (default: 1hr)
2548
+
2549
+ Configuration:
2550
+ Place a .p4config file in your project root or parent directories:
2551
+
2552
+ P4PORT=your-server:1666
2553
+ P4USER=your-username
2554
+ P4CLIENT=your-workspace-name
2555
+
2556
+ IDE Integration:
2557
+ Configure your IDE's MCP client to use this server.
2558
+ See README.md for VS Code and Cursor setup instructions.
2559
+
2560
+ For more information: https://github.com/iPraBhu/mcp-perforce-server
1976
2561
  `);
1977
2562
  process.exit(0);
1978
2563
  }