Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The server registers tools under the MCP server name `@discourse/mcp`. Choose a
- `silent`: No logging output
- `--tools_mode <auto|discourse_api_only|tool_exec_api>` (default: auto)
- `--site <url>`: Tether MCP to a single site and hide `discourse_select_site`.
- `--default-search <prefix>`: Unconditionally prefix every search query (e.g., `tag:ai order:latest-post`).
- `--default-search <prefix>`: Unconditionally prefix every search query (e.g., `tag:ai order:latest`).
- `--max-read-length <number>`: Maximum characters returned for post content (default 50000). Applies to `discourse_read_post` and per-post content in `discourse_read_topic`. The tools prefer `raw` content by requesting `include_raw=true`.
- `--transport <stdio|http>` (default: stdio): Transport type. Use `stdio` for standard input/output (default), or `http` for Streamable HTTP transport (stateless mode with JSON responses).
- `--port <number>` (default: 3000): Port to listen on when using HTTP transport.
Expand All @@ -92,7 +92,7 @@ The server registers tools under the MCP server name `@discourse/mcp`. Choose a
"log_level": "info",
"tools_mode": "auto",
"site": "https://try.discourse.org",
"default_search": "tag:ai order:latest-post",
"default_search": "tag:ai order:latest",
"max_read_length": 50000,
"transport": "stdio",
"port": 3000
Expand Down
40 changes: 20 additions & 20 deletions src/test/tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,24 @@ test('registers built-in tools', async () => {
const logger = new Logger('silent');
const siteState = new SiteState({ logger, timeoutMs: 5000, defaultAuth: { type: 'none' } });

test('registers write-enabled tools when allowWrites=true', async () => {
const logger = new Logger('silent');
const siteState = new SiteState({ logger, timeoutMs: 5000, defaultAuth: { type: 'none' } });

// Minimal fake server to capture tool registrations
const tools: Record<string, { handler: Function }> = {};
const fakeServer: any = {
registerTool(name: string, _meta: any, handler: Function) {
tools[name] = { handler };
},
};

await registerAllTools(fakeServer, siteState, logger, { allowWrites: true, toolsMode: 'discourse_api_only' } as any);

// When writes are enabled, both create tools should be registered
assert.ok('discourse_create_post' in tools);
assert.ok('discourse_create_category' in tools);
});
test('registers write-enabled tools when allowWrites=true', async () => {
const logger = new Logger('silent');
const siteState = new SiteState({ logger, timeoutMs: 5000, defaultAuth: { type: 'none' } });

// Minimal fake server to capture tool registrations
const tools: Record<string, { handler: Function }> = {};
const fakeServer: any = {
registerTool(name: string, _meta: any, handler: Function) {
tools[name] = { handler };
},
};

await registerAllTools(fakeServer, siteState, logger, { allowWrites: true, toolsMode: 'discourse_api_only' } as any);

// When writes are enabled, both create tools should be registered
assert.ok('discourse_create_post' in tools);
assert.ok('discourse_create_category' in tools);
});
const server = new McpServer({ name: 'test', version: '0.0.0' }, { capabilities: { tools: { listChanged: false } } });

await registerAllTools(server as any, siteState, logger, { allowWrites: false, toolsMode: 'discourse_api_only' });
Expand Down Expand Up @@ -189,14 +189,14 @@ test('default-search prefix is applied to queries', async () => {
await client.get('/about.json');
siteState.selectSite(base);

await registerAllTools(fakeServer, siteState, logger, { allowWrites: false, toolsMode: 'discourse_api_only', defaultSearchPrefix: 'tag:ai order:latest-post' } as any);
await registerAllTools(fakeServer, siteState, logger, { allowWrites: false, toolsMode: 'discourse_api_only', defaultSearchPrefix: 'tag:ai order:latest' } as any);

await tools['discourse_search'].handler({ query: 'hello world' }, {});
assert.ok(lastUrl && lastUrl.includes('/search.json?'));
const qs = lastUrl!.split('?')[1] || '';
const params = new URLSearchParams(qs);
assert.equal(params.get('expanded'), 'true');
assert.equal(params.get('q'), 'tag:ai order:latest-post hello world');
assert.equal(params.get('q'), 'tag:ai order:latest hello world');
} finally {
globalThis.fetch = originalFetch as any;
}
Expand Down