@rubytech/create-realagent 1.0.610 → 1.0.612
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/package.json +1 -1
- package/payload/platform/knowledge/maxy.md +107 -38
- package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +31 -8
- package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/mcp-stderr-tee/dist/index.js +162 -14
- package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +1 -1
- package/payload/platform/lib/mcp-stderr-tee/src/index.ts +182 -17
- package/payload/platform/plugins/admin/PLUGIN.md +3 -3
- package/payload/platform/plugins/admin/skills/plugin-management/{skill.md → SKILL.md} +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +20 -0
- package/payload/platform/plugins/sales/references/faq.md +0 -10
- package/payload/platform/plugins/sales/references/pricing.md +0 -1
- package/payload/server/public/assets/{admin-eEnOjpcY.js → admin-DOSuO0gX.js} +1 -1
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +2 -2
- /package/payload/platform/plugins/admin/skills/onboarding/{skill.md → SKILL.md} +0 -0
- /package/payload/platform/plugins/admin/skills/public-agent-manager/{skill.md → SKILL.md} +0 -0
- /package/payload/platform/plugins/admin/skills/specialist-management/{skill.md → SKILL.md} +0 -0
- /package/payload/platform/plugins/admin/skills/update-knowledge/{skill.md → SKILL.md} +0 -0
- /package/payload/platform/plugins/workflows/skills/workflow-manager/{skill.md → SKILL.md} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Maxy
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Time. Knowledge. Money.** Folk wisdom: if you're short on one, you use the other two to get it. Most productivity systems ignore the middle variable. Maxy doesn't. Your business lives in an ontological substrate — a knowledge graph only AI can create and interpret at useful scale. The outcome is time.
|
|
4
|
+
|
|
5
|
+
Not a productivity tool. An operations layer. AI for elite operators.
|
|
4
6
|
|
|
5
7
|
Maxy does three things, continuously, across every session:
|
|
6
8
|
|
|
@@ -20,8 +22,10 @@ Your data never leaves your premises. GDPR compliant. EU AI Act compliant.
|
|
|
20
22
|
|---|---|
|
|
21
23
|
| **Company** | Rubytech LLC |
|
|
22
24
|
| **Product** | Maxy |
|
|
23
|
-
| **
|
|
24
|
-
| **
|
|
25
|
+
| **Tagline** | Time. Knowledge. Money. |
|
|
26
|
+
| **Positioning** | AI for elite operators. Not a productivity tool — an operations layer. |
|
|
27
|
+
| **Thesis** | Knowledge is the underweighted variable in the time/knowledge/money equation. At business scale, only AI can create and interpret it. |
|
|
28
|
+
| **Operating philosophy** | Calibrate. Prevent. Compress. |
|
|
25
29
|
| **Domains** | getmaxy.com (marketing), maxy.bot (product), maxy.chat (alias) |
|
|
26
30
|
| **Stage** | Pre-launch. Building the waitlist. Currently in internal testing (dog-fooding) — Maxy is selling Maxy. |
|
|
27
31
|
|
|
@@ -45,23 +49,49 @@ The public-facing agent greets visitors warmly and demonstrates capability immed
|
|
|
45
49
|
|
|
46
50
|
---
|
|
47
51
|
|
|
48
|
-
##
|
|
52
|
+
## The equation
|
|
53
|
+
|
|
54
|
+
Folk wisdom says if you're short on one of time, knowledge, or money, you use the other two to get it. Most systems built for productive people ignore the middle variable. They treat knowledge as a database to query, not the substrate everything else runs on.
|
|
55
|
+
|
|
56
|
+
Maxy doesn't. Your business — every customer, every commitment, every process, every standard — lives in an ontological substrate. That is not marketing language for "database". It is the ground truth the system reasons against, designed to hold knowledge at a scale and complexity only AI is capable of creating and interpreting. No human can hand-model every customer relationship, every commitment, every workflow dependency at the fidelity this requires — which is why the substrate is the foundation of Maxy, not a side-effect of the application layer.
|
|
57
|
+
|
|
58
|
+
The outcome: time, given back to you. Time you use to apply what you know, earn money, or simply live.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Why nothing worked
|
|
63
|
+
|
|
64
|
+
Every tool you tried stored data. None of them modelled relationships. Your CRM couldn't see your calendar. Your calendar couldn't see your commitments. Your project tracker didn't know who the client was, what you'd promised them, or which tasks depended on which. Each tool was a silo pretending to be a system.
|
|
49
65
|
|
|
50
|
-
|
|
66
|
+
Connected data alone isn't enough. What matters is an AI that can reason across all of it — surfacing insights you didn't ask for, catching commitments before they slip, and acting on the full picture of your operations without being told where to look.
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
The implementation layer wasn't multiplying you. It was fractioning you.
|
|
53
69
|
|
|
54
|
-
|
|
70
|
+
- Researchers who summarised instead of synthesised.
|
|
71
|
+
- Coordinators who managed process without understanding context.
|
|
72
|
+
- Product requirements articulated through layers of PMs and developers — time-consuming, expensive, and never the product conceived in the architect's mind.
|
|
55
73
|
|
|
56
|
-
|
|
74
|
+
Maxy collapses that layer. One operator. The output of a team. Zero dilution.
|
|
75
|
+
|
|
76
|
+
---
|
|
57
77
|
|
|
58
|
-
|
|
78
|
+
## The operating philosophy — Calibrate. Prevent. Compress.
|
|
59
79
|
|
|
60
|
-
|
|
80
|
+
Every session, Maxy is doing three things — not as tasks to complete, but as the lens through which it approaches every interaction.
|
|
61
81
|
|
|
62
|
-
|
|
82
|
+
1. **Calibrate what excellence means here.** Learn your standards — what good looks like in your work, your decisions, your outcomes — and hold you to them. Session 300 is more precise than session 3.
|
|
83
|
+
2. **Prevent what used to go wrong.** Learn the failure patterns from before — what fell through the cracks, what kept breaking, what frustrated you. Break those patterns, not just record them.
|
|
84
|
+
3. **Compress what takes months to learn.** The rhythms, instincts, and patterns that humans build only through sustained repetition — encoded from the first instance. The benefit of experience, immediately.
|
|
63
85
|
|
|
64
|
-
|
|
86
|
+
Every capability below — the five roles, the graph, the workflows — serves this triumvirate. When in doubt about whether a feature belongs, ask whether it calibrates, prevents, or compresses. If it does none, it does not belong.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## How Maxy works for you
|
|
91
|
+
|
|
92
|
+
Maxy learns your preferences from conversation — never from questionnaires or onboarding forms. When you reveal something about how you like to work, Maxy stores it with a confidence score that strengthens when reinforced and fades when contradicted. You can always override: "remember this", "forget that", or simply contradict it in the moment.
|
|
93
|
+
|
|
94
|
+
Your thinking sets you apart. Now the rest doesn't hold you back.
|
|
65
95
|
|
|
66
96
|
### Your business, understood
|
|
67
97
|
|
|
@@ -101,31 +131,55 @@ Connect Maxy to new capabilities by asking — finance tracking, home management
|
|
|
101
131
|
|
|
102
132
|
---
|
|
103
133
|
|
|
104
|
-
##
|
|
134
|
+
## Five roles. One operator. Zero dilution.
|
|
105
135
|
|
|
106
|
-
|
|
136
|
+
Every hour you spend on admin, scheduling, research, or content is an hour not spent on the work only you can do. Maxy dispatches five roles on your behalf — you never configure or manage them. Each admin role has a public-facing counterpart that can be enabled for customer-facing work. You may see activity like "Dispatching personal-assistant..." in the chat timeline when this happens.
|
|
107
137
|
|
|
108
|
-
|
|
138
|
+
| Admin role | What it does | Public counterpart |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| **Project Manager** | Projects, tasks, dependencies, sessions, lifecycle | Client Onboarding — qualification, requirements, 24/7 intake |
|
|
141
|
+
| **Personal Assistant** | Scheduling, admin, messaging, browser automation, briefings | Support — FAQ, knowledge base, triage |
|
|
142
|
+
| **Research Assistant** | Web search, knowledge management, citations, visuals | Booking — scheduling, availability, confirmations |
|
|
143
|
+
| **Content Producer** | Documents, PDFs, ingestion, image generation | Sales Enquiry — lead qualification, pricing, proposals |
|
|
144
|
+
| **Coach** | Accountability, patterns, progress, reviews | Maxy — generalist, 24/7 public agent |
|
|
109
145
|
|
|
110
|
-
|
|
146
|
+
Roles are installed during setup and listed when Maxy introduces itself. Some premium plugins add their own specialists on top (e.g. a manuscript reviewer for writer-craft, a listings specialist for Pro Real Estate). Roles installed mid-session become active from the next session.
|
|
111
147
|
|
|
112
|
-
|
|
148
|
+
### The roles in action
|
|
113
149
|
|
|
114
|
-
|
|
150
|
+
**Follows through — Joel's invoice.** Joel bought a Pi on a colleague's card. Promised to forward the VAT invoice when it arrived. Normally — forgotten. Instead, he told Maxy. One conversation. Nothing more to do. Maxy checked the order daily. When the invoice appeared, it sent it directly to the right email address and closed the task. Joel hasn't thought about it since. This is the Personal Assistant and Project Manager working together. This is how Maxy works.
|
|
115
151
|
|
|
116
|
-
|
|
152
|
+
**Responds — customer enquiry at 10pm.** A customer enquiry comes in at 10pm. You don't see it until morning. They've already called your competitor. With Maxy: the public Sales Enquiry agent answers immediately with your services, availability, and pricing. The meeting is booked before breakfast.
|
|
117
153
|
|
|
118
|
-
|
|
154
|
+
**Handles finances — end of week admin.** End of week — two hours typing up quotes, chasing unpaid invoices, updating the spreadsheet you pretend is a CRM. With Maxy: Content Producer generates the quote on site. Personal Assistant sends it and chases it. Customer profile is a conversation away.
|
|
119
155
|
|
|
120
|
-
Sunday spent coordinating next week — kids, dentist, groceries, car service. With Maxy: already sorted. Dentist confirmed, groceries listed, car booked.
|
|
156
|
+
**Manages your picture — weekend coordination.** Sunday spent coordinating next week — kids, dentist, groceries, car service. With Maxy: already sorted. Dentist confirmed, groceries listed, car booked. One conversation, five roles, done.
|
|
121
157
|
|
|
122
158
|
---
|
|
123
159
|
|
|
124
160
|
## How it works
|
|
125
161
|
|
|
126
|
-
A private device on your network holds your full operational context in a
|
|
162
|
+
A private device on your network holds your full operational context in a seven-layer architecture, grounded in a local knowledge graph that connects every entity in your operation.
|
|
163
|
+
|
|
164
|
+
### The seven layers
|
|
165
|
+
|
|
166
|
+
1. **Device** — Raspberry Pi 5 or a Linux mini PC on your premises. This is the system boundary. Nothing but LLM prompts and responses crosses it.
|
|
167
|
+
2. **Ontological substrate** — Neo4j graph database and vector embeddings. Not a data store bolted onto an LLM — the ground truth the entire system reasons against. Entity relationships, customer history, document knowledge, and agent memory all live here. Vector embeddings enable semantic retrieval; graph relationships enable context traversal.
|
|
168
|
+
3. **Intelligence** — Claude (via the Anthropic API) handles language understanding, synthesis, and generation. Maxy handles everything else: tool routing, knowledge scoping, and workflow execution. The split is deliberate.
|
|
169
|
+
4. **Plugins and capabilities** — the plugin manifest is Maxy's domain ontology. It defines which tools belong to which domain, which skills are available to which agent, and which capabilities are active. When a request arrives, the agent resolves the relevant domain and operates within that boundary — it cannot invoke a tool outside its resolved domain.
|
|
170
|
+
5. **Execution** — Projects, Tasks, and Workflows. Projects are first-class graph entities, grounded in PMBOK 7th Edition and APM Body of Knowledge 5th Edition, adapted for conversational use. Dependency tracking (BLOCKS) and conflict detection (AFFECTS) are graph-native — task sequencing follows graph-defined rules, not model discretion.
|
|
171
|
+
6. **Agents** — three tiers with formally separated access. Admin (owner-facing, full graph access, full tool access). Public (customer-facing, scoped to public knowledge only). Specialist sub-agents (domain-specific execution dispatched by the admin agent). Agents cannot escalate their own permissions.
|
|
172
|
+
7. **Interfaces** — WhatsApp, Web Chat, Telegram, Email. All channels share the same underlying graph. A customer conversation on WhatsApp and a task created via the web interface exist in the same knowledge space. Channel is irrelevant; context is always whole.
|
|
173
|
+
|
|
174
|
+
### The design principle
|
|
175
|
+
|
|
176
|
+
**Deterministic where correctness matters. LLM where judgement matters.**
|
|
127
177
|
|
|
128
|
-
|
|
178
|
+
Tool routing, data access, workflow sequencing, and capability boundaries follow formal rules. The model cannot override them. Language understanding, synthesis, and contextual response generation are handled by Claude — applied to grounded, locally-held data. This places Maxy at L2–L3 of the neurosymbolic coupling maturity model (Tuan, 2026): ontology-constrained tool discovery and knowledge-grounded reasoning, rather than the prompt-injection approach common in competing architectures.
|
|
179
|
+
|
|
180
|
+
### The interface
|
|
181
|
+
|
|
182
|
+
Conversation. Web chat, WhatsApp, Telegram. The same way you briefed a team, except this one retains everything, connects everything, and executes without being managed. No dashboards. No workflows to configure.
|
|
129
183
|
|
|
130
184
|
Plug in the device, open your browser, set a PIN, connect your Claude account, and start talking. Fully operational within 15 minutes.
|
|
131
185
|
|
|
@@ -207,6 +261,24 @@ Maxy runs on any Linux machine with 16 GB or more of RAM. Your own hardware or a
|
|
|
207
261
|
|
|
208
262
|
---
|
|
209
263
|
|
|
264
|
+
## Security and accountability
|
|
265
|
+
|
|
266
|
+
Privacy by architecture is the starting point. Three structural features back it up.
|
|
267
|
+
|
|
268
|
+
### Inbound message screening
|
|
269
|
+
|
|
270
|
+
Every inbound message — regardless of channel (web chat, WhatsApp, Telegram, email) — passes through a centralised screening gateway before reaching the agent. The gateway detects prompt injection patterns, classifies intent, assigns a safety verdict (CLEAN / SUSPICIOUS / DISCARD), and rewrites queries for retrieval. Unsafe messages on public channels are refused without agent invocation. Admin messages receive advisory screening only — flagged, never blocked.
|
|
271
|
+
|
|
272
|
+
### Agent action audit trail
|
|
273
|
+
|
|
274
|
+
Every tool invocation by the admin agent produces a durable audit record in the knowledge graph — tool name, input, output, timestamp, and originating conversation. Records persist indefinitely and are queryable in conversation. Ask Maxy "what tools ran in the last session?" to review. This is not a debug log — it is a first-class data structure designed for accountability and operational review.
|
|
275
|
+
|
|
276
|
+
### AI content provenance
|
|
277
|
+
|
|
278
|
+
When your public agent sends a message to someone — by email, WhatsApp, Telegram, or SMS — the platform automatically includes a brief disclosure that the content was generated by AI. Emails include an `X-AI-Generated` header and a footer line. WhatsApp and Telegram messages have a short line appended. This is transparent and cannot be turned off. Messages you write yourself (typing directly in WhatsApp) are not marked — the disclosure applies only to content composed by the AI agent.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
210
282
|
## Privacy and data sovereignty
|
|
211
283
|
|
|
212
284
|
### Your data stays on your premises
|
|
@@ -215,7 +287,7 @@ All data is stored on the device on your premises. It never goes to a cloud serv
|
|
|
215
287
|
|
|
216
288
|
### What leaves the device
|
|
217
289
|
|
|
218
|
-
Only LLM prompts — the messages you send to Maxy — leave the device. They are sent to Claude (Anthropic's AI) for processing and are subject to Anthropic's privacy policy. Anthropic does not use your conversations to train their models.
|
|
290
|
+
Only LLM prompts — the messages you send to Maxy — leave the device. They are sent to Claude (Anthropic's AI) for processing under Anthropic's zero-retention API terms and are subject to Anthropic's privacy policy. Anthropic does not use your conversations to train their models.
|
|
219
291
|
|
|
220
292
|
### What stays on the device
|
|
221
293
|
|
|
@@ -223,7 +295,12 @@ Everything else. Your contacts, your documents, your conversation history, your
|
|
|
223
295
|
|
|
224
296
|
### GDPR compliance
|
|
225
297
|
|
|
226
|
-
Maxy is GDPR compliant by default. Your data lives on your premises, not ours.
|
|
298
|
+
Maxy is GDPR compliant by default. Your data lives on your premises, not ours. Compliance is the architecture, not a configuration option. Data subject rights are exercised through the same conversational interface used for everything else — no support tickets, no dashboards, no waiting.
|
|
299
|
+
|
|
300
|
+
- **Right of access (Article 15):** ask Maxy to export all data held on a contact. The export gathers the person record, access credentials, conversation history, and emails into a single structured document that can be handed directly to the data subject.
|
|
301
|
+
- **Right to erasure (Article 17):** ask Maxy to delete all data held on a contact. Maxy shows a preview of what would be removed, confirms with you, then performs a full cascade deletion across all data stores. A deletion receipt is returned listing exactly what was removed.
|
|
302
|
+
|
|
303
|
+
On-premises deployment means the data controller is also the data processor — no third-party data sharing, no processor agreements to review.
|
|
227
304
|
|
|
228
305
|
### Cancellation
|
|
229
306
|
|
|
@@ -332,7 +409,7 @@ Claude Desktop, Claude Code, and Perplexity are AI tools that make you more prod
|
|
|
332
409
|
|
|
333
410
|
Maxy is a business and personal operating system that grows with you. It starts by helping — learning your customers, your processes, your way of working. As it proves itself, it takes on more. It answers your customers at 3am. It delivers your morning briefing with your pipeline status, your overdue follow-ups, and your day's appointments before you've opened your laptop. It generates invoices, tracks deals through stages, prepares meeting packs, and runs workflows you've defined — all on a device you own, with data that never leaves your premises, exported in open standards you control.
|
|
334
411
|
|
|
335
|
-
|
|
412
|
+
Your thinking sets you apart. Now the rest doesn't hold you back. The others make you faster. Maxy makes your business run.
|
|
336
413
|
|
|
337
414
|
---
|
|
338
415
|
|
|
@@ -348,13 +425,13 @@ You do not need to install any software, configure any settings, or have any tec
|
|
|
348
425
|
|
|
349
426
|
## Setup
|
|
350
427
|
|
|
351
|
-
1. Plug in your Maxy device and connect it to
|
|
428
|
+
1. Plug in your Maxy device. If there's no ethernet cable and no WiFi configured, the device creates a temporary network called **Maxy-Setup** — connect your phone to it, pick your home WiFi from the setup page, and enter the password. The temporary network disappears and your phone reconnects to your home network.
|
|
352
429
|
2. Open a browser on any device and go to maxy.local.
|
|
353
430
|
3. Set a PIN — this protects your admin chat.
|
|
354
431
|
4. Connect your Claude account — follow the on-screen steps to link your Anthropic subscription.
|
|
355
432
|
5. Start talking — tell Maxy about yourself, your business, your customers.
|
|
356
433
|
|
|
357
|
-
Fully operational in under 15 minutes. No software downloads, no configuration files, no training courses.
|
|
434
|
+
Fully operational in under 15 minutes. No software downloads, no configuration files, no training courses. Setup is resumable — if you close the browser mid-onboarding, Maxy picks up where you left off.
|
|
358
435
|
|
|
359
436
|
---
|
|
360
437
|
|
|
@@ -362,7 +439,7 @@ Fully operational in under 15 minutes. No software downloads, no configuration f
|
|
|
362
439
|
|
|
363
440
|
### Web chat
|
|
364
441
|
|
|
365
|
-
Your primary interface. Open a browser on any device — phone, tablet, laptop — and talk to Maxy. Works on your local network at maxy.local, or from anywhere via your custom domain.
|
|
442
|
+
Your primary interface. Open a browser on any device — phone, tablet, laptop — and talk to Maxy. Works on your local network at maxy.local, or from anywhere via your custom domain. Voice notes are supported when the input is empty — tap the microphone, speak, tap send. Voice recording requires a secure connection (HTTPS), so use your custom domain rather than the local address when recording.
|
|
366
443
|
|
|
367
444
|
### Telegram
|
|
368
445
|
|
|
@@ -578,7 +655,7 @@ We're building the waitlist now. Leave your name and email and we'll be in touch
|
|
|
578
655
|
|
|
579
656
|
### What about GDPR?
|
|
580
657
|
|
|
581
|
-
Maxy is GDPR compliant by default. All data is stored on your device, on your premises. There is no cloud processing of your personal data
|
|
658
|
+
Maxy is GDPR compliant by default. All data is stored on your device, on your premises. There is no cloud processing of your personal data — compliance is the architecture, not a configuration option. Data subject rights (Article 15 access, Article 17 erasure) are exercised through conversation: ask Maxy to export or delete all data held on a contact, and it does, with a confirmation gate and a deletion receipt.
|
|
582
659
|
|
|
583
660
|
### Can I use my own computer instead of the Pi?
|
|
584
661
|
|
|
@@ -636,14 +713,6 @@ Yes. Connect WhatsApp or Telegram for mobile access. Pro users also get a custom
|
|
|
636
713
|
|
|
637
714
|
Yes. Key information from each conversation is stored in a local graph database on your device. When you start a new session, Maxy knows your business, your customers, your preferences, and what was discussed previously. Long conversations are automatically summarised so nothing is lost.
|
|
638
715
|
|
|
639
|
-
### How does Maxy know when to stop explaining things?
|
|
640
|
-
|
|
641
|
-
This is the Earned Autonomy model. Maxy tracks your demonstrated competence with each capability independently, progressing through three stages: Assist (shows its work), Augment (handles things normally, flags exceptions), and Orchestrate (executes without explanation). The first time you create an invoice, Maxy walks you through its tools and approach. After a few successful invoices without corrections, it stops explaining and just does it. If it gets something wrong and you correct it, it steps back and shows more of its work again. You can also tell Maxy directly — "I know how to do this" advances it, "explain this again" resets it. This happens per capability — Maxy might handle your scheduling silently while still walking you through a new feature.
|
|
642
|
-
|
|
643
|
-
### What's the difference between my preferences and my competence?
|
|
644
|
-
|
|
645
|
-
Preferences are how you like things — terse responses, formal tone, specific workflows. Competence is what you can do in Maxy — whether you need guidance on creating an invoice or you've done it dozens of times. They're tracked separately and work independently. You can be an expert who likes detailed educational explanations, or a novice who prefers brief responses. Maxy respects both.
|
|
646
|
-
|
|
647
716
|
### What happens if my conversation gets really long?
|
|
648
717
|
|
|
649
718
|
Nothing bad. Maxy automatically archives older parts of the conversation to your local memory as the session grows, keeping the active conversation responsive. You can keep talking for as long as you need — there's no message limit, no cut-off, and no point where you have to start over. Archived messages are still available; Maxy retrieves them when they're relevant.
|
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP server stderr tee —
|
|
3
|
-
* (for Claude Code) and a dated log file (for logs-read.sh retrieval).
|
|
2
|
+
* MCP server stderr tee — dual-destination log capture.
|
|
4
3
|
*
|
|
5
|
-
* Claude Code spawns MCP servers as child processes and consumes their
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Claude Code spawns MCP servers as child processes and consumes their stderr
|
|
5
|
+
* internally. The platform cannot intercept at the spawn level. Each server
|
|
6
|
+
* must tee its own stderr so diagnostic output is retrievable.
|
|
7
|
+
*
|
|
8
|
+
* ┌─────────────────────────┐
|
|
9
|
+
* │ MCP server process │
|
|
10
|
+
* │ │
|
|
11
|
+
* │ console.error("[x] ..") │
|
|
12
|
+
* │ │ │
|
|
13
|
+
* │ ▼ │
|
|
14
|
+
* │ patched stderr.write() │
|
|
15
|
+
* │ │ │
|
|
16
|
+
* │ ├──► original stderr (consumed by Claude Code — opaque)
|
|
17
|
+
* │ │
|
|
18
|
+
* │ ├──► mcp-{name}-stderr-{YYYY-MM-DD}.log (raw chunks; Task 362)
|
|
19
|
+
* │ │
|
|
20
|
+
* │ └──► claude-agent-stream-{YYYY-MM-DD}.log (per-line, prefixed; Task 524)
|
|
21
|
+
* │ "[<iso>] [mcp:{name}] <line>"
|
|
22
|
+
* │ rotates on date boundary
|
|
23
|
+
* └─────────────────────────┘
|
|
24
|
+
*
|
|
25
|
+
* The stream-log destination puts MCP diagnostic lines in the same file the
|
|
26
|
+
* platform already writes agent events to, so a single logs-read returns a
|
|
27
|
+
* complete session timeline. The per-server file is retained unchanged —
|
|
28
|
+
* it remains the grep-one-plugin surface.
|
|
8
29
|
*/
|
|
9
30
|
/**
|
|
10
|
-
* Patch process.stderr.write to tee
|
|
31
|
+
* Patch process.stderr.write to tee to both a per-server raw log and the
|
|
32
|
+
* unified claude-agent-stream log (with a [mcp:<serverName>] prefix).
|
|
11
33
|
*
|
|
12
34
|
* Reads LOG_DIR from environment. When absent (dev mode, tool discovery),
|
|
13
|
-
* does nothing — stderr
|
|
35
|
+
* does nothing — stderr works as before with no markers emitted.
|
|
14
36
|
*
|
|
15
|
-
*
|
|
37
|
+
* Safe to call once at MCP server module load. Not safe to call twice —
|
|
38
|
+
* repeated calls would stack patches and double-log every chunk.
|
|
16
39
|
*/
|
|
17
40
|
export declare function initStderrTee(serverName: string): void;
|
|
18
41
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAeH;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAkKtD"}
|
|
@@ -1,45 +1,193 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* MCP server stderr tee —
|
|
4
|
-
* (for Claude Code) and a dated log file (for logs-read.sh retrieval).
|
|
3
|
+
* MCP server stderr tee — dual-destination log capture.
|
|
5
4
|
*
|
|
6
|
-
* Claude Code spawns MCP servers as child processes and consumes their
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Claude Code spawns MCP servers as child processes and consumes their stderr
|
|
6
|
+
* internally. The platform cannot intercept at the spawn level. Each server
|
|
7
|
+
* must tee its own stderr so diagnostic output is retrievable.
|
|
8
|
+
*
|
|
9
|
+
* ┌─────────────────────────┐
|
|
10
|
+
* │ MCP server process │
|
|
11
|
+
* │ │
|
|
12
|
+
* │ console.error("[x] ..") │
|
|
13
|
+
* │ │ │
|
|
14
|
+
* │ ▼ │
|
|
15
|
+
* │ patched stderr.write() │
|
|
16
|
+
* │ │ │
|
|
17
|
+
* │ ├──► original stderr (consumed by Claude Code — opaque)
|
|
18
|
+
* │ │
|
|
19
|
+
* │ ├──► mcp-{name}-stderr-{YYYY-MM-DD}.log (raw chunks; Task 362)
|
|
20
|
+
* │ │
|
|
21
|
+
* │ └──► claude-agent-stream-{YYYY-MM-DD}.log (per-line, prefixed; Task 524)
|
|
22
|
+
* │ "[<iso>] [mcp:{name}] <line>"
|
|
23
|
+
* │ rotates on date boundary
|
|
24
|
+
* └─────────────────────────┘
|
|
25
|
+
*
|
|
26
|
+
* The stream-log destination puts MCP diagnostic lines in the same file the
|
|
27
|
+
* platform already writes agent events to, so a single logs-read returns a
|
|
28
|
+
* complete session timeline. The per-server file is retained unchanged —
|
|
29
|
+
* it remains the grep-one-plugin surface.
|
|
9
30
|
*/
|
|
10
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
32
|
exports.initStderrTee = initStderrTee;
|
|
12
33
|
const node_fs_1 = require("node:fs");
|
|
13
34
|
const node_path_1 = require("node:path");
|
|
35
|
+
const node_string_decoder_1 = require("node:string_decoder");
|
|
36
|
+
// Marker on process.stderr.write so repeat calls to initStderrTee() are
|
|
37
|
+
// detected at runtime, not just warned about in the docstring. A second
|
|
38
|
+
// call would stack patches and double-log every chunk.
|
|
39
|
+
const INIT_MARKER = Symbol.for("maxy.mcpStderrTee.installed");
|
|
14
40
|
/**
|
|
15
|
-
* Patch process.stderr.write to tee
|
|
41
|
+
* Patch process.stderr.write to tee to both a per-server raw log and the
|
|
42
|
+
* unified claude-agent-stream log (with a [mcp:<serverName>] prefix).
|
|
16
43
|
*
|
|
17
44
|
* Reads LOG_DIR from environment. When absent (dev mode, tool discovery),
|
|
18
|
-
* does nothing — stderr
|
|
45
|
+
* does nothing — stderr works as before with no markers emitted.
|
|
19
46
|
*
|
|
20
|
-
*
|
|
47
|
+
* Safe to call once at MCP server module load. Not safe to call twice —
|
|
48
|
+
* repeated calls would stack patches and double-log every chunk.
|
|
21
49
|
*/
|
|
22
50
|
function initStderrTee(serverName) {
|
|
23
51
|
const logDir = process.env.LOG_DIR;
|
|
24
52
|
if (!logDir)
|
|
25
53
|
return; // Dev mode or tool discovery — stderr only
|
|
26
|
-
|
|
54
|
+
// Refuse repeat patches — stacking them would double-log every chunk
|
|
55
|
+
// and corrupt the re-entrancy guard (originalWrite would capture the
|
|
56
|
+
// already-patched function on the second call).
|
|
57
|
+
if (process.stderr.write[INIT_MARKER])
|
|
58
|
+
return;
|
|
59
|
+
// Keep a direct reference to the unpatched writer. All diagnostic output
|
|
60
|
+
// from inside this module MUST go through originalWrite to avoid
|
|
61
|
+
// re-entering the patched writer (which would recurse on a tee failure).
|
|
62
|
+
const originalWrite = process.stderr.write.bind(process.stderr);
|
|
63
|
+
// Per-server raw-chunk file (Task 362). Opened once, never rotated —
|
|
64
|
+
// preserves existing behaviour.
|
|
65
|
+
let perServerStream;
|
|
27
66
|
try {
|
|
28
67
|
(0, node_fs_1.mkdirSync)(logDir, { recursive: true });
|
|
29
68
|
const date = new Date().toISOString().slice(0, 10);
|
|
30
|
-
|
|
69
|
+
perServerStream = (0, node_fs_1.createWriteStream)((0, node_path_1.resolve)(logDir, `mcp-${serverName}-stderr-${date}.log`), { flags: "a" });
|
|
70
|
+
perServerStream.on("error", (err) => {
|
|
71
|
+
originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee per-server write error server=${serverName} reason=${err.message}\n`);
|
|
72
|
+
});
|
|
31
73
|
}
|
|
32
74
|
catch (err) {
|
|
33
75
|
const msg = err instanceof Error ? err.message : String(err);
|
|
34
|
-
|
|
76
|
+
originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee disabled reason=${msg} server=${serverName} destination=per-server\n`);
|
|
77
|
+
// If the per-server file can't be opened, the LOG_DIR itself is likely
|
|
78
|
+
// unusable — give up on the whole tee rather than half-install it.
|
|
35
79
|
return;
|
|
36
80
|
}
|
|
37
|
-
|
|
81
|
+
// Stream-log destination (Task 524). Lazily re-opened on date boundary so
|
|
82
|
+
// a long-lived MCP server started yesterday routes today's output into
|
|
83
|
+
// today's stream-log file, matching the platform's rotation semantics.
|
|
84
|
+
let streamLogStream;
|
|
85
|
+
let streamLogDate = ""; // YYYY-MM-DD the current stream was opened for
|
|
86
|
+
let streamLogDisabledReason;
|
|
87
|
+
const openStreamLog = () => {
|
|
88
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
89
|
+
if (streamLogStream && streamLogDate === today)
|
|
90
|
+
return streamLogStream;
|
|
91
|
+
// Date boundary crossed (or first open). Close the old stream before
|
|
92
|
+
// opening the new one so the file descriptor is released. Also clear
|
|
93
|
+
// any prior disabled reason — the previous failure may have been
|
|
94
|
+
// transient (EMFILE, ENOSPC), and the new day's path is fresh.
|
|
95
|
+
if (streamLogStream && streamLogDate !== today) {
|
|
96
|
+
try {
|
|
97
|
+
streamLogStream.end();
|
|
98
|
+
}
|
|
99
|
+
catch { /* ignore */ }
|
|
100
|
+
streamLogStream = undefined;
|
|
101
|
+
streamLogDisabledReason = undefined;
|
|
102
|
+
}
|
|
103
|
+
if (streamLogDisabledReason)
|
|
104
|
+
return undefined;
|
|
105
|
+
const path = (0, node_path_1.resolve)(logDir, `claude-agent-stream-${today}.log`);
|
|
106
|
+
try {
|
|
107
|
+
const s = (0, node_fs_1.createWriteStream)(path, { flags: "a" });
|
|
108
|
+
s.on("error", (err) => {
|
|
109
|
+
originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee stream-log write error server=${serverName} reason=${err.message}\n`);
|
|
110
|
+
});
|
|
111
|
+
streamLogStream = s;
|
|
112
|
+
streamLogDate = today;
|
|
113
|
+
// Startup marker lands in the stream log itself so investigators reading
|
|
114
|
+
// a session's stream-log file can confirm the tee was wired up.
|
|
115
|
+
s.write(`[${new Date().toISOString()}] [platform] attaching mcp stream tee for server=${serverName} logPath=${path}\n`);
|
|
116
|
+
return s;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
120
|
+
streamLogDisabledReason = msg;
|
|
121
|
+
const line = `[${new Date().toISOString()}] [platform] mcp stream tee disabled reason=${msg} server=${serverName} destination=stream-log\n`;
|
|
122
|
+
originalWrite(line);
|
|
123
|
+
if (perServerStream && !perServerStream.destroyed)
|
|
124
|
+
perServerStream.write(line);
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
// Prime the stream log once so the startup marker appears immediately
|
|
129
|
+
// rather than waiting for the first stderr chunk.
|
|
130
|
+
openStreamLog();
|
|
131
|
+
// Line buffer — accumulates characters written to stderr so complete
|
|
132
|
+
// newline-terminated lines can be prefixed and emitted to the stream log.
|
|
133
|
+
// Partial chunks are held until a newline arrives (or beforeExit flushes).
|
|
134
|
+
let lineBuffer = "";
|
|
135
|
+
// StringDecoder buffers trailing UTF-8 continuation bytes across chunks.
|
|
136
|
+
// Without it, a multi-byte codepoint that straddles a write boundary would
|
|
137
|
+
// render as two U+FFFD replacement characters in the stream log. Only
|
|
138
|
+
// matters when a caller writes Uint8Array chunks directly — console.error
|
|
139
|
+
// produces strings — but plugins piping child-process stderr can hit this.
|
|
140
|
+
const utf8 = new node_string_decoder_1.StringDecoder("utf8");
|
|
141
|
+
const emitCompleteLinesToStreamLog = (chunk) => {
|
|
142
|
+
lineBuffer += chunk;
|
|
143
|
+
let newlineIndex;
|
|
144
|
+
// eslint-disable-next-line no-cond-assign
|
|
145
|
+
while ((newlineIndex = lineBuffer.indexOf("\n")) !== -1) {
|
|
146
|
+
const line = lineBuffer.slice(0, newlineIndex);
|
|
147
|
+
lineBuffer = lineBuffer.slice(newlineIndex + 1);
|
|
148
|
+
if (line.length === 0)
|
|
149
|
+
continue; // skip blank lines
|
|
150
|
+
const stream = openStreamLog();
|
|
151
|
+
if (!stream || stream.destroyed || stream.writableEnded)
|
|
152
|
+
continue;
|
|
153
|
+
stream.write(`[${new Date().toISOString()}] [mcp:${serverName}] ${line}\n`);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
38
156
|
process.stderr.write = (chunk, ...args) => {
|
|
39
|
-
|
|
40
|
-
|
|
157
|
+
// 1. Per-server raw file — preserves Task 362 behaviour (no prefix).
|
|
158
|
+
if (perServerStream && !perServerStream.destroyed) {
|
|
159
|
+
perServerStream.write(chunk);
|
|
41
160
|
}
|
|
161
|
+
// 2. Stream log — per-line, prefixed, date-rotating.
|
|
162
|
+
try {
|
|
163
|
+
const text = typeof chunk === "string"
|
|
164
|
+
? chunk
|
|
165
|
+
: utf8.write(Buffer.from(chunk));
|
|
166
|
+
emitCompleteLinesToStreamLog(text);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
170
|
+
originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee emit error server=${serverName} reason=${msg}\n`);
|
|
171
|
+
}
|
|
172
|
+
// 3. Original stderr — Claude Code still gets what it's always got.
|
|
42
173
|
return originalWrite(chunk, ...args);
|
|
43
174
|
};
|
|
175
|
+
// Mark the patch so a second initStderrTee() call is refused at the top.
|
|
176
|
+
process.stderr.write[INIT_MARKER] = true;
|
|
177
|
+
// Flush any trailing unterminated segment on graceful event-loop drain.
|
|
178
|
+
// `beforeExit` fires only when the event loop is empty — it does NOT run
|
|
179
|
+
// on SIGTERM, SIGKILL, or explicit process.exit(). Acceptable: console.error
|
|
180
|
+
// always terminates with \n, so the buffer is effectively always empty at
|
|
181
|
+
// shutdown anyway. This hook exists for the rare caller that uses
|
|
182
|
+
// process.stderr.write without a trailing newline.
|
|
183
|
+
process.on("beforeExit", () => {
|
|
184
|
+
if (lineBuffer.length === 0)
|
|
185
|
+
return;
|
|
186
|
+
const stream = openStreamLog();
|
|
187
|
+
if (stream && !stream.destroyed && !stream.writableEnded) {
|
|
188
|
+
stream.write(`[${new Date().toISOString()}] [mcp:${serverName}] ${lineBuffer}\n`);
|
|
189
|
+
}
|
|
190
|
+
lineBuffer = "";
|
|
191
|
+
});
|
|
44
192
|
}
|
|
45
193
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|