{"id":47736768,"url":"https://github.com/cyanheads/mcp-ts-core","last_synced_at":"2026-04-28T21:01:27.336Z","repository":{"id":283419176,"uuid":"951694053","full_name":"cyanheads/mcp-ts-core","owner":"cyanheads","description":"Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.","archived":false,"fork":false,"pushed_at":"2026-04-20T18:58:40.000Z","size":7220,"stargazers_count":130,"open_issues_count":0,"forks_count":24,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-20T19:16:50.510Z","etag":null,"topics":["agent-native","ai","cloudflare-workers","framework","llm","llm-agent","mcp","mcp-server","model-context-protocol","opentelemetry","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@cyanheads/mcp-ts-core","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cyanheads.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"cyanheads","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":"cyanheads","thanks_dev":null,"custom":null}},"created_at":"2025-03-20T05:07:04.000Z","updated_at":"2026-04-20T18:58:37.000Z","dependencies_parsed_at":"2026-04-02T23:03:38.639Z","dependency_job_id":null,"html_url":"https://github.com/cyanheads/mcp-ts-core","commit_stats":null,"previous_names":["cyanheads/mcp-ts-template","cyanheads/mcp-ts-core"],"tags_count":89,"template":false,"template_full_name":null,"purl":"pkg:github/cyanheads/mcp-ts-core","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Fmcp-ts-core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Fmcp-ts-core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Fmcp-ts-core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Fmcp-ts-core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cyanheads","download_url":"https://codeload.github.com/cyanheads/mcp-ts-core/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Fmcp-ts-core/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32399010,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-native","ai","cloudflare-workers","framework","llm","llm-agent","mcp","mcp-server","model-context-protocol","opentelemetry","typescript"],"created_at":"2026-04-02T22:48:12.837Z","updated_at":"2026-04-28T21:01:27.323Z","avatar_url":"https://github.com/cyanheads.png","language":"TypeScript","funding_links":["https://github.com/sponsors/cyanheads","https://buymeacoffee.com/cyanheads","https://www.buymeacoffee.com/cyanheads"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e@cyanheads/mcp-ts-core\u003c/h1\u003e\n  \u003cp\u003e\u003cb\u003eAgent-native TypeScript framework for building MCP servers. Build tools, not infrastructure. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.\u003c/b\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Version](https://img.shields.io/badge/Version-0.7.6-blue.svg?style=flat-square)](./CHANGELOG.md) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2025--11--25-8A2BE2.svg?style=flat-square)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE)\n\n[![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.2-blueviolet.svg?style=flat-square)](https://bun.sh/)\n\n\u003c/div\u003e\n\n---\n\n## What is this?\n\n`@cyanheads/mcp-ts-core` is the infrastructure layer for TypeScript MCP servers. Install it as a dependency — don't fork it. You write tools, resources, and prompts; the framework handles transports, auth, storage, config, logging, telemetry, and lifecycle.\n\n```ts\nimport { createApp, tool, z } from '@cyanheads/mcp-ts-core';\n\nconst greet = tool('greet', {\n  description: 'Greet someone by name and return a personalized message.',\n  annotations: { readOnlyHint: true },\n  input: z.object({ name: z.string().describe('Name of the person to greet') }),\n  output: z.object({ message: z.string().describe('The greeting message') }),\n  handler: async (input) =\u003e ({ message: `Hello, ${input.name}!` }),\n});\n\nawait createApp({ tools: [greet] });\n```\n\nThat's a complete MCP server. Every tool call is automatically logged with duration, payload sizes, memory usage, and request correlation — no instrumentation code needed. `createApp()` handles config parsing, logger init, transport startup, signal handlers, and graceful shutdown.\n\n## Quick start\n\n```bash\nbunx @cyanheads/mcp-ts-core init my-mcp-server\ncd my-mcp-server\nbun install\n```\n\nYou get a scaffolded project with `CLAUDE.md`, Agent Skills, and a `src/` tree ready for your tools. Infrastructure — transports, auth, storage, telemetry, lifecycle, linting — lives in `node_modules`. What's left is domain: which APIs to wrap, which workflows to expose.\n\nStart your coding agent (Claude Code, Codex, Cursor), describe the system you want to expose, and it drives the build. The included skills cover the full cycle: `setup`, `design-mcp-server`, scaffolding, testing, `security-pass`, `release-and-publish`.\n\n### What you get\n\nHere's what tool definitions look like:\n\n```ts\nimport { tool, z } from '@cyanheads/mcp-ts-core';\n\nexport const search = tool('search', {\n  description: 'Search for items by query.',\n  input: z.object({\n    query: z.string().describe('Search query'),\n    limit: z.number().default(10).describe('Max results'),\n  }),\n  output: z.object({ items: z.array(z.string()).describe('Search results') }),\n  async handler(input) {\n    const results = await doSearch(input.query, input.limit);\n    return { items: results };\n  },\n});\n```\n\nAnd resources:\n\n```ts\nimport { resource, z } from '@cyanheads/mcp-ts-core';\n\nexport const itemData = resource('items://{itemId}', {\n  description: 'Retrieve item data by ID.',\n  params: z.object({ itemId: z.string().describe('Item ID') }),\n  async handler(params, ctx) {\n    return await getItem(params.itemId);\n  },\n});\n```\n\nEverything registers through `createApp()` in your entry point:\n\n```ts\nawait createApp({\n  name: 'my-mcp-server',\n  version: '0.1.0',\n  tools: allToolDefinitions,\n  resources: allResourceDefinitions,\n  prompts: allPromptDefinitions,\n});\n```\n\nIt also works on Cloudflare Workers with `createWorkerHandler()` — same definitions, different entry point.\n\n## Features\n\n- **Declarative definitions** — `tool()`, `resource()`, `prompt()` builders with Zod schemas. `appTool()`/`appResource()` add interactive HTML UIs.\n- **Unified Context** — one `ctx` for logging, tenant-scoped storage, elicitation, sampling, cancellation, and task progress.\n- **Inline auth** — `auth: ['scope']` on definitions. Framework checks scopes before dispatch — no wrapper code.\n- **Task tools** — `task: true` for long-running ops; framework manages create/poll/progress/complete/cancel.\n- **Definition linter** — validates names, schemas, auth scopes, annotation coherence, and format-parity at startup. Standalone CLI (`lint:mcp`) and devcheck step.\n- **Structured errors** — handlers throw; framework catches, classifies, and formats. Error factories (`notFound()`, `validationError()`, …) when the code matters.\n- **Multi-backend storage** — `in-memory`, filesystem, Supabase, Cloudflare D1/KV/R2. Swap providers via env var; handlers don't change.\n- **Pluggable auth** — `none`, `jwt`, or `oauth`. Local secret or JWKS verification.\n- **Observability** — Pino logging, optional OpenTelemetry traces and metrics. Request correlation and tool metrics are automatic.\n- **Local + edge** — same definitions run on stdio, HTTP (Hono), and Cloudflare Workers.\n- **Tiered dependencies** — parsers, OTEL SDK, Supabase, and OpenAI are optional peers. Install what you use.\n- **Agent-first DX** — ships `CLAUDE.md` with the full exports catalog so AI agents ramp up without prompting.\n\n## Server structure\n\n```text\nmy-mcp-server/\n  src/\n    index.ts                              # createApp() entry point\n    worker.ts                             # createWorkerHandler() (optional)\n    config/\n      server-config.ts                    # Server-specific env vars\n    services/\n      [domain]/                           # Domain services (init/accessor pattern)\n    mcp-server/\n      tools/definitions/                  # Tool definitions (.tool.ts)\n      resources/definitions/              # Resource definitions (.resource.ts)\n      prompts/definitions/                # Prompt definitions (.prompt.ts)\n  package.json\n  tsconfig.json                           # extends @cyanheads/mcp-ts-core/tsconfig.base.json\n  CLAUDE.md                               # Points to core's CLAUDE.md for framework docs\n```\n\nNo `src/utils/`, no `src/storage/`, no `src/types-global/`, no `src/mcp-server/transports/` — infrastructure lives in `node_modules`.\n\n## Configuration\n\nAll core config is Zod-validated from environment variables. Server-specific config uses a separate Zod schema with lazy parsing.\n\n| Variable | Description | Default |\n|:---------|:------------|:--------|\n| `MCP_TRANSPORT_TYPE` | `stdio` or `http` | `stdio` |\n| `MCP_HTTP_PORT` | HTTP server port | `3010` |\n| `MCP_HTTP_HOST` | HTTP server hostname | `127.0.0.1` |\n| `MCP_AUTH_MODE` | `none`, `jwt`, or `oauth` | `none` |\n| `MCP_AUTH_SECRET_KEY` | JWT signing secret (required for `jwt` mode) | — |\n| `STORAGE_PROVIDER_TYPE` | `in-memory`, `filesystem`, `supabase`, `cloudflare-d1`/`kv`/`r2` | `in-memory` |\n| `OTEL_ENABLED` | Enable OpenTelemetry | `false` |\n| `OPENROUTER_API_KEY` | OpenRouter LLM API key | — |\n\nSee [CLAUDE.md](CLAUDE.md) for the full configuration reference.\n\n## API overview\n\n### Entry points\n\n| Function | Purpose |\n|:---------|:--------|\n| `createApp(options)` | Node.js server — handles full lifecycle |\n| `createWorkerHandler(options)` | Cloudflare Workers — returns `{ fetch, scheduled }` |\n\n### Builders\n\n| Builder | Usage |\n|:--------|:------|\n| `tool(name, options)` | Define a tool with `handler(input, ctx)` |\n| `resource(uriTemplate, options)` | Define a resource with `handler(params, ctx)` |\n| `prompt(name, options)` | Define a prompt with `generate(args)` |\n| `appTool(name, options)` | Define an MCP Apps tool with auto-populated `_meta.ui` |\n| `appResource(uriTemplate, options)` | Define an MCP Apps HTML resource with the correct MIME type and `_meta.ui` mirroring for read content |\n\n### Context\n\nHandlers receive a unified `Context` object:\n\n| Property | Type | Description |\n|:---------|:-----|:------------|\n| `ctx.log` | `ContextLogger` | Request-scoped logger (auto-correlates requestId, traceId, tenantId) |\n| `ctx.state` | `ContextState` | Tenant-scoped key-value storage |\n| `ctx.elicit` | `Function?` | Ask the user for input (when client supports it) |\n| `ctx.sample` | `Function?` | Request LLM completion from the client |\n| `ctx.signal` | `AbortSignal` | Cancellation signal |\n| `ctx.notifyResourceUpdated` | `Function?` | Notify subscribed clients a resource changed |\n| `ctx.notifyResourceListChanged` | `Function?` | Notify clients the resource list changed |\n| `ctx.progress` | `ContextProgress?` | Task progress reporting (when `task: true`) |\n| `ctx.requestId` | `string` | Unique request ID |\n| `ctx.tenantId` | `string?` | Tenant ID (from JWT or `'default'` for stdio) |\n\n### Subpath exports\n\n```ts\nimport { createApp, tool, resource, prompt } from '@cyanheads/mcp-ts-core';\nimport { createWorkerHandler } from '@cyanheads/mcp-ts-core/worker';\nimport { McpError, JsonRpcErrorCode, notFound, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';\nimport { checkScopes } from '@cyanheads/mcp-ts-core/auth';\nimport { markdown, fetchWithTimeout } from '@cyanheads/mcp-ts-core/utils';\nimport { OpenRouterProvider, GraphService } from '@cyanheads/mcp-ts-core/services';\nimport { validateDefinitions } from '@cyanheads/mcp-ts-core/linter';\nimport { createMockContext } from '@cyanheads/mcp-ts-core/testing';\nimport { fuzzTool, fuzzResource, fuzzPrompt } from '@cyanheads/mcp-ts-core/testing/fuzz';\n```\n\nSee [CLAUDE.md](CLAUDE.md) for the complete exports reference.\n\n## Examples\n\nThe `examples/` directory contains a reference server consuming core through public exports, demonstrating all patterns:\n\n| Tool | Pattern |\n|:-----|:--------|\n| `template_echo_message` | Basic tool with `format`, `auth` |\n| `template_cat_fact` | External API call, error factories |\n| `template_madlibs_elicitation` | `ctx.elicit` for interactive input |\n| `template_code_review_sampling` | `ctx.sample` for LLM completion |\n| `template_image_test` | Image content blocks |\n| `template_async_countdown` | `task: true` with `ctx.progress` |\n| `template_data_explorer` | MCP Apps with linked UI resource via `appTool()`/`appResource()` builders |\n\n## Testing\n\n```ts\nimport { createMockContext } from '@cyanheads/mcp-ts-core/testing';\nimport { myTool } from '@/mcp-server/tools/definitions/my-tool.tool.js';\n\nconst ctx = createMockContext({ tenantId: 'test-tenant' });\nconst input = myTool.input.parse({ query: 'test' });\nconst result = await myTool.handler(input, ctx);\n```\n\n`createMockContext()` provides stubbed `log`, `state`, and `signal`. Pass `{ tenantId }` for state operations, `{ sample }` for LLM mocking, `{ elicit }` for elicitation mocking, `{ progress: true }` for task tools.\n\n### Fuzz testing\n\nSchema-aware fuzz testing via `fast-check`. Generates valid inputs from Zod schemas and adversarial payloads (prototype pollution, injection strings, type confusion) to verify handler invariants.\n\n```ts\nimport { fuzzTool } from '@cyanheads/mcp-ts-core/testing/fuzz';\n\nconst report = await fuzzTool(myTool, { numRuns: 100 });\nexpect(report.crashes).toHaveLength(0);\nexpect(report.leaks).toHaveLength(0);\nexpect(report.prototypePollution).toBe(false);\n```\n\nAlso exports `fuzzResource`, `fuzzPrompt`, `zodToArbitrary`, and `ADVERSARIAL_STRINGS` for custom property-based tests.\n\n## Documentation\n\n- **[CLAUDE.md](CLAUDE.md)** — Framework reference: exports catalog, patterns, Context interface, error codes, auth, config, testing. Ships in the npm package.\n- **[CHANGELOG.md](CHANGELOG.md)** — Version history\n\n## Development\n\n```bash\nbun run rebuild        # clean + build (scripts/clean.ts + scripts/build.ts)\nbun run devcheck       # lint, format, typecheck, MCP defs, audit, outdated\nbun run lint:mcp       # validate MCP definitions against spec\nbun run test:all       # vitest (unit + integration)\n```\n\n## Contributing\n\nIssues and pull requests welcome. Run checks before submitting:\n\n```bash\nbun run devcheck\nbun run test:all\n```\n\n## License\n\nApache 2.0 — see [LICENSE](./LICENSE).\n\n---\n\n\u003cdiv align=\"center\"\u003e\n  \u003cp\u003e\n    \u003ca href=\"https://github.com/sponsors/cyanheads\"\u003eSponsor this project\u003c/a\u003e •\n    \u003ca href=\"https://www.buymeacoffee.com/cyanheads\"\u003eBuy me a coffee\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyanheads%2Fmcp-ts-core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyanheads%2Fmcp-ts-core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyanheads%2Fmcp-ts-core/lists"}