@blockrun/mcp 0.17.0 → 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 +176 -2
- package/package.json +2 -1
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();
|
|
@@ -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;
|
|
@@ -716,14 +860,24 @@ Run blockrun_models to see all 41+ models with pricing.`,
|
|
|
716
860
|
temperature: z2.number().optional().default(1).describe("Creativity 0-2"),
|
|
717
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."),
|
|
718
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)."),
|
|
719
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."),
|
|
720
868
|
messages: z2.array(z2.object({
|
|
721
869
|
role: z2.enum(["user", "assistant", "system"]),
|
|
722
|
-
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.")
|
|
723
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.")
|
|
724
878
|
}
|
|
725
879
|
},
|
|
726
|
-
async ({ message, model, mode, routing, routing_profile, system, max_tokens, temperature, response_format, stop, agent_id, messages }) => {
|
|
880
|
+
async ({ message, model, mode, routing, routing_profile, system, max_tokens, temperature, response_format, stop, thinking, agent_id, messages }) => {
|
|
727
881
|
const llm = getClient();
|
|
728
882
|
const responseFormat = response_format ? { type: response_format } : void 0;
|
|
729
883
|
const estimatedCost = estimateChatCost(mode, model, routing, routing_profile);
|
|
@@ -734,6 +888,26 @@ Run blockrun_models to see all 41+ models with pricing.`,
|
|
|
734
888
|
isError: true
|
|
735
889
|
};
|
|
736
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
|
+
}
|
|
737
911
|
if (routing === "smart") {
|
|
738
912
|
if (!(llm instanceof LLMClient2)) {
|
|
739
913
|
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,6 +44,7 @@
|
|
|
44
44
|
"url": "https://github.com/blockrunai/blockrun-mcp/issues"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
47
48
|
"@blockrun/llm": "^2.11.0",
|
|
48
49
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
50
|
"open": "^11.0.0",
|