Skip to main content
Glama
kvm_mcp_server.cpython-313.pyc39.7 kB
� �@h�u� �h�SSKrSSKrSSKrSSKJr SSKJr SSKJr SSK J r SSK J r SSK r SSKrSSKrSSKrSSKJrJrJr SSKJr SS KJr SSKr\R4R7\R4R9\55r\R4R?\S 5r \RB"\RDS \"\ S S S9\RF"5/S9 \RH"S5r%"SS5r&\&"5r'Sr("SS5r)\)"5r*S,S\+S\,S\+4Sjjr-Sr.\."5r/\"S5r0\0Rc5\(S\,S\+S\24Sj55r3\0Rc5\(S\,S\+S\+4Sj55r4\0Rc5\(S\,S\+S\+4S j55r5\0Rc5S\,S\+S\+4S!j5r6S"\,S\+S\,4S#jr7\0Rc5S\,S\+S\+4S$j5r8\0Rc5S\,S\+S\+4S%j5r9S&r:S'\\,\+4S\+4S(jr;S)r<S-S*jr=\>S+:Xa\ R~"\<"55 gg).�N)�RotatingFileHandler)� load_dotenv)�Server)� stdio_server)�InitializationOptions)�Union�Dict�Optional��wraps)�asynccontextmanagerz kvm_mcp.logz4%(asctime)s - %(name)s - %(levelname)s - %(message)si��)�maxBytes� backupCount)�level�format�handlers�kvm_mcpc�>�\rSrSrSrS SjrSr\S5rSr Sr g) �LibvirtConnectionPool�!zCA simple connection pool for libvirt to avoid repeated connections.c��XlX lX0l[R"US9UlSUlUR5 g)N)�maxsizer)�uri�max_connections�timeout�asyncio�Queue� connections�active_connections� _initialize)�selfrrrs �./home/steve/projects/kvm-mcp/kvm_mcp_server.py�__init__�LibvirtConnectionPool.__init__$s7����.��� �"�=�=��A���"#��� ����c��[UR5Hn[R"UR5nU(aTUR R U5 U=RS- sl[RSUR35 MM� g![Ra,n[RS[U535 SnAM�SnAff=f)z0Initialize the connection pool with connections.�z"Added connection to pool, active: z)Failed to initialize libvirt connection: N) �ranger�libvirt�openrr� put_nowaitr �logger�debug� libvirtError�error�str)r"�_�conn�es r#r!�!LibvirtConnectionPool._initialize,s����t�+�+�,�A� S��|�|�D�H�H�-����$�$�/�/��5��+�+�q�0�+��L�L�#E�d�F]�F]�E^�!_�`��-���'�'� S�� � �H��Q���Q�R�R�� S�s�A9B�C�.!C�Cc�# �Sn[R"URR5UR5IShv�N n[ R S5 U7v� U(aJUR5 URR!U5IShv�N [ R S5 ggNp![RaZ [ RS5 [R"UR5nU(d[R"S5eU7v� N�f=f![Ra'n[ RS[U535 eSnAff=fN�![Ra� UR#5 U=R$S-sl[ RSUR$35 O! O=f[R"UR5nU(a]URR!U5IShv�N U=R$S- sl[ R S UR$35 gg! [ RS 5 g=ff=f!U(GaWUR5 URR!U5IShv�N [ R S5 f![Ra� UR#5 U=R$S-sl[ RSUR$35 O! O=f[R"UR5nU(a]URR!U5IShv�N U=R$S- sl[ R S UR$35 ff! [ RS 5 f=ff=ff=f7f) zGet a connection from the pool.NzGot connection from poolz0Connection pool timeout, creating new connection�#Failed to connect to libvirt daemonzLibvirt connection error: zReturned connection to poolr(z Closed dead connection, active: z(Created replacement connection, active: z'Failed to create replacement connection)r�wait_forr�getrr-r.� TimeoutError�warningr*r+rr/r0r1� getVersion�put�closer )r"r3r4�new_conns r#�get_connection�$LibvirtConnectionPool.get_connection9s�����) P� �$�-�-�d�.>�.>�.B�.B�.D�d�l�l�S�S��� � �7�8�� ��P��O�O�%��*�*�.�.�t�4�4�4��L�L�!>�?� �T���'�'� ����Q�R��|�|�D�H�H�-���!�.�.�/T�U�U�� �  ���#�#� � �L�L�5�c�!�f�X�>� ?� �� ��5���+�+�P��� � � ��/�/�1�4�/����)I�$�Ja�Ja�Ib�'c�d�����P�#*�<�<����#9��#�"&�"2�"2�"6�"6�x�"@�@�@� �3�3�q�8�3�"�L�L�+S�TX�Tk�Tk�Sl�)m�n�$��P�� � �%N�O��#P�� �P��O�O�%��*�*�.�.�t�4�4�4��L�L�!>�?���+�+�P��� � � ��/�/�1�4�/����)I�$�Ja�Ja�Ib�'c�d�����P�#*�<�<����#9��#�"&�"2�"2�"6�"6�x�"@�@�@� �3�3�q�8�3�"�L�L�+S�TX�Tk�Tk�Sl�)m�n�$��P�� � �%N�O��#P�� �sG�O�<B6�B4�B6�"O�*.E'�E%�E'�2O�4B6�6A+D$�!D'�"I3�#D$�$D'�'E"�;"E�E"�"I3�%E'�'I0�<AG�I0�G�I0� AI�H�<I�O�I,�)I0�*O�,I0�0O�3 O�=.K�+J.�,K�O�O�AL$�#O�$L( �&O�,AN2�1M4 �2<N2�.O�2O � O� O� O�O�Oc���# �URR5(d�URR5nUR5 U=RS-sl[ R SUR35 URR5(dM�gg![Ra g[a+n[ RS[U535 SnANhSnAff=f7f)z"Close all connections in the pool.r(zClosed connection, active: zError closing connection: N) r�empty� get_nowaitr>r r-r.r� QueueEmpty� Exceptionr0r1)r"r3r4s r#� close_all�LibvirtConnectionPool.close_allhs�����"�"�(�(�*�*� D��'�'�2�2�4��� � � ��'�'�1�,�'�� � �:�4�;R�;R�:S�T�U� �"�"�(�(�*�*�� �%�%� ��� D�� � �9�#�a�&��B�C�C�� D�sA� C4�A!B'�C4�%C4�'C1�;C4�= C1�!C,�'C4�,C1�1C4)r rrrrN)�qemu:///systemr�) �__name__� __module__� __qualname__�__firstlineno__�__doc__r$r!r r@rG�__static_attributes__�r&r#rr!s-��M�� S��,P��,P�\ Dr&rc�0^�[T5U4Sj5nU$)Nc��^># �[R"5nT"U0UD6IShv�N [R"5U- n[RTRSUSS35 $NB![R"5U- n[RTRSUSS35 f=f7f)Nz took z.4fz seconds)�timer-r.rK)�args�kwargs� start_time�elapsed�funcs �r#�wrapper�!timing_decorator.<locals>.wrapperzs������Y�Y�[� � H��t�.�v�.�.��i�i�k�J�.�G� �L�L�D�M�M�?�&��� �X�F� G�/���i�i�k�J�.�G� �L�L�D�M�M�?�&��� �X�F� G�s,�B-� A*�A(�A*�?B-�(A*�*AB*�*B-r )rYrZs` r#�timing_decoratorr\ys"��� �4�[�H��H� �Nr&c�8�\rSrSrSrS SjrSrSrS SjrSr g) � VMInfoCache�z&A simple LRU cache for VM information.c�8�XlX l0Ul0Ulg�N)�max_size�ttl�cache� timestamps)r"rbrcs r#r$�VMInfoCache.__init__�s�� � ����� ���r&c���XR;a\[R"5URU- UR:aURU$URU URU g)z<Get a VM's info from the cache if available and not expired.N)rdrTrerc�r"�vm_names r#r9�VMInfoCache.get�sV�� �j�j� ��y�y�{�T�_�_�W�5�5����@��z�z�'�*�*�� � �7�#�����(�r&c�*�[UR5UR:�a@[URR 5SS9SnURU URU X RU'[ R "5URU'g)zSet a VM's info in the cache.c� �US$)Nr(rQ)�xs r#�<lambda>�!VMInfoCache.set.<locals>.<lambda>�s��1�Q�4r&)�keyrN)�lenrdrb�minre�itemsrT)r"ri�vm_info� oldest_vms r#�set�VMInfoCache.set�sn�� �t�z�z�?�d�m�m� +��D�O�O�1�1�3��H��K�I�� � �9�%���� �*�%� � �7��#'�9�9�;����� r&Nc���U(a+XR;aURU URU ggURR5 URR5 g)z4Invalidate cache entry for a VM or the entire cache.N)rdre�clearrhs r#� invalidate�VMInfoCache.invalidate�sN�� ��*�*�$��J�J�w�'��O�O�G�,�%� �J�J� � � � �O�O� !� !� #r&)rdrbrerc)�2�<ra) rKrLrMrNrOr$r9rvrzrPrQr&r#r^r^�s��0�� � /�$r&r^�config�prefix�returnc��UR5GH$up#UU3R5n[U[5(a[ X4S35X'MAU[ R ;dMW[ R UnUS:Xa[U[5(aSX'M�[U[5(a6UR5S;aSX'M�UR5S;aSX'M�M�[U[5(a[U5X'M�[U[5(a[U5X'GM XPU'GM' U$![[4a GM?f=f)z5Apply environment variable overrides to configurationr2�)�true�1�yes�onT)�false�0�no�offF)rs�upper� isinstance�dict�_apply_env_overrides�os�environr1�bool�lower�int�float� ValueError� TypeError)r~rrp�value�env_key� env_values r#r�r��s���l�l�n� ���H�S�E�"�(�(�*�� �e�T� "� "�.�u� ��m�D�F�K��"�*�*�$��J�J�w�/� ���?�!�%��-�-�&(�� ���!�%��.�.�$�?�?�,�0J�J�*.�F�K�&�_�_�.�2M�M�*/�F�K�$�#�E�3�/�/�&)�)�n�� �#�E�5�1�1�&+�I�&6�� �&/�s� �3%�: �M��#�I�.����s*�-D<� D<�("D<� "D<�1D<�<E�Ec�$�[RR[RR[RR [ 55S5n[ U5n[R"U5nSSS5 [WSS9 U$!,(df  N=f![a [SU35e[Ra=n[R"S[U53URUR5eSnAff=f)zLLoad configuration from config.json and apply environment variable overridesz config.jsonNz Configuration file not found at z$Invalid JSON in configuration file: r�)r)r��path�join�dirname�abspath�__file__r+�json�load�FileNotFoundError�JSONDecodeErrorr1�doc�posr�)� config_path�fr~r4s r#� load_configr��s����'�'�,�,�r�w�w���r�w�w���x�/H�I�=�Y�K�b� �+� �!��Y�Y�q�\�F�����+� �M�� �� �R��"B�;�-� P�Q�Q� � � �b��"�"�%I�#�a�&��#R�TU�TY�TY�[\�[`�[`�a�a��b�s6� B'�+B�B'� B$� B'�$B'�'+D�8D � Dz kvm-control�name� argumentsc��v# �URSS5(+nU(a3[RS5nU(a[RS5 U$[R S5 [ R 5IShv�N nUR5n/nUH�nUR5up�[RS[RS[RS [RS [RS [RS [R S [R"S0RUS5n UR%UR'5UR)5U UR+5S-UR-5S.5 M� U(a[R5SU5 UsSSS5IShv�N $GNG![R.ajn [R1SUR'5S[3U 535 UR%UR'5S[3U 5S.5 Sn A GM�Sn A ff=fN�!,IShv�N (df  g=f![R.a9n [R7S[3U 535 S[3U 50/sSn A $Sn A ff=f7f)z#List all available virtual machines�no_cacheF� _all_vms_zReturning cached VM listzFetching VM list from libvirtNzno state�running�blocked�paused�shutdown�shutoff�crashed� suspended�unknowni)r��id�state�memory�vcpuzError getting info for domain �: )r�r�r0zError listing VMs: r0)r9� vm_info_cacher-r.�info�connection_poolr@�listAllDomainsr�r*�VIR_DOMAIN_NOSTATE�VIR_DOMAIN_RUNNING�VIR_DOMAIN_BLOCKED�VIR_DOMAIN_PAUSED�VIR_DOMAIN_SHUTDOWN�VIR_DOMAIN_SHUTOFF�VIR_DOMAIN_CRASHED�VIR_DOMAIN_PMSUSPENDED�appendr��ID� maxMemory�maxVcpusr/r;r1rvr0) r�r�� use_cache� cached_listr3�domains�result�domainr��reason� state_strr4s r#�list_vmsr��s$���2#�!� � �j�%�8�8� � �'�+�+�K�8�K��� � �7�8�"�"�� � �3�4�"�1�1�3�3�t��)�)�+�G��F�!���$*�L�L�N�M�E��2�2�J��2�2�I��2�2�I��1�1�8��3�3�Z��2�2�I��2�2�I��6�6� � !��c�%��+���M�M� &� � � �$�i�i�k�!*�"(�"2�"2�"4��"<� &��� 1� #��"�>��!�!�+�v�6��K4�3�3��0�+�+���N�N�%C�F�K�K�M�?�RT�UX�YZ�U[�T\�#]�^��M�M� &� � � �!*�!$�Q��#������14�3�3�3��L � � �#�� � �*�3�q�6�(�3�4��#�a�&�!�"�"��#�s��J9�AI)�J9�-I)�G �I)�I�C6G �"I�7 I)�I �I)�J9� I)� I � AI�>I�I � I� I)�I&�I � I&�"I)�%J9�&I)�)J6�=.J1�+J6�,J9�1J6�6J9c���# �URS5nU(d[RS5 SSS.$[RSU35 [R 5IShv�N nUR U5nUR5upVU[R:Xa3[RSUS35 SSUS3S.sSSS5IShv�N $UR5S :XaM[RU5 [RS 5 [RSUS 35 S SUS 3S.nO [RS U35 SS U3S.nUsSSS5IShv�N $GNN�N !,IShv�N (df  g=f![RaEn[U5n [RSURSS5SU 35 SU S.sSnA$SnAff=f7f)zStart a virtual machine by namer��VM name not providedr0��status�messagez Starting VM: N�VM z is already runningrr�z started successfully�successzFailed to start VM zError starting VM r�r�)r9r-r0r�r�r@� lookupByNamer�r*r�r;�creater�rzr/r1� r�r�rir3r�r�r�r�r4� error_msgs r#�start_vmr�s����9��-�-��'��� �L�L�/� 0�%�2H�I� I�� � �m�G�9�-�.�"�1�1�3�3�t��&�&�w�/�F�"�L�L�N�M�E���2�2�2�����W�I�-@�A�B�")��G�9�DW�6X�Y� 4�3�3��}�}��!�#��(�(��1��(�(��5�� � �c�'��*?�@�A�$-�C��y�H]�:^�_��� � �2�7�)�<�=�$+�:M�g�Y�8W�X���'4�3�3�3�3�3��( � � �9���F� �� � �)�)�-�-�� �*J�)K�2�i�[�Y�Z�!�i�8�8��9���G&�1F �G&�0F �&E)�'F �*AE0� F �E,�F �G&�BE0� F �#E.�$F �(G&�)F �,F �.F �0F�6E9 �7 F�F �G&�F � G#�:G�G#�G&�G#�#G&c���# �URS5nU(d[RS5 SSS.$[RSU35 [R 5IShv�N nUR U5nUR5upVU[R:Xa3[RSUS35 SSUS3S.sSSS5IShv�N $UR5S :XaM[RU5 [RS 5 [RSUS 35 S SUS 3S.nO [RS U35 SS U3S.nUsSSS5IShv�N $GNN�N !,IShv�N (df  g=f![RaEn[U5n [RSURSS5SU 35 SU S.sSnA$SnAff=f7f)zStop a virtual machine by namer�r�r0r�z Stopping VM: Nr�z is already stoppedrr�z stopped successfullyr�zFailed to stop VM zError stopping VM r�r�)r9r-r0r�r�r@r�r�r*r�r;r�r�rzr/r1r�s r#�stop_vmr�As����9��-�-��'��� �L�L�/� 0�%�2H�I� I�� � �m�G�9�-�.�"�1�1�3�3�t��&�&�w�/�F�"�L�L�N�M�E���2�2�2�����W�I�-@�A�B�")��G�9�DW�6X�Y� 4�3�3���� �A�%��(�(��1��(�(��5�� � �c�'��*?�@�A�$-�C��y�H]�:^�_��� � �1�'��;�<�$+�:L�W�I�8V�W���'4�3�3�3�3�3��( � � �9���F� �� � �)�)�-�-�� �*J�)K�2�i�[�Y�Z�!�i�8�8��9�r�c���# �URS5nU(dSSS.$[R"S5nUcSSS.$URU5nUR 5upVU[R :Xa SSUS 3S.$UR 5S :Xa S S US 3S.nOSSU3S.nUR5 U$![RanS[U5S.sSnA$SnAff=f7f)z Reboot a virtual machine by namer�r0r�r�rINr7zCannot reboot VM z: VM is not runningrr�r�z rebooted successfullyzFailed to reboot VM ) r9r*r+r�r�r��rebootr>r/r1) r�r�rir3r�r�r�r�r4s r#� reboot_vmr�es����6��-�-��'���%�2H�I� I��|�|�,�-�� �<�%�2W�X� X��"�"�7�+��� � �� �� �G�.�.� .�%�4E�g�Y�Na�2b�c� c� �=�=�?�a� � )��G�9�DZ�6[�\�F� '�6J�7�)�4T�U�F� � � � �� �� � � �6�!�c�!�f�5�5��6�sT�C&�B7�C&�B7�C&�?B7�>C&�?7B7�6C&�7C#� C�C#�C&�C#�#C&ric �<�URS[SSS5nURS[SSS5nURS[SSS5nURS [SSS 5nU(aU(aU(aU(d [S 5e[RR URS [SSS 55n[RR U5(d[SU35e[US5nUR5R5nSSS5 SS0SUW/S./0SSSSSSU30S.SSSSSU30S./0SSSS US!3S"./0S#.n [R"U S$S%9$!,(df  NV=f)&z4Generate an Ignition configuration for Fedora CoreOS�hostname�vm�ignition�default_hostname�user� default_user�timezone�default_timezone�locale�default_localezDEmpty values are not allowed for hostname, user, timezone, or locale�ssh_key�default_ssh_keyzSSH key not found at �rN�versionz3.3.0�users)r��sshAuthorizedKeys�filesz /etc/hostnamei�T�sourcezdata:,)r��mode� overwrite�contentsz/etc/locale.confz data:,LANG=�unitsztimezone.servicez�[Unit] Description=Set timezone After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/bin/timedatectl set-timezone z: RemainAfterExit=yes [Install] WantedBy=multi-user.target)r��enabledr�)r��passwd�storage�systemd�)�indent) r9r~r�r�r�� expanduser�existsr�r+�read�stripr��dumps) rir�r�r�r�r�� ssh_key_pathr�r��ignition_configs r#�generate_ignition_configr �s����}�}�Z����j�)A�BT�)U�V�H� �=�=�����j�!9�.�!I� J�D��}�}�Z����j�)A�BT�)U�V�H� �]�]�8�V�D�\�*�%=�>N�%O� P�F� �4�x�v��_�`�`��7�7�%�%�i�m�m�I�v�d�|�J�?W�Xi�?j�&k�l�L� �7�7�>�>�,� '� '��"7� �~� F�G�G� �l�C� �A��&�&�(�.�.�"�� !� �w� � � �*1���� � �+��!%� �F�8�*�"5�!� �/��!%� �K��x�"8�!� �� �* �.�#�%-�.6�J�7� !��� �A4�O�l �:�:�o�a� 0�0�u !� �s �F � Fc ��4 # �SnURS5nURS5nURS5nURSS5nURSS5nURS 5nU(a[U[5(dS S S .U(aUR5 $$[ S U55(aS SS .U(aUR5 $$[U[ 5(aUS:aS SS .U(aUR5 $$US:�aS SS .U(aUR5 $$[U[ 5(aUS:aS SS .U(aUR5 $$US:�aS SS .U(aUR5 $$[U[ 5(aUS:aS SS .U(aUR5 $$US:�aS SS .U(aUR5 $$U(a[U[5(dS SS .U(aUR5 $$[ R"S5nUcS SS .U(aUR5 $$URU5n U b!S SUS3S .U(aUR5 $$S US!3n [RRU 5(a!S S"U S3S .U(aUR5 $$[R"S#S$S%S&X�S'3/S(S(S)9n U R S*:wa*S S+U R"3S .U(aUR5 $$S,US-US.US/U S03 n UR%U 5nUcS S1S .U(aUR5 $$UR'5S*:aS S2S .U(aUR5 $$S3SUS43S .U(aUR5 $$![ RaSn S[U 5R5;a+S [U 5S .sSn A U(aUR5 $$Sn A GN�Sn A ff=f![ Ra3n S S5[U 53S .sSn A U(aUR5 $$Sn A f[R(a3n S S6[U 53S .sSn A U(aUR5 $$Sn A f[*a3n S S7[U 53S .sSn A U(aUR5 $$Sn A ff=f!U(aUR5 ff=f7f)8zCreate a new VMNr�r��vcpus� disk_size��network�brforvms� master_imager0zInvalid VM namer�c3�*# �UH oS;v� M g7f)z!@#$%^&*()+={}[]|\:;"'<>?/NrQ)�.0�cs r#� <genexpr>�create_vm.<locals>.<genexpr>�s���D�G�q�2�2�G�s�z#VM name contains invalid characters�zMemory must be at least 256MBiz#Memory exceeds maximum limit of 1TBr(zMust have at least 1 vCPU�z!vCPUs exceed maximum limit of 128zDisk size must be at least 1GBi'z'Disk size exceeds maximum limit of 10TBzInvalid network namerIr7r�z already existszdomain not foundz/vm/z.qcow2z Disk image zqemu-imgr�z-f�qcow2�GT��capture_output�textrzFailed to create disk image: z/ <domain type='kvm'> <name>z'</name> <memory unit='MiB'>z</memory> <vcpu>a(</vcpu> <os> <type arch='x86_64' machine='pc'>hvm</type> <boot dev='hd'/> </os> <devices> <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='au'/> <target dev='vda' bus='virtio'/> </disk> <interface type='bridge'> <source bridge='brforvms'/> <model type='virtio'/> </interface> <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'/> </devices> </domain> zFailed to define VMzFailed to start VMr�z created successfullyzLibvirt error: zSubprocess error: zUnexpected error: )r9r�r1r>�anyr�r*r+r�r/r�r�r�r� subprocess�run� returncode�stderr� defineXMLr��SubprocessErrorrF)r�r�r3rir�r r rr� existing_domr4� disk_pathr��xml�doms r#� create_vmr(�s��� �D�m��-�-��'�����x�(��� � �g�&���M�M�+�r�2� ��-�-� �:�6�� �}�}�^�4� ��j��#�6�6�%�2C�D�B � �J�J�L� � �D�G�D� D� D�%�2W�X�| � �J�J�L� �y�&�#�&�&�&�3�,�%�2Q�R�v � �J�J�L� �s �K� �%�2W�X�p � �J�J�L� �m�%��%�%����%�2M�N�j � �J�J�L� �g �3�;�%�2U�V�d � �J�J�L� �a�)�S�)�)�Y��]�%�2R�S�^ � �J�J�L� �[ �u� �%�2[�\�X � �J�J�L� �U�j��#�6�6�%�2H�I�R � �J�J�L� �M�|�|�,�-�� �<�%�2W�X�H � �J�J�L� �C >��,�,�W�5�L��'�")��G�9�O�6T�U�| � �J�J�L� �(��7�)�6�*� � �7�7�>�>�)� $� $�%�K� �{�/�2Z�[�l � �J�J�L� �g��� ��4��)�{�!�_� M��� �� � � �� !�%�4Q�RX�R_�R_�Q`�2a�b�Z � �J�J�L� �U��)� � &�x�(��'�#�$-�+� . � ��2�n�n�S�!�� �;�%�2G�H� � �J�J�L� � �:�:�<�!� �%�2F�G� � �J�J�L� �$�#�g�Y�>S�0T�U� � �J�J�L� ��{�#�#� >�!��Q�����7�")�c�!�f�=�=�v � �J�J�L� �y8�� >��l � � �J�!���A��x�.H�I�I� � �J�J�L� �� � %� %�M�!�0B�3�q�6�(�.K�L�L� � �J�J�L� �� �M�!�0B�3�q�6�(�.K�L�L� � �J�J�L� ��M�� � �J�J�L� �sg�T�BP1�T�'P1�T�P1�:T� P1�T�6P1�T�. P1�8T�P1�0T� P1�T�, P1� T�%P1�T�O�8T�3P1�T�?P1�T�5'P1�T�5P1� T�&P1�.T�P.�*P)�P.�P1� T�#P1�)P.�.P1�1S8�Q3�S8�S;�T�3S8� R8�S8�S;�T�8 S8�S3�S8�S;�T�3S8�8S;�;T�Tc���# �SSKnSSKnUR"/SQSSS9nURS:waSSURS.$UR R 5Vs/sH)oUR5(dMUR5PM+ nn0nUH�nUR"S S S S U/SSS9nURS:XdM,UR R5(dMMUR R5n U RS 5n [U 5n SU s=::aS::a O M�SU -Xu'M�M� SUS.$s snf![a M�f=f![an S[U 5S.sSn A $Sn A ff=f7f)z!Get VNC ports for all running VMsrN)�virsh�-crI�listz--state-runningz--nameTrr0zFailed to get VM list)r�r�r0r*r+rI� vncdisplay�:�ci r�)r�� vnc_portsr�) rr�rr r!�stdout� splitlinesr�lstripr�r�rFr1) r�r�rr�r�r��vmsr0� port_result�port� display_num�numr4s r#� get_vnc_portsr9Ask���%6������ f�-1��>�� � � �� !�%�2I�TZ�Ta�Ta�b� b�%+�M�M�$<�$<�$>�M�$>�b�(�(�*�z�r�x�x�z�$>��M�� ��B�$�.�.�'�4�9I�<�Y[�)\�59��F�K��%�%��*�{�/A�/A�/G�/G�/I�/I�"�)�)�/�/�1��"�k�k�#�.� ���k�*�C��C�~�2�~�(,�s� � � �&��$ �"� � ��+N��""����� �6�!�c�!�f�5�5��6�s��E4�;E�E4�E�D9�8D9� .E�>E�+E� D>�%E�'D>�. E�8E4�9E�> E �E� E � E� E1� E,�&E1�'E4�,E1�1E4c�h�[S5H�nUR[R5nU(aSUR 5H?up4US(dMUSH%nUS[R :XdMUSs s s $ MA [ R"S5 M� g![a gf=f)zGet the IP address of a VM.rJ�addrs�type�addrr(N) r)�interfaceAddressesr*�(VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASErs�VIR_IP_ADDR_TYPE_IPV4rT�sleeprF)r�r2�ifacesr��valr=s r#� get_vm_iprDks��� ��r��A��.�.�w�/_�/_�`�F��#)�<�<�>�K�T��7�|�|�$'��L�D�#�F�|�w�/L�/L�L�'+�F�|� 3�%1�$2� �J�J�q�M���� ����s$�AB$�B$�8 B$� B$�$ B1�0B1�requestc��# �[U[5(a[R"U5nUR S5nUR S05nUR S 5nU(d SS S S .US.$[U[ 5(d SS SS .US.$US:Xa;UR S5nU(d SS S S .US.$[5R U5nO[5R SU35nU(d SSSS .US.$US:Xa"U"WUR S055IShv�N nOU"S0UD6IShv�N n[U[ 5(a.UR S5S:XaSSUR SS5S .US.$SUUS.$![RanSSS[U5S.SS.sSnA$SnAff=fN�N�![anSS [U5S .US.sSnA$SnAf[anSS[U5S .US.sSnA$SnAff=f![aBnSS[U5S .[U[ 5(aUR S 5OSS.sSnA$SnAff=f7f)zHandle JSON-RPC request�2.0iD���z Parse error)�coder��dataN)�jsonrpcr0r��method�paramsr�i����zInvalid Request�rHr�i����zInvalid paramsz tools/callr��handle_i����zMethod not foundr�r�r0i����r�zInternal error)rJr�r�rQ) r�r1r��loadsr�r9r��globalsr�rF)rEr4rKrL� request_id� tool_name�handlerr�s r#�handle_requestrT|s����a � �g�s� #� #� ��*�*�W�-�����X�&�����X�r�*���[�[��&� �� �"(�5F�G� �� � �&�$�'�'� �"(�5E�F� �� � �\� !�� � �6�*�I��$�&,�9J�K�$��� �i�m�m�I�.�G��i�m�m�g�f�X�$6�7�G�� �"(�5G�H� �� � ( ���%�&�y�&�*�*�[�"�2M�N�N��&�0��0�0���&�$�'�'�F�J�J�x�,@�G�,K�$� &�#)�:�:�i�9I�#J��%� ��!� � �� ���'�'� �$�&,��PS�TU�PV�W����� ��`O�0��$� � �"�"�1�v��!� � ��� � �"�"�1�v��!� � �� �� � ��$��Q��8�'1�'�4�'@�'@�'�+�+�d�#�d� � �� �s�I�H�F�AH�3I�4H�I�&H�8I�9AH�>I�"F9�"F5�#F9�3F7�4AF9�:I�;F9�I�F2�F-�'F2�(H�,I�-F2�2H�5F9�7F9�9 H�G�H�H�I� H�'G>�8H�9H�=I�>H�H� I�7I �I�I� I�Ic ��# �[R"5nSH(nUR[[U5U4Sj5 M* [RS5 [5nU(dO4[U5IShv�N n[[R"U55 MG[RS 5 [&R)5IShv�N g![ [ 4a [RSUS35 M�f=fN�![a [RS5 M�[ aZn[R#S[%U535 [[R"S SS [%U5S .S .55 SnAN�SnAff=fN�![RS 5 [&R)5IShv�N f=f7f)z)Main function to handle JSON-RPC requests)�SIGINT�SIGTERMc�@�[R"[U55$ra)r� create_taskr�)�sig_names r#rn�main.<locals>.<lambda>�s��'�*=�*=�h�x�>P�*Qr&z!Could not add signal handler for z (might be Windows)zStarting KVM MCP serverNzReceived EOF, shutting downzError handling request: rGi���rM)rJr�r0zCleaning up resources)r�get_running_loop�add_signal_handler�getattr�signal�NotImplementedError�AttributeErrorr-r;r��inputrT�printr�r�EOFErrorrFr0r1r�rG)�looprZrE�responser4s r#�mainrg�sz��� � #� #� %�D�*�� ^� � #� #����)� (�Q� �*�*�� � �-�.�� ��'����!/��!8�8���d�j�j��*�+��( � � �+�,��'�'�)�)�)��9$�^�4� ^� �N�N�>�x�j�H[�\� ]� ^��9��� �� � �9�:��� �� � �7��A��x�@�A��d�j�j�$��&,��Q��@�"����� �� *�� � � �+�,��'�'�)�)�)�s��G �$C�G �F�D�/F�0D�>D�?#D�"F�$,G �F�G �)D�?G �D�G �D�F �'F�* F �3AF�F�F � F�G �-G�?G�G�G c���# �U(a[RSUS35 [R5IShv�N [R "5Vs/sHo[R "5LdMUPM nnUHnUR5 M [R"USS06IShv�N [R"5nUR5 gN�s snfN17f)z%Cleanup handler for graceful shutdownz Received z, shutting downN�return_exceptionsT) r-r�r�rGr� all_tasks� current_task�cancel�gatherr\�stop)rZ�t�tasks�taskres r#r�r� s������ � �i��z��9�:� � #� #� %�%�%� �)�)�+� O�+�1��8L�8L�8N�/N�Q�+�E� O��� � � � �� �.�.�%� 8�4� 8�8�8� � #� #� %�D��I�I�K�&�� P�9�s3�8C#�C�C#�C�5C�;4C#�/C!�0+C#�C#�__main__)r�ra)@r*r��logging�logging.handlersr�dotenvr� mcp.serverr�mcp.server.stdior�mcp.server.modelsrrr�rrT�typingrr r � functoolsr � contextlibr r_r�r�r�r��log_dirr��log_file� basicConfig�INFO� StreamHandler� getLoggerr-rr�r\r^r�r�r1r�r�r~�server� call_toolr,r�r�r�r�r r(r9rDrTrgr�rKrrQr&r#�<module>r�s���� ��0���)�3�� �� �(�(��*� � �'�'�/�/�"�'�'�/�/�(�3� 4�� �7�7�<�<��� /����� �,�,� A��H�x�Q�G������� � � �9� %��RD�RD�j(�)�� �&$�&$�R� � ����s��D��D � ��� � � �������4#��4#��4#�$�4#���4#�l����� 9�� 9�� 9�$� 9��� 9�D����� 9�� 9�� 9�� 9��� 9�D����6�#�6�$�6�4�6��6�6K1�c�K1�d�K1�s�K1�Z����p�#�p�$�p�4�p��p�d����'6�c�'6�d�'6�t�'6��'6�R�"c �%��T� �"2�c �t�c �J(*�T�& �z�� �K�K����r&

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steveydevey/kvm-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server