PropHours, a material participation log for rental property owners, recently implemented a remote Model Context Protocol (MCP) server to allow AI assistants like ChatGPT to query and update tax-related activity logs. Rather than relying on a dedicated MCP SDK, the developers built the integration as a single Laravel controller using plain JSON-RPC 2.0. This approach highlights how lightweight the MCP server surface can be when limited to tool exposure, avoiding the overhead of libraries that often obscure the underlying transport mechanics.
The Minimalist Transport Layer
The implementation focuses on the streamable HTTP transport, which is essentially JSON-RPC 2.0 over a single POST endpoint. By supporting only four core methodsβinitialize, ping, tools/list, and tools/callβthe server avoids the complexity of SSE streams or session management. The developers noted that writing the negotiation logic by hand, specifically for protocol versions like 2024-11-05 and 2025-11-25, allowed them to inspect every byte sent to the client, prioritizing transparency over code brevity.
Strict Validation and OAuth Discovery
Before any business logic executes, the server enforces rigorous envelope validation, checking Origin headers against an allowlist and rejecting bodies larger than 64 KB. For authentication, the server publishes standard OAuth discovery documents at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server, mandating PKCE S256 for the authorization code flow. This ensures that assistants can dynamically register themselves and obtain scoped access without handling user passwords directly.
Idempotency and Audit Trails
Because AI assistants may retry calls due to network instability, the log_activity tool requires a request_id UUID to ensure idempotency. If a duplicate request_id with the same payload arrives, the server returns the existing entry rather than creating a duplicate tax record. Every write is stamped with source: "AI assistant" and the OAuth client ID, providing a clear audit trail for CPAs reviewing material participation hours. This granular tracking prevents synthetic data from leaking into real user logs, especially when using the isolated reviewer account for app directory approvals.
Key Takeaways
- The MCP server surface for tool-only apps is small enough to implement in raw JSON-RPC without an SDK.
- Idempotency via request_id is critical for AI-driven writes to prevent duplicate entries in audit-sensitive logs.
- Separating scopes (e.g., activities:read vs. evidence:write) allows users to grant granular permissions to assistants.
- Validation rules must be enforced server-side, not just in prompt instructions, to prevent hallucinated or invalid data.
The Bottom Line
This implementation proves that you don't need heavy frameworks to build robust MCP servers; sometimes, a few hundred lines of precise, hand-written JSON-RPC logic are all you need to bridge the gap between traditional web apps and AI agents.