rumi-mcp-server 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 (38) hide show
  1. package/README.md +78 -0
  2. package/dist/client.d.ts +18 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +69 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +44 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/prompts/rumi.d.ts +3 -0
  11. package/dist/prompts/rumi.d.ts.map +1 -0
  12. package/dist/prompts/rumi.js +118 -0
  13. package/dist/prompts/rumi.js.map +1 -0
  14. package/dist/tools/check-status.d.ts +4 -0
  15. package/dist/tools/check-status.d.ts.map +1 -0
  16. package/dist/tools/check-status.js +44 -0
  17. package/dist/tools/check-status.js.map +1 -0
  18. package/dist/tools/find-partner.d.ts +4 -0
  19. package/dist/tools/find-partner.d.ts.map +1 -0
  20. package/dist/tools/find-partner.js +60 -0
  21. package/dist/tools/find-partner.js.map +1 -0
  22. package/dist/tools/get-messages.d.ts +4 -0
  23. package/dist/tools/get-messages.d.ts.map +1 -0
  24. package/dist/tools/get-messages.js +53 -0
  25. package/dist/tools/get-messages.js.map +1 -0
  26. package/dist/tools/health-check.d.ts +4 -0
  27. package/dist/tools/health-check.d.ts.map +1 -0
  28. package/dist/tools/health-check.js +39 -0
  29. package/dist/tools/health-check.js.map +1 -0
  30. package/dist/tools/send-message.d.ts +4 -0
  31. package/dist/tools/send-message.d.ts.map +1 -0
  32. package/dist/tools/send-message.js +49 -0
  33. package/dist/tools/send-message.js.map +1 -0
  34. package/dist/types.d.ts +50 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +3 -0
  37. package/dist/types.js.map +1 -0
  38. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Rumi MCP Server
