StackpressGitHub

Guides

810 MCP

Learn how Stackpress exposes app behavior as MCP tools that an agent can call. MCP matters because it gives an AI assistant a structured way to ask the app for work instead of relying only on prose, shell commands, or guessed file edits.

Previously: The previous course, AI, introduced AI as an assistance layer that still needs evidence. This course focuses on the implemented Stackpress AI package and the tool registry it builds from config and generated client output.

810.1. Why MCP Matters

An MCP tool is like a labeled doorway into app behavior. The label tells the agent what the tool does, the input schema tells it what payload is valid, and the backing Stackpress event does the real work.

In packages/stackpress-ai/src/plugin.ts, Stackpress builds a registry from mcp.tools, creates an McpServer, and registers each tool by name. When the tool is called, the handler validates the input, resolves the configured Stackpress event, and converts the response results into MCP text output.

810.2. The Smallest Config Shape

A scaffolded app keeps an example MCP config in config/common.ts. It is intentionally not enabled by default, but it shows the shape Stackpress AI expects once the package and plugin are added.

export const mcp = {
  name: 'Stackpress App MCP',
  version: '1.0.0',
  route: '/mcp',
  mode: 'stateful' as 'stateful',
  tools: [
    {
      name: 'article_search',
      title: 'Search Articles',
      description: 'Returns published articles that match the search query.',
      type: 'public' as 'public',
      method: 'GET' as 'GET',
      event: 'article-search',
      input: {
        type: 'object',
        properties: {
          q: { type: 'string' },
          limit: { type: 'number' }
        }
      },
      data: {}
    }
  ]
};

This example defines one public tool named article_search. When an agent calls it, Stackpress validates the payload against the JSON Schema under input, then resolves the article-search event with the caller payload merged into data.

810.3. Config Tools And Plugin Tools

A tool can be described directly in config, or it can be resolved through a plugin-mode event. Direct config is easiest when the operation is already known, while plugin mode lets another part of the app provide the final tool description.

{
  mode: 'plugin' as 'plugin',
  type: 'user' as 'user',
  event: 'comment-create-tool',
  scopes: [ 'comments.write' ]
}

This example asks Stackpress to resolve comment-create-tool before registering the MCP tool. The config still provides important overrides, such as the user tool type and the required comments.write scope.

810.4. What The Plugin Registers

The AI plugin does not register MCP behavior unless the app config has an mcp key. During listen, it loads generated client tools when the client plugin exists, registers an mcp factory, registers mcp-stdio, mcp-http, and mcp-sse events, and attaches HTTP or SSE transports when the config says to do that.

That order matters because generated tools can attach plugin-mode resolver events before the MCP server registry is built. If generated tools are missing, config-defined tools can still work, but plugin-mode tools may not resolve into a complete registry.

810.5. Mistakes To Avoid

MCP mistakes usually happen when the connection is treated as the whole feature. The connection only proves the agent can reach the server; each tool still needs its backing event, schema, scopes, and response shape checked.

810.5.1. Stop At A Working Connection

{
  name: 'article_search',
  event: 'article-search'
}

This example is only valid if article-search is a real event and the event returns data in the shape the MCP caller expects. The tool name is the label the agent sees, but the event is the Stackpress behavior that actually runs.

810.5.2. Add A Tool Without A Real Event

{
  name: 'article_search',
  event: 'article-list'
}

This example looks plausible, but it is wrong if the app only generated article-search. The fix is not to rename the MCP tool randomly; the fix is to point the tool at the event that exists or implement the event intentionally.

810.5.3. Treat Example Config As Enabled Behavior

// MCP is not enabled by default.

The scaffolded MCP config is an example. The comment in config/common.ts says it is not enabled by default, so an app still needs the package, plugin registration, and active config wiring before these tools become reachable.

Learning checkpoint: Before moving on, make sure you can explain how mcp.tools becomes an MCP registry. You should also be able to point to the config tool, the Stackpress event behind it, and the evidence that the app actually registers the AI plugin.

Next course: Continue with stdio Transport. That course shows one local way an agent process can attach to the MCP server.