stoops 0.1.0 → 0.2.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 +208 -4
- package/dist/agent/index.d.ts +553 -0
- package/dist/agent/index.js +41 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/chunk-5ADJGMXQ.js +27 -0
- package/dist/chunk-5ADJGMXQ.js.map +1 -0
- package/dist/chunk-7PKT5MPI.js +115 -0
- package/dist/chunk-7PKT5MPI.js.map +1 -0
- package/dist/chunk-BLGV3QN4.js +692 -0
- package/dist/chunk-BLGV3QN4.js.map +1 -0
- package/dist/chunk-HQS7HBZR.js +25 -0
- package/dist/chunk-HQS7HBZR.js.map +1 -0
- package/dist/chunk-LC5WPWR2.js +690 -0
- package/dist/chunk-LC5WPWR2.js.map +1 -0
- package/dist/chunk-SS5NGUJM.js +968 -0
- package/dist/chunk-SS5NGUJM.js.map +1 -0
- package/dist/claude/index.d.ts +26 -0
- package/dist/claude/index.js +215 -0
- package/dist/claude/index.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +2468 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index-DlxJ95ki.d.ts +637 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +23 -2
- package/dist/index.js.map +1 -0
- package/dist/langgraph/index.d.ts +30 -0
- package/dist/langgraph/index.js +250 -0
- package/dist/langgraph/index.js.map +1 -0
- package/dist/sdk-YTUDDE6G.js +11945 -0
- package/dist/sdk-YTUDDE6G.js.map +1 -0
- package/dist/types-CzHDzfHA.d.ts +201 -0
- package/package.json +69 -11
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleCatchUp,
|
|
3
|
+
handleSearchByMessage,
|
|
4
|
+
handleSearchByText,
|
|
5
|
+
handleSendMessage
|
|
6
|
+
} from "./chunk-BLGV3QN4.js";
|
|
7
|
+
|
|
8
|
+
// src/agent/mcp/full.ts
|
|
9
|
+
import { createServer } from "http";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
function registerTools(server, resolver, options) {
|
|
12
|
+
server.tool(
|
|
13
|
+
"catch_up",
|
|
14
|
+
"Catch up on recent activity in a room. Returns unseen events.",
|
|
15
|
+
{ room: z.string().describe("Name of the room to catch up on") },
|
|
16
|
+
{ readOnlyHint: true },
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
async ({ room }) => handleCatchUp(resolver, { room }, options)
|
|
19
|
+
);
|
|
20
|
+
server.tool(
|
|
21
|
+
"search_by_text",
|
|
22
|
+
"Search chat history by keyword.",
|
|
23
|
+
{
|
|
24
|
+
room: z.string().describe("Name of the room to search"),
|
|
25
|
+
query: z.string().describe("Keyword or phrase to search for"),
|
|
26
|
+
count: z.number().int().min(1).max(10).default(3).optional().describe("Number of matches to return (default 3)"),
|
|
27
|
+
cursor: z.string().optional().describe("Pagination cursor from a previous search")
|
|
28
|
+
},
|
|
29
|
+
{ readOnlyHint: true },
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
async (args) => handleSearchByText(resolver, args, options)
|
|
32
|
+
);
|
|
33
|
+
server.tool(
|
|
34
|
+
"search_by_message",
|
|
35
|
+
"Show messages around a known message ref.",
|
|
36
|
+
{
|
|
37
|
+
room: z.string().describe("Name of the room"),
|
|
38
|
+
ref: z.string().describe("The #XXXX message ref (e.g. #3847)"),
|
|
39
|
+
direction: z.enum(["before", "after"]).default("before").optional().describe("'before' to scroll back (default), 'after' to scroll forward"),
|
|
40
|
+
count: z.number().int().min(1).max(50).default(10).optional().describe("Number of messages to return (not counting anchor, default 10)")
|
|
41
|
+
},
|
|
42
|
+
{ readOnlyHint: true },
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
async (args) => handleSearchByMessage(resolver, args, options)
|
|
45
|
+
);
|
|
46
|
+
server.tool(
|
|
47
|
+
"send_message",
|
|
48
|
+
"Send a message to a room.",
|
|
49
|
+
{
|
|
50
|
+
room: z.string().describe("Name of the room to send to"),
|
|
51
|
+
content: z.string().describe("Message content. @name will notify that participant \u2014 use sparingly."),
|
|
52
|
+
reply_to_id: z.string().optional().describe("Message ref to reply to (e.g. #3847)."),
|
|
53
|
+
image_url: z.string().url().optional().describe("URL of an image to attach"),
|
|
54
|
+
image_mime_type: z.string().optional().describe("MIME type of the image"),
|
|
55
|
+
image_size_bytes: z.number().int().positive().optional().describe("Size of the image in bytes")
|
|
56
|
+
},
|
|
57
|
+
{ readOnlyHint: false, destructiveHint: false },
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
async (args) => handleSendMessage(resolver, args, options)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
async function createFullMcpServer(resolver, options) {
|
|
63
|
+
const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
|
|
64
|
+
const { StreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
65
|
+
const instance = new McpServer({ name: "stoops", version: "1.0.0" });
|
|
66
|
+
registerTools(instance, resolver, options);
|
|
67
|
+
const httpServer = createServer(async (req, res) => {
|
|
68
|
+
if (req.url !== "/mcp") {
|
|
69
|
+
res.writeHead(404).end();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const reqServer = new McpServer({ name: "stoops", version: "1.0.0" });
|
|
73
|
+
registerTools(reqServer, resolver, options);
|
|
74
|
+
const transport = new StreamableHTTPServerTransport({
|
|
75
|
+
sessionIdGenerator: void 0
|
|
76
|
+
// stateless
|
|
77
|
+
});
|
|
78
|
+
await reqServer.connect(transport);
|
|
79
|
+
let body;
|
|
80
|
+
if (req.method === "POST") {
|
|
81
|
+
const chunks = [];
|
|
82
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
83
|
+
try {
|
|
84
|
+
body = JSON.parse(Buffer.concat(chunks).toString());
|
|
85
|
+
} catch {
|
|
86
|
+
body = void 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
await transport.handleRequest(req, res, body);
|
|
90
|
+
});
|
|
91
|
+
const port = await new Promise((resolve, reject) => {
|
|
92
|
+
httpServer.listen(0, "127.0.0.1", () => {
|
|
93
|
+
const addr = httpServer.address();
|
|
94
|
+
if (addr && typeof addr === "object") resolve(addr.port);
|
|
95
|
+
else reject(new Error("Could not determine server port"));
|
|
96
|
+
});
|
|
97
|
+
httpServer.once("error", reject);
|
|
98
|
+
});
|
|
99
|
+
const url = `http://127.0.0.1:${port}/mcp`;
|
|
100
|
+
let stopPromise = null;
|
|
101
|
+
const stop = () => {
|
|
102
|
+
if (!stopPromise) {
|
|
103
|
+
stopPromise = new Promise(
|
|
104
|
+
(resolve, reject) => httpServer.close((err) => err ? reject(err) : resolve())
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return stopPromise;
|
|
108
|
+
};
|
|
109
|
+
return { url, instance, stop };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
createFullMcpServer
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=chunk-7PKT5MPI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agent/mcp/full.ts"],"sourcesContent":["/**\n * Full MCP server — for embedded/API agents without filesystem access.\n *\n * Creates a proper MCP server using @modelcontextprotocol/sdk with\n * StreamableHTTP transport on a random localhost port.\n *\n * 4 tools: catch_up, search_by_text, search_by_message, send_message.\n *\n * Returns { url, instance, stop } where:\n * url — http://127.0.0.1:PORT/mcp (for any MCP-capable client)\n * instance — McpServer instance (for Claude SDK in-process shortcut)\n * stop — shuts down the HTTP listener\n */\n\nimport { createServer } from \"node:http\";\nimport { z } from \"zod\";\nimport type { RoomResolver, ToolHandlerOptions } from \"../types.js\";\nimport {\n handleCatchUp,\n handleSearchByText,\n handleSearchByMessage,\n handleSendMessage,\n} from \"../tool-handlers.js\";\n\nexport interface StoopsMcpServer {\n /** HTTP URL for URL-based MCP clients (e.g. LangGraph, external tools). */\n url: string;\n /**\n * Raw McpServer instance — passed to Claude SDK as\n * `{ type: 'sdk', name: 'stoops_tools', instance }` to avoid HTTP overhead.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n instance: any;\n /** Shut down the HTTP listener. */\n stop: () => Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction registerTools(server: any, resolver: RoomResolver, options: ToolHandlerOptions): void {\n server.tool(\n \"catch_up\",\n \"Catch up on recent activity in a room. Returns unseen events.\",\n { room: z.string().describe(\"Name of the room to catch up on\") },\n { readOnlyHint: true },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async ({ room }: { room: string }) => handleCatchUp(resolver, { room }, options) as any,\n );\n\n server.tool(\n \"search_by_text\",\n \"Search chat history by keyword.\",\n {\n room: z.string().describe(\"Name of the room to search\"),\n query: z.string().describe(\"Keyword or phrase to search for\"),\n count: z.number().int().min(1).max(10).default(3).optional()\n .describe(\"Number of matches to return (default 3)\"),\n cursor: z.string().optional().describe(\"Pagination cursor from a previous search\"),\n },\n { readOnlyHint: true },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async (args: any) => handleSearchByText(resolver, args, options) as any,\n );\n\n server.tool(\n \"search_by_message\",\n \"Show messages around a known message ref.\",\n {\n room: z.string().describe(\"Name of the room\"),\n ref: z.string().describe(\"The #XXXX message ref (e.g. #3847)\"),\n direction: z.enum([\"before\", \"after\"]).default(\"before\").optional()\n .describe(\"'before' to scroll back (default), 'after' to scroll forward\"),\n count: z.number().int().min(1).max(50).default(10).optional()\n .describe(\"Number of messages to return (not counting anchor, default 10)\"),\n },\n { readOnlyHint: true },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async (args: any) => handleSearchByMessage(resolver, args, options) as any,\n );\n\n server.tool(\n \"send_message\",\n \"Send a message to a room.\",\n {\n room: z.string().describe(\"Name of the room to send to\"),\n content: z.string().describe(\"Message content. @name will notify that participant — use sparingly.\"),\n reply_to_id: z.string().optional()\n .describe(\"Message ref to reply to (e.g. #3847).\"),\n image_url: z.string().url().optional().describe(\"URL of an image to attach\"),\n image_mime_type: z.string().optional().describe(\"MIME type of the image\"),\n image_size_bytes: z.number().int().positive().optional()\n .describe(\"Size of the image in bytes\"),\n },\n { readOnlyHint: false, destructiveHint: false },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async (args: any) => handleSendMessage(resolver, args, options) as any,\n );\n}\n\n/**\n * Start a full stoops MCP server (all 4 tools).\n * Call once per session start; call stop() on session stop.\n */\nexport async function createFullMcpServer(\n resolver: RoomResolver,\n options: ToolHandlerOptions,\n): Promise<StoopsMcpServer> {\n const { McpServer } = await import(\"@modelcontextprotocol/sdk/server/mcp.js\");\n const { StreamableHTTPServerTransport } = await import(\n \"@modelcontextprotocol/sdk/server/streamableHttp.js\"\n );\n\n // Singleton instance for Claude SDK in-process shortcut.\n const instance = new McpServer({ name: \"stoops\", version: \"1.0.0\" });\n registerTools(instance, resolver, options);\n\n // ── Start HTTP server on random port ─────────────────────────────────────\n\n const httpServer = createServer(async (req, res) => {\n if (req.url !== \"/mcp\") {\n res.writeHead(404).end();\n return;\n }\n\n // Fresh McpServer per request — McpServer only allows one active transport\n // at a time, so reusing the singleton across requests causes \"Already connected\"\n // errors. Tool registration is cheap; creating per-request is the correct pattern\n // for stateless HTTP MCP.\n const reqServer = new McpServer({ name: \"stoops\", version: \"1.0.0\" });\n registerTools(reqServer, resolver, options);\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined, // stateless\n });\n\n await reqServer.connect(transport);\n\n let body: unknown;\n if (req.method === \"POST\") {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n try { body = JSON.parse(Buffer.concat(chunks).toString()); } catch { body = undefined; }\n }\n\n await transport.handleRequest(req, res, body);\n });\n\n const port = await new Promise<number>((resolve, reject) => {\n httpServer.listen(0, \"127.0.0.1\", () => {\n const addr = httpServer.address();\n if (addr && typeof addr === \"object\") resolve(addr.port);\n else reject(new Error(\"Could not determine server port\"));\n });\n httpServer.once(\"error\", reject);\n });\n\n const url = `http://127.0.0.1:${port}/mcp`;\n\n let stopPromise: Promise<void> | null = null;\n const stop = () => {\n if (!stopPromise) {\n stopPromise = new Promise<void>((resolve, reject) =>\n httpServer.close((err) => (err ? reject(err) : resolve())),\n );\n }\n return stopPromise;\n };\n\n return { url, instance, stop };\n}\n"],"mappings":"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","names":[]}
|