mcp-ts-template 2.3.4 → 2.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -147
- package/dist/index.js +291 -55
- package/package.json +24 -21
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>mcp-ts-template</h1>
|
|
3
|
-
<p><b>Production-grade TypeScript template for building Model Context Protocol (MCP) servers. Ships with declarative tools/resources, robust error handling, DI, easy auth, optional OpenTelemetry, and first-class support for both local and edge (Cloudflare Workers) runtimes.</b
|
|
3
|
+
<p><b>Production-grade TypeScript template for building Model Context Protocol (MCP) servers. Ships with declarative tools/resources, robust error handling, DI, easy auth, optional OpenTelemetry, and first-class support for both local and edge (Cloudflare Workers) runtimes.</b>
|
|
4
|
+
<div>5 Tools • 1 Resource • 1 Prompt</div>
|
|
5
|
+
</p>
|
|
4
6
|
</div>
|
|
5
7
|
|
|
6
8
|
<div align="center">
|
|
7
9
|
|
|
8
|
-
[](./CHANGELOG.md) [](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-06-18/changelog.mdx) [](https://modelcontextprotocol.io/) [](./LICENSE) [](https://github.com/cyanheads/mcp-ts-template/issues) [](https://www.typescriptlang.org/) [](https://bun.sh/) [](./coverage/lcov-report/)
|
|
9
11
|
|
|
10
12
|
</div>
|
|
11
13
|
|
|
@@ -24,6 +26,32 @@
|
|
|
24
26
|
- **Rich Built-in Utility Suite**: Helpers for parsing (PDF, YAML, CSV), scheduling, security, and more.
|
|
25
27
|
- **Edge-Ready**: Write code once and run it seamlessly on your local machine or at the edge on Cloudflare Workers.
|
|
26
28
|
|
|
29
|
+
## 🛠️ Included Capabilities
|
|
30
|
+
|
|
31
|
+
This template includes working examples to get you started.
|
|
32
|
+
|
|
33
|
+
### Tools
|
|
34
|
+
|
|
35
|
+
| Tool | Description |
|
|
36
|
+
| :---------------------------------- | :---------------------------------------------------------------- |
|
|
37
|
+
| **`template_echo_message`** | Echoes a message back with optional formatting and repetition. |
|
|
38
|
+
| **`template_cat_fact`** | Fetches a random cat fact from an external API. |
|
|
39
|
+
| **`template_madlibs_elicitation`** | Demonstrates elicitation by asking for words to complete a story. |
|
|
40
|
+
| **`template_code_review_sampling`** | Uses the LLM service to perform a simulated code review. |
|
|
41
|
+
| **`template_image_test`** | Returns a test image as a base64-encoded data URI. |
|
|
42
|
+
|
|
43
|
+
### Resources
|
|
44
|
+
|
|
45
|
+
| Resource | URI | Description |
|
|
46
|
+
| :--------- | :----------------- | :-------------------------------------------- |
|
|
47
|
+
| **`echo`** | `echo://{message}` | A simple resource that echoes back a message. |
|
|
48
|
+
|
|
49
|
+
### Prompts
|
|
50
|
+
|
|
51
|
+
| Prompt | Description |
|
|
52
|
+
| :---------------- | :--------------------------------------------------------------- |
|
|
53
|
+
| **`code-review`** | A structured prompt for guiding an LLM to perform a code review. |
|
|
54
|
+
|
|
27
55
|
## 🚀 Getting Started
|
|
28
56
|
|
|
29
57
|
### MCP Client Settings/Configuration
|
|
@@ -34,10 +62,14 @@ Add the following to your MCP Client configuration file (e.g., `cline_mcp_settin
|
|
|
34
62
|
{
|
|
35
63
|
"mcpServers": {
|
|
36
64
|
"mcp-ts-template": {
|
|
65
|
+
"type": "stdio",
|
|
37
66
|
"command": "bunx",
|
|
38
67
|
"args": ["mcp-ts-template@latest"],
|
|
39
68
|
"env": {
|
|
40
|
-
"
|
|
69
|
+
"MCP_TRANSPORT_TYPE": "stdio",
|
|
70
|
+
"MCP_LOG_LEVEL": "info",
|
|
71
|
+
"STORAGE_PROVIDER_TYPE": "filesystem",
|
|
72
|
+
"STORAGE_FILESYSTEM_PATH": "/path/to/your/storage"
|
|
41
73
|
}
|
|
42
74
|
}
|
|
43
75
|
}
|
|
@@ -68,151 +100,25 @@ cd mcp-ts-template
|
|
|
68
100
|
bun install
|
|
69
101
|
```
|
|
70
102
|
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
This template includes working examples of tools and resources.
|
|
74
|
-
|
|
75
|
-
### 1. Example Tool: `template_echo_message`
|
|
76
|
-
|
|
77
|
-
This tool echoes back a message with optional formatting. You can find the full source at `src/mcp-server/tools/definitions/template-echo-message.tool.ts`.
|
|
78
|
-
|
|
79
|
-
<details>
|
|
80
|
-
<summary>Click to see the `echoTool` definition structure</summary>
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
// Located at: src/mcp-server/tools/definitions/template-echo-message.tool.ts
|
|
84
|
-
import { z } from 'zod';
|
|
85
|
-
import type {
|
|
86
|
-
SdkContext,
|
|
87
|
-
ToolDefinition,
|
|
88
|
-
} from '@/mcp-server/tools/utils/toolDefinition.js';
|
|
89
|
-
import { withToolAuth } from '@/mcp-server/transports/auth/lib/withAuth.js';
|
|
90
|
-
import { type RequestContext, logger } from '@/utils/index.js';
|
|
91
|
-
|
|
92
|
-
// 1. Define Input and Output Schemas with Zod for validation.
|
|
93
|
-
const InputSchema = z.object({
|
|
94
|
-
message: z.string().min(1).describe('The message to echo back.'),
|
|
95
|
-
mode: z
|
|
96
|
-
.enum(['standard', 'uppercase', 'lowercase'])
|
|
97
|
-
.default('standard')
|
|
98
|
-
.describe('Formatting mode.'),
|
|
99
|
-
repeat: z
|
|
100
|
-
.number()
|
|
101
|
-
.int()
|
|
102
|
-
.min(1)
|
|
103
|
-
.max(5)
|
|
104
|
-
.default(1)
|
|
105
|
-
.describe('Number of times to repeat the message.'),
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const OutputSchema = z.object({
|
|
109
|
-
repeatedMessage: z
|
|
110
|
-
.string()
|
|
111
|
-
.describe('The final, formatted, and repeated message.'),
|
|
112
|
-
// ... other fields from the actual file
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// 2. Implement the pure business logic for the tool.
|
|
116
|
-
async function echoToolLogic(
|
|
117
|
-
input: z.infer<typeof InputSchema>,
|
|
118
|
-
appContext: RequestContext,
|
|
119
|
-
sdkContext: SdkContext,
|
|
120
|
-
): Promise<z.infer<typeof OutputSchema>> {
|
|
121
|
-
// ... logic to format and repeat the message
|
|
122
|
-
const formattedMessage = input.message.toUpperCase(); // simplified for example
|
|
123
|
-
const repeatedMessage = Array(input.repeat).fill(formattedMessage).join(' ');
|
|
124
|
-
return { repeatedMessage };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// 3. Assemble the final Tool Definition.
|
|
128
|
-
export const echoTool: ToolDefinition<typeof InputSchema, typeof OutputSchema> =
|
|
129
|
-
{
|
|
130
|
-
name: 'template_echo_message', // The official tool name
|
|
131
|
-
title: 'Template Echo Message',
|
|
132
|
-
description:
|
|
133
|
-
'Echoes a message back with optional formatting and repetition.',
|
|
134
|
-
inputSchema: InputSchema,
|
|
135
|
-
outputSchema: OutputSchema,
|
|
136
|
-
logic: withToolAuth(['tool:echo:read'], echoToolLogic), // Secure the tool
|
|
137
|
-
};
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
The `echoTool` is registered in `src/mcp-server/tools/definitions/index.ts`, making it available to the server on startup. For an example of how to use the new elicitation feature, see `template_madlibs_elicitation.tool.ts`.
|
|
141
|
-
|
|
142
|
-
</details>
|
|
143
|
-
|
|
144
|
-
### 2. Example Resource: `echo-resource`
|
|
145
|
-
|
|
146
|
-
This resource provides a simple echo response via a URI. The source is located at `src/mcp-server/resources/definitions/echo.resource.ts`.
|
|
147
|
-
|
|
148
|
-
<details>
|
|
149
|
-
<summary>Click to see the `echoResourceDefinition` structure</summary>
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
// Located at: src/mcp-server/resources/definitions/echo.resource.ts
|
|
153
|
-
import { z } from 'zod';
|
|
154
|
-
import type { ResourceDefinition } from '@/mcp-server/resources/utils/resourceDefinition.js';
|
|
155
|
-
import { withResourceAuth } from '@/mcp-server/transports/auth/lib/withAuth.js';
|
|
156
|
-
import { type RequestContext, logger } from '@/utils/index.js';
|
|
157
|
-
|
|
158
|
-
// 1. Define Parameter and Output Schemas.
|
|
159
|
-
const ParamsSchema = z.object({
|
|
160
|
-
message: z.string().optional().describe('Message to echo from the URI.'),
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const OutputSchema = z.object({
|
|
164
|
-
message: z.string().describe('The echoed message.'),
|
|
165
|
-
timestamp: z.string().datetime().describe('Timestamp of the response.'),
|
|
166
|
-
requestUri: z.string().url().describe('The original request URI.'),
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// 2. Implement the pure read logic for the resource.
|
|
170
|
-
function echoLogic(
|
|
171
|
-
uri: URL,
|
|
172
|
-
params: z.infer<typeof ParamsSchema>,
|
|
173
|
-
context: RequestContext,
|
|
174
|
-
): z.infer<typeof OutputSchema> {
|
|
175
|
-
const messageToEcho = params.message || uri.hostname || 'Default echo';
|
|
176
|
-
return {
|
|
177
|
-
message: messageToEcho,
|
|
178
|
-
timestamp: new Date().toISOString(),
|
|
179
|
-
requestUri: uri.href,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 3. Assemble the final Resource Definition.
|
|
184
|
-
export const echoResourceDefinition: ResourceDefinition<
|
|
185
|
-
typeof ParamsSchema,
|
|
186
|
-
typeof OutputSchema
|
|
187
|
-
> = {
|
|
188
|
-
name: 'echo-resource', // The official resource name
|
|
189
|
-
title: 'Echo Message Resource',
|
|
190
|
-
description: 'A simple echo resource that returns a message.',
|
|
191
|
-
uriTemplate: 'echo://{message}',
|
|
192
|
-
paramsSchema: ParamsSchema,
|
|
193
|
-
outputSchema: OutputSchema,
|
|
194
|
-
logic: withResourceAuth(['resource:echo:read'], echoLogic), // Secure the resource
|
|
195
|
-
};
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
Like the tool, `echoResourceDefinition` is registered in `src/mcp-server/resources/definitions/index.ts`.
|
|
199
|
-
|
|
200
|
-
</details>
|
|
201
|
-
|
|
202
|
-
## ⚙️ Core Concepts
|
|
203
|
-
|
|
204
|
-
### Configuration
|
|
103
|
+
## ⚙️ Configuration
|
|
205
104
|
|
|
206
105
|
All configuration is centralized and validated at startup in `src/config/index.ts`. Key environment variables in your `.env` file include:
|
|
207
106
|
|
|
208
|
-
| Variable
|
|
209
|
-
|
|
|
210
|
-
| `MCP_TRANSPORT_TYPE`
|
|
211
|
-
| `MCP_HTTP_PORT`
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
107
|
+
| Variable | Description | Default |
|
|
108
|
+
| :-------------------------- | :----------------------------------------------------------------------------- | :---------- |
|
|
109
|
+
| `MCP_TRANSPORT_TYPE` | The transport to use: `stdio` or `http`. | `http` |
|
|
110
|
+
| `MCP_HTTP_PORT` | The port for the HTTP server. | `3010` |
|
|
111
|
+
| `MCP_HTTP_HOST` | The hostname for the HTTP server. | `127.0.0.1` |
|
|
112
|
+
| `MCP_AUTH_MODE` | Authentication mode: `none`, `jwt`, or `oauth`. | `none` |
|
|
113
|
+
| `MCP_AUTH_SECRET_KEY` | **Required for `jwt` auth mode.** A 32+ character secret. | `(none)` |
|
|
114
|
+
| `OAUTH_ISSUER_URL` | **Required for `oauth` auth mode.** URL of the OIDC provider. | `(none)` |
|
|
115
|
+
| `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv`, `r2`. | `in-memory` |
|
|
116
|
+
| `STORAGE_FILESYSTEM_PATH` | **Required for `filesystem` storage.** Path to the storage directory. | `(none)` |
|
|
117
|
+
| `SUPABASE_URL` | **Required for `supabase` storage.** Your Supabase project URL. | `(none)` |
|
|
118
|
+
| `SUPABASE_SERVICE_ROLE_KEY` | **Required for `supabase` storage.** Your Supabase service role key. | `(none)` |
|
|
119
|
+
| `OTEL_ENABLED` | Set to `true` to enable OpenTelemetry. | `false` |
|
|
120
|
+
| `LOG_LEVEL` | The minimum level for logging (`debug`, `info`, `warn`, `error`). | `info` |
|
|
121
|
+
| `OPENROUTER_API_KEY` | API key for OpenRouter LLM service. | `(none)` |
|
|
216
122
|
|
|
217
123
|
### Authentication & Authorization
|
|
218
124
|
|
|
@@ -267,9 +173,12 @@ bun deploy:dev
|
|
|
267
173
|
```
|
|
268
174
|
|
|
269
175
|
3. **Deploy to Cloudflare**:
|
|
270
|
-
|
|
176
|
+
|
|
177
|
+
```sh
|
|
271
178
|
bun deploy:prod
|
|
272
|
-
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
> **Note**: The `wrangler.toml` file is pre-configured to enable `nodejs_compat` for best results.
|
|
273
182
|
|
|
274
183
|
## 📂 Project Structure
|
|
275
184
|
|
|
@@ -317,3 +226,12 @@ bun test
|
|
|
317
226
|
## 📜 License
|
|
318
227
|
|
|
319
228
|
This project is licensed under the Apache 2.0 License. See the [LICENSE](./LICENSE) file for details.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
<div align="center">
|
|
233
|
+
<p>
|
|
234
|
+
<a href="https://github.com/sponsors/cyanheads">Sponsor this project</a> •
|
|
235
|
+
<a href="https://www.buymeacoffee.com/cyanheads">Buy me a coffee</a>
|
|
236
|
+
</p>
|
|
237
|
+
</div>
|
package/dist/index.js
CHANGED
|
@@ -117493,7 +117493,7 @@ var z = /* @__PURE__ */ Object.freeze({
|
|
|
117493
117493
|
// package.json
|
|
117494
117494
|
var package_default = {
|
|
117495
117495
|
name: "mcp-ts-template",
|
|
117496
|
-
version: "2.3.
|
|
117496
|
+
version: "2.3.5",
|
|
117497
117497
|
mcpName: "io.github.cyanheads/mcp-ts-template",
|
|
117498
117498
|
description: "The definitive, production-grade template for building powerful and scalable Model Context Protocol (MCP) servers with TypeScript, featuring built-in observability (OpenTelemetry), declarative tooling, robust error handling, and a modular, DI-driven architecture.",
|
|
117499
117499
|
main: "dist/index.js",
|
|
@@ -117620,7 +117620,7 @@ var package_default = {
|
|
|
117620
117620
|
msw: "^2.11.3",
|
|
117621
117621
|
prettier: "^3.6.2",
|
|
117622
117622
|
typedoc: "^0.28.13",
|
|
117623
|
-
typescript: "^5.9.
|
|
117623
|
+
typescript: "^5.9.3",
|
|
117624
117624
|
"typescript-eslint": "8.45.0",
|
|
117625
117625
|
vite: "^7.1.9",
|
|
117626
117626
|
"vite-tsconfig-paths": "^5.1.4",
|
|
@@ -117672,6 +117672,9 @@ var package_default = {
|
|
|
117672
117672
|
"repomix",
|
|
117673
117673
|
"mcp-ts-template"
|
|
117674
117674
|
]
|
|
117675
|
+
},
|
|
117676
|
+
publishConfig: {
|
|
117677
|
+
access: "public"
|
|
117675
117678
|
}
|
|
117676
117679
|
};
|
|
117677
117680
|
|
|
@@ -117768,6 +117771,7 @@ var ConfigSchema = z.object({
|
|
|
117768
117771
|
}, z.enum(["development", "production", "testing"])).default("development"),
|
|
117769
117772
|
mcpTransportType: z.preprocess(emptyStringAsUndefined, z.enum(["stdio", "http"]).default("stdio")),
|
|
117770
117773
|
mcpSessionMode: z.preprocess(emptyStringAsUndefined, z.enum(["stateless", "stateful", "auto"]).default("auto")),
|
|
117774
|
+
mcpResponseVerbosity: z.preprocess(emptyStringAsUndefined, z.enum(["minimal", "standard", "full"]).default("standard")),
|
|
117771
117775
|
mcpHttpPort: z.coerce.number().default(3010),
|
|
117772
117776
|
mcpHttpHost: z.string().default("127.0.0.1"),
|
|
117773
117777
|
mcpHttpEndpointPath: z.string().default("/mcp"),
|
|
@@ -117788,7 +117792,7 @@ var ConfigSchema = z.object({
|
|
|
117788
117792
|
openrouterAppUrl: z.string().default("http://localhost:3000"),
|
|
117789
117793
|
openrouterAppName: z.string(),
|
|
117790
117794
|
openrouterApiKey: z.string().optional(),
|
|
117791
|
-
llmDefaultModel: z.string().default("google/gemini-2.5-flash"),
|
|
117795
|
+
llmDefaultModel: z.string().default("google/gemini-2.5-flash-preview-05-20"),
|
|
117792
117796
|
llmDefaultTemperature: z.coerce.number().optional(),
|
|
117793
117797
|
llmDefaultTopP: z.coerce.number().optional(),
|
|
117794
117798
|
llmDefaultMaxTokens: z.coerce.number().optional(),
|
|
@@ -117882,6 +117886,7 @@ var parseConfig = () => {
|
|
|
117882
117886
|
environment: env.NODE_ENV,
|
|
117883
117887
|
mcpTransportType: env.MCP_TRANSPORT_TYPE,
|
|
117884
117888
|
mcpSessionMode: env.MCP_SESSION_MODE,
|
|
117889
|
+
mcpResponseVerbosity: env.MCP_RESPONSE_VERBOSITY,
|
|
117885
117890
|
mcpHttpPort: env.MCP_HTTP_PORT,
|
|
117886
117891
|
mcpHttpHost: env.MCP_HTTP_HOST,
|
|
117887
117892
|
mcpHttpEndpointPath: env.MCP_HTTP_ENDPOINT_PATH,
|
|
@@ -130348,6 +130353,285 @@ var codeReviewSamplingTool = {
|
|
|
130348
130353
|
responseFormatter: responseFormatter2
|
|
130349
130354
|
};
|
|
130350
130355
|
|
|
130356
|
+
// src/mcp-server/tools/utils/core/toolHandlerFactory.ts
|
|
130357
|
+
var defaultResponseFormatter2 = (result) => [
|
|
130358
|
+
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
130359
|
+
];
|
|
130360
|
+
function createMcpToolHandler({
|
|
130361
|
+
toolName,
|
|
130362
|
+
logic,
|
|
130363
|
+
responseFormatter: responseFormatter3 = defaultResponseFormatter2
|
|
130364
|
+
}) {
|
|
130365
|
+
return async (input, callContext) => {
|
|
130366
|
+
const sdkContext = callContext;
|
|
130367
|
+
const sessionId = typeof sdkContext?.sessionId === "string" ? sdkContext.sessionId : undefined;
|
|
130368
|
+
const appContext = requestContextService.createRequestContext({
|
|
130369
|
+
parentContext: sdkContext,
|
|
130370
|
+
operation: "HandleToolRequest",
|
|
130371
|
+
additionalContext: { toolName, sessionId, input }
|
|
130372
|
+
});
|
|
130373
|
+
if ("elicitInput" in sdkContext && typeof sdkContext.elicitInput === "function") {
|
|
130374
|
+
appContext.elicitInput = sdkContext.elicitInput;
|
|
130375
|
+
}
|
|
130376
|
+
try {
|
|
130377
|
+
const result = await measureToolExecution(() => logic(input, appContext, sdkContext), { ...appContext, toolName }, input);
|
|
130378
|
+
return {
|
|
130379
|
+
structuredContent: result,
|
|
130380
|
+
content: responseFormatter3(result)
|
|
130381
|
+
};
|
|
130382
|
+
} catch (error2) {
|
|
130383
|
+
const mcpError = ErrorHandler.handleError(error2, {
|
|
130384
|
+
operation: `tool:${toolName}`,
|
|
130385
|
+
context: appContext,
|
|
130386
|
+
input
|
|
130387
|
+
});
|
|
130388
|
+
return {
|
|
130389
|
+
isError: true,
|
|
130390
|
+
content: [{ type: "text", text: `Error: ${mcpError.message}` }],
|
|
130391
|
+
structuredContent: {
|
|
130392
|
+
code: mcpError.code,
|
|
130393
|
+
message: mcpError.message,
|
|
130394
|
+
data: mcpError.data
|
|
130395
|
+
}
|
|
130396
|
+
};
|
|
130397
|
+
}
|
|
130398
|
+
};
|
|
130399
|
+
}
|
|
130400
|
+
// src/mcp-server/tools/utils/formatters/markdown-builder.ts
|
|
130401
|
+
class MarkdownBuilder {
|
|
130402
|
+
sections = [];
|
|
130403
|
+
h1(text, emoji) {
|
|
130404
|
+
const prefix = emoji ? `${emoji} ` : "";
|
|
130405
|
+
this.sections.push(`# ${prefix}${text}
|
|
130406
|
+
|
|
130407
|
+
`);
|
|
130408
|
+
return this;
|
|
130409
|
+
}
|
|
130410
|
+
h2(text, emoji) {
|
|
130411
|
+
const prefix = emoji ? `${emoji} ` : "";
|
|
130412
|
+
this.sections.push(`## ${prefix}${text}
|
|
130413
|
+
|
|
130414
|
+
`);
|
|
130415
|
+
return this;
|
|
130416
|
+
}
|
|
130417
|
+
h3(text, emoji) {
|
|
130418
|
+
const prefix = emoji ? `${emoji} ` : "";
|
|
130419
|
+
this.sections.push(`### ${prefix}${text}
|
|
130420
|
+
|
|
130421
|
+
`);
|
|
130422
|
+
return this;
|
|
130423
|
+
}
|
|
130424
|
+
h4(text) {
|
|
130425
|
+
this.sections.push(`#### ${text}
|
|
130426
|
+
|
|
130427
|
+
`);
|
|
130428
|
+
return this;
|
|
130429
|
+
}
|
|
130430
|
+
keyValue(key, value) {
|
|
130431
|
+
const displayValue = value === null ? "null" : String(value);
|
|
130432
|
+
this.sections.push(`**${key}:** ${displayValue}
|
|
130433
|
+
`);
|
|
130434
|
+
return this;
|
|
130435
|
+
}
|
|
130436
|
+
keyValuePlain(key, value) {
|
|
130437
|
+
const displayValue = value === null ? "null" : String(value);
|
|
130438
|
+
this.sections.push(`${key}: ${displayValue}
|
|
130439
|
+
`);
|
|
130440
|
+
return this;
|
|
130441
|
+
}
|
|
130442
|
+
list(items, ordered = false) {
|
|
130443
|
+
if (items.length === 0)
|
|
130444
|
+
return this;
|
|
130445
|
+
const marker = ordered ? (i) => `${i + 1}.` : () => "-";
|
|
130446
|
+
this.sections.push(items.map((item, i) => `${marker(i)} ${item}`).join(`
|
|
130447
|
+
`) + `
|
|
130448
|
+
|
|
130449
|
+
`);
|
|
130450
|
+
return this;
|
|
130451
|
+
}
|
|
130452
|
+
codeBlock(content, language = "") {
|
|
130453
|
+
this.sections.push(`\`\`\`${language}
|
|
130454
|
+
${content}
|
|
130455
|
+
\`\`\`
|
|
130456
|
+
|
|
130457
|
+
`);
|
|
130458
|
+
return this;
|
|
130459
|
+
}
|
|
130460
|
+
inlineCode(code) {
|
|
130461
|
+
this.sections.push(`\`${code}\``);
|
|
130462
|
+
return this;
|
|
130463
|
+
}
|
|
130464
|
+
paragraph(text) {
|
|
130465
|
+
this.sections.push(`${text}
|
|
130466
|
+
|
|
130467
|
+
`);
|
|
130468
|
+
return this;
|
|
130469
|
+
}
|
|
130470
|
+
blockquote(text) {
|
|
130471
|
+
const lines = text.split(`
|
|
130472
|
+
`);
|
|
130473
|
+
const quoted = lines.map((line) => `> ${line}`).join(`
|
|
130474
|
+
`);
|
|
130475
|
+
this.sections.push(`${quoted}
|
|
130476
|
+
|
|
130477
|
+
`);
|
|
130478
|
+
return this;
|
|
130479
|
+
}
|
|
130480
|
+
hr() {
|
|
130481
|
+
this.sections.push(`---
|
|
130482
|
+
|
|
130483
|
+
`);
|
|
130484
|
+
return this;
|
|
130485
|
+
}
|
|
130486
|
+
link(text, url) {
|
|
130487
|
+
this.sections.push(`[${text}](${url})`);
|
|
130488
|
+
return this;
|
|
130489
|
+
}
|
|
130490
|
+
table(headers, rows) {
|
|
130491
|
+
if (headers.length === 0 || rows.length === 0)
|
|
130492
|
+
return this;
|
|
130493
|
+
this.sections.push(`| ${headers.join(" | ")} |
|
|
130494
|
+
`);
|
|
130495
|
+
this.sections.push(`| ${headers.map(() => "---").join(" | ")} |
|
|
130496
|
+
`);
|
|
130497
|
+
rows.forEach((row) => {
|
|
130498
|
+
this.sections.push(`| ${row.join(" | ")} |
|
|
130499
|
+
`);
|
|
130500
|
+
});
|
|
130501
|
+
this.sections.push(`
|
|
130502
|
+
`);
|
|
130503
|
+
return this;
|
|
130504
|
+
}
|
|
130505
|
+
section(title, levelOrContent, content) {
|
|
130506
|
+
const level = typeof levelOrContent === "function" ? 2 : levelOrContent;
|
|
130507
|
+
const callback = typeof levelOrContent === "function" ? levelOrContent : content;
|
|
130508
|
+
switch (level) {
|
|
130509
|
+
case 2:
|
|
130510
|
+
this.h2(title);
|
|
130511
|
+
break;
|
|
130512
|
+
case 3:
|
|
130513
|
+
this.h3(title);
|
|
130514
|
+
break;
|
|
130515
|
+
case 4:
|
|
130516
|
+
this.h4(title);
|
|
130517
|
+
break;
|
|
130518
|
+
}
|
|
130519
|
+
callback();
|
|
130520
|
+
return this;
|
|
130521
|
+
}
|
|
130522
|
+
details(summary, details) {
|
|
130523
|
+
this.sections.push(`<details>
|
|
130524
|
+
<summary>${summary}</summary>
|
|
130525
|
+
|
|
130526
|
+
`);
|
|
130527
|
+
this.sections.push(`${details}
|
|
130528
|
+
|
|
130529
|
+
`);
|
|
130530
|
+
this.sections.push(`</details>
|
|
130531
|
+
|
|
130532
|
+
`);
|
|
130533
|
+
return this;
|
|
130534
|
+
}
|
|
130535
|
+
alert(type, content) {
|
|
130536
|
+
const typeUpper = type.toUpperCase();
|
|
130537
|
+
const lines = content.split(`
|
|
130538
|
+
`);
|
|
130539
|
+
this.sections.push(`> [!${typeUpper}]
|
|
130540
|
+
`);
|
|
130541
|
+
lines.forEach((line) => {
|
|
130542
|
+
this.sections.push(`> ${line}
|
|
130543
|
+
`);
|
|
130544
|
+
});
|
|
130545
|
+
this.sections.push(`
|
|
130546
|
+
`);
|
|
130547
|
+
return this;
|
|
130548
|
+
}
|
|
130549
|
+
taskList(items) {
|
|
130550
|
+
if (items.length === 0)
|
|
130551
|
+
return this;
|
|
130552
|
+
this.sections.push(items.map((item) => `- [${item.checked ? "x" : " "}] ${item.text}`).join(`
|
|
130553
|
+
`) + `
|
|
130554
|
+
|
|
130555
|
+
`);
|
|
130556
|
+
return this;
|
|
130557
|
+
}
|
|
130558
|
+
image(altText, url, title) {
|
|
130559
|
+
const titlePart = title ? ` "${title}"` : "";
|
|
130560
|
+
this.sections.push(`
|
|
130561
|
+
|
|
130562
|
+
`);
|
|
130563
|
+
return this;
|
|
130564
|
+
}
|
|
130565
|
+
strikethrough(text) {
|
|
130566
|
+
this.sections.push(`~~${text}~~`);
|
|
130567
|
+
return this;
|
|
130568
|
+
}
|
|
130569
|
+
diff(changes) {
|
|
130570
|
+
const lines = [];
|
|
130571
|
+
if (changes.context) {
|
|
130572
|
+
lines.push(...changes.context.map((line) => ` ${line}`));
|
|
130573
|
+
}
|
|
130574
|
+
if (changes.deletions) {
|
|
130575
|
+
lines.push(...changes.deletions.map((line) => `- ${line}`));
|
|
130576
|
+
}
|
|
130577
|
+
if (changes.additions) {
|
|
130578
|
+
lines.push(...changes.additions.map((line) => `+ ${line}`));
|
|
130579
|
+
}
|
|
130580
|
+
if (lines.length > 0) {
|
|
130581
|
+
this.codeBlock(lines.join(`
|
|
130582
|
+
`), "diff");
|
|
130583
|
+
}
|
|
130584
|
+
return this;
|
|
130585
|
+
}
|
|
130586
|
+
badge(label, message, color = "blue") {
|
|
130587
|
+
const encodedLabel = encodeURIComponent(label);
|
|
130588
|
+
const encodedMessage = encodeURIComponent(message);
|
|
130589
|
+
const url = `https://img.shields.io/badge/${encodedLabel}-${encodedMessage}-${color}`;
|
|
130590
|
+
this.sections.push(``);
|
|
130591
|
+
return this;
|
|
130592
|
+
}
|
|
130593
|
+
bold(text) {
|
|
130594
|
+
this.sections.push(`**${text}**`);
|
|
130595
|
+
return this;
|
|
130596
|
+
}
|
|
130597
|
+
italic(text) {
|
|
130598
|
+
this.sections.push(`*${text}*`);
|
|
130599
|
+
return this;
|
|
130600
|
+
}
|
|
130601
|
+
boldItalic(text) {
|
|
130602
|
+
this.sections.push(`***${text}***`);
|
|
130603
|
+
return this;
|
|
130604
|
+
}
|
|
130605
|
+
raw(markdown) {
|
|
130606
|
+
this.sections.push(markdown);
|
|
130607
|
+
return this;
|
|
130608
|
+
}
|
|
130609
|
+
blankLine() {
|
|
130610
|
+
this.sections.push(`
|
|
130611
|
+
`);
|
|
130612
|
+
return this;
|
|
130613
|
+
}
|
|
130614
|
+
text(text) {
|
|
130615
|
+
this.sections.push(text);
|
|
130616
|
+
return this;
|
|
130617
|
+
}
|
|
130618
|
+
when(condition, content) {
|
|
130619
|
+
if (condition) {
|
|
130620
|
+
content();
|
|
130621
|
+
}
|
|
130622
|
+
return this;
|
|
130623
|
+
}
|
|
130624
|
+
build() {
|
|
130625
|
+
return this.sections.join("").trim();
|
|
130626
|
+
}
|
|
130627
|
+
reset() {
|
|
130628
|
+
this.sections = [];
|
|
130629
|
+
return this;
|
|
130630
|
+
}
|
|
130631
|
+
}
|
|
130632
|
+
function markdown() {
|
|
130633
|
+
return new MarkdownBuilder;
|
|
130634
|
+
}
|
|
130351
130635
|
// src/mcp-server/tools/definitions/template-echo-message.tool.ts
|
|
130352
130636
|
var TOOL_NAME3 = "template_echo_message";
|
|
130353
130637
|
var TOOL_TITLE3 = "Template Echo Message";
|
|
@@ -130404,16 +130688,13 @@ async function echoToolLogic(input, appContext, _sdkContext) {
|
|
|
130404
130688
|
}
|
|
130405
130689
|
function responseFormatter3(result) {
|
|
130406
130690
|
const preview = result.repeatedMessage.length > 200 ? `${result.repeatedMessage.slice(0, 197)}…` : result.repeatedMessage;
|
|
130407
|
-
const
|
|
130408
|
-
|
|
130409
|
-
|
|
130410
|
-
result.timestamp ? `timestamp=${result.timestamp}` : undefined
|
|
130411
|
-
].filter(Boolean);
|
|
130691
|
+
const md = markdown().paragraph(`Echo (mode=${result.mode}, repeat=${result.repeatCount})`).paragraph(preview).when(!!result.timestamp, () => {
|
|
130692
|
+
md.keyValuePlain("timestamp", result.timestamp);
|
|
130693
|
+
});
|
|
130412
130694
|
return [
|
|
130413
130695
|
{
|
|
130414
130696
|
type: "text",
|
|
130415
|
-
text:
|
|
130416
|
-
`)
|
|
130697
|
+
text: md.build()
|
|
130417
130698
|
}
|
|
130418
130699
|
];
|
|
130419
130700
|
}
|
|
@@ -130602,51 +130883,6 @@ var allToolDefinitions = [
|
|
|
130602
130883
|
madlibsElicitationTool
|
|
130603
130884
|
];
|
|
130604
130885
|
|
|
130605
|
-
// src/mcp-server/tools/utils/toolHandlerFactory.ts
|
|
130606
|
-
var defaultResponseFormatter2 = (result) => [
|
|
130607
|
-
{ type: "text", text: JSON.stringify(result, null, 2) }
|
|
130608
|
-
];
|
|
130609
|
-
function createMcpToolHandler({
|
|
130610
|
-
toolName,
|
|
130611
|
-
logic,
|
|
130612
|
-
responseFormatter: responseFormatter6 = defaultResponseFormatter2
|
|
130613
|
-
}) {
|
|
130614
|
-
return async (input, callContext) => {
|
|
130615
|
-
const sdkContext = callContext;
|
|
130616
|
-
const sessionId = typeof sdkContext?.sessionId === "string" ? sdkContext.sessionId : undefined;
|
|
130617
|
-
const appContext = requestContextService.createRequestContext({
|
|
130618
|
-
parentContext: sdkContext,
|
|
130619
|
-
operation: "HandleToolRequest",
|
|
130620
|
-
additionalContext: { toolName, sessionId, input }
|
|
130621
|
-
});
|
|
130622
|
-
if ("elicitInput" in sdkContext && typeof sdkContext.elicitInput === "function") {
|
|
130623
|
-
appContext.elicitInput = sdkContext.elicitInput;
|
|
130624
|
-
}
|
|
130625
|
-
try {
|
|
130626
|
-
const result = await measureToolExecution(() => logic(input, appContext, sdkContext), { ...appContext, toolName }, input);
|
|
130627
|
-
return {
|
|
130628
|
-
structuredContent: result,
|
|
130629
|
-
content: responseFormatter6(result)
|
|
130630
|
-
};
|
|
130631
|
-
} catch (error2) {
|
|
130632
|
-
const mcpError = ErrorHandler.handleError(error2, {
|
|
130633
|
-
operation: `tool:${toolName}`,
|
|
130634
|
-
context: appContext,
|
|
130635
|
-
input
|
|
130636
|
-
});
|
|
130637
|
-
return {
|
|
130638
|
-
isError: true,
|
|
130639
|
-
content: [{ type: "text", text: `Error: ${mcpError.message}` }],
|
|
130640
|
-
structuredContent: {
|
|
130641
|
-
code: mcpError.code,
|
|
130642
|
-
message: mcpError.message,
|
|
130643
|
-
data: mcpError.data
|
|
130644
|
-
}
|
|
130645
|
-
};
|
|
130646
|
-
}
|
|
130647
|
-
};
|
|
130648
|
-
}
|
|
130649
|
-
|
|
130650
130886
|
// src/mcp-server/tools/tool-registration.ts
|
|
130651
130887
|
class ToolRegistry {
|
|
130652
130888
|
toolDefs;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-ts-template",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.6",
|
|
4
4
|
"mcpName": "io.github.cyanheads/mcp-ts-template",
|
|
5
5
|
"description": "The definitive, production-grade template for building powerful and scalable Model Context Protocol (MCP) servers with TypeScript, featuring built-in observability (OpenTelemetry), declarative tooling, robust error handling, and a modular, DI-driven architecture.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -62,30 +62,30 @@
|
|
|
62
62
|
"@hono/node-server": "1.19.5",
|
|
63
63
|
"chrono-node": "2.9.0",
|
|
64
64
|
"dotenv": "17.2.3",
|
|
65
|
-
"hono": "4.9.
|
|
65
|
+
"hono": "4.9.11",
|
|
66
66
|
"zod": "3.23.8",
|
|
67
67
|
"typescript": "5.9.3"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@hono/mcp": "^0.1.4",
|
|
71
71
|
"@hono/node-server": "^1.19.5",
|
|
72
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
73
|
-
"@supabase/supabase-js": "^2.
|
|
72
|
+
"@modelcontextprotocol/sdk": "^1.20.0",
|
|
73
|
+
"@supabase/supabase-js": "^2.75.0",
|
|
74
74
|
"axios": "^1.12.2",
|
|
75
75
|
"chrono-node": "^2.9.0",
|
|
76
76
|
"dotenv": "^17.2.3",
|
|
77
77
|
"fast-xml-parser": "^5.3.0",
|
|
78
|
-
"hono": "^4.9.
|
|
78
|
+
"hono": "^4.9.11",
|
|
79
79
|
"ignore": "^7.0.5",
|
|
80
80
|
"jose": "^6.1.0",
|
|
81
81
|
"js-yaml": "^4.1.0",
|
|
82
82
|
"node-cron": "^4.2.1",
|
|
83
|
-
"openai": "^6.
|
|
83
|
+
"openai": "^6.3.0",
|
|
84
84
|
"papaparse": "^5.5.3",
|
|
85
85
|
"partial-json": "^0.1.7",
|
|
86
86
|
"pdf-lib": "^1.17.1",
|
|
87
87
|
"pino": "^10.0.0",
|
|
88
|
-
"pino-pretty": "^13.1.
|
|
88
|
+
"pino-pretty": "^13.1.2",
|
|
89
89
|
"reflect-metadata": "^0.2.2",
|
|
90
90
|
"repomix": "^1.6.1",
|
|
91
91
|
"sanitize-html": "^2.17.0",
|
|
@@ -94,22 +94,22 @@
|
|
|
94
94
|
"validator": "13.15.15",
|
|
95
95
|
"zod": "^3.23.8",
|
|
96
96
|
"@opentelemetry/api": "^1.9.0",
|
|
97
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
98
|
-
"@opentelemetry/exporter-metrics-otlp-http": "^0.
|
|
99
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
100
|
-
"@opentelemetry/instrumentation-pino": "^0.
|
|
97
|
+
"@opentelemetry/auto-instrumentations-node": "^0.65.0",
|
|
98
|
+
"@opentelemetry/exporter-metrics-otlp-http": "^0.206.0",
|
|
99
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.206.0",
|
|
100
|
+
"@opentelemetry/instrumentation-pino": "^0.53.0",
|
|
101
101
|
"@opentelemetry/resources": "^2.1.0",
|
|
102
102
|
"@opentelemetry/sdk-metrics": "^2.1.0",
|
|
103
|
-
"@opentelemetry/sdk-node": "^0.
|
|
103
|
+
"@opentelemetry/sdk-node": "^0.206.0",
|
|
104
104
|
"@opentelemetry/sdk-trace-node": "^2.1.0",
|
|
105
105
|
"@opentelemetry/semantic-conventions": "^1.37.0"
|
|
106
106
|
},
|
|
107
107
|
"devDependencies": {
|
|
108
|
-
"@cloudflare/workers-types": "^4.
|
|
108
|
+
"@cloudflare/workers-types": "^4.20251011.0",
|
|
109
109
|
"@eslint/js": "^9.37.0",
|
|
110
|
-
"@types/bun": "^1.
|
|
110
|
+
"@types/bun": "^1.3.0",
|
|
111
111
|
"@types/js-yaml": "^4.0.9",
|
|
112
|
-
"@types/node": "^24.
|
|
112
|
+
"@types/node": "^24.7.2",
|
|
113
113
|
"@types/node-cron": "^3.0.11",
|
|
114
114
|
"@types/papaparse": "^5.3.16",
|
|
115
115
|
"@types/sanitize-html": "^2.16.0",
|
|
@@ -117,19 +117,19 @@
|
|
|
117
117
|
"@vitest/coverage-v8": "3.2.4",
|
|
118
118
|
"ajv": "^8.17.1",
|
|
119
119
|
"ajv-formats": "^3.0.1",
|
|
120
|
-
"bun-types": "^1.
|
|
120
|
+
"bun-types": "^1.3.0",
|
|
121
121
|
"clipboardy": "^5.0.0",
|
|
122
122
|
"depcheck": "^1.4.7",
|
|
123
123
|
"eslint": "^9.37.0",
|
|
124
124
|
"execa": "^9.6.0",
|
|
125
125
|
"globals": "^16.4.0",
|
|
126
126
|
"husky": "^9.1.7",
|
|
127
|
-
"msw": "^2.11.
|
|
127
|
+
"msw": "^2.11.5",
|
|
128
128
|
"prettier": "^3.6.2",
|
|
129
|
-
"typedoc": "^0.28.
|
|
130
|
-
"typescript": "^5.9.
|
|
131
|
-
"typescript-eslint": "8.
|
|
132
|
-
"vite": "
|
|
129
|
+
"typedoc": "^0.28.14",
|
|
130
|
+
"typescript": "^5.9.3",
|
|
131
|
+
"typescript-eslint": "8.46.0",
|
|
132
|
+
"vite": "7.1.9",
|
|
133
133
|
"vite-tsconfig-paths": "^5.1.4",
|
|
134
134
|
"vitest": "^3.2.4"
|
|
135
135
|
},
|
|
@@ -179,5 +179,8 @@
|
|
|
179
179
|
"repomix",
|
|
180
180
|
"mcp-ts-template"
|
|
181
181
|
]
|
|
182
|
+
},
|
|
183
|
+
"publishConfig": {
|
|
184
|
+
"access": "public"
|
|
182
185
|
}
|
|
183
186
|
}
|