⚡ Promptolis Original · AI Agents & Automation

🔌 MCP Server Builder Starter — Ship Your First Production MCP Server

The structured MCP server design covering the 3 transport choices (stdio/HTTP/SSE), tool definition patterns, resource vs. tool vs. prompt distinctions, security boundaries, and the testing + deployment setup that turns your MCP server into something Claude users actually install.

⏱️ 12 min to design + ~2 hours to implement 🤖 ~2 min in Claude 🗓️ Updated 2026-04-20

Why this is epic

Most MCP tutorials show toy servers. This Original produces the complete design for a PRODUCTION MCP server — tool definitions, resource organization, prompt templates, auth handling, error contracts, and deployment — based on Anthropic's official MCP spec + analysis of successful community MCP servers (GitHub, Slack, Postgres, Notion MCP servers).

Names the critical distinctions most tutorials skip: tools vs. resources vs. prompts (each has specific semantics and Claude uses them differently), stdio vs. HTTP vs. SSE transports (massive deployment/security implications), and local-install vs. remote-hosted architectures (different risk profiles).

Produces the full server blueprint: TypeScript or Python scaffold decision, tool/resource/prompt definitions, authentication strategy, error response contracts, test plan, distribution strategy (npm/PyPI/binary), and the Claude Desktop/Code integration checklist.

The prompt

Promptolis Original · Copy-ready
<role> You are an MCP (Model Context Protocol) architect with deep experience building production MCP servers for Claude Desktop, Claude Code, and enterprise Claude deployments. You've shipped 10+ MCP servers, contributed to the MCP spec community, and reviewed MCP servers from Anthropic's official repo, GitHub, Slack, Postgres, and Notion teams. You are direct. You will name when a design should be a tool vs. resource vs. prompt, when the transport choice is wrong, when auth is missing or incorrect, and when the server is over-engineered for its use case. </role> <principles> 1. 3 primitives: tools (actions with side effects), resources (read-only data), prompts (reusable templates). Use the right one. 2. 3 transports: stdio (local), HTTP (remote), SSE (streaming remote). Match transport to deployment. 3. Tool names are verbs. Descriptions explain WHEN to use. 4. Every response: structured JSON with content array. Errors: isError: true with reasoning. 5. Auth is your responsibility. stdio = process perms. HTTP = OAuth 2.1. 6. Rate limiting + quotas for remote servers. Not optional. 7. Tool schemas via JSON Schema. Descriptions + enums + required fields all matter. 8. Test with mcp inspector before Claude integration. </principles> <input> <server-purpose>{what does this MCP server expose to Claude}</server-purpose> <target-users>{Claude Desktop users / Claude Code developers / enterprise / your own team}</target-users> <deployment>{local install / hosted remote / private enterprise / npm-package distribution}</deployment> <backing-systems>{APIs, databases, files the server wraps}</backing-systems> <auth-model>{none / API keys / OAuth / SSO / IAM}</auth-model> <data-sensitivity>{public / company-confidential / PII / regulated data}</data-sensitivity> <expected-scale>{local only / hundreds of users / enterprise SaaS scale}</expected-scale> <language-preference>{TypeScript / Python / Go / other}</language-preference> </input> <output-format> # MCP Server Blueprint: [Server name] ## Is MCP The Right Pattern? Honest check: MCP vs. plugin vs. API. ## Language + Transport Decision TS vs Python. stdio vs HTTP vs SSE. ## Primitives Design: Tools, Resources, Prompts Which primitives, what they expose. ## Tool Definitions Exact tool names, descriptions, schemas. ## Resource Definitions URI scheme, data organization. ## Prompt Definitions (if applicable) Reusable prompt templates. ## Auth + Security Architecture Auth flow, permissions, data isolation. ## Error Response Contracts How errors are structured + communicated. ## Rate Limiting + Quotas For remote deployments. ## Testing Plan mcp inspector + Claude integration tests. ## Deployment + Distribution How this ships to users. ## Claude Desktop/Code Integration Config file entry + install instructions. ## Key Takeaways 5 bullets. </output-format> <auto-intake> If input incomplete: ask for server purpose, target users, deployment, backing systems, auth model, data sensitivity, scale, language preference. </auto-intake> Now, design:

Example: input → output

Here's how this prompt actually performs. Real input below, real output from Claude Opus 4.

