xcode-copilot-server 1.0.4 → 2.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.
- package/README.md +56 -29
- package/config.json5 +49 -41
- package/dist/config.d.ts +9 -4
- package/dist/config.js +25 -7
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +1 -0
- package/dist/conversation-manager.d.ts +32 -0
- package/dist/conversation-manager.js +120 -0
- package/dist/conversation-manager.js.map +1 -0
- package/dist/copilot-service.d.ts +1 -3
- package/dist/copilot-service.js +3 -23
- package/dist/copilot-service.js.map +1 -1
- package/dist/handlers/completions/streaming.d.ts +1 -1
- package/dist/handlers/completions/streaming.js +13 -24
- package/dist/handlers/completions/streaming.js.map +1 -1
- package/dist/handlers/completions.d.ts +2 -2
- package/dist/handlers/completions.js +75 -63
- package/dist/handlers/completions.js.map +1 -1
- package/dist/handlers/errors.d.ts +5 -0
- package/dist/handlers/errors.js +10 -0
- package/dist/handlers/errors.js.map +1 -0
- package/dist/handlers/messages/count-tokens.d.ts +3 -0
- package/dist/handlers/messages/count-tokens.js +72 -0
- package/dist/handlers/messages/count-tokens.js.map +1 -0
- package/dist/handlers/messages/streaming.d.ts +6 -0
- package/dist/handlers/messages/streaming.js +274 -0
- package/dist/handlers/messages/streaming.js.map +1 -0
- package/dist/handlers/messages/tool-result-handler.d.ts +4 -0
- package/dist/handlers/messages/tool-result-handler.js +19 -0
- package/dist/handlers/messages/tool-result-handler.js.map +1 -0
- package/dist/handlers/messages.d.ts +4 -0
- package/dist/handlers/messages.js +150 -0
- package/dist/handlers/messages.js.map +1 -0
- package/dist/handlers/models.d.ts +0 -1
- package/dist/handlers/models.js +1 -2
- package/dist/handlers/models.js.map +1 -1
- package/dist/handlers/session-config.d.ts +15 -0
- package/dist/handlers/session-config.js +79 -0
- package/dist/handlers/session-config.js.map +1 -0
- package/dist/handlers/streaming-utils.d.ts +9 -0
- package/dist/handlers/streaming-utils.js +19 -0
- package/dist/handlers/streaming-utils.js.map +1 -0
- package/dist/index.js +22 -7
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +0 -1
- package/dist/logger.js +4 -10
- package/dist/logger.js.map +1 -1
- package/dist/providers/anthropic.d.ts +5 -0
- package/dist/providers/anthropic.js +28 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.js +7 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +5 -0
- package/dist/providers/openai.js +25 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +7 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/schemas/anthropic.d.ts +140 -0
- package/dist/schemas/anthropic.js +58 -0
- package/dist/schemas/anthropic.js.map +1 -0
- package/dist/schemas/config.d.ts +127 -0
- package/dist/schemas/config.js +44 -0
- package/dist/schemas/config.js.map +1 -0
- package/dist/schemas/openai.d.ts +98 -0
- package/dist/schemas/openai.js +76 -0
- package/dist/schemas/openai.js.map +1 -0
- package/dist/server.d.ts +2 -1
- package/dist/server.js +11 -20
- package/dist/server.js.map +1 -1
- package/dist/tool-bridge/index.d.ts +4 -0
- package/dist/tool-bridge/index.js +8 -0
- package/dist/tool-bridge/index.js.map +1 -0
- package/dist/tool-bridge/reply-tracker.d.ts +10 -0
- package/dist/tool-bridge/reply-tracker.js +28 -0
- package/dist/tool-bridge/reply-tracker.js.map +1 -0
- package/dist/tool-bridge/routes.d.ts +4 -0
- package/dist/tool-bridge/routes.js +111 -0
- package/dist/tool-bridge/routes.js.map +1 -0
- package/dist/tool-bridge/session-lifecycle.d.ts +16 -0
- package/dist/tool-bridge/session-lifecycle.js +43 -0
- package/dist/tool-bridge/session-lifecycle.js.map +1 -0
- package/dist/tool-bridge/state.d.ts +34 -0
- package/dist/tool-bridge/state.js +77 -0
- package/dist/tool-bridge/state.js.map +1 -0
- package/dist/tool-bridge/tool-cache.d.ts +10 -0
- package/dist/tool-bridge/tool-cache.js +82 -0
- package/dist/tool-bridge/tool-cache.js.map +1 -0
- package/dist/tool-bridge/tool-router.d.ts +12 -0
- package/dist/tool-bridge/tool-router.js +82 -0
- package/dist/tool-bridge/tool-router.js.map +1 -0
- package/dist/utils/anthropic-prompt.d.ts +2 -0
- package/dist/utils/anthropic-prompt.js +52 -0
- package/dist/utils/anthropic-prompt.js.map +1 -0
- package/dist/utils/model-resolver.d.ts +3 -0
- package/dist/utils/model-resolver.js +45 -0
- package/dist/utils/model-resolver.js.map +1 -0
- package/dist/utils/prompt.d.ts +2 -14
- package/dist/utils/prompt.js +11 -14
- package/dist/utils/prompt.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const MCPLocalServerSchema = z.object({
|
|
3
|
+
type: z.union([z.literal("local"), z.literal("stdio")]),
|
|
4
|
+
command: z.string().min(1, "MCP server command cannot be empty"),
|
|
5
|
+
args: z.array(z.string()),
|
|
6
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
7
|
+
cwd: z.string().optional(),
|
|
8
|
+
allowedTools: z.array(z.string()).optional(),
|
|
9
|
+
timeout: z.number().positive().optional(),
|
|
10
|
+
});
|
|
11
|
+
const MCPRemoteServerSchema = z.object({
|
|
12
|
+
type: z.union([z.literal("http"), z.literal("sse")]),
|
|
13
|
+
url: z.url(),
|
|
14
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
15
|
+
allowedTools: z.array(z.string()).optional(),
|
|
16
|
+
timeout: z.number().positive().optional(),
|
|
17
|
+
});
|
|
18
|
+
const MCPServerSchema = z.union([MCPLocalServerSchema, MCPRemoteServerSchema]);
|
|
19
|
+
const VALID_PERMISSION_KINDS = ["read", "write", "shell", "mcp", "url"];
|
|
20
|
+
const ApprovalRuleSchema = z.union([
|
|
21
|
+
z.boolean(),
|
|
22
|
+
z.array(z.enum(VALID_PERMISSION_KINDS)),
|
|
23
|
+
]);
|
|
24
|
+
const VALID_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
25
|
+
const ReasoningEffortSchema = z.enum(VALID_REASONING_EFFORTS);
|
|
26
|
+
const ToolBridgeServerSchema = z.boolean();
|
|
27
|
+
const ProviderConfigSchema = z.object({
|
|
28
|
+
toolBridge: ToolBridgeServerSchema.optional().default(false),
|
|
29
|
+
mcpServers: z.record(z.string(), MCPServerSchema).default({}),
|
|
30
|
+
});
|
|
31
|
+
export const ServerConfigSchema = z.object({
|
|
32
|
+
openai: ProviderConfigSchema.default({ toolBridge: false, mcpServers: {} }),
|
|
33
|
+
anthropic: ProviderConfigSchema.default({ toolBridge: false, mcpServers: {} }),
|
|
34
|
+
allowedCliTools: z.array(z.string()).refine((arr) => !arr.includes("*") || arr.length === 1, 'allowedCliTools: use ["*"] alone to allow all tools, don\'t mix with other entries').default([]),
|
|
35
|
+
excludedFilePatterns: z.array(z.string()).default([]),
|
|
36
|
+
bodyLimitMiB: z
|
|
37
|
+
.number()
|
|
38
|
+
.positive()
|
|
39
|
+
.max(100, "bodyLimitMiB cannot exceed 100")
|
|
40
|
+
.default(10),
|
|
41
|
+
reasoningEffort: ReasoningEffortSchema.optional(),
|
|
42
|
+
autoApprovePermissions: ApprovalRuleSchema.default(["read", "mcp"]),
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/schemas/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const MessageSchema: z.ZodObject<{
|
|
3
|
+
role: z.ZodOptional<z.ZodEnum<{
|
|
4
|
+
system: "system";
|
|
5
|
+
developer: "developer";
|
|
6
|
+
user: "user";
|
|
7
|
+
assistant: "assistant";
|
|
8
|
+
tool: "tool";
|
|
9
|
+
}>>;
|
|
10
|
+
content: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodObject<{
|
|
11
|
+
type: z.ZodString;
|
|
12
|
+
text: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$loose>>, z.ZodNull]>>;
|
|
14
|
+
name: z.ZodOptional<z.ZodString>;
|
|
15
|
+
tool_calls: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
16
|
+
index: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
id: z.ZodOptional<z.ZodString>;
|
|
18
|
+
type: z.ZodOptional<z.ZodString>;
|
|
19
|
+
function: z.ZodObject<{
|
|
20
|
+
name: z.ZodString;
|
|
21
|
+
arguments: z.ZodString;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
}, z.core.$strip>>>;
|
|
24
|
+
tool_call_id: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
export type ChatCompletionMessage = z.infer<typeof MessageSchema>;
|
|
27
|
+
export declare const ChatCompletionRequestSchema: z.ZodObject<{
|
|
28
|
+
model: z.ZodString;
|
|
29
|
+
messages: z.ZodArray<z.ZodObject<{
|
|
30
|
+
role: z.ZodOptional<z.ZodEnum<{
|
|
31
|
+
system: "system";
|
|
32
|
+
developer: "developer";
|
|
33
|
+
user: "user";
|
|
34
|
+
assistant: "assistant";
|
|
35
|
+
tool: "tool";
|
|
36
|
+
}>>;
|
|
37
|
+
content: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodObject<{
|
|
38
|
+
type: z.ZodString;
|
|
39
|
+
text: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, z.core.$loose>>, z.ZodNull]>>;
|
|
41
|
+
name: z.ZodOptional<z.ZodString>;
|
|
42
|
+
tool_calls: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
43
|
+
index: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
id: z.ZodOptional<z.ZodString>;
|
|
45
|
+
type: z.ZodOptional<z.ZodString>;
|
|
46
|
+
function: z.ZodObject<{
|
|
47
|
+
name: z.ZodString;
|
|
48
|
+
arguments: z.ZodString;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
}, z.core.$strip>>>;
|
|
51
|
+
tool_call_id: z.ZodOptional<z.ZodString>;
|
|
52
|
+
}, z.core.$strip>>;
|
|
53
|
+
temperature: z.ZodOptional<z.ZodNumber>;
|
|
54
|
+
top_p: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
n: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
stop: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
57
|
+
max_tokens: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
presence_penalty: z.ZodOptional<z.ZodNumber>;
|
|
59
|
+
frequency_penalty: z.ZodOptional<z.ZodNumber>;
|
|
60
|
+
tools: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
61
|
+
type: z.ZodString;
|
|
62
|
+
function: z.ZodObject<{
|
|
63
|
+
name: z.ZodString;
|
|
64
|
+
description: z.ZodOptional<z.ZodString>;
|
|
65
|
+
parameters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
}, z.core.$strip>>>;
|
|
68
|
+
tool_choice: z.ZodOptional<z.ZodUnknown>;
|
|
69
|
+
user: z.ZodOptional<z.ZodString>;
|
|
70
|
+
}, z.core.$strip>;
|
|
71
|
+
export type ChatCompletionRequest = z.infer<typeof ChatCompletionRequestSchema>;
|
|
72
|
+
export interface Choice {
|
|
73
|
+
index: number;
|
|
74
|
+
message?: ChatCompletionMessage | undefined;
|
|
75
|
+
delta?: Partial<ChatCompletionMessage> | undefined;
|
|
76
|
+
finish_reason: string | null;
|
|
77
|
+
}
|
|
78
|
+
export interface ChatCompletionChunk {
|
|
79
|
+
id: string;
|
|
80
|
+
object: "chat.completion.chunk";
|
|
81
|
+
created: number;
|
|
82
|
+
model: string;
|
|
83
|
+
choices: Choice[];
|
|
84
|
+
system_fingerprint?: string | undefined;
|
|
85
|
+
}
|
|
86
|
+
export interface Model {
|
|
87
|
+
id: string;
|
|
88
|
+
object: "model";
|
|
89
|
+
created: number;
|
|
90
|
+
owned_by: string;
|
|
91
|
+
}
|
|
92
|
+
export interface ModelsResponse {
|
|
93
|
+
object: "list";
|
|
94
|
+
data: Model[];
|
|
95
|
+
}
|
|
96
|
+
export declare function currentTimestamp(): number;
|
|
97
|
+
export declare function extractContentText(content: ChatCompletionMessage["content"]): string;
|
|
98
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const ContentPartSchema = z.looseObject({
|
|
3
|
+
type: z.string(),
|
|
4
|
+
text: z.string().optional(),
|
|
5
|
+
});
|
|
6
|
+
const VALID_ROLES = ["system", "developer", "user", "assistant", "tool"];
|
|
7
|
+
const MessageSchema = z.object({
|
|
8
|
+
role: z.enum(VALID_ROLES).optional(),
|
|
9
|
+
content: z
|
|
10
|
+
.union([z.string(), z.array(ContentPartSchema), z.null()])
|
|
11
|
+
.optional(),
|
|
12
|
+
name: z.string().optional(),
|
|
13
|
+
tool_calls: z
|
|
14
|
+
.array(z.object({
|
|
15
|
+
index: z.number().optional(),
|
|
16
|
+
id: z.string().optional(),
|
|
17
|
+
type: z.string().optional(),
|
|
18
|
+
function: z.object({
|
|
19
|
+
name: z.string(),
|
|
20
|
+
arguments: z.string(),
|
|
21
|
+
}),
|
|
22
|
+
}))
|
|
23
|
+
.optional(),
|
|
24
|
+
tool_call_id: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
export const ChatCompletionRequestSchema = z.object({
|
|
27
|
+
model: z.string().min(1, "Model is required"),
|
|
28
|
+
messages: z.array(MessageSchema).min(1, "Messages are required"),
|
|
29
|
+
temperature: z.number().optional(),
|
|
30
|
+
top_p: z.number().optional(),
|
|
31
|
+
n: z.number().optional(),
|
|
32
|
+
stop: z.union([z.string(), z.array(z.string())]).optional(),
|
|
33
|
+
max_tokens: z.number().optional(),
|
|
34
|
+
presence_penalty: z.number().optional(),
|
|
35
|
+
frequency_penalty: z.number().optional(),
|
|
36
|
+
tools: z
|
|
37
|
+
.array(z.object({
|
|
38
|
+
type: z.string(),
|
|
39
|
+
function: z.object({
|
|
40
|
+
name: z.string(),
|
|
41
|
+
description: z.string().optional(),
|
|
42
|
+
parameters: z.record(z.string(), z.unknown()).optional(),
|
|
43
|
+
}),
|
|
44
|
+
}))
|
|
45
|
+
.optional(),
|
|
46
|
+
tool_choice: z.unknown().optional(),
|
|
47
|
+
user: z.string().optional(),
|
|
48
|
+
});
|
|
49
|
+
export function currentTimestamp() {
|
|
50
|
+
return Math.floor(Date.now() / 1000);
|
|
51
|
+
}
|
|
52
|
+
// We only support text content, so this rejects anything else early
|
|
53
|
+
// and lets the caller surface a 400.
|
|
54
|
+
export function extractContentText(content) {
|
|
55
|
+
if (content == null) {
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
if (typeof content === "string") {
|
|
59
|
+
return content;
|
|
60
|
+
}
|
|
61
|
+
if (!Array.isArray(content)) {
|
|
62
|
+
throw new Error(`invalid content type: expected string or array, got ${typeof content}`);
|
|
63
|
+
}
|
|
64
|
+
let text = "";
|
|
65
|
+
for (const part of content) {
|
|
66
|
+
if (part.type !== "text") {
|
|
67
|
+
throw new Error(`unsupported content type: ${part.type}`);
|
|
68
|
+
}
|
|
69
|
+
if (typeof part.text !== "string") {
|
|
70
|
+
throw new Error("text content part missing required 'text' field");
|
|
71
|
+
}
|
|
72
|
+
text += part.text;
|
|
73
|
+
}
|
|
74
|
+
return text;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/schemas/openai.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type FastifyInstance } from "fastify";
|
|
2
2
|
import type { AppContext } from "./context.js";
|
|
3
|
-
|
|
3
|
+
import type { Provider } from "./providers/types.js";
|
|
4
|
+
export declare function createServer(ctx: AppContext, provider: Provider): Promise<FastifyInstance>;
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import Fastify from "fastify";
|
|
1
|
+
import Fastify, {} from "fastify";
|
|
2
2
|
import cors from "@fastify/cors";
|
|
3
|
-
import { createModelsHandler } from "./handlers/models.js";
|
|
4
|
-
import { createCompletionsHandler } from "./handlers/completions.js";
|
|
5
3
|
const PINO_LEVEL = {
|
|
6
4
|
none: "silent",
|
|
7
5
|
error: "error",
|
|
@@ -10,11 +8,11 @@ const PINO_LEVEL = {
|
|
|
10
8
|
debug: "debug",
|
|
11
9
|
all: "trace",
|
|
12
10
|
};
|
|
13
|
-
export async function createServer(ctx) {
|
|
11
|
+
export async function createServer(ctx, provider) {
|
|
14
12
|
const app = Fastify({
|
|
15
13
|
bodyLimit: ctx.config.bodyLimit,
|
|
16
14
|
// Destroy active connections immediately on close() so shutdown doesn't
|
|
17
|
-
// hang waiting for SSE streams or pending requests to drain.
|
|
15
|
+
// hang waiting for SSE streams or pending tool bridge requests to drain.
|
|
18
16
|
forceCloseConnections: true,
|
|
19
17
|
logger: {
|
|
20
18
|
level: PINO_LEVEL[ctx.logger.level],
|
|
@@ -23,22 +21,15 @@ export async function createServer(ctx) {
|
|
|
23
21
|
await app.register(cors, {
|
|
24
22
|
origin: "*",
|
|
25
23
|
methods: ["GET", "POST", "OPTIONS"],
|
|
26
|
-
allowedHeaders: [
|
|
24
|
+
allowedHeaders: [
|
|
25
|
+
"Content-Type",
|
|
26
|
+
"Authorization",
|
|
27
|
+
"anthropic-beta",
|
|
28
|
+
"anthropic-version",
|
|
29
|
+
"x-api-key",
|
|
30
|
+
],
|
|
27
31
|
});
|
|
28
|
-
|
|
29
|
-
const ua = request.headers["user-agent"] ?? "";
|
|
30
|
-
if (!ua.startsWith("Xcode/")) {
|
|
31
|
-
ctx.logger.warn(`Rejected request from unexpected user-agent: ${ua}`);
|
|
32
|
-
void reply
|
|
33
|
-
.code(403)
|
|
34
|
-
.type("application/json")
|
|
35
|
-
.send('{"error":"Forbidden"}\n');
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
done();
|
|
39
|
-
});
|
|
40
|
-
app.get("/v1/models", createModelsHandler(ctx));
|
|
41
|
-
app.post("/v1/chat/completions", createCompletionsHandler(ctx));
|
|
32
|
+
provider.register(app, ctx);
|
|
42
33
|
return app;
|
|
43
34
|
}
|
|
44
35
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAwB,MAAM,SAAS,CAAC;AACxD,OAAO,IAAI,MAAM,eAAe,CAAC;AAKjC,MAAM,UAAU,GAAG;IACjB,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,MAAM;IACf,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,OAAO;CACsB,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAe,EACf,QAAkB;IAElB,MAAM,GAAG,GAAG,OAAO,CAAC;QAClB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS;QAC/B,wEAAwE;QACxE,yEAAyE;QACzE,qBAAqB,EAAE,IAAI;QAC3B,MAAM,EAAE;YACN,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;SACpC;KACF,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE;QACvB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC;QACnC,cAAc,EAAE;YACd,cAAc;YACd,eAAe;YACf,gBAAgB;YAChB,mBAAmB;YACnB,WAAW;SACZ;KACF,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAE5B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ConversationManager } from "../conversation-manager.js";
|
|
2
|
+
import { registerRoutes } from "./routes.js";
|
|
3
|
+
export function registerToolBridge(app, logger) {
|
|
4
|
+
const manager = new ConversationManager(logger);
|
|
5
|
+
registerRoutes(app, manager, logger);
|
|
6
|
+
return manager;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tool-bridge/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,UAAU,kBAAkB,CAAC,GAAoB,EAAE,MAAc;IACrE,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAChD,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FastifyReply } from "fastify";
|
|
2
|
+
export declare class ReplyTracker {
|
|
3
|
+
private reply;
|
|
4
|
+
private streamingDone;
|
|
5
|
+
get currentReply(): FastifyReply | null;
|
|
6
|
+
setReply(reply: FastifyReply): void;
|
|
7
|
+
clearReply(): void;
|
|
8
|
+
notifyStreamingDone(): void;
|
|
9
|
+
waitForStreamingDone(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class ReplyTracker {
|
|
2
|
+
reply = null;
|
|
3
|
+
streamingDone = null;
|
|
4
|
+
get currentReply() {
|
|
5
|
+
return this.reply;
|
|
6
|
+
}
|
|
7
|
+
setReply(reply) {
|
|
8
|
+
this.reply = reply;
|
|
9
|
+
}
|
|
10
|
+
clearReply() {
|
|
11
|
+
this.reply = null;
|
|
12
|
+
}
|
|
13
|
+
notifyStreamingDone() {
|
|
14
|
+
if (this.streamingDone) {
|
|
15
|
+
this.streamingDone();
|
|
16
|
+
this.streamingDone = null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
waitForStreamingDone() {
|
|
20
|
+
if (this.streamingDone) {
|
|
21
|
+
throw new Error("Already waiting for streaming to complete");
|
|
22
|
+
}
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
this.streamingDone = resolve;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=reply-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reply-tracker.js","sourceRoot":"","sources":["../../src/tool-bridge/reply-tracker.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,YAAY;IACf,KAAK,GAAwB,IAAI,CAAC;IAClC,aAAa,GAAwB,IAAI,CAAC;IAElD,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,KAAmB;QAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,mBAAmB;QACjB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,oBAAoB;QAClB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { ConversationManager } from "../conversation-manager.js";
|
|
3
|
+
import type { Logger } from "../logger.js";
|
|
4
|
+
export declare function registerRoutes(app: FastifyInstance, manager: ConversationManager, logger: Logger): void;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const JsonRpcRequestSchema = z.object({
|
|
3
|
+
jsonrpc: z.literal("2.0"),
|
|
4
|
+
id: z.union([z.number(), z.string()]).optional(),
|
|
5
|
+
method: z.string(),
|
|
6
|
+
params: z.record(z.string(), z.unknown()).optional(),
|
|
7
|
+
});
|
|
8
|
+
function jsonRpcResult(id, result) {
|
|
9
|
+
return { jsonrpc: "2.0", id, result };
|
|
10
|
+
}
|
|
11
|
+
function jsonRpcError(id, code, message) {
|
|
12
|
+
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
13
|
+
}
|
|
14
|
+
// Pinned to the version the Copilot SDK's MCP client actually speaks
|
|
15
|
+
const PROTOCOL_VERSION = "2024-11-05";
|
|
16
|
+
// Xcode tools arrive with names like "mcp__xcode-tools__XcodeRead" but
|
|
17
|
+
// we need to strip that prefix, otherwise the CLI would expose them to
|
|
18
|
+
// the model as "xcode-bridge-mcp__xcode-tools__XcodeRead" which is ugly
|
|
19
|
+
// and wastes tokens.
|
|
20
|
+
const MCP_TOOL_PREFIX = /^mcp__[^_]+__/;
|
|
21
|
+
function stripMCPToolPrefix(name) {
|
|
22
|
+
return name.replace(MCP_TOOL_PREFIX, "");
|
|
23
|
+
}
|
|
24
|
+
export function registerRoutes(app, manager, logger) {
|
|
25
|
+
// The SDK opens a GET SSE stream after initialize expecting server-initiated
|
|
26
|
+
// messages. We don't push anything, so we just keep it open.
|
|
27
|
+
app.get("/mcp/:convId", (request, reply) => {
|
|
28
|
+
const { convId } = request.params;
|
|
29
|
+
logger.debug(`MCP ${convId}: SSE stream opened`);
|
|
30
|
+
reply.raw.writeHead(200, {
|
|
31
|
+
"Content-Type": "text/event-stream",
|
|
32
|
+
"Cache-Control": "no-cache",
|
|
33
|
+
Connection: "keep-alive",
|
|
34
|
+
});
|
|
35
|
+
request.raw.on("close", () => {
|
|
36
|
+
logger.debug(`MCP ${convId}: SSE stream closed`);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
app.post("/mcp/:convId", async (request, reply) => {
|
|
40
|
+
const { convId } = request.params;
|
|
41
|
+
const parsed = JsonRpcRequestSchema.safeParse(request.body);
|
|
42
|
+
if (!parsed.success) {
|
|
43
|
+
return reply.send(jsonRpcError(0, -32700, "Parse error"));
|
|
44
|
+
}
|
|
45
|
+
const msg = parsed.data;
|
|
46
|
+
logger.debug(`MCP ${convId}: method="${msg.method}", id=${String(msg.id)}`);
|
|
47
|
+
// JSON-RPC notifications have no id, so there's nothing to respond to
|
|
48
|
+
if (msg.id === undefined) {
|
|
49
|
+
return reply.status(202).send();
|
|
50
|
+
}
|
|
51
|
+
const { id, method, params } = msg;
|
|
52
|
+
switch (method) {
|
|
53
|
+
case "initialize":
|
|
54
|
+
return reply.send(jsonRpcResult(id, {
|
|
55
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
56
|
+
capabilities: { tools: {} },
|
|
57
|
+
serverInfo: { name: "xcode-bridge", version: "1.0.0" },
|
|
58
|
+
}));
|
|
59
|
+
case "tools/list": {
|
|
60
|
+
const state = manager.getState(convId);
|
|
61
|
+
if (!state) {
|
|
62
|
+
logger.warn(`MCP ${convId} tools/list: conversation not found`);
|
|
63
|
+
return reply.send(jsonRpcError(id, -32603, "Conversation not found"));
|
|
64
|
+
}
|
|
65
|
+
const tools = state.getCachedTools().map((t) => ({
|
|
66
|
+
name: stripMCPToolPrefix(t.name),
|
|
67
|
+
description: t.description,
|
|
68
|
+
inputSchema: t.input_schema,
|
|
69
|
+
}));
|
|
70
|
+
logger.debug(`MCP ${convId} tools/list: ${String(tools.length)} tools`);
|
|
71
|
+
return reply.send(jsonRpcResult(id, { tools }));
|
|
72
|
+
}
|
|
73
|
+
case "tools/call": {
|
|
74
|
+
const state = manager.getState(convId);
|
|
75
|
+
if (!state) {
|
|
76
|
+
logger.warn(`MCP ${convId} tools/call: conversation not found`);
|
|
77
|
+
return reply.send(jsonRpcError(id, -32603, "Conversation not found"));
|
|
78
|
+
}
|
|
79
|
+
const name = params?.["name"];
|
|
80
|
+
const args = (params?.["arguments"] ?? {});
|
|
81
|
+
if (!name) {
|
|
82
|
+
return reply.send(jsonRpcError(id, -32602, "Missing tool name"));
|
|
83
|
+
}
|
|
84
|
+
const resolved = state.resolveToolName(name);
|
|
85
|
+
if (resolved !== name) {
|
|
86
|
+
logger.info(`MCP ${convId} tools/call: name="${name}" resolved to "${resolved}", args=${JSON.stringify(args)}`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
logger.info(`MCP ${convId} tools/call: name="${name}", args=${JSON.stringify(args)}`);
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const result = await new Promise((resolve, reject) => {
|
|
93
|
+
state.registerMCPRequest(resolved, resolve, reject);
|
|
94
|
+
});
|
|
95
|
+
logger.info(`MCP ${convId} tools/call resolved: name="${name}"`);
|
|
96
|
+
return await reply.send(jsonRpcResult(id, {
|
|
97
|
+
content: [{ type: "text", text: result }],
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
102
|
+
logger.error(`MCP ${convId} tools/call error: ${message}`);
|
|
103
|
+
return reply.send(jsonRpcError(id, -32603, message));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
default:
|
|
107
|
+
return reply.send(jsonRpcError(id, -32601, `Method not found: ${method}`));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/tool-bridge/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ToolRouter } from "./tool-router.js";
|
|
2
|
+
export declare class SessionLifecycle {
|
|
3
|
+
private readonly toolRouter;
|
|
4
|
+
private _sessionActive;
|
|
5
|
+
private _hadError;
|
|
6
|
+
private _onSessionEnd;
|
|
7
|
+
constructor(toolRouter: ToolRouter);
|
|
8
|
+
get sessionActive(): boolean;
|
|
9
|
+
get hadError(): boolean;
|
|
10
|
+
markSessionActive(): void;
|
|
11
|
+
markSessionErrored(): void;
|
|
12
|
+
onSessionEnd(callback: () => void): void;
|
|
13
|
+
markSessionInactive(): void;
|
|
14
|
+
cleanup(): void;
|
|
15
|
+
private fireSessionEnd;
|
|
16
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class SessionLifecycle {
|
|
2
|
+
toolRouter;
|
|
3
|
+
_sessionActive = false;
|
|
4
|
+
_hadError = false;
|
|
5
|
+
_onSessionEnd = null;
|
|
6
|
+
constructor(toolRouter) {
|
|
7
|
+
this.toolRouter = toolRouter;
|
|
8
|
+
}
|
|
9
|
+
get sessionActive() {
|
|
10
|
+
return this._sessionActive;
|
|
11
|
+
}
|
|
12
|
+
get hadError() {
|
|
13
|
+
return this._hadError;
|
|
14
|
+
}
|
|
15
|
+
markSessionActive() {
|
|
16
|
+
this._sessionActive = true;
|
|
17
|
+
}
|
|
18
|
+
markSessionErrored() {
|
|
19
|
+
this._hadError = true;
|
|
20
|
+
}
|
|
21
|
+
onSessionEnd(callback) {
|
|
22
|
+
this._onSessionEnd = callback;
|
|
23
|
+
}
|
|
24
|
+
markSessionInactive() {
|
|
25
|
+
this._sessionActive = false;
|
|
26
|
+
// stale entries from tool calls that never went through the bridge
|
|
27
|
+
// (denied or handled internally) would hang the next continuation
|
|
28
|
+
this.toolRouter.rejectAll("Session ended");
|
|
29
|
+
this.fireSessionEnd();
|
|
30
|
+
}
|
|
31
|
+
cleanup() {
|
|
32
|
+
this._sessionActive = false;
|
|
33
|
+
this.toolRouter.rejectAll("Session cleanup");
|
|
34
|
+
this.fireSessionEnd();
|
|
35
|
+
}
|
|
36
|
+
fireSessionEnd() {
|
|
37
|
+
if (this._onSessionEnd) {
|
|
38
|
+
this._onSessionEnd();
|
|
39
|
+
this._onSessionEnd = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=session-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lifecycle.js","sourceRoot":"","sources":["../../src/tool-bridge/session-lifecycle.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,gBAAgB;IACV,UAAU,CAAa;IAChC,cAAc,GAAG,KAAK,CAAC;IACvB,SAAS,GAAG,KAAK,CAAC;IAClB,aAAa,GAAwB,IAAI,CAAC;IAElD,YAAY,UAAsB;QAChC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC7B,CAAC;IAED,kBAAkB;QAChB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,YAAY,CAAC,QAAoB;QAC/B,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;IAChC,CAAC;IAED,mBAAmB;QACjB,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAE5B,mEAAmE;QACnE,kEAAkE;QAClE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAE3C,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC7C,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAEO,cAAc;QACpB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { FastifyReply } from "fastify";
|
|
2
|
+
import type { AnthropicToolDefinition } from "../schemas/anthropic.js";
|
|
3
|
+
import { ToolCache } from "./tool-cache.js";
|
|
4
|
+
import { ToolRouter } from "./tool-router.js";
|
|
5
|
+
import { ReplyTracker } from "./reply-tracker.js";
|
|
6
|
+
import { SessionLifecycle } from "./session-lifecycle.js";
|
|
7
|
+
export declare class ToolBridgeState {
|
|
8
|
+
readonly toolCache: ToolCache;
|
|
9
|
+
readonly toolRouter: ToolRouter;
|
|
10
|
+
readonly replyTracker: ReplyTracker;
|
|
11
|
+
readonly session: SessionLifecycle;
|
|
12
|
+
cacheTools(tools: AnthropicToolDefinition[]): void;
|
|
13
|
+
getCachedTools(): AnthropicToolDefinition[];
|
|
14
|
+
resolveToolName(name: string): string;
|
|
15
|
+
normalizeArgs(toolName: string, args: Record<string, unknown>): Record<string, unknown>;
|
|
16
|
+
hasPendingToolCall(toolCallId: string): boolean;
|
|
17
|
+
hasExpectedTool(name: string): boolean;
|
|
18
|
+
registerExpected(toolCallId: string, toolName: string): void;
|
|
19
|
+
registerMCPRequest(name: string, resolve: (result: string) => void, reject: (err: Error) => void): void;
|
|
20
|
+
resolveToolCall(toolCallId: string, result: string): boolean;
|
|
21
|
+
get hasPending(): boolean;
|
|
22
|
+
get currentReply(): FastifyReply | null;
|
|
23
|
+
setReply(reply: FastifyReply): void;
|
|
24
|
+
clearReply(): void;
|
|
25
|
+
notifyStreamingDone(): void;
|
|
26
|
+
waitForStreamingDone(): Promise<void>;
|
|
27
|
+
get sessionActive(): boolean;
|
|
28
|
+
get hadError(): boolean;
|
|
29
|
+
markSessionActive(): void;
|
|
30
|
+
markSessionErrored(): void;
|
|
31
|
+
markSessionInactive(): void;
|
|
32
|
+
onSessionEnd(callback: () => void): void;
|
|
33
|
+
cleanup(): void;
|
|
34
|
+
}
|