Framework API Reference
Complete reference for @mctx-ai/app — all exports, types, and patterns.
npm install @mctx-ai/appcreateServer(options?)
Creates an App instance (an MCP server).
import { createServer } from "@mctx-ai/app";
const app = createServer();Returns an object with .tool(), .resource(), .prompt(), and .fetch() methods.
Options
| Option | Type | Description |
|---|---|---|
instructions | string | Runtime instructions sent to AI clients at handshake. |
instructions
A string that tells AI clients what your server does and how to use it effectively. This is part of the MCP specification — it travels to the AI during the initial handshake, before any tool calls happen.
Purpose: The MCP instructions field helps AI clients decide when to use your server and how to use it well. Your tools do the actual work; instructions help the AI understand the context around them.
Best practice: Keep instructions concise. Describe what the server covers, name the key tools, and note any important constraints or input expectations. Let your tool descriptions handle the details.
const app = createServer({
instructions:
"Use 'get_weather' for current conditions and 'get_forecast' for 5-day predictions. " +
"Provide a city name or coordinates. All temperatures are in Celsius.",
});Maximum length: 2000 characters.
Runtime vs. registry: createServer({ instructions }) controls what AI clients receive at runtime — this is the effective value. The instructions field in package.json is used only when publishing to the MCP Community Registry. See package.json Configuration for details on when to use each.
app.tool(name, handler)
Register a tool that AI clients can call.
const greet = ({ name, greeting }) => `${greeting}, ${name}!`;
greet.description = "Greets a person";
greet.input = {
name: T.string({ required: true }),
greeting: T.string({ default: "Hello" }),
};
app.tool("greet", greet);Handler contract:
- Receives parsed arguments as first parameter
- Second parameter
askis alwaysnull(sampling not yet implemented) - Returns
string,object(auto-serialized), or MCP content array - Attach
.description,.input, and.annotationsas properties on the function - Errors are caught and returned as tool error responses with secrets redacted
Binary content types (ImageContent, AudioContent per MCP spec) are planned for a future release.
handler.annotations
Optional hints that tell AI clients how safe and consequential the tool is. All fields are optional booleans.
| Field | Type | Default | Description |
|---|---|---|---|
readOnlyHint | boolean | false | Tool only reads data — no writes, creates, or deletes |
destructiveHint | boolean | true | Tool can permanently destroy data |
openWorldHint | boolean | true | Tool calls external systems (HTTP APIs, databases, files) |
idempotentHint | boolean | false | Calling the tool multiple times with the same input produces the same result |
Defaults are pessimistic. Always set all four explicitly so clients can make accurate permission decisions.
const listRecords = ({ table }) => db.query(`SELECT * FROM ${table}`);
listRecords.description = "List all records in a table";
listRecords.input = { table: T.string({ required: true }) };
listRecords.annotations = {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: false,
};
app.tool("list_records", listRecords);See Tool annotations for the decision checklist and common patterns.
app.resource(uri, handler)
Register a resource. Use exact URIs for static resources, URI templates for dynamic ones.
// Static
const readme = () => "Content here";
readme.mimeType = "text/plain";
app.resource("docs://readme", readme);
// Dynamic (RFC 6570 Level 1 template)
const user = ({ userId }) => JSON.stringify({ id: userId });
user.mimeType = "application/json";
app.resource("user://{userId}", user);app.prompt(name, handler)
Register a prompt template. Return a string for single-message prompts, or use conversation() for multi-message.
// Single message
const review = ({ code }) => `Review: ${code}`;
review.input = { code: T.string({ required: true }) };
app.prompt("code-review", review);
// Multi-message
const debug = ({ error }) =>
conversation(({ user, ai }) => [user.say(`Debug: ${error}`), ai.say("Analyzing...")]);
debug.input = { error: T.string({ required: true }) };
app.prompt("debug", debug);app.fetch(request, env, ctx)
The fetch handler. Compatible with Cloudflare Workers and mctx platform.
export default { fetch: app.fetch };T (Type System)
Defines input schemas for tools and prompts. Produces JSON Schema.
import { T } from "@mctx-ai/app";T.string(options?)
| Option | Type | Description |
|---|---|---|
required | boolean | Mark as required |
description | string | Human-readable description |
enum | string[] | Allowed values |
default | string | Default value |
minLength | number | Minimum length |
maxLength | number | Maximum length |
pattern | string | Regex pattern |
format | string | Format hint (email, uri, etc.) |
T.number(options?)
| Option | Type | Description |
|---|---|---|
required | boolean | Mark as required |
description | string | Human-readable description |
min | number | Minimum value |
max | number | Maximum value |
default | number | Default value |
T.boolean(options?)
| Option | Type | Description |
|---|---|---|
required | boolean | Mark as required |
description | string | Human-readable description |
default | boolean | Default value |
T.array(options?)
T.array({ items: T.string() }); // string array
T.array({ items: T.number(), required: true }); // requiredT.object(options?)
T.object({
properties: {
name: T.string({ required: true }),
age: T.number(),
},
});buildInputSchema(input)
Compiles a fn.input object into a JSON Schema inputSchema with required array. Used internally by the framework — available if you need raw schema generation.
import { buildInputSchema } from "@mctx-ai/app";
const schema = buildInputSchema({
name: T.string({ required: true }),
age: T.number(),
});
// { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' } }, required: ['name'] }conversation()
Builds multi-message prompt responses.
import { conversation } from "@mctx-ai/app";
conversation(({ user, ai }) => [
user.say("Hello"), // text message
user.attach(data, "application/json"), // embedded data
user.embed("image://logo"), // embedded resource
ai.say("How can I help?"), // AI message
]);createProgress(total?)
Creates a step function for generator-based tools.
import { createProgress } from "@mctx-ai/app";
const task = function* ({ data }) {
const step = createProgress(3);
yield step();
yield step();
yield step();
return "Done";
};Call createProgress() without arguments for indeterminate progress. Progress steps are tracked internally. In the current HTTP transport, progress is tracked but not streamed -- the final result is returned when the generator completes.
log
Structured logging with RFC 5424 severity levels.
import { log } from "@mctx-ai/app";
log.debug("Detailed info");
log.info("Informational");
log.notice("Significant event");
log.warning("Warning");
log.error("Error");
log.critical("Critical");
log.alert("Immediate action needed");
log.emergency("System unusable");Logs are buffered internally. In the current HTTP transport, buffered logs are discarded after each request and are not visible in the dashboard. To write logs that appear in the real-time dashboard logs viewer, use console.log(), console.warn(), or console.error() instead.
Sampling (ask)
Tool handlers receive a second parameter named ask for LLM sampling. Sampling requires bidirectional communication, which is not available in the current HTTP transport.
ask always returns null in the current implementation. Sampling support is not yet implemented.
const smart = async ({ question }, ask) => {
// ask is always null currently -- sampling is not yet implemented
return `Answer: ${question}`;
};The ask parameter is reserved for future streaming transport support. Do not rely on it returning a value.
Security
The framework applies security protections automatically:
- Error sanitization — redacts AWS keys, JWTs, connection strings, Bearer tokens, API keys
- Size limits — prevents DoS via large request/response bodies
- URI validation — blocks
file://,javascript:,data:schemes - Path traversal — detects
../sequences including encoded variants - Prototype pollution — strips
__proto__,constructor,prototypekeys
These protections are internal and applied automatically. You don't need to configure them.
See Also
- Getting Started with the Framework — Build your first server in 5 minutes
- Tools, Resources, and Prompts — Practical examples and patterns
- Server Requirements — Package structure and deployment checklist
See something wrong? Report it or suggest an improvement — your feedback helps make these docs better.