Skip to main content
Glama
HarmonyOS MCP Server

HarmonyOS MCP Server

by XixianLiang

   

Intro

This is a MCP server for manipulating harmonyOS Device.

https://github.com/user-attachments/assets/7af7f5af-e8c6-4845-8d92-cd0ab30bfe17

Related MCP server: matter-mcp-server

Quick Start

Installation

  1. Clone this repo

git clone https://github.com/XixianLiang/HarmonyOS-mcp-server.git
cd HarmonyOS-mcp-server
  1. Setup the envirnment.

uv python install 3.13
uv sync

Usage

1.Claude Desktop

You can use Claude Desktop to try our tool.

2.Openai SDK

You can also use openai-agents SDK to try the mcp server. Here's an example

"""
Example: Use Openai-agents SDK to call HarmonyOS-mcp-server
"""
import asyncio
import os

from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServerStdio, MCPServer

async def run(mcp_server: MCPServer):
    agent = Agent(
        name="Assistant",
        instructions="Use the tools to manipulate the HarmonyOS device and finish the task.",
        mcp_servers=[mcp_server],
    )

    message = "Launch the app `settings` on the phone"
    print(f"Running: {message}")
    result = await Runner.run(starting_agent=agent, input=message)
    print(result.final_output)


async def main():

    # Use async context manager to initialize the server
    async with MCPServerStdio(
        params={
            "command": "<...>/bin/uv",
            "args": [
                "--directory",
                "<...>/harmonyos-mcp-server",
                "run",
                "server.py"
            ]
        }
    ) as server:
        trace_id = gen_trace_id()
        with trace(workflow_name="MCP HarmonyOS", trace_id=trace_id):
            print(f"View trace: https://platform.openai.com/traces/trace?trace_id={trace_id}\n")
            await run(server)

if __name__ == "__main__":
    asyncio.run(main())

3.Langchain

You can use LangGraph, a flexible LLM agent framework to design your workflows. Here's an example

"""
langgraph_mcp.py
"""

server_params = StdioServerParameters(
    command="/home/chad/.local/bin/uv",
    args=["--directory",
          ".",
          "run",
          "server.py"],
    
)


#This fucntion would use langgraph to build your own agent workflow
async def create_graph(session):
    llm = ChatOllama(model="qwen2.5:7b", temperature=0)
    #!!!load_mcp_tools is a langchain package function that integrates the mcp into langchain.
    #!!!bind_tools fuction enable your llm to access your mcp tools
    tools = await load_mcp_tools(session)
    llm_with_tool = llm.bind_tools(tools)

    
    system_prompt = await load_mcp_prompt(session, "system_prompt")
    prompt_template = ChatPromptTemplate.from_messages([
        ("system", system_prompt[0].content),
        MessagesPlaceholder("messages")
    ])
    chat_llm = prompt_template | llm_with_tool

    # State Management
    class State(TypedDict):
        messages: Annotated[List[AnyMessage], add_messages]

    # Nodes
    def chat_node(state: State) -> State:
        state["messages"] = chat_llm.invoke({"messages": state["messages"]})
        return state

    # Building the graph
    # graph is like a workflow of your agent.
    #If you want to know more langgraph basic,reference this link (https://langchain-ai.github.io/langgraph/tutorials/get-started/1-build-basic-chatbot/#3-add-a-node)
    graph_builder = StateGraph(State)
    graph_builder.add_node("chat_node", chat_node)
    graph_builder.add_node("tool_node", ToolNode(tools=tools))
    graph_builder.add_edge(START, "chat_node")
    graph_builder.add_conditional_edges("chat_node", tools_condition, {"tools": "tool_node", "__end__": END})
    graph_builder.add_edge("tool_node", "chat_node")
    graph = graph_builder.compile(checkpointer=MemorySaver())
    return graph





async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            config = RunnableConfig(thread_id=1234,recursion_limit=15)
            # Use the MCP Server in the graph
            agent = await create_graph(session)

            while True:
                message = input("User: ")
                try:
                    response = await agent.ainvoke({"messages": message}, config=config)
                    print("AI: "+response["messages"][-1].content)
                except RecursionError:
                    result = None
                    logging.error("Graph recursion limit reached.")


