create-multicast 0.4.2 → 0.4.4
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/package.json +1 -1
- package/templates/default/src/index.ts +179 -47
package/package.json
CHANGED
|
@@ -919,66 +919,118 @@ Clears the cache and re-fetches tools/list from every registered server.`,
|
|
|
919
919
|
|
|
920
920
|
this.server.tool(
|
|
921
921
|
"fetch_result",
|
|
922
|
-
`Retrieve the full output of
|
|
923
|
-
When multicast returns
|
|
924
|
-
|
|
922
|
+
`Retrieve the full output of large results stored by reference.
|
|
923
|
+
When multicast returns output_ref instead of inline output, call this tool
|
|
924
|
+
to get the complete data. Accepts a SINGLE ref or MULTIPLE refs at once.
|
|
925
|
+
Always pass ALL refs in one call — don't call this tool multiple times.
|
|
926
|
+
Results expire after 1 hour.
|
|
927
|
+
|
|
928
|
+
Example (single): { "ref": "ref_abc123" }
|
|
929
|
+
Example (batch): { "refs": ["ref_abc123", "ref_def456"] }`,
|
|
925
930
|
{
|
|
926
931
|
ref: z
|
|
927
932
|
.string()
|
|
928
|
-
.
|
|
933
|
+
.optional()
|
|
934
|
+
.describe("Single output_ref ID (use this OR refs, not both)"),
|
|
935
|
+
refs: z
|
|
936
|
+
.array(z.string())
|
|
937
|
+
.optional()
|
|
938
|
+
.describe("Multiple output_ref IDs to fetch at once (use this OR ref, not both)"),
|
|
929
939
|
},
|
|
930
|
-
async ({ ref }) => {
|
|
940
|
+
async ({ ref, refs }) => {
|
|
931
941
|
const db = this.env.DB;
|
|
932
942
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
)
|
|
937
|
-
|
|
938
|
-
.
|
|
939
|
-
|
|
940
|
-
tool_name: string;
|
|
941
|
-
output: string;
|
|
942
|
-
created_at: string;
|
|
943
|
-
}>();
|
|
944
|
-
|
|
945
|
-
if (!row) {
|
|
943
|
+
// Normalize: single ref or batch refs into one array
|
|
944
|
+
const refIds: string[] = [];
|
|
945
|
+
if (refs && refs.length > 0) {
|
|
946
|
+
refIds.push(...refs);
|
|
947
|
+
} else if (ref) {
|
|
948
|
+
refIds.push(ref);
|
|
949
|
+
} else {
|
|
946
950
|
return {
|
|
947
|
-
content: [
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
error: `Result not found for ref "${ref}". It may have expired (results are kept for 1 hour).`,
|
|
952
|
-
}),
|
|
953
|
-
},
|
|
954
|
-
],
|
|
951
|
+
content: [{
|
|
952
|
+
type: "text" as const,
|
|
953
|
+
text: JSON.stringify({ error: "Provide either 'ref' (single) or 'refs' (array)." }),
|
|
954
|
+
}],
|
|
955
955
|
};
|
|
956
956
|
}
|
|
957
957
|
|
|
958
|
-
//
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
958
|
+
// Fetch all refs in one D1 batch
|
|
959
|
+
const results: Array<{
|
|
960
|
+
ref_id: string;
|
|
961
|
+
server: string;
|
|
962
|
+
tool: string;
|
|
963
|
+
output: unknown;
|
|
964
|
+
cached_at: string;
|
|
965
|
+
error?: string;
|
|
966
|
+
}> = [];
|
|
967
|
+
|
|
968
|
+
for (const refId of refIds) {
|
|
969
|
+
const row = await db
|
|
970
|
+
.prepare(
|
|
971
|
+
"SELECT server_name, tool_name, output, created_at FROM result_cache WHERE ref_id = ?"
|
|
972
|
+
)
|
|
973
|
+
.bind(refId)
|
|
974
|
+
.first<{
|
|
975
|
+
server_name: string;
|
|
976
|
+
tool_name: string;
|
|
977
|
+
output: string;
|
|
978
|
+
created_at: string;
|
|
979
|
+
}>();
|
|
980
|
+
|
|
981
|
+
if (!row) {
|
|
982
|
+
results.push({
|
|
983
|
+
ref_id: refId,
|
|
984
|
+
server: "unknown",
|
|
985
|
+
tool: "unknown",
|
|
986
|
+
output: null,
|
|
987
|
+
cached_at: "",
|
|
988
|
+
error: `Not found — may have expired (1 hour TTL).`,
|
|
989
|
+
});
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
let parsedOutput: unknown;
|
|
994
|
+
try {
|
|
995
|
+
parsedOutput = JSON.parse(row.output);
|
|
996
|
+
} catch {
|
|
997
|
+
parsedOutput = row.output;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
results.push({
|
|
1001
|
+
ref_id: refId,
|
|
1002
|
+
server: row.server_name,
|
|
1003
|
+
tool: row.tool_name,
|
|
1004
|
+
output: parsedOutput,
|
|
1005
|
+
cached_at: row.created_at,
|
|
1006
|
+
});
|
|
964
1007
|
}
|
|
965
1008
|
|
|
966
|
-
return
|
|
967
|
-
|
|
968
|
-
|
|
1009
|
+
// Single ref: return flat (backwards compatible)
|
|
1010
|
+
if (refIds.length === 1 && results.length === 1 && !results[0].error) {
|
|
1011
|
+
return {
|
|
1012
|
+
content: [{
|
|
969
1013
|
type: "text" as const,
|
|
970
|
-
text: JSON.stringify(
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1014
|
+
text: JSON.stringify({
|
|
1015
|
+
server: results[0].server,
|
|
1016
|
+
tool: results[0].tool,
|
|
1017
|
+
output: results[0].output,
|
|
1018
|
+
cached_at: results[0].cached_at,
|
|
1019
|
+
}, null, 2),
|
|
1020
|
+
}],
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Batch: return array of results
|
|
1025
|
+
return {
|
|
1026
|
+
content: [{
|
|
1027
|
+
type: "text" as const,
|
|
1028
|
+
text: JSON.stringify({
|
|
1029
|
+
results,
|
|
1030
|
+
fetched: results.filter((r) => !r.error).length,
|
|
1031
|
+
errors: results.filter((r) => r.error).length,
|
|
1032
|
+
}, null, 2),
|
|
1033
|
+
}],
|
|
982
1034
|
};
|
|
983
1035
|
}
|
|
984
1036
|
);
|
|
@@ -1024,6 +1076,86 @@ export default {
|
|
|
1024
1076
|
);
|
|
1025
1077
|
}
|
|
1026
1078
|
|
|
1079
|
+
// Status endpoint — full server + tool listing
|
|
1080
|
+
// Use: curl https://your-multicast.workers.dev/status
|
|
1081
|
+
// Or: curl https://your-multicast.workers.dev/status | jq .
|
|
1082
|
+
if (url.pathname === "/status") {
|
|
1083
|
+
try {
|
|
1084
|
+
const registry = getRegisteredServers(env);
|
|
1085
|
+
const db = env.DB;
|
|
1086
|
+
|
|
1087
|
+
const servers = await db
|
|
1088
|
+
.prepare(
|
|
1089
|
+
"SELECT name, url, status, tool_count, last_error, last_discovered_at FROM servers ORDER BY name"
|
|
1090
|
+
)
|
|
1091
|
+
.all<{
|
|
1092
|
+
name: string;
|
|
1093
|
+
url: string;
|
|
1094
|
+
status: string;
|
|
1095
|
+
tool_count: number;
|
|
1096
|
+
last_error: string | null;
|
|
1097
|
+
last_discovered_at: string | null;
|
|
1098
|
+
}>();
|
|
1099
|
+
|
|
1100
|
+
const serverDetails = [];
|
|
1101
|
+
for (const server of servers.results) {
|
|
1102
|
+
// Only include servers that are in the current registry
|
|
1103
|
+
if (!registry.has(server.name)) continue;
|
|
1104
|
+
|
|
1105
|
+
const regServer = registry.get(server.name)!;
|
|
1106
|
+
const tools = await db
|
|
1107
|
+
.prepare(
|
|
1108
|
+
"SELECT tool_name, description FROM tools WHERE server_name = ? ORDER BY tool_name"
|
|
1109
|
+
)
|
|
1110
|
+
.bind(server.name)
|
|
1111
|
+
.all<{ tool_name: string; description: string }>();
|
|
1112
|
+
|
|
1113
|
+
serverDetails.push({
|
|
1114
|
+
name: server.name,
|
|
1115
|
+
url: server.url,
|
|
1116
|
+
status: server.status,
|
|
1117
|
+
auth: regServer.isOAuth ? "oauth" : regServer.auth ? "static" : "none",
|
|
1118
|
+
tool_count: server.tool_count,
|
|
1119
|
+
last_error: server.last_error,
|
|
1120
|
+
last_discovered: server.last_discovered_at,
|
|
1121
|
+
tools: tools.results.map((t) => ({
|
|
1122
|
+
name: t.tool_name,
|
|
1123
|
+
description: t.description.slice(0, 120) + (t.description.length > 120 ? "..." : ""),
|
|
1124
|
+
})),
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
const totalTools = serverDetails.reduce((sum, s) => sum + s.tool_count, 0);
|
|
1129
|
+
|
|
1130
|
+
return new Response(
|
|
1131
|
+
JSON.stringify(
|
|
1132
|
+
{
|
|
1133
|
+
name: "Multicast",
|
|
1134
|
+
version: "0.4.2",
|
|
1135
|
+
description: "MCP gateway — one integration, all your servers, parallel execution",
|
|
1136
|
+
total_servers: serverDetails.length,
|
|
1137
|
+
total_tools: totalTools,
|
|
1138
|
+
servers: serverDetails,
|
|
1139
|
+
},
|
|
1140
|
+
null,
|
|
1141
|
+
2
|
|
1142
|
+
),
|
|
1143
|
+
{
|
|
1144
|
+
headers: {
|
|
1145
|
+
"Content-Type": "application/json",
|
|
1146
|
+
"Access-Control-Allow-Origin": "*",
|
|
1147
|
+
},
|
|
1148
|
+
}
|
|
1149
|
+
);
|
|
1150
|
+
} catch (err: unknown) {
|
|
1151
|
+
const message = err instanceof Error ? err.message : "unknown error";
|
|
1152
|
+
return new Response(
|
|
1153
|
+
JSON.stringify({ error: message }),
|
|
1154
|
+
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1027
1159
|
// Return clean 404 for OAuth discovery and other non-MCP paths
|
|
1028
1160
|
// Claude Code's HTTP transport probes /.well-known/oauth-authorization-server
|
|
1029
1161
|
// If this returns a JSON-RPC error, Claude Code's OAuth parser breaks.
|