doc-sample-mcp 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.
Files changed (3) hide show
  1. package/README.md +87 -0
  2. package/dist/index.js +125 -0
  3. package/package.json +43 -0
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # DocSample MCP
2
+
3
+ MCP server for the DocSample public API. It exposes read-only tools for AI
4
+ clients to list and read documents through the dedicated `/api/mcp` endpoints.
5
+
6
+ Repository: <https://github.com/juan-malonso/doc-sample-mcp>
7
+
8
+ ## Tools
9
+
10
+ - `list_documents`: list document summaries by `country` and `type`.
11
+ - `get_document`: get one document and its base64 files by `country`, `type`, and `id`.
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 20 or newer.
16
+ - A DocSample API key with the permissions required by the tools you want to use:
17
+ `mcp:list` and/or `mcp:read`.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ git clone https://github.com/juan-malonso/doc-sample-mcp.git
23
+ cd doc-sample-mcp
24
+ npm install
25
+ npm run build
26
+ ```
27
+
28
+ ## MCP Client Configuration
29
+
30
+ Register the built server in your MCP client:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "doc-sample-api": {
36
+ "command": "node",
37
+ "args": ["/absolute/path/to/doc-sample-mcp/dist/index.js"],
38
+ "env": {
39
+ "DOC_SAMPLE_BASE_URL": "https://doc-sample.pages.dev",
40
+ "DOC_SAMPLE_API_KEY": "YOUR_API_KEY"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ For local development:
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "doc-sample-api-local": {
53
+ "command": "node",
54
+ "args": ["/absolute/path/to/doc-sample-mcp/dist/index.js"],
55
+ "env": {
56
+ "DOC_SAMPLE_BASE_URL": "http://localhost:3000",
57
+ "DOC_SAMPLE_API_KEY": "YOUR_API_KEY"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Publish To npm
65
+
66
+ The package is ready to publish if you want one-command installs:
67
+
68
+ ```bash
69
+ npm publish --access public
70
+ ```
71
+
72
+ After publishing, MCP clients can use `npx`:
73
+
74
+ ```json
75
+ {
76
+ "mcpServers": {
77
+ "doc-sample-api": {
78
+ "command": "npx",
79
+ "args": ["-y", "doc-sample-mcp"],
80
+ "env": {
81
+ "DOC_SAMPLE_BASE_URL": "https://doc-sample.pages.dev",
82
+ "DOC_SAMPLE_API_KEY": "YOUR_API_KEY"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ const DOCUMENT_TYPES = [
6
+ "PASSPORT",
7
+ "ID_CARD",
8
+ "DRIVER_LICENSE",
9
+ "RESIDENT",
10
+ ];
11
+ const env = readEnvironment();
12
+ const server = new McpServer({
13
+ name: "doc-sample-api",
14
+ version: "0.1.0",
15
+ });
16
+ server.registerTool("list_documents", {
17
+ title: "List documents",
18
+ description: "List document summaries by ISO alpha-2 country code and document type.",
19
+ inputSchema: {
20
+ country: z
21
+ .string()
22
+ .min(2)
23
+ .max(2)
24
+ .describe("ISO 3166-1 alpha-2 country code, for example ES or DE."),
25
+ type: z.enum(DOCUMENT_TYPES).describe("Document type."),
26
+ },
27
+ }, async ({ country, type }) => {
28
+ const payload = await requestJson(`/api/mcp/${encodeURIComponent(country.toUpperCase())}/${type}`);
29
+ return asJsonContent(payload);
30
+ });
31
+ server.registerTool("get_document", {
32
+ title: "Get document",
33
+ description: "Get one document and its files as base64 by country, type, and numeric id.",
34
+ inputSchema: {
35
+ country: z
36
+ .string()
37
+ .min(2)
38
+ .max(2)
39
+ .describe("ISO 3166-1 alpha-2 country code, for example ES or DE."),
40
+ type: z.enum(DOCUMENT_TYPES).describe("Document type."),
41
+ id: z.number().int().positive().describe("Document id."),
42
+ },
43
+ }, async ({ country, type, id }) => {
44
+ const payload = await requestJson(`/api/mcp/${encodeURIComponent(country.toUpperCase())}/${type}/${id}`);
45
+ return asJsonContent(payload);
46
+ });
47
+ server.registerResource("api-configuration", "doc-sample://configuration", {
48
+ title: "DocSample API configuration",
49
+ description: "Current DocSample API base URL used by this MCP server.",
50
+ mimeType: "application/json",
51
+ }, async (uri) => ({
52
+ contents: [
53
+ {
54
+ uri: uri.href,
55
+ mimeType: "application/json",
56
+ text: JSON.stringify({
57
+ baseUrl: env.baseUrl,
58
+ availableDocumentTypes: DOCUMENT_TYPES,
59
+ requiredPermissions: ["mcp:list", "mcp:read"],
60
+ }, null, 2),
61
+ },
62
+ ],
63
+ }));
64
+ const transport = new StdioServerTransport();
65
+ await server.connect(transport);
66
+ function readEnvironment() {
67
+ const baseUrl = process.env.DOC_SAMPLE_BASE_URL?.replace(/\/+$/, "");
68
+ const apiKey = process.env.DOC_SAMPLE_API_KEY;
69
+ if (!baseUrl) {
70
+ throw new Error("DOC_SAMPLE_BASE_URL is required");
71
+ }
72
+ if (!apiKey) {
73
+ throw new Error("DOC_SAMPLE_API_KEY is required");
74
+ }
75
+ return { baseUrl, apiKey };
76
+ }
77
+ async function requestJson(path, method = "GET") {
78
+ const response = await fetch(`${env.baseUrl}${path}`, {
79
+ method,
80
+ headers: {
81
+ "x-api-key": env.apiKey,
82
+ },
83
+ });
84
+ const text = await response.text();
85
+ const payload = parseJson(text);
86
+ if (!response.ok) {
87
+ throw new Error(`DocSample API returned ${response.status}: ${formatErrorPayload(payload, text)}`);
88
+ }
89
+ return payload;
90
+ }
91
+ function parseJson(text) {
92
+ if (text.length === 0)
93
+ return null;
94
+ try {
95
+ return JSON.parse(text);
96
+ }
97
+ catch {
98
+ return text;
99
+ }
100
+ }
101
+ function formatErrorPayload(payload, rawText) {
102
+ if (typeof payload === "object" &&
103
+ payload !== null &&
104
+ "message" in payload &&
105
+ typeof payload.message === "string") {
106
+ return payload.message;
107
+ }
108
+ if (typeof payload === "object" &&
109
+ payload !== null &&
110
+ "error" in payload &&
111
+ typeof payload.error === "string") {
112
+ return payload.error;
113
+ }
114
+ return rawText || "Request failed";
115
+ }
116
+ function asJsonContent(payload) {
117
+ return {
118
+ content: [
119
+ {
120
+ type: "text",
121
+ text: JSON.stringify(payload, null, 2),
122
+ },
123
+ ],
124
+ };
125
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "doc-sample-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for the DocSample public API.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "doc-sample-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json",
15
+ "prepublishOnly": "npm run type-check && npm run build",
16
+ "type-check": "tsc -p tsconfig.json --noEmit",
17
+ "start": "node dist/index.js"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/juan-malonso/doc-sample-mcp.git"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "model-context-protocol",
26
+ "doc-sample"
27
+ ],
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.17.5",
31
+ "zod": "^3.25.76"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.10.7",
35
+ "typescript": "^5.7.3"
36
+ },
37
+ "engines": {
38
+ "node": ">=20"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ }
43
+ }