@aisystemresources/emdee 0.3.0 → 0.3.2
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 +2 -2
- package/package.json +1 -1
- package/src/cli/remote-client.ts +25 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Emdee
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A markdown knowledge graph shared by humans and their AI agents. Files you can edit like Obsidian, a vault you can share like Google Drive, and a live MCP endpoint every agent (Claude Code, Claude.ai, Cursor, Codex) reads and writes to natively. One vault, three surfaces — with the plain markdown you wrote as the only source of truth.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Every knowledge tool picks two of three: human-friendly, team-shareable, agent-native. Notion is human-friendly and shareable, but its blocks aren't markdown and agents can't natively touch them. Obsidian is human-friendly and agent-touchable (they're just markdown files), but private-by-default — sharing is a paid add-on or a git repo. Google Drive is shareable but not markdown-native. Emdee is all three: the markdown a human edits is the exact bytes an agent reads through MCP and a teammate reads through a share link — no hidden index, no parallel summaries, no schema gymnastics. Build up a working journal that survives across sessions and travels with the people you invite in.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
package/package.json
CHANGED
package/src/cli/remote-client.ts
CHANGED
|
@@ -37,6 +37,9 @@ export async function callTool(
|
|
|
37
37
|
method: "POST",
|
|
38
38
|
headers: {
|
|
39
39
|
"Content-Type": "application/json",
|
|
40
|
+
// MCP streamable-HTTP transport requires BOTH content types in Accept
|
|
41
|
+
// or it 406s with "Client must accept both application/json and
|
|
42
|
+
// text/event-stream". We handle whichever shape the server picks below.
|
|
40
43
|
"Accept": "application/json, text/event-stream",
|
|
41
44
|
Authorization: `Bearer ${creds.access_token}`,
|
|
42
45
|
},
|
|
@@ -54,12 +57,33 @@ export async function callTool(
|
|
|
54
57
|
throw new Error(`remote call failed: ${res.status} ${body}`);
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const body =
|
|
60
|
+
const body = await parseJsonRpc(res);
|
|
58
61
|
if (body.error) throw new Error(`remote tool error: ${body.error.message}`);
|
|
59
62
|
if (!body.result) throw new Error("remote call returned no result");
|
|
60
63
|
return body.result;
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
// The MCP streamable-HTTP transport content-negotiates on the request Accept:
|
|
67
|
+
// when the client offers both JSON and SSE (as required — see 406 above),
|
|
68
|
+
// the server may reply with either. JSON we parse directly; SSE arrives as
|
|
69
|
+
// one or more `event: message\ndata: <json>\n\n` frames — for a single
|
|
70
|
+
// tools/call response we take the first `data:` payload.
|
|
71
|
+
async function parseJsonRpc(res: Response): Promise<JsonRpcResponse> {
|
|
72
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
73
|
+
if (ct.includes("text/event-stream")) {
|
|
74
|
+
const text = await res.text();
|
|
75
|
+
const dataLine = text.split(/\r?\n/).find((l) => l.startsWith("data:"));
|
|
76
|
+
if (!dataLine) throw new Error(`remote call returned SSE with no data frame: ${text.slice(0, 200)}`);
|
|
77
|
+
const payload = dataLine.slice("data:".length).trim();
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(payload) as JsonRpcResponse;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
throw new Error(`remote call SSE data frame is not valid JSON: ${(e as Error).message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return (await res.json()) as JsonRpcResponse;
|
|
85
|
+
}
|
|
86
|
+
|
|
63
87
|
/**
|
|
64
88
|
* Convenience: unwrap the MCP text envelope back to a string. Tools that
|
|
65
89
|
* return JSON encode it inside `content[0].text`; tools that return plain
|