@orboto/mcp 0.193.0 → 0.194.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.
- package/dist/event-bridge.js +1 -47
- package/dist/event-bridge.test.js +0 -19
- package/dist/http-transport-binding.test.js +1 -4
- package/dist/http-transport.js +1 -213
- package/dist/http-transport.test.js +0 -56
- package/dist/index.js +1 -48
- package/dist/input-schema.js +2 -76
- package/dist/input-schema.test.js +1 -15
- package/dist/instructions-budget.test.js +0 -2
- package/dist/manifest-size.test.js +3 -72
- package/dist/oauth-bootstrap.js +0 -47
- package/dist/oauth-bootstrap.test.js +4 -16
- package/dist/orboto-client.js +0 -30
- package/dist/orboto-client.test.js +0 -12
- package/dist/prompts.js +0 -39
- package/dist/protect-text-usage.test.js +1 -9
- package/dist/required-rules-wrapper.test.js +0 -5
- package/dist/required-rules.js +0 -2
- package/dist/resources.js +0 -62
- package/dist/resources.test.js +0 -16
- package/dist/response-budget.js +2 -150
- package/dist/response-budget.test.js +1 -32
- package/dist/server.js +4 -180
- package/dist/session-nudge.js +0 -9
- package/dist/session-nudge.test.js +2 -10
- package/dist/session-nudge.transport.test.js +0 -23
- package/dist/tool-annotations.test.js +0 -44
- package/dist/tool-docs.js +1 -12
- package/dist/tool-docs.test.js +2 -8
- package/dist/tool-parameters.test.js +0 -19
- package/dist/tools/absence-writes.js +1 -17
- package/dist/tools/admin-translations.js +0 -11
- package/dist/tools/admin-writes.js +0 -28
- package/dist/tools/admin-writes.test.js +0 -1
- package/dist/tools/agent-coordination.js +2 -24
- package/dist/tools/agent-coordination.test.js +0 -2
- package/dist/tools/agent-instructions.js +0 -10
- package/dist/tools/agent-instructions.test.js +0 -2
- package/dist/tools/agent-messages.js +0 -1
- package/dist/tools/ai-status.js +0 -14
- package/dist/tools/ai-usage.js +0 -10
- package/dist/tools/analytics.js +0 -2
- package/dist/tools/api-call.js +0 -12
- package/dist/tools/api-call.test.js +1 -2
- package/dist/tools/api-search.js +1 -6
- package/dist/tools/approvals.js +0 -6
- package/dist/tools/attach.js +0 -25
- package/dist/tools/backup.js +0 -13
- package/dist/tools/backup.test.js +1 -2
- package/dist/tools/bulk-create.js +1 -25
- package/dist/tools/bulk-create.test.js +0 -4
- package/dist/tools/bulk-writes.js +69 -61
- package/dist/tools/bulk-writes.test.js +34 -4
- package/dist/tools/check-similar.js +0 -17
- package/dist/tools/checklist-writes.js +82 -27
- package/dist/tools/checklist-writes.test.js +69 -1
- package/dist/tools/claim.js +1 -71
- package/dist/tools/claim.test.js +0 -2
- package/dist/tools/critical-path.js +0 -5
- package/dist/tools/critical-path.test.js +0 -3
- package/dist/tools/cross-project-links.ee.js +1 -24
- package/dist/tools/doc-attachments.js +0 -28
- package/dist/tools/doc-attachments.test.js +1 -4
- package/dist/tools/doc-comments.js +1 -37
- package/dist/tools/doc-comments.test.js +1 -2
- package/dist/tools/doc-edits.js +1 -31
- package/dist/tools/doc-edits.test.js +0 -9
- package/dist/tools/doc-export.js +0 -24
- package/dist/tools/doc-export.test.js +0 -12
- package/dist/tools/doc-revisions.js +0 -23
- package/dist/tools/docs-ai.js +0 -34
- package/dist/tools/docs-ai.test.js +0 -11
- package/dist/tools/docs.js +1 -75
- package/dist/tools/docs.test.js +1 -3
- package/dist/tools/embedding-status.js +0 -13
- package/dist/tools/embedding-status.test.js +0 -1
- package/dist/tools/feedback.js +1 -15
- package/dist/tools/feedback.test.js +0 -1
- package/dist/tools/get-checklists.js +6 -14
- package/dist/tools/get-project-primer.js +1 -25
- package/dist/tools/get-project.js +0 -17
- package/dist/tools/get-ticket.js +1 -61
- package/dist/tools/get-timer.js +0 -11
- package/dist/tools/git-health-reasons.js +0 -1
- package/dist/tools/identity.js +0 -13
- package/dist/tools/list-projects.js +0 -20
- package/dist/tools/list-projects.test.js +0 -1
- package/dist/tools/list-tickets.js +10 -9
- package/dist/tools/milestones-crud.test.js +0 -4
- package/dist/tools/milestones.js +0 -43
- package/dist/tools/my-tickets.js +0 -7
- package/dist/tools/phase-b-tools.test.js +14 -21
- package/dist/tools/primer-facts.js +1 -49
- package/dist/tools/primer-facts.test.js +0 -20
- package/dist/tools/project-listings.js +0 -25
- package/dist/tools/query.js +1 -12
- package/dist/tools/query.test.js +0 -8
- package/dist/tools/raci.js +0 -11
- package/dist/tools/requirements-spec.js +1 -12
- package/dist/tools/response-expand.js +0 -19
- package/dist/tools/review-policy.js +0 -24
- package/dist/tools/review-policy.test.js +0 -2
- package/dist/tools/search.js +1 -13
- package/dist/tools/session-start.js +1 -136
- package/dist/tools/session-start.test.js +1 -44
- package/dist/tools/set-parent.js +1 -19
- package/dist/tools/shared.js +0 -41
- package/dist/tools/shared.test.js +0 -4
- package/dist/tools/similar-projection.js +0 -15
- package/dist/tools/ticket-attachments.js +1 -25
- package/dist/tools/ticket-schedules.js +0 -9
- package/dist/tools/ticket-schedules.test.js +0 -1
- package/dist/tools/ticket-writes.js +1 -110
- package/dist/tools/ticket-writes.test.js +1 -15
- package/dist/tools/time-writes.js +26 -46
- package/dist/tools/time-writes.test.js +18 -0
- package/dist/tools/update-project.js +1 -27
- package/dist/tools/update-project.test.js +1 -1
- package/dist/tools/wiki.js +0 -8
- package/dist/tools/work-next.js +0 -5
- package/dist/tools/work-sessions-start.test.js +0 -2
- package/dist/tools/work-sessions.js +0 -48
- package/dist/tools/work-sessions.test.js +0 -5
- package/dist/toolset.js +2 -81
- package/dist/toolset.test.js +0 -20
- package/dist/version-guard.test.js +0 -4
- package/dist/version.js +0 -12
- package/dist/with-metrics-output-schema.test.js +1 -14
- package/dist/with-metrics.js +2 -74
- package/dist/with-metrics.test.js +7 -29
- package/package.json +1 -1
package/dist/with-metrics.js
CHANGED
|
@@ -33,10 +33,6 @@ function formatApiError(err) {
|
|
|
33
33
|
: 'Request rejected.';
|
|
34
34
|
return `orboto API error ${err.status}. ${hint}\nDetail: ${detail || '(no message)'}`;
|
|
35
35
|
}
|
|
36
|
-
// ORB-1817 - McpLogEntry / redactSecrets / postLogEntry live in
|
|
37
|
-
// mcp-instrument.ts (a leaf module) so input-schema.ts can log validation
|
|
38
|
-
// failures through the same instrument path without an import cycle
|
|
39
|
-
// (this file already imports FROM input-schema.ts).
|
|
40
36
|
/**
|
|
41
37
|
* Wrap a CallToolResult-returning handler so every invocation posts
|
|
42
38
|
* one row to mcp_call_log via /admin/mcp/instrument.
|
|
@@ -46,27 +42,10 @@ function formatApiError(err) {
|
|
|
46
42
|
* Wired centrally in `server.ts` so per-tool files don't need to
|
|
47
43
|
* know about instrumentation.
|
|
48
44
|
*/
|
|
49
|
-
export function withMetrics(client, toolName, clientHint,
|
|
50
|
-
// ORB-1252 - forward the SDK's `extra` (RequestHandlerExtra) so handlers can
|
|
51
|
-
// read the per-connection MCP sessionId (distinct per HTTP client even on a
|
|
52
|
-
// shared server). Backward-compatible: handlers that ignore it are unaffected.
|
|
53
|
-
handler,
|
|
54
|
-
// ORB-1331 - per-session/-process nudge state. When present, the first
|
|
55
|
-
// tool dispatch that is not `orboto_session_start` gets the one-time
|
|
56
|
-
// reminder prepended to its response. Optional so direct callers/tests
|
|
57
|
-
// that don't care are unaffected.
|
|
58
|
-
nudge) {
|
|
45
|
+
export function withMetrics(client, toolName, clientHint, handler, nudge) {
|
|
59
46
|
return async (args, extra) => {
|
|
60
47
|
const start = Date.now();
|
|
61
|
-
// ORB-1331 - decide (and advance the flag) once per dispatch, before
|
|
62
|
-
// the handler runs, so the "first tool call" is the first dispatch
|
|
63
|
-
// regardless of its outcome. Applied to the returned result below.
|
|
64
48
|
const wantsNudge = nudge ? shouldNudge(nudge, toolName) : false;
|
|
65
|
-
// ORB-1471 - HARD session-start gate. When the workspace requires it,
|
|
66
|
-
// refuse every tool call until `orboto_session_start` succeeds. The gate message
|
|
67
|
-
// supersedes the soft nudge, so we return it directly without running the
|
|
68
|
-
// handler. Disabled by default => `shouldGate` returns false and nothing
|
|
69
|
-
// below changes.
|
|
70
49
|
if (nudge && shouldGate(nudge, toolName)) {
|
|
71
50
|
const gated = gateResult();
|
|
72
51
|
void postLogEntry(client, {
|
|
@@ -80,17 +59,8 @@ nudge) {
|
|
|
80
59
|
}
|
|
81
60
|
try {
|
|
82
61
|
const handlerResult = await handler(args, extra);
|
|
83
|
-
// ORB-1697 - the central response budget. Applied HERE, once, so it
|
|
84
|
-
// covers every registered tool instead of relying on 168 handlers to
|
|
85
|
-
// each stay small. Over-budget payloads come back truncated with an
|
|
86
|
-
// explicit `__truncation` block + a handle for the remainder; the
|
|
87
|
-
// measured sizes go into the call log either way.
|
|
88
62
|
const budgeted = applyResponseBudget(toolName, handlerResult);
|
|
89
63
|
const result = budgeted.result;
|
|
90
|
-
// Success path - but the handler can also signal a "soft"
|
|
91
|
-
// failure via { isError: true } in the result. We treat that
|
|
92
|
-
// as success=false in the log so dashboards reflect actual
|
|
93
|
-
// user-visible failures.
|
|
94
64
|
const isError = result.isError === true;
|
|
95
65
|
if (nudge)
|
|
96
66
|
recordSessionStartResult(nudge, toolName, !isError);
|
|
@@ -105,11 +75,6 @@ nudge) {
|
|
|
105
75
|
responseChars: budgeted.responseChars,
|
|
106
76
|
truncatedChars: budgeted.truncatedChars,
|
|
107
77
|
});
|
|
108
|
-
// ORB-1727 - agent-mail piggyback: the client captured the api's
|
|
109
|
-
// `x-orboto-agent-mail` header on the request this tool just made.
|
|
110
|
-
// Append a compact pointer while mail is pending - zero bytes when
|
|
111
|
-
// the inbox is empty, and never on the messages tool itself (the
|
|
112
|
-
// caller is already fetching).
|
|
113
78
|
const withMail = appendMailNudge(client, toolName, result);
|
|
114
79
|
return wantsNudge ? prependNudge(withMail) : withMail;
|
|
115
80
|
}
|
|
@@ -121,20 +86,12 @@ nudge) {
|
|
|
121
86
|
void postLogEntry(client, { toolName, durationMs, success: false, statusCode: err.status, errorMessage: err.message, clientHint });
|
|
122
87
|
return { isError: true, content: [{ type: 'text', text: err.message }], structuredContent: { errorKey: err.errorKey, reason: err.reason, ...(err.status ? { status: err.status } : {}) } };
|
|
123
88
|
}
|
|
124
|
-
// ORB-1174 - an OrbotoApiError carries a real HTTP status + message.
|
|
125
|
-
// Return it as a structured isError result so the agent + human see
|
|
126
|
-
// WHY (401 vs 404 vs 500) and can self-correct, instead of the
|
|
127
|
-
// runtime's opaque "Error occurred during tool execution".
|
|
128
89
|
if (err instanceof OrbotoApiError) {
|
|
129
90
|
const text = formatApiError(err);
|
|
130
91
|
void postLogEntry(client, { toolName, durationMs, success: false, statusCode: err.status, errorMessage: redactSecrets(text).slice(0, 500), clientHint });
|
|
131
|
-
// Still surface the reminder alongside the actionable error so a
|
|
132
|
-
// first-call failure doesn't swallow the one-time nudge.
|
|
133
92
|
const errResult = { isError: true, content: [{ type: 'text', text }] };
|
|
134
93
|
return wantsNudge ? prependNudge(errResult) : errResult;
|
|
135
94
|
}
|
|
136
|
-
// Anything else is unexpected (a bug, not an API rejection) - log
|
|
137
|
-
// and re-throw so it surfaces loudly rather than being swallowed.
|
|
138
95
|
void postLogEntry(client, {
|
|
139
96
|
toolName,
|
|
140
97
|
durationMs,
|
|
@@ -163,52 +120,23 @@ nudge) {
|
|
|
163
120
|
export function appendMailNudge(client, toolName, result) {
|
|
164
121
|
if (!client.pendingAgentMail || toolName === 'orboto_messages')
|
|
165
122
|
return result;
|
|
166
|
-
// ORB-1733: the nudge lives ONLY in a text content block. Never touch
|
|
167
|
-
// structuredContent - the SDK validates it against the tool's declared
|
|
168
|
-
// outputSchema, and an injected extra key fails validation and errors
|
|
169
|
-
// the entire tool call for as long as mail sits unread.
|
|
170
123
|
const line = `You have ${client.pendingAgentMail} unread agent message(s) - fetch them with orboto_messages.`;
|
|
171
124
|
return {
|
|
172
125
|
...result,
|
|
173
126
|
content: [...result.content, { type: 'text', text: line }],
|
|
174
127
|
};
|
|
175
128
|
}
|
|
176
|
-
export function registerWithMetrics(server, client, clientHint,
|
|
177
|
-
// ORB-1331 - shared per-session/-process nudge state. Every tool
|
|
178
|
-
// registered through this closure reads/advances the same flag, so the
|
|
179
|
-
// one-time session-start reminder fires on whichever tool is called
|
|
180
|
-
// first (unless it is `orboto_session_start`).
|
|
181
|
-
nudge) {
|
|
129
|
+
export function registerWithMetrics(server, client, clientHint, nudge) {
|
|
182
130
|
return (toolName, config, handler) => {
|
|
183
|
-
// ORB-1692 - every raw-shape input becomes strict + alias-resolving.
|
|
184
|
-
// Central here so a new tool cannot ship permissive; empty/absent
|
|
185
|
-
// shapes stay untouched (they carry no fields to guard).
|
|
186
131
|
let cfg = config?.inputSchema && isRawShape(config.inputSchema)
|
|
187
|
-
// ORB-1817 - `client` + `clientHint` let a validation failure log
|
|
188
|
-
// through the same instrument path as a handler error (Part C):
|
|
189
|
-
// the SDK validates BEFORE this handler wrapper ever runs, so this
|
|
190
|
-
// is the only place that sees the failure.
|
|
191
132
|
? { ...config, inputSchema: buildStrictInputSchema(toolName, config.inputSchema, client, clientHint) }
|
|
192
133
|
: config;
|
|
193
|
-
// ORB-1738 - every declared output shape ADVERTISES the response
|
|
194
|
-
// budget's optional __truncation marker. The SDK emits
|
|
195
|
-
// additionalProperties:false for output schemas, and strict clients
|
|
196
|
-
// validate structuredContent against that - an over-budget response
|
|
197
|
-
// carrying the (undeclared) marker was rejected wholesale
|
|
198
|
-
// (list_projects failed on EVERY call). Same central place as the
|
|
199
|
-
// input fix so no tool can ship an outputSchema that fights the
|
|
200
|
-
// budget layer.
|
|
201
134
|
if (cfg?.outputSchema && isRawShape(cfg.outputSchema)) {
|
|
202
135
|
cfg = {
|
|
203
136
|
...cfg,
|
|
204
|
-
// ORB-1805 - the ADVERTISED (compact, open) form; the exact
|
|
205
|
-
// shape stays TruncationBlockSchema for runtime assertions.
|
|
206
137
|
outputSchema: { ...cfg.outputSchema, __truncation: TruncationBlockAdvertisedSchema.optional() },
|
|
207
138
|
};
|
|
208
139
|
}
|
|
209
|
-
// ORB-1741 - manifest diet: the wire manifest carries a one-sentence
|
|
210
|
-
// summary; the full guidance is captured for orboto_help. Central
|
|
211
|
-
// here so a new tool cannot ship a manifest essay by accident.
|
|
212
140
|
if (typeof cfg?.description === 'string' && cfg.description.length > 0) {
|
|
213
141
|
captureToolDoc(toolName, cfg.description);
|
|
214
142
|
cfg = { ...cfg, description: summarizeToolDescription(toolName, cfg.description) };
|
|
@@ -37,7 +37,6 @@ describe('withMetrics', () => {
|
|
|
37
37
|
}));
|
|
38
38
|
const result = await wrapped({});
|
|
39
39
|
expect(result.content[0]).toEqual({ type: 'text', text: 'ok' });
|
|
40
|
-
// Wait one microtask so the fire-and-forget POST resolves.
|
|
41
40
|
await new Promise((r) => setImmediate(r));
|
|
42
41
|
expect(calls).toHaveLength(1);
|
|
43
42
|
expect(calls[0].url).toContain('/admin/mcp/instrument');
|
|
@@ -57,8 +56,6 @@ describe('withMetrics', () => {
|
|
|
57
56
|
expect(body.success).toBe(false);
|
|
58
57
|
expect(body.errorMessage).toBe('kaboom');
|
|
59
58
|
});
|
|
60
|
-
// ORB-1174 - an OrbotoApiError becomes a structured, actionable isError
|
|
61
|
-
// result (not the runtime's opaque generic) so the agent can self-correct.
|
|
62
59
|
it('maps OrbotoApiError to a distinct, actionable isError result per status', async () => {
|
|
63
60
|
captureFetch();
|
|
64
61
|
const run = async (status, body) => {
|
|
@@ -72,14 +69,13 @@ describe('withMetrics', () => {
|
|
|
72
69
|
const unauth = await run(401, '{"error":"Invalid API key"}');
|
|
73
70
|
expect(unauth).toContain('401');
|
|
74
71
|
expect(unauth).toMatch(/re-?authenticate/i);
|
|
75
|
-
expect(unauth).toContain('Invalid API key');
|
|
72
|
+
expect(unauth).toContain('Invalid API key');
|
|
76
73
|
const notFound = await run(404, '{"error":"Ticket not found"}');
|
|
77
74
|
expect(notFound).toContain('404');
|
|
78
75
|
expect(notFound).toMatch(/not found/i);
|
|
79
76
|
const server = await run(503, 'upstream down');
|
|
80
77
|
expect(server).toContain('503');
|
|
81
78
|
expect(server).toMatch(/retry/i);
|
|
82
|
-
// distinct messages, not the same opaque blob
|
|
83
79
|
expect(unauth).not.toBe(notFound);
|
|
84
80
|
expect(notFound).not.toBe(server);
|
|
85
81
|
});
|
|
@@ -107,10 +103,6 @@ describe('withMetrics', () => {
|
|
|
107
103
|
expect(body.clientHint).toBe('cursor');
|
|
108
104
|
});
|
|
109
105
|
it('does not block when the instrument POST fails - handler still resolves', async () => {
|
|
110
|
-
// First call (the handler-as-fetch?) returns ok; but our handler
|
|
111
|
-
// doesn't fetch; the only fetch the wrapper makes is to /instrument.
|
|
112
|
-
// Mock that one to fail - caller must still get the original
|
|
113
|
-
// result back.
|
|
114
106
|
vi.spyOn(globalThis, 'fetch').mockImplementation(async () => ({
|
|
115
107
|
ok: false, status: 500, statusText: 'Server Error',
|
|
116
108
|
json: async () => ({ error: 'kaboom' }),
|
|
@@ -122,8 +114,6 @@ describe('withMetrics', () => {
|
|
|
122
114
|
const result = await wrapped({});
|
|
123
115
|
expect(result.content[0]).toEqual({ type: 'text', text: 'survives' });
|
|
124
116
|
});
|
|
125
|
-
// ORB-1180 - admin-panel visibility: the failure log carries the
|
|
126
|
-
// structured HTTP status, and any secret-shaped text is redacted.
|
|
127
117
|
it('logs the structured statusCode on an OrbotoApiError', async () => {
|
|
128
118
|
const calls = captureFetch();
|
|
129
119
|
const wrapped = withMetrics(client, 'orboto_x', undefined, async () => {
|
|
@@ -136,10 +126,6 @@ describe('withMetrics', () => {
|
|
|
136
126
|
expect(body.statusCode).toBe(403);
|
|
137
127
|
expect(body.errorMessage).toContain('403');
|
|
138
128
|
});
|
|
139
|
-
// ORB-1331 - the shared nudge state threads through the wrapper: the
|
|
140
|
-
// first non-session_start dispatch carries the one-time reminder,
|
|
141
|
-
// later dispatches are clean, and a session_start-first flow never
|
|
142
|
-
// sees it. structuredContent is left intact.
|
|
143
129
|
it('prepends the session-start nudge on the first non-session_start dispatch, once', async () => {
|
|
144
130
|
captureFetch();
|
|
145
131
|
const nudge = createNudgeState();
|
|
@@ -151,9 +137,9 @@ describe('withMetrics', () => {
|
|
|
151
137
|
const first = await list({});
|
|
152
138
|
expect(first.content[0].text).toBe(SESSION_START_NUDGE);
|
|
153
139
|
expect(first.content[1].text).toBe('ok');
|
|
154
|
-
expect(first.structuredContent).toEqual({ a: 1 });
|
|
140
|
+
expect(first.structuredContent).toEqual({ a: 1 });
|
|
155
141
|
const second = await list({});
|
|
156
|
-
expect(second.content[0].text).toBe('ok');
|
|
142
|
+
expect(second.content[0].text).toBe('ok');
|
|
157
143
|
});
|
|
158
144
|
it('does not nudge when the first dispatch IS orboto_session_start', async () => {
|
|
159
145
|
captureFetch();
|
|
@@ -162,9 +148,9 @@ describe('withMetrics', () => {
|
|
|
162
148
|
const start = withMetrics(client, 'orboto_session_start', undefined, handler, nudge);
|
|
163
149
|
const list = withMetrics(client, 'orboto_list_projects', undefined, handler, nudge);
|
|
164
150
|
const first = await start({});
|
|
165
|
-
expect(first.content[0].text).toBe('rules');
|
|
151
|
+
expect(first.content[0].text).toBe('rules');
|
|
166
152
|
const second = await list({});
|
|
167
|
-
expect(second.content[0].text).toBe('rules');
|
|
153
|
+
expect(second.content[0].text).toBe('rules');
|
|
168
154
|
});
|
|
169
155
|
it('surfaces the nudge alongside an OrbotoApiError on a first-call failure', async () => {
|
|
170
156
|
captureFetch();
|
|
@@ -177,18 +163,13 @@ describe('withMetrics', () => {
|
|
|
177
163
|
expect(res.content[0].text).toBe(SESSION_START_NUDGE);
|
|
178
164
|
expect(res.content[1].text).toContain('403');
|
|
179
165
|
});
|
|
180
|
-
// ORB-1471 - the HARD session-start gate. When the workspace requires it,
|
|
181
|
-
// the wrapper REFUSES a non-session-start tool without running its handler,
|
|
182
|
-
// returns the instructive gate message, and logs the refusal. A
|
|
183
|
-
// session_start call unlocks the session; after that everything runs.
|
|
184
166
|
it('gates a non-session-start tool until session_start runs, without invoking the handler', async () => {
|
|
185
167
|
const calls = captureFetch();
|
|
186
|
-
const nudge = createNudgeState(true);
|
|
168
|
+
const nudge = createNudgeState(true);
|
|
187
169
|
const listHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'projects' }] }));
|
|
188
170
|
const startHandler = vi.fn(async () => ({ content: [{ type: 'text', text: 'rules' }] }));
|
|
189
171
|
const list = withMetrics(client, 'orboto_list_projects', undefined, listHandler, nudge);
|
|
190
172
|
const start = withMetrics(client, 'orboto_session_start', undefined, startHandler, nudge);
|
|
191
|
-
// First call is refused - handler never runs.
|
|
192
173
|
const refused = await list({});
|
|
193
174
|
expect(refused.isError).toBe(true);
|
|
194
175
|
expect(refused.content[0].text).toBe(SESSION_START_GATE_MESSAGE);
|
|
@@ -196,22 +177,19 @@ describe('withMetrics', () => {
|
|
|
196
177
|
await new Promise((r) => setImmediate(r));
|
|
197
178
|
const gateLog = calls.find((c) => c.body?.errorMessage?.includes('session-start gate'));
|
|
198
179
|
expect(gateLog).toBeDefined();
|
|
199
|
-
// Running session_start unlocks the session.
|
|
200
180
|
const rules = await start({});
|
|
201
181
|
expect(rules.content[0].text).toBe('rules');
|
|
202
182
|
expect(startHandler).toHaveBeenCalledTimes(1);
|
|
203
|
-
// Now the previously-gated tool runs normally.
|
|
204
183
|
const ok = await list({});
|
|
205
184
|
expect(ok.content[0].text).toBe('projects');
|
|
206
185
|
expect(listHandler).toHaveBeenCalledTimes(1);
|
|
207
186
|
});
|
|
208
187
|
it('does not gate anything when the gate is disabled (default)', async () => {
|
|
209
188
|
captureFetch();
|
|
210
|
-
const nudge = createNudgeState();
|
|
189
|
+
const nudge = createNudgeState();
|
|
211
190
|
const handler = vi.fn(async () => ({ content: [{ type: 'text', text: 'ok' }] }));
|
|
212
191
|
const list = withMetrics(client, 'orboto_list_projects', undefined, handler, nudge);
|
|
213
192
|
const res = await list({});
|
|
214
|
-
// Still nudged (soft), but NOT refused - the handler ran.
|
|
215
193
|
expect(res.content[1].text).toBe('ok');
|
|
216
194
|
expect(handler).toHaveBeenCalledTimes(1);
|
|
217
195
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orboto/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.194.1",
|
|
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",
|