anyclaude-sdk 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/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/agent.d.ts +110 -0
- package/dist/agent.js +897 -0
- package/dist/background/index.d.ts +3 -0
- package/dist/background/index.js +9 -0
- package/dist/background/manager.d.ts +32 -0
- package/dist/background/manager.js +108 -0
- package/dist/background/tools.d.ts +5 -0
- package/dist/background/tools.js +98 -0
- package/dist/background/worker.d.ts +19 -0
- package/dist/background/worker.js +30 -0
- package/dist/commands/builtins.d.ts +2 -0
- package/dist/commands/builtins.js +306 -0
- package/dist/commands/index.d.ts +21 -0
- package/dist/commands/index.js +56 -0
- package/dist/commands/types.d.ts +110 -0
- package/dist/commands/types.js +5 -0
- package/dist/compact.d.ts +22 -0
- package/dist/compact.js +67 -0
- package/dist/fs/dexie.d.ts +57 -0
- package/dist/fs/dexie.js +243 -0
- package/dist/fs/index.d.ts +4 -0
- package/dist/fs/index.js +13 -0
- package/dist/fs/linuxTree.d.ts +11 -0
- package/dist/fs/linuxTree.js +43 -0
- package/dist/fs/opfs.d.ts +23 -0
- package/dist/fs/opfs.js +112 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +29 -0
- package/dist/llm/anthropic.d.ts +24 -0
- package/dist/llm/anthropic.js +280 -0
- package/dist/llm/index.d.ts +3 -0
- package/dist/llm/index.js +3 -0
- package/dist/llm/inlineTools.d.ts +11 -0
- package/dist/llm/inlineTools.js +72 -0
- package/dist/llm/openai.d.ts +29 -0
- package/dist/llm/openai.js +224 -0
- package/dist/llm/responses.d.ts +18 -0
- package/dist/llm/responses.js +256 -0
- package/dist/mcp/client.d.ts +20 -0
- package/dist/mcp/client.js +156 -0
- package/dist/mcp/index.d.ts +24 -0
- package/dist/mcp/index.js +157 -0
- package/dist/mcp/proxy.d.ts +3 -0
- package/dist/mcp/proxy.js +25 -0
- package/dist/mcp/sdkServer.d.ts +21 -0
- package/dist/mcp/sdkServer.js +28 -0
- package/dist/mcp/types.d.ts +92 -0
- package/dist/mcp/types.js +5 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/render.d.ts +7 -0
- package/dist/memory/render.js +46 -0
- package/dist/memory/store.d.ts +20 -0
- package/dist/memory/store.js +79 -0
- package/dist/memory/tools.d.ts +5 -0
- package/dist/memory/tools.js +95 -0
- package/dist/memory/types.d.ts +15 -0
- package/dist/memory/types.js +4 -0
- package/dist/permissions/dangerous.d.ts +4 -0
- package/dist/permissions/dangerous.js +24 -0
- package/dist/permissions/gate.d.ts +21 -0
- package/dist/permissions/gate.js +66 -0
- package/dist/permissions/index.d.ts +5 -0
- package/dist/permissions/index.js +6 -0
- package/dist/permissions/match.d.ts +19 -0
- package/dist/permissions/match.js +104 -0
- package/dist/permissions/planMode.d.ts +3 -0
- package/dist/permissions/planMode.js +33 -0
- package/dist/permissions/types.d.ts +19 -0
- package/dist/permissions/types.js +2 -0
- package/dist/persist.d.ts +15 -0
- package/dist/persist.js +58 -0
- package/dist/prompt.d.ts +6 -0
- package/dist/prompt.js +34 -0
- package/dist/query.d.ts +105 -0
- package/dist/query.js +115 -0
- package/dist/queue.d.ts +23 -0
- package/dist/queue.js +43 -0
- package/dist/sandbox/cloudflare.d.ts +48 -0
- package/dist/sandbox/cloudflare.js +124 -0
- package/dist/sandbox/daytona.d.ts +48 -0
- package/dist/sandbox/daytona.js +79 -0
- package/dist/sandbox/e2b.d.ts +54 -0
- package/dist/sandbox/e2b.js +87 -0
- package/dist/sandbox/index.d.ts +8 -0
- package/dist/sandbox/index.js +19 -0
- package/dist/sandbox/local.d.ts +51 -0
- package/dist/sandbox/local.js +155 -0
- package/dist/sandbox/types.d.ts +18 -0
- package/dist/sandbox/types.js +27 -0
- package/dist/sandbox/util.d.ts +15 -0
- package/dist/sandbox/util.js +100 -0
- package/dist/sandbox/vercel.d.ts +48 -0
- package/dist/sandbox/vercel.js +130 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +6 -0
- package/dist/session/store.d.ts +28 -0
- package/dist/session/store.js +122 -0
- package/dist/session/types.d.ts +22 -0
- package/dist/session/types.js +2 -0
- package/dist/settings/index.d.ts +3 -0
- package/dist/settings/index.js +3 -0
- package/dist/settings/load.d.ts +20 -0
- package/dist/settings/load.js +36 -0
- package/dist/settings/merge.d.ts +13 -0
- package/dist/settings/merge.js +65 -0
- package/dist/settings/types.d.ts +17 -0
- package/dist/settings/types.js +3 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +5 -0
- package/dist/skills/load.d.ts +23 -0
- package/dist/skills/load.js +54 -0
- package/dist/skills/parse.d.ts +7 -0
- package/dist/skills/parse.js +40 -0
- package/dist/skills/tool.d.ts +2 -0
- package/dist/skills/tool.js +39 -0
- package/dist/skills/types.d.ts +10 -0
- package/dist/skills/types.js +4 -0
- package/dist/team/dispatch.d.ts +2 -0
- package/dist/team/dispatch.js +41 -0
- package/dist/team/index.d.ts +9 -0
- package/dist/team/index.js +11 -0
- package/dist/team/mailbox.d.ts +24 -0
- package/dist/team/mailbox.js +33 -0
- package/dist/team/prompt.d.ts +1 -0
- package/dist/team/prompt.js +12 -0
- package/dist/team/runner.d.ts +20 -0
- package/dist/team/runner.js +45 -0
- package/dist/team/taskBoard.d.ts +41 -0
- package/dist/team/taskBoard.js +73 -0
- package/dist/team/tools.d.ts +7 -0
- package/dist/team/tools.js +190 -0
- package/dist/tools/bash.d.ts +2 -0
- package/dist/tools/bash.js +45 -0
- package/dist/tools/config.d.ts +2 -0
- package/dist/tools/config.js +44 -0
- package/dist/tools/define.d.ts +18 -0
- package/dist/tools/define.js +21 -0
- package/dist/tools/delete_file.d.ts +2 -0
- package/dist/tools/delete_file.js +33 -0
- package/dist/tools/edit_file.d.ts +2 -0
- package/dist/tools/edit_file.js +93 -0
- package/dist/tools/fileTypes.d.ts +32 -0
- package/dist/tools/fileTypes.js +166 -0
- package/dist/tools/glob.d.ts +2 -0
- package/dist/tools/glob.js +53 -0
- package/dist/tools/grep.d.ts +2 -0
- package/dist/tools/grep.js +110 -0
- package/dist/tools/imageProcessor.d.ts +15 -0
- package/dist/tools/imageProcessor.js +83 -0
- package/dist/tools/index.d.ts +28 -0
- package/dist/tools/index.js +45 -0
- package/dist/tools/list_files.d.ts +2 -0
- package/dist/tools/list_files.js +42 -0
- package/dist/tools/multi_edit.d.ts +2 -0
- package/dist/tools/multi_edit.js +112 -0
- package/dist/tools/notebook_edit.d.ts +2 -0
- package/dist/tools/notebook_edit.js +118 -0
- package/dist/tools/plan_mode.d.ts +4 -0
- package/dist/tools/plan_mode.js +44 -0
- package/dist/tools/read_file.d.ts +2 -0
- package/dist/tools/read_file.js +193 -0
- package/dist/tools/task.d.ts +2 -0
- package/dist/tools/task.js +77 -0
- package/dist/tools/todo_write.d.ts +2 -0
- package/dist/tools/todo_write.js +104 -0
- package/dist/tools/tool_search.d.ts +2 -0
- package/dist/tools/tool_search.js +49 -0
- package/dist/tools/types.d.ts +82 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/walk.d.ts +29 -0
- package/dist/tools/walk.js +82 -0
- package/dist/tools/web_fetch.d.ts +2 -0
- package/dist/tools/web_fetch.js +76 -0
- package/dist/tools/web_search.d.ts +22 -0
- package/dist/tools/web_search.js +195 -0
- package/dist/tools/write_file.d.ts +2 -0
- package/dist/tools/write_file.js +39 -0
- package/dist/types/index.d.ts +363 -0
- package/dist/types/index.js +9 -0
- package/dist/util/ids.d.ts +3 -0
- package/dist/util/ids.js +22 -0
- package/dist/util/paths.d.ts +16 -0
- package/dist/util/paths.js +72 -0
- package/dist/util/pricing.d.ts +15 -0
- package/dist/util/pricing.js +81 -0
- package/dist/workspace/index.d.ts +2 -0
- package/dist/workspace/index.js +2 -0
- package/dist/workspace/memory.d.ts +28 -0
- package/dist/workspace/memory.js +97 -0
- package/dist/workspace/webcontainer.d.ts +65 -0
- package/dist/workspace/webcontainer.js +156 -0
- package/package.json +78 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Tool } from '../tools/types.js';
|
|
2
|
+
import type { McpServers, McpServerStatus } from './types.js';
|
|
3
|
+
export * from './types.js';
|
|
4
|
+
export { McpClient } from './client.js';
|
|
5
|
+
export { applyProxy, type McpProxy } from './proxy.js';
|
|
6
|
+
export { tool, createSdkMcpServer } from './sdkServer.js';
|
|
7
|
+
import type { McpProxy } from './proxy.js';
|
|
8
|
+
/** Options for loadMcpServers. */
|
|
9
|
+
export interface LoadMcpOptions {
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
/** Route remote (http/sse) MCP requests through a proxy (browser CORS). */
|
|
12
|
+
proxy?: McpProxy;
|
|
13
|
+
}
|
|
14
|
+
/** Tool name namespacing: mcp__<server>__<tool>, matching the official SDK. */
|
|
15
|
+
export declare function mcpToolName(server: string, toolName: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Load every configured MCP server and return their tools (wrapped as native
|
|
18
|
+
* Tools) plus per-server connection status. Never throws — a server that fails
|
|
19
|
+
* to connect contributes a 'failed' status and no tools.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadMcpServers(servers: McpServers, opts?: AbortSignal | LoadMcpOptions): Promise<{
|
|
22
|
+
tools: Tool[];
|
|
23
|
+
statuses: McpServerStatus[];
|
|
24
|
+
}>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// MCP integration: load tools from external (HTTP/SSE) and in-process (SDK)
|
|
2
|
+
// MCP servers, wrapped as native browser-claude-sdk `Tool`s so they drop
|
|
3
|
+
// straight into the agent's tool list.
|
|
4
|
+
import { McpClient } from './client.js';
|
|
5
|
+
export * from './types.js';
|
|
6
|
+
export { McpClient } from './client.js';
|
|
7
|
+
export { applyProxy } from './proxy.js';
|
|
8
|
+
export { tool, createSdkMcpServer } from './sdkServer.js';
|
|
9
|
+
/** Tool name namespacing: mcp__<server>__<tool>, matching the official SDK. */
|
|
10
|
+
export function mcpToolName(server, toolName) {
|
|
11
|
+
return `mcp__${server}__${toolName}`;
|
|
12
|
+
}
|
|
13
|
+
/** Coerce an MCP inputSchema into our OpenAI-shape parameters object. */
|
|
14
|
+
function toToolParameters(schema) {
|
|
15
|
+
const s = schema ?? {};
|
|
16
|
+
return {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: s.properties ?? {},
|
|
19
|
+
...(Array.isArray(s.required) ? { required: s.required } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Map an MCP tool result's content blocks to our ContentBlockParam[]. */
|
|
23
|
+
function mapMcpContent(result) {
|
|
24
|
+
const blocks = [];
|
|
25
|
+
for (const c of result.content ?? []) {
|
|
26
|
+
const block = c;
|
|
27
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
28
|
+
blocks.push({ type: 'text', text: block.text });
|
|
29
|
+
}
|
|
30
|
+
else if (block.type === 'image' || block.type === 'audio') {
|
|
31
|
+
const b = block;
|
|
32
|
+
if (typeof b.data === 'string') {
|
|
33
|
+
// Audio isn't a first-class block for us; surface images natively and
|
|
34
|
+
// note audio as text so nothing is silently dropped.
|
|
35
|
+
if (block.type === 'image') {
|
|
36
|
+
blocks.push({
|
|
37
|
+
type: 'image',
|
|
38
|
+
source: { type: 'base64', media_type: b.mimeType ?? 'image/png', data: b.data },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
blocks.push({ type: 'text', text: `[audio: ${b.mimeType ?? 'audio'} omitted]` });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (block.type === 'resource') {
|
|
47
|
+
const r = block.resource;
|
|
48
|
+
if (r?.text)
|
|
49
|
+
blocks.push({ type: 'text', text: r.text });
|
|
50
|
+
else if (r?.uri)
|
|
51
|
+
blocks.push({ type: 'text', text: `[resource: ${r.uri}]` });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Unknown block kind — stringify so it isn't lost.
|
|
55
|
+
try {
|
|
56
|
+
blocks.push({ type: 'text', text: JSON.stringify(block) });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
/* skip */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!blocks.length)
|
|
64
|
+
blocks.push({ type: 'text', text: '(no content)' });
|
|
65
|
+
return { content: blocks, isError: result.isError || undefined };
|
|
66
|
+
}
|
|
67
|
+
function wrapSdkTool(serverName, t) {
|
|
68
|
+
return {
|
|
69
|
+
def: {
|
|
70
|
+
type: 'function',
|
|
71
|
+
function: {
|
|
72
|
+
name: mcpToolName(serverName, t.name),
|
|
73
|
+
description: t.description,
|
|
74
|
+
parameters: toToolParameters(t.inputSchema),
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
async run(input) {
|
|
78
|
+
try {
|
|
79
|
+
const res = await t.handler(input);
|
|
80
|
+
return mapMcpContent(res);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return {
|
|
84
|
+
content: `MCP error: ${err instanceof Error ? err.message : String(err)}`,
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function wrapRemoteTool(serverName, client, info) {
|
|
92
|
+
return {
|
|
93
|
+
def: {
|
|
94
|
+
type: 'function',
|
|
95
|
+
function: {
|
|
96
|
+
name: mcpToolName(serverName, info.name),
|
|
97
|
+
description: info.description ?? `MCP tool ${info.name}`,
|
|
98
|
+
parameters: toToolParameters(info.inputSchema),
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
async run(input, ctx) {
|
|
102
|
+
try {
|
|
103
|
+
const res = await client.callTool(info.name, input, ctx.signal);
|
|
104
|
+
return mapMcpContent(res);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
return {
|
|
108
|
+
content: `MCP error: ${err instanceof Error ? err.message : String(err)}`,
|
|
109
|
+
isError: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Load every configured MCP server and return their tools (wrapped as native
|
|
117
|
+
* Tools) plus per-server connection status. Never throws — a server that fails
|
|
118
|
+
* to connect contributes a 'failed' status and no tools.
|
|
119
|
+
*/
|
|
120
|
+
export async function loadMcpServers(servers, opts = {}) {
|
|
121
|
+
// Back-compat: accept a bare AbortSignal as the second arg.
|
|
122
|
+
const options = opts && typeof opts.aborted === 'boolean'
|
|
123
|
+
? { signal: opts }
|
|
124
|
+
: opts;
|
|
125
|
+
const signal = options.signal;
|
|
126
|
+
const proxy = options.proxy;
|
|
127
|
+
const tools = [];
|
|
128
|
+
const statuses = [];
|
|
129
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
130
|
+
if (config.type === 'sdk') {
|
|
131
|
+
const wrapped = config.server.tools.map((t) => wrapSdkTool(name, t));
|
|
132
|
+
tools.push(...wrapped);
|
|
133
|
+
statuses.push({
|
|
134
|
+
name,
|
|
135
|
+
status: 'connected',
|
|
136
|
+
tools: config.server.tools.map((t) => t.name),
|
|
137
|
+
});
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
// Remote (http / sse).
|
|
141
|
+
try {
|
|
142
|
+
const client = new McpClient(config, name, proxy);
|
|
143
|
+
await client.connect(signal);
|
|
144
|
+
const infos = await client.listTools(signal);
|
|
145
|
+
tools.push(...infos.map((info) => wrapRemoteTool(name, client, info)));
|
|
146
|
+
statuses.push({ name, status: 'connected', tools: infos.map((i) => i.name) });
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
statuses.push({
|
|
150
|
+
name,
|
|
151
|
+
status: 'failed',
|
|
152
|
+
error: err instanceof Error ? err.message : String(err),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { tools, statuses };
|
|
157
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// MCP request proxying. Browsers block direct cross-origin fetches to most MCP
|
|
2
|
+
// servers (no permissive CORS), so the SDK lets callers route MCP requests
|
|
3
|
+
// through a proxy of their choosing.
|
|
4
|
+
//
|
|
5
|
+
// Forms accepted:
|
|
6
|
+
// - function: (targetUrl) => requestUrl full control
|
|
7
|
+
// - string with `{url}`: 'https://cors.example/?u={url}' (URL-encoded)
|
|
8
|
+
// - string with `{rawUrl}':'https://cors.example/{rawUrl}' (raw)
|
|
9
|
+
// - bare prefix: 'https://cors.example/' (raw URL appended)
|
|
10
|
+
//
|
|
11
|
+
// The proxy must forward the request (method, body, headers — including
|
|
12
|
+
// `Mcp-Session-Id` and `Content-Type`) to the target and echo permissive CORS
|
|
13
|
+
// headers and the `Mcp-Session-Id` response header back.
|
|
14
|
+
/** Resolve the actual request URL for a target MCP endpoint given a proxy. */
|
|
15
|
+
export function applyProxy(targetUrl, proxy) {
|
|
16
|
+
if (!proxy)
|
|
17
|
+
return targetUrl;
|
|
18
|
+
if (typeof proxy === 'function')
|
|
19
|
+
return proxy(targetUrl);
|
|
20
|
+
if (proxy.includes('{url}'))
|
|
21
|
+
return proxy.replace('{url}', encodeURIComponent(targetUrl));
|
|
22
|
+
if (proxy.includes('{rawUrl}'))
|
|
23
|
+
return proxy.replace('{rawUrl}', targetUrl);
|
|
24
|
+
return proxy + targetUrl;
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpSdkServerConfig, McpToolResult, SdkMcpTool } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ergonomic builder for an in-process MCP tool.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* tool('add', 'Add two numbers',
|
|
7
|
+
* { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a','b'] },
|
|
8
|
+
* async ({ a, b }) => ({ content: [{ type: 'text', text: String((a as number) + (b as number)) }] })
|
|
9
|
+
* )
|
|
10
|
+
*/
|
|
11
|
+
export declare function tool(name: string, description: string, inputSchema: SdkMcpTool['inputSchema'], handler: (args: Record<string, unknown>) => Promise<McpToolResult> | McpToolResult): SdkMcpTool;
|
|
12
|
+
/**
|
|
13
|
+
* Create an in-process MCP server config that can be passed to query() via
|
|
14
|
+
* `mcpServers`. Its tools run locally and appear to the model as
|
|
15
|
+
* `mcp__<name>__<toolName>`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createSdkMcpServer(opts: {
|
|
18
|
+
name: string;
|
|
19
|
+
version?: string;
|
|
20
|
+
tools: SdkMcpTool[];
|
|
21
|
+
}): McpSdkServerConfig;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// In-process ("SDK") MCP servers. Define tools in plain JS that run in the same
|
|
2
|
+
// process as the agent — no network, no transport. Mirrors the official SDK's
|
|
3
|
+
// createSdkMcpServer / tool() helpers.
|
|
4
|
+
/**
|
|
5
|
+
* Ergonomic builder for an in-process MCP tool.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* tool('add', 'Add two numbers',
|
|
9
|
+
* { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a','b'] },
|
|
10
|
+
* async ({ a, b }) => ({ content: [{ type: 'text', text: String((a as number) + (b as number)) }] })
|
|
11
|
+
* )
|
|
12
|
+
*/
|
|
13
|
+
export function tool(name, description, inputSchema, handler) {
|
|
14
|
+
return { name, description, inputSchema, handler };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create an in-process MCP server config that can be passed to query() via
|
|
18
|
+
* `mcpServers`. Its tools run locally and appear to the model as
|
|
19
|
+
* `mcp__<name>__<toolName>`.
|
|
20
|
+
*/
|
|
21
|
+
export function createSdkMcpServer(opts) {
|
|
22
|
+
const server = {
|
|
23
|
+
name: opts.name,
|
|
24
|
+
version: opts.version,
|
|
25
|
+
tools: opts.tools,
|
|
26
|
+
};
|
|
27
|
+
return { type: 'sdk', name: opts.name, server };
|
|
28
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export interface McpHttpServerConfig {
|
|
2
|
+
type: 'http';
|
|
3
|
+
url: string;
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export interface McpSSEServerConfig {
|
|
7
|
+
type: 'sse';
|
|
8
|
+
url: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export interface McpSdkServerConfig {
|
|
12
|
+
type: 'sdk';
|
|
13
|
+
name: string;
|
|
14
|
+
server: SdkMcpServer;
|
|
15
|
+
}
|
|
16
|
+
export type McpServerConfig = McpHttpServerConfig | McpSSEServerConfig | McpSdkServerConfig;
|
|
17
|
+
/** Map of server name → configuration. The key is the server's name. */
|
|
18
|
+
export type McpServers = Record<string, McpServerConfig>;
|
|
19
|
+
export interface SdkMcpTool {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object';
|
|
24
|
+
properties?: Record<string, unknown>;
|
|
25
|
+
required?: string[];
|
|
26
|
+
};
|
|
27
|
+
handler(args: Record<string, unknown>): Promise<McpToolResult> | McpToolResult;
|
|
28
|
+
}
|
|
29
|
+
export interface SdkMcpServer {
|
|
30
|
+
name: string;
|
|
31
|
+
version?: string;
|
|
32
|
+
tools: SdkMcpTool[];
|
|
33
|
+
}
|
|
34
|
+
export interface JsonRpcRequest {
|
|
35
|
+
jsonrpc: '2.0';
|
|
36
|
+
id?: number | string;
|
|
37
|
+
method: string;
|
|
38
|
+
params?: unknown;
|
|
39
|
+
}
|
|
40
|
+
export interface JsonRpcResponse<T = unknown> {
|
|
41
|
+
jsonrpc: '2.0';
|
|
42
|
+
id: number | string | null;
|
|
43
|
+
result?: T;
|
|
44
|
+
error?: {
|
|
45
|
+
code: number;
|
|
46
|
+
message: string;
|
|
47
|
+
data?: unknown;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface McpToolInfo {
|
|
51
|
+
name: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type?: string;
|
|
55
|
+
properties?: Record<string, unknown>;
|
|
56
|
+
required?: string[];
|
|
57
|
+
[k: string]: unknown;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export type McpContentBlock = {
|
|
61
|
+
type: 'text';
|
|
62
|
+
text: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'image';
|
|
65
|
+
data: string;
|
|
66
|
+
mimeType: string;
|
|
67
|
+
} | {
|
|
68
|
+
type: 'audio';
|
|
69
|
+
data: string;
|
|
70
|
+
mimeType: string;
|
|
71
|
+
} | {
|
|
72
|
+
type: 'resource';
|
|
73
|
+
resource: {
|
|
74
|
+
uri?: string;
|
|
75
|
+
text?: string;
|
|
76
|
+
mimeType?: string;
|
|
77
|
+
blob?: string;
|
|
78
|
+
};
|
|
79
|
+
} | {
|
|
80
|
+
type: string;
|
|
81
|
+
[k: string]: unknown;
|
|
82
|
+
};
|
|
83
|
+
export interface McpToolResult {
|
|
84
|
+
content: McpContentBlock[];
|
|
85
|
+
isError?: boolean;
|
|
86
|
+
}
|
|
87
|
+
export interface McpServerStatus {
|
|
88
|
+
name: string;
|
|
89
|
+
status: 'connected' | 'failed' | 'pending';
|
|
90
|
+
error?: string;
|
|
91
|
+
tools?: string[];
|
|
92
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Persistent memory system: durable user/feedback/project/reference memories
|
|
2
|
+
// (IndexedDB-backed) that load into the system prompt across sessions.
|
|
3
|
+
export { MemoryStore } from './store.js';
|
|
4
|
+
export { renderMemories } from './render.js';
|
|
5
|
+
export { MEMORY_TOOLS, memoryWrite, memoryList, memoryDelete, } from './tools.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MemoryEntry } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Render memory entries into a compact Markdown section for the system prompt.
|
|
4
|
+
* Returns '' for empty input. Caps total length at ~6000 chars, appending a
|
|
5
|
+
* truncation note when entries are dropped.
|
|
6
|
+
*/
|
|
7
|
+
export declare function renderMemories(entries: MemoryEntry[]): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Pure rendering of memory entries into a system-prompt section.
|
|
2
|
+
const MAX_CHARS = 6000;
|
|
3
|
+
const TYPE_ORDER = ['user', 'feedback', 'project', 'reference'];
|
|
4
|
+
/**
|
|
5
|
+
* Render memory entries into a compact Markdown section for the system prompt.
|
|
6
|
+
* Returns '' for empty input. Caps total length at ~6000 chars, appending a
|
|
7
|
+
* truncation note when entries are dropped.
|
|
8
|
+
*/
|
|
9
|
+
export function renderMemories(entries) {
|
|
10
|
+
if (!entries.length)
|
|
11
|
+
return '';
|
|
12
|
+
// Group by type, preserving a stable type ordering.
|
|
13
|
+
const byType = new Map();
|
|
14
|
+
for (const e of entries) {
|
|
15
|
+
const arr = byType.get(e.type) ?? [];
|
|
16
|
+
arr.push(e);
|
|
17
|
+
byType.set(e.type, arr);
|
|
18
|
+
}
|
|
19
|
+
const header = '# Memory\n' +
|
|
20
|
+
'The following are persistent memories from prior sessions. Treat them as ' +
|
|
21
|
+
'background context (true when written) — verify against the current code ' +
|
|
22
|
+
'before relying on specifics.\n';
|
|
23
|
+
const blocks = [];
|
|
24
|
+
let used = header.length;
|
|
25
|
+
let dropped = 0;
|
|
26
|
+
for (const type of TYPE_ORDER) {
|
|
27
|
+
const list = byType.get(type);
|
|
28
|
+
if (!list?.length)
|
|
29
|
+
continue;
|
|
30
|
+
for (const e of list) {
|
|
31
|
+
const block = `\n## ${e.type} — ${e.name}\n${e.description}\n${e.body}\n`;
|
|
32
|
+
if (used + block.length > MAX_CHARS) {
|
|
33
|
+
dropped++;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
blocks.push(block);
|
|
37
|
+
used += block.length;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!blocks.length)
|
|
41
|
+
return '';
|
|
42
|
+
let out = header + blocks.join('');
|
|
43
|
+
if (dropped > 0)
|
|
44
|
+
out += `\n_(+${dropped} more memories omitted to save context)_\n`;
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MemoryEntry, MemoryStoreOptions, MemoryType } from './types.js';
|
|
2
|
+
export declare class MemoryStore {
|
|
3
|
+
private readonly dbName;
|
|
4
|
+
private db;
|
|
5
|
+
private opening;
|
|
6
|
+
constructor(options?: MemoryStoreOptions);
|
|
7
|
+
private open;
|
|
8
|
+
/** Upsert a memory entry by name. */
|
|
9
|
+
save(entry: MemoryEntry): Promise<void>;
|
|
10
|
+
/** Get a memory by name, or null. */
|
|
11
|
+
get(name: string): Promise<MemoryEntry | null>;
|
|
12
|
+
/** List memories (optionally filtered by type), newest updatedAt first. */
|
|
13
|
+
list(type?: MemoryType): Promise<MemoryEntry[]>;
|
|
14
|
+
/** Delete a memory by name. */
|
|
15
|
+
remove(name: string): Promise<void>;
|
|
16
|
+
/** Wipe all memories. */
|
|
17
|
+
clear(): Promise<void>;
|
|
18
|
+
/** Render all memories into a system-prompt section ('' if none). */
|
|
19
|
+
render(): Promise<string>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// MemoryStore — durable, queryable memory backed by IndexedDB via Dexie.
|
|
2
|
+
// Loads persistent memories into the system prompt across sessions.
|
|
3
|
+
//
|
|
4
|
+
// `dexie` is an OPTIONAL peer dependency — imported dynamically so this module
|
|
5
|
+
// loads even when Dexie isn't installed (the error surfaces only on first use).
|
|
6
|
+
import { renderMemories } from './render.js';
|
|
7
|
+
export class MemoryStore {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.db = null;
|
|
10
|
+
this.opening = null;
|
|
11
|
+
this.dbName = options.dbName ?? 'bcs-memory';
|
|
12
|
+
}
|
|
13
|
+
async open() {
|
|
14
|
+
if (this.db)
|
|
15
|
+
return this.db;
|
|
16
|
+
if (this.opening)
|
|
17
|
+
return this.opening;
|
|
18
|
+
this.opening = (async () => {
|
|
19
|
+
// @ts-ignore optional peer dependency, resolved at runtime
|
|
20
|
+
const mod = await import('dexie');
|
|
21
|
+
const Dexie = mod.default ?? mod;
|
|
22
|
+
const db = new Dexie(this.dbName);
|
|
23
|
+
db.version(1).stores({
|
|
24
|
+
memories: 'name, type, updatedAt',
|
|
25
|
+
});
|
|
26
|
+
this.db = db;
|
|
27
|
+
return db;
|
|
28
|
+
})();
|
|
29
|
+
return this.opening;
|
|
30
|
+
}
|
|
31
|
+
/** Upsert a memory entry by name. */
|
|
32
|
+
async save(entry) {
|
|
33
|
+
const db = await this.open();
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
const existing = await db.memories.get(entry.name);
|
|
36
|
+
await db.memories.put({
|
|
37
|
+
...entry,
|
|
38
|
+
createdAt: existing?.createdAt ?? now,
|
|
39
|
+
updatedAt: now,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Get a memory by name, or null. */
|
|
43
|
+
async get(name) {
|
|
44
|
+
try {
|
|
45
|
+
const db = await this.open();
|
|
46
|
+
return (await db.memories.get(name)) ?? null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** List memories (optionally filtered by type), newest updatedAt first. */
|
|
53
|
+
async list(type) {
|
|
54
|
+
try {
|
|
55
|
+
const db = await this.open();
|
|
56
|
+
const rows = await db.memories.toArray();
|
|
57
|
+
return rows
|
|
58
|
+
.filter((r) => !type || r.type === type)
|
|
59
|
+
.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Delete a memory by name. */
|
|
66
|
+
async remove(name) {
|
|
67
|
+
const db = await this.open();
|
|
68
|
+
await db.memories.delete(name);
|
|
69
|
+
}
|
|
70
|
+
/** Wipe all memories. */
|
|
71
|
+
async clear() {
|
|
72
|
+
const db = await this.open();
|
|
73
|
+
await db.memories.clear();
|
|
74
|
+
}
|
|
75
|
+
/** Render all memories into a system-prompt section ('' if none). */
|
|
76
|
+
async render() {
|
|
77
|
+
return renderMemories(await this.list());
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Memory management tools. They read `ctx.memory` (a MemoryStore the agent loop
|
|
2
|
+
// injects); accessed via a cast so this file compiles standalone.
|
|
3
|
+
const TYPES = ['user', 'feedback', 'project', 'reference'];
|
|
4
|
+
function getStore(ctx) {
|
|
5
|
+
return ctx.memory;
|
|
6
|
+
}
|
|
7
|
+
export const memoryWrite = {
|
|
8
|
+
def: {
|
|
9
|
+
type: 'function',
|
|
10
|
+
function: {
|
|
11
|
+
name: 'memory_write',
|
|
12
|
+
description: 'Persist a memory across sessions. Use for durable facts worth remembering: who the user is (user), how to work / corrections (feedback), ongoing project goals/constraints (project), or external references (reference). Re-writing an existing name updates it.',
|
|
13
|
+
parameters: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
name: { type: 'string', description: 'Short unique kebab-case slug.' },
|
|
17
|
+
type: { type: 'string', enum: TYPES, description: 'Memory category.' },
|
|
18
|
+
description: { type: 'string', description: 'One-line summary used for recall.' },
|
|
19
|
+
body: { type: 'string', description: 'The full memory content.' },
|
|
20
|
+
},
|
|
21
|
+
required: ['name', 'type', 'description', 'body'],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
async run(input, ctx) {
|
|
26
|
+
const store = getStore(ctx);
|
|
27
|
+
if (!store)
|
|
28
|
+
return { content: 'Memory is not enabled.', isError: true };
|
|
29
|
+
const name = String(input.name ?? '').trim();
|
|
30
|
+
const type = String(input.type ?? '');
|
|
31
|
+
const description = String(input.description ?? '').trim();
|
|
32
|
+
const body = String(input.body ?? '').trim();
|
|
33
|
+
if (!name || !description || !body || !TYPES.includes(type)) {
|
|
34
|
+
return { content: 'Error: name, type (user|feedback|project|reference), description, and body are required.', isError: true };
|
|
35
|
+
}
|
|
36
|
+
await store.save({ name, type, description, body });
|
|
37
|
+
return { content: `Saved memory "${name}" (${type}).` };
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export const memoryList = {
|
|
41
|
+
def: {
|
|
42
|
+
type: 'function',
|
|
43
|
+
function: {
|
|
44
|
+
name: 'memory_list',
|
|
45
|
+
description: 'List persisted memories (optionally filtered by type).',
|
|
46
|
+
parameters: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
type: { type: 'string', enum: TYPES, description: 'Optional filter.' },
|
|
50
|
+
},
|
|
51
|
+
required: [],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
async run(input, ctx) {
|
|
56
|
+
const store = getStore(ctx);
|
|
57
|
+
if (!store)
|
|
58
|
+
return { content: 'Memory is not enabled.', isError: true };
|
|
59
|
+
const type = input.type ? String(input.type) : undefined;
|
|
60
|
+
const entries = await store.list(type);
|
|
61
|
+
if (!entries.length)
|
|
62
|
+
return { content: type ? `No ${type} memories.` : 'No memories stored.' };
|
|
63
|
+
return {
|
|
64
|
+
content: `Memories (${entries.length}):\n` +
|
|
65
|
+
entries.map((e) => ` ${e.name} [${e.type}] — ${e.description}`).join('\n'),
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
export const memoryDelete = {
|
|
70
|
+
def: {
|
|
71
|
+
type: 'function',
|
|
72
|
+
function: {
|
|
73
|
+
name: 'memory_delete',
|
|
74
|
+
description: 'Delete a persisted memory by name.',
|
|
75
|
+
parameters: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
name: { type: 'string', description: 'The memory name to delete.' },
|
|
79
|
+
},
|
|
80
|
+
required: ['name'],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
async run(input, ctx) {
|
|
85
|
+
const store = getStore(ctx);
|
|
86
|
+
if (!store)
|
|
87
|
+
return { content: 'Memory is not enabled.', isError: true };
|
|
88
|
+
const name = String(input.name ?? '').trim();
|
|
89
|
+
if (!name)
|
|
90
|
+
return { content: 'Error: `name` is required.', isError: true };
|
|
91
|
+
await store.remove(name);
|
|
92
|
+
return { content: `Deleted memory "${name}".` };
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
export const MEMORY_TOOLS = [memoryWrite, memoryList, memoryDelete];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type MemoryType = 'user' | 'feedback' | 'project' | 'reference';
|
|
2
|
+
export type MemoryEntry = {
|
|
3
|
+
/** Stable, unique slug (used as the primary key). */
|
|
4
|
+
name: string;
|
|
5
|
+
type: MemoryType;
|
|
6
|
+
/** One-line summary used for recall/relevance. */
|
|
7
|
+
description: string;
|
|
8
|
+
/** The full memory content. */
|
|
9
|
+
body: string;
|
|
10
|
+
createdAt?: number;
|
|
11
|
+
updatedAt?: number;
|
|
12
|
+
};
|
|
13
|
+
export type MemoryStoreOptions = {
|
|
14
|
+
dbName?: string;
|
|
15
|
+
};
|