Skip to content

Commit c9c41ae

Browse files
committed
url elicitation (and AGENTS to be removed)
1 parent 618cf48 commit c9c41ae

File tree

7 files changed

+327
-55
lines changed

7 files changed

+327
-55
lines changed

src/everything/AGENTS.md

Lines changed: 99 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,99 @@
1-
# MCP "Everything" Server - Development Guidelines
2-
3-
## Build, Test & Run Commands
4-
5-
- Build: `npm run build` - Compiles TypeScript to JavaScript
6-
- Watch mode: `npm run watch` - Watches for changes and rebuilds automatically
7-
- Run STDIO server: `npm run start:stdio` - Starts the MCP server using stdio transport
8-
- Run SSE server: `npm run start:sse` - Starts the MCP server with SSE transport
9-
- Run StreamableHttp server: `npm run start:stremableHttp` - Starts the MCP server with StreamableHttp transport
10-
- Prepare release: `npm run prepare` - Builds the project for publishing
11-
12-
## Code Style Guidelines
13-
14-
- Use ES modules with `.js` extension in import paths
15-
- Strictly type all functions and variables with TypeScript
16-
- Follow zod schema patterns for tool input validation
17-
- Prefer async/await over callbacks and Promise chains
18-
- Place all imports at top of file, grouped by external then internal
19-
- Use descriptive variable names that clearly indicate purpose
20-
- Implement proper cleanup for timers and resources in server shutdown
21-
- Handle errors with try/catch blocks and provide clear error messages
22-
- Use consistent indentation (2 spaces) and trailing commas in multi-line objects
23-
- Match existing code style, import order, and module layout in the respective folder.
24-
- Use camelCase for variables/functions,
25-
- Use PascalCase for types/classes,
26-
- Use UPPER_CASE for constants
27-
- Use kebab-case for file names and registered tools, prompts, and resources.
28-
- Use verbs for tool names, e.g., `get-annotated-message` instead of `annotated-message`
29-
30-
## Extending the Server
31-
32-
The Everything Server is designed to be extended at well-defined points.
33-
See [Extension Points](docs/extension.md) and [Project Structure](docs/structure.md).
34-
The server factory is `src/everything/server/index.ts` and registers all features during startup as well as handling post-connection setup.
35-
36-
### High-level
37-
38-
- Tools live under `src/everything/tools/` and are registered via `registerTools(server)`.
39-
- Resources live under `src/everything/resources/` and are registered via `registerResources(server)`.
40-
- Prompts live under `src/everything/prompts/` and are registered via `registerPrompts(server)`.
41-
- Subscriptions and simulated update routines are under `src/everything/resources/subscriptions.ts`.
42-
- Logging helpers are under `src/everything/server/logging.ts`.
43-
- Transport managers are under `src/everything/transports/`.
44-
45-
### When adding a new feature
46-
47-
- Follow the existing file/module pattern in its folder (naming, exports, and registration function).
48-
- Export a `registerX(server)` function that registers new items with the MCP SDK in the same style as existing ones.
49-
- Wire your new module into the central index (e.g., update `tools/index.ts`, `resources/index.ts`, or `prompts/index.ts`).
50-
- Ensure schemas (for tools) are accurate JSON Schema and include helpful descriptions and examples.
51-
`server/index.ts` and usages in `logging.ts` and `subscriptions.ts`.
52-
- Keep the docs in `src/everything/docs/` up to date if you add or modify noteworthy features.
1+
# MCP Everything Server — Agent Memory
2+
3+
_Last oriented: 2026-02-13_
4+
5+
## Workspace + Scope
6+
7+
- Workspace root: `/home/ssmith/source/servers/src/everything`
8+
- This directory is the package root for this server.
9+
- Generated output is in `dist/`; **edit source `.ts` files only**.
10+
11+
## Fast Orientation Map
12+
13+
- CLI transport selector: `index.ts`
14+
- Server factory: `server/index.ts`
15+
- Creates `McpServer` with capabilities + instructions
16+
- Registers tools/resources/prompts
17+
- Installs subscription handlers
18+
- Registers capability-gated tools in `oninitialized`
19+
- Delays initial roots sync via `setTimeout(..., 350)`
20+
- Registries:
21+
- `tools/index.ts``registerTools(server)` + `registerConditionalTools(server)`
22+
- `resources/index.ts``registerResources(server)` + `readInstructions()`
23+
- `prompts/index.ts``registerPrompts(server)`
24+
- Key support files:
25+
- `resources/subscriptions.ts` (resource subscribe/unsubscribe + simulated updates)
26+
- `server/logging.ts` (simulated logging)
27+
- `server/roots.ts` (client roots sync/cache)
28+
29+
## Build / Test / Run
30+
31+
- Build: `npm run build`
32+
- Watch: `npm run watch`
33+
- Typecheck: `npx tsc --noEmit --pretty false`
34+
- Tests: `npm test`
35+
- STDIO: `npm run start:stdio`
36+
- SSE: `npm run start:sse`
37+
- Streamable HTTP: `npm run start:streamableHttp`
38+
- Prepare release: `npm run prepare`
39+
40+
## Search Setup (ripgrep-first)
41+
42+
Use rg with generated/vendor exclusions:
43+
44+
```bash
45+
rg -n --glob '!dist/**' --glob '!node_modules/**' '<pattern>'
46+
```
47+
48+
Useful presets:
49+
50+
```bash
51+
rg -n --glob '!dist/**' 'registerTools|registerResources|registerPrompts|registerConditionalTools' .
52+
rg -n --glob '!dist/**' 'createServer|oninitialized|setSubscriptionHandlers|syncRoots|cleanup' server tools resources transports
53+
rg -n --glob '!dist/**' 'z\.object|inputSchema|outputSchema|zodToJsonSchema' tools
54+
rg -n --glob '!dist/**' 'setInterval|setTimeout|clearInterval|clearTimeout|onclose|SIGINT' .
55+
```
56+
57+
## LSP Setup (TypeScript)
58+
59+
- Project config: `tsconfig.json` (extends `../../tsconfig.json`).
60+
- TS SDK path: `../../node_modules/typescript/lib`.
61+
- Confirm LSP/type health: `npx tsc --noEmit --pretty false`.
62+
- High-value symbol navigation targets:
63+
- `createServer`
64+
- `registerTools` / `registerConditionalTools`
65+
- `registerResources`
66+
- `registerPrompts`
67+
68+
## Coding Rules to Preserve
69+
70+
- ES modules with `.js` extension in TS imports.
71+
- Strong typing for functions/variables.
72+
- Prefer `async/await` over callback chains.
73+
- Tool input validation via zod schema patterns.
74+
- Keep imports at top; external imports before internal imports.
75+
- Use descriptive names.
76+
- Handle errors with `try/catch` and clear messages.
77+
- Ensure cleanup for timers/resources during shutdown.
78+
- Formatting: 2-space indent, trailing commas in multiline objects.
79+
- Naming:
80+
- camelCase: variables/functions
81+
- PascalCase: types/classes
82+
- UPPER_CASE: constants
83+
- kebab-case: filenames + registered tools/prompts/resources
84+
- Tool names should be verbs (e.g. `get-annotated-message`)
85+
86+
## Extension Workflow
87+
88+
1. Add module in `tools/`, `resources/`, or `prompts/` following existing pattern.
89+
2. Export `registerX(server)` from that module.
90+
3. Wire it in the domain index file.
91+
4. Keep schemas accurate and descriptive (for tools).
92+
5. Update `docs/` for notable behavioral changes.
93+
6. If registrations change, update tests that assert counts/content.
94+
95+
## Practical Gotchas
96+
97+
- Conditional tools are registered **after** client initialization.
98+
- Server instructions are loaded from `docs/instructions.md`.
99+
- Session-scoped behavior exists (logging, subscription updates, session resources).

