@mcpflo/server-everything 0.0.1

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 (44) hide show
  1. package/README.md +84 -0
  2. package/dist/createServer.js +42 -0
  3. package/dist/docs/architecture.md +19 -0
  4. package/dist/docs/extension.md +20 -0
  5. package/dist/docs/features.md +23 -0
  6. package/dist/docs/how-it-works.md +22 -0
  7. package/dist/docs/instructions.md +16 -0
  8. package/dist/docs/startup.md +20 -0
  9. package/dist/docs/structure.md +21 -0
  10. package/dist/index.js +10 -0
  11. package/dist/prompts/args.js +19 -0
  12. package/dist/prompts/completions.js +32 -0
  13. package/dist/prompts/index.js +19 -0
  14. package/dist/prompts/resource.js +35 -0
  15. package/dist/prompts/simple.js +16 -0
  16. package/dist/resources/docsDir.js +18 -0
  17. package/dist/resources/file-resources.js +52 -0
  18. package/dist/resources/index.js +24 -0
  19. package/dist/resources/session.js +34 -0
  20. package/dist/resources/subscriptions.js +38 -0
  21. package/dist/resources/templates.js +91 -0
  22. package/dist/server/logging.js +45 -0
  23. package/dist/server/roots.js +44 -0
  24. package/dist/tools/add.js +12 -0
  25. package/dist/tools/annotated-message.js +67 -0
  26. package/dist/tools/echo.js +12 -0
  27. package/dist/tools/get-resource-links.js +36 -0
  28. package/dist/tools/get-resource-reference.js +32 -0
  29. package/dist/tools/get-roots-list.js +51 -0
  30. package/dist/tools/get-structured-content.js +35 -0
  31. package/dist/tools/get-tiny-image.js +23 -0
  32. package/dist/tools/gzip-file-as-resource.js +115 -0
  33. package/dist/tools/index.js +63 -0
  34. package/dist/tools/print-env.js +10 -0
  35. package/dist/tools/simulate-research-query.js +173 -0
  36. package/dist/tools/toggle-simulated-logging.js +33 -0
  37. package/dist/tools/toggle-subscriber-updates.js +33 -0
  38. package/dist/tools/trigger-elicitation-request-async.js +136 -0
  39. package/dist/tools/trigger-elicitation-request.js +167 -0
  40. package/dist/tools/trigger-long-running-operation.js +36 -0
  41. package/dist/tools/trigger-sampling-request-async.js +112 -0
  42. package/dist/tools/trigger-sampling-request.js +39 -0
  43. package/dist/tools/trigger-url-elicitation.js +0 -0
  44. package/package.json +36 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @mcpflo/server-everything
