@mcpolyglot/cli 0.2.0 → 0.4.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.
Files changed (43) hide show
  1. package/README.md +27 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/agents.test.d.ts +2 -0
  4. package/dist/__tests__/agents.test.d.ts.map +1 -0
  5. package/dist/__tests__/agents.test.js +172 -0
  6. package/dist/__tests__/agents.test.js.map +1 -0
  7. package/dist/__tests__/examples.test.d.ts +2 -0
  8. package/dist/__tests__/examples.test.d.ts.map +1 -0
  9. package/dist/__tests__/examples.test.js +140 -0
  10. package/dist/__tests__/examples.test.js.map +1 -0
  11. package/dist/__tests__/init-doctor.test.d.ts +2 -0
  12. package/dist/__tests__/init-doctor.test.d.ts.map +1 -0
  13. package/dist/__tests__/init-doctor.test.js +79 -0
  14. package/dist/__tests__/init-doctor.test.js.map +1 -0
  15. package/dist/bin.js +16 -3
  16. package/dist/bin.js.map +1 -1
  17. package/dist/commands/doctor-agents.d.ts +5 -0
  18. package/dist/commands/doctor-agents.d.ts.map +1 -0
  19. package/dist/commands/doctor-agents.js +33 -0
  20. package/dist/commands/doctor-agents.js.map +1 -0
  21. package/dist/commands/doctor.d.ts +10 -0
  22. package/dist/commands/doctor.d.ts.map +1 -1
  23. package/dist/commands/doctor.js +79 -1
  24. package/dist/commands/doctor.js.map +1 -1
  25. package/dist/commands/init.d.ts +16 -0
  26. package/dist/commands/init.d.ts.map +1 -1
  27. package/dist/commands/init.js +85 -5
  28. package/dist/commands/init.js.map +1 -1
  29. package/dist/commands/serve.d.ts.map +1 -1
  30. package/dist/commands/serve.js +34 -12
  31. package/dist/commands/serve.js.map +1 -1
  32. package/dist/commands/token.d.ts +5 -0
  33. package/dist/commands/token.d.ts.map +1 -0
  34. package/dist/commands/token.js +23 -0
  35. package/dist/commands/token.js.map +1 -0
  36. package/dist/factory.d.ts +6 -2
  37. package/dist/factory.d.ts.map +1 -1
  38. package/dist/factory.js +99 -14
  39. package/dist/factory.js.map +1 -1
  40. package/dist/ui.d.ts.map +1 -1
  41. package/dist/ui.js +0 -2
  42. package/dist/ui.js.map +1 -1
  43. package/package.json +9 -6
