agent-common 1.0.2 → 1.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/CHANGELOG.md +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/database.d.ts +26 -1
- package/dist/storage/database.d.ts.map +1 -1
- package/dist/storage/database.js +33 -6
- package/dist/storage/database.js.map +1 -1
- package/dist/transport/mcp.d.ts +10 -2
- package/dist/transport/mcp.d.ts.map +1 -1
- package/dist/transport/mcp.js +77 -24
- package/dist/transport/mcp.js.map +1 -1
- package/dist/transport/rate-limit.d.ts +31 -0
- package/dist/transport/rate-limit.d.ts.map +1 -0
- package/dist/transport/rate-limit.js +71 -0
- package/dist/transport/rate-limit.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.0] - 2026-04-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Rate limiter** (`transport/rate-limit.ts`) — IP-keyed fixed-window limiter
|
|
8
|
+
with optional secondary `heavy` bucket for expensive endpoints. Single
|
|
9
|
+
`createRateLimiter({ windows, cleanupIntervalMs?, getClientId? })` instance
|
|
10
|
+
manages both buckets, lazy cleanup of expired entries via an unref'd timer,
|
|
11
|
+
and exposes `check(req, bucket?)` and `dispose()`. Replaces the duplicated
|
|
12
|
+
rate-limit code in `agent-tasks` and `agent-knowledge`.
|
|
13
|
+
- **Migration runner — `adoptUserVersion`** option on `runMigrations` (and
|
|
14
|
+
forwarded by `createDb`). When set, the runner seeds `_meta.schema_version`
|
|
15
|
+
from the legacy `pragma user_version` value before applying migrations,
|
|
16
|
+
letting consumers adopt agent-common's runner on existing DBs without
|
|
17
|
+
re-running migrations against tables that already exist. Removes the need
|
|
18
|
+
for the inline pre-open shim that `agent-discover` carried.
|
|
19
|
+
- **`addColumnIfMissing(raw, table, column, definition)`** and
|
|
20
|
+
**`hasColumn(raw, table, column)`** — idempotent ALTER TABLE ADD COLUMN
|
|
21
|
+
helper backed by `PRAGMA table_info`. Saves boilerplate in any migration
|
|
22
|
+
that needs to remain safe to re-run on partially-migrated DBs.
|
|
23
|
+
|
|
24
|
+
## [1.0.3] - 2026-04-07
|
|
25
|
+
|
|
26
|
+
- `startMcpServer`:
|
|
27
|
+
- `handleTool` may return a Promise (async tool handlers).
|
|
28
|
+
- `tools` may be a function returning the list (dynamic tool lists).
|
|
29
|
+
- New `formatResult(result, toolName)` to customize tool response text (e.g. append a trailing instructions footer).
|
|
30
|
+
- New `onToolCalled(name, args, notify)` post-response hook with an injected `notify(method, params)` callback for emitting JSON-RPC notifications like `notifications/tools/list_changed`.
|
|
31
|
+
- New `capabilities` option to override the default `{ tools: {} }` (e.g. `{ tools: { listChanged: true } }`).
|
|
32
|
+
- New `logLabel` for stderr error prefixes.
|
|
33
|
+
- `McpServerHandle` now exposes `notify(method, params)`.
|
|
34
|
+
|
|
3
35
|
## [1.0.2] - 2026-04-07
|
|
4
36
|
|
|
5
37
|
- `setupWebSocket`:
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './types.js';
|
|
|
2
2
|
export * from './storage/database.js';
|
|
3
3
|
export * from './storage/fts.js';
|
|
4
4
|
export * from './transport/rest.js';
|
|
5
|
+
export * from './transport/rate-limit.js';
|
|
5
6
|
export * from './transport/ws.js';
|
|
6
7
|
export * from './transport/mcp.js';
|
|
7
8
|
export * from './domain/events.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from './types.js';
|
|
|
5
5
|
export * from './storage/database.js';
|
|
6
6
|
export * from './storage/fts.js';
|
|
7
7
|
export * from './transport/rest.js';
|
|
8
|
+
export * from './transport/rate-limit.js';
|
|
8
9
|
export * from './transport/ws.js';
|
|
9
10
|
export * from './transport/mcp.js';
|
|
10
11
|
export * from './domain/events.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,4BAA4B;AAC5B,gFAAgF;AAEhF,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,4BAA4B;AAC5B,gFAAgF;AAEhF,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC"}
|
|
@@ -20,8 +20,33 @@ export interface DbOptions {
|
|
|
20
20
|
migrations?: Migration[];
|
|
21
21
|
/** Enable verbose logging to stderr. */
|
|
22
22
|
verbose?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* If true, before running migrations, seed _meta.schema_version from the
|
|
25
|
+
* legacy `pragma user_version` value when _meta is empty and user_version > 0.
|
|
26
|
+
* Use this when adopting agent-common's migration runner on an existing DB
|
|
27
|
+
* that previously tracked its schema version via pragma user_version.
|
|
28
|
+
*/
|
|
29
|
+
adoptUserVersion?: boolean;
|
|
23
30
|
}
|
|
24
31
|
export declare function createDb(options: DbOptions): Db;
|
|
25
|
-
export
|
|
32
|
+
export interface RunMigrationsOptions {
|
|
33
|
+
/**
|
|
34
|
+
* If true, seed _meta.schema_version from `pragma user_version` when _meta
|
|
35
|
+
* is empty and user_version > 0. Lets consumers adopt the runner on legacy
|
|
36
|
+
* DBs without re-running migrations against tables that already exist.
|
|
37
|
+
*/
|
|
38
|
+
adoptUserVersion?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function runMigrations(raw: Database.Database, migrations: Migration[], options?: RunMigrationsOptions): void;
|
|
26
41
|
export declare function getSchemaVersion(raw: Database.Database): number;
|
|
42
|
+
/**
|
|
43
|
+
* ALTER TABLE ADD COLUMN guarded by a PRAGMA table_info check. No-op if the
|
|
44
|
+
* column already exists. Saves boilerplate in migrations that need to remain
|
|
45
|
+
* safe to re-run on partially-migrated DBs.
|
|
46
|
+
*/
|
|
47
|
+
export declare function addColumnIfMissing(raw: Database.Database, table: string, column: string, definition: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Returns true if the given column exists on the given table.
|
|
50
|
+
*/
|
|
51
|
+
export declare function hasColumn(raw: Database.Database, table: string, column: string): boolean;
|
|
27
52
|
//# sourceMappingURL=database.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"AAQA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,WAAW,EAAE;IACjB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC;IACzD,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;IAClD,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACvD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,EAAE,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,SAAS;IACxB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,wCAAwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"AAQA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,WAAW,EAAE;IACjB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC;IACzD,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;IAClD,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACvD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,EAAE,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,SAAS;IACxB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,wCAAwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,EAAE,CAwC/C;AAMD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,QAAQ,CAAC,QAAQ,EACtB,UAAU,EAAE,SAAS,EAAE,EACvB,OAAO,GAAE,oBAAyB,GACjC,IAAI,CAoCN;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAK/D;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,CAAC,QAAQ,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,IAAI,CAIN;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGxF"}
|
package/dist/storage/database.js
CHANGED
|
@@ -15,7 +15,7 @@ export function createDb(options) {
|
|
|
15
15
|
raw.pragma('synchronous = NORMAL');
|
|
16
16
|
raw.pragma('foreign_keys = ON');
|
|
17
17
|
if (options.migrations && options.migrations.length > 0) {
|
|
18
|
-
runMigrations(raw, options.migrations);
|
|
18
|
+
runMigrations(raw, options.migrations, { adoptUserVersion: options.adoptUserVersion });
|
|
19
19
|
}
|
|
20
20
|
return {
|
|
21
21
|
raw,
|
|
@@ -45,17 +45,23 @@ export function createDb(options) {
|
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
// Migration runner
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
|
-
export function runMigrations(raw, migrations) {
|
|
48
|
+
export function runMigrations(raw, migrations, options = {}) {
|
|
52
49
|
raw.exec(`
|
|
53
50
|
CREATE TABLE IF NOT EXISTS _meta (
|
|
54
51
|
key TEXT PRIMARY KEY,
|
|
55
52
|
value TEXT NOT NULL
|
|
56
53
|
)
|
|
57
54
|
`);
|
|
58
|
-
|
|
55
|
+
let row = raw.prepare(`SELECT value FROM _meta WHERE key = 'schema_version'`).get();
|
|
56
|
+
if (!row && options.adoptUserVersion) {
|
|
57
|
+
const userVersion = raw.pragma('user_version', { simple: true });
|
|
58
|
+
if (typeof userVersion === 'number' && userVersion > 0) {
|
|
59
|
+
raw
|
|
60
|
+
.prepare(`INSERT INTO _meta (key, value) VALUES ('schema_version', ?)`)
|
|
61
|
+
.run(String(userVersion));
|
|
62
|
+
row = { value: String(userVersion) };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
59
65
|
const currentVersion = row ? parseInt(row.value, 10) : 0;
|
|
60
66
|
const sorted = [...migrations].sort((a, b) => a.version - b.version);
|
|
61
67
|
const target = sorted.length > 0 ? sorted[sorted.length - 1].version : 0;
|
|
@@ -72,4 +78,25 @@ export function getSchemaVersion(raw) {
|
|
|
72
78
|
const row = raw.prepare(`SELECT value FROM _meta WHERE key = 'schema_version'`).get();
|
|
73
79
|
return row ? parseInt(row.value, 10) : 0;
|
|
74
80
|
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Idempotent schema helpers
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
/**
|
|
85
|
+
* ALTER TABLE ADD COLUMN guarded by a PRAGMA table_info check. No-op if the
|
|
86
|
+
* column already exists. Saves boilerplate in migrations that need to remain
|
|
87
|
+
* safe to re-run on partially-migrated DBs.
|
|
88
|
+
*/
|
|
89
|
+
export function addColumnIfMissing(raw, table, column, definition) {
|
|
90
|
+
const cols = raw.prepare(`PRAGMA table_info(${table})`).all();
|
|
91
|
+
if (cols.some((c) => c.name === column))
|
|
92
|
+
return;
|
|
93
|
+
raw.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Returns true if the given column exists on the given table.
|
|
97
|
+
*/
|
|
98
|
+
export function hasColumn(raw, table, column) {
|
|
99
|
+
const cols = raw.prepare(`PRAGMA table_info(${table})`).all();
|
|
100
|
+
return cols.some((c) => c.name === column);
|
|
101
|
+
}
|
|
75
102
|
//# sourceMappingURL=database.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,+BAA+B;AAC/B,EAAE;AACF,uEAAuE;AACvE,4EAA4E;AAC5E,6BAA6B;AAC7B,gFAAgF;AAEhF,OAAO,QAAQ,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
|
package/dist/transport/mcp.d.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import type { ToolDefinition } from '../types.js';
|
|
2
|
+
export type ToolList = ToolDefinition[] | (() => ToolDefinition[]);
|
|
3
|
+
export type NotifyFn = (method: string, params?: unknown) => void;
|
|
2
4
|
export interface McpServerOptions {
|
|
3
5
|
serverInfo: {
|
|
4
6
|
name: string;
|
|
5
7
|
version: string;
|
|
6
8
|
};
|
|
7
|
-
tools:
|
|
8
|
-
handleTool: (name: string, args: Record<string, unknown>) => unknown
|
|
9
|
+
tools: ToolList;
|
|
10
|
+
handleTool: (name: string, args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
9
11
|
onInitialize?: () => void;
|
|
12
|
+
formatResult?: (result: unknown, toolName: string) => string;
|
|
13
|
+
onToolCalled?: (name: string, args: Record<string, unknown>, notify: NotifyFn) => void;
|
|
14
|
+
capabilities?: Record<string, unknown>;
|
|
10
15
|
protocolVersion?: string;
|
|
16
|
+
/** Label used in stderr error prefixes. Default: "agent-common". */
|
|
17
|
+
logLabel?: string;
|
|
11
18
|
}
|
|
12
19
|
export interface McpServerHandle {
|
|
13
20
|
close(): void;
|
|
21
|
+
notify: NotifyFn;
|
|
14
22
|
}
|
|
15
23
|
export declare function startMcpServer(opts: McpServerOptions): McpServerHandle;
|
|
16
24
|
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/transport/mcp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/transport/mcp.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,MAAM,QAAQ,GAAG,cAAc,EAAE,GAAG,CAAC,MAAM,cAAc,EAAE,CAAC,CAAC;AAEnE,MAAM,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,KAAK,EAAE,QAAQ,CAAC;IAChB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxF,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,eAAe,CAoKtE"}
|
package/dist/transport/mcp.js
CHANGED
|
@@ -2,20 +2,51 @@
|
|
|
2
2
|
// agent-common — MCP transport
|
|
3
3
|
//
|
|
4
4
|
// Generic JSON-RPC over stdio dispatcher. The consumer provides:
|
|
5
|
-
// - tools: ToolDefinition[]
|
|
6
|
-
// - handleTool(name, args): unknown (the per-tool dispatcher)
|
|
7
5
|
// - serverInfo: { name, version }
|
|
8
|
-
// -
|
|
6
|
+
// - tools: ToolDefinition[] | () => ToolDefinition[] (dynamic supported)
|
|
7
|
+
// - handleTool(name, args): unknown | Promise<unknown> (async supported)
|
|
8
|
+
// - onInitialize(): optional hook fired on MCP initialize
|
|
9
|
+
// - formatResult?: custom JSON → text formatting for tool responses
|
|
10
|
+
// - onToolCalled?: post-response hook with a `notify` callback to emit
|
|
11
|
+
// JSON-RPC notifications like `notifications/tools/list_changed`
|
|
12
|
+
// - capabilities?: custom capabilities object merged over the default
|
|
9
13
|
// =============================================================================
|
|
10
14
|
import { createInterface } from 'readline';
|
|
11
15
|
import { KitError } from '../types.js';
|
|
12
16
|
export function startMcpServer(opts) {
|
|
13
17
|
const protocolVersion = opts.protocolVersion ?? '2024-11-05';
|
|
14
|
-
const capabilities = { tools: {} };
|
|
18
|
+
const capabilities = opts.capabilities ?? { tools: {} };
|
|
19
|
+
const label = opts.logLabel ?? 'agent-common';
|
|
15
20
|
function writeResponse(response) {
|
|
16
21
|
process.stdout.write(JSON.stringify(response) + '\n');
|
|
17
22
|
}
|
|
18
|
-
|
|
23
|
+
const notify = (method, params) => {
|
|
24
|
+
const payload = { jsonrpc: '2.0', method };
|
|
25
|
+
if (params !== undefined)
|
|
26
|
+
payload.params = params;
|
|
27
|
+
process.stdout.write(JSON.stringify(payload) + '\n');
|
|
28
|
+
};
|
|
29
|
+
function resolveTools() {
|
|
30
|
+
return typeof opts.tools === 'function' ? opts.tools() : opts.tools;
|
|
31
|
+
}
|
|
32
|
+
function formatToolText(result, toolName) {
|
|
33
|
+
return opts.formatResult
|
|
34
|
+
? opts.formatResult(result, toolName)
|
|
35
|
+
: JSON.stringify(result, null, 2);
|
|
36
|
+
}
|
|
37
|
+
function errorResponse(id, err) {
|
|
38
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
39
|
+
const code = err instanceof KitError ? err.code : 'UNKNOWN_ERROR';
|
|
40
|
+
return {
|
|
41
|
+
jsonrpc: '2.0',
|
|
42
|
+
id,
|
|
43
|
+
result: {
|
|
44
|
+
content: [{ type: 'text', text: `Error [${code}]: ${message}` }],
|
|
45
|
+
isError: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function handleRequest(request) {
|
|
19
50
|
const { method, params, id } = request;
|
|
20
51
|
switch (method) {
|
|
21
52
|
case 'initialize':
|
|
@@ -24,7 +55,9 @@ export function startMcpServer(opts) {
|
|
|
24
55
|
opts.onInitialize();
|
|
25
56
|
}
|
|
26
57
|
catch (err) {
|
|
27
|
-
process.stderr.write('[
|
|
58
|
+
process.stderr.write('[' +
|
|
59
|
+
label +
|
|
60
|
+
'] onInitialize error: ' +
|
|
28
61
|
(err instanceof Error ? err.message : String(err)) +
|
|
29
62
|
'\n');
|
|
30
63
|
}
|
|
@@ -41,7 +74,7 @@ export function startMcpServer(opts) {
|
|
|
41
74
|
case 'notifications/initialized':
|
|
42
75
|
return null;
|
|
43
76
|
case 'tools/list':
|
|
44
|
-
return { jsonrpc: '2.0', id, result: { tools:
|
|
77
|
+
return { jsonrpc: '2.0', id, result: { tools: resolveTools() } };
|
|
45
78
|
case 'tools/call': {
|
|
46
79
|
const toolName = String(params?.name ?? '');
|
|
47
80
|
const rawArgs = params?.arguments;
|
|
@@ -49,26 +82,32 @@ export function startMcpServer(opts) {
|
|
|
49
82
|
? rawArgs
|
|
50
83
|
: {};
|
|
51
84
|
try {
|
|
52
|
-
const
|
|
85
|
+
const raw = opts.handleTool(toolName, toolArgs);
|
|
86
|
+
const result = raw instanceof Promise ? await raw : raw;
|
|
53
87
|
return {
|
|
54
88
|
jsonrpc: '2.0',
|
|
55
89
|
id,
|
|
56
90
|
result: {
|
|
57
|
-
content: [{ type: 'text', text:
|
|
91
|
+
content: [{ type: 'text', text: formatToolText(result, toolName) }],
|
|
58
92
|
},
|
|
59
93
|
};
|
|
60
94
|
}
|
|
61
95
|
catch (err) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
96
|
+
return errorResponse(id, err);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
if (opts.onToolCalled) {
|
|
100
|
+
try {
|
|
101
|
+
opts.onToolCalled(toolName, toolArgs, notify);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
process.stderr.write('[' +
|
|
105
|
+
label +
|
|
106
|
+
'] onToolCalled error: ' +
|
|
107
|
+
(err instanceof Error ? err.message : String(err)) +
|
|
108
|
+
'\n');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
72
111
|
}
|
|
73
112
|
}
|
|
74
113
|
case 'ping':
|
|
@@ -85,14 +124,14 @@ export function startMcpServer(opts) {
|
|
|
85
124
|
rl.on('line', (line) => {
|
|
86
125
|
if (!line.trim())
|
|
87
126
|
return;
|
|
127
|
+
let request;
|
|
88
128
|
try {
|
|
89
|
-
|
|
90
|
-
const response = handleRequest(request);
|
|
91
|
-
if (response)
|
|
92
|
-
writeResponse(response);
|
|
129
|
+
request = JSON.parse(line);
|
|
93
130
|
}
|
|
94
131
|
catch (err) {
|
|
95
|
-
process.stderr.write('[
|
|
132
|
+
process.stderr.write('[' +
|
|
133
|
+
label +
|
|
134
|
+
'] JSON-RPC parse error: ' +
|
|
96
135
|
(err instanceof Error ? err.message : String(err)) +
|
|
97
136
|
'\n');
|
|
98
137
|
writeResponse({
|
|
@@ -100,12 +139,26 @@ export function startMcpServer(opts) {
|
|
|
100
139
|
id: null,
|
|
101
140
|
error: { code: -32700, message: 'Parse error' },
|
|
102
141
|
});
|
|
142
|
+
return;
|
|
103
143
|
}
|
|
144
|
+
handleRequest(request)
|
|
145
|
+
.then((response) => {
|
|
146
|
+
if (response)
|
|
147
|
+
writeResponse(response);
|
|
148
|
+
})
|
|
149
|
+
.catch((err) => {
|
|
150
|
+
process.stderr.write('[' +
|
|
151
|
+
label +
|
|
152
|
+
'] Handler error: ' +
|
|
153
|
+
(err instanceof Error ? err.message : String(err)) +
|
|
154
|
+
'\n');
|
|
155
|
+
});
|
|
104
156
|
});
|
|
105
157
|
return {
|
|
106
158
|
close() {
|
|
107
159
|
rl.close();
|
|
108
160
|
},
|
|
161
|
+
notify,
|
|
109
162
|
};
|
|
110
163
|
}
|
|
111
164
|
//# sourceMappingURL=mcp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/transport/mcp.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,+BAA+B;AAC/B,EAAE;AACF,iEAAiE;AACjE,
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/transport/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'http';
|
|
2
|
+
export interface RateLimitWindow {
|
|
3
|
+
max: number;
|
|
4
|
+
windowMs: number;
|
|
5
|
+
}
|
|
6
|
+
export interface RateLimitWindows {
|
|
7
|
+
default: RateLimitWindow;
|
|
8
|
+
heavy?: RateLimitWindow;
|
|
9
|
+
}
|
|
10
|
+
export interface RateLimitResult {
|
|
11
|
+
allowed: boolean;
|
|
12
|
+
remaining: number;
|
|
13
|
+
resetAt: number;
|
|
14
|
+
limit: number;
|
|
15
|
+
}
|
|
16
|
+
export type RateLimitBucketName = 'default' | 'heavy';
|
|
17
|
+
export interface RateLimiterOptions {
|
|
18
|
+
windows: RateLimitWindows;
|
|
19
|
+
/** How often to sweep expired entries from the bucket maps. Default: 5 minutes. */
|
|
20
|
+
cleanupIntervalMs?: number;
|
|
21
|
+
/** Extract the client identifier (default: x-forwarded-for first, then remoteAddress). */
|
|
22
|
+
getClientId?: (req: IncomingMessage) => string;
|
|
23
|
+
}
|
|
24
|
+
export interface RateLimiter {
|
|
25
|
+
check(req: IncomingMessage, bucket?: RateLimitBucketName): RateLimitResult;
|
|
26
|
+
/** Stop the cleanup timer. Safe to call multiple times. */
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function defaultGetClientId(req: IncomingMessage): string;
|
|
30
|
+
export declare function createRateLimiter(options: RateLimiterOptions): RateLimiter;
|
|
31
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/transport/rate-limit.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0FAA0F;IAC1F,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAC;CAChD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,mBAAmB,GAAG,eAAe,CAAC;IAC3E,2DAA2D;IAC3D,OAAO,IAAI,IAAI,CAAC;CACjB;AASD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAS/D;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CA6D1E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// agent-common — Rate limiter
|
|
3
|
+
//
|
|
4
|
+
// IP-keyed fixed-window rate limiter with optional secondary "heavy" bucket
|
|
5
|
+
// for expensive endpoints. Single instance manages both buckets, lazy
|
|
6
|
+
// cleanup of expired entries on a timer (timer is unref'd so it doesn't
|
|
7
|
+
// keep the event loop alive).
|
|
8
|
+
// =============================================================================
|
|
9
|
+
const DEFAULT_CLEANUP_INTERVAL_MS = 5 * 60_000;
|
|
10
|
+
export function defaultGetClientId(req) {
|
|
11
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
12
|
+
if (typeof forwarded === 'string' && forwarded.length > 0) {
|
|
13
|
+
return forwarded.split(',')[0].trim();
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(forwarded) && forwarded.length > 0) {
|
|
16
|
+
return forwarded[0].split(',')[0].trim();
|
|
17
|
+
}
|
|
18
|
+
return req.socket?.remoteAddress ?? 'unknown';
|
|
19
|
+
}
|
|
20
|
+
export function createRateLimiter(options) {
|
|
21
|
+
if (!options.windows?.default) {
|
|
22
|
+
throw new Error('createRateLimiter: windows.default is required');
|
|
23
|
+
}
|
|
24
|
+
const getClientId = options.getClientId ?? defaultGetClientId;
|
|
25
|
+
const cleanupIntervalMs = options.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS;
|
|
26
|
+
const buckets = {
|
|
27
|
+
default: new Map(),
|
|
28
|
+
heavy: new Map(),
|
|
29
|
+
};
|
|
30
|
+
function sweep() {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
for (const map of [buckets.default, buckets.heavy]) {
|
|
33
|
+
for (const [key, entry] of map) {
|
|
34
|
+
if (entry.resetAt <= now)
|
|
35
|
+
map.delete(key);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const timer = cleanupIntervalMs > 0 ? setInterval(sweep, cleanupIntervalMs) : null;
|
|
40
|
+
if (timer && typeof timer.unref === 'function')
|
|
41
|
+
timer.unref();
|
|
42
|
+
function check(req, bucketName = 'default') {
|
|
43
|
+
const window = bucketName === 'heavy'
|
|
44
|
+
? (options.windows.heavy ?? options.windows.default)
|
|
45
|
+
: options.windows.default;
|
|
46
|
+
const map = buckets[bucketName];
|
|
47
|
+
const id = getClientId(req);
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
let entry = map.get(id);
|
|
50
|
+
if (!entry || entry.resetAt <= now) {
|
|
51
|
+
entry = { count: 0, resetAt: now + window.windowMs };
|
|
52
|
+
map.set(id, entry);
|
|
53
|
+
}
|
|
54
|
+
entry.count++;
|
|
55
|
+
const remaining = Math.max(0, window.max - entry.count);
|
|
56
|
+
return {
|
|
57
|
+
allowed: entry.count <= window.max,
|
|
58
|
+
remaining,
|
|
59
|
+
resetAt: entry.resetAt,
|
|
60
|
+
limit: window.max,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function dispose() {
|
|
64
|
+
if (timer)
|
|
65
|
+
clearInterval(timer);
|
|
66
|
+
buckets.default.clear();
|
|
67
|
+
buckets.heavy.clear();
|
|
68
|
+
}
|
|
69
|
+
return { check, dispose };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/transport/rate-limit.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED