a digital entity named phi that roams bsky
1# mcp integration
2
3phi uses the [model context protocol](https://modelcontextprotocol.io) to interact with bluesky.
4
5## what is mcp
6
7mcp is a protocol for connecting language models to external tools and data sources via a client-server architecture.
8
9**why mcp instead of direct API calls?**
10- clean separation: tools live in external server
11- extensibility: add new tools without modifying agent
12- reusability: same server can be used by other agents
13- standard protocol: tools, resources, prompts
14
15## architecture
16
17```
18PhiAgent (PydanticAI)
19 ↓ stdio
20ATProto MCP Server
21 ↓ HTTPS
22Bluesky API
23```
24
25the agent communicates with the MCP server via stdio. the server handles all bluesky API interactions.
26
27## available tools
28
29from the ATProto MCP server:
30
31- `post(text, reply_to?, quote?)` - create posts and replies
32- `like(uri)` - like a post
33- `repost(uri)` - share a post
34- `follow(handle)` - follow a user
35- `search(query)` - search posts
36- `create_thread(posts)` - create multi-post threads
37
38## how it works
39
401. agent decides to use a tool (e.g., "i should reply")
412. pydantic-ai sends tool call to MCP server via stdio
423. MCP server executes bluesky API call
434. result returned to agent
445. agent continues with next action
45
46## agent configuration
47
48```python
49# src/bot/agent.py
50agent = Agent(
51 "claude-3-5-sonnet-20241022",
52 deps_type=AgentDeps,
53 result_type=Response,
54 system_prompt=personality,
55)
56
57# mcp server connected via stdio
58mcp = MCPManager()
59mcp.add_server(
60 name="atproto",
61 command=["uvx", "atproto-mcp"],
62 env={"BLUESKY_HANDLE": handle, "BLUESKY_PASSWORD": password}
63)
64
65# tools exposed to agent
66async with mcp.run() as context:
67 for tool in context.list_tools():
68 agent.register_tool(tool)
69```
70
71## structured outputs
72
73agent returns typed responses instead of using tools directly:
74
75```python
76class Response(BaseModel):
77 action: Literal["reply", "like", "repost", "ignore"]
78 text: str | None = None
79 reason: str | None = None
80```
81
82message handler interprets the response and executes via MCP tools if needed.
83
84**why structured outputs?**
85- clear contract between agent and handler
86- easier testing (mock response objects)
87- explicit decision tracking
88- agent focuses on "what to do", handler focuses on "how to do it"