@chrrxs/robloxstudio-mcp 2.12.0 → 2.13.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/index.js CHANGED
@@ -700,9 +700,9 @@ var init_http_server = __esm({
700
700
  compare_instances: (tools, body) => tools.compareInstances(body.instancePathA, body.instancePathB, body.instance_id),
701
701
  get_output_log: (tools, body) => tools.getOutputLog(body.maxEntries, body.messageType, body.instance_id),
702
702
  bulk_set_attributes: (tools, body) => tools.bulkSetAttributes(body.instancePath, body.attributes, body.instance_id),
703
- capture_screenshot: (tools, body) => tools.captureScreenshot(body.instance_id),
703
+ capture_screenshot: (tools, body) => tools.captureScreenshot(body.instance_id, body.format, body.quality),
704
704
  simulate_mouse_input: (tools, body) => tools.simulateMouseInput(body.action, body.x, body.y, body.button, body.scrollDirection, body.target, body.instance_id),
705
- simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.target, body.instance_id),
705
+ simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.text, body.target, body.instance_id),
706
706
  character_navigation: (tools, body) => tools.characterNavigation(body.position, body.instancePath, body.waitForCompletion, body.timeout, body.target, body.instance_id),
707
707
  get_memory_breakdown: (tools, body) => tools.getMemoryBreakdown(body.target, body.tags, body.instance_id),
708
708
  export_rbxm: (tools, body) => tools.exportRbxm(body.instance_paths, body.output_path, body.target, body.instance_id),
@@ -1468,6 +1468,963 @@ var init_roblox_cookie_client = __esm({
1468
1468
  }
1469
1469
  });
1470
1470
 
