HarmonyOS MCP Server
Allows AI agents to manipulate HarmonyOS devices, such as launching apps, through MCP tools.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@HarmonyOS MCP Server Launch the settings app on my phone"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
Clone this repo
git clone https://github.com/XixianLiang/HarmonyOS-mcp-server.git
cd HarmonyOS-mcp-serverSetup the envirnment.
uv python install 3.13
uv syncUsage
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 toolsclickB
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
Scored across 16 tools
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.
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.
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.
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
Related MCP Connectors
- DNS-DATA RemoteDeprecatedcom.dns-data
Connect AI assistants to authorized Windows and macOS devices for files, commands, and management.
Control your Tesla from your AI assistant - climate, charging, access, and security.
Turns a phone into a camera+Bluetooth remote so AI assistants can see and control any PC.
Let AI agents control Android and iOS devices on Mobilerun Cloud: screen, input, apps, workflows.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables AI assistants to control mobile and desktop devices with natural language, including running automation tasks, taking screenshots, and managing devices.65 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to directly interact with Matter devices and protocol operations, including device commissioning, attribute read/write, commands, and event monitoring, through natural language.7MIT
- AlicenseNot gradedqualityFmaintenanceEnables AI assistants to operate Huawei Cloud resources (ECS, OBS, GaussDB, etc.) through conversational workflows via the Model Context Protocol.36 PyPIApache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to control Xiaomi Mi Home smart devices through natural language, supporting device listing, property control, actions, and scenes.8MIT