📝 Input
<server-purpose>Expose our internal Snowflake data warehouse to our data team via MCP so analysts can use Claude to query, explore, and build reports without writing SQL from scratch. We have ~40 tables (orders, users, sessions, events, products, etc.) and ~15 pre-built views for common queries.</server-purpose>
<target-users>20 data analysts + 10 product managers + 5 executives at our B2B SaaS company. All internal. All using Claude Desktop via our company Anthropic subscription.</target-users>
<deployment>Internal hosted. Deployed on AWS inside our VPC. Not public. Users connect via company VPN or Cloudflare Zero Trust.</deployment>
<backing-systems>Snowflake warehouse (prod account). Schema discovery via information_schema. Query execution with Snowflake's REST API. We have a dedicated service account with read-only access to relevant schemas.</backing-systems>
<auth-model>Company SSO (Okta). Users authenticate via OIDC. Server uses their token to check they're in the 'data-users' Okta group. Server-to-Snowflake uses dedicated service account, not user tokens.</auth-model>
<data-sensitivity>PII (user emails, names), commercially sensitive (revenue data, product metrics). Subject to SOC2 + GDPR. Query results must be audit-logged.</data-sensitivity>
<expected-scale>~35 concurrent users at peak. ~500 queries/day. Read-only. Analytics-heavy (aggregations over large tables).</expected-scale>
<language-preference>Python. Our data team already has Python tooling. Most analysts can debug Python.</language-preference>
🤖 Output

MCP Server Blueprint: Internal Snowflake Analytics MCP

An MCP server blueprint produces the complete production-grade design — primitives (tools/resources/prompts), transport, auth, schemas, rate limiting, and deployment — based on Anthropic's MCP spec + analysis of successful community servers (GitHub, Slack, Postgres, Notion MCP). Your use case is a classic enterprise remote MCP: internal users, authenticated, wrapping a controlled data source (Snowflake), with audit + compliance requirements. This blueprint produces the Python HTTP MCP server with 6 tools, 3 resources, 2 prompts, Okta OIDC auth, per-user rate limiting, full audit logging, and Claude Desktop integration — ready to ship in ~2 weeks of implementation.

Is MCP The Right Pattern?

MCP is appropriate here. Honest check:

For:

  • Structured Claude access to internal data ✓
  • Multi-user with centralized governance ✓
  • Wraps existing system (Snowflake) — MCP's intended use case ✓
  • Users already use Claude Desktop ✓

Against (arguments for alternatives):

  • Could be a simple slash-command in a Slack bot (but loses Claude conversation context)
  • Could be a Python library each analyst imports (but requires Python skills + less natural)
  • Could be a web UI (but not integrated with Claude workflow)

Verdict: MCP is right. It gives analysts + PMs + execs conversational Claude access to data without SQL expertise, with central governance via the MCP server's auth layer.

Language + Transport Decision

Language: Python (per your preference + team skill)

  • Use the mcp-python-sdk (Anthropic's official SDK)
  • FastAPI for HTTP transport layer if using HTTP
  • asyncio for concurrent query handling

Transport: HTTP (streamable)

  • NOT stdio (you need remote hosted, multi-user)
  • HTTP with SSE for streaming longer query results
  • MCP 2025-06 spec supports this well

Architecture:

Claude Desktop (analyst's machine)
      ↓ HTTPS over company VPN or Cloudflare Zero Trust
MCP Server (AWS inside VPC)
      ↓ SQL (service account)
Snowflake Data Warehouse

Primitives Design: Tools, Resources, Prompts

TOOLS (actions):

  • run_query — execute a SQL query with safety limits
  • explain_query — Claude writes SQL, this explains what it would do without running
  • get_query_cost — estimate cost before running expensive queries
  • save_query_as_view — for analysts building reusable queries (with approval workflow)
  • describe_table — get detailed schema + sample rows for a specific table
  • list_recent_queries — show user's recent query history

RESOURCES (read-only data):

  • snowflake://schemas — list of accessible schemas
  • snowflake://tables/{schema} — list of tables in schema
  • snowflake://views/prebuilt — catalog of pre-built views with descriptions

PROMPTS (reusable templates):

  • weekly_metrics_report — pre-built prompt for weekly KPI report generation
  • user_cohort_analysis — pre-built prompt for cohort analysis

Why this split:

  • Tools = actions Claude executes (running queries has side effects: cost, audit log, rate limit consumption)
  • Resources = reference data Claude can browse (schemas, table lists)
  • Prompts = canned analytical templates users + Claude can reuse

Tool Definitions

Tool 1: `run_query`
@server.tool()
async def run_query(
    query: str,
    max_rows: int = 1000,
    timeout_seconds: int = 30
) -> ToolResult:
    """
    Execute a SQL query against the Snowflake warehouse. Use when the user
    asks a data question that requires fetching actual data (not schema
    exploration). Results are limited to max_rows (default 1000) and time-
    out after timeout_seconds (default 30, max 120). Query must be SELECT
    only — modifications are blocked at the server level.
    
    Returns: list of rows (JSON) + metadata about the query execution.
    
    Use `explain_query` first if the user is unsure what the query does.
    Use `get_query_cost` first for exploratory queries against large tables.
    """

Schema:

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "SQL SELECT query. Max 4000 chars. Must be read-only (SELECT only)."
    },
    "max_rows": {
      "type": "integer",
      "description": "Maximum rows to return (1-10000, default 1000)",
      "minimum": 1,
      "maximum": 10000,
      "default": 1000
    },
    "timeout_seconds": {
      "type": "integer",
      "description": "Query timeout (5-120 seconds, default 30)",
      "minimum": 5,
      "maximum": 120,
      "default": 30
    }
  },
  "required": ["query"]
}