1471
+ // ../core/dist/jpeg-encoder.js
1472
+ function rgbaToJpeg(rgba, width, height, quality = 80) {
1473
+ if (width <= 0 || height <= 0)
1474
+ throw new Error(`Invalid JPEG dimensions: ${width}x${height}`);
1475
+ const expected = width * height * 4;
1476
+ if (rgba.length < expected)
1477
+ throw new Error(`Buffer too small: got ${rgba.length}, need ${expected}`);
1478
+ const encoder = new JpegEncoder(quality);
1479
+ return encoder.encode(rgba, width, height);
1480
+ }
1481
+ var ZIGZAG, STD_DC_LUMINANCE_NRCODES, STD_DC_LUMINANCE_VALUES, STD_AC_LUMINANCE_NRCODES, STD_AC_LUMINANCE_VALUES, STD_DC_CHROMINANCE_NRCODES, STD_DC_CHROMINANCE_VALUES, STD_AC_CHROMINANCE_NRCODES, STD_AC_CHROMINANCE_VALUES, JpegEncoder;
1482
+ var init_jpeg_encoder = __esm({
1483
+ "../core/dist/jpeg-encoder.js"() {
1484
+ "use strict";
1485
+ ZIGZAG = [
1486
+ 0,
1487
+ 1,
1488
+ 5,
1489
+ 6,
1490
+ 14,
1491
+ 15,
1492
+ 27,
1493
+ 28,
1494
+ 2,
1495
+ 4,
1496
+ 7,
1497
+ 13,
1498
+ 16,
1499
+ 26,
1500
+ 29,
1501
+ 42,
1502
+ 3,
1503
+ 8,
1504
+ 12,
1505
+ 17,
1506
+ 25,
1507
+ 30,
1508
+ 41,
1509
+ 43,
1510
+ 9,
1511
+ 11,
1512
+ 18,
1513
+ 24,
1514
+ 31,
1515
+ 40,
1516
+ 44,
1517
+ 53,
1518
+ 10,
1519
+ 19,
1520
+ 23,
1521
+ 32,
1522
+ 39,
1523
+ 45,
1524
+ 52,
1525
+ 54,
1526
+ 20,
1527
+ 22,
1528
+ 33,
1529
+ 38,
1530
+ 46,
1531
+ 51,
1532
+ 55,
1533
+ 60,
1534
+ 21,
1535
+ 34,
1536
+ 37,
1537
+ 47,
1538
+ 50,
1539
+ 56,
1540
+ 59,
1541
+ 61,
1542
+ 35,
1543
+ 36,
1544
+ 48,
1545
+ 49,
1546
+ 57,
1547
+ 58,
1548
+ 62,
1549
+ 63
1550
+ ];
1551
+ STD_DC_LUMINANCE_NRCODES = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0];
1552
+ STD_DC_LUMINANCE_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
1553
+ STD_AC_LUMINANCE_NRCODES = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125];
1554
+ STD_AC_LUMINANCE_VALUES = [
1555
+ 1,
1556
+ 2,
1557
+ 3,
1558
+ 0,
1559
+ 4,
1560
+ 17,
1561
+ 5,
1562
+ 18,
1563
+ 33,
1564
+ 49,
1565
+ 65,
1566
+ 6,
1567
+ 19,
1568
+ 81,
1569
+ 97,
1570
+ 7,
1571
+ 34,
1572
+ 113,
1573
+ 20,
1574
+ 50,
1575
+ 129,
1576
+ 145,
1577
+ 161,
1578
+ 8,
1579
+ 35,
1580
+ 66,
1581
+ 177,
1582
+ 193,
1583
+ 21,
1584
+ 82,
1585
+ 209,
1586
+ 240,
1587
+ 36,
1588
+ 51,
1589
+ 98,
1590
+ 114,
1591
+ 130,
1592
+ 9,
1593
+ 10,
1594
+ 22,
1595
+ 23,
1596
+ 24,
1597
+ 25,
1598
+ 26,
1599
+ 37,
1600
+ 38,
1601
+ 39,
1602
+ 40,
1603
+ 41,
1604
+ 42,
1605
+ 52,
1606
+ 53,
1607
+ 54,
1608
+ 55,
1609
+ 56,
1610
+ 57,
1611
+ 58,
1612
+ 67,
1613
+ 68,
1614
+ 69,
1615
+ 70,
1616
+ 71,
1617
+ 72,
1618
+ 73,
1619
+ 74,
1620
+ 83,
1621
+ 84,
1622
+ 85,
1623
+ 86,
1624
+ 87,
1625
+ 88,
1626
+ 89,
1627
+ 90,
1628
+ 99,
1629
+ 100,
1630
+ 101,
1631
+ 102,
1632
+ 103,
1633
+ 104,
1634
+ 105,
1635
+ 106,
1636
+ 115,
1637
+ 116,
1638
+ 117,
1639
+ 118,
1640
+ 119,
1641
+ 120,
1642
+ 121,
1643
+ 122,
1644
+ 131,
1645
+ 132,
1646
+ 133,
1647
+ 134,
1648
+ 135,
1649
+ 136,
1650
+ 137,
1651
+ 138,
1652
+ 146,
1653
+ 147,
1654
+ 148,
1655
+ 149,
1656
+ 150,
1657
+ 151,
1658
+ 152,
1659
+ 153,
1660
+ 154,
1661
+ 162,
1662
+ 163,
1663
+ 164,
1664
+ 165,
1665
+ 166,
1666
+ 167,
1667
+ 168,
1668
+ 169,
1669
+ 170,
1670
+ 178,
1671
+ 179,
1672
+ 180,
1673
+ 181,
1674
+ 182,
1675
+ 183,
1676
+ 184,
1677
+ 185,
1678
+ 186,
1679
+ 194,
1680
+ 195,
1681
+ 196,
1682
+ 197,
1683
+ 198,
1684
+ 199,
1685
+ 200,
1686
+ 201,
1687
+ 202,
1688
+ 210,
1689
+ 211,
1690
+ 212,
1691
+ 213,
1692
+ 214,
1693
+ 215,
1694
+ 216,
1695
+ 217,
1696
+ 218,
1697
+ 225,
1698
+ 226,
1699
+ 227,
1700
+ 228,
1701
+ 229,
1702
+ 230,
1703
+ 231,
1704
+ 232,
1705
+ 233,
1706
+ 234,
1707
+ 241,
1708
+ 242,
1709
+ 243,
1710
+ 244,
1711
+ 245,
1712
+ 246,
1713
+ 247,
1714
+ 248,
1715
+ 249,
1716
+ 250
1717
+ ];
1718
+ STD_DC_CHROMINANCE_NRCODES = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0];
1719
+ STD_DC_CHROMINANCE_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
1720
+ STD_AC_CHROMINANCE_NRCODES = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119];
1721
+ STD_AC_CHROMINANCE_VALUES = [
1722
+ 0,
1723
+ 1,
1724
+ 2,
1725
+ 3,
1726
+ 17,
1727
+ 4,
1728
+ 5,
1729
+ 33,
1730
+ 49,
1731
+ 6,
1732
+ 18,
1733
+ 65,
1734
+ 81,
1735
+ 7,
1736
+ 97,
1737
+ 113,
1738
+ 19,
1739
+ 34,
1740
+ 50,
1741
+ 129,
1742
+ 8,
1743
+ 20,
1744
+ 66,
1745
+ 145,
1746
+ 161,
1747
+ 177,
1748
+ 193,
1749
+ 9,
1750
+ 35,
1751
+ 51,
1752
+ 82,
1753
+ 240,
1754
+ 21,
1755
+ 98,
1756
+ 114,
1757
+ 209,
1758
+ 10,
1759
+ 22,
1760
+ 36,
1761
+ 52,
1762
+ 225,
1763
+ 37,
1764
+ 241,
1765
+ 23,
1766
+ 24,
1767
+ 25,
1768
+ 26,
1769
+ 38,
1770
+ 39,
1771
+ 40,
1772
+ 41,
1773
+ 42,
1774
+ 53,
1775
+ 54,
1776
+ 55,
1777
+ 56,
1778
+ 57,
1779
+ 58,
1780
+ 67,
1781
+ 68,
1782
+ 69,
1783
+ 70,
1784
+ 71,
1785
+ 72,
1786
+ 73,
1787
+ 74,
1788
+ 83,
1789
+ 84,
1790
+ 85,
1791
+ 86,
1792
+ 87,
1793
+ 88,
1794
+ 89,
1795
+ 90,
1796
+ 99,
1797
+ 100,
1798
+ 101,
1799
+ 102,
1800
+ 103,
1801
+ 104,
1802
+ 105,
1803
+ 106,
1804
+ 115,
1805
+ 116,
1806
+ 117,
1807
+ 118,
1808
+ 119,
1809
+ 120,
1810
+ 121,
1811
+ 122,
1812
+ 130,
1813
+ 131,
1814
+ 132,
1815
+ 133,
1816
+ 134,
1817
+ 135,
1818
+ 136,
1819
+ 137,
1820
+ 138,
1821
+ 146,
1822
+ 147,
1823
+ 148,
1824
+ 149,
1825
+ 150,
1826
+ 151,
1827
+ 152,
1828
+ 153,
1829
+ 154,
1830
+ 162,
1831
+ 163,
1832
+ 164,
1833
+ 165,
1834
+ 166,
1835
+ 167,
1836
+ 168,
1837
+ 169,
1838
+ 170,
1839
+ 178,
1840
+ 179,
1841
+ 180,
1842
+ 181,
1843
+ 182,
1844
+ 183,
1845
+ 184,
1846
+ 185,
1847
+ 186,
1848
+ 194,
1849
+ 195,
1850
+ 196,
1851
+ 197,
1852
+ 198,
1853
+ 199,
1854
+ 200,
1855
+ 201,
1856
+ 202,
1857
+ 210,
1858
+ 211,
1859
+ 212,
1860
+ 213,
1861
+ 214,
1862
+ 215,
1863
+ 216,
1864
+ 217,
1865
+ 218,
1866
+ 226,
1867
+ 227,
1868
+ 228,
1869
+ 229,
1870
+ 230,
1871
+ 231,
1872
+ 232,
1873
+ 233,
1874
+ 234,
1875
+ 242,
1876
+ 243,
1877
+ 244,
1878
+ 245,
1879
+ 246,
1880
+ 247,
1881
+ 248,
1882
+ 249,
1883
+ 250
1884
+ ];
1885
+ JpegEncoder = class {
1886
+ YTable = new Int32Array(64);
1887
+ UVTable = new Int32Array(64);
1888
+ fdtbl_Y = new Float32Array(64);
1889
+ fdtbl_UV = new Float32Array(64);
1890
+ YDC_HT;
1891
+ UVDC_HT;
1892
+ YAC_HT;
1893
+ UVAC_HT;
1894
+ bitcode = new Array(65535);
1895
+ category = new Int32Array(65535);
1896
+ outputfDCTQuant = new Float32Array(64);
1897
+ DU = new Int32Array(64);
1898
+ // RGB->YUV lookup tables
1899
+ RGB_YUV_TABLE = new Int32Array(2048);
1900
+ byteout = [];
1901
+ bytenew = 0;
1902
+ bytepos = 7;
1903
+ constructor(quality) {
1904
+ this.initHuffmanTbl();
1905
+ this.initCategoryNumber();
1906
+ this.initRGBYUVTable();
1907
+ this.setQuality(quality);
1908
+ }
1909
+ initQuantTables(sf) {
1910
+ const YQT = [
1911
+ 16,
1912
+ 11,
1913
+ 10,
1914
+ 16,
1915
+ 24,
1916
+ 40,
1917
+ 51,
1918
+ 61,
1919
+ 12,
1920
+ 12,
1921
+ 14,
1922
+ 19,
1923
+ 26,
1924
+ 58,
1925
+ 60,
1926
+ 55,
1927
+ 14,
1928
+ 13,
1929
+ 16,
1930
+ 24,
1931
+ 40,
1932
+ 57,
1933
+ 69,
1934
+ 56,
1935
+ 14,
1936
+ 17,
1937
+ 22,
1938
+ 29,
1939
+ 51,
1940
+ 87,
1941
+ 80,
1942
+ 62,
1943
+ 18,
1944
+ 22,
1945
+ 37,
1946
+ 56,
1947
+ 68,
1948
+ 109,
1949
+ 103,
1950
+ 77,
1951
+ 24,
1952
+ 35,
1953
+ 55,
1954
+ 64,
1955
+ 81,
1956
+ 104,
1957
+ 113,
1958
+ 92,
1959
+ 49,
1960
+ 64,
1961
+ 78,
1962
+ 87,
1963
+ 103,
1964
+ 121,
1965
+ 120,
1966
+ 101,
1967
+ 72,
1968
+ 92,
1969
+ 95,
1970
+ 98,
1971
+ 112,
1972
+ 100,
1973
+ 103,
1974
+ 99
1975
+ ];
1976
+ for (let i = 0; i < 64; i++) {
1977
+ let t = Math.floor((YQT[i] * sf + 50) / 100);
1978
+ if (t < 1)
1979
+ t = 1;
1980
+ else if (t > 255)
1981
+ t = 255;
1982
+ this.YTable[ZIGZAG[i]] = t;
1983
+ }
1984
+ const UVQT = [
1985
+ 17,
1986
+ 18,
1987
+ 24,
1988
+ 47,
1989
+ 99,
1990
+ 99,
1991
+ 99,
1992
+ 99,
1993
+ 18,
1994
+ 21,
1995
+ 26,
1996
+ 66,
1997
+ 99,
1998
+ 99,
1999
+ 99,
2000
+ 99,
2001
+ 24,
2002
+ 26,
2003
+ 56,
2004
+ 99,
2005
+ 99,
2006
+ 99,
2007
+ 99,
2008
+ 99,
2009
+ 47,
2010
+ 66,
2011
+ 99,
2012
+ 99,
2013
+ 99,
2014
+ 99,
2015
+ 99,
2016
+ 99,
2017
+ 99,
2018
+ 99,
2019
+ 99,
2020
+ 99,
2021
+ 99,
2022
+ 99,
2023
+ 99,
2024
+ 99,
2025
+ 99,
2026
+ 99,
2027
+ 99,
2028
+ 99,
2029
+ 99,
2030
+ 99,
2031
+ 99,
2032
+ 99,
2033
+ 99,
2034
+ 99,
2035
+ 99,
2036
+ 99,
2037
+ 99,
2038
+ 99,
2039
+ 99,
2040
+ 99,
2041
+ 99,
2042
+ 99,
2043
+ 99,
2044
+ 99,
2045
+ 99,
2046
+ 99,
2047
+ 99,
2048
+ 99
2049
+ ];
2050
+ for (let j = 0; j < 64; j++) {
2051
+ let u = Math.floor((UVQT[j] * sf + 50) / 100);
2052
+ if (u < 1)
2053
+ u = 1;
2054
+ else if (u > 255)
2055
+ u = 255;
2056
+ this.UVTable[ZIGZAG[j]] = u;
2057
+ }
2058
+ const aasf = [
2059
+ 1,
2060
+ 1.387039845,
2061
+ 1.306562965,
2062
+ 1.175875602,
2063
+ 1,
2064
+ 0.785694958,
2065
+ 0.5411961,
2066
+ 0.275899379
2067
+ ];
2068
+ let k = 0;
2069
+ for (let row = 0; row < 8; row++) {
2070
+ for (let col = 0; col < 8; col++) {
2071
+ this.fdtbl_Y[k] = 1 / (this.YTable[ZIGZAG[k]] * aasf[row] * aasf[col] * 8);
2072
+ this.fdtbl_UV[k] = 1 / (this.UVTable[ZIGZAG[k]] * aasf[row] * aasf[col] * 8);
2073
+ k++;
2074
+ }
2075
+ }
2076
+ }
2077
+ computeHuffmanTbl(nrcodes, std_table) {
2078
+ let codevalue = 0;
2079
+ let pos_in_table = 0;
2080
+ const HT = [];
2081
+ for (let k = 1; k <= 16; k++) {
2082
+ for (let j = 1; j <= nrcodes[k]; j++) {
2083
+ HT[std_table[pos_in_table]] = { code: codevalue, length: k };
2084
+ pos_in_table++;
2085
+ codevalue++;
2086
+ }
2087
+ codevalue *= 2;
2088
+ }
2089
+ return HT;
2090
+ }
2091
+ initHuffmanTbl() {
2092
+ this.YDC_HT = this.computeHuffmanTbl(STD_DC_LUMINANCE_NRCODES, STD_DC_LUMINANCE_VALUES);
2093
+ this.UVDC_HT = this.computeHuffmanTbl(STD_DC_CHROMINANCE_NRCODES, STD_DC_CHROMINANCE_VALUES);
2094
+ this.YAC_HT = this.computeHuffmanTbl(STD_AC_LUMINANCE_NRCODES, STD_AC_LUMINANCE_VALUES);
2095
+ this.UVAC_HT = this.computeHuffmanTbl(STD_AC_CHROMINANCE_NRCODES, STD_AC_CHROMINANCE_VALUES);
2096
+ }
2097
+ initCategoryNumber() {
2098
+ let nrlower = 1;
2099
+ let nrupper = 2;
2100
+ for (let cat = 1; cat <= 15; cat++) {
2101
+ for (let nr = nrlower; nr < nrupper; nr++) {
2102
+ this.category[32767 + nr] = cat;
2103
+ this.bitcode[32767 + nr] = { length: cat, code: nr };
2104
+ }
2105
+ for (let nrneg = -(nrupper - 1); nrneg <= -nrlower; nrneg++) {
2106
+ this.category[32767 + nrneg] = cat;
2107
+ this.bitcode[32767 + nrneg] = { length: cat, code: nrupper - 1 + nrneg };
2108
+ }
2109
+ nrlower <<= 1;
2110
+ nrupper <<= 1;
2111
+ }
2112
+ }
2113
+ initRGBYUVTable() {
2114
+ for (let i = 0; i < 256; i++) {
2115
+ this.RGB_YUV_TABLE[i] = 19595 * i;
2116
+ this.RGB_YUV_TABLE[i + 256 >> 0] = 38470 * i;
2117
+ this.RGB_YUV_TABLE[i + 512 >> 0] = 7471 * i + 32768;
2118
+ this.RGB_YUV_TABLE[i + 768 >> 0] = -11059 * i;
2119
+ this.RGB_YUV_TABLE[i + 1024 >> 0] = -21709 * i;
2120
+ this.RGB_YUV_TABLE[i + 1280 >> 0] = 32768 * i + 8421375;
2121
+ this.RGB_YUV_TABLE[i + 1536 >> 0] = -27439 * i;
2122
+ this.RGB_YUV_TABLE[i + 1792 >> 0] = -5329 * i;
2123
+ }
2124
+ }
2125
+ setQuality(quality) {
2126
+ let q = quality;
2127
+ if (q <= 0)
2128
+ q = 1;
2129
+ if (q > 100)
2130
+ q = 100;
2131
+ const sf = q < 50 ? Math.floor(5e3 / q) : Math.floor(200 - q * 2);
2132
+ this.initQuantTables(sf);
2133
+ }
2134
+ writeBits(bs) {
2135
+ const value = bs.code;
2136
+ let posval = bs.length - 1;
2137
+ while (posval >= 0) {
2138
+ if (value & 1 << posval) {
2139
+ this.bytenew |= 1 << this.bytepos;
2140
+ }
2141
+ posval--;
2142
+ this.bytepos--;
2143
+ if (this.bytepos < 0) {
2144
+ if (this.bytenew === 255) {
2145
+ this.writeByte(255);
2146
+ this.writeByte(0);
2147
+ } else {
2148
+ this.writeByte(this.bytenew);
2149
+ }
2150
+ this.bytepos = 7;
2151
+ this.bytenew = 0;
2152
+ }
2153
+ }
2154
+ }
2155
+ writeByte(value) {
2156
+ this.byteout.push(value & 255);
2157
+ }
2158
+ writeWord(value) {
2159
+ this.writeByte(value >> 8 & 255);
2160
+ this.writeByte(value & 255);
2161
+ }
2162
+ fDCTQuant(data, fdtbl) {
2163
+ let d0, d1, d2, d3, d4, d5, d6, d7;
2164
+ let dataOff = 0;
2165
+ const I8 = 8;
2166
+ const I64 = 64;
2167
+ for (let i = 0; i < I8; ++i) {
2168
+ d0 = data[dataOff];
2169
+ d1 = data[dataOff + 1];
2170
+ d2 = data[dataOff + 2];
2171
+ d3 = data[dataOff + 3];
2172
+ d4 = data[dataOff + 4];
2173
+ d5 = data[dataOff + 5];
2174
+ d6 = data[dataOff + 6];
2175
+ d7 = data[dataOff + 7];
2176
+ const tmp0 = d0 + d7;
2177
+ const tmp7 = d0 - d7;
2178
+ const tmp1 = d1 + d6;
2179
+ const tmp6 = d1 - d6;
2180
+ const tmp2 = d2 + d5;
2181
+ const tmp5 = d2 - d5;
2182
+ const tmp3 = d3 + d4;
2183
+ const tmp4 = d3 - d4;
2184
+ let tmp10 = tmp0 + tmp3;
2185
+ const tmp13 = tmp0 - tmp3;
2186
+ let tmp11 = tmp1 + tmp2;
2187
+ let tmp12 = tmp1 - tmp2;
2188
+ data[dataOff] = tmp10 + tmp11;
2189
+ data[dataOff + 4] = tmp10 - tmp11;
2190
+ const z1 = (tmp12 + tmp13) * 0.707106781;
2191
+ data[dataOff + 2] = tmp13 + z1;
2192
+ data[dataOff + 6] = tmp13 - z1;
2193
+ tmp10 = tmp4 + tmp5;
2194
+ tmp11 = tmp5 + tmp6;
2195
+ tmp12 = tmp6 + tmp7;
2196
+ const z5 = (tmp10 - tmp12) * 0.382683433;
2197
+ const z2 = 0.5411961 * tmp10 + z5;
2198
+ const z4 = 1.306562965 * tmp12 + z5;
2199
+ const z3 = tmp11 * 0.707106781;
2200
+ const z11 = tmp7 + z3;
2201
+ const z13 = tmp7 - z3;
2202
+ data[dataOff + 5] = z13 + z2;
2203
+ data[dataOff + 3] = z13 - z2;
2204
+ data[dataOff + 1] = z11 + z4;
2205
+ data[dataOff + 7] = z11 - z4;
2206
+ dataOff += 8;
2207
+ }
2208
+ dataOff = 0;
2209
+ for (let i = 0; i < I8; ++i) {
2210
+ d0 = data[dataOff];
2211
+ d1 = data[dataOff + 8];
2212
+ d2 = data[dataOff + 16];
2213
+ d3 = data[dataOff + 24];
2214
+ d4 = data[dataOff + 32];
2215
+ d5 = data[dataOff + 40];
2216
+ d6 = data[dataOff + 48];
2217
+ d7 = data[dataOff + 56];
2218
+ const tmp0p2 = d0 + d7;
2219
+ const tmp7p2 = d0 - d7;
2220
+ const tmp1p2 = d1 + d6;
2221
+ const tmp6p2 = d1 - d6;
2222
+ const tmp2p2 = d2 + d5;
2223
+ const tmp5p2 = d2 - d5;
2224
+ const tmp3p2 = d3 + d4;
2225
+ const tmp4p2 = d3 - d4;
2226
+ let tmp10p2 = tmp0p2 + tmp3p2;
2227
+ const tmp13p2 = tmp0p2 - tmp3p2;
2228
+ let tmp11p2 = tmp1p2 + tmp2p2;
2229
+ let tmp12p2 = tmp1p2 - tmp2p2;
2230
+ data[dataOff] = tmp10p2 + tmp11p2;
2231
+ data[dataOff + 32] = tmp10p2 - tmp11p2;
2232
+ const z1p2 = (tmp12p2 + tmp13p2) * 0.707106781;
2233
+ data[dataOff + 16] = tmp13p2 + z1p2;
2234
+ data[dataOff + 48] = tmp13p2 - z1p2;
2235
+ tmp10p2 = tmp4p2 + tmp5p2;
2236
+ tmp11p2 = tmp5p2 + tmp6p2;
2237
+ tmp12p2 = tmp6p2 + tmp7p2;
2238
+ const z5p2 = (tmp10p2 - tmp12p2) * 0.382683433;
2239
+ const z2p2 = 0.5411961 * tmp10p2 + z5p2;
2240
+ const z4p2 = 1.306562965 * tmp12p2 + z5p2;
2241
+ const z3p2 = tmp11p2 * 0.707106781;
2242
+ const z11p2 = tmp7p2 + z3p2;
2243
+ const z13p2 = tmp7p2 - z3p2;
2244
+ data[dataOff + 40] = z13p2 + z2p2;
2245
+ data[dataOff + 24] = z13p2 - z2p2;
2246
+ data[dataOff + 8] = z11p2 + z4p2;
2247
+ data[dataOff + 56] = z11p2 - z4p2;
2248
+ dataOff++;
2249
+ }
2250
+ for (let i = 0; i < I64; ++i) {
2251
+ const fDCTVal = data[i] * fdtbl[i];
2252
+ this.outputfDCTQuant[i] = fDCTVal > 0 ? fDCTVal + 0.5 | 0 : fDCTVal - 0.5 | 0;
2253
+ }
2254
+ return this.outputfDCTQuant;
2255
+ }
2256
+ processDU(CDU, fdtbl, DC, HTDC, HTAC) {
2257
+ const EOB = HTAC[0];
2258
+ const M16zeroes = HTAC[240];
2259
+ let pos;
2260
+ const I16 = 16;
2261
+ const I63 = 63;
2262
+ const I64 = 64;
2263
+ const DU_DCT = this.fDCTQuant(CDU, fdtbl);
2264
+ for (let j = 0; j < I64; ++j) {
2265
+ this.DU[ZIGZAG[j]] = DU_DCT[j];
2266
+ }
2267
+ const Diff = this.DU[0] - DC;
2268
+ DC = this.DU[0];
2269
+ if (Diff === 0) {
2270
+ this.writeBits(HTDC[0]);
2271
+ } else {
2272
+ pos = 32767 + Diff;
2273
+ this.writeBits(HTDC[this.category[pos]]);
2274
+ this.writeBits(this.bitcode[pos]);
2275
+ }
2276
+ let end0pos = 63;
2277
+ for (; end0pos > 0 && this.DU[end0pos] === 0; end0pos--) {
2278
+ }
2279
+ if (end0pos === 0) {
2280
+ this.writeBits(EOB);
2281
+ return DC;
2282
+ }
2283
+ let i = 1;
2284
+ let lng;
2285
+ while (i <= end0pos) {
2286
+ const startpos = i;
2287
+ for (; this.DU[i] === 0 && i <= end0pos; ++i) {
2288
+ }
2289
+ let nrzeroes = i - startpos;
2290
+ if (nrzeroes >= I16) {
2291
+ lng = nrzeroes >> 4;
2292
+ for (let nrmarker = 1; nrmarker <= lng; ++nrmarker)
2293
+ this.writeBits(M16zeroes);
2294
+ nrzeroes = nrzeroes & 15;
2295
+ }
2296
+ pos = 32767 + this.DU[i];
2297
+ this.writeBits(HTAC[(nrzeroes << 4) + this.category[pos]]);
2298
+ this.writeBits(this.bitcode[pos]);
2299
+ i++;
2300
+ }
2301
+ if (end0pos !== I63) {
2302
+ this.writeBits(EOB);
2303
+ }
2304
+ return DC;
2305
+ }
2306
+ encode(rgba, width, height) {
2307
+ this.byteout = [];
2308
+ this.bytenew = 0;
2309
+ this.bytepos = 7;
2310
+ this.writeWord(65496);
2311
+ this.writeWord(65504);
2312
+ this.writeWord(16);
2313
+ this.writeByte(74);
2314
+ this.writeByte(70);
2315
+ this.writeByte(73);
2316
+ this.writeByte(70);
2317
+ this.writeByte(0);
2318
+ this.writeByte(1);
2319
+ this.writeByte(1);
2320
+ this.writeByte(0);
2321
+ this.writeWord(1);
2322
+ this.writeWord(1);
2323
+ this.writeByte(0);
2324
+ this.writeByte(0);
2325
+ this.writeWord(65499);
2326
+ this.writeWord(132);
2327
+ this.writeByte(0);
2328
+ for (let i = 0; i < 64; i++)
2329
+ this.writeByte(this.YTable[i]);
2330
+ this.writeByte(1);
2331
+ for (let j = 0; j < 64; j++)
2332
+ this.writeByte(this.UVTable[j]);
2333
+ this.writeWord(65472);
2334
+ this.writeWord(17);
2335
+ this.writeByte(8);
2336
+ this.writeWord(height);
2337
+ this.writeWord(width);
2338
+ this.writeByte(3);
2339
+ this.writeByte(1);
2340
+ this.writeByte(17);
2341
+ this.writeByte(0);
2342
+ this.writeByte(2);
2343
+ this.writeByte(17);
2344
+ this.writeByte(1);
2345
+ this.writeByte(3);
2346
+ this.writeByte(17);
2347
+ this.writeByte(1);
2348
+ this.writeWord(65476);
2349
+ this.writeWord(418);
2350
+ this.writeByte(0);
2351
+ for (let i = 0; i < 16; i++)
2352
+ this.writeByte(STD_DC_LUMINANCE_NRCODES[i + 1]);
2353
+ for (let i = 0; i <= 11; i++)
2354
+ this.writeByte(STD_DC_LUMINANCE_VALUES[i]);
2355
+ this.writeByte(16);
2356
+ for (let i = 0; i < 16; i++)
2357
+ this.writeByte(STD_AC_LUMINANCE_NRCODES[i + 1]);
2358
+ for (let i = 0; i <= 161; i++)
2359
+ this.writeByte(STD_AC_LUMINANCE_VALUES[i]);
2360
+ this.writeByte(1);
2361
+ for (let i = 0; i < 16; i++)
2362
+ this.writeByte(STD_DC_CHROMINANCE_NRCODES[i + 1]);
2363
+ for (let i = 0; i <= 11; i++)
2364
+ this.writeByte(STD_DC_CHROMINANCE_VALUES[i]);
2365
+ this.writeByte(17);
2366
+ for (let i = 0; i < 16; i++)
2367
+ this.writeByte(STD_AC_CHROMINANCE_NRCODES[i + 1]);
2368
+ for (let i = 0; i <= 161; i++)
2369
+ this.writeByte(STD_AC_CHROMINANCE_VALUES[i]);
2370
+ this.writeWord(65498);
2371
+ this.writeWord(12);
2372
+ this.writeByte(3);
2373
+ this.writeByte(1);
2374
+ this.writeByte(0);
2375
+ this.writeByte(2);
2376
+ this.writeByte(17);
2377
+ this.writeByte(3);
2378
+ this.writeByte(17);
2379
+ this.writeByte(0);
2380
+ this.writeByte(63);
2381
+ this.writeByte(0);
2382
+ let DCY = 0;
2383
+ let DCU = 0;
2384
+ let DCV = 0;
2385
+ this.bytenew = 0;
2386
+ this.bytepos = 7;
2387
+ const YDU = new Float32Array(64);
2388
+ const UDU = new Float32Array(64);
2389
+ const VDU = new Float32Array(64);
2390
+ const quadWidth = width * 4;
2391
+ const fdtbl_Y = this.fdtbl_Y;
2392
+ const fdtbl_UV = this.fdtbl_UV;
2393
+ const RGB_YUV_TABLE = this.RGB_YUV_TABLE;
2394
+ for (let y = 0; y < height; y += 8) {
2395
+ for (let x = 0; x < width; x += 8) {
2396
+ let start = quadWidth * y + x * 4;
2397
+ for (let pos = 0; pos < 64; pos++) {
2398
+ const row = pos >> 3;
2399
+ const col = (pos & 7) * 4;
2400
+ let p = start + row * quadWidth + col;
2401
+ if (y + row >= height)
2402
+ p -= quadWidth * (y + row - height + 1);
2403
+ if (x + (col >> 2) >= width)
2404
+ p -= 4 * (x + (col >> 2) - width + 1);
2405
+ const r = rgba[p];
2406
+ const g = rgba[p + 1];
2407
+ const b = rgba[p + 2];
2408
+ YDU[pos] = (RGB_YUV_TABLE[r] + RGB_YUV_TABLE[g + 256] + RGB_YUV_TABLE[b + 512] >> 16) - 128;
2409
+ UDU[pos] = (RGB_YUV_TABLE[r + 768] + RGB_YUV_TABLE[g + 1024] + RGB_YUV_TABLE[b + 1280] >> 16) - 128;
2410
+ VDU[pos] = (RGB_YUV_TABLE[r + 1280] + RGB_YUV_TABLE[g + 1536] + RGB_YUV_TABLE[b + 1792] >> 16) - 128;
2411
+ }
2412
+ DCY = this.processDU(YDU, fdtbl_Y, DCY, this.YDC_HT, this.YAC_HT);
2413
+ DCU = this.processDU(UDU, fdtbl_UV, DCU, this.UVDC_HT, this.UVAC_HT);
2414
+ DCV = this.processDU(VDU, fdtbl_UV, DCV, this.UVDC_HT, this.UVAC_HT);
2415
+ }
2416
+ }
2417
+ if (this.bytepos >= 0) {
2418
+ const fillbits = { length: this.bytepos + 1, code: (1 << this.bytepos + 1) - 1 };
2419
+ this.writeBits(fillbits);
2420
+ }
2421
+ this.writeWord(65497);
2422
+ return Buffer.from(this.byteout);
2423
+ }
2424
+ };
2425
+ }
2426
+ });
2427
+
1471
2428
  // ../core/dist/png-encoder.js
