MCP Primitives

Expose tools, resources, and prompts to clients

These decorators turn methods into what MCP clients see: tools the model can call, resources it can read, and prompts users can pick. @InitialTool marks a tool to run when a client connects.

@Tool

Defines an MCP tool that AI models can invoke.

import { ToolDecorator as Tool, z, ExecutionContext } from '@nitrostack/core';

@Tool(options: ToolOptions)
@tool(name, description, input_schema, title=..., output_schema=..., annotations=..., task_support=..., visibility=..., examples=..., invocation=..., metadata=...)

input_schema is a Pydantic BaseModel. Related: @initial_tool, @widget, ToolAnnotations, ToolExamples, ToolInvocation.

Options:

PropertyTypeRequiredDescription
namestringYesUnique tool identifier (snake_case recommended)
titlestringNoHuman-readable display name
descriptionstringYesClear description of the tool's purpose
inputSchemaZodObjectNoZod schema for input validation
outputSchemaZodObjectNoZod schema for output validation
taskSupport'forbidden' | 'optional' | 'required'NoMCP Task support mode (default: 'forbidden')
visibility'visible' | 'hidden'NoCatalog visibility state (default: 'visible')
defaultVisiblebooleanNoConvenience alias (defaultVisible: false maps to visibility: 'hidden')
annotationsToolAnnotationsNoBehavioral hints for AI models
invocationToolInvocationMessagesNoUI status messages during execution
examplesobjectNoExample request/response for AI guidance and widget preview

ToolInvocationMessages:

PropertyTypeDescription
invokingstringMessage shown while tool is executing (e.g., "Loading...")
invokedstringMessage shown when tool completes (e.g., "Done")

ToolAnnotations:

PropertyTypeDefaultDescription
destructiveHintbooleantrueMay perform destructive updates
idempotentHintbooleanfalseSafe to retry with same arguments
readOnlyHintbooleanfalseDoes not modify state
openWorldHintbooleantrueMay interact with external systems
alwaysVisiblebooleanfalseTool remains directly visible even when progressive search transforms collapse catalog

Example:

@Tool({
  name: 'create_user',
  title: 'Create User Account',
  description: 'Create a new user account with email and profile information',
  inputSchema: z.object({
    email: z.string().email().describe('User email address'),
    name: z.string().min(2).describe('Full name'),
    role: z.enum(['user', 'admin']).optional().describe('User role')
  }),
  outputSchema: z.object({
    id: z.string(),
    email: z.string(),
    name: z.string()
  }),
  annotations: {
    destructiveHint: false,
    idempotentHint: false,
    readOnlyHint: false,
    openWorldHint: false
  },
  examples: {
    request: { email: 'user@example.com', name: 'Jane Doe' },
    response: { id: 'usr_123', email: 'user@example.com', name: 'Jane Doe' }
  }
})
async createUser(input: CreateUserInput, ctx: ExecutionContext) {
  return this.userService.create(input);
}

@Resource

Defines an MCP resource that provides data to AI models.

import { ResourceDecorator as Resource, ExecutionContext } from '@nitrostack/core';

@Resource(options: ResourceOptions)
@resource(uri, name, description, mime_type=..., annotations=...)

Options:

PropertyTypeRequiredDescription
uristringYesURI template with optional parameters
namestringYesHuman-readable resource name
titlestringNoHuman-readable display title
descriptionstringYesDescription of what data the resource provides
mimeTypestringNoContent MIME type (default: 'text/plain')
sizenumberNoSize in bytes (for binary resources)
annotationsResourceAnnotationsNoMetadata hints for clients
examplesobjectNoExample response

ResourceAnnotations:

PropertyTypeDescription
audience('user' | 'assistant')[]Who should see this resource
prioritynumberImportance (0.0-1.0, higher = more important)
lastModifiedstringISO 8601 timestamp of last modification

Example:

@Resource({
  uri: 'user://{userId}/profile',
  name: 'User Profile',
  title: 'User Profile Details',
  description: 'Complete user profile with preferences and settings',
  mimeType: 'application/json',
  annotations: {
    audience: ['user', 'assistant'],
    priority: 0.8,
    lastModified: '2024-01-15T10:30:00Z'
  }
})
async getUserProfile(uri: string, ctx: ExecutionContext) {
  const userId = uri.match(/user:\/\/([^\/]+)/)?.[1];
  const profile = await this.userService.getProfile(userId);
  return {
    contents: [{
      uri,
      mimeType: 'application/json',
      text: JSON.stringify(profile, null, 2)
    }]
  };
}

@Prompt

Defines an MCP prompt template for AI conversations.

import { PromptDecorator as Prompt, ExecutionContext } from '@nitrostack/core';

@Prompt(options: PromptOptions)
@prompt(name, description, arguments=[PromptArgument(...)])

Options:

PropertyTypeRequiredDescription
namestringYesUnique prompt identifier
titlestringNoHuman-readable display title
descriptionstringYesDescription of the prompt's purpose
argumentsArrayNoInput parameters for the prompt

Example:

@Prompt({
  name: 'code_review',
  title: 'Code Review Assistant',
  description: 'Generate a code review request with best practices checklist',
  arguments: [
    { name: 'language', description: 'Programming language', required: true },
    { name: 'focus', description: 'Review focus areas', required: false }
  ]
})
async getCodeReviewPrompt(args: { language: string; focus?: string }, ctx: ExecutionContext) {
  return [{
    role: 'user' as const,
    content: `Review this ${args.language} code focusing on: ${args.focus || 'general best practices'}`
  }];
}

@InitialTool

Marks a tool method to be automatically invoked when an MCP client establishes a connection session.

import { InitialTool, ToolDecorator as Tool, ExecutionContext } from '@nitrostack/core';

@InitialTool()

Parameters: None. Applied as a method decorator to methods already decorated with @Tool().

Behavior:

  • When an MCP client connects and initializes its session, NitroStack automatically executes all registered @InitialTool handlers.
  • Commonly used for bootstrapping client environment configuration, exchanging dynamic capabilities, and asserting initial authorization state.

Example:

import { Controller, ToolDecorator as Tool, InitialTool, ExecutionContext, z } from '@nitrostack/core';

@Controller('system')
export class SystemController {
  @Tool({
    name: 'bootstrap_session',
    description: 'Initializes the client session and returns server capabilities',
    inputSchema: z.object({}),
  })
  @InitialTool()
  async bootstrapSession(input: Record<string, never>, ctx: ExecutionContext) {
    ctx.logger.info('Session established', { sessionId: ctx.sessionId });

    return {
      serverName: 'Enterprise Order Management',
      version: '1.2.0',
      authenticatedSubject: ctx.auth?.subject ?? 'anonymous',
      capabilities: ['orders_create', 'orders_get_status'],
    };
  }
}

On this page