spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / mods / sec-default / tests / sec-default.test.ts
196 lines · 4.7 KBRaw
1import type {
2 CommandRunInput,
3 On,
4 PromptSectionInput,
5 SessionStartInput,
6 Settings,
7 ToolInfo,
8} from 'claude-code'
9import type { Plugin } from 'claude-code/testing'
10import { expect, test, tier } from 'claude-code/testing'
11
12tier('prepend')
13
14const SESSION: SessionStartInput = {
15 surface: 'terminal',
16 isInteractive: true,
17 cwd: '/work',
18}
19const TOOLS_COMMAND: CommandRunInput = {
20 command: 'tools',
21 args: '',
22 origin: { kind: 'composer' },
23}
24const ALLOWLIST: Settings = { allowedMcpServers: [{ serverName: 'corp' }] }
25const NO_ALLOWLIST: Settings = { permissions: { allow: [] } }
26const MEMORY: PromptSectionInput = { name: 'memory', text: 'the org says hi' }
27const TOOLS: ToolInfo[] = [
28 {
29 name: 'mcp__corp__search',
30 description: 'Searches the corp wiki.',
31 mcp: true,
32 },
33 { name: 'Bash', description: 'Runs a command.', mcp: false },
34]
35
36/**
37 * A plugin that registers a tool when the session starts, in the tier
38 * given: the person's own by default.
39 */
40const registering = (name: string, tier?: Plugin['tier']): Plugin => ({
41 name,
42 tier,
43 register(on) {
44 on('session.start', async ($, e, next) => {
45 await $.tool.register({
46 name: 'greet',
47 description: 'Says hello.',
48 inputSchema: { type: 'object' },
49 })
50
51 return next(e)
52 })
53 },
54})
55
56/**
57 * A session starting, where each tool a plugin asks for is registered and
58 * kept by the name of the plugin that asked.
59 */
60function toolsRegistered(on: On) {
61 const registered: string[] = []
62 on('session.start', ($, e) => ({ cwd: e.cwd }))
63 on('tool.register', ($, e, next) => {
64 registered.push(next.origin.plugin)
65
66 return { value: { tool: `mcp__plugin_${next.origin.plugin}__${e.name}` } }
67 })
68
69 return registered
70}
71
72/**
73 * A plugin the person installed that hides the organization's tools from
74 * the listing and relabels every other one.
75 */
76const relabeling: Plugin = {
77 name: 'relabeling',
78 register(on) {
79 on('tool.list', async ($, e, next) => {
80 const listed = await next(e)
81
82 return listed.value === undefined
83 ? listed
84 : {
85 value: listed.value
86 .filter(tool => !tool.name.startsWith('mcp__corp__'))
87 .map(tool => ({ ...tool, description: 'relabeled' })),
88 }
89 })
90 },
91}
92
93/**
94 * A plugin whose `/tools` answers the tool listing it reads, one
95 * `name: description` line per tool.
96 */
97const listing: Plugin = {
98 name: 'listing',
99 register(on) {
100 on('command.run', { command: 'tools' }, async $ => ({
101 text: (await $.tool.list())
102 .map(tool => `${tool.name}: ${tool.description}`)
103 .join('\n'),
104 }))
105 },
106}
107
108/**
109 * A plugin the person installed that drops the memory section.
110 */
111const dropping: Plugin = {
112 name: 'dropping',
113 register(on) {
114 on('prompt.section', () => ({ text: null }))
115 },
116}
117
118/**
119 * The organization's own plugin, in its last tier, which signs the section.
120 */
121const signing: Plugin = {
122 name: 'signing',
123 tier: 'append',
124 register(on) {
125 on('prompt.section', ($, e, next) =>
126 next({ ...e, text: `${e.text} (signed)` }),
127 )
128 },
129}
130
131test(
132 'under an MCP allowlist a plugin the person installed may not add a tool',
133 { plugins: [registering('mine'), registering('bundled', 'builtin')] },
134 async ($, on) => {
135 on('settings.read', () => ({ value: ALLOWLIST }))
136 const registered = toolsRegistered(on)
137
138 await $.session.start(SESSION)
139
140 expect(registered).toEqual(['bundled'])
141 },
142)
143
144test(
145 'with no allowlist, a plugin the person installed adds its tool',
146 { plugins: [registering('mine')] },
147 async ($, on) => {
148 on('settings.read', () => ({ value: NO_ALLOWLIST }))
149 const registered = toolsRegistered(on)
150
151 await $.session.start(SESSION)
152
153 expect(registered).toEqual(['mine'])
154 },
155)
156
157test(
158 'a policy that cannot be read counts as one in force',
159 { plugins: [registering('mine')] },
160 async ($, on) => {
161 on('settings.read', () => ({ deny: 'managed settings unreadable' }))
162 const registered = toolsRegistered(on)
163
164 await $.session.start(SESSION)
165
166 expect(registered).toEqual([])
167 },
168)
169
170test(
171 'the organization tools are listed as its tiers listed them',
172 { plugins: [relabeling, listing] },
173 async ($, on) => {
174 on('settings.read', () => ({ value: ALLOWLIST }))
175 on('tool.list', () => ({ value: TOOLS }))
176
177 const { text } = await $.command.run(TOOLS_COMMAND)
178
179 expect(text?.split('\n')).toEqual([
180 'mcp__corp__search: Searches the corp wiki.',
181 'Bash: relabeled',
182 ])
183 },
184)
185
186test(
187 'a prompt section skips the plugins the person installed, never the organization ones',
188 { plugins: [dropping, signing] },
189 async ($, on) => {
190 on('prompt.section', ($, e) => ({ text: e.text }))
191
192 expect(await $.prompt.section(MEMORY)).toEqual({
193 text: 'the org says hi (signed)',
194 })
195 },
196)