Skip to main content
Glama
lukegskw

mcp-typescript-starter

MCP TypeScript Starter

TypeScript CI Container License

MCP TypeScript Starter is a production-conscious foundation for building a Model Context Protocol server with TypeScript. It includes one typed example tool, stdio and Streamable HTTP transports, strict validation, tests, a hardened container, npm artifact verification, automated GHCR publication, and opt-in npm/MCP Registry releases.

Clone it, replace the example domain, and keep the infrastructure that real MCP servers need.

Navigation

Related MCP server: mcp-server-http-streamable

Use this starter

Click Use this template on GitHub to create a new MCP server with an independent Git history. After creating it, replace the example tool and update the project identity by following Customizing the starter.

Fork this repository when you want to contribute improvements back through a pull request. See Contributing before submitting changes.

If this starter helped you, consider giving the repository a star. It helps other TypeScript developers discover the project.

About

The starter demonstrates the complete path from a validated MCP tool definition to a client-visible structured result. The server uses the current modular MCP TypeScript SDK and Hono's Web-standard HTTP model rather than a custom server framework.

The default stdio transport is intended for local clients that launch the server as a child process. Streamable HTTP is stateless and creates a fresh MCP server for each request, so it can be replicated without shared session storage.

The example performs bounded in-memory work. There is no telemetry, application database, persistent storage, authentication, or external service dependency.

Features

  • Registers tools with strict Zod input and output schemas.

  • Demonstrates server instructions and descriptions for every tool input and output.

  • Returns both human-readable content and typed structured content.

  • Includes accurate MCP safety annotations.

  • Supports stdio and stateless Streamable HTTP.

  • Uses Hono with Host and Origin validation against DNS rebinding.

  • Binds HTTP to loopback by default and requires an allowlist for other interfaces.

  • Limits tool inputs and HTTP request bodies.

  • Keeps stdout exclusive to MCP protocol messages in stdio mode.

  • Handles SIGINT and SIGTERM with idempotent graceful shutdown.

  • Runs as a non-root container with read-only-root-filesystem support.

  • Tests configuration, stdio wiring, MCP behavior, Hono routes, and real HTTP traffic.

  • Packs, installs, and starts the npm artifact in CI before it can be published.

  • Publishes multi-architecture images only after quality checks pass.

  • Provides an opt-in, resumable release workflow for npm, GHCR, and the MCP Registry.

  • Validates release identity and metadata locally with the same checks used by CI.

  • Provides an opt-in Gemini CLI extension manifest for gallery discovery.

MCP tools

echo

Echoes a validated message and optional string metadata. It is deliberately simple so the repository teaches MCP schemas, registration, annotations, and results without inventing a business domain.

Example input:

{
  "message": "Hello, MCP!",
  "metadata": {
    "source": "example-client"
  }
}

Example structured output:

{
  "message": "Hello, MCP!",
  "metadata": {
    "source": "example-client"
  }
}

Messages are limited to 10,000 characters. Metadata accepts at most 20 entries; keys are limited to 64 characters and values to 1,024 characters.

Tech stack

Installation

Prerequisites

  • Node.js 24+ and pnpm 11 for local development.

  • Docker and Docker Compose for container deployment.

Docker Compose

The recommended HTTP deployment uses the published multi-architecture image:

ghcr.io/lukegskw/mcp-typescript-starter:latest

Download the Compose example and provide the hostname clients will use:

curl -O https://raw.githubusercontent.com/lukegskw/mcp-typescript-starter/main/compose.example.yaml
export MCP_ALLOWED_HOSTS='mcp.example.internal'
docker compose -f compose.example.yaml up -d

The Streamable HTTP and health endpoints will be available at:

http://<host>:3000/mcp
http://<host>:3000/healthz

To publish a different host port, set MCP_PUBLISHED_PORT. The application still uses port 3000 inside the container.

The latest tag follows the newest successful build from the default branch. Use a version or immutable sha-* tag for controlled deployment and rollback.

Docker run

docker run -d \
  --name mcp-typescript-starter \
  --restart unless-stopped \
  --read-only \
  --user 10001:10001 \
  --cap-drop ALL \
  --security-opt no-new-privileges:true \
  --tmpfs /tmp:size=16m,mode=1777 \
  -e MCP_TRANSPORT=streamable-http \
  -e MCP_HOST=0.0.0.0 \
  -e MCP_ALLOWED_HOSTS=127.0.0.1,localhost,mcp.example.internal \
  -p 3000:3000 \
  ghcr.io/lukegskw/mcp-typescript-starter:latest

Build the container from source

git clone https://github.com/lukegskw/mcp-typescript-starter.git
cd mcp-typescript-starter
docker buildx build --load -t mcp-typescript-starter:local .

Local Node.js installation

