Expose your app to Claude Desktop via MCP

@happyvertical/smrt-app-mcp turns your running SMRT app's objects into Model Context Protocol tools, served over HTTP from your own routes. A small stdio bridge then connects Claude Desktop to that HTTP surface. This guide wires both ends.

How it fits together

text
Claude Desktop ──stdio──► smrt-mcp-bridge ──HTTP──► your app
                                                  β”œβ”€ GET  /api/mcp/tools
                                                  └─ POST /api/mcp/call
                                                        β”‚
                                                        β–Ό
                                            @smrt() objects β†’ MCP tools

Why a bridge? Claude Desktop launches MCP servers as local processes that talk over stdio. Your app speaks HTTP. The bridge is a thin stdio-to-HTTP adapter that also attaches your auth token.

Step 1 β€” Make objects MCP-callable

Only objects you opt in are exposed. Set mcp: true on the @smrt() decorator (the same flag that powers the generated MCP tools).

typescript
// src/lib/models/Application.ts
import { smrt, SmrtObject } from '@happyvertical/smrt-core';

@smrt({ mcp: true })
export class Application extends SmrtObject {
  applicantName: string = '';
  status: string = 'submitted';
  approvedByUserId: string = '';
}

Step 2 β€” Build the MCP server

createMcpAppServer() returns { listTools, callTool } wired to your objects. You decide which classes are reachable (allowedClassNames), which tools are public, and any per-tool guards (workflowAssertions).

typescript
// src/lib/server/mcp.ts
import { createMcpAppServer, McpAccessError } from '@happyvertical/smrt-app-mcp';

export const mcpServer = createMcpAppServer({
  // Thunk returning the SMRT options (db, ai, …) for each call.
  smrtOptions: () => ({ db: { type: 'postgres', url: process.env.DATABASE_URL! } }),

  serverInfo: { name: 'my-app', version: '0.1.0' },

  // Allow-list: only these classes' tools are reachable.
  allowedClassNames: ['Application'],

  // Tools matching these patterns are callable without auth (optional).
  publicToolPatterns: () => ['application_list'],

  // Per-tool guards run before a mutating tool executes.
  workflowAssertions: {
    application_update: (args, user) => {
      if (!user?.id) throw new McpAccessError(401, 'sign in first');
      args.approvedByUserId = user.id; // stamp the actor
    }
  }
});

Step 3 β€” Mount the routes

The ./sveltekit subpath provides handlers for the two endpoints the bridge calls.

typescript
// src/routes/api/mcp/tools/+server.ts
import { mountMcpToolsRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
import { mcpServer } from '$lib/server/mcp';

export const GET = mountMcpToolsRoute(mcpServer);
typescript
// src/routes/api/mcp/call/+server.ts
import { mountMcpCallRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
import { mcpServer } from '$lib/server/mcp';

export const POST = mountMcpCallRoute(mcpServer);

That is the whole server side. Run your app and the MCP surface is live at /api/mcp/tools and /api/mcp/call.

Step 4 β€” Install and authenticate the bridge

The client side uses @happyvertical/smrt-app-cli, which ships a generic smrt-mcp-bridge bin. It resolves the app URL and bearer token from environment variables prefixed by --env-prefix. Pick a prefix for your app (here: MYAPP) β€” it reads MYAPP_SERVER_URL and MYAPP_TOKEN.

bash
pnpm add -g @happyvertical/smrt-app-cli

If your app requires auth, get a token through the device-code login flow shipped by smrt-users (this stores the token in ~/.config/<app>):

bash
# Either log in interactively (stores a token for the bridge to read) …
MYAPP_SERVER_URL=https://my-app.example.com smrt-app-cli auth login

# … or export the token directly for the bridge
export MYAPP_TOKEN=your-bearer-token

Step 5 β€” Register with Claude Desktop

Add the bridge to Claude Desktop's config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
json
{
  "mcpServers": {
    "my-app": {
      "command": "smrt-mcp-bridge",
      "args": ["--env-prefix=MYAPP", "--name=my-app", "--version=0.1.0"],
      "env": {
        "MYAPP_SERVER_URL": "https://my-app.example.com",
        "MYAPP_TOKEN": "your-bearer-token"
      }
    }
  }
}

Restart Claude Desktop. Your app's allow-listed objects now appear as tools (for example application_list, application_get, application_update), and Claude can call them in conversation.

Defaults and overrides

SettingSourceDefault
App URL${PREFIX}_SERVER_URL β†’ config file β†’ --default-server-urllocal dev URL
Auth token${PREFIX}_TOKEN β†’ stored confignone (header omitted)
Tools endpointtoolsPath option/api/mcp/tools
Call endpointcallPath option/api/mcp/call

Related

Verified against SMRT v0.29.34.