@orboto/mcp 0.221.0 → 0.221.11
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/dist/event-bridge.js +2 -0
- package/dist/http-transport.js +28 -6
- package/dist/inbox-channel.js +14 -11
- package/dist/instructions-budget.test.js +72 -2
- package/dist/manifest-size.test.js +8 -0
- package/dist/orboto-client.js +3 -1
- package/dist/orboto-client.test.js +1 -1
- package/dist/server.js +18 -28
- package/dist/session-origin.js +100 -0
- package/dist/session-origin.test.js +127 -0
- package/dist/tools/agent-coordination.js +39 -11
- package/dist/tools/agent-coordination.test.js +53 -35
- package/dist/tools/agent-messages.js +16 -5
- package/dist/tools/agent-who.js +23 -8
- package/dist/tools/agent-who.test.js +12 -0
- package/dist/tools/check-similar.js +5 -3
- package/dist/tools/doc-edits.js +6 -4
- package/dist/tools/doc-replace-wrapper.test.js +81 -0
- package/dist/tools/docs-ai.js +4 -0
- package/dist/tools/docs.js +25 -14
- package/dist/tools/docs.test.js +25 -12
- package/dist/tools/embedding-status.js +3 -0
- package/dist/tools/knowledge.js +2 -2
- package/dist/tools/knowledge.test.js +8 -0
- package/dist/tools/list-tickets.js +12 -9
- package/dist/tools/phase-b-tools.test.js +18 -0
- package/dist/tools/responsibilities.js +10 -0
- package/dist/tools/responsibilities.test.js +60 -0
- package/dist/tools/search.js +5 -3
- package/dist/tools/semantic-degraded-wrapper.test.js +70 -0
- package/dist/tools/semantic-degraded.test.js +65 -0
- package/dist/tools/session-start.js +8 -2
- package/dist/tools/shared.js +31 -3
- package/dist/tools/spec-schemas.js +2 -0
- package/dist/tools/wiki.js +15 -9
- package/dist/tools/wiki.test.js +23 -0
- package/dist/toolset.js +3 -2
- package/package.json +1 -1
package/dist/tools/wiki.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* descriptions per the skill/MCP English rule.
|
|
8
8
|
*/
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
+
import { SEMANTIC_DEGRADED_LINE, replacedLines } from './shared.js';
|
|
10
11
|
import { resolveDocId } from './docs.js';
|
|
11
12
|
function text(t, structured) {
|
|
12
13
|
return { content: [{ type: 'text', text: t }], ...(structured ? { structuredContent: structured } : {}) };
|
|
@@ -49,7 +50,8 @@ export function makeWikiAskHandler(client) {
|
|
|
49
50
|
body.limit = input.limit;
|
|
50
51
|
const res = await client.post(`/ai/ask-docs`, body);
|
|
51
52
|
const cites = res.citations.map((c) => `[${c.index}] ${c.title} - ${c.link}`).join('\n');
|
|
52
|
-
|
|
53
|
+
const degraded = res.semanticDegraded ? `\n\n(${SEMANTIC_DEGRADED_LINE})` : '';
|
|
54
|
+
return text(`${res.answer}\n\n${cites ? `Sources:\n${cites}` : ''}`.trim() + degraded, { answer: res.answer, citations: res.citations, mode: res.mode, semanticDegraded: res.semanticDegraded ?? false });
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
export const wikiLintToolConfig = {
|
|
@@ -98,17 +100,19 @@ export function makeWikiPlanUpdateHandler(client) {
|
|
|
98
100
|
}
|
|
99
101
|
export const wikiApplyPlanToolConfig = {
|
|
100
102
|
title: 'Apply a previously-planned wiki edit',
|
|
101
|
-
description: 'Commit the page operations from a plan created by orboto_wiki_plan_update. Applies every op atomically (each page edit is snapshotted for rollback), then rebuilds the index doc of the space and appends a line to its log doc. Fails with 410 if the plan has expired (>15 min) or was already applied. Wraps POST /spaces/:id/docs/apply-plan.',
|
|
103
|
+
description: 'Commit the page operations from a plan created by orboto_wiki_plan_update. Applies every op atomically (each page edit is snapshotted for rollback), then rebuilds the index doc of the space and appends a line to its log doc. ORB-2468: a patch op that drops more than half of its page refuses the whole plan with 409 (nothing written) unless replace is true; each rewritten page comes back in replaced with the revision and the restore command. Fails with 410 if the plan has expired (>15 min) or was already applied. Wraps POST /spaces/:id/docs/apply-plan.',
|
|
102
104
|
inputSchema: z.object({
|
|
103
105
|
spaceId: z.string().uuid(),
|
|
104
|
-
planId: z.string().uuid().describe('
|
|
106
|
+
planId: z.string().uuid().describe('From orboto_wiki_plan_update.'),
|
|
107
|
+
replace: z.boolean().optional(),
|
|
105
108
|
}).shape,
|
|
106
109
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },
|
|
107
110
|
};
|
|
108
111
|
export function makeWikiApplyPlanHandler(client) {
|
|
109
112
|
return async (input) => {
|
|
110
|
-
const res = await client.post(`/spaces/${input.spaceId}/docs/apply-plan`, { planId: input.planId });
|
|
111
|
-
|
|
113
|
+
const res = await client.post(`/spaces/${input.spaceId}/docs/apply-plan`, { planId: input.planId, ...(input.replace ? { replace: true } : {}) });
|
|
114
|
+
const replaced = res.replaced ?? [];
|
|
115
|
+
return text(`Applied plan ${input.planId}: ${res.touchedDocs.length} page(s) updated.${replacedLines(replaced)}`, { touchedDocs: res.touchedDocs, replaced });
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
export const wikiRecordToolConfig = {
|
|
@@ -150,21 +154,23 @@ export function makeWikiAppendSectionHandler(client) {
|
|
|
150
154
|
}
|
|
151
155
|
export const wikiSaveAnswerToolConfig = {
|
|
152
156
|
title: 'Save a Q&A answer as a wiki page',
|
|
153
|
-
description: 'Turn an answer (e.g. from orboto_wiki_ask) into a curated wiki page with smart-links to its citations. Idempotent per (space, question): re-saving the same question updates the page instead of duplicating it. Wraps POST /ai/save-answer-to-wiki.',
|
|
157
|
+
description: 'Turn an answer (e.g. from orboto_wiki_ask) into a curated wiki page with smart-links to its citations. Idempotent per (space, question): re-saving the same question updates the page instead of duplicating it. ORB-2468: a re-save that drops more than half of the saved page is refused with 409 unless replace is true; an update names the revision it replaced and the restore command. Wraps POST /ai/save-answer-to-wiki.',
|
|
154
158
|
inputSchema: z.object({
|
|
155
159
|
spaceId: z.string().uuid(),
|
|
156
160
|
question: z.string().min(3).max(1000),
|
|
157
161
|
answer: z.string().min(1).max(50_000),
|
|
158
|
-
title: z.string().max(200).optional().describe('
|
|
162
|
+
title: z.string().max(200).optional().describe('Defaults to the question.'),
|
|
159
163
|
parentDocId: z.string().uuid().optional(),
|
|
160
|
-
citations: z.array(z.object({ docId: z.string().min(1).describe('Doc UUID or human-readable doc key (ORB-D12 / DOC-5).'), title: z.string() })).max(50).optional().describe('
|
|
164
|
+
citations: z.array(z.object({ docId: z.string().min(1).describe('Doc UUID or human-readable doc key (ORB-D12 / DOC-5).'), title: z.string() })).max(50).optional().describe('From orboto_wiki_ask.'),
|
|
165
|
+
replace: z.boolean().optional(),
|
|
161
166
|
}).shape,
|
|
162
167
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },
|
|
163
168
|
};
|
|
164
169
|
export function makeWikiSaveAnswerHandler(client) {
|
|
165
170
|
return async (input) => {
|
|
166
171
|
const res = await client.post(`/ai/save-answer-to-wiki`, { ...input, citations: input.citations ?? [] });
|
|
167
|
-
|
|
172
|
+
const replaced = res.replaced ? [res.replaced] : [];
|
|
173
|
+
return text(res.created ? `Saved as a new wiki page (docId: ${res.docId}).` : `Updated the existing wiki page (docId: ${res.docId}).${replacedLines(replaced)}`, { docId: res.docId, created: res.created, replaced: res.replaced ?? null });
|
|
168
174
|
};
|
|
169
175
|
}
|
|
170
176
|
export const wikiFlagStaleToolConfig = {
|
package/dist/tools/wiki.test.js
CHANGED
|
@@ -69,6 +69,22 @@ describe('orboto_wiki_plan_update + apply_plan', () => {
|
|
|
69
69
|
const res = await makeWikiApplyPlanHandler(client)({ spaceId: SPACE, planId: 'p1' });
|
|
70
70
|
expect(res.content[0].text).toContain('1 page(s) updated');
|
|
71
71
|
});
|
|
72
|
+
it('ORB-2468 - apply-plan sends replace only when asked and prints each replaced revision with its restore command', async () => {
|
|
73
|
+
const replaced = { docId: DOC, revisionId: 'r0000000-0000-4000-8000-000000000001', restoreCommand: 'orboto doc restore KB-D4 r0000000-0000-4000-8000-000000000001' };
|
|
74
|
+
const calls = stubJSON([{ json: { touchedDocs: [DOC] } }, { json: { touchedDocs: [DOC], replaced: [replaced] } }]);
|
|
75
|
+
await makeWikiApplyPlanHandler(client)({ spaceId: SPACE, planId: 'p1' });
|
|
76
|
+
expect(calls[0].body).toEqual({ planId: 'p1' });
|
|
77
|
+
const res = await makeWikiApplyPlanHandler(client)({ spaceId: SPACE, planId: 'p1', replace: true });
|
|
78
|
+
expect(calls[1].body).toEqual({ planId: 'p1', replace: true });
|
|
79
|
+
const out = res.content[0].text;
|
|
80
|
+
expect(out).toContain('Replaced: 1');
|
|
81
|
+
expect(out).toContain(replaced.restoreCommand);
|
|
82
|
+
expect(res.structuredContent).toMatchObject({ replaced: [replaced] });
|
|
83
|
+
});
|
|
84
|
+
it('ORB-2468 - apply-plan surfaces the 409 shrink refusal as OrbotoApiError', async () => {
|
|
85
|
+
stubJSON([{ ok: false, status: 409, json: { error: 'This write would drop 90% of the existing page KB-D4', errorKey: 'errors.docs.replace_drops_content' } }]);
|
|
86
|
+
await expect(makeWikiApplyPlanHandler(client)({ spaceId: SPACE, planId: 'p1' })).rejects.toBeInstanceOf(OrbotoApiError);
|
|
87
|
+
});
|
|
72
88
|
it('apply-plan surfaces a 410 expired plan as OrbotoApiError', async () => {
|
|
73
89
|
stubJSON([{ ok: false, status: 410, json: { error: 'Plan has expired' } }]);
|
|
74
90
|
await expect(makeWikiApplyPlanHandler(client)({ spaceId: SPACE, planId: 'p1' })).rejects.toBeInstanceOf(OrbotoApiError);
|
|
@@ -100,6 +116,13 @@ describe('orboto_wiki_save_answer', () => {
|
|
|
100
116
|
expect(calls[0]).toMatchObject({ method: 'POST', url: 'https://orboto.example.com/ai/save-answer-to-wiki', body: { spaceId: SPACE, question: 'how?', answer: 'like this', citations: [] } });
|
|
101
117
|
expect(res.content[0].text).toMatch(/new wiki page/);
|
|
102
118
|
});
|
|
119
|
+
it('ORB-2468 - a re-save passes replace and names the replaced revision', async () => {
|
|
120
|
+
const replaced = { docId: DOC, revisionId: 'r0000000-0000-4000-8000-000000000002', restoreCommand: 'orboto doc restore KB-D5 r0000000-0000-4000-8000-000000000002' };
|
|
121
|
+
const calls = stubJSON([{ json: { docId: DOC, created: false, replaced } }]);
|
|
122
|
+
const res = await makeWikiSaveAnswerHandler(client)({ spaceId: SPACE, question: 'how?', answer: 'new', replace: true });
|
|
123
|
+
expect(calls[0].body).toMatchObject({ replace: true });
|
|
124
|
+
expect(res.content[0].text).toContain(replaced.restoreCommand);
|
|
125
|
+
});
|
|
103
126
|
});
|
|
104
127
|
describe('orboto_wiki_flag_stale', () => {
|
|
105
128
|
it('defaults stale=true and reports the flag', async () => {
|
package/dist/toolset.js
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
/** ORB-2210 - no tools: the agent works through the orboto CLI, the server carries the wake channel. */
|
|
8
8
|
export const CHANNEL_TOOLSET_INSTRUCTIONS = [
|
|
9
|
+
'FIRST ACTION, before anything else: run `orboto session-start` in the shell - it prints the binding workspace rules and your in-progress work; run it again after a context compaction.',
|
|
9
10
|
'orboto is a ticket and project management system; ticket keys look like `PROJ-123`.',
|
|
10
|
-
'This server carries no tools. Work through the orboto CLI
|
|
11
|
-
'`orboto session-start` prints the binding workspace rules and your in-progress work (run it first and after a context compaction),',
|
|
11
|
+
'This server carries no tools. Work through the orboto CLI: `orboto help` lists the commands,',
|
|
12
12
|
'and `orboto agent-heartbeat --role <role> --scope-projects <KEY,KEY>` declares what this session is responsible for.',
|
|
13
|
+
'Before you send agent mail, `orboto owner "<topic>"` names who owns the topic.',
|
|
13
14
|
'Always: claim or create a ticket before touching code, one commit per ticket with the key in the subject, never mark work done that is not done.',
|
|
14
15
|
'MCP tools come back on request: `?toolset=curated|full` (HTTP) or ORBOTO_MCP_TOOLSET=curated|full (stdio).',
|
|
15
16
|
].join(' ');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orboto/mcp",
|
|
3
|
-
"version": "0.221.
|
|
3
|
+
"version": "0.221.11",
|
|
4
4
|
"description": "Model Context Protocol server for orboto - exposes tickets, projects, milestones, time, docs, primer-facts, alerts and absences as MCP tools for Claude Desktop / Cursor / Copilot Chat / any MCP-aware AI client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|