hiloop-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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,252 @@
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 BASE_URL = process.env.HILOOP_BASE_URL || "https://api.hi-loop.com";
6
+ const API_KEY = process.env.HILOOP_API_KEY || "";
7
+ async function apiFetch(path, options = {}) {
8
+ const url = `${BASE_URL}/v1${path}`;
9
+ const headers = {
10
+ "Content-Type": "application/json",
11
+ "X-API-Key": API_KEY,
12
+ ...options.headers,
13
+ };
14
+ const res = await fetch(url, { ...options, headers });
15
+ const body = await res.json();
16
+ if (!res.ok) {
17
+ throw new Error(`API error ${res.status}: ${body.error || JSON.stringify(body)}`);
18
+ }
19
+ return body;
20
+ }
21
+ const server = new McpServer({
22
+ name: "hiloop",
23
+ version: "0.1.0",
24
+ });
25
+ // -- Tools --------------------------------------------------------------------
26
+ server.tool("create_interaction", "Create a new human-in-the-loop interaction (approval, chat, review, form, notification)", {
27
+ type: z
28
+ .enum([
29
+ "approval",
30
+ "chat",
31
+ "review",
32
+ "form",
33
+ "notification",
34
+ "live_session",
35
+ "voice_call",
36
+ "payment",
37
+ "scheduled_call",
38
+ ])
39
+ .describe("The type of interaction"),
40
+ title: z.string().describe("Title of the interaction (will be sent as-is)"),
41
+ description: z
42
+ .string()
43
+ .optional()
44
+ .describe("Optional description with more context"),
45
+ priority: z
46
+ .enum(["critical", "high", "normal", "low"])
47
+ .default("normal")
48
+ .describe("Priority level"),
49
+ deadline_minutes: z
50
+ .number()
51
+ .optional()
52
+ .describe("Deadline in minutes from now"),
53
+ options: z
54
+ .string()
55
+ .optional()
56
+ .describe("JSON array of options for approval type, e.g. [\"approve\",\"reject\"]"),
57
+ }, async (params) => {
58
+ const body = {
59
+ type: params.type,
60
+ encryptedTitle: params.title,
61
+ priority: params.priority,
62
+ };
63
+ if (params.description)
64
+ body.encryptedDescription = params.description;
65
+ if (params.deadline_minutes)
66
+ body.deadlineMinutes = params.deadline_minutes;
67
+ if (params.options)
68
+ body.encryptedOptions = params.options;
69
+ const result = await apiFetch("/agent/interactions", {
70
+ method: "POST",
71
+ body: JSON.stringify(body),
72
+ });
73
+ return {
74
+ content: [
75
+ {
76
+ type: "text",
77
+ text: JSON.stringify(result, null, 2),
78
+ },
79
+ ],
80
+ };
81
+ });
82
+ server.tool("list_interactions", "List interactions created by this agent", {
83
+ status: z
84
+ .enum([
85
+ "created",
86
+ "assigned",
87
+ "viewed",
88
+ "responded",
89
+ "completed",
90
+ "cancelled",
91
+ "expired",
92
+ "escalated",
93
+ "snoozed",
94
+ ])
95
+ .optional()
96
+ .describe("Filter by status"),
97
+ type: z
98
+ .enum([
99
+ "approval",
100
+ "chat",
101
+ "review",
102
+ "form",
103
+ "notification",
104
+ "live_session",
105
+ "voice_call",
106
+ "payment",
107
+ "scheduled_call",
108
+ ])
109
+ .optional()
110
+ .describe("Filter by type"),
111
+ page: z.number().default(1).describe("Page number"),
112
+ }, async (params) => {
113
+ const query = new URLSearchParams();
114
+ if (params.status)
115
+ query.set("status", params.status);
116
+ if (params.type)
117
+ query.set("type", params.type);
118
+ query.set("page", String(params.page));
119
+ const result = await apiFetch(`/agent/interactions?${query.toString()}`);
120
+ return {
121
+ content: [
122
+ {
123
+ type: "text",
124
+ text: JSON.stringify(result, null, 2),
125
+ },
126
+ ],
127
+ };
128
+ });
129
+ server.tool("get_interaction", "Get details of a specific interaction", {
130
+ interaction_id: z.string().describe("The interaction ID"),
131
+ }, async (params) => {
132
+ const result = await apiFetch(`/agent/interactions/${params.interaction_id}`);
133
+ return {
134
+ content: [
135
+ {
136
+ type: "text",
137
+ text: JSON.stringify(result, null, 2),
138
+ },
139
+ ],
140
+ };
141
+ });
142
+ server.tool("await_response", "Wait for a human to respond to an interaction (long-polls up to 30s)", {
143
+ interaction_id: z.string().describe("The interaction ID"),
144
+ timeout: z
145
+ .number()
146
+ .default(30)
147
+ .describe("Timeout in seconds (max 30)"),
148
+ }, async (params) => {
149
+ const result = await apiFetch(`/agent/interactions/${params.interaction_id}/await?timeout=${params.timeout}`);
150
+ return {
151
+ content: [
152
+ {
153
+ type: "text",
154
+ text: JSON.stringify(result, null, 2),
155
+ },
156
+ ],
157
+ };
158
+ });
159
+ server.tool("cancel_interaction", "Cancel a pending interaction", {
160
+ interaction_id: z.string().describe("The interaction ID to cancel"),
161
+ }, async (params) => {
162
+ const result = await apiFetch(`/agent/interactions/${params.interaction_id}`, { method: "DELETE" });
163
+ return {
164
+ content: [
165
+ {
166
+ type: "text",
167
+ text: JSON.stringify(result, null, 2),
168
+ },
169
+ ],
170
+ };
171
+ });
172
+ server.tool("acknowledge_interaction", "Acknowledge (complete) an interaction after processing the response", {
173
+ interaction_id: z.string().describe("The interaction ID to acknowledge"),
174
+ }, async (params) => {
175
+ const result = await apiFetch(`/agent/interactions/${params.interaction_id}/acknowledge`, { method: "POST" });
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: JSON.stringify(result, null, 2),
181
+ },
182
+ ],
183
+ };
184
+ });
185
+ server.tool("send_message", "Send a chat message on an interaction", {
186
+ interaction_id: z.string().describe("The interaction ID"),
187
+ content: z.string().describe("Message content to send"),
188
+ }, async (params) => {
189
+ const result = await apiFetch(`/agent/interactions/${params.interaction_id}/messages`, {
190
+ method: "POST",
191
+ body: JSON.stringify({ encryptedContent: params.content }),
192
+ });
193
+ return {
194
+ content: [
195
+ {
196
+ type: "text",
197
+ text: JSON.stringify(result, null, 2),
198
+ },
199
+ ],
200
+ };
201
+ });
202
+ server.tool("list_messages", "List chat messages on an interaction", {
203
+ interaction_id: z.string().describe("The interaction ID"),
204
+ }, async (params) => {
205
+ const result = await apiFetch(`/agent/interactions/${params.interaction_id}/messages`);
206
+ return {
207
+ content: [
208
+ {
209
+ type: "text",
210
+ text: JSON.stringify(result, null, 2),
211
+ },
212
+ ],
213
+ };
214
+ });
215
+ server.tool("check_quota", "Check the current agent's usage quota", {}, async () => {
216
+ const result = await apiFetch("/agent/quota");
217
+ return {
218
+ content: [
219
+ {
220
+ type: "text",
221
+ text: JSON.stringify(result, null, 2),
222
+ },
223
+ ],
224
+ };
225
+ });
226
+ server.tool("get_transitions", "Get the status transition history of an interaction", {
227
+ interaction_id: z.string().describe("The interaction ID"),
228
+ }, async (params) => {
229
+ const result = await apiFetch(`/agent/interactions/${params.interaction_id}/transitions`);
230
+ return {
231
+ content: [
232
+ {
233
+ type: "text",
234
+ text: JSON.stringify(result, null, 2),
235
+ },
236
+ ],
237
+ };
238
+ });
239
+ // -- Start server -------------------------------------------------------------
240
+ async function main() {
241
+ if (!API_KEY) {
242
+ console.error("Error: HILOOP_API_KEY environment variable is required.\n" +
243
+ "Set it to your agent API key (starts with hlp_).");
244
+ process.exit(1);
245
+ }
246
+ const transport = new StdioServerTransport();
247
+ await server.connect(transport);
248
+ }
249
+ main().catch((err) => {
250
+ console.error("Fatal error:", err);
251
+ process.exit(1);
252
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "hiloop-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Hiloop MCP server - Human-in-the-loop for AI agents via Model Context Protocol",
5
+ "type": "module",
6
+ "bin": {
7
+ "hiloop-mcp": "dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "prepublishOnly": "tsc",
17
+ "start": "node dist/index.js",
18
+ "dev": "tsx src/index.ts"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/sjorsdev/hiloop.git",
23
+ "directory": "mcp"
24
+ },
25
+ "homepage": "https://www.hi-loop.com",
26
+ "bugs": {
27
+ "url": "https://github.com/sjorsdev/hiloop/issues"
28
+ },
29
+ "keywords": [
30
+ "mcp",
31
+ "model-context-protocol",
32
+ "hiloop",
33
+ "human-in-the-loop",
34
+ "ai-agents",
35
+ "approval",
36
+ "review",
37
+ "chat"
38
+ ],
39
+ "license": "MIT",
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.0.0",
45
+ "zod": "^3.24"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^25.3.0",
49
+ "tsx": "^4",
50
+ "typescript": "^5.7"
51
+ }
52
+ }