2
+
3
+ A deterministic, stdio-only MCP test-fixture server, built for stress-testing
4
+ [MCPFlo](../..) — a desktop testing tool for MCP servers. It exercises the
5
+ full protocol surface (tools, resources, prompts, elicitation, sampling,
6
+ tasks, logging, subscriptions) so MCPFlo's own UI has something real to
7
+ render against, beyond the official
8
+ [`@modelcontextprotocol/server-everything`](https://github.com/modelcontextprotocol/servers/tree/main/src/everything)
9
+ it was originally seeded with.
10
+
11
+ Every tool's description ends with "Demo/test fixture." so a user connecting
12
+ to this server doesn't mistake an intentional failure, delay, or capability
13
+ mismatch for a bug in MCPFlo itself.
14
+
15
+ ## Quick start
16
+
17
+ ```bash
18
+ npm run build
19
+ node dist/index.js
20
+ ```
21
+
22
+ Or point any MCP client at it directly via stdio — no configuration required,
23
+ every tool has sane defaults for its arguments.
24
+
25
+ ## What's here
26
+
27
+ **Tools** — 19, covering every tool in the upstream reference server
28
+ (including capability-gated ones like `get-roots-list` and
29
+ `trigger-url-elicitation`), plus fixes for several real bugs found while
30
+ porting them (see `docs/` for specifics). Capability-gated tools are
31
+ registered from the SDK's `oninitialized` hook, not eagerly — they only
32
+ appear in `tools/list` for clients that actually declare the capability they
33
+ need, matching upstream's own behavior exactly.
34
+
35
+ **Prompts** — 4: a no-argument prompt, one with required/optional arguments,
36
+ one with dependent argument completion, and one that embeds a resource.
37
+
38
+ **Resources** — 7 static documentation files (this package's own `docs/`,
39
+ describing its architecture, features, and how to extend it) plus 2 dynamic
40
+ resource templates (`mcpflo://dynamic/text/{resourceId}`,
41
+ `mcpflo://dynamic/blob/{resourceId}`) that regenerate content with a live
42
+ timestamp on every read and are deliberately excluded from `resources/list`.
43
+
44
+ Read [`docs/architecture.md`](docs/architecture.md) and
45
+ [`docs/extension.md`](docs/extension.md) for the actual code layout and how
46
+ to add a new tool or resource — those docs are themselves served by this
47
+ server, so they're worth reading through a connected client too.
48
+
49
+ ## Testing
50
+
51
+ ```bash
52
+ npm test
53
+ ```
54
+
55
+ 61 tests across 26 files, one test file per tool/prompt/resource module,
56
+ using the MCP SDK's `InMemoryTransport` to connect a real `Client` to a real
57
+ server instance in-process — no spawned child processes, no mocking of
58
+ protocol behavior. Most of the suite runs in milliseconds; a handful of tests
59
+ that verify actual timing/polling behavior (`simulate-research-query`, the
60
+ async trigger tools) deliberately use real timers and take a few seconds
61
+ each.
62
+
63
+ ## Design notes
64
+
65
+ - **Deterministic by default.** No Playwright, no browser automation, no
66
+ live network calls — with one intentional exception: `gzip-file-as-resource`
67
+ defaults to fetching a real URL, kept for parity with the upstream
68
+ reference server it was ported from.
69
+ - **Tools/resources are added one at a time, by hand.** There's no bulk
70
+ generator, even for template-shaped families (see `docs/extension.md`).
71
+ - **Not yet wired as MCPFlo's seeded default server.** Currently a
72
+ workspace-local devDependency, exercised by this package's own test suite.
73
+ If it's ever seeded for real end users, it should launch a `sane`/`benign`
74
+ preset, not the full adversarial catalog — that preset system doesn't
75
+ exist yet.
76
+
77
+ ## Publishing
78
+
79
+ This package declares `mcpName` for eventual MCP registry publication, in
80
+ addition to standard npm publishing. Neither has happened yet. When it does,
81
+ pin the exact version anywhere this package is spawned via `npx` (e.g. in
82
+ MCPFlo's own seeded server config) rather than leaving it unpinned — a new
83
+ publish should only reach users through a reviewed MCPFlo release, not
84
+ silently on next connect.
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createServer = createServer;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const index_js_1 = require("@modelcontextprotocol/sdk/experimental/tasks/index.js");
6
+ const index_1 = require("./tools/index");
7
+ const session_1 = require("./resources/session");
8
+ const logging_1 = require("./server/logging");
9
+ const subscriptions_1 = require("./resources/subscriptions");
10
+ const index_2 = require("./resources/index");
11
+ const index_3 = require("./prompts/index");
12
+ // Builds a fully-configured server, not yet connected to any transport.
13
+ // Shared by index.ts (stdio, real usage) and the test harness (in-memory).
14
+ function createServer() {
15
+ const server = new mcp_js_1.McpServer({
16
+ name: '@mcpflo/server-everything',
17
+ version: '0.0.1'
18
+ }, {
19
+ taskStore: new index_js_1.InMemoryTaskStore(),
20
+ instructions: (0, index_2.readInstructions)(),
21
+ // Unlike tools/resources, task-creation support isn't auto-declared by
22
+ // registering a task tool — it must be advertised explicitly here. Same
23
+ // for resources.subscribe: registering a resource never implies it.
24
+ capabilities: {
25
+ tasks: { requests: { tools: { call: {} } } },
26
+ logging: {},
27
+ resources: { subscribe: true }
28
+ }
29
+ });
30
+ (0, index_1.registerTools)(server);
31
+ (0, session_1.initSessionResources)(server);
32
+ (0, logging_1.registerLoggingCapability)(server);
33
+ (0, subscriptions_1.registerSubscriptionsCapability)(server);
34
+ (0, index_2.registerResources)(server);
35
+ (0, index_3.registerPrompts)(server);
36
+ // Capability-gated tools are registered here, once the client's initialize
37
+ // handshake has completed and its declared capabilities are actually known.
38
+ server.server.oninitialized = () => {
39
+ (0, index_1.registerConditionalTools)(server);
40
+ };
41
+ return server;
42
+ }
@@ -0,0 +1,19 @@
1
+ # Architecture
2
+
3
+ `@mcpflo/server-everything` is a single stdio MCP server (`src/index.ts`) built
4
+ from small, individually-registered pieces:
5
+
6
+ - `src/tools/` — one file per tool, each exporting a `register<Name>(server)`
7
+ function that calls `server.registerTool(...)` directly. `src/tools/index.ts`
8
+ is a barrel that aggregates them into `registerTools(server)`.
9
+ - `src/resources/` — resource-side infrastructure: static file resources
10
+ (this doc set), session-scoped dynamic resources (e.g. gzip output),
11
+ templated resources, and the subscribe/unsubscribe handlers.
12
+ - `src/server/` — server-wide capability plumbing that isn't resource- or
13
+ tool-specific, e.g. the simulated logging level tracking.
14
+
15
+ Tools and resources are added one at a time by hand — there is no bulk
16
+ generator. `index.ts` wires everything together: constructs the `McpServer`,
17
+ declares capabilities that aren't auto-detected by the SDK (`tasks`,
18
+ `logging`, `resources.subscribe`), registers all tools/resources, then
19
+ connects over stdio.
@@ -0,0 +1,20 @@
1
+ # Extending This Server
2
+
3
+ To add a new tool:
4
+
5
+ 1. Create `src/tools/my-new-tool.ts`, exporting `registerMyNewTool(server)`
6
+ that calls `server.registerTool(...)` directly (raw-shape zod for
7
+ `inputSchema`, not a wrapped `z.object(...)`, to match the rest of this
8
+ codebase).
9
+ 2. Append its description text with `Demo/test fixture.` so a real user
10
+ connecting to this server understands intentional failures/delays aren't
11
+ a bug.
12
+ 3. Import and add it to the array in `src/tools/index.ts`.
13
+ 4. Rebuild (`npm run build`) and manually verify with a raw JSON-RPC smoke
14
+ test over stdio before wiring it into MCPFlo or the e2e suite.
15
+
16
+ To add a new resource, follow the same one-file-per-item pattern under
17
+ `src/resources/`. If it needs a capability the SDK doesn't auto-declare
18
+ (check by trying it first — `tasks`, `logging`, and `resources.subscribe`
19
+ all needed this), add it explicitly to the `capabilities` object in
20
+ `src/index.ts`.
@@ -0,0 +1,23 @@
1
+ # Features
2
+
3
+ This server exercises a broad slice of the MCP protocol, deliberately kept
4
+ deterministic — no real chaos, no live browser automation, one exception
5
+ (the gzip tool's default network fetch, kept for compatibility with the
6
+ upstream reference server it was ported from).
7
+
8
+ - **Tools**: primitives, structured content, resource links, binary content,
9
+ progress notifications, cancellation-safe long-running work.
10
+ - **Resources**: static file resources, session-scoped dynamic resources,
11
+ subscribe/unsubscribe with simulated update notifications.
12
+ - **Elicitation**: form-mode and URL-mode, both request-path
13
+ (`elicitation/create`) and error-path (`UrlElicitationRequiredError`).
14
+ - **Sampling**: server-initiated `sampling/createMessage`, synchronous and
15
+ task-based (bidirectional MCP Tasks).
16
+ - **Tasks (SEP-1686)**: long-running tool execution via `tasks/get` and
17
+ `tasks/result`, including a mid-flight elicitation pause
18
+ (`input_required`).
19
+ - **Logging**: `logging/setLevel` with real RFC 5424 severity filtering.
20
+
21
+ Every tool's description ends with a "Demo/test fixture" note so a user
22
+ connecting to this server doesn't mistake intentional failures or delays for
23
+ a bug in MCPFlo itself.
@@ -0,0 +1,22 @@
1
+ # How It Works
2
+
3
+ A single Node process talks MCP over stdio — no HTTP, no browser, no
4
+ external services except where a tool's whole purpose is to demonstrate one
5
+ (the gzip tool's optional network fetch).
6
+
7
+ Request lifecycle for a typical tool call:
8
+
9
+ 1. Client sends `tools/call`.
10
+ 2. The registered handler runs, using only what the SDK gives it
11
+ (`args`, `extra.sendRequest`, `extra.sessionId`, etc.) — no shared mutable
12
+ state between unrelated tools.
13
+ 3. Tools that need server-initiated round trips (elicitation, sampling) call
14
+ `extra.sendRequest(...)` and await the client's reply on the same
15
+ connection.
16
+ 4. Tools that register a resource at call time (e.g. gzip output) do so
17
+ through `server.registerResource(...)`, which triggers
18
+ `notifications/resources/list_changed` automatically.
19
+
20
+ Several capabilities are intentionally *not* auto-declared by the SDK and
21
+ have to be turned on explicitly at server construction: `tasks`, `logging`,
22
+ and `resources.subscribe`. See `architecture.md` for where that happens.
@@ -0,0 +1,16 @@
1
+ # Instructions
2
+
3
+ Connect to this server the same way as any stdio MCP server: spawn
4
+ `node dist/index.js` (or the published `mcpflo-server-everything` bin) and
5
+ speak MCP over its stdin/stdout.
6
+
7
+ Nothing here requires configuration to get started — every tool has sane
8
+ defaults for its arguments. A few tools depend on the connecting client's
9
+ declared capabilities and will return a clear `isError` message instead of
10
+ failing silently if the client doesn't support what they need:
11
+
12
+ - `trigger-elicitation-request` / `-async` need `capabilities.elicitation`.
13
+ - `trigger-url-elicitation` needs `capabilities.elicitation.url`.
14
+ - `trigger-sampling-request` / `-async` need `capabilities.sampling`.
15
+
16
+ Everything else works with a bare-minimum client.
@@ -0,0 +1,20 @@
1
+ # Startup
2
+
3
+ On launch, before the transport connects:
4
+
5
+ 1. The `McpServer` is constructed with an in-memory task store and explicit
6
+ `tasks`/`logging`/`resources.subscribe` capabilities (none of these are
7
+ inferred automatically by the SDK from registering a tool or resource).
8
+ 2. All tools register themselves via `registerTools(server)`.
9
+ 3. A throwaway resource is registered and immediately disabled
10
+ (`initSessionResources`) — purely to work around an SDK quirk where the
11
+ very first `registerResource()` call tries to finalize the resources
12
+ capability, which throws if it happens after `connect()`. Warming it up
13
+ here means later, real dynamic resource registrations (e.g. from the
14
+ gzip tool) work without special-casing.
15
+ 4. Logging (`registerLoggingCapability`) and subscription
16
+ (`registerSubscriptionsCapability`) request handlers are wired up.
17
+ 5. The server connects over stdio and starts serving requests.
18
+
19
+ There is no seeding of demo data beyond this file-resource set — everything
20
+ else appears only when a tool creates it.
@@ -0,0 +1,21 @@
1
+ # Structure
2
+
3
+ ```
4
+ packages/server-everything/
5
+ ├── docs/ this file set — copied into dist/ at build time
6
+ ├── src/
7
+ │ ├── index.ts server construction, capability wiring, connect
8
+ │ ├── tools/ one file per tool + a barrel (index.ts)
9
+ │ ├── resources/
10
+ │ │ ├── templates.ts static text/blob resource-reference helpers
11
+ │ │ ├── session.ts dynamic session-scoped resource registration
12
+ │ │ ├── subscriptions.ts resources/subscribe + simulated update loop
13
+ │ │ └── file-resources.ts registers this docs/ set as static resources
14
+ │ └── server/
15
+ │ └── logging.ts logging/setLevel + simulated log messages
16
+ ├── package.json
17
+ └── tsconfig.json
18
+ ```
19
+
20
+ New tools and resources are added one file at a time, then wired into the
21
+ relevant barrel — never generated in bulk.
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
5
+ const createServer_1 = require("./createServer");
6
+ const server = (0, createServer_1.createServer)();
7
+ server.connect(new stdio_js_1.StdioServerTransport()).catch((error) => {
8
+ console.error(error);
9
+ process.exit(1);
10
+ });
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerArgumentsPrompt = registerArgumentsPrompt;
4
+ const zod_1 = require("zod");
5
+ function registerArgumentsPrompt(server) {
6
+ server.registerPrompt('args-prompt', {
7
+ title: 'Arguments Prompt',
8
+ description: 'A prompt with two arguments, one required and one optional. Demo/test fixture.',
9
+ argsSchema: {
10
+ city: zod_1.z.string().describe('Name of the city'),
11
+ state: zod_1.z.string().optional().describe('Name of the state')
12
+ }
13
+ }, (args) => {
14
+ const location = `${args.city}${args.state ? `, ${args.state}` : ''}`;
15
+ return {
16
+ messages: [{ role: 'user', content: { type: 'text', text: `What's weather in ${location}?` } }]
17
+ };
18
+ });
19
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPromptWithCompletions = registerPromptWithCompletions;
4
+ const zod_1 = require("zod");
5
+ const completable_js_1 = require("@modelcontextprotocol/sdk/server/completable.js");
6
+ const DEPARTMENT_MEMBERS = {
7
+ Engineering: ['Alice', 'Bob', 'Charlie'],
8
+ Sales: ['David', 'Eve', 'Frank'],
9
+ Marketing: ['Grace', 'Henry', 'Iris'],
10
+ Support: ['John', 'Kim', 'Lee']
11
+ };
12
+ function registerPromptWithCompletions(server) {
13
+ server.registerPrompt('completable-prompt', {
14
+ title: 'Team Management',
15
+ description: 'First argument choice narrows values for second argument. Demo/test fixture.',
16
+ argsSchema: {
17
+ department: (0, completable_js_1.completable)(zod_1.z.string().describe('Choose the department.'), (value) => ['Engineering', 'Sales', 'Marketing', 'Support'].filter((d) => d.startsWith(value))),
18
+ name: (0, completable_js_1.completable)(zod_1.z.string().describe('Choose a team member to lead the selected department.'), (value, context) => {
19
+ const department = context?.arguments?.['department'];
20
+ const members = department ? (DEPARTMENT_MEMBERS[department] ?? []) : [];
21
+ return members.filter((n) => n.startsWith(value));
22
+ })
23
+ }
24
+ }, ({ department, name }) => ({
25
+ messages: [
26
+ {
27
+ role: 'user',
28
+ content: { type: 'text', text: `Please promote ${name} to the head of the ${department} team.` }
29
+ }
30
+ ]
31
+ }));
32
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerPrompts = registerPrompts;
4
+ const simple_1 = require("./simple");
5
+ const args_1 = require("./args");
6
+ const completions_1 = require("./completions");
7
+ const resource_1 = require("./resource");
8
+ // Add one entry per prompt file here as prompts are added, one at a time.
9
+ const registerFns = [
10
+ simple_1.registerSimplePrompt,
11
+ args_1.registerArgumentsPrompt,
12
+ completions_1.registerPromptWithCompletions,
13
+ resource_1.registerEmbeddedResourcePrompt
14
+ ];
15
+ function registerPrompts(server) {
16
+ for (const register of registerFns) {
17
+ register(server);
18
+ }
19
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerEmbeddedResourcePrompt = registerEmbeddedResourcePrompt;
4
+ const templates_1 = require("../resources/templates");
5
+ function registerEmbeddedResourcePrompt(server) {
6
+ server.registerPrompt('resource-prompt', {
7
+ title: 'Resource Prompt',
8
+ description: 'A prompt that includes an embedded resource reference. Demo/test fixture.',
9
+ argsSchema: {
10
+ resourceType: templates_1.resourceTypeCompleter,
11
+ resourceId: templates_1.resourceIdForPromptCompleter
12
+ }
13
+ }, (args) => {
14
+ const resourceId = Number(args.resourceId);
15
+ if (!Number.isFinite(resourceId) || !Number.isInteger(resourceId) || resourceId < 1) {
16
+ throw new Error(`Invalid resourceId: ${args.resourceId}. Must be a finite positive integer.`);
17
+ }
18
+ const uri = args.resourceType === templates_1.RESOURCE_TYPE_TEXT ? (0, templates_1.textResourceUri)(resourceId) : (0, templates_1.blobResourceUri)(resourceId);
19
+ const resource = args.resourceType === templates_1.RESOURCE_TYPE_TEXT
20
+ ? (0, templates_1.textResource)(uri, resourceId)
21
+ : (0, templates_1.blobResource)(uri, resourceId);
22
+ return {
23
+ messages: [
24
+ {
25
+ role: 'user',
26
+ content: {
27
+ type: 'text',
28
+ text: `This prompt includes the ${args.resourceType} resource with id: ${resourceId}. Please analyze the following resource:`
29
+ }
30
+ },
31
+ { role: 'user', content: { type: 'resource', resource } }
32
+ ]
33
+ };
34
+ });
35
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerSimplePrompt = registerSimplePrompt;
4
+ function registerSimplePrompt(server) {
5
+ server.registerPrompt('simple-prompt', {
6
+ title: 'Simple Prompt',
7
+ description: 'A prompt with no arguments. Demo/test fixture.'
8
+ }, () => ({
9
+ messages: [
10
+ {
11
+ role: 'user',
12
+ content: { type: 'text', text: 'This is a simple prompt without arguments.' }
13
+ }
14
+ ]
15
+ }));
16
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveDocsDir = resolveDocsDir;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ // The build script copies docs/ into dist/docs so the published package is
7
+ // self-contained (dist/resources/../docs resolves correctly at runtime).
8
+ // Vitest runs directly against src/ instead of the compiled output, where
9
+ // that same relative path would land on a nonexistent src/docs — the real
10
+ // docs/ is one level further up, at the package root. Rather than
11
+ // duplicating docs/ into src/ just for tests, resolve whichever actually
12
+ // exists.
13
+ function resolveDocsDir(fromDir) {
14
+ const distRelative = (0, path_1.join)(fromDir, '..', 'docs');
15
+ if ((0, fs_1.existsSync)(distRelative))
16
+ return distRelative;
17
+ return (0, path_1.join)(fromDir, '..', '..', 'docs');
18
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerFileResources = registerFileResources;
4
+ const path_1 = require("path");
5
+ const fs_1 = require("fs");
6
+ const docsDir_1 = require("./docsDir");
7
+ function getMimeType(fileName) {
8
+ const lower = fileName.toLowerCase();
9
+ if (lower.endsWith('.md') || lower.endsWith('.markdown'))
10
+ return 'text/markdown';
11
+ if (lower.endsWith('.txt'))
12
+ return 'text/plain';
13
+ if (lower.endsWith('.json'))
14
+ return 'application/json';
15
+ return 'text/plain';
16
+ }
17
+ function readFileSafe(path) {
18
+ try {
19
+ return (0, fs_1.readFileSync)(path, 'utf-8');
20
+ }
21
+ catch (error) {
22
+ return `Error reading file: ${path}. ${error}`;
23
+ }
24
+ }
25
+ function registerFileResources(server) {
26
+ // __dirname is a real CommonJS module-scope binding here, no import.meta
27
+ // shim needed.
28
+ const docsDir = (0, docsDir_1.resolveDocsDir)(__dirname);
29
+ let entries = [];
30
+ try {
31
+ entries = (0, fs_1.readdirSync)(docsDir);
32
+ }
33
+ catch {
34
+ return;
35
+ }
36
+ for (const name of entries) {
37
+ const fullPath = (0, path_1.join)(docsDir, name);
38
+ try {
39
+ if (!(0, fs_1.statSync)(fullPath).isFile())
40
+ continue;
41
+ }
42
+ catch {
43
+ continue;
44
+ }
45
+ const uri = `mcpflo://static/document/${encodeURIComponent(name)}`;
46
+ const mimeType = getMimeType(name);
47
+ const description = `Static document file exposed from docs/: ${name}`;
48
+ server.registerResource(name, uri, { mimeType, description }, async (readUri) => ({
49
+ contents: [{ uri: readUri.toString(), mimeType, text: readFileSafe(fullPath) }]
50
+ }));
51
+ }
52
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerResources = registerResources;
4
+ exports.readInstructions = readInstructions;
5
+ const path_1 = require("path");
6
+ const fs_1 = require("fs");
7
+ const templates_1 = require("./templates");
8
+ const file_resources_1 = require("./file-resources");
9
+ const docsDir_1 = require("./docsDir");
10
+ function registerResources(server) {
11
+ (0, templates_1.registerResourceTemplates)(server);
12
+ (0, file_resources_1.registerFileResources)(server);
13
+ }
14
+ function readInstructions() {
15
+ // __dirname is a real CommonJS module-scope binding here, no import.meta
16
+ // shim needed.
17
+ const filePath = (0, path_1.join)((0, docsDir_1.resolveDocsDir)(__dirname), 'instructions.md');
18
+ try {
19
+ return (0, fs_1.readFileSync)(filePath, 'utf-8');
20
+ }
21
+ catch (error) {
22
+ return `Server instructions not loaded: ${error}`;
23
+ }
24
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initSessionResources = initSessionResources;
4
+ exports.getSessionResourceURI = getSessionResourceURI;
5
+ exports.registerSessionResource = registerSessionResource;
6
+ let counter = 0;
7
+ // McpServer.registerResource() calls the SDK's registerCapabilities() on its
8
+ // very first-ever invocation, which throws once server.connect() has already
9
+ // run. Tools register session resources at call-time, which is always
10
+ // post-connect, so we register (and immediately disable) one throwaway
11
+ // resource before connect to trip that one-time init path early. Disabling
12
+ // it hides it from resources/list without undoing the warm-up.
13
+ function initSessionResources(server) {
14
+ server.registerResource('__session_warmup__', 'mcpflo://session/warmup', { mimeType: 'text/plain' }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: 'text/plain', text: '' }] })).disable();
15
+ }
16
+ function getSessionResourceURI(name) {
17
+ counter += 1;
18
+ return `mcpflo://session/resource/${counter}/${encodeURIComponent(name)}`;
19
+ }
20
+ function registerSessionResource(server, resource, kind, data) {
21
+ server.registerResource(resource.name, resource.uri, { mimeType: resource.mimeType }, async (uri) => ({
22
+ contents: [
23
+ kind === 'blob'
24
+ ? { uri: uri.href, mimeType: resource.mimeType, blob: data }
25
+ : { uri: uri.href, mimeType: resource.mimeType, text: data }
26
+ ]
27
+ }));
28
+ return {
29
+ type: 'resource_link',
30
+ uri: resource.uri,
31
+ name: resource.name,
32
+ mimeType: resource.mimeType
33
+ };
34
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerSubscriptionsCapability = registerSubscriptionsCapability;
4
+ exports.beginSimulatedResourceUpdates = beginSimulatedResourceUpdates;
5
+ exports.stopSimulatedResourceUpdates = stopSimulatedResourceUpdates;
6
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
+ const INTERVAL_MS = 5000;
8
+ const sessionSubscriptions = new Map();
9
+ const sessionTimers = new Map();
10
+ function registerSubscriptionsCapability(server) {
11
+ server.server.setRequestHandler(types_js_1.SubscribeRequestSchema, async (request, extra) => {
12
+ const uris = sessionSubscriptions.get(extra.sessionId) ?? new Set();
13
+ uris.add(request.params.uri);
14
+ sessionSubscriptions.set(extra.sessionId, uris);
15
+ return {};
16
+ });
17
+ server.server.setRequestHandler(types_js_1.UnsubscribeRequestSchema, async (request, extra) => {
18
+ sessionSubscriptions.get(extra.sessionId)?.delete(request.params.uri);
19
+ return {};
20
+ });
21
+ }
22
+ function beginSimulatedResourceUpdates(server, sessionId) {
23
+ const timer = setInterval(() => {
24
+ const uris = sessionSubscriptions.get(sessionId);
25
+ if (!uris || uris.size === 0)
26
+ return;
27
+ for (const uri of uris) {
28
+ server.server.sendResourceUpdated({ uri }).catch(() => { });
29
+ }
30
+ }, INTERVAL_MS);
31
+ sessionTimers.set(sessionId, timer);
32
+ }
33
+ function stopSimulatedResourceUpdates(sessionId) {
34
+ const timer = sessionTimers.get(sessionId);
35
+ if (timer)
36
+ clearInterval(timer);
37
+ sessionTimers.delete(sessionId);
38
+ }