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.
- package/README.md +87 -0
- package/dist/index.js +125 -0
- 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
|
+
}
|