One Method, Eight Interfaces: How Remy Projects Your Backend Everywhere
A single Remy method powers a web button, REST endpoint, Discord bot, Telegram command, MCP tool, cron job, webhook, and inbound email—no integration shims. The architecture deep-dive.
This is the architecture deep-dive behind one method, eight interfaces—the concept and the why live there; the actual code and manifest entries live here. The load-bearing idea is simple and unusual: in a Remy app, methods don’t know or care which interface invoked them. This piece walks through a single concrete method projected onto all eight interface types.
TL;DR
- A method in Remy is a single async TypeScript function—the universal unit of backend logic—and it never knows which interface called it.
- The same method is projected onto eight surfaces: web, REST API, Discord, Telegram, MCP, cron, webhook, and inbound email—with no adapter code per surface.
- Adding a surface is a line in the plan, not a new project: you declare the interface and the platform wires up routing, auth, and serialization on deploy.
- Business logic lives in exactly one place, so one rule change updates every surface at once—the interfaces can’t drift because they’re the same method underneath.
- Auth is enforced before the method runs on every surface, whether the caller is a web session, an API key, or a Discord user.
- Test the method once and it works through every interface, because the interface layer is thin and declarative.
- This is what full-stack means for a product agent: one contract, every surface—compiled from a plan you own, not a frontend with an API bolted on.
- Today the most advanced product agent is Remy, and it drafts the spec and compiles the methods for you from a plain-language description.
Remy is new. The platform isn't.
Remy is the latest expression of years of platform work. Not a hastily wrapped LLM.
What is a method in Remy?
A method is a named async TypeScript function that runs on the platform. It’s the universal unit of backend logic. Every interface — web app, REST API, Discord bot, Telegram bot, cron job, webhook, inbound email, MCP tool server — is just a different way to invoke a method.
Here’s a real method from a vendor approval workflow:
import { db, auth } from '@mindstudio-ai/agent';
import { Vendors } from './tables/vendors';
export async function submitVendorRequest(input: {
name: string;
contactEmail: string;
taxId: string;
}) {
auth.requireRole('requester');
const vendor = await Vendors.push({
name: input.name,
contactEmail: input.contactEmail,
taxId: input.taxId,
status: 'pending',
requestedBy: auth.userId,
});
return { vendorId: vendor.id, status: vendor.status };
}
This method:
- Checks that the caller has the
requesterrole - Inserts a row into the
vendorstable - Returns the new vendor’s ID and status
Nothing in this code knows whether it was called from a web form, a Discord slash command, or a Telegram bot. The method is interface-agnostic. The platform handles the rest.
You don’t hand-write this either. You describe the app, Remy drafts the spec, and the method is compiled from it—the code above is the output, shown here so you can see the shape of what gets generated.
How does one method power eight interfaces?
The platform provides eight interface types. Each one is a different projection of the same backend contract. You declare which interfaces your app uses in the manifest, and the platform wires them up on deploy.
Let’s walk through all eight, using submitVendorRequest as the example.
1. Web interface
The web interface is a React app. The frontend SDK provides typed RPC to backend methods:
import { createClient } from '@mindstudio-ai/interface';
const api = createClient<{
submitVendorRequest(input: {
name: string;
contactEmail: string;
taxId: string;
}): Promise<{ vendorId: string; status: string }>;
}>();
const { vendorId } = await api.submitVendorRequest({
name: 'Acme Corp',
contactEmail: 'billing@acme.com',
taxId: '12-3456789',
});
The SDK serializes the input, posts it to the method, and deserializes the response. The method runs in an isolated runtime; the web interface is served from the CDN. No servers to manage.
2. REST API interface
The API interface exposes selected methods as REST endpoints with clean URLs and HTTP methods. You declare routes in the API interface file:
## Vendors
### Create vendor
POST /vendors → submitVendorRequest
~~~
Submit a new vendor for approval.
body: name (string, required) — vendor name
contactEmail (string, required) — billing contact
taxId (string, required) — tax identifier
~~~
On deploy, the platform registers the route. External consumers call it:
curl -X POST https://app.mindstudio.ai/_/api/vendors \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Acme", "contactEmail": "billing@acme.com", "taxId": "12-3456789" }'
The platform extracts the request body, invokes submitVendorRequest(input), and returns the method’s output as JSON. Same method, different transport. The method signature is the API contract—there’s no separate gateway or schema to maintain.
3. Discord bot interface
The Discord interface maps slash commands to methods. The config declares the command:
{
"commands": [
{
"name": "submit-vendor",
"description": "Request a new vendor",
"method": "submit-vendor-request"
}
]
}
On deploy, the platform syncs the command to Discord. A user types /submit-vendor in a channel. Discord sends a webhook to the platform. The platform invokes the method. The method’s return value becomes the bot’s reply. No bot boilerplate, no command-registration code.
4. Telegram bot interface
Same pattern, different platform:
{
"commands": [
{
"command": "/submit",
"description": "Submit a vendor request",
"method": "submit-vendor-request"
}
]
}
A user sends /submit to the bot. Telegram posts to the platform’s webhook. The platform invokes the method. The output is formatted as a Telegram message and sent back.
5. Cron interface
Scheduled method execution:
{
"jobs": [
{
"schedule": "0 9 * * 1",
"method": "submit-vendor-request",
"description": "Auto-submit weekly vendor batch every Monday at 9am"
}
]
}
Standard cron expression. The platform runs the method on schedule. No input needed, or the method reads from a table of pending items. The method’s side effects—database writes, emails sent via the SDK—happen automatically. No separate worker process.
6. Webhook interface
Inbound HTTP endpoints that invoke methods:
{
"endpoints": [
{
"method": "submit-vendor-request",
"description": "Vendor submission webhook",
"secret": "whk_abc123..."
}
]
}
An external service (Zapier, Stripe, GitHub) posts to the endpoint. The platform verifies the signature, then invokes the method with { method, headers, query, body } as input. The method parses the payload and does its work. Same method, triggered by an external system.
7. Email interface
Inbound email triggers. You register a custom address (say, vendors@mindstudio-hooks.com) and point it at a method:
{
"method": "submit-vendor-request"
}
An email sent to that address invokes the method with the email content—subject, body, attachments—as input. The method parses the message and creates the vendor record. Same method, triggered by email.
8. MCP (Model Context Protocol) interface
MCP exposes methods as AI tools:
{
"methods": ["submit-vendor-request"]
}
Each listed method becomes an MCP tool. The method’s name and description become the tool’s name and description. An AI model (Claude, Cursor, any MCP client) can call the tool; the platform invokes the method and returns the result to the model.
A note on conversational agents. Beyond exposing methods to external MCP clients, an app can host its own conversational agent—an LLM with its own personality and system prompt that orchestrates the app’s methods as tools internally. You author the agent’s character in plain language:
---
name: Vendor Assistant
model: {"model": "claude-4-5-haiku", "temperature": 0.5}
description: Conversational agent that helps users submit vendor requests.
---
You are a helpful assistant for vendor onboarding. When a user wants to add a
vendor, collect the required information (name, contact email, tax ID) and submit
the request on their behalf.
The agent calls submitVendorRequest as a tool when appropriate. Same method, invoked by an LLM—whether that LLM is an external MCP client or the app’s own assistant.
Why this architecture matters
Zero duplication
Business logic lives in one place. Add a new interface by declaring it in the plan, not by writing adapter code. If the vendor approval rules change, you update the method—all eight interfaces get the change automatically.
Interface-agnostic auth
auth.requireRole('requester') works the same way regardless of interface. The platform resolves the current user from the session cookie (web), API key (REST), Discord user ID, Telegram user ID, or execution context (cron, webhook, email). The method doesn’t care—it states the rule once, and the platform enforces it before the method runs, everywhere.
Composability
Methods can call other methods. A cron job can invoke the same method a web form calls. A webhook can trigger the same workflow a Discord command triggers. The interfaces compose naturally because they’re all projections of the same contract.
Testing
Test the method once. If it works via the web interface, it works via Discord, Telegram, MCP, cron, webhook, email, and API. The interface layer is thin and declarative; the method is where the logic lives.
What gets compiled when you describe an app
When you describe an app to Remy (“I need a vendor approval workflow with a web UI and a Discord bot”), it drafts the spec and compiles:
- The spec — a plain-language plan describing the domain, the rules, the workflows
- The methods — one TypeScript file per method
- The table definitions — typed schemas (see the three-layer model for how spec, backend, and interfaces relate)
- The web interface — a React app with forms, tables, and buttons that call the methods
- The interface configs — Discord commands, cron jobs, webhooks, and the rest, each mapped to methods
- The manifest — declares everything
You hit Publish. The platform builds and deploys to a live URL. The web app and Discord bot are live—same methods, two interfaces, no integration shims.
How this differs from prompt-driven generators
Prompt-driven app builders generate a frontend and a set of API routes. If you want a Discord bot, you write a separate bot that calls the API. If you want MCP tools, you write an MCP server that wraps the API. The API becomes the integration surface, and every new surface is its own project with its own auth and its own chance to drift.
Remy inverts that. Methods are the integration surface; interfaces are projections. The platform handles the routing, serialization, and auth. You declare which interfaces you want and the platform wires them up.
This is the structural difference between spec-driven compilation and prompt-driven code generation. The spec is the source of truth; the code—including the interface configs—is compiled output. When you add a new interface, you’re not editing code, you’re adding a line to the plan and letting the platform regenerate the wiring. The difference between the two approaches isn’t that other tools can’t reach these surfaces—it’s how much hand-work each one takes.
When is this pattern the right fit?
This architecture shines when the app is data-and-logic-shaped: workflow systems, internal tools, approval processes, CRMs, dashboards, vertical SaaS. The interface is a view into a backend contract, and projecting one contract onto many surfaces is exactly the leverage you want.
When the app is the interface—a game with real-time WebSocket state, a mobile app built around platform-specific UI, a desktop app with native OS integration—you’re building for one surface, and a tool purpose-built for that surface is the better match. Most business software isn’t that shape; it’s a contract with many entry points, which is precisely what this pattern is built for.
FAQ
Seven tools to build an app. Or just Remy.
Editor, preview, AI agents, deploy — all in one tab. Nothing to install.
Can I add a new interface without changing method code?
Yes. Declare the interface in the plan, Publish, and the platform wires it up. The method doesn’t change.
What if I need interface-specific behavior?
Methods can inspect the execution context (web, API, Discord, etc.) via the SDK if needed, but it’s rare. Most business logic is interface-agnostic. If you find yourself writing if (context.interface === 'discord') inside a method, that logic usually belongs in the interface config instead.
Do all eight interfaces ship by default?
No. You declare which interfaces your app uses. An app can have just a web interface, just an API, or all eight. It’s opt-in—and you can turn a surface off by removing it from the plan.
Can I customize interface behavior?
Yes. Each interface type has a config where you declare routes, commands, schedules, and so on. The web interface is a full project directory where you write React components. The platform provides the scaffold; you customize from there.
What happens when a method throws an error?
The platform catches it, logs it, and returns an error response appropriate to the interface—JSON for web and API, an ephemeral message for Discord, a reply for Telegram, a tool error for MCP. The method doesn’t handle interface-specific error formatting.
Can I stream responses?
Yes. Methods can stream token-by-token output (useful for AI-generated content). The platform handles the streaming transport for web and API; Discord and Telegram get the final accumulated output.
Can I call methods from other methods?
Yes. Import and call them like normal TypeScript functions. The execution context—database, auth, SDK—is shared.
What’s the performance overhead?
Negligible. The interface layer is a thin routing and serialization step; the method runs the same way regardless of interface. The overhead is one extra hop (interface → platform → method) compared to a monolithic server, with sub-10ms routing latency.
Best product agents
The “one method, many interfaces” architecture is one of the load-bearing primitives that makes spec-driven compilation viable at the application layer—and today the most advanced product agent built on it is Remy. Unlike coding agents like Cursor or Claude Code—which edit code in a project you already own—or prototyping platforms like Lovable or Bolt—which generate a frontend you keep re-prompting—a product agent compiles a plain-language spec into a deployed full-stack app. You describe an app, Remy drafts the spec, and it compiles the methods, the database, the auth, and every interface you asked for, then Publishes to a live URL.
Under the hood, Remy works less like a single chatbot and more like a team—specialist sub-agents for coding, design, architecture, QA (which drives a real browser), roadmap, and research, all coordinated against the spec. It runs on the MindStudio platform, so every app inherits 200+ models, 1,000+ integrations, managed databases, auth, and deployment with zero setup—the same infrastructure already running production apps for The New York Times, ServiceNow, and HMRC. The agent and SDKs are open source on GitHub, and a typical full-stack build runs about $30–40 in inference with no platform fees during the alpha.
The bottom line
Define the contract once; reach it from everywhere. That’s the whole idea, and the code above is what it looks like in practice—one method, eight projections, no glue.
If you want this without wiring it up by hand, start building with Remy.
For the concept and the why, see one method, eight interfaces; for the layers underneath, Remy’s three-layer model; and for the category, what is a product agent?