Building pyRPC’s MCP surface taught us more about AI-native development than we expected. Not because MCP is complicated, but because designing tools for agents forces you to confront assumptions about interfaces, trust, and what “automation” actually means.
The trust boundary insight
AI agents need ground truth, not guesses. An agent that hallucinates a procedure signature produces code that looks right and fails at runtime. An agent that calls introspect_project and reads the actual registry produces code that matches reality. The trust boundary is the MCP server: it is the one place where your code is read deterministically, not inferred from context.
The read-only-first principle
Every tool we exposed first went through a read-only phase. introspect_project reads the registry. check_call validates without executing. Only run_codegen writes files, and it does so through the same pipeline the CLI uses. The principle is simple: validate before execute, never execute through the agent. The agent proposes, the tool validates, the human approves.
The thin-server pattern
Each tool does one thing. introspect_project returns the procedure tree. check_call validates one payload. run_codegen regenerates clients. The agent composes these tools into workflows. We do not build a “generate a full RPC client for my app” mega-tool because the agent can do that composition itself. Thin servers are easier to test, easier to reason about, and less likely to surprise.
The two-server architecture
Local servers handle code. Remote servers handle documentation. This split is not arbitrary: it follows the data residency boundary. Your code lives on your machine and must be imported by your interpreter. Framework docs are public knowledge that benefits from centralized hosting. Mixing these into one server creates trust and availability problems that the split avoids entirely.
agent
|
+---> local stdio (code introspection)
| imports your routers
| validates your payloads
| generates your clients
|
+---> remote HTTP (framework knowledge)
serves current docs
config patterns
API references
|
v
ground truth -> agent -> corrected code
The protocol-is-the-interface lesson
MCP is the standard layer. Not REST, not gRPC, not a custom protocol. MCP gives us JSON-RPC with structured tool definitions that every major agent already speaks. By building on MCP, we inherited an ecosystem: Claude Code, Cursor, VS Code, and dozens of other clients all understand the same wire format. The protocol is the interface, and the interface is already deployed.
What did not work
We tried exposing too many tools early on. A “manage project” tool, a “scaffold app” tool, a “deploy” tool. Agents ignored most of them because the tool descriptions were ambiguous and the return shapes were complex. We also tried to be a general AI assistant, offering chat-style help through the MCP surface. That duplicated what the agent already does well and added latency without value.
What worked
Focused tools with precise descriptions. introspect_project does not describe what it returns in vague terms. It returns a tree with names, kinds, parameters, and types. Structured errors that name the parameter that failed and the constraint that was violated. Dry-run-first defaults where every write operation has a read counterpart. These patterns made the tools predictable, which made the agent effective.
The ecosystem observation
The adoption pattern across the ecosystem is revealing. add-mcp is becoming the standard installation verb. Agents now number in the dozens, with 19 or more supporting MCP natively. Community-driven config formats are converging on a small set of conventions: stdio for local, HTTP for remote, JSON config files with tool lists. The protocol is eating the interface layer, and the interface layer is where the value compounds.
The development loop
The AI-native loop looks like this: the agent proposes code, the MCP server provides ground truth, the agent corrects itself. This loop only works if the ground truth is fast, structured, and deterministic. Latency kills the loop. Ambiguity kills the loop. Non-deterministic responses kill the loop. Everything we built was in service of making this loop tight: fast local servers, structured JSON responses, validation that always returns the same result for the same input.
The broader lesson is that AI-native development is not about adding AI to existing tools. It is about designing tools whose primary consumer is an agent, not a human. That changes what interfaces look like, what errors contain, and what “user experience” means. The human still approves. The agent still proposes. But the tools belong to the agent.

pyRPC