hive-skills 0.1.0
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/LICENSE +28 -0
- package/README.md +235 -0
- package/assets/LICENSE +32 -0
- package/assets/THIRD_PARTY_NOTICES.md +72 -0
- package/assets/manifest.json +1290 -0
- package/assets/skills/authored/code-review/composable/BUNDLE.md +192 -0
- package/assets/skills/authored/code-review/composable/INDEX.md +13 -0
- package/assets/skills/authored/code-review/composable/VERSION +1 -0
- package/assets/skills/authored/code-review/composable/mini/01-review-method.md +34 -0
- package/assets/skills/authored/code-review/composable/mini/02-security-review.md +20 -0
- package/assets/skills/authored/code-review/composable/mini/03-correctness-bugs.md +19 -0
- package/assets/skills/authored/code-review/composable/mini/04-concurrency.md +16 -0
- package/assets/skills/authored/code-review/composable/mini/05-error-handling.md +12 -0
- package/assets/skills/authored/code-review/composable/mini/06-performance.md +11 -0
- package/assets/skills/authored/code-review/composable/mini/07-api-design.md +20 -0
- package/assets/skills/authored/code-review/composable/mini/08-missing-tests.md +25 -0
- package/assets/skills/authored/code-review/composable/mini/09-refactoring-and-communication.md +32 -0
- package/assets/skills/authored/data-analysis/composable/BUNDLE.md +186 -0
- package/assets/skills/authored/data-analysis/composable/INDEX.md +13 -0
- package/assets/skills/authored/data-analysis/composable/VERSION +1 -0
- package/assets/skills/authored/data-analysis/composable/mini/01-data-quality-profiling.md +26 -0
- package/assets/skills/authored/data-analysis/composable/mini/02-aggregation-pitfalls.md +21 -0
- package/assets/skills/authored/data-analysis/composable/mini/03-time-series.md +19 -0
- package/assets/skills/authored/data-analysis/composable/mini/04-segmentation.md +23 -0
- package/assets/skills/authored/data-analysis/composable/mini/05-discount-pricing.md +17 -0
- package/assets/skills/authored/data-analysis/composable/mini/06-insight-generation.md +25 -0
- package/assets/skills/authored/data-analysis/composable/mini/07-executive-writing.md +26 -0
- package/assets/skills/authored/data-analysis/composable/mini/08-recommendations.md +20 -0
- package/assets/skills/authored/data-analysis/composable/mini/09-reproducibility-assumptions.md +20 -0
- package/assets/skills/authored/financial-analysis/composable/BUNDLE.md +1293 -0
- package/assets/skills/authored/financial-analysis/composable/INDEX.md +17 -0
- package/assets/skills/authored/financial-analysis/composable/VERSION +1 -0
- package/assets/skills/authored/financial-analysis/composable/mini/00-core.md +69 -0
- package/assets/skills/authored/financial-analysis/composable/mini/01-profitability-efficiency-ratios.md +173 -0
- package/assets/skills/authored/financial-analysis/composable/mini/02-liquidity-leverage-ratios.md +120 -0
- package/assets/skills/authored/financial-analysis/composable/mini/03-valuation-ratios-interpretation.md +107 -0
- package/assets/skills/authored/financial-analysis/composable/mini/04-dcf-projection-wacc.md +122 -0
- package/assets/skills/authored/financial-analysis/composable/mini/05-dcf-terminal-value-sensitivity.md +75 -0
- package/assets/skills/authored/financial-analysis/composable/mini/06-comparables-precedents.md +78 -0
- package/assets/skills/authored/financial-analysis/composable/mini/07-budget-variance-analysis.md +47 -0
- package/assets/skills/authored/financial-analysis/composable/mini/08-driver-based-forecasting-scenarios.md +111 -0
- package/assets/skills/authored/financial-analysis/composable/mini/09-rolling-forecasts-accuracy.md +114 -0
- package/assets/skills/authored/financial-analysis/composable/mini/10-reporting-exec-summary.md +58 -0
- package/assets/skills/authored/financial-analysis/composable/mini/11-industry-adaptations.md +130 -0
- package/assets/skills/authored/financial-analysis/composable/mini/12-toolkit-scripts-io.md +102 -0
- package/assets/skills/authored/python-api/composable/BUNDLE.md +389 -0
- package/assets/skills/authored/python-api/composable/INDEX.md +13 -0
- package/assets/skills/authored/python-api/composable/VERSION +1 -0
- package/assets/skills/authored/python-api/composable/mini/01-project-structure.md +44 -0
- package/assets/skills/authored/python-api/composable/mini/02-routing-and-app.md +38 -0
- package/assets/skills/authored/python-api/composable/mini/03-pydantic-models.md +45 -0
- package/assets/skills/authored/python-api/composable/mini/04-validation-and-errors.md +59 -0
- package/assets/skills/authored/python-api/composable/mini/05-dependencies-and-auth.md +60 -0
- package/assets/skills/authored/python-api/composable/mini/06-pagination-filtering.md +44 -0
- package/assets/skills/authored/python-api/composable/mini/07-async-performance.md +30 -0
- package/assets/skills/authored/python-api/composable/mini/08-testing.md +45 -0
- package/assets/skills/authored/python-api/composable/mini/09-middleware-observability.md +29 -0
- package/assets/skills/authored/tech-writing/composable/BUNDLE.md +220 -0
- package/assets/skills/authored/tech-writing/composable/INDEX.md +12 -0
- package/assets/skills/authored/tech-writing/composable/VERSION +1 -0
- package/assets/skills/authored/tech-writing/composable/mini/01-audience-analysis.md +26 -0
- package/assets/skills/authored/tech-writing/composable/mini/02-document-types.md +27 -0
- package/assets/skills/authored/tech-writing/composable/mini/03-readme-quickstart-changelog.md +22 -0
- package/assets/skills/authored/tech-writing/composable/mini/04-information-architecture.md +21 -0
- package/assets/skills/authored/tech-writing/composable/mini/05-style-clarity.md +29 -0
- package/assets/skills/authored/tech-writing/composable/mini/06-breaking-changes-migrations.md +33 -0
- package/assets/skills/authored/tech-writing/composable/mini/07-troubleshooting-security.md +30 -0
- package/assets/skills/authored/tech-writing/composable/mini/08-faq-voice-tone.md +32 -0
- package/assets/skills/converted/claude-api/composable/BUNDLE.md +14996 -0
- package/assets/skills/converted/claude-api/composable/INDEX.md +98 -0
- package/assets/skills/converted/claude-api/composable/VERSION +1 -0
- package/assets/skills/converted/claude-api/composable/mini/00-core.md +235 -0
- package/assets/skills/converted/claude-api/composable/mini/01-model-api-quick-reference.md +258 -0
- package/assets/skills/converted/claude-api/composable/mini/02-common-pitfalls.md +42 -0
- package/assets/skills/converted/claude-api/composable/mini/10-models.md +135 -0
- package/assets/skills/converted/claude-api/composable/mini/11-platform-availability.md +96 -0
- package/assets/skills/converted/claude-api/composable/mini/12-claude-platform-on-aws.md +59 -0
- package/assets/skills/converted/claude-api/composable/mini/13-live-sources.md +143 -0
- package/assets/skills/converted/claude-api/composable/mini/14-error-codes.md +256 -0
- package/assets/skills/converted/claude-api/composable/mini/15-prompt-caching.md +223 -0
- package/assets/skills/converted/claude-api/composable/mini/16-token-counting.md +56 -0
- package/assets/skills/converted/claude-api/composable/mini/17-tool-use-concepts.md +444 -0
- package/assets/skills/converted/claude-api/composable/mini/18-agent-design.md +101 -0
- package/assets/skills/converted/claude-api/composable/mini/19-anthropic-cli.md +246 -0
- package/assets/skills/converted/claude-api/composable/mini/20-model-migration.md +1301 -0
- package/assets/skills/converted/claude-api/composable/mini/30-managed-agents-overview.md +71 -0
- package/assets/skills/converted/claude-api/composable/mini/31-managed-agents-core.md +278 -0
- package/assets/skills/converted/claude-api/composable/mini/32-managed-agents-environments.md +219 -0
- package/assets/skills/converted/claude-api/composable/mini/33-managed-agents-tools.md +366 -0
- package/assets/skills/converted/claude-api/composable/mini/34-managed-agents-events.md +252 -0
- package/assets/skills/converted/claude-api/composable/mini/35-managed-agents-outcomes.md +106 -0
- package/assets/skills/converted/claude-api/composable/mini/36-managed-agents-multiagent.md +101 -0
- package/assets/skills/converted/claude-api/composable/mini/37-managed-agents-webhooks.md +123 -0
- package/assets/skills/converted/claude-api/composable/mini/38-managed-agents-memory.md +197 -0
- package/assets/skills/converted/claude-api/composable/mini/39-managed-agents-scheduled-deployments.md +146 -0
- package/assets/skills/converted/claude-api/composable/mini/40-managed-agents-client-patterns.md +211 -0
- package/assets/skills/converted/claude-api/composable/mini/41-managed-agents-onboarding.md +82 -0
- package/assets/skills/converted/claude-api/composable/mini/42-managed-agents-api-reference.md +441 -0
- package/assets/skills/converted/claude-api/composable/mini/43-managed-agents-self-hosted-sandboxes.md +174 -0
- package/assets/skills/converted/claude-api/composable/mini/50-python-readme.md +564 -0
- package/assets/skills/converted/claude-api/composable/mini/51-python-tool-use.md +590 -0
- package/assets/skills/converted/claude-api/composable/mini/52-python-streaming-batches-files.md +549 -0
- package/assets/skills/converted/claude-api/composable/mini/53-python-managed-agents.md +335 -0
- package/assets/skills/converted/claude-api/composable/mini/55-typescript-readme.md +404 -0
- package/assets/skills/converted/claude-api/composable/mini/56-typescript-tool-use.md +548 -0
- package/assets/skills/converted/claude-api/composable/mini/57-typescript-streaming-batches-files.md +384 -0
- package/assets/skills/converted/claude-api/composable/mini/58-typescript-managed-agents.md +358 -0
- package/assets/skills/converted/claude-api/composable/mini/60-csharp-readme.md +361 -0
- package/assets/skills/converted/claude-api/composable/mini/61-csharp-tool-use.md +163 -0
- package/assets/skills/converted/claude-api/composable/mini/62-csharp-streaming-batches-files.md +64 -0
- package/assets/skills/converted/claude-api/composable/mini/65-go-readme.md +185 -0
- package/assets/skills/converted/claude-api/composable/mini/66-go-tool-use.md +219 -0
- package/assets/skills/converted/claude-api/composable/mini/67-go-streaming-files.md +63 -0
- package/assets/skills/converted/claude-api/composable/mini/68-go-managed-agents.md +564 -0
- package/assets/skills/converted/claude-api/composable/mini/70-java-readme.md +237 -0
- package/assets/skills/converted/claude-api/composable/mini/71-java-tool-use.md +227 -0
- package/assets/skills/converted/claude-api/composable/mini/72-java-streaming-files.md +49 -0
- package/assets/skills/converted/claude-api/composable/mini/73-java-managed-agents.md +443 -0
- package/assets/skills/converted/claude-api/composable/mini/75-php-readme.md +173 -0
- package/assets/skills/converted/claude-api/composable/mini/76-php-tool-use.md +252 -0
- package/assets/skills/converted/claude-api/composable/mini/77-php-streaming-batches-files.md +53 -0
- package/assets/skills/converted/claude-api/composable/mini/78-php-managed-agents.md +443 -0
- package/assets/skills/converted/claude-api/composable/mini/80-ruby-readme.md +133 -0
- package/assets/skills/converted/claude-api/composable/mini/81-ruby-tool-use-streaming.md +56 -0
- package/assets/skills/converted/claude-api/composable/mini/82-ruby-managed-agents.md +394 -0
- package/assets/skills/converted/claude-api/composable/mini/85-curl-examples.md +255 -0
- package/assets/skills/converted/claude-api/composable/mini/86-curl-managed-agents.md +340 -0
- package/assets/skills/converted/claude-api/composable/presets/csharp.md +1151 -0
- package/assets/skills/converted/claude-api/composable/presets/curl.md +1154 -0
- package/assets/skills/converted/claude-api/composable/presets/go.md +1598 -0
- package/assets/skills/converted/claude-api/composable/presets/java.md +1523 -0
- package/assets/skills/converted/claude-api/composable/presets/php.md +1488 -0
- package/assets/skills/converted/claude-api/composable/presets/python.md +2605 -0
- package/assets/skills/converted/claude-api/composable/presets/ruby.md +1146 -0
- package/assets/skills/converted/claude-api/composable/presets/shared.md +6478 -0
- package/assets/skills/converted/claude-api/composable/presets/typescript.md +2261 -0
- package/assets/skills/converted/docx/composable/BUNDLE.md +617 -0
- package/assets/skills/converted/docx/composable/INDEX.md +12 -0
- package/assets/skills/converted/docx/composable/VERSION +1 -0
- package/assets/skills/converted/docx/composable/mini/00-core.md +26 -0
- package/assets/skills/converted/docx/composable/mini/01-reading-converting.md +36 -0
- package/assets/skills/converted/docx/composable/mini/02-creating-setup-layout.md +85 -0
- package/assets/skills/converted/docx/composable/mini/03-creating-elements.md +257 -0
- package/assets/skills/converted/docx/composable/mini/04-editing-existing.md +57 -0
- package/assets/skills/converted/docx/composable/mini/05-xml-reference.md +128 -0
- package/assets/skills/converted/internal-comms/composable/BUNDLE.md +220 -0
- package/assets/skills/converted/internal-comms/composable/INDEX.md +11 -0
- package/assets/skills/converted/internal-comms/composable/VERSION +1 -0
- package/assets/skills/converted/internal-comms/composable/mini/00-core.md +30 -0
- package/assets/skills/converted/internal-comms/composable/mini/01-3p-updates.md +49 -0
- package/assets/skills/converted/internal-comms/composable/mini/02-company-newsletter.md +67 -0
- package/assets/skills/converted/internal-comms/composable/mini/03-faq-answers.md +32 -0
- package/assets/skills/converted/internal-comms/composable/mini/04-general-comms.md +18 -0
- package/assets/skills/converted/mcp-builder/composable/BUNDLE.md +2460 -0
- package/assets/skills/converted/mcp-builder/composable/INDEX.md +23 -0
- package/assets/skills/converted/mcp-builder/composable/VERSION +1 -0
- package/assets/skills/converted/mcp-builder/composable/mini/00-core.md +43 -0
- package/assets/skills/converted/mcp-builder/composable/mini/01-workflow-research-planning.md +54 -0
- package/assets/skills/converted/mcp-builder/composable/mini/02-implementation-process.md +78 -0
- package/assets/skills/converted/mcp-builder/composable/mini/03-naming-conventions.md +33 -0
- package/assets/skills/converted/mcp-builder/composable/mini/04-response-formats.md +21 -0
- package/assets/skills/converted/mcp-builder/composable/mini/05-pagination.md +24 -0
- package/assets/skills/converted/mcp-builder/composable/mini/06-transport.md +47 -0
- package/assets/skills/converted/mcp-builder/composable/mini/07-annotations.md +20 -0
- package/assets/skills/converted/mcp-builder/composable/mini/08-security.md +35 -0
- package/assets/skills/converted/mcp-builder/composable/mini/09-error-handling.md +32 -0
- package/assets/skills/converted/mcp-builder/composable/mini/10-testing-and-documentation.md +21 -0
- package/assets/skills/converted/mcp-builder/composable/mini/11-python-implementation.md +432 -0
- package/assets/skills/converted/mcp-builder/composable/mini/12-python-advanced-and-checklist.md +233 -0
- package/assets/skills/converted/mcp-builder/composable/mini/13-node-implementation.md +717 -0
- package/assets/skills/converted/mcp-builder/composable/mini/14-node-advanced-and-checklist.md +200 -0
- package/assets/skills/converted/mcp-builder/composable/mini/15-evaluation-design.md +200 -0
- package/assets/skills/converted/mcp-builder/composable/mini/16-evaluation-running.md +198 -0
- package/assets/skills/converted/mcp-builder/composable/presets/node-server.md +1381 -0
- package/assets/skills/converted/mcp-builder/composable/presets/python-server.md +1129 -0
- package/assets/skills/converted/pdf/composable/BUNDLE.md +1284 -0
- package/assets/skills/converted/pdf/composable/INDEX.md +14 -0
- package/assets/skills/converted/pdf/composable/VERSION +1 -0
- package/assets/skills/converted/pdf/composable/mini/00-core.md +46 -0
- package/assets/skills/converted/pdf/composable/mini/01-creation.md +176 -0
- package/assets/skills/converted/pdf/composable/mini/02-extraction.md +178 -0
- package/assets/skills/converted/pdf/composable/mini/03-merge-split-manipulate.md +240 -0
- package/assets/skills/converted/pdf/composable/mini/04-images-ocr.md +168 -0
- package/assets/skills/converted/pdf/composable/mini/05-encryption.md +51 -0
- package/assets/skills/converted/pdf/composable/mini/06-forms.md +300 -0
- package/assets/skills/converted/pdf/composable/mini/07-libraries-reference.md +89 -0
- package/assets/skills/converted/pptx/composable/BUNDLE.md +911 -0
- package/assets/skills/converted/pptx/composable/INDEX.md +12 -0
- package/assets/skills/converted/pptx/composable/VERSION +1 -0
- package/assets/skills/converted/pptx/composable/mini/00-core.md +37 -0
- package/assets/skills/converted/pptx/composable/mini/01-reading.md +37 -0
- package/assets/skills/converted/pptx/composable/mini/02-creating-from-scratch.md +422 -0
- package/assets/skills/converted/pptx/composable/mini/03-editing-templates.md +207 -0
- package/assets/skills/converted/pptx/composable/mini/04-design.md +89 -0
- package/assets/skills/converted/pptx/composable/mini/05-qa-and-rendering.md +91 -0
- package/assets/skills/converted/pptx/composable/presets/creating.md +659 -0
- package/assets/skills/converted/pptx/composable/presets/editing.md +485 -0
- package/assets/skills/converted/skill-creator/composable/BUNDLE.md +1681 -0
- package/assets/skills/converted/skill-creator/composable/INDEX.md +21 -0
- package/assets/skills/converted/skill-creator/composable/VERSION +1 -0
- package/assets/skills/converted/skill-creator/composable/mini/00-core.md +59 -0
- package/assets/skills/converted/skill-creator/composable/mini/01-creating-a-skill.md +27 -0
- package/assets/skills/converted/skill-creator/composable/mini/02-skill-writing-guide.md +71 -0
- package/assets/skills/converted/skill-creator/composable/mini/03-test-cases.md +21 -0
- package/assets/skills/converted/skill-creator/composable/mini/04-running-and-evaluating.md +128 -0
- package/assets/skills/converted/skill-creator/composable/mini/05-improving-the-skill.md +30 -0
- package/assets/skills/converted/skill-creator/composable/mini/06-blind-comparison.md +5 -0
- package/assets/skills/converted/skill-creator/composable/mini/07-description-optimization.md +74 -0
- package/assets/skills/converted/skill-creator/composable/mini/08-packaging.md +11 -0
- package/assets/skills/converted/skill-creator/composable/mini/09-environment-specific.md +38 -0
- package/assets/skills/converted/skill-creator/composable/mini/10-scripts-and-references.md +34 -0
- package/assets/skills/converted/skill-creator/composable/mini/11-json-schemas.md +414 -0
- package/assets/skills/converted/skill-creator/composable/mini/12-agent-grader.md +225 -0
- package/assets/skills/converted/skill-creator/composable/mini/13-agent-comparator.md +204 -0
- package/assets/skills/converted/skill-creator/composable/mini/14-agent-analyzer.md +276 -0
- package/assets/skills/converted/skill-creator/composable/presets/authoring.md +198 -0
- package/assets/skills/converted/skill-creator/composable/presets/evaluating.md +1334 -0
- package/assets/skills/converted/skill-creator/composable/presets/optimizing.md +160 -0
- package/assets/skills/converted/skill-creator/composable/presets.json +5 -0
- package/assets/skills/meta/ccs-skill-creator/composable/BUNDLE.md +465 -0
- package/assets/skills/meta/ccs-skill-creator/composable/INDEX.md +8 -0
- package/assets/skills/meta/ccs-skill-creator/composable/VERSION +1 -0
- package/assets/skills/meta/ccs-skill-creator/composable/mini/00-core.md +46 -0
- package/assets/skills/meta/ccs-skill-creator/composable/mini/01-create-new-skill.md +167 -0
- package/assets/skills/meta/ccs-skill-creator/composable/mini/02-convert-existing-skill.md +120 -0
- package/assets/skills/meta/ccs-skill-creator/composable/mini/03-review-and-maintain.md +112 -0
- package/assets/skills/sources/anthropic/PROVENANCE.md +3 -0
- package/assets/skills/sources/anthropic/claude-api/LICENSE.txt +202 -0
- package/assets/skills/sources/anthropic/docx/LICENSE.txt +30 -0
- package/assets/skills/sources/anthropic/internal-comms/LICENSE.txt +202 -0
- package/assets/skills/sources/anthropic/mcp-builder/LICENSE.txt +202 -0
- package/assets/skills/sources/anthropic/pdf/LICENSE.txt +30 -0
- package/assets/skills/sources/anthropic/pptx/LICENSE.txt +30 -0
- package/assets/skills/sources/anthropic/skill-creator/LICENSE.txt +202 -0
- package/assets/skills/sources/financial-analyst/PROVENANCE.md +1 -0
- package/assets/tools/hive.py +728 -0
- package/dist/cli.js +7494 -0
- package/package.json +40 -0
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
# Node/TypeScript — Implementation Patterns
|
|
2
|
+
|
|
3
|
+
Node/TypeScript-specific best practices and code for implementing MCP servers with the MCP TypeScript SDK: project structure, server setup, tool registration, input validation with Zod, error handling, and a complete working example. Server naming (`{service}-mcp-server`) and tool naming are in the naming mini; this mini is the how-in-TypeScript.
|
|
4
|
+
|
|
5
|
+
## MCP TypeScript SDK
|
|
6
|
+
|
|
7
|
+
The official MCP TypeScript SDK provides:
|
|
8
|
+
- `McpServer` class for server initialization
|
|
9
|
+
- `registerTool` method for tool registration
|
|
10
|
+
- Zod schema integration for runtime input validation
|
|
11
|
+
- Type-safe tool handler implementations
|
|
12
|
+
|
|
13
|
+
**IMPORTANT — Use Modern APIs Only:**
|
|
14
|
+
- **DO use:** `server.registerTool()`, `server.registerResource()`, `server.registerPrompt()`
|
|
15
|
+
- **DO NOT use:** old deprecated APIs such as `server.tool()`, `server.setRequestHandler(ListToolsRequestSchema, ...)`, or manual handler registration.
|
|
16
|
+
- The `register*` methods provide better type safety, automatic schema handling, and are the recommended approach.
|
|
17
|
+
|
|
18
|
+
For complete SDK details use WebFetch: `https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md`
|
|
19
|
+
|
|
20
|
+
## Quick Reference
|
|
21
|
+
|
|
22
|
+
### Key imports
|
|
23
|
+
```typescript
|
|
24
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
26
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
27
|
+
import express from "express";
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Server initialization
|
|
32
|
+
```typescript
|
|
33
|
+
const server = new McpServer({
|
|
34
|
+
name: "service-mcp-server",
|
|
35
|
+
version: "1.0.0"
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Tool registration pattern
|
|
40
|
+
```typescript
|
|
41
|
+
server.registerTool(
|
|
42
|
+
"tool_name",
|
|
43
|
+
{
|
|
44
|
+
title: "Tool Display Name",
|
|
45
|
+
description: "What the tool does",
|
|
46
|
+
inputSchema: { param: z.string() },
|
|
47
|
+
outputSchema: { result: z.string() }
|
|
48
|
+
},
|
|
49
|
+
async ({ param }) => {
|
|
50
|
+
const output = { result: `Processed: ${param}` };
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text", text: JSON.stringify(output) }],
|
|
53
|
+
structuredContent: output // Modern pattern for structured data
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Project Structure
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
{service}-mcp-server/
|
|
63
|
+
├── package.json
|
|
64
|
+
├── tsconfig.json
|
|
65
|
+
├── README.md
|
|
66
|
+
├── src/
|
|
67
|
+
│ ├── index.ts # Main entry point with McpServer initialization
|
|
68
|
+
│ ├── types.ts # TypeScript type definitions and interfaces
|
|
69
|
+
│ ├── tools/ # Tool implementations (one file per domain)
|
|
70
|
+
│ ├── services/ # API clients and shared utilities
|
|
71
|
+
│ ├── schemas/ # Zod validation schemas
|
|
72
|
+
│ └── constants.ts # Shared constants (API_URL, CHARACTER_LIMIT, etc.)
|
|
73
|
+
└── dist/ # Built JavaScript files (entry point: dist/index.js)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Tool Structure
|
|
77
|
+
|
|
78
|
+
Use snake_case, action-oriented tool names with a service prefix (e.g., `slack_send_message`, `github_create_issue`, `asana_list_tasks`). Tools are registered using `registerTool`:
|
|
79
|
+
- Use Zod schemas for runtime input validation and type safety.
|
|
80
|
+
- The `description` field must be explicitly provided — JSDoc comments are NOT automatically extracted.
|
|
81
|
+
- Explicitly provide `title`, `description`, `inputSchema`, and `annotations`.
|
|
82
|
+
- The `inputSchema` must be a Zod schema object (not a JSON schema).
|
|
83
|
+
- Type all parameters and return values explicitly.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
87
|
+
import { z } from "zod";
|
|
88
|
+
|
|
89
|
+
const server = new McpServer({
|
|
90
|
+
name: "example-mcp",
|
|
91
|
+
version: "1.0.0"
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Zod schema for input validation
|
|
95
|
+
const UserSearchInputSchema = z.object({
|
|
96
|
+
query: z.string()
|
|
97
|
+
.min(2, "Query must be at least 2 characters")
|
|
98
|
+
.max(200, "Query must not exceed 200 characters")
|
|
99
|
+
.describe("Search string to match against names/emails"),
|
|
100
|
+
limit: z.number()
|
|
101
|
+
.int()
|
|
102
|
+
.min(1)
|
|
103
|
+
.max(100)
|
|
104
|
+
.default(20)
|
|
105
|
+
.describe("Maximum results to return"),
|
|
106
|
+
offset: z.number()
|
|
107
|
+
.int()
|
|
108
|
+
.min(0)
|
|
109
|
+
.default(0)
|
|
110
|
+
.describe("Number of results to skip for pagination"),
|
|
111
|
+
response_format: z.nativeEnum(ResponseFormat)
|
|
112
|
+
.default(ResponseFormat.MARKDOWN)
|
|
113
|
+
.describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
|
|
114
|
+
}).strict();
|
|
115
|
+
|
|
116
|
+
// Type definition from Zod schema
|
|
117
|
+
type UserSearchInput = z.infer<typeof UserSearchInputSchema>;
|
|
118
|
+
|
|
119
|
+
server.registerTool(
|
|
120
|
+
"example_search_users",
|
|
121
|
+
{
|
|
122
|
+
title: "Search Example Users",
|
|
123
|
+
description: `Search for users in the Example system by name, email, or team.
|
|
124
|
+
|
|
125
|
+
This tool searches across all user profiles in the Example platform, supporting partial matches and various search filters. It does NOT create or modify users, only searches existing ones.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
- query (string): Search string to match against names/emails
|
|
129
|
+
- limit (number): Maximum results to return, between 1-100 (default: 20)
|
|
130
|
+
- offset (number): Number of results to skip for pagination (default: 0)
|
|
131
|
+
- response_format ('markdown' | 'json'): Output format (default: 'markdown')
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
For JSON format: Structured data with schema:
|
|
135
|
+
{
|
|
136
|
+
"total": number, // Total number of matches found
|
|
137
|
+
"count": number, // Number of results in this response
|
|
138
|
+
"offset": number, // Current pagination offset
|
|
139
|
+
"users": [
|
|
140
|
+
{
|
|
141
|
+
"id": string, // User ID (e.g., "U123456789")
|
|
142
|
+
"name": string, // Full name (e.g., "John Doe")
|
|
143
|
+
"email": string, // Email address
|
|
144
|
+
"team": string, // Team name (optional)
|
|
145
|
+
"active": boolean // Whether user is active
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"has_more": boolean, // Whether more results are available
|
|
149
|
+
"next_offset": number // Offset for next page (if has_more is true)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Examples:
|
|
153
|
+
- Use when: "Find all marketing team members" -> params with query="team:marketing"
|
|
154
|
+
- Use when: "Search for John's account" -> params with query="john"
|
|
155
|
+
- Don't use when: You need to create a user (use example_create_user instead)
|
|
156
|
+
|
|
157
|
+
Error Handling:
|
|
158
|
+
- Returns "Error: Rate limit exceeded" if too many requests (429 status)
|
|
159
|
+
- Returns "No users found matching '<query>'" if search returns empty`,
|
|
160
|
+
inputSchema: UserSearchInputSchema,
|
|
161
|
+
annotations: {
|
|
162
|
+
readOnlyHint: true,
|
|
163
|
+
destructiveHint: false,
|
|
164
|
+
idempotentHint: true,
|
|
165
|
+
openWorldHint: true
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
async (params: UserSearchInput) => {
|
|
169
|
+
try {
|
|
170
|
+
// Input validation is handled by Zod schema
|
|
171
|
+
// Make API request using validated parameters
|
|
172
|
+
const data = await makeApiRequest<any>(
|
|
173
|
+
"users/search",
|
|
174
|
+
"GET",
|
|
175
|
+
undefined,
|
|
176
|
+
{
|
|
177
|
+
q: params.query,
|
|
178
|
+
limit: params.limit,
|
|
179
|
+
offset: params.offset
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const users = data.users || [];
|
|
184
|
+
const total = data.total || 0;
|
|
185
|
+
|
|
186
|
+
if (!users.length) {
|
|
187
|
+
return {
|
|
188
|
+
content: [{
|
|
189
|
+
type: "text",
|
|
190
|
+
text: `No users found matching '${params.query}'`
|
|
191
|
+
}]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Prepare structured output
|
|
196
|
+
const output = {
|
|
197
|
+
total,
|
|
198
|
+
count: users.length,
|
|
199
|
+
offset: params.offset,
|
|
200
|
+
users: users.map((user: any) => ({
|
|
201
|
+
id: user.id,
|
|
202
|
+
name: user.name,
|
|
203
|
+
email: user.email,
|
|
204
|
+
...(user.team ? { team: user.team } : {}),
|
|
205
|
+
active: user.active ?? true
|
|
206
|
+
})),
|
|
207
|
+
has_more: total > params.offset + users.length,
|
|
208
|
+
...(total > params.offset + users.length ? {
|
|
209
|
+
next_offset: params.offset + users.length
|
|
210
|
+
} : {})
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Format text representation based on requested format
|
|
214
|
+
let textContent: string;
|
|
215
|
+
if (params.response_format === ResponseFormat.MARKDOWN) {
|
|
216
|
+
const lines = [`# User Search Results: '${params.query}'`, "",
|
|
217
|
+
`Found ${total} users (showing ${users.length})`, ""];
|
|
218
|
+
for (const user of users) {
|
|
219
|
+
lines.push(`## ${user.name} (${user.id})`);
|
|
220
|
+
lines.push(`- **Email**: ${user.email}`);
|
|
221
|
+
if (user.team) lines.push(`- **Team**: ${user.team}`);
|
|
222
|
+
lines.push("");
|
|
223
|
+
}
|
|
224
|
+
textContent = lines.join("\n");
|
|
225
|
+
} else {
|
|
226
|
+
textContent = JSON.stringify(output, null, 2);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
content: [{ type: "text", text: textContent }],
|
|
231
|
+
structuredContent: output // Modern pattern for structured data
|
|
232
|
+
};
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return {
|
|
235
|
+
content: [{
|
|
236
|
+
type: "text",
|
|
237
|
+
text: handleApiError(error)
|
|
238
|
+
}]
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Zod Schemas for Input Validation
|
|
246
|
+
|
|
247
|
+
Zod provides runtime type validation:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { z } from "zod";
|
|
251
|
+
|
|
252
|
+
// Basic schema with validation
|
|
253
|
+
const CreateUserSchema = z.object({
|
|
254
|
+
name: z.string()
|
|
255
|
+
.min(1, "Name is required")
|
|
256
|
+
.max(100, "Name must not exceed 100 characters"),
|
|
257
|
+
email: z.string()
|
|
258
|
+
.email("Invalid email format"),
|
|
259
|
+
age: z.number()
|
|
260
|
+
.int("Age must be a whole number")
|
|
261
|
+
.min(0, "Age cannot be negative")
|
|
262
|
+
.max(150, "Age cannot be greater than 150")
|
|
263
|
+
}).strict(); // Use .strict() to forbid extra fields
|
|
264
|
+
|
|
265
|
+
// Enums
|
|
266
|
+
enum ResponseFormat {
|
|
267
|
+
MARKDOWN = "markdown",
|
|
268
|
+
JSON = "json"
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const SearchSchema = z.object({
|
|
272
|
+
response_format: z.nativeEnum(ResponseFormat)
|
|
273
|
+
.default(ResponseFormat.MARKDOWN)
|
|
274
|
+
.describe("Output format")
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Optional fields with defaults
|
|
278
|
+
const PaginationSchema = z.object({
|
|
279
|
+
limit: z.number()
|
|
280
|
+
.int()
|
|
281
|
+
.min(1)
|
|
282
|
+
.max(100)
|
|
283
|
+
.default(20)
|
|
284
|
+
.describe("Maximum results to return"),
|
|
285
|
+
offset: z.number()
|
|
286
|
+
.int()
|
|
287
|
+
.min(0)
|
|
288
|
+
.default(0)
|
|
289
|
+
.describe("Number of results to skip")
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Response Format Options
|
|
294
|
+
|
|
295
|
+
Support multiple output formats for flexibility (concept in the response-formats mini):
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
enum ResponseFormat {
|
|
299
|
+
MARKDOWN = "markdown",
|
|
300
|
+
JSON = "json"
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const inputSchema = z.object({
|
|
304
|
+
query: z.string(),
|
|
305
|
+
response_format: z.nativeEnum(ResponseFormat)
|
|
306
|
+
.default(ResponseFormat.MARKDOWN)
|
|
307
|
+
.describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Markdown format:** use headers, lists, and formatting for clarity; convert timestamps to human-readable format; show display names with IDs in parentheses; omit verbose metadata; group related information logically.
|
|
312
|
+
|
|
313
|
+
**JSON format:** return complete, structured data suitable for programmatic processing; include all available fields and metadata; use consistent field names and types.
|
|
314
|
+
|
|
315
|
+
## Pagination Implementation
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const ListSchema = z.object({
|
|
319
|
+
limit: z.number().int().min(1).max(100).default(20),
|
|
320
|
+
offset: z.number().int().min(0).default(0)
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
async function listItems(params: z.infer<typeof ListSchema>) {
|
|
324
|
+
const data = await apiRequest(params.limit, params.offset);
|
|
325
|
+
|
|
326
|
+
const response = {
|
|
327
|
+
total: data.total,
|
|
328
|
+
count: data.items.length,
|
|
329
|
+
offset: params.offset,
|
|
330
|
+
items: data.items,
|
|
331
|
+
has_more: data.total > params.offset + data.items.length,
|
|
332
|
+
next_offset: data.total > params.offset + data.items.length
|
|
333
|
+
? params.offset + data.items.length
|
|
334
|
+
: undefined
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
return JSON.stringify(response, null, 2);
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Character Limits and Truncation
|
|
342
|
+
|
|
343
|
+
Add a CHARACTER_LIMIT constant to prevent overwhelming responses:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// At module level in constants.ts
|
|
347
|
+
export const CHARACTER_LIMIT = 25000; // Maximum response size in characters
|
|
348
|
+
|
|
349
|
+
async function searchTool(params: SearchInput) {
|
|
350
|
+
let result = generateResponse(data);
|
|
351
|
+
|
|
352
|
+
// Check character limit and truncate if needed
|
|
353
|
+
if (result.length > CHARACTER_LIMIT) {
|
|
354
|
+
const truncatedData = data.slice(0, Math.max(1, data.length / 2));
|
|
355
|
+
response.data = truncatedData;
|
|
356
|
+
response.truncated = true;
|
|
357
|
+
response.truncation_message =
|
|
358
|
+
`Response truncated from ${data.length} to ${truncatedData.length} items. ` +
|
|
359
|
+
`Use 'offset' parameter or add filters to see more results.`;
|
|
360
|
+
result = JSON.stringify(response, null, 2);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Error Handling
|
|
368
|
+
|
|
369
|
+
Provide clear, actionable error messages:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import axios, { AxiosError } from "axios";
|
|
373
|
+
|
|
374
|
+
function handleApiError(error: unknown): string {
|
|
375
|
+
if (error instanceof AxiosError) {
|
|
376
|
+
if (error.response) {
|
|
377
|
+
switch (error.response.status) {
|
|
378
|
+
case 404:
|
|
379
|
+
return "Error: Resource not found. Please check the ID is correct.";
|
|
380
|
+
case 403:
|
|
381
|
+
return "Error: Permission denied. You don't have access to this resource.";
|
|
382
|
+
case 429:
|
|
383
|
+
return "Error: Rate limit exceeded. Please wait before making more requests.";
|
|
384
|
+
default:
|
|
385
|
+
return `Error: API request failed with status ${error.response.status}`;
|
|
386
|
+
}
|
|
387
|
+
} else if (error.code === "ECONNABORTED") {
|
|
388
|
+
return "Error: Request timed out. Please try again.";
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return `Error: Unexpected error occurred: ${error instanceof Error ? error.message : String(error)}`;
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Shared Utilities
|
|
396
|
+
|
|
397
|
+
Extract common functionality into reusable functions:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
// Shared API request function
|
|
401
|
+
async function makeApiRequest<T>(
|
|
402
|
+
endpoint: string,
|
|
403
|
+
method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
|
|
404
|
+
data?: any,
|
|
405
|
+
params?: any
|
|
406
|
+
): Promise<T> {
|
|
407
|
+
try {
|
|
408
|
+
const response = await axios({
|
|
409
|
+
method,
|
|
410
|
+
url: `${API_BASE_URL}/${endpoint}`,
|
|
411
|
+
data,
|
|
412
|
+
params,
|
|
413
|
+
timeout: 30000,
|
|
414
|
+
headers: {
|
|
415
|
+
"Content-Type": "application/json",
|
|
416
|
+
"Accept": "application/json"
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
return response.data;
|
|
420
|
+
} catch (error) {
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Async/Await Best Practices
|
|
427
|
+
|
|
428
|
+
Always use async/await for network requests and I/O operations:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
// Good: Async network request
|
|
432
|
+
async function fetchData(resourceId: string): Promise<ResourceData> {
|
|
433
|
+
const response = await axios.get(`${API_URL}/resource/${resourceId}`);
|
|
434
|
+
return response.data;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Bad: Promise chains
|
|
438
|
+
function fetchData(resourceId: string): Promise<ResourceData> {
|
|
439
|
+
return axios.get(`${API_URL}/resource/${resourceId}`)
|
|
440
|
+
.then(response => response.data); // Harder to read and maintain
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## TypeScript Best Practices
|
|
445
|
+
|
|
446
|
+
1. **Use Strict TypeScript:** enable strict mode in tsconfig.json.
|
|
447
|
+
2. **Define Interfaces:** create clear interface definitions for all data structures.
|
|
448
|
+
3. **Avoid `any`:** use proper types or `unknown` instead of `any`.
|
|
449
|
+
4. **Zod for Runtime Validation:** use Zod schemas to validate external data.
|
|
450
|
+
5. **Type Guards:** create type guard functions for complex type checking.
|
|
451
|
+
6. **Error Handling:** always use try-catch with proper error type checking.
|
|
452
|
+
7. **Null Safety:** use optional chaining (`?.`) and nullish coalescing (`??`).
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
// Good: Type-safe with Zod and interfaces
|
|
456
|
+
interface UserResponse {
|
|
457
|
+
id: string;
|
|
458
|
+
name: string;
|
|
459
|
+
email: string;
|
|
460
|
+
team?: string;
|
|
461
|
+
active: boolean;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const UserSchema = z.object({
|
|
465
|
+
id: z.string(),
|
|
466
|
+
name: z.string(),
|
|
467
|
+
email: z.string().email(),
|
|
468
|
+
team: z.string().optional(),
|
|
469
|
+
active: z.boolean()
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
type User = z.infer<typeof UserSchema>;
|
|
473
|
+
|
|
474
|
+
async function getUser(id: string): Promise<User> {
|
|
475
|
+
const data = await apiCall(`/users/${id}`);
|
|
476
|
+
return UserSchema.parse(data); // Runtime validation
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Bad: Using any
|
|
480
|
+
async function getUser(id: string): Promise<any> {
|
|
481
|
+
return await apiCall(`/users/${id}`); // No type safety
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Package Configuration
|
|
486
|
+
|
|
487
|
+
### package.json
|
|
488
|
+
|
|
489
|
+
```json
|
|
490
|
+
{
|
|
491
|
+
"name": "{service}-mcp-server",
|
|
492
|
+
"version": "1.0.0",
|
|
493
|
+
"description": "MCP server for {Service} API integration",
|
|
494
|
+
"type": "module",
|
|
495
|
+
"main": "dist/index.js",
|
|
496
|
+
"scripts": {
|
|
497
|
+
"start": "node dist/index.js",
|
|
498
|
+
"dev": "tsx watch src/index.ts",
|
|
499
|
+
"build": "tsc",
|
|
500
|
+
"clean": "rm -rf dist"
|
|
501
|
+
},
|
|
502
|
+
"engines": {
|
|
503
|
+
"node": ">=18"
|
|
504
|
+
},
|
|
505
|
+
"dependencies": {
|
|
506
|
+
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
507
|
+
"axios": "^1.7.9",
|
|
508
|
+
"zod": "^3.23.8"
|
|
509
|
+
},
|
|
510
|
+
"devDependencies": {
|
|
511
|
+
"@types/node": "^22.10.0",
|
|
512
|
+
"tsx": "^4.19.2",
|
|
513
|
+
"typescript": "^5.7.2"
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### tsconfig.json
|
|
519
|
+
|
|
520
|
+
```json
|
|
521
|
+
{
|
|
522
|
+
"compilerOptions": {
|
|
523
|
+
"target": "ES2022",
|
|
524
|
+
"module": "Node16",
|
|
525
|
+
"moduleResolution": "Node16",
|
|
526
|
+
"lib": ["ES2022"],
|
|
527
|
+
"outDir": "./dist",
|
|
528
|
+
"rootDir": "./src",
|
|
529
|
+
"strict": true,
|
|
530
|
+
"esModuleInterop": true,
|
|
531
|
+
"skipLibCheck": true,
|
|
532
|
+
"forceConsistentCasingInFileNames": true,
|
|
533
|
+
"declaration": true,
|
|
534
|
+
"declarationMap": true,
|
|
535
|
+
"sourceMap": true,
|
|
536
|
+
"allowSyntheticDefaultImports": true
|
|
537
|
+
},
|
|
538
|
+
"include": ["src/**/*"],
|
|
539
|
+
"exclude": ["node_modules", "dist"]
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Complete Example
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
#!/usr/bin/env node
|
|
547
|
+
/**
|
|
548
|
+
* MCP Server for Example Service.
|
|
549
|
+
*
|
|
550
|
+
* This server provides tools to interact with Example API, including user search,
|
|
551
|
+
* project management, and data export capabilities.
|
|
552
|
+
*/
|
|
553
|
+
|
|
554
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
555
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
556
|
+
import { z } from "zod";
|
|
557
|
+
import axios, { AxiosError } from "axios";
|
|
558
|
+
|
|
559
|
+
// Constants
|
|
560
|
+
const API_BASE_URL = "https://api.example.com/v1";
|
|
561
|
+
const CHARACTER_LIMIT = 25000;
|
|
562
|
+
|
|
563
|
+
// Enums
|
|
564
|
+
enum ResponseFormat {
|
|
565
|
+
MARKDOWN = "markdown",
|
|
566
|
+
JSON = "json"
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Zod schemas
|
|
570
|
+
const UserSearchInputSchema = z.object({
|
|
571
|
+
query: z.string()
|
|
572
|
+
.min(2, "Query must be at least 2 characters")
|
|
573
|
+
.max(200, "Query must not exceed 200 characters")
|
|
574
|
+
.describe("Search string to match against names/emails"),
|
|
575
|
+
limit: z.number()
|
|
576
|
+
.int()
|
|
577
|
+
.min(1)
|
|
578
|
+
.max(100)
|
|
579
|
+
.default(20)
|
|
580
|
+
.describe("Maximum results to return"),
|
|
581
|
+
offset: z.number()
|
|
582
|
+
.int()
|
|
583
|
+
.min(0)
|
|
584
|
+
.default(0)
|
|
585
|
+
.describe("Number of results to skip for pagination"),
|
|
586
|
+
response_format: z.nativeEnum(ResponseFormat)
|
|
587
|
+
.default(ResponseFormat.MARKDOWN)
|
|
588
|
+
.describe("Output format: 'markdown' for human-readable or 'json' for machine-readable")
|
|
589
|
+
}).strict();
|
|
590
|
+
|
|
591
|
+
type UserSearchInput = z.infer<typeof UserSearchInputSchema>;
|
|
592
|
+
|
|
593
|
+
// Shared utility functions
|
|
594
|
+
async function makeApiRequest<T>(
|
|
595
|
+
endpoint: string,
|
|
596
|
+
method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
|
|
597
|
+
data?: any,
|
|
598
|
+
params?: any
|
|
599
|
+
): Promise<T> {
|
|
600
|
+
try {
|
|
601
|
+
const response = await axios({
|
|
602
|
+
method,
|
|
603
|
+
url: `${API_BASE_URL}/${endpoint}`,
|
|
604
|
+
data,
|
|
605
|
+
params,
|
|
606
|
+
timeout: 30000,
|
|
607
|
+
headers: {
|
|
608
|
+
"Content-Type": "application/json",
|
|
609
|
+
"Accept": "application/json"
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
return response.data;
|
|
613
|
+
} catch (error) {
|
|
614
|
+
throw error;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function handleApiError(error: unknown): string {
|
|
619
|
+
if (error instanceof AxiosError) {
|
|
620
|
+
if (error.response) {
|
|
621
|
+
switch (error.response.status) {
|
|
622
|
+
case 404:
|
|
623
|
+
return "Error: Resource not found. Please check the ID is correct.";
|
|
624
|
+
case 403:
|
|
625
|
+
return "Error: Permission denied. You don't have access to this resource.";
|
|
626
|
+
case 429:
|
|
627
|
+
return "Error: Rate limit exceeded. Please wait before making more requests.";
|
|
628
|
+
default:
|
|
629
|
+
return `Error: API request failed with status ${error.response.status}`;
|
|
630
|
+
}
|
|
631
|
+
} else if (error.code === "ECONNABORTED") {
|
|
632
|
+
return "Error: Request timed out. Please try again.";
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return `Error: Unexpected error occurred: ${error instanceof Error ? error.message : String(error)}`;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Create MCP server instance
|
|
639
|
+
const server = new McpServer({
|
|
640
|
+
name: "example-mcp",
|
|
641
|
+
version: "1.0.0"
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
// Register tools
|
|
645
|
+
server.registerTool(
|
|
646
|
+
"example_search_users",
|
|
647
|
+
{
|
|
648
|
+
title: "Search Example Users",
|
|
649
|
+
description: `[Full description as shown above]`,
|
|
650
|
+
inputSchema: UserSearchInputSchema,
|
|
651
|
+
annotations: {
|
|
652
|
+
readOnlyHint: true,
|
|
653
|
+
destructiveHint: false,
|
|
654
|
+
idempotentHint: true,
|
|
655
|
+
openWorldHint: true
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
async (params: UserSearchInput) => {
|
|
659
|
+
// Implementation as shown above
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
// Main function
|
|
664
|
+
// For stdio (local):
|
|
665
|
+
async function runStdio() {
|
|
666
|
+
if (!process.env.EXAMPLE_API_KEY) {
|
|
667
|
+
console.error("ERROR: EXAMPLE_API_KEY environment variable is required");
|
|
668
|
+
process.exit(1);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const transport = new StdioServerTransport();
|
|
672
|
+
await server.connect(transport);
|
|
673
|
+
console.error("MCP server running via stdio");
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// For streamable HTTP (remote):
|
|
677
|
+
async function runHTTP() {
|
|
678
|
+
if (!process.env.EXAMPLE_API_KEY) {
|
|
679
|
+
console.error("ERROR: EXAMPLE_API_KEY environment variable is required");
|
|
680
|
+
process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const app = express();
|
|
684
|
+
app.use(express.json());
|
|
685
|
+
|
|
686
|
+
app.post('/mcp', async (req, res) => {
|
|
687
|
+
const transport = new StreamableHTTPServerTransport({
|
|
688
|
+
sessionIdGenerator: undefined,
|
|
689
|
+
enableJsonResponse: true
|
|
690
|
+
});
|
|
691
|
+
res.on('close', () => transport.close());
|
|
692
|
+
await server.connect(transport);
|
|
693
|
+
await transport.handleRequest(req, res, req.body);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
const port = parseInt(process.env.PORT || '3000');
|
|
697
|
+
app.listen(port, () => {
|
|
698
|
+
console.error(`MCP server running on http://localhost:${port}/mcp`);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Choose transport based on environment
|
|
703
|
+
const transport = process.env.TRANSPORT || 'stdio';
|
|
704
|
+
if (transport === 'http') {
|
|
705
|
+
runHTTP().catch(error => {
|
|
706
|
+
console.error("Server error:", error);
|
|
707
|
+
process.exit(1);
|
|
708
|
+
});
|
|
709
|
+
} else {
|
|
710
|
+
runStdio().catch(error => {
|
|
711
|
+
console.error("Server error:", error);
|
|
712
|
+
process.exit(1);
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
Advanced MCP features (resources, transport, notifications), build/run commands, and the quality checklist are in `14-node-advanced-and-checklist.md`.
|