Skip to content

Commit 6cde739

Browse files
authored
feat: in-process toolRulesProvider + analyzeApiCalls in atp-compiler (#69)
1 parent 09cea15 commit 6cde739

8 files changed

Lines changed: 787 additions & 11 deletions

File tree

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* Verifies that `toolRulesProvider` runs for the in-process session path,
3+
* not only for HTTP requests. The provider reads rules from ctx.headers,
4+
* which the session populates from per-call `headers` options.
5+
*
6+
* Before this change, callers in the same process had to supply
7+
* `body.toolRules` (via client.execute(code, { toolRules })) — the
8+
* provider registered on createServer was dormant for in-process calls.
9+
*/
10+
11+
import { createServer, type AgentToolProtocolServer } from '../../packages/server/src/index';
12+
import { AgentToolProtocolClient } from '../../packages/client/src/index';
13+
14+
describe('toolRulesProvider runs for the in-process session path', () => {
15+
let server: AgentToolProtocolServer;
16+
let client: AgentToolProtocolClient;
17+
18+
beforeAll(async () => {
19+
server = createServer({
20+
// Provider reads an opaque header and returns per-call rules.
21+
toolRulesProvider: (ctx) => {
22+
const raw = ctx.headers['x-test-tool-rules'];
23+
if (!raw) return undefined;
24+
try {
25+
return JSON.parse(raw);
26+
} catch {
27+
return undefined;
28+
}
29+
},
30+
});
31+
32+
server.use({
33+
name: 'math',
34+
type: 'custom',
35+
functions: [
36+
{
37+
name: 'add',
38+
description: 'Add two numbers',
39+
inputSchema: {
40+
type: 'object',
41+
properties: { a: { type: 'number' }, b: { type: 'number' } },
42+
required: ['a', 'b'],
43+
},
44+
handler: async (input: unknown) => {
45+
const { a, b } = input as { a: number; b: number };
46+
return { result: a + b };
47+
},
48+
},
49+
],
50+
});
51+
server.use({
52+
name: 'text',
53+
type: 'custom',
54+
functions: [
55+
{
56+
name: 'uppercase',
57+
description: 'Uppercase text',
58+
inputSchema: {
59+
type: 'object',
60+
properties: { text: { type: 'string' } },
61+
required: ['text'],
62+
},
63+
handler: async (input: unknown) => {
64+
const { text } = input as { text: string };
65+
return { result: text.toUpperCase() };
66+
},
67+
},
68+
],
69+
});
70+
71+
await server.start();
72+
client = new AgentToolProtocolClient({ server: server as any });
73+
await client.init({ name: 'test-client', version: '1.0.0' });
74+
await client.connect();
75+
});
76+
77+
describe('handleExplore', () => {
78+
test('provider applies rules from per-call headers', async () => {
79+
// With math-only rules coming from the provider's header read,
80+
// the text group should disappear from the explore tree.
81+
const rules = JSON.stringify({ allowOnlyApiGroups: ['math'] });
82+
const r = (await client.exploreAPI('/custom', {
83+
headers: { 'x-test-tool-rules': rules },
84+
} as any)) as { items: Array<{ name: string }> };
85+
86+
const groupNames = r.items.map((i) => i.name);
87+
expect(groupNames).toContain('math');
88+
expect(groupNames).not.toContain('text');
89+
});
90+
91+
test('body.toolRules takes precedence over provider', async () => {
92+
// Provider says math-only, but caller's explicit body.toolRules says
93+
// text-only. Explicit wins.
94+
const providerRules = JSON.stringify({ allowOnlyApiGroups: ['math'] });
95+
const r = (await client.exploreAPI('/custom', {
96+
headers: { 'x-test-tool-rules': providerRules },
97+
toolRules: { allowOnlyApiGroups: ['text'] },
98+
} as any)) as { items: Array<{ name: string }> };
99+
100+
const groupNames = r.items.map((i) => i.name);
101+
expect(groupNames).toContain('text');
102+
expect(groupNames).not.toContain('math');
103+
});
104+
105+
test('no provider header and no body rules: unrestricted', async () => {
106+
const r = (await client.exploreAPI('/custom')) as {
107+
items: Array<{ name: string }>;
108+
};
109+
const groupNames = r.items.map((i) => i.name);
110+
expect(groupNames).toEqual(expect.arrayContaining(['math', 'text']));
111+
});
112+
});
113+
});

0 commit comments

Comments
 (0)