git clone https://github.com/lukegskw/mcp-typescript-starter.git
cd mcp-typescript-starter
pnpm install --frozen-lockfile
pnpm build
pnpm start -- --transport stdio

For local Streamable HTTP development:

MCP_TRANSPORT=streamable-http pnpm dev

Configuration

Variable

Required

Default

Description

MCP_TRANSPORT

No

stdio

stdio or streamable-http.

MCP_HOST

No

127.0.0.1

HTTP bind address.

MCP_PORT

No

3000

HTTP listening port.

MCP_ALLOWED_HOSTS

Outside loopback

None

Comma-separated Host and Origin hostname allowlist.

The --transport command-line option overrides MCP_TRANSPORT. MCP_ALLOWED_HOSTS contains hostnames, not URLs; include every hostname legitimate clients and health checks use.

The server has no secrets in its example configuration. Add domain credentials through the deployment platform or environment, never as MCP tool arguments or committed files.

MCP client setup

For a client that accepts Streamable HTTP server definitions:

mcp_servers:
  starter:
    url: http://127.0.0.1:3000/mcp

For a client that launches a local stdio server:

{
  "mcpServers": {
    "starter": {
      "command": "node",
      "args": [
        "/absolute/path/to/mcp-typescript-starter/dist/main.js",
        "--transport",
        "stdio"
      ]
    }
  }
}

After publishing a project derived from this starter, clients can launch its pinned npm package without cloning the repository:

{
  "mcpServers": {
    "example": {
      "command": "npx",
      "args": ["--yes", "@example/example-mcp@0.1.0"]
    }
  }
}

When testing the published command from inside its own source checkout, some npm versions prefer the root package. Use the local node dist/main.js configuration above or set the MCP server's working directory outside the checkout.

Claude Code and Codex accept the equivalent CLI definitions:

claude mcp add --transport stdio example -- npx --yes @example/example-mcp@0.1.0
codex mcp add example -- npx --yes @example/example-mcp@0.1.0

Gemini CLI uses the same mcpServers JSON structure in its settings.json. Add any domain credentials through each client's environment configuration and keep those files private.

To let a local client launch the container over stdio, use docker run -i --rm and pass --transport stdio after the image name. -i is required so the client can exchange MCP messages through standard input and output.

Client configuration formats differ. Consult the client's documentation for its exact schema and restart or reload the client after changing its server definition.

Customizing the starter

The main extension points are intentionally direct:

  1. Copy or replace src/tools/echo.ts.

  2. Define strict input and output schemas before writing the handler.

  3. Register the tool in src/server.ts.

  4. Add MCP behavior tests and any domain integration tests.

  5. Replace the package name, executable name, server identity, image references, repository metadata, and README content.

  6. Keep private: true until the distribution checklist below is complete.

Keep tool modules responsible for their own schemas and handlers. Keep transport modules independent from domain tools. Introduce services or persistence only when real behavior requires them.

Treat tool metadata as part of the public API. Describe every input and output field, state prerequisites and side effects in each tool description, publish accurate safety annotations, and add server instructions when callers need to understand a workflow across multiple tools. The behavior test demonstrates how to inspect the definitions an MCP client actually receives.

Distribution and releases

The starter verifies its npm artifact on every pull request but cannot publish by default. This prevents a newly generated repository from releasing under the starter's identity.

To enable distribution in a derived project:

  1. Update name, bin, repository, homepage, bugs, and keywords in package.json. Add mcpName with the official reverse-DNS MCP name.

  2. Copy server.example.json to server.json, then replace its name, repository, npm identifier, OCI identifier, description, and environment variables. Use an exact OCI version tag, not latest.

  3. Update SERVER_NAME in src/server.ts and the default MCP server label in the Dockerfile.

  4. Run pnpm test:package, then remove private: true from package.json and run pnpm test:distribution. The distribution check rejects stale versions, mismatched package/Registry/container identities, and remaining starter placeholders.

  5. Configure npm Trusted Publishing for .github/workflows/publish.yml, or add an NPM_TOKEN repository secret as a fallback.

  6. Create the repository variable MCP_RELEASE_ENABLED with the value true only when all identities and registry permissions are ready.

To make a derived server installable as a Gemini CLI extension, copy and customize the example manifest:

cp gemini-extension.example.json gemini-extension.json

Replace the extension name, description, MCP server key, npm package, and any settings. Represent required credentials with environment substitutions such as ${EXAMPLE_API_KEY} and declare a matching settings entry; mark keys, passwords, secrets, and tokens as sensitive: true. Add the gemini-cli-extension GitHub topic after the customized manifest is committed.

The Gemini gallery crawls public tagged repositories that have that topic and a gemini-extension.json at the repository root. The release preparation script updates the manifest version automatically when the optional file exists. Projects that do not create it retain the same release behavior.

