DirectBooker/mcp-chatgpt
If you are the rightful owner of mcp-chatgpt and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
The Model Context Protocol (MCP) server is a TypeScript-based server that provides a structured environment for managing tools and resources, facilitating communication through JSON-RPC over HTTP.
mcp-chatgpt
TypeScript MCP server with an Express HTTP surface, auto-discovered React-based MCP resources, and a small tool system powered by Zod schemas. Ships JSON-RPC over HTTP (StreamableHTTP) for multi-client compatibility.
Overview of the architecture
- MCP core:
src/index.tsinstantiatesMcpServerand exposes StreamableHTTP endpoints via Express. - Tool system: Zod-typed tools are registered through a
ToolRegistryabstraction. - Resource system: Static resources plus auto-discovered TypeScript/React resources using a factory that bundles and serves HTML snippets.
- Front-end resources: React
.tsxentries undersrc/ts-resources/are bundled with esbuild and returned as HTML via MCP resources. They read tool output using lightweight hooks. - HTTP surface:
/mcp(JSON-RPC, SSE),/health,/info, and static/assets.
Runtime dependencies
- @modelcontextprotocol/sdk: MCP server, types, and StreamableHTTP transport
- express, cors: HTTP endpoints and CORS for web clients
- zod: schema validation for tool I/O
- react, react-dom: render interactive MCP resources
- embla-carousel-react: carousel UI for hotel cards
- mapbox-gl: interactive map MCP resource
- react-router-dom: client-side routing inside resources
- clsx: conditional class names
Dev/build tooling
- typescript, tsx: TS compilation and dev watch
- esbuild: bundles each
src/ts-resources/*.tsxintodist/ts-resources-bundles/*.js - tailwindcss, @tailwindcss/cli, postcss, autoprefixer: styles for resource UIs
- eslint (+ @typescript-eslint, prettier): linting/formatting
- concurrently: parallel dev processes (server, bundler, CSS watcher)
How the MCP server is implemented
src/index.tsconstructsnew McpServer({ name, version }, { capabilities: { tools: {}, resources: {} }}).- Express sets up:
/mcpwithStreamableHTTPServerTransportper request (POST for JSON-RPC; GET for SSE; DELETE to end sessions)/healthand/infofor diagnostics/assetsto serve compiled CSS
- Tools and resources are registered through registries which wrap SDK
registerToolandregisterResource.
Interaction with @modelcontextprotocol/sdk
- Tools:
mcpServer.registerTool(name, config, impl)where config includes descriptions and optionalinputSchema/outputSchema(Zod shapes). TheToolRegistryvalidates inputs with Zod and returnsCallToolResult. - Resources:
mcpServer.registerResource(name, uri, annotations, readImpl); theResourceRegistryturns returnedtextorblobinto MCP-compliantcontents. - Transport:
StreamableHTTPServerTransportis created per request to avoid ID collisions across clients;transport.handleRequest(req, res, body)handles JSON-RPC and SSE.
Adding a tool
- Create a file under
src/tools/instances/implementingToolDefinitionwith Zod schemas. - Export the tool and add it to
availableToolsinsrc/tools/index.ts. - The server auto-registers all tools at startup.
Example skeleton:
import { z } from 'zod';
import { ToolDefinition } from '../types.js';
const inputSchema = { q: z.string().describe('query') };
const outputSchema = { ok: z.boolean() };
async function implementation(args: { q: string }) {
return { content: [{ type: 'text', text: `You searched for: ${args.q}` }], structuredContent: { ok: true } };
}
export const myTool: ToolDefinition<typeof inputSchema, typeof outputSchema> = {
config: {
name: 'my-tool',
description: 'Does a thing',
inputSchema,
outputSchema,
},
implementation,
};
Then add to availableTools in src/tools/index.ts.
Adding a resource
There are two paths:
-
Static resources
- Create
src/resources/instances/your-resource.tsthat returns{ text: string }or{ blob: base64 }. - Add it to
getAvailableResources()insrc/resources/index.ts(thestaticResourcesarray).
- Create
-
TypeScript/React resources (auto-discovered)
- Drop a
.tsor.tsxfile insrc/ts-resources/. - Optionally add metadata in a leading comment block:
@mcp-name: "Display Name"@mcp-description: "What it shows"@mcp-uri: "custom-uri"
- It will be bundled and exposed automatically as
dbk-ts://<uriId>?salt=<salt>.
- Drop a
Auto-detection of React resources
- Discovery:
src/resources/typescript-auto-discovery.tsscanssrc/ts-resources/for.ts/.tsx, reads optional@mcp-*metadata, and creates resources via the factory. - Bundling:
scripts/build-ts-resources.mjsuses esbuild to emitdist/ts-resources-bundles/<name>.js(one bundle per entry). - Serving:
src/resources/typescript-resource-factory.tsreads the bundle and inlines Tailwind CSS into minimal HTML returned as the resource body (mimeType: 'text/html+skybridge'). - Cache-busting: Each URI includes a
saltfromTS_SALTor app start time (createSaltedUri()), ensuring fresh loads when output templates change. - React hooks:
src/shared/open-ai-globals.tsexposesuseToolOutput,useMaxHeight, anduseDisplayModeby reading awindow.openaiglobal updated by the host; components inside resources can render against live tool output.
TypeScript resource salt (cache-busting)
The “salt” is appended to TypeScript resource URIs as a query param, for example: dbk-ts://carousel?salt=<value>. Changing the salt forces clients to reload the HTML/JS so you don’t serve stale bundles.
- Source of truth:
initializeUrlSalt()insrc/resources/typescript-resource-factory.ts.- If
TS_SALTis set, that value is used. - Otherwise, the salt defaults to the server start timestamp.
- If
- Where it’s used:
createSaltedUri(id)generates salted URIs for resources and output templates (e.g., a tool can returndbk-ts://carousel?...). - Why set
TS_SALT:- Deterministic URIs per release (stable across restarts).
- Consistent URIs across multiple replicas behind a load balancer.
- Intentional cache busts when you deploy new UI bundles.
- Recommended values: a deploy/version identifier such as a Git SHA, release tag, or a timestamp you control.
Examples
# Stable per-deploy using the short Git SHA
TS_SALT=$(git rev-parse --short HEAD) pnpm start
# Explicit version string
TS_SALT=v1.2.3 pnpm start
# One-off cache bust
TS_SALT=$(date +%s) pnpm start
Notes
- If
TS_SALTis unset, restarting the server changes the salt (new URIs), which is convenient in local dev. - In multi-instance deployments, set the same
TS_SALTon all instances to avoid mismatched URIs being advertised.
Build process
pnpm buildruns:tsctodist/node scripts/build-ts-resources.mjsto createdist/ts-resources-bundles/*.js- Tailwind CLI to produce
dist/assets/tailwind.css
pnpm devruns server (tsx watch), esbuild watch forsrc/ts-resources, and Tailwind watcher in parallel.
Useful scripts:
pnpm install
pnpm run dev # dev server + ts-resource bundler + css watcher
pnpm run build # tsc + bundle ts-resources + build CSS
pnpm start # run built server (dist/index.js)
pnpm run lint # lint
pnpm run lint:fix # auto-fix
pnpm run format # format code
pnpm run format:check
Deployment
- Build once:
pnpm run build. - Run the server:
PORT=3000 TS_SALT=$(date +%s) pnpm start. - Expose
/mcp(POST for JSON-RPC, GET for SSE, DELETE to end sessions),/health,/info, and/assets. - Set
TS_SALTto a stable value per release if you want deterministic resource URIs across restarts. - Production hardening: restrict CORS, run behind a reverse proxy, manage logs on stderr, and secure any third‑party API tokens (e.g., Mapbox) via environment variables.
How resources and tools connect
- Tools can hint a display template via OpenAI
_meta. Example:openai/outputTemplatereturns a salteddbk-ts://carouselURI, prompting compatible clients to render the React resource using the tool’sstructuredContent. - React resources use
useToolOutput()to read tool output and render UI (carousels, maps, etc.).
HTTP endpoints
GET /health→{ status, timestamp }GET /info→{ name, version, tools, resources, transport, endpoints }/mcp→ StreamableHTTP JSON-RPC endpointPOST /mcpJSON-RPCGET /mcpServer‑Sent EventsDELETE /mcpsession termination
Directory layout
src/
index.ts # MCP + Express server
tools/ # Tool types, registry, instances
resources/ # Resource types, registry, factory, discovery
typescript-resource-factory.ts
typescript-auto-discovery.ts
instances/hello-world.ts
ts-resources/ # React/TS entries (auto-discovered)
carousel.tsx
map.tsx
components/, shared/, directbooker/
scripts/
build-ts-resources.mjs # esbuild bundler
watch-ts-resources.mjs # esbuild watch for dev
dist/
ts-resources-bundles/*.js # built bundles (generated)
assets/tailwind.css # built CSS (generated)
Configuration and environment
PORT: HTTP port (default 3000)TS_SALT: cache-busting salt for TypeScript resource URIs; optional but recommended for stable releases- Third‑party API keys: configure via env vars; do not hard‑code tokens in source
Notes worth calling out
- Logging goes to stderr to avoid MCP stdout interference.
- Per-request transports enable multiple concurrent clients without JSON-RPC ID collisions.
- Zod schemas document and validate tool inputs/outputs, returning clear error content on validation failure.
Contributing
See CONTRIBUTING.md for branch protection, PR workflow, and admin bypass policy for main.