TypeScript
Use the framework's TypeScript types to get autocompletion, type-checked inputs, and compile-time errors on your handler code.
@mctx-ai/mcp ships full TypeScript definitions. Handler argument types are inferred from your T schema definitions, and the return types, context shape, and options interfaces are all typed.
tsconfig setup
The scaffolded project from npm create mctx-app includes a working tsconfig.json. If you are adding TypeScript to an existing project, the minimum recommended configuration is:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true
}
}strict: true enables the full suite of TypeScript checks, including strictNullChecks. This is important for ask (which is AskFunction | null) and ctx (which is McpContext | undefined). Without strict mode, null guards compile but carry no enforcement.
Type imports
Import handler types from @mctx-ai/mcp:
import type {
ToolHandler,
GeneratorToolHandler,
ResourceHandler,
PromptHandler,
McpContext,
} from "@mctx-ai/mcp";| Type | Use for |
|---|---|
ToolHandler | Standard (non-generator) tool handlers |
GeneratorToolHandler | Generator tools that yield progress notifications |
ResourceHandler | Resource handlers (static and URI template) |
PromptHandler | Prompt handlers returning strings or conversations |
McpContext | The ctx parameter type if you need it explicitly |
Typed handler example
Annotate a handler with ToolHandler and TypeScript enforces the call signature, including the null check on ask:
import { createServer, T, log } from "@mctx-ai/mcp";
import type { ToolHandler } from "@mctx-ai/mcp";
const server = createServer();
const greet: ToolHandler = (args, ask, ctx) => {
// args is Record<string, any> — cast to your expected shape
const { name } = args as { name: string };
log.info({ name, userId: ctx?.userId });
// ask is AskFunction | null | undefined — TypeScript requires the guard
if (ask) {
// ask() is callable here — TypeScript narrows the type
}
return `Hello, ${name}!`;
};
greet.description = "Greet a person by name";
greet.input = {
name: T.string({ required: true, description: "Name to greet" }),
};
greet.annotations = {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: false,
idempotentHint: true,
};
server.tool("greet", greet);
export default { fetch: server.fetch };TypeScript infers that ask is AskFunction | null | undefined, so calling ask(prompt) without the guard is a type error. The same applies to ctx?.userId — ctx is optional, so the optional chain is required.
Handler type generics
The handler types accept a generic for the args shape, which avoids the cast inside the handler body:
// Without generic — cast required inside
const myTool: ToolHandler = (args) => {
const { query } = args as { query: string };
return query;
};
// With explicit cast at declaration — the type is narrow from the start
type MyArgs = { query: string };
const myTool: ToolHandler = (args: MyArgs) => {
return args.query; // no cast needed
};Both approaches compile. The explicit parameter type is preferred for longer handlers where the cast at the top would be far from the usage.
buildInputSchema
buildInputSchema is a low-level export that compiles a T schema map into a JSON Schema object. The framework calls it internally when you set handler.input. You do not need it in normal handler code.
It is useful if you are building tools programmatically or need the raw JSON Schema for another purpose:
import { T, buildInputSchema } from "@mctx-ai/mcp";
const schema = buildInputSchema({
name: T.string({ required: true }),
age: T.number({ min: 0, max: 150 }),
});
// => { type: "object", properties: { name: {...}, age: {...} }, required: ["name"] }Next steps
- Testing — test typed handlers with standard test tooling
- Framework API Reference — all exported types in one place
See something wrong? Report it or suggest an improvement — your feedback helps make these docs better.
Logging
Use log.* to emit structured log messages from your handlers. See how logs appear in the dev server terminal and in the platform's log viewer.
Testing
Test your MCP server's handlers as plain functions, run integration tests via server.fetch, use MCP Inspector for manual testing, and smoke-test with curl.