Call MCP tools from your code
A step-by-step guide to calling an MCP server's tools through Veefore from a TypeScript or Python agent with the SDK.
This guide is for an agent you write yourself. Your code calls MCP tools through the Veefore SDK instead of straight at the server, and Veefore checks each call, calls the server with its own credential, and returns the server's answer unchanged.
If your code already calls the server with the official MCP SDK, the CLI can make this change for you: see Reroute MCP calls with the CLI. If you do not write code at all, use an MCP client instead.
You will need: Node.js 20.10 or newer (and Python 3.9+ for a Python agent), an agent project, and a Veefore account. The example uses DeepWiki, which needs no token.
#Step by step
Set up the project with the CLI
In your agent project:
bashnpm install -g veefore
cd your-agent-project
veefore
The CLI opens the console to sign you in, creates an agent for this project in the Development environment, and writes two files:
veefore.config.json— settings, no secrets; commit it..env.local— the agent's private key and the gateway address; gitignored. The SDK reads it.
It also adds a starter policy as a draft. Answer y when it asks. If it offers
to change your code and you have no tools to wrap yet, you can skip that.
Connect the MCP server
- Sign in at console.veefore.com and set the Environment switcher at the top to Development — the environment the CLI created your agent in.
- Open MCP → Connect server.
- Paste
https://mcp.deepwiki.com/mcp, tick I know who runs this server and trust it, and click Connect. - Under Agent, tick the agent the CLI created (named after your project).
- Keep the tools you want ticked and click Authorize N tools.
The server's slug — deepwiki here — is how your code names it. It is on
the server's page and in its address.
Install the SDK
bashnpm install @veefore/sdk@1.8.0
mcpServer() needs 1.8.0 or later.
bashpip install veefore-py==1.7.0
Call a tool
tsimport { mcpServer } from '@veefore/sdk';
const result = await mcpServer('deepwiki').callTool({
name: 'read_wiki_structure',
arguments: { repoName: 'modelcontextprotocol/typescript-sdk' },
});
console.log(result.content); // the server's own tools/call result
mcpServer() builds its client from .env.local. It has the same callTool
as the official MCP SDK's client, so if you already use one, change only the
receiver:
ts// Before
const result = await client.callTool({ name: 'read_wiki_structure', arguments });
// After
const result = await mcpServer('deepwiki').callTool({ name: 'read_wiki_structure', arguments });
pythonfrom veefore import guard_client
decision = guard_client().mcp_call(
server="deepwiki",
tool="read_wiki_structure",
arguments={"repoName": "modelcontextprotocol/typescript-sdk"},
)
print(decision.result) # the server's own tools/call result
guard_client() builds its client from .env.local, found from the current
directory upwards. Variables already set in the environment win, which is how
you configure it in production.
Run it, and check it worked
Run your agent as you normally do. To watch decisions as they happen, run
veefore dev in another terminal in the project.
Then open Actions in the console. The call is listed with your agent, the
tool mcp_deepwiki_read_wiki_structure, the decision and the time.
Decide what happens when a person must approve
If a rule requires approval, the SDK waits for the decision by default. A chat agent usually should not block its turn, so it can defer instead:
tsimport { isAwaitingApproval, mcpServer } from '@veefore/sdk';
const outcome = await mcpServer('deepwiki', { onApproval: 'defer' }).callTool({
name: 'ask_wiki_question',
arguments: { repoName: 'facebook/react', question: 'How does reconciliation work?' },
});
if (isAwaitingApproval(outcome)) return outcome.say; // tell the user, carry on
On a later turn, resumeApprovals() finds the decision and, if it was approved,
performs the call once. The modes are 'wait' (default), 'defer' and
'throw'; VEEFORE_APPROVAL_MODE sets the default for the project. See
Handle approvals.
Turn on protection
New environments start in Observe: calls run and are recorded, and nothing is blocked. When you have watched it for a while:
bashveefore policies test
veefore deploy --mode ENFORCE --reason "MCP tools reviewed"
policies test shows which recent calls would start being refused before
anything changes. See What your rules do
for how MCP calls are decided.
#When a call is refused
Under Enforce, a refused call throws VeeforeBlockedError in TypeScript
(and raises in Python) with the reason, and the server is never contacted:
tsimport { VeeforeBlockedError, mcpServer } from '@veefore/sdk';
try {
await mcpServer('deepwiki').callTool({ name: 'ask_wiki_question', arguments });
} catch (error) {
if (error instanceof VeeforeBlockedError) console.warn(error.message);
else throw error;
}
MCP troubleshooting lists every refusal and its fix.
#Without the SDK
The SDK calls one gateway endpoint, POST /gateway/v1/mcp/call, with a session
token from the agent's signed identity. Any language can do the same; see
Sign your own assertion and the
JavaScript SDK reference for mcpCall().