docorbit 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +660 -0
- package/apps/cli/bin/docorbit.js +8 -0
- package/apps/cli/src/commands/add.ts +44 -0
- package/apps/cli/src/commands/api.ts +38 -0
- package/apps/cli/src/commands/context.ts +47 -0
- package/apps/cli/src/commands/dashboard.ts +55 -0
- package/apps/cli/src/commands/diff.ts +30 -0
- package/apps/cli/src/commands/evaluate.ts +133 -0
- package/apps/cli/src/commands/examples.ts +39 -0
- package/apps/cli/src/commands/export.ts +89 -0
- package/apps/cli/src/commands/impact.ts +31 -0
- package/apps/cli/src/commands/init.ts +69 -0
- package/apps/cli/src/commands/inspect.ts +30 -0
- package/apps/cli/src/commands/mcp.ts +72 -0
- package/apps/cli/src/commands/pitfalls.ts +38 -0
- package/apps/cli/src/commands/recipes.ts +35 -0
- package/apps/cli/src/commands/search.ts +48 -0
- package/apps/cli/src/commands/update.ts +73 -0
- package/apps/cli/src/commands/verify.ts +48 -0
- package/apps/cli/src/formatters/colors.ts +23 -0
- package/apps/cli/src/formatters/inspection.ts +102 -0
- package/apps/cli/src/formatters/knowledge.ts +272 -0
- package/apps/cli/src/formatters/retrieval.ts +74 -0
- package/apps/cli/src/formatters/terminal.ts +6 -0
- package/apps/cli/src/formatters/verification.ts +126 -0
- package/apps/cli/src/index.ts +409 -0
- package/bin/docorbit.js +8 -0
- package/package.json +46 -0
- package/packages/core/src/dashboard/server.ts +314 -0
- package/packages/core/src/dashboard/ui.ts +586 -0
- package/packages/core/src/implementation-service.ts +451 -0
- package/packages/core/src/index.ts +7 -0
- package/packages/core/src/inspector.ts +71 -0
- package/packages/core/src/pipeline.ts +331 -0
- package/packages/crawler/src/config.ts +12 -0
- package/packages/crawler/src/fetcher.ts +185 -0
- package/packages/crawler/src/index.ts +2 -0
- package/packages/discovery/src/index.ts +31 -0
- package/packages/discovery/src/provider.ts +47 -0
- package/packages/discovery/src/providers/generic.ts +98 -0
- package/packages/discovery/src/providers/github.ts +61 -0
- package/packages/discovery/src/providers/llms-txt.ts +73 -0
- package/packages/discovery/src/providers/markdown.ts +48 -0
- package/packages/discovery/src/providers/openapi.ts +91 -0
- package/packages/discovery/src/providers/sitemap.ts +62 -0
- package/packages/discovery/src/providers/skill.ts +54 -0
- package/packages/discovery/src/ranker.ts +123 -0
- package/packages/evaluation/src/dataset.ts +963 -0
- package/packages/evaluation/src/index.ts +8 -0
- package/packages/evaluation/src/runner.ts +241 -0
- package/packages/evaluation/src/strategies/context7-runner.ts +269 -0
- package/packages/evaluation/src/strategies/docorbit-runner.ts +228 -0
- package/packages/evaluation/src/strategies/firecrawl-runner.ts +172 -0
- package/packages/evaluation/src/strategies/web-search-runner.ts +194 -0
- package/packages/evaluation/src/types.ts +34 -0
- package/packages/evaluation/src/version-matcher.ts +73 -0
- package/packages/export/src/agents-md.ts +200 -0
- package/packages/export/src/claude-md.ts +141 -0
- package/packages/export/src/docs-map.ts +150 -0
- package/packages/export/src/index.ts +6 -0
- package/packages/export/src/llms-txt.ts +96 -0
- package/packages/export/src/service.ts +250 -0
- package/packages/export/src/skill-md.ts +128 -0
- package/packages/mcp/src/index.ts +46 -0
- package/packages/mcp/src/resources/index.ts +189 -0
- package/packages/mcp/src/server.ts +278 -0
- package/packages/mcp/src/tools/analyze-impact.ts +74 -0
- package/packages/mcp/src/tools/check-api.ts +86 -0
- package/packages/mcp/src/tools/diff-docs.ts +68 -0
- package/packages/mcp/src/tools/export-context.ts +73 -0
- package/packages/mcp/src/tools/find-api.ts +99 -0
- package/packages/mcp/src/tools/find-example.ts +100 -0
- package/packages/mcp/src/tools/find-pitfall.ts +94 -0
- package/packages/mcp/src/tools/find-recipe.ts +98 -0
- package/packages/mcp/src/tools/get-doc.ts +130 -0
- package/packages/mcp/src/tools/get-docs-map.ts +64 -0
- package/packages/mcp/src/tools/get-version.ts +118 -0
- package/packages/mcp/src/tools/implementation-context.ts +88 -0
- package/packages/mcp/src/tools/index.ts +59 -0
- package/packages/mcp/src/tools/list-sources.ts +85 -0
- package/packages/mcp/src/tools/search-docs.ts +123 -0
- package/packages/mcp/src/tools/types.ts +28 -0
- package/packages/mcp/src/transports/http.ts +256 -0
- package/packages/mcp/src/transports/stdio.ts +105 -0
- package/packages/mcp/src/transports/types.ts +6 -0
- package/packages/mcp/src/types.ts +102 -0
- package/packages/normalizer/src/example-indexer.ts +240 -0
- package/packages/normalizer/src/html.ts +253 -0
- package/packages/normalizer/src/index.ts +8 -0
- package/packages/normalizer/src/llms.ts +83 -0
- package/packages/normalizer/src/openapi/endpoint-parser.ts +406 -0
- package/packages/normalizer/src/openapi/schema-resolver.ts +111 -0
- package/packages/normalizer/src/openapi.ts +2 -0
- package/packages/normalizer/src/page.ts +184 -0
- package/packages/normalizer/src/pitfall-extractor.ts +190 -0
- package/packages/normalizer/src/slicer.ts +455 -0
- package/packages/retrieval/src/engine.ts +120 -0
- package/packages/retrieval/src/index.ts +7 -0
- package/packages/retrieval/src/intent.ts +43 -0
- package/packages/retrieval/src/packer.ts +145 -0
- package/packages/retrieval/src/recipe-engine.ts +313 -0
- package/packages/retrieval/src/scorer.ts +139 -0
- package/packages/retrieval/src/weights.ts +31 -0
- package/packages/security/src/annotations.ts +112 -0
- package/packages/security/src/index.ts +2 -0
- package/packages/security/src/ssrf.ts +153 -0
- package/packages/shared/src/errors.ts +53 -0
- package/packages/shared/src/hashing.ts +23 -0
- package/packages/shared/src/index.ts +3 -0
- package/packages/shared/src/types.ts +881 -0
- package/packages/storage/src/db.ts +72 -0
- package/packages/storage/src/index.ts +11 -0
- package/packages/storage/src/interfaces.ts +115 -0
- package/packages/storage/src/repositories/api-repository.ts +219 -0
- package/packages/storage/src/repositories/chunk-repository.ts +316 -0
- package/packages/storage/src/repositories/example-repository.ts +206 -0
- package/packages/storage/src/repositories/page-repository.ts +205 -0
- package/packages/storage/src/repositories/pitfall-repository.ts +188 -0
- package/packages/storage/src/repositories/source-repository.ts +205 -0
- package/packages/storage/src/repository.ts +256 -0
- package/packages/storage/src/schema.ts +269 -0
- package/packages/storage/src/search-tokens.ts +28 -0
- package/packages/verification/src/diff-engine.ts +258 -0
- package/packages/verification/src/extractor.ts +339 -0
- package/packages/verification/src/impact-scanner.ts +203 -0
- package/packages/verification/src/index.ts +5 -0
- package/packages/verification/src/services.ts +238 -0
- package/packages/verification/src/verifier.ts +375 -0
- package/packages/workspace/src/detector.ts +143 -0
- package/packages/workspace/src/ecosystems/cargo.ts +84 -0
- package/packages/workspace/src/ecosystems/composer.ts +42 -0
- package/packages/workspace/src/ecosystems/go.ts +54 -0
- package/packages/workspace/src/ecosystems/index.ts +34 -0
- package/packages/workspace/src/ecosystems/maven.ts +34 -0
- package/packages/workspace/src/ecosystems/npm.ts +83 -0
- package/packages/workspace/src/ecosystems/pub.ts +40 -0
- package/packages/workspace/src/ecosystems/pypi.ts +100 -0
- package/packages/workspace/src/ecosystems/rubygems.ts +30 -0
- package/packages/workspace/src/ecosystems/types.ts +18 -0
- package/packages/workspace/src/index.ts +5 -0
- package/packages/workspace/src/lockfile.ts +194 -0
- package/packages/workspace/src/resolver.ts +234 -0
- package/packages/workspace/src/semver.ts +259 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { CallToolResult, McpTool } from '../types.ts';
|
|
2
|
+
import type { McpContext, McpToolHandler } from './types.ts';
|
|
3
|
+
|
|
4
|
+
export class FindExampleTool implements McpToolHandler {
|
|
5
|
+
readonly definition: McpTool = {
|
|
6
|
+
name: 'find_example',
|
|
7
|
+
description: 'Find verified, framework-specific code examples by task, language, framework, or target API.',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
query: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
description: 'Implementation task or symbol to look for examples of (e.g. "verify webhook signature", "constructEvent").',
|
|
14
|
+
},
|
|
15
|
+
language: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Programming language filter (e.g. "typescript", "python", "go").',
|
|
18
|
+
},
|
|
19
|
+
framework: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Framework filter (e.g. "express", "fastapi", "next").',
|
|
22
|
+
},
|
|
23
|
+
version: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Target documentation version filter.',
|
|
26
|
+
},
|
|
27
|
+
limit: {
|
|
28
|
+
type: 'number',
|
|
29
|
+
description: 'Maximum number of examples to return (default: 5).',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ['query'],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
async execute(args: Record<string, unknown>, ctx: McpContext): Promise<CallToolResult> {
|
|
37
|
+
const query = typeof args.query === 'string'
|
|
38
|
+
? args.query.trim()
|
|
39
|
+
: (typeof args.task === 'string' ? args.task.trim() : '');
|
|
40
|
+
if (!query) {
|
|
41
|
+
return {
|
|
42
|
+
isError: true,
|
|
43
|
+
content: [{ type: 'text', text: JSON.stringify({ error: 'Missing required parameter: query (or task)' }) }],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const language = typeof args.language === 'string' ? args.language : undefined;
|
|
48
|
+
const framework = typeof args.framework === 'string' ? args.framework : undefined;
|
|
49
|
+
const version = typeof args.docVersion === 'string'
|
|
50
|
+
? args.docVersion
|
|
51
|
+
: (typeof args.version === 'string' ? args.version : undefined);
|
|
52
|
+
const limit = typeof args.limit === 'number' && args.limit > 0 ? args.limit : 5;
|
|
53
|
+
|
|
54
|
+
const examples = ctx.repo.searchIndexedExamples(query, {
|
|
55
|
+
language,
|
|
56
|
+
framework,
|
|
57
|
+
docVersion: version,
|
|
58
|
+
limit,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (examples.length === 0) {
|
|
62
|
+
return {
|
|
63
|
+
content: [
|
|
64
|
+
{
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: JSON.stringify({
|
|
67
|
+
markdown: `### Code Examples for "${query}"\n\nNo verified code examples found.`,
|
|
68
|
+
data: { query, count: 0, examples: [] },
|
|
69
|
+
}, null, 2),
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const lines: string[] = [`### Verified Code Examples for "${query}" (${examples.length} found)\n`];
|
|
76
|
+
for (const ex of examples) {
|
|
77
|
+
lines.push(`#### ${ex.task} [${ex.language}${ex.framework ? ` / ${ex.framework}` : ''}] (${ex.sourceAuthority})`);
|
|
78
|
+
if (ex.relatedApi) lines.push(`*Target API*: \`${ex.relatedApi}\``);
|
|
79
|
+
lines.push('```' + ex.language);
|
|
80
|
+
lines.push(ex.code);
|
|
81
|
+
lines.push('```\n');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
content: [
|
|
86
|
+
{
|
|
87
|
+
type: 'text',
|
|
88
|
+
text: JSON.stringify({
|
|
89
|
+
markdown: lines.join('\n'),
|
|
90
|
+
data: {
|
|
91
|
+
query,
|
|
92
|
+
count: examples.length,
|
|
93
|
+
examples,
|
|
94
|
+
},
|
|
95
|
+
}, null, 2),
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { PitfallKind } from '../../../shared/src/index.ts';
|
|
2
|
+
import type { CallToolResult, McpTool } from '../types.ts';
|
|
3
|
+
import type { McpContext, McpToolHandler } from './types.ts';
|
|
4
|
+
|
|
5
|
+
export class FindPitfallTool implements McpToolHandler {
|
|
6
|
+
readonly definition: McpTool = {
|
|
7
|
+
name: 'find_pitfall',
|
|
8
|
+
description: 'Search documented pitfalls, gotchas, deprecations, breaking changes, and runtime restrictions.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
query: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Topic, feature, or symbol to inspect for pitfalls (e.g. "webhook raw body", "route params").',
|
|
15
|
+
},
|
|
16
|
+
kind: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Category filter for pitfalls.',
|
|
19
|
+
enum: ['deprecated', 'removed', 'breaking_change', 'gotcha', 'security', 'runtime_restriction', 'server_only', 'rate_limit'],
|
|
20
|
+
},
|
|
21
|
+
version: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Target documentation version filter.',
|
|
24
|
+
},
|
|
25
|
+
limit: {
|
|
26
|
+
type: 'number',
|
|
27
|
+
description: 'Maximum number of pitfalls to return (default: 5).',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['query'],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
async execute(args: Record<string, unknown>, ctx: McpContext): Promise<CallToolResult> {
|
|
35
|
+
const query = typeof args.query === 'string'
|
|
36
|
+
? args.query.trim()
|
|
37
|
+
: (typeof args.task === 'string' ? args.task.trim() : '');
|
|
38
|
+
if (!query) {
|
|
39
|
+
return {
|
|
40
|
+
isError: true,
|
|
41
|
+
content: [{ type: 'text', text: JSON.stringify({ error: 'Missing required parameter: query (or task)' }) }],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const kind = typeof args.kind === 'string' ? (args.kind as PitfallKind) : undefined;
|
|
46
|
+
const version = typeof args.docVersion === 'string'
|
|
47
|
+
? args.docVersion
|
|
48
|
+
: (typeof args.version === 'string' ? args.version : undefined);
|
|
49
|
+
const limit = typeof args.limit === 'number' && args.limit > 0 ? args.limit : 5;
|
|
50
|
+
|
|
51
|
+
const pitfalls = ctx.repo.searchPitfalls(query, {
|
|
52
|
+
kind,
|
|
53
|
+
docVersion: version,
|
|
54
|
+
limit,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (pitfalls.length === 0) {
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: 'text',
|
|
62
|
+
text: JSON.stringify({
|
|
63
|
+
markdown: `### Pitfalls & Warnings for "${query}"\n\nNo matching pitfalls or warnings found.`,
|
|
64
|
+
data: { query, count: 0, pitfalls: [] },
|
|
65
|
+
}, null, 2),
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const lines: string[] = [`### Pitfalls & Warnings for "${query}" (${pitfalls.length} found)\n`];
|
|
72
|
+
for (const pf of pitfalls) {
|
|
73
|
+
lines.push(`- ⚠️ **[${pf.kind.toUpperCase()}] ${pf.title}**${pf.docVersion ? ` (${pf.docVersion})` : ''}`);
|
|
74
|
+
if (pf.relatedApi) lines.push(` *Related API*: \`${pf.relatedApi}\``);
|
|
75
|
+
lines.push(` ${pf.content}\n`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: JSON.stringify({
|
|
83
|
+
markdown: lines.join('\n'),
|
|
84
|
+
data: {
|
|
85
|
+
query,
|
|
86
|
+
count: pitfalls.length,
|
|
87
|
+
pitfalls,
|
|
88
|
+
},
|
|
89
|
+
}, null, 2),
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { RecipeEngine } from '../../../retrieval/src/index.ts';
|
|
2
|
+
import type { CallToolResult, McpTool } from '../types.ts';
|
|
3
|
+
import type { McpContext, McpToolHandler } from './types.ts';
|
|
4
|
+
|
|
5
|
+
export class FindRecipeTool implements McpToolHandler {
|
|
6
|
+
readonly definition: McpTool = {
|
|
7
|
+
name: 'find_recipe',
|
|
8
|
+
description: 'Assemble an evidence-grounded implementation recipe with explicit prerequisites, ordered steps, and validation steps.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
goal: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'The implementation goal or workflow to construct a recipe for.',
|
|
15
|
+
},
|
|
16
|
+
version: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Target documentation version.',
|
|
19
|
+
},
|
|
20
|
+
projectPath: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Workspace root for project-aware dependency detection.',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['goal'],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
async execute(args: Record<string, unknown>, ctx: McpContext): Promise<CallToolResult> {
|
|
30
|
+
const goal = typeof args.goal === 'string'
|
|
31
|
+
? args.goal.trim()
|
|
32
|
+
: (typeof args.task === 'string' ? args.task.trim() : (typeof args.query === 'string' ? args.query.trim() : ''));
|
|
33
|
+
if (!goal) {
|
|
34
|
+
return {
|
|
35
|
+
isError: true,
|
|
36
|
+
content: [{ type: 'text', text: JSON.stringify({ error: 'Missing required parameter: goal (or task)' }) }],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const version = typeof args.docVersion === 'string'
|
|
41
|
+
? args.docVersion
|
|
42
|
+
: (typeof args.version === 'string' ? args.version : undefined);
|
|
43
|
+
const projectPath = typeof args.projectPath === 'string'
|
|
44
|
+
? args.projectPath
|
|
45
|
+
: (typeof args.project === 'string' ? args.project : ctx.workspaceRoot);
|
|
46
|
+
|
|
47
|
+
const engine = new RecipeEngine(ctx.repo);
|
|
48
|
+
const recipe = await engine.assembleRecipe(goal, {
|
|
49
|
+
docVersion: version,
|
|
50
|
+
projectDir: projectPath,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const lines: string[] = [
|
|
54
|
+
`### Implementation Recipe: ${recipe.goal}`,
|
|
55
|
+
`**Confidence**: ${Math.round(recipe.confidence * 100)}%${recipe.docVersion ? ` | **Version**: \`${recipe.docVersion}\`` : ''}\n`,
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
if (recipe.prerequisites.length > 0) {
|
|
59
|
+
lines.push('#### Prerequisites');
|
|
60
|
+
for (const p of recipe.prerequisites) {
|
|
61
|
+
lines.push(`- [${p.evidenceLevel === 'documented_fact' ? 'FACT' : 'INFERRED'}] ${p.text}`);
|
|
62
|
+
}
|
|
63
|
+
lines.push('');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (recipe.orderedSteps.length > 0) {
|
|
67
|
+
lines.push('#### Implementation Steps');
|
|
68
|
+
for (const s of recipe.orderedSteps) {
|
|
69
|
+
const badge = s.evidenceLevel === 'documented_fact' ? 'FACT' : (s.evidenceLevel === 'missing_information' ? 'MISSING' : 'INFERRED');
|
|
70
|
+
lines.push(`${s.step}. **[${badge}] ${s.title}**`);
|
|
71
|
+
if (s.apiEndpoint) lines.push(` - Endpoint: \`${s.apiEndpoint}\``);
|
|
72
|
+
lines.push(` - ${s.description}`);
|
|
73
|
+
}
|
|
74
|
+
lines.push('');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (recipe.validationSteps.length > 0) {
|
|
78
|
+
lines.push('#### Validation Steps');
|
|
79
|
+
for (const vs of recipe.validationSteps) {
|
|
80
|
+
lines.push(`${vs.step}. **[FACT]** ${vs.description}`);
|
|
81
|
+
if (vs.expectedResponse) lines.push(` - Expected: \`${vs.expectedResponse}\``);
|
|
82
|
+
}
|
|
83
|
+
lines.push('');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: 'text',
|
|
90
|
+
text: JSON.stringify({
|
|
91
|
+
markdown: lines.join('\n'),
|
|
92
|
+
data: recipe,
|
|
93
|
+
}, null, 2),
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { CallToolResult, McpTool } from '../types.ts';
|
|
2
|
+
import type { McpContext, McpToolHandler } from './types.ts';
|
|
3
|
+
|
|
4
|
+
export class GetDocTool implements McpToolHandler {
|
|
5
|
+
readonly definition: McpTool = {
|
|
6
|
+
name: 'get_doc',
|
|
7
|
+
description: 'Retrieve a specific documentation page or contextual chunk by ID or URL with complete metadata and security annotations.',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
chunkId: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
description: 'Unique chunk identifier (e.g. "chk_...").',
|
|
14
|
+
},
|
|
15
|
+
pageId: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Unique page identifier (e.g. "page_...").',
|
|
18
|
+
},
|
|
19
|
+
url: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Normalized source URL of the documentation page.',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
async execute(args: Record<string, unknown>, ctx: McpContext): Promise<CallToolResult> {
|
|
28
|
+
const chunkId = typeof args.chunkId === 'string' ? args.chunkId.trim() : undefined;
|
|
29
|
+
const pageId = typeof args.pageId === 'string' ? args.pageId.trim() : undefined;
|
|
30
|
+
const url = typeof args.url === 'string' ? args.url.trim() : undefined;
|
|
31
|
+
|
|
32
|
+
if (!chunkId && !pageId && !url) {
|
|
33
|
+
return {
|
|
34
|
+
isError: true,
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: JSON.stringify({ error: 'Please provide at least one identifier: chunkId, pageId, or url.' }),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (chunkId) {
|
|
45
|
+
const chunk = ctx.repo.getChunk(chunkId);
|
|
46
|
+
if (!chunk) {
|
|
47
|
+
return {
|
|
48
|
+
isError: true,
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: 'text',
|
|
52
|
+
text: JSON.stringify({ error: `Chunk not found: ${chunkId}`, available: false }),
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const codeSnippets = ctx.repo.getChunkCode(chunkId);
|
|
59
|
+
const symbols = ctx.repo.getChunkSymbols(chunkId);
|
|
60
|
+
const relationships = ctx.repo.getChunkRelationships(chunkId);
|
|
61
|
+
|
|
62
|
+
const breadcrumb = chunk.sectionPath.length > 0 ? chunk.sectionPath.join(' > ') : (chunk.title || 'General');
|
|
63
|
+
const md = [
|
|
64
|
+
`### Chunk: ${breadcrumb}`,
|
|
65
|
+
`**ID**: \`${chunk.id}\` | **Type**: \`${chunk.chunkType}\` | **Est. Tokens**: ~${chunk.tokenEstimate}`,
|
|
66
|
+
chunk.docVersion ? `**Version**: \`${chunk.docVersion}\`` : '',
|
|
67
|
+
`> [!NOTE] External content is untrusted.\n`,
|
|
68
|
+
chunk.content,
|
|
69
|
+
].filter(Boolean).join('\n\n');
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: 'text',
|
|
75
|
+
text: JSON.stringify({
|
|
76
|
+
markdown: md,
|
|
77
|
+
data: {
|
|
78
|
+
chunk,
|
|
79
|
+
codeSnippets,
|
|
80
|
+
symbols,
|
|
81
|
+
relationships,
|
|
82
|
+
untrusted: true,
|
|
83
|
+
},
|
|
84
|
+
}, null, 2),
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Page lookup by ID or URL
|
|
91
|
+
const page = pageId ? ctx.repo.getPage(pageId) : (url ? ctx.repo.getPageByUrl(url) : null);
|
|
92
|
+
if (!page) {
|
|
93
|
+
return {
|
|
94
|
+
isError: true,
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: 'text',
|
|
98
|
+
text: JSON.stringify({ error: `Page not found for ${pageId ? `pageId: ${pageId}` : `url: ${url}`}`, available: false }),
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const links = ctx.repo.getPageLinks(page.id);
|
|
105
|
+
const md = [
|
|
106
|
+
`### ${page.title}`,
|
|
107
|
+
`**URL**: ${page.url} | **Tokens**: ~${page.estimatedTokens} | **Hash**: \`${page.contentHash.slice(0, 8)}\``,
|
|
108
|
+
page.securityAnnotations.length > 0
|
|
109
|
+
? `> [!WARNING] Security alerts detected: ${page.securityAnnotations.map(a => a.patternName).join(', ')}`
|
|
110
|
+
: `> [!NOTE] External content is untrusted.`,
|
|
111
|
+
`\n${page.content}`,
|
|
112
|
+
].join('\n\n');
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: 'text',
|
|
118
|
+
text: JSON.stringify({
|
|
119
|
+
markdown: md,
|
|
120
|
+
data: {
|
|
121
|
+
page,
|
|
122
|
+
links,
|
|
123
|
+
untrusted: true,
|
|
124
|
+
},
|
|
125
|
+
}, null, 2),
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { CallToolResult, McpTool } from '../types.ts';
|
|
2
|
+
import type { McpContext, McpToolHandler } from './types.ts';
|
|
3
|
+
import { ExportService } from '../../../export/src/index.ts';
|
|
4
|
+
|
|
5
|
+
export class GetDocumentationMapTool implements McpToolHandler {
|
|
6
|
+
readonly definition: McpTool = {
|
|
7
|
+
name: 'get_documentation_map',
|
|
8
|
+
description: 'Retrieve the hierarchical documentation map, page tree, section headings, and estimated token budget footprints for indexed documentation.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
sourceId: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Optional source ID or source URL substring filter.',
|
|
15
|
+
},
|
|
16
|
+
docVersion: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Optional documentation version filter (e.g. "v14", "15.0").',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async execute(args: Record<string, unknown>, ctx: McpContext): Promise<CallToolResult> {
|
|
25
|
+
const sourceId = typeof args.sourceId === 'string' ? args.sourceId : undefined;
|
|
26
|
+
const docVersion = typeof args.docVersion === 'string' ? args.docVersion : undefined;
|
|
27
|
+
|
|
28
|
+
const exportService = ctx.exportService || new ExportService(ctx.repo);
|
|
29
|
+
const map = exportService.getDocumentationMap({ sourceId, docVersion });
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
content: [
|
|
33
|
+
{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify(
|
|
36
|
+
{
|
|
37
|
+
markdown: map.markdownTree,
|
|
38
|
+
data: {
|
|
39
|
+
totalSources: map.totalSources,
|
|
40
|
+
totalPages: map.totalPages,
|
|
41
|
+
totalChunks: map.totalChunks,
|
|
42
|
+
totalEstimatedTokens: map.totalEstimatedTokens,
|
|
43
|
+
sources: map.sources,
|
|
44
|
+
pages: map.pages.map(p => ({
|
|
45
|
+
id: p.id,
|
|
46
|
+
title: p.title,
|
|
47
|
+
url: p.url,
|
|
48
|
+
version: p.version,
|
|
49
|
+
chunkCount: p.chunkCount,
|
|
50
|
+
estimatedTokens: p.estimatedTokens,
|
|
51
|
+
apis: p.apis,
|
|
52
|
+
pitfallCount: p.pitfallCount,
|
|
53
|
+
})),
|
|
54
|
+
untrusted: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
null,
|
|
58
|
+
2
|
|
59
|
+
),
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectWorkspaceDependencies,
|
|
3
|
+
WorkspaceResolver,
|
|
4
|
+
} from '../../../workspace/src/index.ts';
|
|
5
|
+
import type { CallToolResult, McpTool } from '../types.ts';
|
|
6
|
+
import type { McpContext, McpToolHandler } from './types.ts';
|
|
7
|
+
|
|
8
|
+
export class GetVersionTool implements McpToolHandler {
|
|
9
|
+
readonly definition: McpTool = {
|
|
10
|
+
name: 'get_version',
|
|
11
|
+
description: 'Inspect workspace dependencies and resolve the exact compatible documentation version using the SemVer confidence ladder.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
library: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Package or library name to check (e.g. "next", "stripe", "fastapi").',
|
|
18
|
+
},
|
|
19
|
+
projectPath: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Workspace root directory containing package manifests (default: current directory).',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
required: ['library'],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async execute(args: Record<string, unknown>, ctx: McpContext): Promise<CallToolResult> {
|
|
29
|
+
const library = typeof args.library === 'string' ? args.library.trim() : '';
|
|
30
|
+
if (!library) {
|
|
31
|
+
return {
|
|
32
|
+
isError: true,
|
|
33
|
+
content: [{ type: 'text', text: JSON.stringify({ error: 'Missing required parameter: library' }) }],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const projectPath = typeof args.projectPath === 'string' ? args.projectPath : (ctx.workspaceRoot || '.');
|
|
38
|
+
|
|
39
|
+
let scan;
|
|
40
|
+
try {
|
|
41
|
+
scan = detectWorkspaceDependencies(projectPath);
|
|
42
|
+
} catch (err: unknown) {
|
|
43
|
+
return {
|
|
44
|
+
isError: true,
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: 'text',
|
|
48
|
+
text: JSON.stringify({ error: `Failed to inspect workspace manifests at ${projectPath}: ${err instanceof Error ? err.message : String(err)}` }),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const libLower = library.toLowerCase();
|
|
55
|
+
const matchedDep = scan.dependencies.find(d => d.name.toLowerCase() === libLower);
|
|
56
|
+
|
|
57
|
+
if (!matchedDep) {
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: 'text',
|
|
62
|
+
text: JSON.stringify({
|
|
63
|
+
markdown: `### Version Resolution for "${library}"\n\nNo installed dependency named \`${library}\` was found in workspace manifests.`,
|
|
64
|
+
data: {
|
|
65
|
+
library,
|
|
66
|
+
foundInWorkspace: false,
|
|
67
|
+
ecosystemsScanned: scan.ecosystems,
|
|
68
|
+
},
|
|
69
|
+
}, null, 2),
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const resolver = new WorkspaceResolver(ctx.repo);
|
|
76
|
+
const resolution = resolver.resolveWorkspace(scan);
|
|
77
|
+
const match = resolution.matches.find(m => m.dependency.name.toLowerCase() === libLower);
|
|
78
|
+
|
|
79
|
+
const data = {
|
|
80
|
+
library: matchedDep.name,
|
|
81
|
+
ecosystem: matchedDep.ecosystem,
|
|
82
|
+
requestedVersion: matchedDep.requestedVersion,
|
|
83
|
+
resolvedVersion: matchedDep.resolvedVersion,
|
|
84
|
+
sourceFile: matchedDep.sourceFile,
|
|
85
|
+
docVersionMatch: match ? {
|
|
86
|
+
targetVersion: match.targetVersion,
|
|
87
|
+
confidence: match.confidence,
|
|
88
|
+
snapshotId: match.snapshotId,
|
|
89
|
+
matchedBy: match.matchedBy,
|
|
90
|
+
} : {
|
|
91
|
+
targetVersion: 'unresolved',
|
|
92
|
+
confidence: 'unresolved',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const lines: string[] = [
|
|
97
|
+
`### Version Intelligence: \`${matchedDep.name}\``,
|
|
98
|
+
`- **Ecosystem**: \`${matchedDep.ecosystem}\``,
|
|
99
|
+
`- **Requested in Manifest**: \`${matchedDep.requestedVersion}\` (${matchedDep.sourceFile})`,
|
|
100
|
+
matchedDep.resolvedVersion ? `- **Resolved in Lockfile**: \`${matchedDep.resolvedVersion}\`` : '',
|
|
101
|
+
match?.targetVersion
|
|
102
|
+
? `- **Recommended Doc Version**: \`${match.targetVersion}\` (Confidence: \`${match.confidence}\`, Matched via: \`${match.matchedBy}\`)`
|
|
103
|
+
: `- **Recommended Doc Version**: \`unresolved\` (No compatible indexed documentation found)`,
|
|
104
|
+
].filter(Boolean);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: JSON.stringify({
|
|
111
|
+
markdown: lines.join('\n'),
|
|
112
|
+
data,
|
|
113
|
+
}, null, 2),
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { CallToolResult, McpTool } from '../types.ts';
|
|
2
|
+
import type { McpContext, McpToolHandler } from './types.ts';
|
|
3
|
+
|
|
4
|
+
export class ImplementationContextTool implements McpToolHandler {
|
|
5
|
+
readonly definition: McpTool = {
|
|
6
|
+
name: 'get_implementation_context',
|
|
7
|
+
description: 'High-level documentation intelligence orchestrator. Automatically resolves workspace dependencies and versions, detects task intent, retrieves relevant chunks, APIs, verified examples, and pitfalls, and compiles an evidence-grounded recipe within a strict token budget.',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
task: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
description: 'The specific coding task or feature to implement (e.g. "Implement Stripe webhook signature verification in Express").',
|
|
14
|
+
},
|
|
15
|
+
project: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Path to repository workspace root for project-aware dependency detection (default: current directory).',
|
|
18
|
+
},
|
|
19
|
+
library: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Optional primary library or package to focus documentation on (e.g. "stripe", "next").',
|
|
22
|
+
},
|
|
23
|
+
version: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Optional explicit target documentation version (e.g. "v14", "15.0").',
|
|
26
|
+
},
|
|
27
|
+
tokenBudget: {
|
|
28
|
+
type: 'number',
|
|
29
|
+
description: 'Maximum token budget for packed context (default: 4000).',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ['task'],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
async execute(args: Record<string, unknown>, ctx: McpContext): Promise<CallToolResult> {
|
|
37
|
+
const task = typeof args.task === 'string' ? args.task.trim() : '';
|
|
38
|
+
if (!task) {
|
|
39
|
+
return {
|
|
40
|
+
isError: true,
|
|
41
|
+
content: [{ type: 'text', text: JSON.stringify({ error: 'Missing required parameter: task' }) }],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const projectPath = typeof args.project === 'string'
|
|
46
|
+
? args.project
|
|
47
|
+
: (ctx.workspaceRoot || '.');
|
|
48
|
+
const library = typeof args.library === 'string' ? args.library : undefined;
|
|
49
|
+
const version = typeof args.version === 'string' ? args.version : undefined;
|
|
50
|
+
const tokenBudget = typeof args.tokenBudget === 'number' && args.tokenBudget > 0
|
|
51
|
+
? args.tokenBudget
|
|
52
|
+
: 4000;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const result = await ctx.implService.getContext({
|
|
56
|
+
task,
|
|
57
|
+
projectPath,
|
|
58
|
+
library,
|
|
59
|
+
version,
|
|
60
|
+
tokenBudget,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: JSON.stringify({
|
|
68
|
+
markdown: result.markdown,
|
|
69
|
+
data: result,
|
|
70
|
+
}, null, 2),
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
} catch (err: unknown) {
|
|
75
|
+
return {
|
|
76
|
+
isError: true,
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: JSON.stringify({
|
|
81
|
+
error: `Failed to compile implementation context: ${err instanceof Error ? err.message : String(err)}`,
|
|
82
|
+
}),
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|