Prepare a release with one command:

pnpm release:prepare 0.1.0

This synchronizes the package version, Registry version, npm package version, OCI tag, and optional Gemini extension version. Review and commit the result, run pnpm test:distribution, then push it to main. The release workflow runs all quality gates, validates the same distribution contract, creates v0.1.0, and publishes the versioned container, npm package, MCP Registry entry, and GitHub release.

The regular container workflow owns latest, branch, pull-request, and SHA tags. The release workflow exclusively owns immutable version tags and checks each external artifact independently, so rerunning a partial release resumes the missing work.

See the distribution design for the reliability and ownership decisions behind this workflow.

Verification

Run the complete repository suite:

pnpm install --frozen-lockfile
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test:unit
pnpm test:integration
pnpm build
pnpm test:package
pnpm test:distribution

For container changes:

docker buildx build --load -t mcp-typescript-starter:test .

Finally, connect an MCP client and confirm that echo is listed and returns both text and structured content. In HTTP mode, confirm /healthz reports {"status":"ok"}.

Limitations

  • The example exposes one tool and no resources or prompts.

  • Streamable HTTP has no authentication. Restrict it to loopback, a trusted LAN, a VPN, a private container network, or an authenticated reverse proxy.

  • Host and Origin allowlists prevent classes of DNS rebinding attacks but do not authenticate callers.

  • The HTTP server is stateless and contains no shared persistence or distributed coordination.

  • Rate limiting, tracing, metrics, and domain-specific logging are not included.

  • The repository is a source starter, not a published npm library.

Review SECURITY.md before exposing the HTTP transport or reporting a security issue.

Contributing

Contributions are welcome. Before opening a pull request:

pnpm install --frozen-lockfile
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test
pnpm build
docker buildx build --load -t mcp-typescript-starter:test .

Changes must preserve strict typing, bounded validation, structured MCP results, stdout protocol purity, secure HTTP defaults, deterministic tests, and documentation for user-visible behavior. Do not add abstractions without a concrete use case for them.

License

MIT. See LICENSE.

Available Tools

1 tool
echoEchoB
Read-onlyIdempotent

Echo a validated message and optional string metadata.

ParametersJSON Schema
NameRequiredDescriptionDefault
messageYes
metadataNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
messageYes
metadataNo

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, covering safety and side-effect expectations. The description adds minimal behavioral context, mainly 'validated', which hints that inputs are checked but not how. No contradiction with annotations; the bar is lower, so a 3 is appropriate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, direct sentence with no unnecessary words. The verb is front-loaded, and the optional part is clearly flagged. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a trivial echo tool, the description is nearly sufficient. The output schema exists, and annotations cover read-only/idempotent behavior, so return-value details are not needed. The only gap is vague 'validated' wording, but the tool is simple enough that an agent can invoke it correctly with the given information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It names 'message' and 'optional string metadata', but the schema already provides types and constraints. Calling metadata 'string metadata' is potentially misleading because the schema defines metadata as an object with string values, not a string. This adds little value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('echo') and resource ('a validated message and optional string metadata'), making the core function clear. There are no sibling tools to differentiate from, so the lack of distinction is not a flaw. However, 'validated' is slightly ambiguous — it might imply an extra validation step that is actually handled by the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is given on when to use this tool or what to prefer it over. There are no siblings, but the description still does not state typical scenarios (e.g., debugging, round-trip testing). The intended usage is only implied by the verb 'echo'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 1 tool updatev0.1.0
    • First observedecho

TDQS

B3.4/5.0
Disambiguation5/5

With only one tool, there is no possibility of confusion or overlap. The single 'echo' tool is clearly distinct by virtue of being the only tool available.

Naming Consistency5/5

The single tool name 'echo' is short, clear, and directly describes its function. Since there is only one tool, naming consistency is trivially satisfied.

Tool Count3/5

One tool feels minimal even for a starter server, but it is a common pattern for a simple template. The count is at the lower edge of being acceptable rather than egregiously insufficient.

Completeness2/5

The server only provides an echo function, which is useful for testing connectivity but lacks any real domain operations. There are no CRUD or workflow capabilities, representing a significant gap for any practical use beyond a basic demo.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A minimal Model Context Protocol server that facilitates network-based client connections using Streamable HTTP transport. It provides a greeting tool and is optimized for consistent deployment across local environments, Docker, and Kubernetes.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides a production-ready Model Context Protocol server with dual STDIO and Streamable HTTP transports, enabling file operations, memory, database queries, RAG, web search, GitHub integration, background tasks, and prompt-based workflows.
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lukegskw/mcp-typescript-starter'

If you have feedback or need assistance with the MCP directory API, please join our Discord server