Safety checks (before execution):

  • Query parser validates SELECT-only (block INSERT, UPDATE, DELETE, DROP, CREATE, ALTER)
  • Reject queries touching restricted schemas (audit_log, auth tables)
  • Enforce max_rows at query level (add LIMIT if not present)
  • Log query + user to audit before execution
Tool 2: `explain_query`

Returns what the query WOULD do (parsed + semantic analysis) without running it.

Tool 3: `get_query_cost`

Uses Snowflake EXPLAIN to estimate credits consumed. Essential for large-table queries.

Tool 4: `save_query_as_view`

Lets analysts save a query as a view. REQUIRES approval workflow (posts to #data-eng Slack for review before actual view creation).

Tool 5: `describe_table`

Returns: column names, types, sample 5 rows, foreign keys, row count, last updated.

Tool 6: `list_recent_queries`

Returns user's last 20 queries with timestamps + status. Helps Claude understand analyst's current thread.

Resource Definitions

Resource: `snowflake://schemas`
@server.resource("snowflake://schemas")
async def list_schemas() -> ResourceContent:
    # Returns JSON: { schemas: [{name, table_count, description}, ...] }
Resource: `snowflake://tables/{schema}`
@server.resource("snowflake://tables/{schema}")
async def list_tables(schema: str) -> ResourceContent:
    # Returns JSON: { tables: [{name, row_count, description, last_updated}, ...] }
Resource: `snowflake://views/prebuilt`

Catalog of 15 pre-built analytical views with descriptions. The 'menu' of curated queries.

Prompt Definitions

Prompt: `weekly_metrics_report`

Pre-composed analytical prompt that:

  • Queries weekly active users, revenue, new signups, churn
  • Compares to previous week + 4-week average
  • Identifies notable changes
  • Produces markdown report ready to paste into Notion
Prompt: `user_cohort_analysis`

Structured cohort analysis prompt that analysts can invoke with specific date ranges.

Auth + Security Architecture

User auth: Okta OIDC

Flow:

1. User opens Claude Desktop, MCP server configured in settings

2. First tool call: server requires OIDC token (user has already authenticated via Okta in Claude Desktop's OAuth flow)

3. Server validates token signature + issuer + expiry via Okta JWKS

4. Server checks user is member of 'data-users' Okta group

5. If valid: proceed. If invalid: return isError with auth-failure message.

Server-to-Snowflake auth: Service account

  • Dedicated Snowflake service account with role DATA_ANALYST_READ_ONLY
  • Role has access to relevant schemas ONLY (no PII schemas containing raw user data)
  • Service account credentials in AWS Secrets Manager
  • Rotated quarterly

Data isolation:

  • No user token ever reaches Snowflake (server uses its own service account)
  • User queries are audit-logged with user_id from Okta token
  • Queries cannot escape allowed schemas (enforced at SQL parsing)

Network isolation:

  • MCP server in private AWS subnet
  • Access only via company VPN or Cloudflare Zero Trust
  • No public internet exposure
  • Snowflake connection via PrivateLink (no public network path)

Error Response Contracts

All errors return structured:

{
  "isError": true,
  "content": [
    {
      "type": "text",
      "text": "Clear error message Claude can reason about."
    }
  ],
  "meta": {
    "error_code": "AUTH_EXPIRED",
    "retry_advice": "Reconnect via Okta."
  }
}

Error categories:

  • AUTH_EXPIRED — user needs re-auth
  • PERMISSION_DENIED — user not in allowed group
  • RATE_LIMITED — user exceeded quota
  • QUERY_INVALID — SQL parsing or safety violation
  • QUERY_TIMEOUT — exceeded timeout
  • QUERY_COST_EXCEEDED — preflight cost check blocked query
  • UPSTREAM_ERROR — Snowflake unavailable or returned error

Claude can reason about these to suggest appropriate next actions ('let me break this query into smaller pieces' for timeout, etc.).

Rate Limiting + Quotas

Per-user quotas:

  • 50 queries per hour (enforced via Redis counter keyed by user_id)
  • 500 queries per day
  • Max 5 concurrent queries per user

Cost quotas:

  • 10 Snowflake credits per user per day
  • Preflight check via get_query_cost if query is estimated >0.5 credit
  • Queries estimated >2 credits require explicit user confirmation via tool meta

Global rate limit:

  • 100 queries/minute total server-wide (protects Snowflake from DDoS from us)
  • Circuit breaker: if Snowflake returns 5+ errors in 60s, pause all queries 5 min

Testing Plan

Local development testing:

1. Run server locally with `uvicorn server:app --reload`

2. Use `mcp inspector` (official Anthropic tool) to probe all tools/resources/prompts

3. Test each tool with valid + invalid inputs

4. Test each error category

Staging environment testing:

1. Deploy to staging AWS env (staging Snowflake copy)

2. Integrate with Claude Desktop config

3. Run through 15 real analyst workflows with team

4. Stress test: 35 concurrent simulated users

Security testing:

1. Auth bypass attempts (expired tokens, fake tokens, wrong group)

2. SQL injection attempts (despite SELECT-only — test parser robustness)

3. Schema escape attempts (try querying restricted schemas)

4. Rate limit bypass attempts

Pre-production checklist:

  • All tool schemas validated via JSON Schema validator
  • All resources return valid content types
  • Error responses match contract for every error category
  • Audit log entries created for every query
  • Metrics exported to Datadog/Grafana
  • Runbook documented for on-call

Deployment + Distribution

Server deployment:

  • Docker image pushed to internal ECR
  • Deployed to ECS Fargate in private VPC
  • 2 tasks minimum, auto-scale to 5 based on CPU
  • ALB in front with ACM cert
  • CloudFlare Zero Trust for user access

Infrastructure:

  • Secrets in AWS Secrets Manager (Snowflake service account, Okta config)
  • Redis (ElastiCache) for rate limit counters
  • CloudWatch + Datadog for monitoring
  • Audit logs to S3 + Athena for queryable history

Distribution to users:

  • NOT via npm/PyPI — internal only
  • Users add entry to their Claude Desktop config
  • IT pushes the config via MDM to user machines
  • README in internal wiki with setup instructions

Claude Desktop/Code Integration

Claude Desktop config entry:

{
  "mcpServers": {
    "snowflake-analytics": {
      "url": "https://snowflake-mcp.internal.company.com",
      "auth": {
        "type": "oauth",
        "okta_issuer": "https://company.okta.com"
      }
    }
  }
}

Install instructions (internal wiki):

1. Open Claude Desktop settings

2. Navigate to 'Developer' → 'MCP Servers'

3. Add 'snowflake-analytics' with config above

4. Authenticate via Okta when prompted

5. Restart Claude Desktop

6. Test with: 'List available data schemas'

Troubleshooting (for wiki):

  • Auth errors → re-run Okta auth
  • Permission denied → request 'data-users' group access
  • Rate limited → wait 1 hour or contact data-eng
  • Query timeout → use get_query_cost before running

Key Takeaways

  • 6 tools + 3 resources + 2 prompts. Python HTTP MCP server. Okta OIDC auth. Service-account access to Snowflake. Private deployment via Cloudflare Zero Trust.
  • Primitive split: TOOLS = actions with side effects (queries cost money/time/audit). RESOURCES = read-only reference (schemas, tables). PROMPTS = reusable analytical templates.
  • Auth is your responsibility: user → Okta OIDC → MCP server → (with service account) → Snowflake. User token never reaches Snowflake.
  • Rate limiting + cost quotas are non-negotiable. 50 queries/hour/user, 10 Snowflake credits/day/user, 100 queries/min server-wide.
  • Test with mcp inspector first, then Claude Desktop integration. Security test auth bypass + SQL injection + schema-escape attempts before production.

Common use cases

  • Developers building their first MCP server for Claude Desktop or Claude Code
  • Teams wrapping internal APIs/services for team-wide Claude access
  • Open-source maintainers shipping community MCP servers
  • SaaS companies building official MCP integrations for their products
  • Developer-relations teams demonstrating MCP for customer education
  • Platform engineers providing Claude-accessible infrastructure tools
  • Security teams building auditable MCP servers for regulated environments
  • Data teams exposing read-only data tools via MCP for analyst Claude-use
  • Enterprise teams building MCP servers behind SSO/auth for internal rollout

Best AI model for this

Claude Opus 4 or Sonnet 4.5. MCP server design requires understanding Claude's invocation semantics + server architecture + deployment + security simultaneously. Top-tier reasoning matters.

Pro tips

  • Tool names matter. Claude chooses tools based on name + description. 'fetch_user' is clearer than 'get_record.' Name tools like verbs, and make the description explain WHEN to use (not just what it does).
  • Resources ≠ tools. Resources are read-only data sources (like 'database://users' URI) Claude can reference. Tools are actions with side effects. Use resources for queryable knowledge, tools for actions.
  • stdio transport is correct for: local developer tools, Claude Desktop/Code integration, single-user scenarios. HTTP/SSE is correct for: remote servers, multi-user scenarios, cloud deployments. Don't mix these up.
  • Every tool response should be consistent: structured JSON with a content array. Errors should return `isError: true` with a clear message Claude can reason about (not just throw exceptions).
  • Auth for MCP servers is YOUR responsibility. stdio servers inherit process permissions (so: careful what they can do). HTTP servers need proper auth flow (OAuth 2.1 is the spec). Never rely on 'Claude wouldn't do that' — assume adversarial tool-use.
  • Tool schemas (JSON Schema) are how Claude knows what parameters to pass. Write them CAREFULLY — descriptions matter, required fields matter, enums are better than free-text when possible.
  • For remote MCP servers (HTTP/SSE), implement rate limiting + per-user quotas. Your server will be hit by Claude making many tool calls — you need to protect yourself.
  • Test your MCP server with the official `mcp inspector` tool before integrating with Claude. Most integration bugs show up there first.

Customization tips

  • Start small: ship with 3-5 tools, not 20. Your first MCP server should do one thing well. Expand based on usage data after 2-4 weeks in production.
  • Write tool descriptions as if you're telling a junior engineer when to reach for each one. Claude uses descriptions for tool selection — clarity directly affects tool-use quality.
  • For remote MCP servers, monitor request latency and error rates carefully. When these spike, Claude retries (sometimes aggressively). A sluggish MCP server creates a bad loop.
  • Don't expose destructive actions (DELETE, DROP, UPDATE) without explicit multi-step approval. Even with auth, an LLM-driven tool use can get surprising. Read-only first, write-only behind human-approval.
  • For open-source MCP servers, include a clear security section in README. Users and security teams want to know: what data the server accesses, what permissions it needs, how auth works, what's logged.

Variants

Local/stdio Mode

For single-user local tools (file systems, local databases, desktop apps). Covers stdio transport specifics, process lifecycle, local security.

Remote/HTTP Mode

For hosted MCP servers serving multiple users. Covers OAuth 2.1, rate limiting, multi-tenant design.

Enterprise SSO Mode

For internal enterprise deployments. Covers SAML/OIDC integration, audit logging, compliance considerations.

Read-Only Data Mode

For MCP servers exposing data (analytics, docs, search). Emphasizes resources + prompts over tools.

Frequently asked questions

How do I use the MCP Server Builder Starter — Ship Your First Production MCP Server prompt?

Open the prompt page, click 'Copy prompt', paste it into ChatGPT, Claude, or Gemini, and replace the placeholders in curly braces with your real input. The prompt is also launchable directly in each model with one click.

Which AI model works best with MCP Server Builder Starter — Ship Your First Production MCP Server?

Claude Opus 4 or Sonnet 4.5. MCP server design requires understanding Claude's invocation semantics + server architecture + deployment + security simultaneously. Top-tier reasoning matters.

Can I customize the MCP Server Builder Starter — Ship Your First Production MCP Server prompt for my use case?

Yes — every Promptolis Original is designed to be customized. Key levers: Tool names matter. Claude chooses tools based on name + description. 'fetch_user' is clearer than 'get_record.' Name tools like verbs, and make the description explain WHEN to use (not just what it does).; Resources ≠ tools. Resources are read-only data sources (like 'database://users' URI) Claude can reference. Tools are actions with side effects. Use resources for queryable knowledge, tools for actions.

Explore more Originals

Hand-crafted 2026-grade prompts that actually change how you work.

← All Promptolis Originals