@agentvalet/mcp-server 0.3.10 → 1.1.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/config.js CHANGED
@@ -80,16 +80,28 @@ export async function validateConfig() {
80
80
  process.exit(1);
81
81
  }
82
82
  }
83
- // Nothing usable — fall through to the original missing-env diagnostic.
83
+ // Nothing usable — boot credential-less instead of exiting.
84
+ //
85
+ // Introspection (initialize + tools/list) must work with NO credentials so
86
+ // a sandbox (e.g. Glama) can start the server and read the static tool
87
+ // catalogue. We therefore return a config with an EMPTY identity and a NULL
88
+ // key rather than process.exit(1). The credential requirement is deferred to
89
+ // tools/call: an authed tool invoked with no usable identity returns a clean
90
+ // "credentials not configured" tool result (see index.ts requireCredentials),
91
+ // never a crash. An empty agentId is the signal for "state C — not configured"
92
+ // (distinct from "state B — identity present, key pending" which keeps the
93
+ // existing invite-bind pending response).
84
94
  const missing = [];
85
95
  for (const key of ["AGENT_ID", "OWNER_ID", "PROXY_URL"]) {
86
96
  if (!process.env[key])
87
97
  missing.push(key);
88
98
  }
89
- process.stderr.write(`[mcp-server] Missing required environment variables: ${missing.join(", ")}\n` +
90
- `Either set them, run the invite-bind flow with INVITE_BIND_SECRET, ` +
91
- `or restore ~/.agentvalet/agent.{key,json} from a previous bind.\n`);
92
- process.exit(1);
99
+ process.stderr.write(`[mcp-server] No credentials configured (${missing.join(", ")} unset, no disk ` +
100
+ `identity, no INVITE_BIND_SECRET). Booting in introspection-only mode: ` +
101
+ `tools/list works; tools/call returns a credentials-required message. ` +
102
+ `Set the env vars, run the invite-bind flow, or restore ` +
103
+ `~/.agentvalet/agent.{key,json} to enable platform calls.\n`);
104
+ return buildConfig({ agentId: "", ownerId: "", proxyUrl: DEFAULT_PROXY_URL });
93
105
  }
94
106
  async function buildConfig(args) {
95
107
  const proxyUrl = args.proxyUrl.replace(/\/$/, "");
@@ -106,8 +118,12 @@ async function buildConfig(args) {
106
118
  privateKey = await importPKCS8(privateKeyPem, "RS256");
107
119
  }
108
120
  catch (err) {
109
- process.stderr.write(`[mcp-server] Invalid private key: ${err instanceof Error ? err.message : err}\n`);
110
- process.exit(1);
121
+ // Don't crash the process on a malformed key that would also take down
122
+ // introspection. Drop the unusable key to null and log; tools/call will
123
+ // return the credentials-required message instead of a hard exit.
124
+ process.stderr.write(`[mcp-server] Invalid private key (ignored): ${err instanceof Error ? err.message : err}\n`);
125
+ privateKeyPem = null;
126
+ privateKey = null;
111
127
  }
112
128
  }
113
129
  return { agentId: args.agentId, ownerId: args.ownerId, proxyUrl, privateKeyPem, privateKey };
package/dist/index.js CHANGED
@@ -68,6 +68,40 @@ function pendingFirstCallResponse() {
68
68
  isError: true,
69
69
  };
70
70
  }