1472
2429
  import { deflateSync } from "zlib";
1473
2430
  function crc32(buf) {
@@ -1531,12 +2488,18 @@ var init_png_encoder = __esm({
1531
2488
  import * as fs from "fs";
1532
2489
  import * as os from "os";
1533
2490
  import * as path from "path";
1534
- function encodePngFromRgbaResponse(response) {
2491
+ function encodeImageFromRgbaResponse(response, format, quality) {
1535
2492
  if (!response.data || response.width === void 0 || response.height === void 0) {
1536
2493
  throw new Error("Render response missing data, width, or height");
1537
2494
  }
1538
2495
  const rgbaBuffer = Buffer.from(response.data, "base64");
1539
- return rgbaToPng(rgbaBuffer, response.width, response.height);
2496
+ if (format === "png") {
2497
+ return { buffer: rgbaToPng(rgbaBuffer, response.width, response.height), mimeType: "image/png" };
2498
+ }
2499
+ return {
2500
+ buffer: rgbaToJpeg(rgbaBuffer, response.width, response.height, quality),
2501
+ mimeType: "image/jpeg"
2502
+ };
1540
2503
  }
1541
2504
  function luaLongQuote(s) {
1542
2505
  let level = 0;
@@ -1760,6 +2723,7 @@ var init_tools = __esm({
1760
2723
  init_build_executor();
1761
2724
  init_opencloud_client();
1762
2725
  init_roblox_cookie_client();
2726
+ init_jpeg_encoder();
1763
2727
  init_png_encoder();
1764
2728
  SERVER_LOCAL_NAME = "__MCP_ServerEvalLocal";
1765
2729
  CLIENT_LOCAL_NAME = "__MCP_ClientEvalBridge";
@@ -1796,6 +2760,20 @@ var init_tools = __esm({
1796
2760
  }
1797
2761
  return this.client.request(endpoint, data, r.targetInstanceId, r.targetRole);
1798
2762
  }
2763
+ // Resolves which connected place a tool should target and whether a playtest
2764
+ // CLIENT peer is present on it. Used by capture/input to auto-route to the
2765
+ // running client (where the live viewport + input pipeline are) without the
2766
+ // caller having to pass target. Throws RoutingFailure with the standard
2767
+ // instance list if the place is ambiguous (multiple connected, no instance_id).
2768
+ _resolveRuntime(instance_id) {
2769
+ const r = this.bridge.resolveTarget({ instance_id, target: void 0 });
2770
+ if (!r.ok)
2771
+ throw new RoutingFailure(r.error);
2772
+ const resolvedId = r.targetInstanceId;
2773
+ const roles = this.bridge.getInstances().filter((i) => i.instanceId === resolvedId).map((i) => i.role);
2774
+ const clientRoles = roles.filter((role) => role.startsWith("client")).sort();
2775
+ return { instanceId: resolvedId, clientRole: clientRoles[0] };
2776
+ }
1799
2777
  async getFileTree(path2 = "", instance_id) {
1800
2778
  const response = await this._callSingle("/api/file-tree", { path: path2 }, void 0, instance_id);
1801
2779
  return {
@@ -2325,7 +3303,7 @@ ${code}`
2325
3303
  const wrapper = buildModuleScriptInvokeWrapper({
2326
3304
  service: "ServerScriptService",
2327
3305
  bridgeName: SERVER_LOCAL_NAME,
2328
- missingError: "ServerEvalBridge not installed. Bridges are auto-installed at start_playtest and removed at stop_playtest. Start a playtest before calling eval_server_runtime.",
3306
+ missingError: "ServerEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
2329
3307
  userCode: code
2330
3308
  });
2331
3309
  const response = await this._callSingle("/api/execute-luau", { code: wrapper }, "server", instance_id);
@@ -2349,7 +3327,7 @@ ${code}`
2349
3327
  const wrapper = buildModuleScriptInvokeWrapper({
2350
3328
  service: "ReplicatedStorage",
2351
3329
  bridgeName: CLIENT_LOCAL_NAME,
2352
- missingError: "ClientEvalBridge not installed. Bridges are auto-installed at start_playtest and removed at stop_playtest. Start a playtest before calling eval_client_runtime.",
3330
+ missingError: "ClientEvalBridge not found. The bridge runs inside the play DM, so a playtest must be running. The bridge installs automatically (including for manually-started playtests); if a playtest is running and you still see this, reconnect the plugin in the edit window so the bridge reinstalls, then start the playtest again.",
2353
3331
  userCode: code
2354
3332
  });
2355
3333
  const response = await this._callSingle("/api/execute-luau", { code: wrapper }, clientTarget, instance_id);
@@ -3195,13 +4173,13 @@ ${code}`
3195
4173
  if (!action) {
3196
4174
  throw new Error("action is required for simulate_mouse_input");
3197
4175
  }
4176
+ const { instanceId, clientRole } = this._resolveRuntime(instance_id);
3198
4177
  const response = await this._callSingle("/api/simulate-mouse-input", {
3199
4178
  action,
3200
4179
  x,
3201
4180
  y,
3202
- button,
3203
- scrollDirection
3204
- }, target || "edit", instance_id);
4181
+ button
4182
+ }, target || clientRole || "edit", instanceId);
3205
4183
  return {
3206
4184
  content: [{
3207
4185
  type: "text",
@@ -3209,15 +4187,17 @@ ${code}`
3209
4187
  }]
3210
4188
  };
3211
4189
  }
3212
- async simulateKeyboardInput(keyCode, action, duration, target, instance_id) {
3213
- if (!keyCode) {
3214
- throw new Error("keyCode is required for simulate_keyboard_input");
4190
+ async simulateKeyboardInput(keyCode, action, duration, text, target, instance_id) {
4191
+ if (!keyCode && text === void 0) {
4192
+ throw new Error("keyCode or text is required for simulate_keyboard_input");
3215
4193
  }
4194
+ const { instanceId, clientRole } = this._resolveRuntime(instance_id);
3216
4195
  const response = await this._callSingle("/api/simulate-keyboard-input", {
3217
4196
  keyCode,
3218
4197
  action,
3219
- duration
3220
- }, target || "edit", instance_id);
4198
+ duration,
4199
+ text
4200
+ }, target || clientRole || "edit", instanceId);
3221
4201
  return {
3222
4202
  content: [{
3223
4203
  type: "text",
@@ -3429,8 +4409,21 @@ ${code}`
3429
4409
  }, tgt, instance_id);
3430
4410
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
3431
4411
  }
3432
- async captureScreenshot(instance_id) {
3433
- const response = await this._callSingle("/api/capture-screenshot", {}, void 0, instance_id);
4412
+ async captureScreenshot(instance_id, format, quality) {
4413
+ const { instanceId, clientRole } = this._resolveRuntime(instance_id);
4414
+ let response;
4415
+ if (clientRole) {
4416
+ const begin = await this._callSingle("/api/capture-begin", {}, clientRole, instanceId);
4417
+ if (begin.error) {
4418
+ return { content: [{ type: "text", text: begin.error }] };
4419
+ }
4420
+ if (!begin.contentId) {
4421
+ return { content: [{ type: "text", text: "Screenshot capture failed: no content id returned from client." }] };
4422
+ }
4423
+ response = await this._callSingle("/api/capture-read", { contentId: begin.contentId }, "edit", instanceId);
4424
+ } else {
4425
+ response = await this._callSingle("/api/capture-screenshot", {}, "edit", instanceId);
4426
+ }
3434
4427
  if (response.error) {
3435
4428
  return {
3436
4429
  content: [{
@@ -3439,13 +4432,45 @@ ${code}`
3439
4432
  }]
3440
4433
  };
3441
4434
  }
3442
- const pngBuffer = encodePngFromRgbaResponse(response);
4435
+ const w = response.width;
4436
+ const h = response.height;
4437
+ if (w === void 0 || h === void 0) {
4438
+ return { content: [{ type: "text", text: "Screenshot response missing dimensions." }] };
4439
+ }
4440
+ const fmt = format === "png" ? "png" : "jpeg";
4441
+ const q = quality === void 0 ? 92 : Math.max(1, Math.min(100, Math.floor(quality)));
4442
+ const MAX_IMAGE_BYTES = 6e6;
4443
+ let { buffer, mimeType } = encodeImageFromRgbaResponse(response, fmt, q);
4444
+ let usedQ = q;
4445
+ let note = "";
4446
+ if (buffer.length > MAX_IMAGE_BYTES) {
4447
+ if (fmt === "png") {
4448
+ const mb = (buffer.length / 1048576).toFixed(1);
4449
+ return {
4450
+ content: [{
4451
+ type: "text",
4452
+ text: `PNG screenshot is ${mb}MB, over the ~${(MAX_IMAGE_BYTES / 1048576).toFixed(0)}MB inline image limit. Use the default jpeg format (optionally with a "quality" value) or make the Studio window smaller for a lossless capture.`
4453
+ }]
4454
+ };
4455
+ }
4456
+ while (buffer.length > MAX_IMAGE_BYTES && usedQ > 25) {
4457
+ usedQ = Math.max(25, usedQ - 20);
4458
+ buffer = encodeImageFromRgbaResponse(response, "jpeg", usedQ).buffer;
4459
+ }
4460
+ note = ` \u2014 auto-reduced to q${usedQ} to fit the inline size limit; enlarge the Studio window or capture a smaller region for finer detail`;
4461
+ }
3443
4462
  return {
3444
- content: [{
3445
- type: "image",
3446
- data: pngBuffer.toString("base64"),
3447
- mimeType: "image/png"
3448
- }]
4463
+ content: [
4464
+ {
4465
+ type: "text",
4466
+ text: `Screenshot ${w}x${h}px (${fmt}${fmt === "jpeg" ? ` q${usedQ}` : ""})${note}. For simulate_mouse_input, x/y are pixel coordinates in this exact image with (0,0) at the top-left; it is not downscaled, so use coordinates as you read them off the image.`
4467
+ },
4468
+ {
4469
+ type: "image",
4470
+ data: buffer.toString("base64"),
4471
+ mimeType
4472
+ }
4473
+ ]
3449
4474
  };
3450
4475
  }
3451
4476
  };
@@ -4605,7 +5630,7 @@ var init_definitions = __esm({
4605
5630
  {
4606
5631
  name: "eval_server_runtime",
4607
5632
  category: "write",
4608
- description: "Execute Luau on the server peer in the running game's Script VM (shares require cache with user game scripts). Use this instead of execute_luau target=server when you need to see runtime-mutated module state. Auto-installed at start_playtest, removed at stop_playtest.",
5633
+ description: "Execute Luau on the server peer in the running game's Script VM (shares require cache with user game scripts). Use this instead of execute_luau target=server when you need to see runtime-mutated module state. Requires a running playtest; the bridge is installed automatically (including for playtests started manually via the Studio Play button).",
4609
5634
  inputSchema: {
4610
5635
  type: "object",
4611
5636
  properties: {
@@ -4624,7 +5649,7 @@ var init_definitions = __esm({
4624
5649
  {
4625
5650
  name: "eval_client_runtime",
4626
5651
  category: "write",
4627
- description: "Execute Luau on a client peer in the running game's LocalScript VM (shares require cache with user game scripts). Use this instead of execute_luau target=client-N when you need to see runtime-mutated module state. Auto-installed at start_playtest, removed at stop_playtest.",
5652
+ description: "Execute Luau on a client peer in the running game's LocalScript VM (shares require cache with user game scripts). Use this instead of execute_luau target=client-N when you need to see runtime-mutated module state. Requires a running playtest; the bridge is installed automatically (including for playtests started manually via the Studio Play button).",
4628
5653
  inputSchema: {
4629
5654
  type: "object",
4630
5655
  properties: {
@@ -5298,10 +6323,19 @@ part(0,2,0,2,1,1,"b")`,
5298
6323
  {
5299
6324
  name: "capture_screenshot",
5300
6325
  category: "read",
5301
- description: 'Capture a screenshot of the Roblox Studio viewport and return it as a PNG image. Requires EditableImage API to be enabled: Game Settings > Security > "Allow Mesh / Image APIs". Only works in Edit mode with the viewport visible.',
6326
+ description: 'Capture the Roblox Studio viewport at native resolution and return it as an image, plus a text line stating the exact pixel dimensions. Works in Edit mode and during a playtest (auto-detects a running client and captures the live play viewport). The returned image is never downscaled, so its pixel grid is exactly the coordinate space simulate_mouse_input uses \u2014 read click positions straight off this image. For reading fine text/UI, use format="png" (lossless) or a higher quality; enlarging the Studio window raises resolution. Requires EditableImage API enabled (Game Settings > Security > "Allow Mesh / Image APIs") and the window to be visible.',
5302
6327
  inputSchema: {
5303
6328
  type: "object",
5304
6329
  properties: {
6330
+ format: {
6331
+ type: "string",
6332
+ enum: ["jpeg", "png"],
6333
+ description: 'Image format. "jpeg" (default) is compact and crisp at high quality. "png" is lossless \u2014 best for reading dense text/UI, but larger (a busy 3D scene may be big).'
6334
+ },
6335
+ quality: {
6336
+ type: "number",
6337
+ description: "JPEG quality 1-100 (default 92). Higher = sharper text, larger size. Ignored for png."
6338
+ },
5305
6339
  instance_id: {
5306
6340
  type: "string",
5307
6341
  description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
@@ -5313,36 +6347,31 @@ part(0,2,0,2,1,1,"b")`,
5313
6347
  {
5314
6348
  name: "simulate_mouse_input",
5315
6349
  category: "write",
5316
- description: "Simulate mouse input in the Roblox Studio viewport via VirtualInputManager. Use during playtest to click UI buttons, interact with objects, or navigate menus. Coordinates are viewport pixels (top-left is 0,0). Use capture_screenshot to identify UI element positions before clicking.",
6350
+ description: "Simulate a mouse click in the running game via UserInputService:CreateVirtualInput. Use during a playtest to click UI buttons, interact with objects, or aim. Fires real UserInputService input and activates GUI buttons. Coordinates are viewport pixels matching capture_screenshot (top-left is 0,0) \u2014 take a screenshot first to find positions. Auto-targets the running client; only works during a playtest. Note: only click/mouseDown/mouseUp are supported (the API has no mouse-move or scroll).",
5317
6351
  inputSchema: {
5318
6352
  type: "object",
5319
6353
  properties: {
5320
6354
  action: {
5321
6355
  type: "string",
5322
- enum: ["click", "mouseDown", "mouseUp", "move", "scroll"],
5323
- description: 'Mouse action to perform. "click" does mouseDown + short delay + mouseUp.'
6356
+ enum: ["click", "mouseDown", "mouseUp"],
6357
+ description: 'Mouse action. "click" does mouseDown + short delay + mouseUp.'
5324
6358
  },
5325
6359
  x: {
5326
6360
  type: "number",
5327
- description: "Viewport pixel X coordinate"
6361
+ description: "Viewport pixel X coordinate (as seen in capture_screenshot)"
5328
6362
  },
5329
6363
  y: {
5330
6364
  type: "number",
5331
- description: "Viewport pixel Y coordinate"
6365
+ description: "Viewport pixel Y coordinate (as seen in capture_screenshot)"
5332
6366
  },
5333
6367
  button: {
5334
6368
  type: "string",
5335
6369
  enum: ["Left", "Right", "Middle"],
5336
6370
  description: "Mouse button (default: Left)"
5337
6371
  },
5338
- scrollDirection: {
5339
- type: "string",
5340
- enum: ["up", "down"],
5341
- description: 'Scroll direction (only for "scroll" action)'
5342
- },
5343
6372
  target: {
5344
6373
  type: "string",
5345
- description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
6374
+ description: 'Instance target. Defaults to the running playtest client (client-1) when present, else "edit". Override with "server", "client-2", etc.'
5346
6375
  },
5347
6376
  instance_id: {
5348
6377
  type: "string",
@@ -5355,13 +6384,13 @@ part(0,2,0,2,1,1,"b")`,
5355
6384
  {
5356
6385
  name: "simulate_keyboard_input",
5357
6386
  category: "write",
5358
- description: 'Simulate keyboard input via VirtualInputManager. Use during playtest for character movement (W/A/S/D), jumping (Space), interactions (E), or any key-driven action. For sustained movement, use "press" to hold and "release" to let go.',
6387
+ description: 'Simulate keyboard input in the running game via UserInputService:CreateVirtualInput. Use during a playtest for character movement (W/A/S/D walks at full WalkSpeed with player controls intact), jumping (Space), interactions (E), or any key-driven action. Drives the real input pipeline so game scripts and control modules respond. For sustained movement use action="press" to hold and "release" to let go. Pass "text" instead of keyCode to type a string into the focused TextBox. Auto-targets the running client; only works during a playtest.',
5359
6388
  inputSchema: {
5360
6389
  type: "object",
5361
6390
  properties: {
5362
6391
  keyCode: {
5363
6392
  type: "string",
5364
- description: 'Enum.KeyCode name: "W", "A", "S", "D", "Space", "E", "F", "LeftShift", "LeftControl", "Return", "Tab", "Escape", "One", "Two", etc.'
6393
+ description: 'Enum.KeyCode name: "W", "A", "S", "D", "Space", "E", "F", "LeftShift", "LeftControl", "Return", "Tab", "Escape", "One", "Two", etc. Omit if using "text".'
5365
6394
  },
5366
6395
  action: {
5367
6396
  type: "string",
@@ -5372,16 +6401,19 @@ part(0,2,0,2,1,1,"b")`,
5372
6401
  type: "number",
5373
6402
  description: 'Hold duration in seconds for "tap" action (default: 0.1). Use longer values for sustained input like walking.'
5374
6403
  },
6404
+ text: {
6405
+ type: "string",
6406
+ description: "Type this string into the currently focused TextBox (uses SendTextInput). When provided, keyCode/action are ignored."
6407
+ },
5375
6408
  target: {
5376
6409
  type: "string",
5377
- description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
6410
+ description: 'Instance target. Defaults to the running playtest client (client-1) when present, else "edit". Override with "server", "client-2", etc.'
5378
6411
  },
5379
6412
  instance_id: {
5380
6413
  type: "string",
5381
6414
  description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
5382
6415
  }
5383
- },
5384
- required: ["keyCode"]
6416
+ }
5385
6417
  }
5386
6418
  },
5387
6419
  // === Character Navigation ===