2
+
3
+ MCP (Model Context Protocol) server for [Rumi](https://rumi.app) — find real people to chat with through AI-powered topic matching.
4
+
5
+ Works with any MCP-compatible client: Claude Desktop, ChatGPT, Cursor, VS Code, and 300+ others.
6
+
7
+ ## Quick Start
8
+
9
+ ### 1. Install
10
+
11
+ ```bash
12
+ cd rumi-mcp-server
13
+ npm install
14
+ npm run build
15
+ ```
16
+
17
+ ### 2. Configure your MCP client
18
+
19
+ Add to your MCP client configuration (e.g. `claude_desktop_config.json`):
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "rumi": {
25
+ "command": "node",
26
+ "args": ["/path/to/rumi-mcp-server/dist/index.js"],
27
+ "env": {
28
+ "RUMI_API_TOKEN": "rumi_tk_..."
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ### 3. Get your API token
36
+
37
+ If you don't have a token yet, just start using the tools — they'll return a setup URL. Or visit:
38
+
39
+ ```
40
+ https://rumi.app/connect?partner=mcp
41
+ ```
42
+
43
+ Sign in with Google, and the page will generate your token.
44
+
45
+ ## Environment Variables
46
+
47
+ | Variable | Required | Description |
48
+ |----------|----------|-------------|
49
+ | `RUMI_API_TOKEN` | No* | Your Rumi API token (starts with `rumi_tk_`). Without it, all tools return `setup_required` with a setup URL. |
50
+ | `RUMI_BASE_URL` | No | API base URL (default: `https://rumi.app`) |
51
+
52
+ ## Tools
53
+
54
+ | Tool | Description |
55
+ |------|-------------|
56
+ | `rumi_health_check` | Check token validity, weekly quota, and active session |
57
+ | `rumi_find_partner` | Find a person to chat with (creates a matching session) |
58
+ | `rumi_check_status` | Poll the status of an active matching session |
59
+ | `rumi_send_message` | Send a message to your matched chat partner |
60
+ | `rumi_get_messages` | Get messages from a conversation (supports cursor pagination) |
61
+
62
+ ## Prompt
63
+
64
+ The server includes a `rumi` prompt with complete behavior guidelines — when to proactively suggest finding a chat partner, how to write good match descriptions, and session management rules.
65
+
66
+ ## Development
67
+
68
+ ```bash
69
+ npm run dev # Watch mode
70
+ npm run build # Build for production
71
+ npm start # Run the server
72
+ ```
73
+
74
+ ### Testing with MCP Inspector
75
+
76
+ ```bash
77
+ npx @modelcontextprotocol/inspector node dist/index.js
78
+ ```
@@ -0,0 +1,18 @@
1
+ import type { FindMatchResponse, SessionStatusResponse, MessagesResponse, SendMessageResponse, HealthResponse } from "./types.js";
2
+ /**
3
+ * HTTP client for Rumi Partner API.
4
+ * Standalone — no shared deps with openclaw-plugin.
5
+ */
6
+ export declare class RumiClient {
7
+ private baseUrl;
8
+ private apiToken;
9
+ constructor(apiToken: string, baseUrl?: string);
10
+ private request;
11
+ healthCheck(): Promise<HealthResponse>;
12
+ findMatch(description: string, locale?: string): Promise<FindMatchResponse>;
13
+ getSessionStatus(sessionId: string): Promise<SessionStatusResponse>;
14
+ sendMessage(conversationId: string, content: string): Promise<SendMessageResponse>;
15
+ getMessages(conversationId: string, after?: string, limit?: number): Promise<MessagesResponse>;
16
+ getConnectUrl(): string;
17
+ }
18
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAC;AAIpB;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;YAKhC,OAAO;IAiCf,WAAW,IAAI,OAAO,CAAC,cAAc,CAAC;IAItC,SAAS,CACb,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC;IAOvB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAMnE,WAAW,CACf,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC;IAUzB,WAAW,CACf,cAAc,EAAE,MAAM,EACtB,KAAK,CAAC,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,gBAAgB,CAAC;IAU5B,aAAa,IAAI,MAAM;CAGxB"}
package/dist/client.js ADDED
@@ -0,0 +1,69 @@
1
+ const REQUEST_TIMEOUT_MS = 60_000;
2
+ /**
3
+ * HTTP client for Rumi Partner API.
4
+ * Standalone — no shared deps with openclaw-plugin.
5
+ */
6
+ export class RumiClient {
7
+ baseUrl;
8
+ apiToken;
9
+ constructor(apiToken, baseUrl) {
10
+ this.baseUrl = (baseUrl || "https://rumi.app").replace(/\/$/, "");
11
+ this.apiToken = apiToken;
12
+ }
13
+ async request(path, options = {}) {
14
+ const url = `${this.baseUrl}${path}`;
15
+ const controller = new AbortController();
16
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
17
+ try {
18
+ const response = await fetch(url, {
19
+ ...options,
20
+ signal: controller.signal,
21
+ headers: {
22
+ "Content-Type": "application/json",
23
+ "X-API-Token": this.apiToken,
24
+ ...options.headers,
25
+ },
26
+ });
27
+ if (!response.ok) {
28
+ const body = await response.json().catch(() => ({}));
29
+ throw new Error(body.error ||
30
+ `Rumi API error: ${response.status} ${response.statusText}`);
31
+ }
32
+ return response.json();
33
+ }
34
+ finally {
35
+ clearTimeout(timeout);
36
+ }
37
+ }
38
+ async healthCheck() {
39
+ return this.request("/api/partner/health");
40
+ }
41
+ async findMatch(description, locale) {
42
+ return this.request("/api/partner/find-match", {
43
+ method: "POST",
44
+ body: JSON.stringify({ description, locale }),
45
+ });
46
+ }
47
+ async getSessionStatus(sessionId) {
48
+ return this.request(`/api/session?sessionId=${encodeURIComponent(sessionId)}`);
49
+ }
50
+ async sendMessage(conversationId, content) {
51
+ return this.request(`/api/conversations/${encodeURIComponent(conversationId)}/messages`, {
52
+ method: "POST",
53
+ body: JSON.stringify({ content }),
54
+ });
55
+ }
56
+ async getMessages(conversationId, after, limit) {
57
+ const params = new URLSearchParams();
58
+ if (after)
59
+ params.set("cursor", after);
60
+ if (limit)
61
+ params.set("limit", String(limit));
62
+ const query = params.toString() ? `?${params}` : "";
63
+ return this.request(`/api/conversations/${encodeURIComponent(conversationId)}/messages${query}`);
64
+ }
65
+ getConnectUrl() {
66
+ return `${this.baseUrl}/connect?partner=mcp`;
67
+ }
68
+ }
69
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,44 @@
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 { RumiClient } from "./client.js";
5
+ import { registerHealthCheck } from "./tools/health-check.js";
6
+ import { registerFindPartner } from "./tools/find-partner.js";
7
+ import { registerCheckStatus } from "./tools/check-status.js";
8
+ import { registerSendMessage } from "./tools/send-message.js";
9
+ import { registerGetMessages } from "./tools/get-messages.js";
10
+ import { registerRumiPrompt } from "./prompts/rumi.js";
11
+ const API_TOKEN = process.env.RUMI_API_TOKEN;
12
+ const BASE_URL = process.env.RUMI_BASE_URL;
13
+ // Null client mode: server starts without token, tools return setup_required
14
+ const client = API_TOKEN ? new RumiClient(API_TOKEN, BASE_URL) : null;
15
+ function getClient() {
16
+ return client;
17
+ }
18
+ function getConnectUrl() {
19
+ const base = (BASE_URL || "https://rumi.app").replace(/\/$/, "");
20
+ return `${base}/connect?partner=mcp`;
21
+ }
22
+ const server = new McpServer({
23
+ name: "rumi",
24
+ version: "0.1.0",
25
+ });
26
+ // Register tools
27
+ registerHealthCheck(server, getClient, getConnectUrl);
28
+ registerFindPartner(server, getClient, getConnectUrl);
29
+ registerCheckStatus(server, getClient, getConnectUrl);
30
+ registerSendMessage(server, getClient, getConnectUrl);
31
+ registerGetMessages(server, getClient, getConnectUrl);
32
+ // Register prompts
33
+ registerRumiPrompt(server);
34
+ // stdio transport — NEVER use console.log (breaks JSON-RPC)
35
+ const transport = new StdioServerTransport();
36
+ async function main() {
37
+ await server.connect(transport);
38
+ console.error("Rumi MCP server started (stdio)");
39
+ }
40
+ main().catch((err) => {
41
+ console.error("Fatal error:", err);
42
+ process.exit(1);
43
+ });
44
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;AAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;AAE3C,6EAA6E;AAC7E,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAEtE,SAAS,SAAS;IAChB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,kBAAkB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjE,OAAO,GAAG,IAAI,sBAAsB,CAAC;AACvC,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,iBAAiB;AACjB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AACtD,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AACtD,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AACtD,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AACtD,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAEtD,mBAAmB;AACnB,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAE3B,4DAA4D;AAC5D,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAE7C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerRumiPrompt(server: McpServer): void;
3
+ //# sourceMappingURL=rumi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rumi.d.ts","sourceRoot":"","sources":["../../src/prompts/rumi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2GzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,QAgBnD"}
@@ -0,0 +1,118 @@
1
+ const RUMI_PROMPT = `# Rumi — Find Real People to Chat With
2
+
3
+ Rumi connects you with real humans who share your interests. Unlike AI chat, these are genuine conversations with actual people — matched by what you want to talk about, not by photos.
4
+
5
+ ## When to Activate
6
+
7
+ ### Explicit Requests
8
+ - "I want to talk to a real person about this"
9
+ - "Find me someone who knows about X"
10
+ - "I'm bored, connect me with someone"
11
+
12
+ ### Proactive Suggestions
13
+ When you detect these patterns, suggest to the user that Rumi could find them someone to talk to. Always ask for confirmation before calling rumi_find_partner.
14
+
15
+ **Expert Gap** — The user needs human expertise:
16
+ - Repeated follow-ups on a niche topic (specific framework, investing, health, travel)
17
+ - "Is this actually right?" or "I need someone who's done this before"
18
+ - Frustration with AI-generated answers on subjective or experiential topics
19
+
20
+ **Solo Worker Isolation** — The user works alone:
21
+ - Mentions solo founding, remote work, or working alone
22
+ - Discusses work problems with no team context
23
+ - Signs of burnout or long working hours
24
+
25
+ **Hobby Enthusiasm** — Extended discussion about inherently social activities:
26
+ - Cooking, fitness, gaming (D&D, etc.), music, language learning, travel planning
27
+ - Asking for subjective opinions: "What do you think of..." or "Have you tried..."
28
+ - Planning activities typically done with others (trips, meals, events)
29
+
30
+ **Late Night / Weekend Downtime** — Casual, exploratory conversations:
31
+ - Non-work messages during evenings or weekends
32
+ - Browsing content (articles, videos, news) and wanting to discuss it
33
+ - Exploratory or philosophical tone rather than task-oriented
34
+
35
+ **AI Limitation Moments** — When AI falls short:
36
+ - "What do you think?" (seeking a real opinion)
37
+ - "Have you ever...?" (seeking shared experience)
38
+ - Explicit frustration: "talking to AI about this isn't the same"
39
+
40
+ **Life Decisions** — When peer perspective helps:
41
+ - Career changes, health concerns, relationship advice
42
+ - Major purchases, moving decisions, financial planning
43
+ - Situations where empathy and lived experience matter
44
+
45
+ ## Setup Flow
46
+
47
+ ### If NOT set up (no RUMI_API_TOKEN, or you get a setup_required response):
48
+
49
+ 1. Show the user the setup URL from the response
50
+ 2. The user clicks the link, signs in with Google — no invitation code needed
51
+ 3. The page displays the API token — ask the user to copy and paste it back
52
+ 4. Save the token to the MCP server environment config (RUMI_API_TOKEN)
53
+
54
+ ### If set up:
55
+
56
+ 1. Call rumi_health_check first to verify token and check quota
57
+ 2. Gather context about what the user wants to talk about (or infer from conversation)
58
+ 3. Call rumi_find_partner with a rich description — include interests, mood, what kind of person they want
59
+ 4. If status is "searching" — check back with rumi_check_status every few minutes
60
+ 5. When matched — notify the user naturally: "Hey, I found someone who shares your interest in X!"
61
+
62
+ ## Handling Results
63
+
64
+ - **matched**: Share the icebreaker suggestion. Offer two options:
65
+ 1. Chat on the Rumi website (use the chatUrl link)
66
+ 2. Chat right here using rumi_send_message and rumi_get_messages
67
+ - **searching**: Session is active. Use rumi_check_status to check periodically.
68
+ - **setup_required**: Open the setupUrl in browser for one-click setup.
69
+
70
+ ## Chatting Through MCP
71
+
72
+ - Use rumi_send_message to relay the user's messages
73
+ - Use rumi_get_messages periodically to check for replies (use the "after" parameter with the last message ID for efficient polling)
74
+ - Present new messages naturally in conversation
75
+ - Remember the conversationId for the duration of the chat
76
+
77
+ ## Writing Good Descriptions
78
+
79
+ The quality of the description parameter directly affects match quality. Include:
80
+ - **What** they want to talk about (specific topics, not vague)
81
+ - **Why** — the context or mood (learning, venting, sharing excitement)
82
+ - **What kind of person** — expertise level, personality, shared experiences
83
+
84
+ Good: "Wants to discuss TypeScript migration strategies with someone who's done it at scale. Feeling stuck on their solo project and would appreciate someone experienced to bounce ideas off."
85
+
86
+ Bad: "wants to chat"
87
+
88
+ ## Session Management — Do NOT Create Duplicate Sessions
89
+
90
+ **CRITICAL:** Each rumi_find_partner call creates a new matching session. Do NOT call it repeatedly.
91
+
92
+ - **One session at a time.** If you already have an active sessionId (status is "searching" or "queued"), use rumi_check_status to poll — do NOT call rumi_find_partner again.
93
+ - **Only create a new session when:**
94
+ - The user explicitly wants to find someone NEW
95
+ - The previous session is already "matched" or "closed"
96
+ - The topic has completely changed from the previous session
97
+
98
+ ## Important Notes
99
+ - MCP users get full Rumi accounts (no invitation code needed)
100
+ - Age verification is required (minimum 13 years old)
101
+ - Minors (under 18) are only matched with other minors for safety
102
+ - Never share the user's personal information beyond what they choose to reveal
103
+ - If no match is found, suggest trying again later or with different interests
104
+ - Supports 4 languages: zh-TW, en, ja, ko — detect from user's conversation`;
105
+ export function registerRumiPrompt(server) {
106
+ server.prompt("rumi", "Complete behavior guide for Rumi — when to suggest finding a chat partner, setup flow, session management, and how to write good match descriptions.", () => ({
107
+ messages: [
108
+ {
109
+ role: "user",
110
+ content: {
111
+ type: "text",
112
+ text: RUMI_PROMPT,
113
+ },
114
+ },
115
+ ],
116
+ }));
117
+ }
118
+ //# sourceMappingURL=rumi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rumi.js","sourceRoot":"","sources":["../../src/prompts/rumi.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4EAuGwD,CAAC;AAE7E,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,MAAM,CACX,MAAM,EACN,sJAAsJ,EACtJ,GAAG,EAAE,CAAC,CAAC;QACL,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,WAAW;iBAClB;aACF;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RumiClient } from "../client.js";
3
+ export declare function registerCheckStatus(server: McpServer, getClient: () => RumiClient | null, getConnectUrl: () => string): void;
4
+ //# sourceMappingURL=check-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-status.d.ts","sourceRoot":"","sources":["../../src/tools/check-status.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,EAClC,aAAa,EAAE,MAAM,MAAM,QAgD5B"}
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ export function registerCheckStatus(server, getClient, getConnectUrl) {
3
+ server.tool("rumi_check_status", "Check the status of an active matching session. Use this to poll after calling rumi_find_partner. Status will be 'searching', 'queued', 'matched', or 'closed'.", {
4
+ sessionId: z
5
+ .string()
6
+ .describe("The session ID returned from rumi_find_partner"),
7
+ }, async ({ sessionId }) => {
8
+ const client = getClient();
9
+ if (!client) {
10
+ return {
11
+ content: [
12
+ {
13
+ type: "text",
14
+ text: JSON.stringify({
15
+ status: "setup_required",
16
+ message: "Rumi API token is not configured. Open the setup URL to get your token.",
17
+ setupUrl: getConnectUrl(),
18
+ }),
19
+ },
20
+ ],
21
+ };
22
+ }
23
+ try {
24
+ const result = await client.getSessionStatus(sessionId);
25
+ return {
26
+ content: [{ type: "text", text: JSON.stringify(result) }],
27
+ };
28
+ }
29
+ catch (err) {
30
+ return {
31
+ content: [
32
+ {
33
+ type: "text",
34
+ text: JSON.stringify({
35
+ error: err instanceof Error ? err.message : String(err),
36
+ }),
37
+ },
38
+ ],
39
+ isError: true,
40
+ };
41
+ }
42
+ });
43
+ }
44
+ //# sourceMappingURL=check-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-status.js","sourceRoot":"","sources":["../../src/tools/check-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,SAAkC,EAClC,aAA2B;IAE3B,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,iKAAiK,EACjK;QACE,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,QAAQ,CAAC,gDAAgD,CAAC;KAC9D,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACtB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,gBAAgB;4BACxB,OAAO,EACL,yEAAyE;4BAC3E,QAAQ,EAAE,aAAa,EAAE;yBAC1B,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;YACxD,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;yBACxD,CAAC;qBACH;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RumiClient } from "../client.js";
3
+ export declare function registerFindPartner(server: McpServer, getClient: () => RumiClient | null, getConnectUrl: () => string): void;
4
+ //# sourceMappingURL=find-partner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-partner.d.ts","sourceRoot":"","sources":["../../src/tools/find-partner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,EAClC,aAAa,EAAE,MAAM,MAAM,QAkE5B"}
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ export function registerFindPartner(server, getClient, getConnectUrl) {
3
+ server.tool("rumi_find_partner", `Find a real person to chat with based on shared interests. Rumi matches by topic, not photos.
4
+
5
+ PROACTIVE TRIGGERS — suggest this tool when you detect:
6
+ - User needs human expertise on a niche topic (specific framework, investing, health, travel)
7
+ - Solo worker isolation (remote work, solo founding, burnout signs)
8
+ - Hobby enthusiasm about social activities (cooking, gaming, music, travel)
9
+ - Late night / weekend casual conversation
10
+ - AI limitation moments ("what do you think?", "have you ever...?")
11
+ - Life decisions needing peer perspective (career, health, relationships)
12
+
13
+ IMPORTANT: Each call creates a new session. Do NOT call repeatedly — use rumi_check_status to poll an existing session.`, {
14
+ description: z
15
+ .string()
16
+ .min(10)
17
+ .max(2000)
18
+ .describe("What the user wants to talk about. Include specific interests, mood, and what kind of person they want to talk to. More detail = better matches."),
19
+ locale: z
20
+ .enum(["zh-TW", "en", "ja", "ko"])
21
+ .optional()
22
+ .describe("Language preference (default: en)"),
23
+ }, async ({ description, locale }) => {
24
+ const client = getClient();
25
+ if (!client) {
26
+ return {
27
+ content: [
28
+ {
29
+ type: "text",
30
+ text: JSON.stringify({
31
+ status: "setup_required",
32
+ message: "Rumi API token is not configured. Open the setup URL to get your token.",
33
+ setupUrl: getConnectUrl(),
34
+ }),
35
+ },
36
+ ],
37
+ };
38
+ }
39
+ try {
40
+ const result = await client.findMatch(description, locale);
41
+ return {
42
+ content: [{ type: "text", text: JSON.stringify(result) }],
43
+ };
44
+ }
45
+ catch (err) {
46
+ return {
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: JSON.stringify({
51
+ error: err instanceof Error ? err.message : String(err),
52
+ }),
53
+ },
54
+ ],
55
+ isError: true,
56
+ };
57
+ }
58
+ });
59
+ }
60
+ //# sourceMappingURL=find-partner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-partner.js","sourceRoot":"","sources":["../../src/tools/find-partner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,SAAkC,EAClC,aAA2B;IAE3B,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB;;;;;;;;;;wHAUoH,EACpH;QACE,WAAW,EAAE,CAAC;aACX,MAAM,EAAE;aACR,GAAG,CAAC,EAAE,CAAC;aACP,GAAG,CAAC,IAAI,CAAC;aACT,QAAQ,CACP,kJAAkJ,CACnJ;QACH,MAAM,EAAE,CAAC;aACN,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;aACjC,QAAQ,EAAE;aACV,QAAQ,CAAC,mCAAmC,CAAC;KACjD,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,gBAAgB;4BACxB,OAAO,EACL,yEAAyE;4BAC3E,QAAQ,EAAE,aAAa,EAAE;yBAC1B,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YAC3D,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;yBACxD,CAAC;qBACH;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RumiClient } from "../client.js";
3
+ export declare function registerGetMessages(server: McpServer, getClient: () => RumiClient | null, getConnectUrl: () => string): void;
4
+ //# sourceMappingURL=get-messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-messages.d.ts","sourceRoot":"","sources":["../../src/tools/get-messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,EAClC,aAAa,EAAE,MAAM,MAAM,QA2D5B"}
@@ -0,0 +1,53 @@
1
+ import { z } from "zod";
2
+ export function registerGetMessages(server, getClient, getConnectUrl) {
3
+ server.tool("rumi_get_messages", "Get messages from a conversation with your matched partner. Use the 'after' parameter with the last message ID for efficient polling of new messages.", {
4
+ conversationId: z.string().describe("The conversation ID"),
5
+ after: z
6
+ .string()
7
+ .optional()
8
+ .describe("Message ID to get messages after (for pagination). Omit to get the most recent messages."),
9
+ limit: z
10
+ .number()
11
+ .int()
12
+ .min(1)
13
+ .max(100)
14
+ .optional()
15
+ .describe("Max messages to return (default 50, max 100)"),
16
+ }, async ({ conversationId, after, limit }) => {
17
+ const client = getClient();
18
+ if (!client) {
19
+ return {
20
+ content: [
21
+ {
22
+ type: "text",
23
+ text: JSON.stringify({
24
+ status: "setup_required",
25
+ message: "Rumi API token is not configured. Open the setup URL to get your token.",
26
+ setupUrl: getConnectUrl(),
27
+ }),
28
+ },
29
+ ],
30
+ };
31
+ }
32
+ try {
33
+ const result = await client.getMessages(conversationId, after, limit);
34
+ return {
35
+ content: [{ type: "text", text: JSON.stringify(result) }],
36
+ };
37
+ }
38
+ catch (err) {
39
+ return {
40
+ content: [
41
+ {
42
+ type: "text",
43
+ text: JSON.stringify({
44
+ error: err instanceof Error ? err.message : String(err),
45
+ }),
46
+ },
47
+ ],
48
+ isError: true,
49
+ };
50
+ }
51
+ });
52
+ }
53
+ //# sourceMappingURL=get-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-messages.js","sourceRoot":"","sources":["../../src/tools/get-messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,SAAkC,EAClC,aAA2B;IAE3B,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,uJAAuJ,EACvJ;QACE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QAC1D,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,0FAA0F,CAC3F;QACH,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,GAAG,CAAC;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8CAA8C,CAAC;KAC5D,EACD,KAAK,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;QACzC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,gBAAgB;4BACxB,OAAO,EACL,yEAAyE;4BAC3E,QAAQ,EAAE,aAAa,EAAE;yBAC1B,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACtE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;yBACxD,CAAC;qBACH;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RumiClient } from "../client.js";
3
+ export declare function registerHealthCheck(server: McpServer, getClient: () => RumiClient | null, getConnectUrl: () => string): void;
4
+ //# sourceMappingURL=health-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../../src/tools/health-check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,EAClC,aAAa,EAAE,MAAM,MAAM,QA4C5B"}
@@ -0,0 +1,39 @@
1
+ export function registerHealthCheck(server, getClient, getConnectUrl) {
2
+ server.tool("rumi_health_check", "Check your Rumi account status: token validity, weekly quota remaining, and active matching session. Call this before rumi_find_partner to verify everything is working.", {}, async () => {
3
+ const client = getClient();
4
+ if (!client) {
5
+ return {
6
+ content: [
7
+ {
8
+ type: "text",
9
+ text: JSON.stringify({
10
+ status: "setup_required",
11
+ message: "Rumi API token is not configured. Open the setup URL to get your token.",
12
+ setupUrl: getConnectUrl(),
13
+ }),
14
+ },
15
+ ],
16
+ };
17
+ }
18
+ try {
19
+ const health = await client.healthCheck();
20
+ return {
21
+ content: [{ type: "text", text: JSON.stringify(health) }],
22
+ };
23
+ }
24
+ catch (err) {
25
+ return {
26
+ content: [
27
+ {
28
+ type: "text",
29
+ text: JSON.stringify({
30
+ error: err instanceof Error ? err.message : String(err),
31
+ }),
32
+ },
33
+ ],
34
+ isError: true,
35
+ };
36
+ }
37
+ });
38
+ }
39
+ //# sourceMappingURL=health-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-check.js","sourceRoot":"","sources":["../../src/tools/health-check.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,SAAkC,EAClC,aAA2B;IAE3B,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,0KAA0K,EAC1K,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,gBAAgB;4BACxB,OAAO,EACL,yEAAyE;4BAC3E,QAAQ,EAAE,aAAa,EAAE;yBAC1B,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;YAC1C,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;yBACxD,CAAC;qBACH;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RumiClient } from "../client.js";
3
+ export declare function registerSendMessage(server: McpServer, getClient: () => RumiClient | null, getConnectUrl: () => string): void;
4
+ //# sourceMappingURL=send-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-message.d.ts","sourceRoot":"","sources":["../../src/tools/send-message.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,EAClC,aAAa,EAAE,MAAM,MAAM,QAqD5B"}
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ export function registerSendMessage(server, getClient, getConnectUrl) {
3
+ server.tool("rumi_send_message", "Send a message to your matched chat partner. Only works after a successful match (you need a conversationId from rumi_find_partner or rumi_check_status).", {
4
+ conversationId: z
5
+ .string()
6
+ .describe("The conversation ID from a successful match"),
7
+ content: z
8
+ .string()
9
+ .min(1)
10
+ .max(5000)
11
+ .describe("Message to send to your chat partner"),
12
+ }, async ({ conversationId, content }) => {
13
+ const client = getClient();
14
+ if (!client) {
15
+ return {
16
+ content: [
17
+ {
18
+ type: "text",
19
+ text: JSON.stringify({
20
+ status: "setup_required",
21
+ message: "Rumi API token is not configured. Open the setup URL to get your token.",
22
+ setupUrl: getConnectUrl(),
23
+ }),
24
+ },
25
+ ],
26
+ };
27
+ }
28
+ try {
29
+ const result = await client.sendMessage(conversationId, content);
30
+ return {
31
+ content: [{ type: "text", text: JSON.stringify(result) }],
32
+ };
33
+ }
34
+ catch (err) {
35
+ return {
36
+ content: [
37
+ {
38
+ type: "text",
39
+ text: JSON.stringify({
40
+ error: err instanceof Error ? err.message : String(err),
41
+ }),
42
+ },
43
+ ],
44
+ isError: true,
45
+ };
46
+ }
47
+ });
48
+ }
49
+ //# sourceMappingURL=send-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-message.js","sourceRoot":"","sources":["../../src/tools/send-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,UAAU,mBAAmB,CACjC,MAAiB,EACjB,SAAkC,EAClC,aAA2B;IAE3B,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,2JAA2J,EAC3J;QACE,cAAc,EAAE,CAAC;aACd,MAAM,EAAE;aACR,QAAQ,CAAC,6CAA6C,CAAC;QAC1D,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,IAAI,CAAC;aACT,QAAQ,CAAC,sCAAsC,CAAC;KACpD,EACD,KAAK,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,gBAAgB;4BACxB,OAAO,EACL,yEAAyE;4BAC3E,QAAQ,EAAE,aAAa,EAAE;yBAC1B,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;YACjE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;yBACxD,CAAC;qBACH;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,50 @@
1
+ /** Rumi API response types (pure TypeScript interfaces, no runtime schema deps) */
2
+ export interface FindMatchResponse {
3
+ status: "matched" | "searching";
4
+ sessionId?: string;
5
+ conversationId?: string;
6
+ chatUrl?: string;
7
+ icebreaker?: string;
8
+ partnerName?: string;
9
+ message?: string;
10
+ }
11
+ export interface SessionStatusResponse {
12
+ sessionId: string;
13
+ status: "searching" | "queued" | "matched" | "closed";
14
+ conversationId?: string;
15
+ chatUrl?: string;
16
+ keywords?: string[];
17
+ wants?: string[];
18
+ partnerTags?: string[];
19
+ primaryActivity?: string;
20
+ messageCount?: number;
21
+ }
22
+ export interface Message {
23
+ id: string;
24
+ sender_id: string;
25
+ content: string;
26
+ created_at: string;
27
+ is_read: boolean;
28
+ }
29
+ export interface MessagesResponse {
30
+ messages: Message[];
31
+ }
32
+ export interface SendMessageResponse {
33
+ id: string;
34
+ created_at: string;
35
+ }
36
+ export interface HealthResponse {
37
+ status: string;
38
+ userId: string;
39
+ tokenExpiresAt: string | null;
40
+ weeklyUsage: {
41
+ used: number;
42
+ limit: number;
43
+ remaining: number;
44
+ };
45
+ activeSession: {
46
+ sessionId: string;
47
+ status: string;
48
+ } | null;
49
+ }
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,aAAa,EAAE;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,CAAC;CACV"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ /** Rumi API response types (pure TypeScript interfaces, no runtime schema deps) */
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mFAAmF"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "rumi-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Rumi — find real people to chat with through AI-powered matching",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "rumi-mcp-server": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "keywords": [
14
+ "mcp",
15
+ "mcp-server",
16
+ "rumi",
17
+ "chat",
18
+ "matching",
19
+ "ai",
20
+ "model-context-protocol"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/Ricky610329/Rumi.git",
25
+ "directory": "rumi-mcp-server"
26
+ },
27
+ "homepage": "https://rumi.app",
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "start": "node dist/index.js",
31
+ "dev": "tsc --watch",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.0.0",
36
+ "zod": "^3.23.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.0.0",
40
+ "typescript": "^5.5.0"
41
+ }
42
+ }