Documentation Index
Fetch the complete documentation index at: https://redop.useagents.site/docs/llms.txt
Use this file to discover all available pages before exploring further.
Use .use(...) as the main composition primitive when your server grows past a single file.
The simplest mental model is:
- one feature folder owns one area of the server
- that folder exports one
Redop instance
- the root server imports those feature modules and attaches them with
.use(...)
Why this pattern works
This keeps composition obvious:
- folder structure matches server structure
- each feature can register its own tools, resources, prompts, middleware, and hooks
- the root server stays small
- naming stays explicit instead of being rewritten by a grouping helper
Recommended folder shape
src/
features/
notes/
create.ts
list.ts
index.ts
users/
get.ts
index.ts
index.ts
Feature module
Each feature folder builds one Redop module and exports it.
// src/features/notes/create.ts
import type { Redop } from "@redopjs/redop";
export function registerCreateNote(app: Redop) {
app.tool("notes.create", {
description: "Create a note",
inputSchema: {
type: "object",
properties: {
title: { type: "string" },
},
required: ["title"],
},
handler: ({ input }) => ({
id: crypto.randomUUID(),
title: input.title,
}),
});
}
// src/features/notes/list.ts
import type { Redop } from "@redopjs/redop";
export function registerListNotes(app: Redop) {
app.tool("notes.list", {
description: "List notes",
handler: () => ({
notes: [],
}),
});
}
// src/features/notes/index.ts
import { Redop } from "@redopjs/redop";
import { registerCreateNote } from "./create";
import { registerListNotes } from "./list";
const notes = new Redop();
registerCreateNote(notes);
registerListNotes(notes);
notes.resource("notes://{id}", {
name: "Note",
handler: async ({ params }) => ({
type: "text",
text: JSON.stringify({ id: params.id }),
}),
});
export default notes;
Root server
The main server becomes a small composition layer.
// src/index.ts
import { Redop } from "@redopjs/redop";
import notes from "./features/notes";
import users from "./features/users";
new Redop({
serverInfo: {
name: "composed-server",
version: "0.1.0",
},
})
.use(notes)
.use(users)
.listen(3000);
Naming guidance
Since Redop no longer rewrites names for you, make names explicit:
- tools:
notes.list, notes.create, users.get
- prompts:
notes.summarise
- resources:
notes://{id}, users://{id}/profile
This keeps the public MCP surface easy to read and stable over time.
When to use a plugin instead
Use a feature module when the registrations belong to one server.
Use definePlugin(...) when the behavior should be packaged and reused across multiple servers or projects.
Related pages