if __name__ == "__main__":
    asyncio.run(main())

Write the system prompt in server.py

"""
server.py
"""
@mcp.prompt()
def system_prompt() -> str:
    """System prompt description"""
    return """
    You are an AI assistant use the tools if needed.
    """

Use load_mcp_prompt function to get your prompt from mcp server.

"""
langgraph_mcp.py
"""
prompts = await load_mcp_prompt(session, "system_prompt")

Available Tools

16 tools
clickB

click the given coordinate Args: center: a string like "(x, y)", sample: "(227, 168)"

Inputs: center (required). Returns: result

current_appA

Get the current foreground application information.

Returns: Tuple[str, str]: A tuple contain the package_name andpage_name of the foreground application. If no foreground application is found, returns (None, None).

Inputs: no named parameters. Returns: result

get_screenshotA

Takes a screenshot of the device and returns it. Returns: Image: the screenshot

Inputs: no named parameters.

get_uilayoutA

Retrieves information about clickable elements in the current UI. Returns a formatted string containing details about each clickable element, including its text, content description, bounds, and center coordinates.

Returns: str: A formatted list of clickable elements with their properties

Inputs: no named parameters. Returns: result

input_textC

input text to the given coordinate Args: center: a string like "(x, y)", sample: "(227, 168)" text: the text to input

Inputs: center (required), text (required). Returns: result

launch_appC

launch app accrodingt to the given package name. Args: package_name: the package name of the package.

Inputs: package_name (required). Returns: result

list_appA

Get all installed packages on the device Returns: A list of all installed packages on the device as a string

Inputs: no named parameters. Returns: result

long_clickB

long click the given coordinate Args: center: a string like "(x, y)", sample: "(227, 168)"

Inputs: center (required). Returns: result

media_nextC

play the next media

Inputs: no named parameters. Returns: result

media_play_pauseA

Play or pause media on the phone.

Sends the media play/pause keycode to control any currently active media. Can be used to play music or videos that were recently playing.

Returns: str: Success message if the command was sent, or an error message if the command failed.

Inputs: no named parameters. Returns: result

media_previousB

play the previous media

Inputs: no named parameters. Returns: result

stop_appD

Inputs: package_name (required).

swipeD

Inputs: x1 (required), y1 (required), x2 (required), y2 (required), +1 more.

volume_downB

turn down the volume

Inputs: no named parameters. Returns: result

volume_muteC

mute the volume

Inputs: no named parameters. Returns: result

volume_upB

turn up the volume

Inputs: no named parameters. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3/5.0

    Scored across 16 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose: UI interaction tools (click, long_click, swipe, input_text, get_screenshot, get_uilayout) are well-separated from app management (launch_app, stop_app, list_app, current_app) and media controls (media_*, volume_*). No two tools have overlapping functionality.

    Naming Consistency4/5

    Tools follow a predictable pattern: UI tools use verb_noun (e.g., get_screenshot, launch_app) and media/volume tools use noun_verb (e.g., media_next, volume_down). While consistent within subdomains, the two patterns differ slightly, but overall naming is readable and logical.

    Tool Count5/5

    16 tools is well-scoped for a server covering both UI automation and media control. Each tool serves a clear function without redundancy, and the number is appropriate for the domain.

    Completeness4/5

    The tool set covers core UI actions (click, input, screenshot, layout) and app lifecycle (launch, stop, list, current), plus media and volume controls. Minor gaps like missing swipe description and common UI actions (e.g., back, scroll) are present, but the set is largely complete for typical tasks.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Enables AI assistants to control mobile and desktop devices with natural language, including running automation tasks, taking screenshots, and managing devices.
      6
      5 npm
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to directly interact with Matter devices and protocol operations, including device commissioning, attribute read/write, commands, and event monitoring, through natural language.
      7
      MIT
    • A
      license
      Not graded
      quality
      F
      maintenance
      Enables AI assistants to operate Huawei Cloud resources (ECS, OBS, GaussDB, etc.) through conversational workflows via the Model Context Protocol.
      36 PyPI
      Apache 2.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to control Xiaomi Mi Home smart devices through natural language, supporting device listing, property control, actions, and scenes.
      8
      MIT