src/everything/__tests__/registrations.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('Registration Index Files', () => {
5454
server: {
5555
getClientCapabilities: vi.fn(() => ({
5656
roots: {},
57-
elicitation: {},
57+
elicitation: { url: {} },
5858
sampling: {},
5959
})),
6060
},
@@ -67,14 +67,15 @@ describe('Registration Index Files', () => {
6767

6868
registerConditionalTools(mockServerWithCapabilities);
6969

70-
// Should register 3 conditional tools + 3 task-based tools when all capabilities present
71-
expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(3);
70+
// Should register 4 conditional tools + 3 task-based tools when all capabilities present
71+
expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(4);
7272

7373
const registeredTools = (
7474
mockServerWithCapabilities.registerTool as any
7575
).mock.calls.map((call: any[]) => call[0]);
7676
expect(registeredTools).toContain('get-roots-list');
7777
expect(registeredTools).toContain('trigger-elicitation-request');
78+
expect(registeredTools).toContain('trigger-url-elicitation-request');
7879
expect(registeredTools).toContain('trigger-sampling-request');
7980

8081
// Task-based tools are registered via experimental.tasks.registerToolTask

src/everything/__tests__/tools.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { registerToggleSimulatedLoggingTool } from '../tools/toggle-simulated-lo
1313
import { registerToggleSubscriberUpdatesTool } from '../tools/toggle-subscriber-updates.js';
1414
import { registerTriggerSamplingRequestTool } from '../tools/trigger-sampling-request.js';
1515
import { registerTriggerElicitationRequestTool } from '../tools/trigger-elicitation-request.js';
16+
import { registerTriggerUrlElicitationRequestTool } from '../tools/trigger-url-elicitation-request.js';
1617
import { registerGetRootsListTool } from '../tools/get-roots-list.js';
1718
import { registerGZipFileAsResourceTool } from '../tools/gzip-file-as-resource.js';
1819

@@ -706,6 +707,101 @@ describe('Tools', () => {
706707
});
707708
});
708709

710+
describe('trigger-url-elicitation-request', () => {
711+
it('should not register when client does not support URL elicitation', () => {
712+
const handlers: Map<string, Function> = new Map();
713+
const mockServer = {
714+
registerTool: vi.fn((name: string, config: any, handler: Function) => {
715+
handlers.set(name, handler);
716+
}),
717+
server: {
718+
getClientCapabilities: vi.fn(() => ({ elicitation: { form: {} } })),
719+
},
720+
} as unknown as McpServer;
721+
722+
registerTriggerUrlElicitationRequestTool(mockServer);
723+
724+
expect(mockServer.registerTool).not.toHaveBeenCalled();
725+
});
726+
727+
it('should register when client supports URL elicitation', () => {
728+
const handlers: Map<string, Function> = new Map();
729+
const mockServer = {
730+
registerTool: vi.fn((name: string, config: any, handler: Function) => {
731+
handlers.set(name, handler);
732+
}),
733+
server: {
734+
getClientCapabilities: vi.fn(() => ({ elicitation: { url: {} } })),
735+
createElicitationCompletionNotifier: vi.fn(() => vi.fn()),
736+
},
737+
} as unknown as McpServer;
738+
739+
registerTriggerUrlElicitationRequestTool(mockServer);
740+
741+
expect(mockServer.registerTool).toHaveBeenCalledWith(
742+
'trigger-url-elicitation-request',
743+
expect.objectContaining({
744+
title: 'Trigger URL Elicitation Request Tool',
745+
description: expect.stringContaining('URL elicitation'),
746+
}),
747+
expect.any(Function)
748+
);
749+
});
750+
751+
it('should send URL-mode elicitation request and notify completion when requested', async () => {
752+
const handlers: Map<string, Function> = new Map();
753+
const mockSendRequest = vi.fn().mockResolvedValue({
754+
action: 'accept',
755+
});
756+
const mockNotifyComplete = vi.fn().mockResolvedValue(undefined);
757+
758+
const mockServer = {
759+
registerTool: vi.fn((name: string, config: any, handler: Function) => {
760+
handlers.set(name, handler);
761+
}),
762+
server: {
763+
getClientCapabilities: vi.fn(() => ({ elicitation: { url: {} } })),
764+
createElicitationCompletionNotifier: vi
765+
.fn()
766+
.mockReturnValue(mockNotifyComplete),
767+
},
768+
} as unknown as McpServer;
769+
770+
registerTriggerUrlElicitationRequestTool(mockServer);
771+
772+
const handler = handlers.get('trigger-url-elicitation-request')!;
773+
const result = await handler(
774+
{
775+
url: 'https://example.com/verify',
776+
message: 'Open this page to verify your identity',
777+
elicitationId: 'elicitation-123',
778+
sendCompletionNotification: true,
779+
},
780+
{ sendRequest: mockSendRequest }
781+
);
782+
783+
expect(mockSendRequest).toHaveBeenCalledWith(
784+
expect.objectContaining({
785+
method: 'elicitation/create',
786+
params: expect.objectContaining({
787+
mode: 'url',
788+
url: 'https://example.com/verify',
789+
message: 'Open this page to verify your identity',
790+
elicitationId: 'elicitation-123',
791+
}),
792+
}),
793+
expect.anything(),
794+
expect.anything()
795+
);
796+
797+
expect(mockServer.server.createElicitationCompletionNotifier).toHaveBeenCalledWith(
798+
'elicitation-123'
799+
);
800+
expect(mockNotifyComplete).toHaveBeenCalledTimes(1);
801+
expect(result.content[0].text).toContain('URL elicitation action: accept');
802+
});
803+
});
804+
709805
describe('get-roots-list', () => {
710806
it('should not register when client does not support roots', () => {
711807
const { mockServer } = createMockServer();

src/everything/docs/features.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
- `trigger-long-running-operation` (tools/trigger-trigger-long-running-operation.ts): Simulates a multi-step operation over a given `duration` and number of `steps`; reports progress via `notifications/progress` when a `progressToken` is provided by the client.
2323
- `toggle-simulated-logging` (tools/toggle-simulated-logging.ts): Starts or stops simulated, random‑leveled logging for the invoking session. Respects the client’s selected minimum logging level.
2424
- `toggle-subscriber-updates` (tools/toggle-subscriber-updates.ts): Starts or stops simulated resource update notifications for URIs the invoking session has subscribed to.
25+
- `trigger-elicitation-request` (tools/trigger-elicitation-request.ts): Issues an `elicitation/create` request using form-mode fields (strings, numbers, booleans, enums, and format validation) and returns the resulting action/content.
26+
- `trigger-url-elicitation-request` (tools/trigger-url-elicitation-request.ts): Issues an `elicitation/create` request in URL mode (`mode: "url"`) with an `elicitationId`, and can optionally emit `notifications/elicitation/complete` after acceptance. Requires client capability `elicitation.url`.
2527
- `trigger-sampling-request` (tools/trigger-sampling-request.ts): Issues a `sampling/createMessage` request to the client/LLM using provided `prompt` and optional generation controls; returns the LLM's response payload.
2628
- `simulate-research-query` (tools/simulate-research-query.ts): Demonstrates MCP Tasks (SEP-1686) with a simulated multi-stage research operation. Accepts `topic` and `ambiguous` parameters. Returns a task that progresses through stages with status updates. If `ambiguous` is true and client supports elicitation, sends an elicitation request directly to gather clarification before completing.
2729
- `trigger-sampling-request-async` (tools/trigger-sampling-request-async.ts): Demonstrates bidirectional tasks where the server sends a sampling request that the client executes as a background task. Server polls for status and retrieves the LLM result when complete. Requires client to support `tasks.requests.sampling.createMessage`.

src/everything/docs/structure.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ src/everything
5252
│ ├── toggle-subscriber-updates.ts
5353
│ ├── trigger-elicitation-request.ts
5454
│ ├── trigger-long-running-operation.ts
55+
│ ├── trigger-url-elicitation-request.ts
5556
│ └── trigger-sampling-request.ts
5657
└── transports
5758
├── sse.ts
@@ -149,6 +150,8 @@ src/everything
149150
- `GZIP_ALLOWED_DOMAINS` (comma-separated allowlist; empty means all domains allowed)
150151
- `trigger-elicitation-request.ts`
151152
- Registers a `trigger-elicitation-request` tool that sends an `elicitation/create` request to the client/LLM and returns the elicitation result.
153+
- `trigger-url-elicitation-request.ts`
154+
- Registers a `trigger-url-elicitation-request` tool that sends an out-of-band URL-mode `elicitation/create` request (`mode: "url"`) including an `elicitationId`, and can optionally emit `notifications/elicitation/complete` after acceptance.
152155
- `trigger-sampling-request.ts`
153156
- Registers a `trigger-sampling-request` tool that sends a `sampling/createMessage` request to the client/LLM and returns the sampling result.
154157
- `get-structured-content.ts`

src/everything/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { registerTriggerSamplingRequestTool } from "./trigger-sampling-request.j
1717
import { registerTriggerSamplingRequestAsyncTool } from "./trigger-sampling-request-async.js";
1818
import { registerTriggerElicitationRequestAsyncTool } from "./trigger-elicitation-request-async.js";
1919
import { registerSimulateResearchQueryTool } from "./simulate-research-query.js";
20+
import { registerTriggerUrlElicitationRequestTool } from "./trigger-url-elicitation-request.js";
2021

2122
/**
2223
* Register the tools with the MCP server.
@@ -44,6 +45,7 @@ export const registerTools = (server: McpServer) => {
4445
export const registerConditionalTools = (server: McpServer) => {
4546
registerGetRootsListTool(server);
4647
registerTriggerElicitationRequestTool(server);
48+
registerTriggerUrlElicitationRequestTool(server);
4749
registerTriggerSamplingRequestTool(server);
4850
// Task-based research tool (uses experimental tasks API)
4951
registerSimulateResearchQueryTool(server);

0 commit comments

Comments
 (0)