@@ -0,0 +1,172 @@
1
+ import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
6
+ import { ConfigSchema } from '@mcpolyglot/config';
7
+ import { hashToken, StreamableHttpTransport } from '@mcpolyglot/core/transports/streamable-http';
8
+ import { buildServerFromConfig } from '../factory.js';
9
+ // Real HTTP transport + real SQLite through the CLI factory: two agents, one source.
10
+ // better-sqlite3 is connector-sql's dependency; borrow it from there to seed the file.
11
+ const Database = createRequire(new URL('../../../connector-sql/package.json', import.meta.url))('better-sqlite3');
12
+ const silent = { debug() { }, info() { }, warn() { }, error() { } };
13
+ let dir;
14
+ let dbPath;
15
+ beforeAll(() => {
16
+ dir = mkdtempSync(join(tmpdir(), 'mcpolyglot-agents-'));
17
+ dbPath = join(dir, 'app.db');
18
+ const db = new Database(dbPath);
19
+ db.exec(`
20
+ CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT);
21
+ CREATE TABLE orders (id INTEGER PRIMARY KEY, total INTEGER);
22
+ INSERT INTO users (email) VALUES ('a@x.io');
23
+ INSERT INTO orders (total) VALUES (42);
24
+ `);
25
+ db.close();
26
+ });
27
+ afterAll(() => rmSync(dir, { recursive: true, force: true }));
28
+ let server;
29
+ afterEach(async () => {
30
+ await server?.stop();
31
+ server = undefined;
32
+ });
33
+ async function boot(extra) {
34
+ const auditPath = join(dir, `audit-${Math.random().toString(36).slice(2)}.jsonl`);
35
+ const cfg = ConfigSchema.parse({
36
+ sources: [
37
+ {
38
+ id: 'app',
39
+ kind: 'sqlite',
40
+ url: dbPath,
41
+ scopes: ['schema:read', 'tables:read', 'query:raw'],
42
+ },
43
+ ],
44
+ audit: { console: false, path: auditPath },
45
+ ...extra,
46
+ });
47
+ const built = await buildServerFromConfig(cfg, silent);
48
+ server = built.server;
49
+ const transport = new StreamableHttpTransport({
50
+ host: '127.0.0.1',
51
+ port: 0,
52
+ auth: cfg.agents ? { kind: 'agents', agents: cfg.agents } : { kind: 'bearer', token: 'shared' },
53
+ logger: silent,
54
+ });
55
+ await server.start(transport);
56
+ const port = transport.httpServer.address().port;
57
+ const rpc = async (token, method, params = {}, headers = {}) => {
58
+ const res = await fetch(`http://127.0.0.1:${port}/mcp`, {
59
+ method: 'POST',
60
+ headers: {
61
+ authorization: `Bearer ${token}`,
62
+ 'content-type': 'application/json',
63
+ accept: 'application/json, text/event-stream',
64
+ ...headers,
65
+ },
66
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
67
+ });
68
+ const body = await res.text();
69
+ if (res.status !== 200)
70
+ return { status: res.status, body };
71
+ const data = body.split('\n').find((l) => l.startsWith('data: '));
72
+ return { status: res.status, body: JSON.parse(data ? data.slice(6) : body) };
73
+ };
74
+ const audit = () => readFileSync(auditPath, 'utf8')
75
+ .trim()
76
+ .split('\n')
77
+ .map((l) => JSON.parse(l));
78
+ return { rpc, audit };
79
+ }
80
+ const toolNames = (r) => r.body.result.tools.map((t) => t.name).sort();
81
+ const text = (r) => r.body.result.content.map((c) => c.text).join('');
82
+ describe('multi-agent HTTP', () => {
83
+ const agents = [
84
+ {
85
+ id: 'analyst',
86
+ tokens: [{ hash: hashToken('tok-analyst') }],
87
+ sources: { app: { policy: { tables: { users: 'none' } } } },
88
+ },
89
+ {
90
+ id: 'browser',
91
+ tokens: [{ hash: hashToken('tok-old'), revoked: true }, { hash: hashToken('tok-browser') }],
92
+ scopes: ['schema:read'],
93
+ sources: { app: {} },
94
+ },
95
+ ];
96
+ it('filters tools per agent scopes and applies each agent its own policy', async () => {
97
+ const { rpc } = await boot({ agents });
98
+ expect(toolNames(await rpc('tok-analyst', 'tools/list'))).toEqual([
99
+ 'app.describe_table',
100
+ 'app.list_tables',
101
+ 'app.query',
102
+ ]);
103
+ // `browser` lacks query:raw/tables:read, so `app.query` doesn't exist for it.
104
+ expect(toolNames(await rpc('tok-browser', 'tools/list'))).toEqual([
105
+ 'app.describe_table',
106
+ 'app.list_tables',
107
+ ]);
108
+ const call = { name: 'app.query', arguments: { sql: 'SELECT email FROM users' } };
109
+ expect((await rpc('tok-browser', 'tools/call', call)).body.error.message).toMatch(/Unknown tool/);
110
+ // Same table, same DB: analyst's policy hides `users`, the source default doesn't.
111
+ const denied = await rpc('tok-analyst', 'tools/call', call);
112
+ expect(denied.body.result.isError).toBe(true);
113
+ expect(text(denied)).toMatch(/forbidden\.policy/);
114
+ const listA = text(await rpc('tok-analyst', 'tools/call', { name: 'app.list_tables', arguments: {} }));
115
+ const listB = text(await rpc('tok-browser', 'tools/call', { name: 'app.list_tables', arguments: {} }));
116
+ expect(listA).not.toMatch(/"users"/);
117
+ expect(listB).toMatch(/"users"/);
118
+ const orders = await rpc('tok-analyst', 'tools/call', {
119
+ name: 'app.query',
120
+ arguments: { sql: 'SELECT total FROM orders' },
121
+ });
122
+ expect(text(orders)).toMatch(/42/);
123
+ });
124
+ it("an agent's policy can narrow the source policy but never widen it", async () => {
125
+ const { rpc } = await boot({
126
+ sources: [
127
+ {
128
+ id: 'app',
129
+ kind: 'sqlite',
130
+ url: dbPath,
131
+ scopes: ['schema:read', 'tables:read', 'query:raw'],
132
+ policy: { tables: { users: 'none' } },
133
+ },
134
+ ],
135
+ agents: [
136
+ {
137
+ id: 'sneaky',
138
+ tokens: [{ hash: hashToken('tok-sneaky') }],
139
+ sources: { app: { policy: { tables: { users: 'write' }, defaultAccess: 'read' } } },
140
+ },
141
+ ],
142
+ });
143
+ const r = await rpc('tok-sneaky', 'tools/call', {
144
+ name: 'app.query',
145
+ arguments: { sql: 'SELECT email FROM users' },
146
+ });
147
+ expect(text(r)).toMatch(/forbidden\.policy/);
148
+ // Widening to `write` must not register the write tool either.
149
+ expect(toolNames(await rpc('tok-sneaky', 'tools/list'))).not.toContain('app.execute');
150
+ });
151
+ it('rejects revoked and unknown tokens with 401', async () => {
152
+ const { rpc } = await boot({ agents });
153
+ expect((await rpc('tok-old', 'tools/list')).status).toBe(401);
154
+ expect((await rpc('shared', 'tools/list')).status).toBe(401);
155
+ });
156
+ it('audit agentId comes from the token, not the header', async () => {
157
+ const { rpc, audit } = await boot({ agents });
158
+ await rpc('tok-browser', 'tools/call', { name: 'app.list_tables', arguments: {} }, { 'x-mcpolyglot-agent': 'analyst' });
159
+ const [entry] = audit();
160
+ expect(entry.agentId).toBe('browser');
161
+ expect(entry.scopes).toEqual(['schema:read']);
162
+ expect(JSON.stringify(audit())).not.toMatch(/tok-/);
163
+ });
164
+ it('without `agents`: shared token, all tools, header-labelled audit (unchanged)', async () => {
165
+ const { rpc, audit } = await boot({});
166
+ expect(toolNames(await rpc('shared', 'tools/list'))).toHaveLength(3);
167
+ const r = await rpc('shared', 'tools/call', { name: 'app.query', arguments: { sql: 'SELECT email FROM users' } }, { 'x-mcpolyglot-agent': 'whoever' });
168
+ expect(text(r)).toMatch(/"rowCount": 1/);
169
+ expect(audit()[0].agentId).toBe('whoever');
170
+ });
171
+ });
172
+ //# sourceMappingURL=agents.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.test.js","sourceRoot":"","sources":["../../src/__tests__/agents.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=examples.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"examples.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/examples.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,140 @@
1
+ import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { afterAll, afterEach, describe, expect, it } from 'vitest';
6
+ import { ConfigSchema } from '@mcpolyglot/config';
7
+ import { StreamableHttpTransport } from '@mcpolyglot/core/transports/streamable-http';
8
+ import { buildServerFromConfig } from '../factory.js';
9
+ // Keeps examples/*/README.md honest: every row of each README's table is exercised here
10
+ // against that example's own config and seed.sql, over the real HTTP transport.
11
+ const Database = createRequire(new URL('../../../connector-sql/package.json', import.meta.url))('better-sqlite3');
12
+ const EXAMPLES = new URL('../../../../examples/', import.meta.url);
13
+ const silent = { debug() { }, info() { }, warn() { }, error() { } };
14
+ const dir = mkdtempSync(join(tmpdir(), 'mcpolyglot-examples-'));
15
+ afterAll(() => rmSync(dir, { recursive: true, force: true }));
16
+ let server;
17
+ afterEach(async () => {
18
+ await server?.stop();
19
+ server = undefined;
20
+ });
21
+ async function boot(example) {
22
+ const dbPath = join(dir, `${example}.db`);
23
+ const seed = new Database(dbPath);
24
+ seed.exec(readFileSync(new URL(`${example}/seed.sql`, EXAMPLES), 'utf8'));
25
+ seed.close();
26
+ const raw = JSON.parse(readFileSync(new URL(`${example}/mcpolyglot.config.json`, EXAMPLES), 'utf8'));
27
+ raw.sources[0].url = dbPath;
28
+ raw.audit = { console: false, path: join(dir, `${example}.audit.jsonl`) };
29
+ const cfg = ConfigSchema.parse(raw);
30
+ const built = await buildServerFromConfig(cfg, silent);
31
+ server = built.server;
32
+ const transport = new StreamableHttpTransport({
33
+ host: '127.0.0.1',
34
+ port: 0,
35
+ auth: cfg.agents ? { kind: 'agents', agents: cfg.agents } : { kind: 'bearer', token: 'shared' },
36
+ logger: silent,
37
+ });
38
+ await server.start(transport);
39
+ const port = transport.httpServer.address().port;
40
+ const rpc = async (token, method, params = {}) => {
41
+ const res = await fetch(`http://127.0.0.1:${port}/mcp`, {
42
+ method: 'POST',
43
+ headers: {
44
+ authorization: `Bearer ${token}`,
45
+ 'content-type': 'application/json',
46
+ accept: 'application/json, text/event-stream',
47
+ },
48
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
49
+ });
50
+ const body = await res.text();
51
+ if (res.status !== 200)
52
+ return { status: res.status, text: body, tools: [] };
53
+ const line = body.split('\n').find((l) => l.startsWith('data: '));
54
+ const msg = JSON.parse(line ? line.slice(6) : body);
55
+ return {
56
+ status: res.status,
57
+ text: msg.error
58
+ ? `rpc-error: ${msg.error.message}`
59
+ : (msg.result.content ?? []).map((c) => c.text).join(''),
60
+ isError: Boolean(msg.error || msg.result?.isError),
61
+ tools: (msg.result?.tools ?? []).map((t) => t.name),
62
+ };
63
+ };
64
+ const call = (token, name, args) => rpc(token, 'tools/call', { name, arguments: args });
65
+ const count = (sql) => {
66
+ const db = new Database(dbPath);
67
+ try {
68
+ return db.prepare(sql).get().n;
69
+ }
70
+ finally {
71
+ db.close();
72
+ }
73
+ };
74
+ return { rpc, call, count };
75
+ }
76
+ describe('examples/readonly-analytics', () => {
77
+ it('matches the README table', async () => {
78
+ const { call, rpc } = await boot('readonly-analytics');
79
+ const q = (sql) => call('shared', 'analytics.query', { sql });
80
+ const plans = await q('SELECT plan, count(*) AS n FROM users GROUP BY plan');
81
+ expect(plans.isError).toBe(false);
82
+ expect(plans.text).toContain('"pro"');
83
+ const joined = await q("SELECT u.plan, count(*) AS exports FROM events e JOIN users u ON u.id = e.user_id WHERE e.name = 'export' GROUP BY u.plan");
84
+ expect(joined.isError).toBe(false);
85
+ expect((await q('SELECT password_hash FROM users')).text).toMatch(/forbidden\.policy.*denied column/);
86
+ expect((await q('SELECT * FROM users')).text).toMatch(/forbidden\.policy.*SELECT \*/);
87
+ expect((await q('SELECT body FROM internal_notes')).text).toMatch(/forbidden\.policy.*not accessible/);
88
+ expect((await q('DELETE FROM events WHERE id = 1')).text).toMatch(/forbidden\.policy.*read-only/);
89
+ const list = (await call('shared', 'analytics.list_tables', {})).text;
90
+ expect(list).not.toContain('internal_notes');
91
+ expect(list).not.toContain('password_hash');
92
+ expect((await rpc('shared', 'tools/list')).tools).not.toContain('analytics.execute');
93
+ });
94
+ });
95
+ describe('examples/spend-policy', () => {
96
+ it('matches the README table', async () => {
97
+ const { call, count } = await boot('spend-policy');
98
+ const exec = (sql, extra = {}) => call('shared', 'bank.execute', { sql, ...extra });
99
+ const txCount = () => count('SELECT count(*) AS n FROM transactions');
100
+ const coffee = "INSERT INTO transactions (account_id, amount_cents, memo) VALUES (1, -2500, 'Coffee')";
101
+ expect((await exec(coffee)).text).toContain('"rowsAffected": 1');
102
+ expect(txCount()).toBe(3);
103
+ const tea = "INSERT INTO transactions (account_id, amount_cents, memo) VALUES (2, -300, 'Tea')";
104
+ await exec(tea, { idempotencyKey: 'coffee-1' });
105
+ await exec(tea, { idempotencyKey: 'coffee-1' });
106
+ expect(txCount()).toBe(4);
107
+ const tv = await exec("INSERT INTO transactions (account_id, amount_cents, memo) VALUES (1, -90000, 'TV')");
108
+ expect(tv.isError).toBe(true);
109
+ expect(tv.text).toMatch(/CHECK/i);
110
+ expect(txCount()).toBe(4);
111
+ expect((await exec('UPDATE accounts SET balance_cents = 999999 WHERE id = 1')).text).toMatch(/forbidden\.policy.*read-only/);
112
+ const del = await exec('DELETE FROM transactions WHERE account_id = 1');
113
+ expect(del.text).toMatch(/forbidden\.policy/);
114
+ expect(txCount()).toBe(4);
115
+ expect((await call('shared', 'bank.query', { sql: 'SELECT ssn FROM customers' })).text).toMatch(/forbidden\.policy.*denied column/);
116
+ expect((await exec('DROP TABLE transactions')).text).toMatch(/forbidden\.policy.*always blocked/);
117
+ });
118
+ });
119
+ describe('examples/multi-agent', () => {
120
+ it('matches the README table', async () => {
121
+ const { call, rpc } = await boot('multi-agent');
122
+ const support = 'demo-support-token';
123
+ const finance = 'demo-finance-token';
124
+ // support-bot
125
+ expect((await call(support, 'app.query', { sql: 'SELECT email FROM customers' })).isError).toBe(false);
126
+ expect((await call(support, 'app.execute', {
127
+ sql: "UPDATE tickets SET status = 'closed' WHERE id = 1",
128
+ })).text).toContain('"rowsAffected": 1');
129
+ expect((await call(support, 'app.query', { sql: 'SELECT id FROM invoices' })).text).toMatch(/forbidden\.policy/);
130
+ expect((await call(support, 'app.list_tables', {})).text).not.toContain('invoices');
131
+ // finance-bot
132
+ expect((await call(finance, 'app.query', { sql: 'SELECT id, status FROM invoices' })).isError).toBe(false);
133
+ expect((await call(finance, 'app.query', { sql: 'SELECT email FROM customers' })).text).toMatch(/forbidden\.policy.*denied column/);
134
+ expect((await call(finance, 'app.query', { sql: 'SELECT id FROM tickets' })).text).toMatch(/forbidden\.policy/);
135
+ expect((await rpc(finance, 'tools/list')).tools).not.toContain('app.execute');
136
+ // retired-bot
137
+ expect((await rpc('demo-retired-token', 'tools/list')).status).toBe(401);
138
+ });
139
+ });
140
+ //# sourceMappingURL=examples.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"examples.test.js","sourceRoot":"","sources":["../../src/__tests__/examples.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=init-doctor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-doctor.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/init-doctor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
5
+ import { PolicySchema, SqliteDialect } from '@mcpolyglot/connector-sql';
6
+ import { generatePolicy, initCommand } from '../commands/init.js';
7
+ import { checkPolicy } from '../commands/doctor.js';
8
+ let canRun = true;
9
+ try {
10
+ await import('better-sqlite3');
11
+ }
12
+ catch {
13
+ canRun = false;
14
+ }
15
+ const d = canRun ? describe : describe.skip;
16
+ d('init <url> / doctor policy checks against SQLite', () => {
17
+ let dir;
18
+ let dbPath;
19
+ let tables;
20
+ beforeAll(async () => {
21
+ dir = mkdtempSync(join(tmpdir(), 'mcpolyglot-init-'));
22
+ dbPath = join(dir, 'app.db');
23
+ const Database = (await import('better-sqlite3')).default;
24
+ const db = new Database(dbPath);
25
+ db.exec(`
26
+ CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT, ssn TEXT, password_hash TEXT);
27
+ CREATE TABLE accounts (id INTEGER PRIMARY KEY, customer_id INTEGER, api_key TEXT, balance INTEGER);
28
+ CREATE TABLE transactions (id INTEGER PRIMARY KEY, account_id INTEGER, amount INTEGER);
29
+ `);
30
+ db.close();
31
+ const dialect = new SqliteDialect(dbPath);
32
+ await dialect.connect();
33
+ tables = await dialect.listTables();
34
+ await dialect.close();
35
+ });
36
+ afterAll(() => rmSync(dir, { recursive: true, force: true }));
37
+ it('generates an all-read policy with defaultAccess none and heuristic denies', () => {
38
+ const policy = PolicySchema.parse(generatePolicy(tables));
39
+ expect(policy.defaultAccess).toBe('none');
40
+ expect(policy.tables).toEqual({ accounts: 'read', customers: 'read', transactions: 'read' });
41
+ expect(policy.denyColumns.sort()).toEqual([
42
+ 'accounts.api_key',
43
+ 'customers.password_hash',
44
+ 'customers.ssn',
45
+ ]);
46
+ });
47
+ it('writes the policy into mcpolyglot.config.ts, marked as suggestions', async () => {
48
+ await initCommand({ cwd: dir, url: dbPath, id: 'sqlite.app' });
49
+ const text = readFileSync(join(dir, 'mcpolyglot.config.ts'), 'utf8');
50
+ expect(text).toContain(`defaultAccess: 'none'`);
51
+ expect(text).toContain(`"transactions": 'read'`);
52
+ expect(text).not.toContain(`: 'write'`);
53
+ expect(text).toMatch(/Suggested from column names[\s\S]*"customers.ssn"/);
54
+ // Only a type import: the file must load under npx with no @mcpolyglot/config installed.
55
+ expect(text).toMatch(/^import type \{ McpolyglotConfig \} from '@mcpolyglot\/config';/m);
56
+ expect(text).not.toMatch(/^import \{/m);
57
+ await expect(initCommand({ cwd: dir, url: dbPath })).rejects.toThrow(/already exists/);
58
+ });
59
+ it('doctor flags policy keys and denyColumns that are not in the schema', () => {
60
+ const r = checkPolicy({
61
+ defaultAccess: 'none',
62
+ tables: { customers: 'read', accounts: 'write', ledger: 'read' },
63
+ denyColumns: ['customers.ssn', 'customers.pin', '*.nope'],
64
+ }, tables);
65
+ expect(r.problems).toEqual([
66
+ 'policy.tables names "ledger", which is not in the schema',
67
+ 'denyColumns has "customers.pin", which matches no column in the schema',
68
+ 'denyColumns has "*.nope", which matches no column in the schema',
69
+ ]);
70
+ expect(r.readable).toEqual(['customers']);
71
+ expect(r.writable).toEqual(['accounts']);
72
+ expect(r.hidden).toEqual(['transactions']);
73
+ expect(r.hiddenColumns).toEqual(['customers.ssn']);
74
+ });
75
+ it('doctor reports a clean generated policy with no problems', () => {
76
+ expect(checkPolicy(generatePolicy(tables), tables).problems).toEqual([]);
77
+ });
78
+ });
79
+ //# sourceMappingURL=init-doctor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-doctor.test.js","sourceRoot":"","sources":["../../src/__tests__/init-doctor.test.ts"],"names":[],"mappings":"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"}
package/dist/bin.js CHANGED
@@ -5,6 +5,7 @@ import { serveCommand } from './commands/serve.js';
5
5
  import { initCommand } from './commands/init.js';
6
6
  import { doctorCommand } from './commands/doctor.js';
7
7
  import { toolsCommand } from './commands/tools.js';
8
+ import { tokenCreateCommand, tokenHashCommand } from './commands/token.js';
8
9
  const VERSION = '0.0.1';
9
10
  function bannerHeader() {
10
11
  const wordmark = pc.cyan('m') +
@@ -32,8 +33,10 @@ program
32
33
  .command('init')
33
34
  .description('Interactively scaffold an mcpolyglot.config.ts in the current directory.')
34
35
  .option('--cwd <dir>', 'Directory to scaffold into', process.cwd())
35
- .action(async (opts) => {
36
- await initCommand({ cwd: opts.cwd });
36
+ .argument('[url]', 'DB URL or SQLite path: introspect it and generate a read-only policy')
37
+ .option('--id <id>', 'Source id (with a URL)')
38
+ .action(async (url, opts) => {
39
+ await initCommand({ cwd: opts.cwd, url, id: opts.id });
37
40
  });
38
41
  program
39
42
  .command('serve')
@@ -65,8 +68,18 @@ program
65
68
  .action(async (opts) => {
66
69
  await toolsCommand({ config: opts.config });
67
70
  });
71
+ const token = program.command('token').description('Manage per-agent HTTP bearer tokens.');
72
+ token
73
+ .command('create <agentId>')
74
+ .description('Mint a new token; prints it once with its sha256 hash and a config snippet.')
75
+ .action((agentId) => tokenCreateCommand(agentId));
76
+ token
77
+ .command('hash')
78
+ .description('Print the sha256 hash of a token read from stdin.')
79
+ .action(async () => {
80
+ await tokenHashCommand();
81
+ });
68
82
  program.parseAsync(process.argv).catch((err) => {
69
- // eslint-disable-next-line no-console
70
83
  console.error(`mcpolyglot: ${err.message}`);
71
84
  process.exit(1);
72
85
  });
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE3E,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB,SAAS,YAAY;IACnB,MAAM,QAAQ,GACZ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACf,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACf,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACf,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACf,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACf,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACf,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAClB,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,OAAO,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,6CAA6C,CAAC,IAAI,CAAC;AACxJ,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,mEAAmE,CAAC;KAChF,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,oBAAoB,CAAC;KACvD,UAAU,CAAC,YAAY,EAAE,cAAc,CAAC;KACxC,WAAW,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC;KACxC,WAAW,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,qCAAqC,CAAC,IAAI,CAAC,CAAC;AAErF,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,0EAA0E,CAAC;KACvF,MAAM,CAAC,aAAa,EAAE,4BAA4B,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KAClE,QAAQ,CAAC,OAAO,EAAE,sEAAsE,CAAC;KACzF,MAAM,CAAC,WAAW,EAAE,wBAAwB,CAAC;KAC7C,MAAM,CAAC,KAAK,EAAE,GAAuB,EAAE,IAAI,EAAE,EAAE;IAC9C,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,sBAAsB,CAAC;KAC5E,MAAM,CAAC,QAAQ,EAAE,gDAAgD,EAAE,KAAK,CAAC;KACzE,MAAM,CAAC,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,CAAC;KAC5D,MAAM,CAAC,eAAe,EAAE,yBAAyB,EAAE,WAAW,CAAC;KAC/D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,YAAY,CAAC;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;KACxB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,+EAA+E,CAAC;KAC5F,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,sBAAsB,CAAC;KAC5E,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,qEAAqE,CAAC;KAClF,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,sBAAsB,CAAC;KAC5E,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,YAAY,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEL,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,sCAAsC,CAAC,CAAC;AAC3F,KAAK;KACF,OAAO,CAAC,kBAAkB,CAAC;KAC3B,WAAW,CAAC,6EAA6E,CAAC;KAC1F,MAAM,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5D,KAAK;KACF,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,gBAAgB,EAAE,CAAC;AAC3B,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;IACpD,OAAO,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Connector } from '@mcpolyglot/core';
2
+ import type { McpolyglotConfig } from '@mcpolyglot/config';
3
+ /** Per agent: which sources and tools it can use over HTTP, and which it can't. */
4
+ export declare function doctorAgents(cfg: McpolyglotConfig, connectors: Connector[]): void;
5
+ //# sourceMappingURL=doctor-agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-agents.d.ts","sourceRoot":"","sources":["../../src/commands/doctor-agents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,GAAG,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,CA0BjF"}
@@ -0,0 +1,33 @@
1
+ import pc from 'picocolors';
2
+ import { agentScopes } from '../factory.js';
3
+ import { section, ok, warn, stdoutSink, sym } from '../ui.js';
4
+ /** Per agent: which sources and tools it can use over HTTP, and which it can't. */
5
+ export function doctorAgents(cfg, connectors) {
6
+ if (!cfg.agents)
7
+ return;
8
+ section('Agents', stdoutSink);
9
+ for (const a of cfg.agents) {
10
+ const listed = cfg.sources.filter((s) => s.id in a.sources);
11
+ const scopes = agentScopes(a, listed);
12
+ const active = a.tokens.filter((t) => !t.revoked).length;
13
+ const detail = `${active} active token(s) · scopes: ${scopes.join(', ') || 'none'}`;
14
+ if (active === 0)
15
+ warn(a.id, `${detail} — every token is revoked`, stdoutSink);
16
+ else
17
+ ok(a.id, detail, stdoutSink);
18
+ for (const c of connectors) {
19
+ const policy = a.sources[c.id]?.policy ? pc.dim(' (own policy)') : '';
20
+ if (!(c.id in a.sources)) {
21
+ process.stdout.write(` ${sym.err} ${pc.dim(c.id)} ${pc.dim('not listed')}\n`);
22
+ continue;
23
+ }
24
+ for (const t of c.listPrimitiveTools()) {
25
+ const missing = t.scopes.filter((s) => !scopes.includes(s));
26
+ process.stdout.write(missing.length === 0
27
+ ? ` ${sym.ok} ${pc.bold(t.name)}${policy}\n`
28
+ : ` ${sym.err} ${t.name} ${pc.dim(`missing ${missing.join(', ')}`)}\n`);
29
+ }
30
+ }
31
+ }
32
+ }
33
+ //# sourceMappingURL=doctor-agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-agents.js","sourceRoot":"","sources":["../../src/commands/doctor-agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE9D,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,GAAqB,EAAE,UAAuB;IACzE,IAAI,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO;IACxB,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QACzD,MAAM,MAAM,GAAG,GAAG,MAAM,8BAA8B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC;QACpF,IAAI,MAAM,KAAK,CAAC;YAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,2BAA2B,EAAE,UAAU,CAAC,CAAC;;YAC1E,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBACpF,SAAS;YACX,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,kBAAkB,EAAE,EAAE,CAAC;gBACvC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,OAAO,CAAC,MAAM,KAAK,CAAC;oBAClB,CAAC,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI;oBACjD,CAAC,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAC/E,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -1,5 +1,15 @@
1
+ import type { TableSchema } from '@mcpolyglot/core';
1
2
  export interface DoctorOptions {
2
3
  config: string;
3
4
  }
4
5
  export declare function doctorCommand(opts: DoctorOptions): Promise<boolean>;
6
+ export interface PolicyReport {
7
+ problems: string[];
8
+ readable: string[];
9
+ writable: string[];
10
+ hidden: string[];
11
+ hiddenColumns: string[];
12
+ }
13
+ /** Check a source's policy against its live schema. `policy` must already parse. */
14
+ export declare function checkPolicy(raw: unknown, tables: TableSchema[]): PolicyReport;
5
15
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CA4EzE"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAsBpD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAyGzE;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,YAAY,CAyC7E"}