71
+ // State C — no credentials at all (empty env / Glama-style sandbox). The
72
+ // server still boots and answers introspection; an authed tool call lands
73
+ // here and gets an actionable message instead of a crash. Distinct from the
74
+ // state-B "owner confirmation pending" response above, which means an identity
75
+ // IS configured but its key hasn't arrived yet.
76
+ function credentialsNotConfiguredResponse() {
77
+ return {
78
+ content: [{
79
+ type: "text",
80
+ text: "AgentValet credentials are not configured. Set AGENTVALET_AGENT_ID, " +
81
+ "AGENTVALET_OWNER_ID, and the agent private key (and optionally " +
82
+ "AGENTVALET_PROXY_URL). Run npx @agentvalet/register to create an agent. " +
83
+ "Docs: https://github.com/AgentValet/AgentValet#quickstart",
84
+ }],
85
+ isError: true,
86
+ };
87
+ }
88
+ // Credential gate for authed tools/call. Returns null when the call may
89
+ // proceed (state A — a JWT can be signed), or a ready-to-return MCP tool
90
+ // result for the two no-key states:
91
+ // • state B (identity present, key pending) → existing invite-bind pending
92
+ // response, preserving the MCPB first-run flow.
93
+ // • state C (no identity at all) → credentials-not-configured.
94
+ // The no-auth tools (agent_register / agent_status) intentionally never call
95
+ // this — you must be able to register in order to OBTAIN credentials.
96
+ async function requireCredentials() {
97
+ if (AGENT_PRIVATE_KEY_RAW !== null)
98
+ return null;
99
+ if (AGENT_ID && OWNER_ID) {
100
+ await notifyBindSecret();
101
+ return pendingFirstCallResponse();
102
+ }
103
+ return credentialsNotConfiguredResponse();
104
+ }
71
105
  // ---------------------------------------------------------------------------
72
106
  // Tool definitions
73
107
  // ---------------------------------------------------------------------------
@@ -499,10 +533,9 @@ function tryParseJson(text) {
499
533
  // Tool handlers
500
534
  // ---------------------------------------------------------------------------
501
535
  async function handleListPlatforms() {
502
- if (AGENT_PRIVATE_KEY_RAW === null) {
503
- await notifyBindSecret();
504
- return pendingFirstCallResponse();
505
- }
536
+ const gate = await requireCredentials();
537
+ if (gate)
538
+ return gate;
506
539
  let response;
507
540
  try {
508
541
  response = await fetchWithAuth(`${PROXY_URL}/v1/agent/permissions`, { method: "GET", headers: {} });
@@ -558,10 +591,9 @@ const APPROVAL_POLL_BUDGET_MS = 50_000;
558
591
  const APPROVAL_POLL_INTERVAL_MS = 2_000;
559
592
  const APPROVAL_PROGRESS_INTERVAL_MS = 5_000;
560
593
  async function handleUsePlatform(params, progressToken) {
561
- if (AGENT_PRIVATE_KEY_RAW === null) {
562
- await notifyBindSecret();
563
- return pendingFirstCallResponse();
564
- }
594
+ const gate = await requireCredentials();
595
+ if (gate)
596
+ return gate;
565
597
  const requestBody = {
566
598
  platform: params.platform,
567
599
  endpoint: params.endpoint,
@@ -729,6 +761,9 @@ async function handleAgentStatus(token) {
729
761
  return jsonContent(body);
730
762
  }
731
763
  async function handleAuthzenEvaluate(platformId, scope) {
764
+ const gate = await requireCredentials();
765
+ if (gate)
766
+ return gate;
732
767
  const authzenBody = {
733
768
  subject: { type: "agent", id: AGENT_ID },
734
769
  action: { name: "tool_call" },
@@ -750,10 +785,9 @@ async function handleAuthzenEvaluate(platformId, scope) {
750
785
  return jsonContent(body);
751
786
  }
752
787
  async function handleListMyPendingActions() {
753
- if (AGENT_PRIVATE_KEY_RAW === null) {
754
- await notifyBindSecret();
755
- return pendingFirstCallResponse();
756
- }
788
+ const gate = await requireCredentials();
789
+ if (gate)
790
+ return gate;
757
791
  let response;
758
792
  try {
759
793
  response = await fetchWithAuth(`${PROXY_URL}/v1/agents/me/pending-actions`, { method: "GET" });
@@ -767,10 +801,9 @@ async function handleListMyPendingActions() {
767
801
  return jsonContent(text);
768
802
  }
769
803
  async function handleReportSelfDiagnostic(args) {
770
- if (AGENT_PRIVATE_KEY_RAW === null) {
771
- await notifyBindSecret();
772
- return pendingFirstCallResponse();
773
- }
804
+ const gate = await requireCredentials();
805
+ if (gate)
806
+ return gate;
774
807
  // Whitelist body fields — never forward owner_id/agent_id (proxy derives those from JWT).
775
808
  const body = {
776
809
  severity: args.severity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentvalet/mcp-server",
3
- "version": "0.3.10",
3
+ "version": "1.1.0",
4
4
  "description": "AgentValet MCP server — lets AI agents call approved platforms via the AgentValet proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",