@blockrun/mcp 0.16.1 → 0.18.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/dist/index.js +197 -9
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
ImageClient,
|
|
15
15
|
PriceClient,
|
|
16
16
|
SolanaLLMClient,
|
|
17
|
+
AnthropicClient,
|
|
17
18
|
getOrCreateWallet,
|
|
18
19
|
getOrCreateSolanaWallet,
|
|
19
20
|
loadSolanaWallet,
|
|
@@ -33,6 +34,7 @@ var _priceClient = null;
|
|
|
33
34
|
var _freePriceClient = null;
|
|
34
35
|
var _evmWalletInfo = null;
|
|
35
36
|
var _solanaClient = null;
|
|
37
|
+
var _anthropicClient = null;
|
|
36
38
|
function readChainPreference() {
|
|
37
39
|
for (const file of CHAIN_PREFERENCE_FILES) {
|
|
38
40
|
try {
|
|
@@ -58,6 +60,7 @@ var CHAIN_FILE = path.join(BLOCKRUN_DIR, ".chain");
|
|
|
58
60
|
function resetChainCaches() {
|
|
59
61
|
_evmClient = null;
|
|
60
62
|
_solanaClient = null;
|
|
63
|
+
_anthropicClient = null;
|
|
61
64
|
_imageClient = null;
|
|
62
65
|
_priceClient = null;
|
|
63
66
|
_freePriceClient = null;
|
|
@@ -78,6 +81,12 @@ async function ensureBothWallets() {
|
|
|
78
81
|
solana: { address: sol.address, isNew: sol.isNew }
|
|
79
82
|
};
|
|
80
83
|
}
|
|
84
|
+
function baseOnlyMessage(capability) {
|
|
85
|
+
if (getChain() === "solana") {
|
|
86
|
+
return `${capability} currently supports Base-chain payment only \u2014 your active chain is Solana. Switch with: blockrun_wallet action:"chain" chain:"base" (switch back later with chain:"solana"). Solana support depends on the @blockrun/llm SDK adding this capability.`;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
81
90
|
function ensureEvmWallet() {
|
|
82
91
|
if (!_evmWalletInfo) {
|
|
83
92
|
_evmWalletInfo = getOrCreateWallet();
|
|
@@ -108,6 +117,13 @@ function getClient() {
|
|
|
108
117
|
}
|
|
109
118
|
return _evmClient;
|
|
110
119
|
}
|
|
120
|
+
function getAnthropicClient() {
|
|
121
|
+
if (!_anthropicClient) {
|
|
122
|
+
const privateKey = getOrCreateWalletKey();
|
|
123
|
+
_anthropicClient = new AnthropicClient({ privateKey });
|
|
124
|
+
}
|
|
125
|
+
return _anthropicClient;
|
|
126
|
+
}
|
|
111
127
|
function getImageClient() {
|
|
112
128
|
if (!_imageClient) {
|
|
113
129
|
const privateKey = getOrCreateWalletKey();
|
|
@@ -214,11 +230,11 @@ var BASE_CHAIN_ID = "8453";
|
|
|
214
230
|
var MODEL_TIERS = {
|
|
215
231
|
fast: ["google/gemini-3.5-flash", "google/gemini-2.5-flash", "google/gemini-3.1-flash-lite", "openai/gpt-5-mini", "deepseek/deepseek-chat", "google/gemini-3-flash-preview"],
|
|
216
232
|
balanced: ["openai/gpt-5.4", "anthropic/claude-sonnet-4.6", "google/gemini-2.5-pro", "moonshot/kimi-k2.6", "openai/gpt-5.3", "google/gemini-3.1-pro"],
|
|
217
|
-
powerful: ["openai/gpt-5.4-pro", "anthropic/claude-opus-4.7", "anthropic/claude-opus-4.6", "openai/o3", "openai/gpt-5.4"],
|
|
233
|
+
powerful: ["anthropic/claude-opus-4.8", "openai/gpt-5.4-pro", "anthropic/claude-opus-4.7", "anthropic/claude-opus-4.6", "openai/o3", "openai/gpt-5.4"],
|
|
218
234
|
cheap: ["zai/glm-5", "zai/glm-5-turbo", "nvidia/gpt-oss-120b", "nvidia/deepseek-v3.2", "google/gemini-2.5-flash", "deepseek/deepseek-chat", "openai/gpt-5.4-nano"],
|
|
219
|
-
reasoning: ["openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner", "moonshot/kimi-k2.6", "openai/gpt-5.3-codex"],
|
|
235
|
+
reasoning: ["anthropic/claude-opus-4.8", "openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner", "moonshot/kimi-k2.6", "openai/gpt-5.3-codex"],
|
|
220
236
|
free: ["nvidia/qwen3-next-80b-a3b-thinking", "nvidia/mistral-small-4-119b", "nvidia/gpt-oss-120b", "nvidia/deepseek-v3.2", "nvidia/qwen3-coder-480b", "nvidia/llama-4-maverick", "nvidia/gpt-oss-20b", "nvidia/glm-4.7"],
|
|
221
|
-
coding: ["zai/glm-5", "openai/gpt-5.3-codex", "moonshot/kimi-k2.6", "nvidia/qwen3-coder-480b", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4"],
|
|
237
|
+
coding: ["anthropic/claude-opus-4.8", "zai/glm-5", "openai/gpt-5.3-codex", "moonshot/kimi-k2.6", "nvidia/qwen3-coder-480b", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4"],
|
|
222
238
|
glm: ["zai/glm-5", "zai/glm-5-turbo", "nvidia/glm-4.7"]
|
|
223
239
|
};
|
|
224
240
|
|
|
@@ -669,6 +685,134 @@ function recordSpending(budget, cost, agentId) {
|
|
|
669
685
|
}
|
|
670
686
|
}
|
|
671
687
|
|
|
688
|
+
// src/tools/chat-anthropic.ts
|
|
689
|
+
function isAnthropicModel(model) {
|
|
690
|
+
const id = model.trim();
|
|
691
|
+
return /^anthropic\//i.test(id) || /^claude-/i.test(id);
|
|
692
|
+
}
|
|
693
|
+
function parseDataUri(url) {
|
|
694
|
+
const match = /^data:(image\/(?:jpeg|png|gif|webp));base64,(.+)$/i.exec(url.trim());
|
|
695
|
+
if (!match) return null;
|
|
696
|
+
return {
|
|
697
|
+
type: "base64",
|
|
698
|
+
media_type: match[1].toLowerCase(),
|
|
699
|
+
data: match[2]
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function toAnthropicContent(content) {
|
|
703
|
+
if (typeof content === "string") return content;
|
|
704
|
+
const blocks = [];
|
|
705
|
+
for (const part of content) {
|
|
706
|
+
if (part.type === "text") {
|
|
707
|
+
if (part.text) blocks.push({ type: "text", text: part.text });
|
|
708
|
+
} else if (part.type === "image_url") {
|
|
709
|
+
const url = part.image_url?.url;
|
|
710
|
+
if (!url) continue;
|
|
711
|
+
const base64 = parseDataUri(url);
|
|
712
|
+
const source = base64 ? base64 : { type: "url", url };
|
|
713
|
+
blocks.push({ type: "image", source });
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return blocks;
|
|
717
|
+
}
|
|
718
|
+
function isTextBlock(b) {
|
|
719
|
+
return b.type === "text";
|
|
720
|
+
}
|
|
721
|
+
function isThinkingBlock(b) {
|
|
722
|
+
return b.type === "thinking";
|
|
723
|
+
}
|
|
724
|
+
async function handleAnthropicNative(args) {
|
|
725
|
+
const {
|
|
726
|
+
client,
|
|
727
|
+
model,
|
|
728
|
+
message,
|
|
729
|
+
system,
|
|
730
|
+
messages,
|
|
731
|
+
maxTokens,
|
|
732
|
+
temperature,
|
|
733
|
+
stop,
|
|
734
|
+
thinking,
|
|
735
|
+
budget,
|
|
736
|
+
agentId,
|
|
737
|
+
estimatedCost
|
|
738
|
+
} = args;
|
|
739
|
+
const systemParts = [];
|
|
740
|
+
if (system) systemParts.push(system);
|
|
741
|
+
const apiMessages = [];
|
|
742
|
+
for (const m of messages ?? []) {
|
|
743
|
+
if (m.role === "system") {
|
|
744
|
+
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
745
|
+
if (text) systemParts.push(text);
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
apiMessages.push({ role: m.role, content: toAnthropicContent(m.content) });
|
|
749
|
+
}
|
|
750
|
+
if (message.trim()) apiMessages.push({ role: "user", content: message });
|
|
751
|
+
if (apiMessages.length === 0) {
|
|
752
|
+
return { content: [{ type: "text", text: "No message content to send." }], isError: true };
|
|
753
|
+
}
|
|
754
|
+
let effectiveMax = maxTokens ?? 1024;
|
|
755
|
+
let raisedMaxTokens = false;
|
|
756
|
+
if (thinking && effectiveMax <= thinking.budget_tokens) {
|
|
757
|
+
effectiveMax = thinking.budget_tokens + 1024;
|
|
758
|
+
raisedMaxTokens = true;
|
|
759
|
+
}
|
|
760
|
+
const params = {
|
|
761
|
+
model,
|
|
762
|
+
max_tokens: effectiveMax,
|
|
763
|
+
messages: apiMessages
|
|
764
|
+
};
|
|
765
|
+
if (systemParts.length) params.system = systemParts.join("\n\n");
|
|
766
|
+
if (stop && stop.length) params.stop_sequences = stop;
|
|
767
|
+
if (thinking) {
|
|
768
|
+
params.thinking = { type: "enabled", budget_tokens: thinking.budget_tokens };
|
|
769
|
+
} else if (temperature !== void 0) {
|
|
770
|
+
params.temperature = Math.max(0, Math.min(1, temperature));
|
|
771
|
+
}
|
|
772
|
+
let native;
|
|
773
|
+
try {
|
|
774
|
+
native = await client.messages.create(params);
|
|
775
|
+
} catch (error) {
|
|
776
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
777
|
+
return { content: [{ type: "text", text: formatError(errorMessage) }], isError: true };
|
|
778
|
+
}
|
|
779
|
+
recordSpending(budget, estimatedCost, agentId);
|
|
780
|
+
const thinkingBlocks = native.content.filter(isThinkingBlock);
|
|
781
|
+
const textBlocks = native.content.filter(isTextBlock);
|
|
782
|
+
const answerText = textBlocks.map((b) => b.text).join("\n");
|
|
783
|
+
const thinkingText = thinkingBlocks.map((b) => b.thinking).join("\n");
|
|
784
|
+
const signaturePresent = thinkingBlocks.some(
|
|
785
|
+
(b) => typeof b.signature === "string" && b.signature.length > 0
|
|
786
|
+
);
|
|
787
|
+
const headerBits = [native.model, "native /v1/messages", `thinking ${thinking ? "on" : "off"}`];
|
|
788
|
+
if (raisedMaxTokens) headerBits.push(`max_tokens\u2192${effectiveMax}`);
|
|
789
|
+
const header = `[${headerBits.join(" | ")}]`;
|
|
790
|
+
const content = [{ type: "text", text: `${header}
|
|
791
|
+
|
|
792
|
+
${answerText}` }];
|
|
793
|
+
if (thinkingText) {
|
|
794
|
+
content.push({ type: "text", text: `\u{1F9E0} Thinking (signature ${signaturePresent ? "present" : "absent"}):
|
|
795
|
+
${thinkingText}` });
|
|
796
|
+
}
|
|
797
|
+
return {
|
|
798
|
+
content,
|
|
799
|
+
structuredContent: {
|
|
800
|
+
requested_model: model,
|
|
801
|
+
// Verbatim upstream model id — proof the call hit real Claude with no
|
|
802
|
+
// substitution. Intentionally NOT rewritten back to the requested id.
|
|
803
|
+
model: native.model,
|
|
804
|
+
response: answerText,
|
|
805
|
+
thinking: thinkingText || void 0,
|
|
806
|
+
// Native thinking blocks verbatim, including their original signature.
|
|
807
|
+
thinking_blocks: thinkingBlocks,
|
|
808
|
+
signature_present: signaturePresent,
|
|
809
|
+
stop_reason: native.stop_reason,
|
|
810
|
+
usage: native.usage,
|
|
811
|
+
native
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
672
816
|
// src/tools/chat.ts
|
|
673
817
|
function estimateChatCost(mode, model, routing, routingProfile) {
|
|
674
818
|
if (mode === "free") return 0;
|
|
@@ -714,15 +858,28 @@ Run blockrun_models to see all 41+ models with pricing.`,
|
|
|
714
858
|
system: z2.string().optional().describe("Optional system prompt"),
|
|
715
859
|
max_tokens: z2.number().optional().default(1024).describe("Max tokens in response"),
|
|
716
860
|
temperature: z2.number().optional().default(1).describe("Creativity 0-2"),
|
|
861
|
+
response_format: z2.enum(["text", "json_object"]).optional().describe("Set to 'json_object' to force valid JSON output (no markdown fences). Works across all providers."),
|
|
862
|
+
stop: z2.array(z2.string()).max(4).optional().describe("Up to 4 stop sequences; generation halts when any is produced"),
|
|
863
|
+
thinking: z2.object({
|
|
864
|
+
type: z2.literal("enabled"),
|
|
865
|
+
budget_tokens: z2.number().min(1).describe("Tokens Claude may spend reasoning before answering. max_tokens is auto-raised above this if needed.")
|
|
866
|
+
}).optional().describe("Anthropic extended thinking. Only honored for anthropic/claude-* models \u2014 these go direct to the native /v1/messages endpoint and the response includes verbatim type:'thinking' blocks with their original signature. Ignored for non-Claude models (no native thinking channel)."),
|
|
717
867
|
agent_id: z2.string().optional().describe("Agent identifier. If a budget was delegated for this agent_id via blockrun_wallet action:'delegate', spending is tracked and enforced. The agent is hard-stopped when its budget is exhausted."),
|
|
718
868
|
messages: z2.array(z2.object({
|
|
719
869
|
role: z2.enum(["user", "assistant", "system"]),
|
|
720
|
-
content: z2.
|
|
870
|
+
content: z2.union([
|
|
871
|
+
z2.string(),
|
|
872
|
+
z2.array(z2.union([
|
|
873
|
+
z2.object({ type: z2.literal("text"), text: z2.string() }),
|
|
874
|
+
z2.object({ type: z2.literal("image_url"), image_url: z2.object({ url: z2.string().describe("https URL or data:<mime>;base64,<...> URI") }) })
|
|
875
|
+
]))
|
|
876
|
+
]).describe("Plain text, or an array of parts for multimodal input (text + image_url). Images are honored on the native anthropic/claude-* path.")
|
|
721
877
|
})).optional().describe("Conversation history for multi-turn context. When provided, 'message' is appended as the final user turn. Use with explicit 'model' param (defaults to 'openai/gpt-5.4' if not specified). Note: if you include a role:'system' entry in messages[], do not also pass the system param to avoid duplicate system messages.")
|
|
722
878
|
}
|
|
723
879
|
},
|
|
724
|
-
async ({ message, model, mode, routing, routing_profile, system, max_tokens, temperature, agent_id, messages }) => {
|
|
880
|
+
async ({ message, model, mode, routing, routing_profile, system, max_tokens, temperature, response_format, stop, thinking, agent_id, messages }) => {
|
|
725
881
|
const llm = getClient();
|
|
882
|
+
const responseFormat = response_format ? { type: response_format } : void 0;
|
|
726
883
|
const estimatedCost = estimateChatCost(mode, model, routing, routing_profile);
|
|
727
884
|
const budgetCheck = checkBudget(budget, agent_id, estimatedCost);
|
|
728
885
|
if (!budgetCheck.allowed) {
|
|
@@ -731,6 +888,26 @@ Run blockrun_models to see all 41+ models with pricing.`,
|
|
|
731
888
|
isError: true
|
|
732
889
|
};
|
|
733
890
|
}
|
|
891
|
+
if (model && isAnthropicModel(model)) {
|
|
892
|
+
const solanaBlock = baseOnlyMessage("Native Anthropic (claude-*) calls");
|
|
893
|
+
if (solanaBlock) {
|
|
894
|
+
return { content: [{ type: "text", text: solanaBlock }], isError: true };
|
|
895
|
+
}
|
|
896
|
+
return handleAnthropicNative({
|
|
897
|
+
client: getAnthropicClient(),
|
|
898
|
+
model,
|
|
899
|
+
message,
|
|
900
|
+
system,
|
|
901
|
+
messages,
|
|
902
|
+
maxTokens: max_tokens,
|
|
903
|
+
temperature,
|
|
904
|
+
stop,
|
|
905
|
+
thinking,
|
|
906
|
+
budget,
|
|
907
|
+
agentId: agent_id,
|
|
908
|
+
estimatedCost
|
|
909
|
+
});
|
|
910
|
+
}
|
|
734
911
|
if (routing === "smart") {
|
|
735
912
|
if (!(llm instanceof LLMClient2)) {
|
|
736
913
|
return {
|
|
@@ -744,7 +921,12 @@ Run blockrun_models to see all 41+ models with pricing.`,
|
|
|
744
921
|
maxTokens: max_tokens,
|
|
745
922
|
maxOutputTokens: max_tokens,
|
|
746
923
|
temperature,
|
|
747
|
-
|
|
924
|
+
// @blockrun/llm 2.x dropped the "free" routing profile; the gateway
|
|
925
|
+
// already routes to the most cost-effective model by default, so we
|
|
926
|
+
// omit it and let ClawRouter pick (matches the SDK upgrade path).
|
|
927
|
+
routingProfile: routing_profile === "free" ? void 0 : routing_profile,
|
|
928
|
+
responseFormat,
|
|
929
|
+
stop
|
|
748
930
|
});
|
|
749
931
|
recordSpending(budget, result.routing.costEstimate || 1e-3, agent_id);
|
|
750
932
|
return {
|
|
@@ -772,7 +954,9 @@ ${result.response}` }],
|
|
|
772
954
|
try {
|
|
773
955
|
const result = await llm.chatCompletion(targetModel, fullMessages, {
|
|
774
956
|
maxTokens: max_tokens,
|
|
775
|
-
temperature
|
|
957
|
+
temperature,
|
|
958
|
+
responseFormat,
|
|
959
|
+
stop
|
|
776
960
|
});
|
|
777
961
|
const reply = result.choices?.[0]?.message?.content || "";
|
|
778
962
|
recordSpending(budget, estimatedCost, agent_id);
|
|
@@ -792,7 +976,9 @@ ${reply}` }],
|
|
|
792
976
|
const response = await llm.chat(model, message, {
|
|
793
977
|
system,
|
|
794
978
|
maxTokens: max_tokens,
|
|
795
|
-
temperature
|
|
979
|
+
temperature,
|
|
980
|
+
responseFormat,
|
|
981
|
+
stop
|
|
796
982
|
});
|
|
797
983
|
recordSpending(budget, estimatedCost, agent_id);
|
|
798
984
|
return { content: [{ type: "text", text: response }] };
|
|
@@ -812,7 +998,9 @@ ${reply}` }],
|
|
|
812
998
|
const response = await llm.chat(m, message, {
|
|
813
999
|
system,
|
|
814
1000
|
maxTokens: max_tokens,
|
|
815
|
-
temperature
|
|
1001
|
+
temperature,
|
|
1002
|
+
responseFormat,
|
|
1003
|
+
stop
|
|
816
1004
|
});
|
|
817
1005
|
recordSpending(budget, estimatedCost, agent_id);
|
|
818
1006
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"mcpName": "io.github.BlockRunAI/blockrun-mcp",
|
|
5
5
|
"description": "BlockRun MCP Server - Give your AI agent web search, deep research, prediction markets, crypto data, X/Twitter intelligence. Paid via x402 micropayments.",
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"url": "https://github.com/blockrunai/blockrun-mcp/issues"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@
|
|
47
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
48
|
+
"@blockrun/llm": "^2.11.0",
|
|
48
49
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
50
|
"open": "^11.0.0",
|
|
50
51
|
"qrcode": "^1.5.4",
|