@xyd-js/mcp-server 0.0.0-build-9f87f13-20250930210637
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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +89 -0
- package/bin/xyd-mcp-server.mjs +4 -0
- package/demo/client/client.ts +94 -0
- package/demo/simple/openapi.json +641 -0
- package/demo/simple/server.ts +217 -0
- package/dist/index.js +190770 -0
- package/package.json +35 -0
- package/src/index.ts +18 -0
- package/src/mcp.ts +220 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
|
|
4
|
+
type Role = "user" | "admin" | "moderator";
|
|
5
|
+
|
|
6
|
+
interface User {
|
|
7
|
+
id: string;
|
|
8
|
+
email: string;
|
|
9
|
+
firstName: string;
|
|
10
|
+
lastName: string;
|
|
11
|
+
role: Role;
|
|
12
|
+
isActive: boolean;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
address?: { street?: string };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const app = express();
|
|
19
|
+
app.use(express.json());
|
|
20
|
+
|
|
21
|
+
const startedAt = Date.now();
|
|
22
|
+
|
|
23
|
+
const users: User[] = [
|
|
24
|
+
{
|
|
25
|
+
id: randomUUID(),
|
|
26
|
+
email: "alice@example.com",
|
|
27
|
+
firstName: "Alice",
|
|
28
|
+
lastName: "Anderson",
|
|
29
|
+
role: "admin",
|
|
30
|
+
isActive: true,
|
|
31
|
+
createdAt: new Date().toISOString(),
|
|
32
|
+
updatedAt: new Date().toISOString(),
|
|
33
|
+
address: { street: "123 Main St" },
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: randomUUID(),
|
|
37
|
+
email: "bob@example.com",
|
|
38
|
+
firstName: "Bob",
|
|
39
|
+
lastName: "Brown",
|
|
40
|
+
role: "user",
|
|
41
|
+
isActive: true,
|
|
42
|
+
createdAt: new Date().toISOString(),
|
|
43
|
+
updatedAt: new Date().toISOString(),
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// Health
|
|
48
|
+
app.get("/v1/health", (_req, res) => {
|
|
49
|
+
res.json({
|
|
50
|
+
status: "healthy",
|
|
51
|
+
timestamp: new Date().toISOString(),
|
|
52
|
+
version: "1.0.0",
|
|
53
|
+
uptime: (Date.now() - startedAt) / 1000,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Auth
|
|
58
|
+
app.post("/v1/auth/login", (req, res) => {
|
|
59
|
+
const { email, password } = req.body || {};
|
|
60
|
+
if (typeof email !== "string" || typeof password !== "string") {
|
|
61
|
+
return res
|
|
62
|
+
.status(400)
|
|
63
|
+
.json({
|
|
64
|
+
error: "Invalid body",
|
|
65
|
+
code: "BAD_REQUEST",
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const user = users.find((u) => u.email === email);
|
|
70
|
+
if (!user) {
|
|
71
|
+
return res
|
|
72
|
+
.status(401)
|
|
73
|
+
.json({
|
|
74
|
+
error: "Invalid credentials",
|
|
75
|
+
code: "UNAUTHORIZED",
|
|
76
|
+
timestamp: new Date().toISOString(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
res.json({
|
|
80
|
+
accessToken: "access-" + randomUUID(),
|
|
81
|
+
refreshToken: "refresh-" + randomUUID(),
|
|
82
|
+
expiresIn: 3600,
|
|
83
|
+
user,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
app.post("/v1/auth/refresh", (req, res) => {
|
|
88
|
+
const { refreshToken } = req.body || {};
|
|
89
|
+
if (!refreshToken) {
|
|
90
|
+
return res
|
|
91
|
+
.status(401)
|
|
92
|
+
.json({
|
|
93
|
+
error: "Invalid refresh token",
|
|
94
|
+
code: "UNAUTHORIZED",
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
res.json({
|
|
99
|
+
accessToken: "access-" + randomUUID(),
|
|
100
|
+
refreshToken: "refresh-" + randomUUID(),
|
|
101
|
+
expiresIn: 3600,
|
|
102
|
+
user: users[0],
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Users list/create
|
|
107
|
+
app.get("/v1/users", (req, res) => {
|
|
108
|
+
const page = Math.max(1, parseInt(String(req.query.page ?? "1"), 10) || 1);
|
|
109
|
+
const limit = Math.min(
|
|
110
|
+
100,
|
|
111
|
+
Math.max(1, parseInt(String(req.query.limit ?? "20"), 10) || 20)
|
|
112
|
+
);
|
|
113
|
+
const search = String(req.query.search ?? "").toLowerCase();
|
|
114
|
+
const filtered = search
|
|
115
|
+
? users.filter((u) =>
|
|
116
|
+
[u.email, u.firstName, u.lastName].some((v) =>
|
|
117
|
+
(v ?? "").toLowerCase().includes(search)
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
: users;
|
|
121
|
+
|
|
122
|
+
const start = (page - 1) * limit;
|
|
123
|
+
const data = filtered.slice(start, start + limit);
|
|
124
|
+
const total = filtered.length;
|
|
125
|
+
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
126
|
+
|
|
127
|
+
res.json({ data, pagination: { page, limit, total, totalPages } });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
app.post("/v1/users", (req, res) => {
|
|
131
|
+
const { email, password, firstName, lastName, role } = req.body || {};
|
|
132
|
+
if (!email || !password || !firstName || !lastName) {
|
|
133
|
+
return res
|
|
134
|
+
.status(400)
|
|
135
|
+
.json({
|
|
136
|
+
error: "Missing required fields",
|
|
137
|
+
code: "BAD_REQUEST",
|
|
138
|
+
timestamp: new Date().toISOString(),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (users.some((u) => u.email === email)) {
|
|
142
|
+
return res
|
|
143
|
+
.status(409)
|
|
144
|
+
.json({
|
|
145
|
+
error: "User already exists",
|
|
146
|
+
code: "CONFLICT",
|
|
147
|
+
timestamp: new Date().toISOString(),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const now = new Date().toISOString();
|
|
151
|
+
const user: User = {
|
|
152
|
+
id: randomUUID(),
|
|
153
|
+
email,
|
|
154
|
+
firstName,
|
|
155
|
+
lastName,
|
|
156
|
+
role: (role as Role) ?? "user",
|
|
157
|
+
isActive: true,
|
|
158
|
+
createdAt: now,
|
|
159
|
+
updatedAt: now,
|
|
160
|
+
};
|
|
161
|
+
users.push(user);
|
|
162
|
+
res.status(201).json(user);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Users get/update/delete by id
|
|
166
|
+
app.get("/v1/users/:userId", (req, res) => {
|
|
167
|
+
const user = users.find((u) => u.id === req.params.userId);
|
|
168
|
+
if (!user)
|
|
169
|
+
return res
|
|
170
|
+
.status(404)
|
|
171
|
+
.json({
|
|
172
|
+
error: "User not found",
|
|
173
|
+
code: "NOT_FOUND",
|
|
174
|
+
timestamp: new Date().toISOString(),
|
|
175
|
+
});
|
|
176
|
+
res.json(user);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
app.put("/v1/users/:userId", (req, res) => {
|
|
180
|
+
const user = users.find((u) => u.id === req.params.userId);
|
|
181
|
+
if (!user)
|
|
182
|
+
return res
|
|
183
|
+
.status(404)
|
|
184
|
+
.json({
|
|
185
|
+
error: "User not found",
|
|
186
|
+
code: "NOT_FOUND",
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
188
|
+
});
|
|
189
|
+
const { firstName, lastName, role, isActive } = req.body || {};
|
|
190
|
+
if (typeof firstName === "string") user.firstName = firstName;
|
|
191
|
+
if (typeof lastName === "string") user.lastName = lastName;
|
|
192
|
+
if (role === "user" || role === "admin" || role === "moderator")
|
|
193
|
+
user.role = role;
|
|
194
|
+
if (typeof isActive === "boolean") user.isActive = isActive;
|
|
195
|
+
user.updatedAt = new Date().toISOString();
|
|
196
|
+
res.json(user);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
app.delete("/v1/users/:userId", (req, res) => {
|
|
200
|
+
const index = users.findIndex((u) => u.id === req.params.userId);
|
|
201
|
+
if (index === -1)
|
|
202
|
+
return res
|
|
203
|
+
.status(404)
|
|
204
|
+
.json({
|
|
205
|
+
error: "User not found",
|
|
206
|
+
code: "NOT_FOUND",
|
|
207
|
+
timestamp: new Date().toISOString(),
|
|
208
|
+
});
|
|
209
|
+
users.splice(index, 1);
|
|
210
|
+
res.status(204).send();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const port = Number(process.env.PORT || 5050);
|
|
214
|
+
app.listen(port, () => {
|
|
215
|
+
// eslint-disable-next-line no-console
|
|
216
|
+
console.log(`Demo API listening at http://localhost:${port}/v1`);
|
|
217
|
+
});
|