1
development
Jonathan Miller edited this page 2026-05-20 01:25:21 +00:00

Home

Development Guide

Best practices and guidelines for developing MCP servers built from the Template-MCP template.


Prerequisites

  • Node.js 20.0.0 or later
  • npm (included with Node.js)
  • TypeScript knowledge (strict mode, ES modules)
  • A REST API to bridge (with documentation and credentials)

Getting Started

1. Create a Repo from the Template

On Gitea, click Use this template on the Template-MCP page.

Name your repo following the convention:

  • mcp-moko{service} for service-specific servers (e.g., mcp-mokowaas, mcp-mokocrm)
  • {function}-mcp for function-specific servers (e.g., ssh-mcp, deploy-mcp)

2. Clone and Install

git clone git@git.mokoconsulting.tech:MokoConsulting/my-mcp.git
cd my-mcp
npm install

3. Update Project Metadata

File What to Change
package.json name, description, version
src/config.ts Config file name (default: ~/.<project>.json)
scripts/setup.mjs Prompts and defaults for the setup wizard
README.md Project overview and tool documentation

Project Structure

my-mcp/
├── src/
│   ├── index.ts       # Server entry point -- tool registration
│   ├── client.ts      # HTTP client (ApiClient class)
│   ├── config.ts      # Config loader (multi-connection)
│   └── types.ts       # TypeScript interfaces
├── scripts/
│   └── setup.mjs      # Interactive setup wizard
├── dist/              # Compiled output (gitignored)
├── package.json
├── tsconfig.json
└── Makefile

Adding MCP Tools

Tools are registered in src/index.ts. Each tool maps to one or more REST API operations.

1. Define the Tool

server.tool(
  'resource_list',                    // Tool name (snake_case)
  'List all resources with optional search',  // Description
  {
    search: z.string().optional().describe('Search query'),
    limit: z.number().optional().describe('Max results'),
    ...PaginationParams,
    ...ConnectionParam,
  },
  async ({ search, limit, page, connection }) => {
    const client = getClient(connection);
    const params: Record<string, string> = {};
    if (search) params.search = search;
    if (limit) params.limit = String(limit);
    if (page !== undefined) params.page = String(page);

    const result = await client.get('/api/resources', params);
    return formatResponse(result);
  }
);

2. Use Shared Helpers

The template provides these reusable patterns:

Helper Purpose
ConnectionParam Zod spread for optional connection parameter
PaginationParams Zod spread for limit and page parameters
formatResponse() Normalizes API responses into MCP text content
paginationQuery() Builds pagination query params from input
getClient() Resolves a named connection to an ApiClient instance

3. Naming Conventions

Pattern Example
List resources resource_list
Get single resource resource_get
Create resource resource_create
Update resource resource_update
Delete resource resource_delete
Custom actions resource_action_name

The ApiClient

src/client.ts handles all HTTP communication:

const client = new ApiClient(connection);

// GET with query params
const result = await client.get('/api/endpoint', { key: 'value' });

// POST with JSON body
const result = await client.post('/api/endpoint', { name: 'New Resource' });

// PUT, PATCH, DELETE
const result = await client.put('/api/endpoint/1', { name: 'Updated' });
const result = await client.patch('/api/endpoint/1', { status: 'active' });
const result = await client.delete('/api/endpoint/1');

The client uses node:https / node:http (not fetch) for reliable self-signed certificate support. Set "insecure": true in the connection config to skip TLS verification.


Configuration System

The multi-connection config at ~/.<project>.json:

{
  "defaultConnection": "production",
  "connections": {
    "production": {
      "baseUrl": "https://api.example.com",
      "apiKey": "prod-key"
    },
    "staging": {
      "baseUrl": "https://api-staging.example.com",
      "apiKey": "staging-key",
      "insecure": true
    }
  }
}

Every tool accepts an optional connection parameter. If omitted, the default connection is used. This lets users target different environments from the same MCP server.


Build and Run

npm run build        # Compile TypeScript to dist/
npm start            # Run the MCP server (stdio)
npm run dev          # Watch mode for development
npm run clean        # Remove dist/
npm run lint         # Run ESLint
npm run setup        # Interactive config setup wizard

Or use Make:

make build           # npm run build
make dev             # npm run dev
make clean           # npm run clean
make lint            # npm run lint

Testing

Manual Testing with Claude Code

  1. Build the server: npm run build
  2. Register it in .mcp.json or ~/.claude.json:
{
  "mcpServers": {
    "my-mcp": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/my-mcp/dist/index.js"]
    }
  }
}
  1. Restart Claude Code and test the tools interactively

Testing API Responses

Use npm run dev for watch mode. Make changes to src/index.ts, and the server recompiles automatically.


Branching Strategy

Branch Purpose
main Stable release
dev Integration branch
feature/* Feature work (merges to dev)

Common Patterns

Handling Paginated APIs

server.tool(
  'items_list',
  'List items with pagination',
  {
    ...PaginationParams,
    ...ConnectionParam,
  },
  async ({ limit, page, connection }) => {
    const client = getClient(connection);
    const params = paginationQuery({ limit, page });
    const result = await client.get('/api/items', params);
    return formatResponse(result);
  }
);

Handling File Uploads

// For APIs that accept multipart form data,
// extend ApiClient with a custom method or use
// the raw api_request tool for complex cases.

Error Handling

formatResponse() handles error normalization. For custom error handling:

const result = await client.get('/api/resource/' + id);
if (result.statusCode === 404) {
  return { content: [{ type: 'text', text: `Resource ${id} not found` }] };
}
return formatResponse(result);


Repo: Template-MCP · MokoStandards

Revision Date Author Description
1.0 2026-05-19 Moko Consulting Initial version