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.
- package/README.md +78 -0
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +69 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/rumi.d.ts +3 -0
- package/dist/prompts/rumi.d.ts.map +1 -0
- package/dist/prompts/rumi.js +118 -0
- package/dist/prompts/rumi.js.map +1 -0
- package/dist/tools/check-status.d.ts +4 -0
- package/dist/tools/check-status.d.ts.map +1 -0
- package/dist/tools/check-status.js +44 -0
- package/dist/tools/check-status.js.map +1 -0
- package/dist/tools/find-partner.d.ts +4 -0
- package/dist/tools/find-partner.d.ts.map +1 -0
- package/dist/tools/find-partner.js +60 -0
- package/dist/tools/find-partner.js.map +1 -0
- package/dist/tools/get-messages.d.ts +4 -0
- package/dist/tools/get-messages.d.ts.map +1 -0
- package/dist/tools/get-messages.js +53 -0
- package/dist/tools/get-messages.js.map +1 -0
- package/dist/tools/health-check.d.ts +4 -0
- package/dist/tools/health-check.d.ts.map +1 -0
- package/dist/tools/health-check.js +39 -0
- package/dist/tools/health-check.js.map +1 -0
- package/dist/tools/send-message.d.ts +4 -0
- package/dist/tools/send-message.d.ts.map +1 -0
- package/dist/tools/send-message.js +49 -0
- package/dist/tools/send-message.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- 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
|
+
```
|
package/dist/client.d.ts
ADDED
|
@@ -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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
+
}
|