AgentStack
SKILL verified Apache-2.0 Self-run

Mcp Server Builder

skill-jignesh-ponamwar-skills-mcp-mcp-server-builder · by Jignesh-Ponamwar

>

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add skill-jignesh-ponamwar-skills-mcp-mcp-server-builder

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Mcp Server Builder? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

MCP Server Builder Skill

MCP Architecture

An MCP server exposes three primitives to AI clients:

| Primitive | Purpose | When to Use | |-----------|---------|-------------| | Tools | Callable functions the agent invokes (tools/call) | Actions, API calls, computations | | Resources | Read-only data the agent fetches (resources/read) | Files, database records, configuration | | Prompts | Reusable prompt templates (prompts/get) | Guided workflows, structured interactions |


Option A: Python with FastMCP (Recommended)

pip install fastmcp

Minimal Server

# server.py
from fastmcp import FastMCP

mcp = FastMCP("My Tools Server", version="1.0.0")

@mcp.tool(description="Add two numbers together")
def add(a: float, b: float) -> float:
    """Add two numbers and return the result."""
    return a + b

@mcp.tool(description="Fetch the current weather for a city")
async def get_weather(city: str, unit: str = "celsius") -> dict:
    """Get current weather. unit: 'celsius' or 'fahrenheit'"""
    import httpx
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"https://wttr.in/{city}?format=j1")
        resp.raise_for_status()
        data = resp.json()
    return {
        "city": city,
        "temperature": data["current_condition"][0]["temp_C" if unit == "celsius" else "temp_F"],
        "condition": data["current_condition"][0]["weatherDesc"][0]["value"],
    }

if __name__ == "__main__":
    mcp.run()  # stdio mode (default)

Run Modes

# stdio (for Claude Code, Cursor - recommended for local tools)
python server.py

# SSE (for browser clients and remote access)
MCP_TRANSPORT=sse python server.py           # http://localhost:8000/sse

# Streamable HTTP (modern, bidirectional)
MCP_TRANSPORT=streamable-http python server.py

Resources

@mcp.resource("config://app-settings")
def get_app_settings() -> str:
    """Return current application settings."""
    import json
    return json.dumps({
        "version": "1.0.0",
        "features": ["auth", "analytics"],
        "max_users": 1000,
    })

@mcp.resource("file://{path}")  # template URI with parameter
def read_file(path: str) -> str:
    """Read a file by path."""
    import pathlib
    # IMPORTANT: validate path to prevent traversal
    base = pathlib.Path("./allowed_dir").resolve()
    target = (base / path).resolve()
    if base not in target.parents and base != target:
        raise ValueError("Path outside allowed directory")
    return target.read_text()

Prompts

@mcp.prompt(description="Generate a code review checklist for a PR")
def code_review_prompt(language: str, pr_description: str) -> str:
    return f"""Review the following {language} pull request:

{pr_description}

Check for:
1. Correctness - does it do what it claims?
2. Security - any vulnerabilities (injection, auth issues, path traversal)?
3. Performance - any N+1 queries, unnecessary allocations?
4. Error handling - all edge cases covered?
5. Tests - adequate coverage?

Use severity tags: [CRITICAL] [HIGH] [MEDIUM] [LOW]"""

Option B: TypeScript MCP SDK

npm install @modelcontextprotocol/sdk
// server.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'

const server = new Server(
  { name: 'my-tools-server', version: '1.0.0' },
  { capabilities: { tools: {}, resources: {} } }
)

// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'calculate',
      description: 'Perform arithmetic calculations',
      inputSchema: {
        type: 'object',
        properties: {
          expression: { type: 'string', description: 'Math expression, e.g. "2 + 2"' },
        },
        required: ['expression'],
      },
    },
  ],
}))

// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'calculate') {
    const { expression } = request.params.arguments as { expression: string }
    // Safe eval - use a proper math library in production
    const result = Function(`'use strict'; return (${expression})`)()
    return { content: [{ type: 'text', text: String(result) }] }
  }
  throw new Error(`Unknown tool: ${request.params.name}`)
})

// Start
const transport = new StdioServerTransport()
await server.connect(transport)

Tool Design Best Practices

Write Descriptions for LLMs, Not Humans

The description is what the agent reads to decide when to call the tool:

# ❌ Too vague
@mcp.tool(description="Database lookup")
def db_lookup(query: str) -> list: ...

# ✅ Tells the agent exactly when and how to use it
@mcp.tool(description="""Search the product catalog by keyword, category, or SKU.
Returns a list of matching products with id, name, price, and stock_count.
Use when the user asks about a product, wants to find items, or asks about availability.
Returns empty list if no products match. Max 20 results.""")
def search_products(query: str, category: str = "", limit: int = 20) -> list[dict]: ...

Return Structured Data

# ❌ String concatenation - hard for agent to parse
return f"User {user.name} has {len(orders)} orders totalling ${total}"

# ✅ Structured dict - agent can reason about individual fields
return {
    "user": {"id": user.id, "name": user.name},
    "order_count": len(orders),
    "total_spent": total,
    "currency": "USD",
}

Handle Errors Explicitly

@mcp.tool(description="Look up a user by email address")
async def get_user(email: str) -> dict:
    user = await db.find_user_by_email(email)
    if not user:
        # Return structured error - don't raise (let agent handle it)
        return {"found": False, "error": f"No user found with email: {email}"}
    return {"found": True, "user": {"id": user.id, "name": user.name, "email": user.email}}

MCP Client Configuration

{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["server.py"],
      "cwd": "/path/to/server",
      "env": {
        "DATABASE_URL": "postgresql://...",
        "API_KEY": "sk-..."
      }
    }
  }
}

For SSE transport:

{
  "mcpServers": {
    "my-tools": {
      "transport": "sse",
      "url": "https://my-mcp-server.com/sse"
    }
  }
}

Testing and Debugging

# Test with MCP Inspector
npx @modelcontextprotocol/inspector python server.py

# Or for SSE:
npx @modelcontextprotocol/inspector sse http://localhost:8000/sse

The Inspector provides a web UI to list tools, call them, and view responses without needing an AI client.


Security Checklist

  • [ ] Path validation - never accept arbitrary file paths; resolve and check against a whitelist
  • [ ] Input validation - validate all inputs, especially those used in SQL queries or shell commands
  • [ ] No secrets in tool descriptions - descriptions are sent to the LLM; don't include credentials
  • [ ] Rate limiting - protect expensive operations (API calls, DB queries) from being called in tight loops
  • [ ] Authentication - for remote (SSE/HTTP) servers, require an API key or OAuth token
  • [ ] Least privilege - tools should only have access to what they need

Common Mistakes

  • Descriptions too short - agents use descriptions to decide when to call tools; make them specific and complete
  • Raising exceptions on not-found - return a structured {"found": false} response so the agent can handle it gracefully
  • Synchronous blocking I/O in async handlers - use httpx (not requests), asyncpg (not psycopg2)
  • Not sanitizing file paths - always resolve and validate against a base directory
  • Returning unstructured strings - structured dicts make it easy for agents to extract relevant fields
  • No logging - add structured logging to diagnose issues when the agent calls your tools

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.