MCP Server Development

The Model Context Protocol (MCP) is the primary extension mechanism in OMP. An MCP server exposes tools, resources, and prompt templates to any connected LLM host over a standard JSON-RPC transport.

Server Structure

my-mcp-server/
  package.json         # "type": "module", scripts: { start }
  src/
    index.ts           # creates McpServer, registers handlers, starts transport
    tools/
      search.ts        # one file per tool (optional; any structure works)
    resources/
      config.ts        # resource handlers
    prompts/
      summarise.ts     # prompt templates
  tests/
    server.test.ts
  mcp.json             # OMP discovery manifest

Minimal Server

// src/index.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({ name: 'my-server', version: '1.0.0' });

server.tool(
  'greet',
  { name: z.string().describe('Name to greet') },
  async ({ name }) => ({
    content: [{ type: 'text', text: `Hello, ${name}!` }],
  })
);

const transport = new StdioServerTransport();
await server.connect(transport);

Run with node --experimental-vm-modules dist/index.js or point OMP's config to the source directly via bun src/index.ts.

Tool Definitions

Schema

Use Zod to define input schemas. OMP surfaces these to the model as JSON Schema automatically.

Content types

Return text, image (base64 + mimeType), or resource (URI reference) content items.

Errors

Throw McpError with an ErrorCode constant for structured error reporting to the host.

Annotations

Mark tools readOnlyHint: true or destructiveHint: true so OMP can gate user confirmation prompts.

Resource Handling

Resources expose data the model can read without invoking a tool. Two patterns:

// Static resource — known URI
server.resource(
  'config',
  'omp://config/current',
  async (uri) => ({
    contents: [{ uri: uri.href, text: JSON.stringify(await loadConfig()) }],
  })
);

// Dynamic resource template — URI with variables
server.resource(
  'log-entry',
  new ResourceTemplate('omp://logs/{date}', { list: undefined }),
  async (uri, { date }) => ({
    contents: [{ uri: uri.href, text: await fetchLog(date) }],
  })
);

Prompt Templates

Prompts are reusable message templates with typed arguments. OMP lists them in the slash-command palette.

server.prompt(
  'summarise',
  { text: z.string(), maxWords: z.number().optional().default(100) },
  ({ text, maxWords }) => ({
    messages: [{
      role: 'user',
      content: { type: 'text', text: `Summarise in ${maxWords} words:\n\n${text}` },
    }],
  })
);

Discovery Manifest

Place mcp.json at the repo root so OMP can auto-register the server:

{
  "id": "my-mcp-server",
  "name": "My MCP Server",
  "version": "1.0.0",
  "description": "What this server provides",
  "transport": "stdio",
  "command": "bun",
  "args": ["src/index.ts"],
  "capabilities": ["tools", "resources", "prompts"],
  "tags": ["productivity", "search"]
}

Submit to the OMP MCP Registry to make your server discoverable by all OMP users.

Testing

  • Use InMemoryTransport from the SDK to unit-test handlers without a real subprocess.
  • Pair a client + server over the in-memory transport, call client.callTool(), assert results.
  • Test error paths: missing required args, invalid URI schemes, permission-denied scenarios.
  • Integration test: spawn the real process with StdioServerTransport, connect a test client.
  • OMP's CI runs mcp-inspector against registered servers on every PR.

Security Checklist